Unverified Commit 12be663c authored by Chris Kim's avatar Chris Kim Committed by GitHub

[release-1.20] Bump Kubernetes to v1.20.9 (#3655)

* Bump golang to 1.15.14 * Update to v1.20.9 Signed-off-by: 's avatarChris Kim <oats87g@gmail.com>
parent bd62da13
ARG GOLANG=golang:1.15.12-alpine3.12
ARG GOLANG=golang:1.15.14-alpine3.13
FROM ${GOLANG}
ARG http_proxy=$http_proxy
......@@ -9,7 +9,8 @@ ENV https_proxy=$https_proxy
ENV no_proxy=$no_proxy
RUN apk -U --no-cache add bash git gcc musl-dev docker vim less file curl wget ca-certificates jq linux-headers zlib-dev tar zip squashfs-tools npm coreutils \
python2 openssl-dev libffi-dev libseccomp libseccomp-dev make libuv-static sqlite-dev sqlite-static libselinux libselinux-dev zlib-dev zlib-static zstd
python2 openssl-dev libffi-dev libseccomp libseccomp-dev libseccomp-static make libuv-static sqlite-dev sqlite-static libselinux libselinux-dev zlib-dev \
zlib-static zstd
RUN if [ "$(go env GOARCH)" = "arm64" ]; then \
wget https://github.com/aquasecurity/trivy/releases/download/v0.11.0/trivy_0.11.0_Linux-ARM64.tar.gz && \
tar -zxvf trivy_0.11.0_Linux-ARM64.tar.gz && \
......
ARG GOLANG=golang:1.15.12-alpine3.12
ARG GOLANG=golang:1.15.14-alpine3.13
FROM ${GOLANG}
COPY --from=plugins/manifest:1.2.3 /bin/* /bin/
......
ARG GOLANG=golang:1.15.12-alpine3.12
ARG GOLANG=golang:1.15.14-alpine3.13
FROM ${GOLANG}
RUN apk -U --no-cache add bash git gcc musl-dev docker curl jq coreutils python2 openssl py-pip
......
ARG GOLANG=golang:1.15.12-alpine3.12
ARG GOLANG=golang:1.15.14-alpine3.13
FROM ${GOLANG}
RUN apk -U --no-cache add bash jq
......
......@@ -35,34 +35,34 @@ replace (
google.golang.org/genproto => google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63
google.golang.org/grpc => google.golang.org/grpc v1.27.1
gopkg.in/square/go-jose.v2 => gopkg.in/square/go-jose.v2 v2.2.2
k8s.io/api => github.com/k3s-io/kubernetes/staging/src/k8s.io/api v1.20.8-k3s1
k8s.io/apiextensions-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.20.8-k3s1
k8s.io/apimachinery => github.com/k3s-io/kubernetes/staging/src/k8s.io/apimachinery v1.20.8-k3s1
k8s.io/apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiserver v1.20.8-k3s1
k8s.io/cli-runtime => github.com/k3s-io/kubernetes/staging/src/k8s.io/cli-runtime v1.20.8-k3s1
k8s.io/client-go => github.com/k3s-io/kubernetes/staging/src/k8s.io/client-go v1.20.8-k3s1
k8s.io/cloud-provider => github.com/k3s-io/kubernetes/staging/src/k8s.io/cloud-provider v1.20.8-k3s1
k8s.io/cluster-bootstrap => github.com/k3s-io/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.20.8-k3s1
k8s.io/code-generator => github.com/k3s-io/kubernetes/staging/src/k8s.io/code-generator v1.20.8-k3s1
k8s.io/component-base => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-base v1.20.8-k3s1
k8s.io/component-helpers => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-helpers v1.20.8-k3s1
k8s.io/controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/controller-manager v1.20.8-k3s1
k8s.io/cri-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/cri-api v1.20.8-k3s1
k8s.io/csi-translation-lib => github.com/k3s-io/kubernetes/staging/src/k8s.io/csi-translation-lib v1.20.8-k3s1
k8s.io/kube-aggregator => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-aggregator v1.20.8-k3s1
k8s.io/kube-controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-controller-manager v1.20.8-k3s1
k8s.io/kube-proxy => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-proxy v1.20.8-k3s1
k8s.io/kube-scheduler => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-scheduler v1.20.8-k3s1
k8s.io/kubectl => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubectl v1.20.8-k3s1
k8s.io/kubelet => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubelet v1.20.8-k3s1
k8s.io/kubernetes => github.com/k3s-io/kubernetes v1.20.8-k3s1
k8s.io/legacy-cloud-providers => github.com/k3s-io/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.20.8-k3s1
k8s.io/metrics => github.com/k3s-io/kubernetes/staging/src/k8s.io/metrics v1.20.8-k3s1
k8s.io/mount-utils => github.com/k3s-io/kubernetes/staging/src/k8s.io/mount-utils v1.20.8-k3s1
k8s.io/node-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/node-api v1.20.8-k3s1
k8s.io/sample-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-apiserver v1.20.8-k3s1
k8s.io/sample-cli-plugin => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-cli-plugin v1.20.8-k3s1
k8s.io/sample-controller => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-controller v1.20.8-k3s1
k8s.io/api => github.com/k3s-io/kubernetes/staging/src/k8s.io/api v1.20.9-k3s1
k8s.io/apiextensions-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.20.9-k3s1
k8s.io/apimachinery => github.com/k3s-io/kubernetes/staging/src/k8s.io/apimachinery v1.20.9-k3s1
k8s.io/apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiserver v1.20.9-k3s1
k8s.io/cli-runtime => github.com/k3s-io/kubernetes/staging/src/k8s.io/cli-runtime v1.20.9-k3s1
k8s.io/client-go => github.com/k3s-io/kubernetes/staging/src/k8s.io/client-go v1.20.9-k3s1
k8s.io/cloud-provider => github.com/k3s-io/kubernetes/staging/src/k8s.io/cloud-provider v1.20.9-k3s1
k8s.io/cluster-bootstrap => github.com/k3s-io/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.20.9-k3s1
k8s.io/code-generator => github.com/k3s-io/kubernetes/staging/src/k8s.io/code-generator v1.20.9-k3s1
k8s.io/component-base => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-base v1.20.9-k3s1
k8s.io/component-helpers => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-helpers v1.20.9-k3s1
k8s.io/controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/controller-manager v1.20.9-k3s1
k8s.io/cri-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/cri-api v1.20.9-k3s1
k8s.io/csi-translation-lib => github.com/k3s-io/kubernetes/staging/src/k8s.io/csi-translation-lib v1.20.9-k3s1
k8s.io/kube-aggregator => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-aggregator v1.20.9-k3s1
k8s.io/kube-controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-controller-manager v1.20.9-k3s1
k8s.io/kube-proxy => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-proxy v1.20.9-k3s1
k8s.io/kube-scheduler => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-scheduler v1.20.9-k3s1
k8s.io/kubectl => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubectl v1.20.9-k3s1
k8s.io/kubelet => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubelet v1.20.9-k3s1
k8s.io/kubernetes => github.com/k3s-io/kubernetes v1.20.9-k3s1
k8s.io/legacy-cloud-providers => github.com/k3s-io/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.20.9-k3s1
k8s.io/metrics => github.com/k3s-io/kubernetes/staging/src/k8s.io/metrics v1.20.9-k3s1
k8s.io/mount-utils => github.com/k3s-io/kubernetes/staging/src/k8s.io/mount-utils v1.20.9-k3s1
k8s.io/node-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/node-api v1.20.9-k3s1
k8s.io/sample-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-apiserver v1.20.9-k3s1
k8s.io/sample-cli-plugin => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-cli-plugin v1.20.9-k3s1
k8s.io/sample-controller => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-controller v1.20.9-k3s1
mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34
)
......@@ -126,7 +126,7 @@ require (
k8s.io/cri-api v1.20.0
k8s.io/klog v1.0.0
k8s.io/kubectl v1.20.5
k8s.io/kubernetes v1.20.8
k8s.io/kubernetes v1.20.9
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
sigs.k8s.io/yaml v1.2.0
)
......@@ -3,8 +3,8 @@ package version
var (
gitMajor = "1"
gitMinor = "20"
gitVersion = "v1.20.8-k3s1"
gitCommit = "d3d0f184376c7a0e7eedac78e4e3c018da07eba9"
gitVersion = "v1.20.9-k3s1"
gitCommit = "d30af26bd94ad3b90ba23bb51c962cfa46e15db7"
gitTreeState = "clean"
buildDate = "2021-06-17T20:33:07Z"
buildDate = "2021-07-15T23:42:22Z"
)
......@@ -673,8 +673,8 @@ rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.19 h1:0jaDAAxtqIrrqas4vtTqxct4xS5kHfRNycTRLTyJmVM=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.19/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.3 h1:4oyYo8NREp49LBBhKxEqCulFjg26rawYKrnCmg+Sr6c=
sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
......@@ -3,8 +3,8 @@ package version
var (
gitMajor = "1"
gitMinor = "20"
gitVersion = "v1.20.8-k3s1"
gitCommit = "d3d0f184376c7a0e7eedac78e4e3c018da07eba9"
gitVersion = "v1.20.9-k3s1"
gitCommit = "d30af26bd94ad3b90ba23bb51c962cfa46e15db7"
gitTreeState = "clean"
buildDate = "2021-06-17T20:33:07Z"
buildDate = "2021-07-15T23:42:22Z"
)
......@@ -199,8 +199,8 @@ k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.3 h1:4oyYo8NREp49LBBhKxEqCulFjg26rawYKrnCmg+Sr6c=
sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
......@@ -138,9 +138,6 @@ func parseMaxSize(size string) (int64, error) {
if !ok {
return 0, fmt.Errorf("invalid max log size")
}
if maxSize < 0 {
return 0, fmt.Errorf("negative max log size %d", maxSize)
}
return maxSize, nil
}
......@@ -161,6 +158,10 @@ func NewContainerLogManager(runtimeService internalapi.RuntimeService, osInterfa
if err != nil {
return nil, fmt.Errorf("failed to parse container log max size %q: %v", maxSize, err)
}
// Negative number means to disable container log rotation
if parsedMaxSize < 0 {
return NewStubContainerLogManager(), nil
}
// policy LogRotatePolicy
return &containerLogManager{
osInterface: osInterface,
......
......@@ -1228,7 +1228,7 @@ func (proxier *Proxier) syncProxyRules() {
}
hnsLoadBalancer, err := hns.getLoadBalancer(
lbIngressEndpoints,
loadBalancerFlags{isDSR: svcInfo.preserveDIP || proxier.isDSR || svcInfo.localTrafficDSR, useMUX: svcInfo.preserveDIP, preserveDIP: svcInfo.preserveDIP, sessionAffinity: sessionAffinityClientIP, isIPv6: proxier.isIPv6Mode},
loadBalancerFlags{isDSR: svcInfo.preserveDIP || svcInfo.localTrafficDSR, useMUX: svcInfo.preserveDIP, preserveDIP: svcInfo.preserveDIP, sessionAffinity: sessionAffinityClientIP, isIPv6: proxier.isIPv6Mode},
sourceVip,
lbIngressIP.ip,
Enum(svcInfo.Protocol()),
......
......@@ -80,12 +80,16 @@ func NewBalancedAllocation(_ runtime.Object, h framework.Handle) (framework.Plug
// todo: use resource weights in the scorer function
func balancedResourceScorer(requested, allocable resourceToValueMap, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64 {
// This to find a node which has most balanced CPU, memory and volume usage.
cpuFraction := fractionOfCapacity(requested[v1.ResourceCPU], allocable[v1.ResourceCPU])
memoryFraction := fractionOfCapacity(requested[v1.ResourceMemory], allocable[v1.ResourceMemory])
// This to find a node which has most balanced CPU, memory and volume usage.
if cpuFraction >= 1 || memoryFraction >= 1 {
// if requested >= capacity, the corresponding host should never be preferred.
return 0
// fractions might be greater than 1 because pods with no requests get minimum
// values.
if cpuFraction > 1 {
cpuFraction = 1
}
if memoryFraction > 1 {
memoryFraction = 1
}
if includeVolumes && utilfeature.DefaultFeatureGate.Enabled(features.BalanceAttachedNodeVolumes) && allocatableVolumes > 0 {
......
......@@ -110,7 +110,9 @@ func mostRequestedScore(requested, capacity int64) int64 {
return 0
}
if requested > capacity {
return 0
// `requested` might be greater than `capacity` because pods with no
// requests get minimum values.
requested = capacity
}
return (requested * framework.MaxNodeScore) / capacity
......
......@@ -353,17 +353,17 @@ func truncateMessage(message string) string {
func updatePod(client clientset.Interface, pod *v1.Pod, condition *v1.PodCondition, nominatedNode string) error {
klog.V(3).Infof("Updating pod condition for %s/%s to (%s==%s, Reason=%s)", pod.Namespace, pod.Name, condition.Type, condition.Status, condition.Reason)
podCopy := pod.DeepCopy()
podStatusCopy := pod.Status.DeepCopy()
// NominatedNodeName is updated only if we are trying to set it, and the value is
// different from the existing one.
if !podutil.UpdatePodCondition(&podCopy.Status, condition) &&
if !podutil.UpdatePodCondition(podStatusCopy, condition) &&
(len(nominatedNode) == 0 || pod.Status.NominatedNodeName == nominatedNode) {
return nil
}
if nominatedNode != "" {
podCopy.Status.NominatedNodeName = nominatedNode
podStatusCopy.NominatedNodeName = nominatedNode
}
return util.PatchPod(client, pod, podCopy)
return util.PatchPodStatus(client, pod, podStatusCopy)
}
// assume signals to the cache that a pod is already in the cache, so that binding can be asynchronous.
......
......@@ -25,6 +25,7 @@ go_test(
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/client-go/testing:go_default_library",
"//staging/src/k8s.io/kube-scheduler/extender/v1:go_default_library",
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
],
)
......
......@@ -118,15 +118,19 @@ func GetPodAntiAffinityTerms(affinity *v1.Affinity) (terms []v1.PodAffinityTerm)
return terms
}
// PatchPod calculates the delta bytes change from <old> to <new>,
// PatchPodStatus calculates the delta bytes change from <old.Status> to <newStatus>,
// and then submit a request to API server to patch the pod changes.
func PatchPod(cs kubernetes.Interface, old *v1.Pod, new *v1.Pod) error {
oldData, err := json.Marshal(old)
func PatchPodStatus(cs kubernetes.Interface, old *v1.Pod, newStatus *v1.PodStatus) error {
if newStatus == nil {
return nil
}
oldData, err := json.Marshal(v1.Pod{Status: old.Status})
if err != nil {
return err
}
newData, err := json.Marshal(new)
newData, err := json.Marshal(v1.Pod{Status: *newStatus})
if err != nil {
return err
}
......@@ -156,9 +160,9 @@ func ClearNominatedNodeName(cs kubernetes.Interface, pods ...*v1.Pod) utilerrors
if len(p.Status.NominatedNodeName) == 0 {
continue
}
podCopy := p.DeepCopy()
podCopy.Status.NominatedNodeName = ""
if err := PatchPod(cs, p, podCopy); err != nil {
podStatusCopy := p.Status.DeepCopy()
podStatusCopy.NominatedNodeName = ""
if err := PatchPodStatus(cs, p, podStatusCopy); err != nil {
errs = append(errs, err)
}
}
......
......@@ -153,7 +153,7 @@ func (shared *sharedDatastore) getNodeHosts(ctx context.Context, nodes []NodeInf
var vmoList []mo.VirtualMachine
err := pc.Retrieve(ctx, vmRefs, []string{nameProperty, runtimeHost}, &vmoList)
if err != nil {
klog.Errorf("SharedHost.getNodeHosts: unable to fetch vms from datacenter %s: %w", nodeInfo.dataCenter.String(), err)
klog.Errorf("SharedHost.getNodeHosts: unable to fetch vms from datacenter %s: %v", nodeInfo.dataCenter.String(), err)
return nil, err
}
var hostMoList []mo.HostSystem
......@@ -169,7 +169,7 @@ func (shared *sharedDatastore) getNodeHosts(ctx context.Context, nodes []NodeInf
pc = property.DefaultCollector(nodeInfo.dataCenter.Client())
err = pc.Retrieve(ctx, hostRefs, []string{summary}, &hostMoList)
if err != nil {
klog.Errorf("SharedHost.getNodeHosts: unable to fetch hosts from datacenter %s: %w", nodeInfo.dataCenter.String(), err)
klog.Errorf("SharedHost.getNodeHosts: unable to fetch hosts from datacenter %s: %v", nodeInfo.dataCenter.String(), err)
return nil, err
}
var hosts []hostInfo
......
......@@ -933,11 +933,10 @@ func (vs *VSphere) AttachDisk(vmDiskPath string, storagePolicyName string, nodeN
return "", err
}
// try and get canonical path for disk and if we can't throw error
vmDiskPath, err = getcanonicalVolumePath(ctx, vm.Datacenter, vmDiskPath)
if err != nil {
klog.Errorf("failed to get canonical path for %s on node %s: %v", vmDiskPath, convertToString(nodeName), err)
return "", err
// try and get canonical path for disk and if we can't use provided vmDiskPath
canonicalPath, pathFetchErr := getcanonicalVolumePath(ctx, vm.Datacenter, vmDiskPath)
if canonicalPath != "" && pathFetchErr == nil {
vmDiskPath = canonicalPath
}
diskUUID, err = vm.AttachDisk(ctx, vmDiskPath, &vclib.VolumeOptions{SCSIControllerType: vclib.PVSCSIControllerType, StoragePolicyName: storagePolicyName})
......
......@@ -206,6 +206,40 @@ func (s *Set) WithPrefix(pe PathElement) *Set {
return subset
}
// Leaves returns a set containing only the leaf paths
// of a set.
func (s *Set) Leaves() *Set {
leaves := PathElementSet{}
im := 0
ic := 0
// any members that are not also children are leaves
outer:
for im < len(s.Members.members) {
member := s.Members.members[im]
for ic < len(s.Children.members) {
d := member.Compare(s.Children.members[ic].pathElement)
if d == 0 {
ic++
im++
continue outer
} else if d < 0 {
break
} else /* if d > 0 */ {
ic++
}
}
leaves.members = append(leaves.members, member)
im++
}
return &Set{
Members: leaves,
Children: *s.Children.Leaves(),
}
}
// setNode is a pair of PathElement / Set, for the purpose of expressing
// nested set membership.
type setNode struct {
......@@ -455,3 +489,17 @@ func (s *SetNodeMap) iteratePrefix(prefix Path, f func(Path)) {
n.set.iteratePrefix(append(prefix, pe), f)
}
}
// Leaves returns a SetNodeMap containing
// only setNodes with leaf PathElements.
func (s *SetNodeMap) Leaves() *SetNodeMap {
out := &SetNodeMap{}
out.members = make(sortedSetNode, len(s.members))
for i, n := range s.members {
out.members[i] = setNode{
pathElement: n.pathElement,
set: n.set.Leaves(),
}
}
return out
}
......@@ -124,13 +124,6 @@ func ReconcileFieldSetWithSchema(fieldset *fieldpath.Set, tv *TypedValue) (*fiel
v.schema = tv.schema
v.typeRef = tv.typeRef
// We don't reconcile deduced types, which are primarily for use by unstructured CRDs. Deduced
// types do not support atomic or granular tags. Nor does the dynamic schema deduction
// interact well with the reconcile logic.
if v.schema == DeducedParseableType.Schema {
return nil, nil
}
defer v.finished()
errs := v.reconcile()
......@@ -187,19 +180,17 @@ func (v *reconcileWithSchemaWalker) visitListItems(t *schema.List, element *fiel
}
func (v *reconcileWithSchemaWalker) doList(t *schema.List) (errs ValidationErrors) {
// reconcile lists changed from granular to atomic
// reconcile lists changed from granular to atomic.
// Note that migrations from atomic to granular are not recommended and will
// be treated as if they were always granular.
//
// In this case, the manager that owned the previously atomic field (and all subfields),
// will now own just the top-level field and none of the subfields.
if !v.isAtomic && t.ElementRelationship == schema.Atomic {
v.toRemove = fieldpath.NewSet(v.path) // remove all root and all children fields
v.toAdd = fieldpath.NewSet(v.path) // add the root of the atomic
return errs
}
// reconcile lists changed from atomic to granular
if v.isAtomic && t.ElementRelationship == schema.Associative {
v.toAdd, errs = buildGranularFieldSet(v.path, v.value)
if errs != nil {
return errs
}
}
if v.fieldSet != nil {
errs = v.visitListItems(t, v.fieldSet)
}
......@@ -231,7 +222,18 @@ func (v *reconcileWithSchemaWalker) visitMapItems(t *schema.Map, element *fieldp
}
func (v *reconcileWithSchemaWalker) doMap(t *schema.Map) (errs ValidationErrors) {
// reconcile maps and structs changed from granular to atomic
// We don't currently reconcile deduced types (unstructured CRDs) or maps that contain only unknown
// fields since deduced types do not yet support atomic or granular tags.
if isUntypedDeducedMap(t) {
return errs
}
// reconcile maps and structs changed from granular to atomic.
// Note that migrations from atomic to granular are not recommended and will
// be treated as if they were always granular.
//
// In this case the manager that owned the previously atomic field (and all subfields),
// will now own just the top-level field and none of the subfields.
if !v.isAtomic && t.ElementRelationship == schema.Atomic {
if v.fieldSet != nil && v.fieldSet.Size() > 0 {
v.toRemove = fieldpath.NewSet(v.path) // remove all root and all children fields
......@@ -239,34 +241,12 @@ func (v *reconcileWithSchemaWalker) doMap(t *schema.Map) (errs ValidationErrors)
}
return errs
}
// reconcile maps changed from atomic to granular
if v.isAtomic && (t.ElementRelationship == schema.Separable || t.ElementRelationship == "") {
v.toAdd, errs = buildGranularFieldSet(v.path, v.value)
if errs != nil {
return errs
}
}
if v.fieldSet != nil {
errs = v.visitMapItems(t, v.fieldSet)
}
return errs
}
func buildGranularFieldSet(path fieldpath.Path, value *TypedValue) (*fieldpath.Set, ValidationErrors) {
valueFieldSet, err := value.ToFieldSet()
if err != nil {
return nil, errorf("toFieldSet: %v", err)
}
if valueFieldSetAtPath, ok := fieldSetAtPath(valueFieldSet, path); ok {
result := fieldpath.NewSet(path)
resultAtPath := descendToPath(result, path)
*resultAtPath = *valueFieldSetAtPath
return result, nil
}
return nil, nil
}
func fieldSetAtPath(node *fieldpath.Set, path fieldpath.Path) (*fieldpath.Set, bool) {
ok := true
for _, pe := range path {
......@@ -293,3 +273,18 @@ func typeRefAtPath(t *schema.Map, pe fieldpath.PathElement) (schema.TypeRef, boo
}
return tr, tr != schema.TypeRef{}
}
// isUntypedDeducedMap returns true if m has no fields defined, but allows untyped elements.
// This is equivalent to a openAPI object that has x-kubernetes-preserve-unknown-fields=true
// but does not have any properties defined on the object.
func isUntypedDeducedMap(m *schema.Map) bool {
return isUntypedDeducedRef(m.ElementType) && m.Fields == nil
}
func isUntypedDeducedRef(t schema.TypeRef) bool {
if t.NamedType != nil {
return *t.NamedType == "__untyped_deduced_"
}
atom := t.Inlined
return atom.Scalar != nil && *atom.Scalar == "untyped"
}
......@@ -20,19 +20,26 @@ import (
)
type removingWalker struct {
value value.Value
out interface{}
schema *schema.Schema
toRemove *fieldpath.Set
allocator value.Allocator
value value.Value
out interface{}
schema *schema.Schema
toRemove *fieldpath.Set
allocator value.Allocator
shouldExtract bool
}
func removeItemsWithSchema(val value.Value, toRemove *fieldpath.Set, schema *schema.Schema, typeRef schema.TypeRef) value.Value {
// removeItemsWithSchema will walk the given value and look for items from the toRemove set.
// Depending on whether shouldExtract is set true or false, it will return a modified version
// of the input value with either:
// 1. only the items in the toRemove set (when shouldExtract is true) or
// 2. the items from the toRemove set removed from the value (when shouldExtract is false).
func removeItemsWithSchema(val value.Value, toRemove *fieldpath.Set, schema *schema.Schema, typeRef schema.TypeRef, shouldExtract bool) value.Value {
w := &removingWalker{
value: val,
schema: schema,
toRemove: toRemove,
allocator: value.NewFreelistAllocator(),
value: val,
schema: schema,
toRemove: toRemove,
allocator: value.NewFreelistAllocator(),
shouldExtract: shouldExtract,
}
resolveSchema(schema, typeRef, val, w)
return value.NewValueInterface(w.out)
......@@ -44,10 +51,22 @@ func (w *removingWalker) doScalar(t *schema.Scalar) ValidationErrors {
}
func (w *removingWalker) doList(t *schema.List) (errs ValidationErrors) {
if !w.value.IsList() {
return nil
}
l := w.value.AsListUsing(w.allocator)
defer w.allocator.Free(l)
// If list is null, empty, or atomic just return
if l == nil || l.Length() == 0 || t.ElementRelationship == schema.Atomic {
// If list is null or empty just return
if l == nil || l.Length() == 0 {
return nil
}
// atomic lists should return everything in the case of extract
// and nothing in the case of remove (!w.shouldExtract)
if t.ElementRelationship == schema.Atomic {
if w.shouldExtract {
w.out = w.value.Unstructured()
}
return nil
}
......@@ -59,11 +78,22 @@ func (w *removingWalker) doList(t *schema.List) (errs ValidationErrors) {
// Ignore error because we have already validated this list
pe, _ := listItemToPathElement(w.allocator, w.schema, t, i, item)
path, _ := fieldpath.MakePath(pe)
// save items on the path when we shouldExtract
// but ignore them when we are removing (i.e. !w.shouldExtract)
if w.toRemove.Has(path) {
continue
if w.shouldExtract {
newItems = append(newItems, removeItemsWithSchema(item, w.toRemove, w.schema, t.ElementType, w.shouldExtract).Unstructured())
} else {
continue
}
}
if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
item = removeItemsWithSchema(item, subset, w.schema, t.ElementType)
item = removeItemsWithSchema(item, subset, w.schema, t.ElementType, w.shouldExtract)
} else {
// don't save items not on the path when we shouldExtract.
if w.shouldExtract {
continue
}
}
newItems = append(newItems, item.Unstructured())
}
......@@ -74,12 +104,24 @@ func (w *removingWalker) doList(t *schema.List) (errs ValidationErrors) {
}
func (w *removingWalker) doMap(t *schema.Map) ValidationErrors {
if !w.value.IsMap() {
return nil
}
m := w.value.AsMapUsing(w.allocator)
if m != nil {
defer w.allocator.Free(m)
}
// If map is null, empty, or atomic just return
if m == nil || m.Empty() || t.ElementRelationship == schema.Atomic {
// If map is null or empty just return
if m == nil || m.Empty() {
return nil
}
// atomic maps should return everything in the case of extract
// and nothing in the case of remove (!w.shouldExtract)
if t.ElementRelationship == schema.Atomic {
if w.shouldExtract {
w.out = w.value.Unstructured()
}
return nil
}
......@@ -96,11 +138,22 @@ func (w *removingWalker) doMap(t *schema.Map) ValidationErrors {
if ft, ok := fieldTypes[k]; ok {
fieldType = ft
}
// save values on the path when we shouldExtract
// but ignore them when we are removing (i.e. !w.shouldExtract)
if w.toRemove.Has(path) {
if w.shouldExtract {
newMap[k] = removeItemsWithSchema(val, w.toRemove, w.schema, fieldType, w.shouldExtract).Unstructured()
}
return true
}
if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
val = removeItemsWithSchema(val, subset, w.schema, fieldType)
val = removeItemsWithSchema(val, subset, w.schema, fieldType, w.shouldExtract)
} else {
// don't save values not on the path when we shouldExtract.
if w.shouldExtract {
return true
}
}
newMap[k] = val.Unstructured()
return true
......
......@@ -150,7 +150,13 @@ func (tv TypedValue) Compare(rhs *TypedValue) (c *Comparison, err error) {
// RemoveItems removes each provided list or map item from the value.
func (tv TypedValue) RemoveItems(items *fieldpath.Set) *TypedValue {
tv.value = removeItemsWithSchema(tv.value, items, tv.schema, tv.typeRef)
tv.value = removeItemsWithSchema(tv.value, items, tv.schema, tv.typeRef, false)
return &tv
}
// ExtractItems returns a value with only the provided list or map items extracted from the value.
func (tv TypedValue) ExtractItems(items *fieldpath.Set) *TypedValue {
tv.value = removeItemsWithSchema(tv.value, items, tv.schema, tv.typeRef, true)
return &tv
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment