diff --git a/pkg/controller/nodelifecycle/node_lifecycle_controller.go b/pkg/controller/nodelifecycle/node_lifecycle_controller.go index beab0128e86..2b2c2bf02cf 100644 --- a/pkg/controller/nodelifecycle/node_lifecycle_controller.go +++ b/pkg/controller/nodelifecycle/node_lifecycle_controller.go @@ -51,11 +51,11 @@ import ( "k8s.io/client-go/util/flowcontrol" "k8s.io/client-go/util/workqueue" "k8s.io/component-base/metrics/prometheus/ratelimiter" + utilnode "k8s.io/component-helpers/node/topology" kubeletapis "k8s.io/kubelet/pkg/apis" "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller/nodelifecycle/scheduler" nodeutil "k8s.io/kubernetes/pkg/controller/util/node" - utilnode "k8s.io/kubernetes/pkg/util/node" taintutils "k8s.io/kubernetes/pkg/util/taints" ) diff --git a/pkg/controller/testutil/test_utils.go b/pkg/controller/testutil/test_utils.go index 4003febd251..716720bbd35 100644 --- a/pkg/controller/testutil/test_utils.go +++ b/pkg/controller/testutil/test_utils.go @@ -40,10 +40,10 @@ import ( v1core "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/tools/cache" ref "k8s.io/client-go/tools/reference" + utilnode "k8s.io/component-helpers/node/topology" "k8s.io/klog/v2" "k8s.io/kubernetes/pkg/api/legacyscheme" api "k8s.io/kubernetes/pkg/apis/core" - utilnode "k8s.io/kubernetes/pkg/util/node" jsonpatch "github.com/evanphx/json-patch" ) diff --git a/pkg/scheduler/framework/plugins/selectorspread/selector_spread.go b/pkg/scheduler/framework/plugins/selectorspread/selector_spread.go index 0e36e58cfce..402c1ce69fe 100644 --- a/pkg/scheduler/framework/plugins/selectorspread/selector_spread.go +++ b/pkg/scheduler/framework/plugins/selectorspread/selector_spread.go @@ -25,9 +25,9 @@ import ( "k8s.io/apimachinery/pkg/runtime" appslisters "k8s.io/client-go/listers/apps/v1" corelisters "k8s.io/client-go/listers/core/v1" + utilnode "k8s.io/component-helpers/node/topology" "k8s.io/kubernetes/pkg/scheduler/framework" "k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper" - utilnode "k8s.io/kubernetes/pkg/util/node" ) // SelectorSpread is a plugin that calculates selector spread priority. diff --git a/pkg/scheduler/internal/cache/node_tree.go b/pkg/scheduler/internal/cache/node_tree.go index d226cfd7f3f..31bd4496519 100644 --- a/pkg/scheduler/internal/cache/node_tree.go +++ b/pkg/scheduler/internal/cache/node_tree.go @@ -21,8 +21,8 @@ import ( "fmt" v1 "k8s.io/api/core/v1" + utilnode "k8s.io/component-helpers/node/topology" "k8s.io/klog/v2" - utilnode "k8s.io/kubernetes/pkg/util/node" ) // nodeTree is a tree-like data structure that holds node names in each zone. Zone names are diff --git a/pkg/util/node/node.go b/pkg/util/node/node.go index 15cabbb4fb0..1f0e2f0f56c 100644 --- a/pkg/util/node/node.go +++ b/pkg/util/node/node.go @@ -174,42 +174,6 @@ func GetNodeIP(client clientset.Interface, name string) net.IP { return nodeIP } -// GetZoneKey is a helper function that builds a string identifier that is unique per failure-zone; -// it returns empty-string for no zone. -// Since there are currently two separate zone keys: -// * "failure-domain.beta.kubernetes.io/zone" -// * "topology.kubernetes.io/zone" -// GetZoneKey will first check failure-domain.beta.kubernetes.io/zone and if not exists, will then check -// topology.kubernetes.io/zone -func GetZoneKey(node *v1.Node) string { - labels := node.Labels - if labels == nil { - return "" - } - - // TODO: "failure-domain.beta..." names are deprecated, but will - // stick around a long time due to existing on old extant objects like PVs. - // Maybe one day we can stop considering them (see #88493). - zone, ok := labels[v1.LabelFailureDomainBetaZone] - if !ok { - zone, _ = labels[v1.LabelTopologyZone] - } - - region, ok := labels[v1.LabelFailureDomainBetaRegion] - if !ok { - region, _ = labels[v1.LabelTopologyRegion] - } - - if region == "" && zone == "" { - return "" - } - - // We include the null character just in case region or failureDomain has a colon - // (We do assume there's no null characters in a region or failureDomain) - // As a nice side-benefit, the null character is not printed by fmt.Print or glog - return region + ":\x00:" + zone -} - type nodeForConditionPatch struct { Status nodeStatusForPatch `json:"status"` } diff --git a/pkg/util/node/node_test.go b/pkg/util/node/node_test.go index 0222581b978..21be30520d8 100644 --- a/pkg/util/node/node_test.go +++ b/pkg/util/node/node_test.go @@ -266,84 +266,3 @@ func TestGetHostname(t *testing.T) { } } - -func Test_GetZoneKey(t *testing.T) { - tests := []struct { - name string - node *v1.Node - zone string - }{ - { - name: "has no zone or region keys", - node: &v1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{}, - }, - }, - zone: "", - }, - { - name: "has beta zone and region keys", - node: &v1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - v1.LabelFailureDomainBetaZone: "zone1", - v1.LabelFailureDomainBetaRegion: "region1", - }, - }, - }, - zone: "region1:\x00:zone1", - }, - { - name: "has GA zone and region keys", - node: &v1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - v1.LabelTopologyZone: "zone1", - v1.LabelTopologyRegion: "region1", - }, - }, - }, - zone: "region1:\x00:zone1", - }, - { - name: "has both beta and GA zone and region keys", - node: &v1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - v1.LabelTopologyZone: "zone1", - v1.LabelTopologyRegion: "region1", - v1.LabelFailureDomainBetaZone: "zone1", - v1.LabelFailureDomainBetaRegion: "region1", - }, - }, - }, - zone: "region1:\x00:zone1", - }, - { - name: "has both beta and GA zone and region keys, beta labels take precedent", - node: &v1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - v1.LabelTopologyZone: "zone1", - v1.LabelTopologyRegion: "region1", - v1.LabelFailureDomainBetaZone: "zone2", - v1.LabelFailureDomainBetaRegion: "region2", - }, - }, - }, - zone: "region2:\x00:zone2", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - zone := GetZoneKey(test.node) - if zone != test.zone { - t.Logf("actual zone key: %q", zone) - t.Logf("expected zone key: %q", test.zone) - t.Errorf("unexpected zone key") - } - }) - } -} diff --git a/staging/src/k8s.io/component-helpers/node/OWNERS b/staging/src/k8s.io/component-helpers/node/OWNERS new file mode 100644 index 00000000000..b0f99a4c5e5 --- /dev/null +++ b/staging/src/k8s.io/component-helpers/node/OWNERS @@ -0,0 +1,8 @@ +# See the OWNERS docs at https://go.k8s.io/owners + +approvers: +- sig-node-approvers +reviewers: +- sig-node-reviewers +labels: +- sig/node diff --git a/staging/src/k8s.io/component-helpers/node/topology/helpers.go b/staging/src/k8s.io/component-helpers/node/topology/helpers.go new file mode 100644 index 00000000000..18c838cca56 --- /dev/null +++ b/staging/src/k8s.io/component-helpers/node/topology/helpers.go @@ -0,0 +1,57 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package topology + +import ( + "k8s.io/api/core/v1" +) + +// GetZoneKey is a helper function that builds a string identifier that is unique per failure-zone; +// it returns empty-string for no zone. +// Since there are currently two separate zone keys: +// * "failure-domain.beta.kubernetes.io/zone" +// * "topology.kubernetes.io/zone" +// GetZoneKey will first check failure-domain.beta.kubernetes.io/zone and if not exists, will then check +// topology.kubernetes.io/zone +func GetZoneKey(node *v1.Node) string { + labels := node.Labels + if labels == nil { + return "" + } + + // TODO: "failure-domain.beta..." names are deprecated, but will + // stick around a long time due to existing on old extant objects like PVs. + // Maybe one day we can stop considering them (see #88493). + zone, ok := labels[v1.LabelFailureDomainBetaZone] + if !ok { + zone, _ = labels[v1.LabelTopologyZone] + } + + region, ok := labels[v1.LabelFailureDomainBetaRegion] + if !ok { + region, _ = labels[v1.LabelTopologyRegion] + } + + if region == "" && zone == "" { + return "" + } + + // We include the null character just in case region or failureDomain has a colon + // (We do assume there's no null characters in a region or failureDomain) + // As a nice side-benefit, the null character is not printed by fmt.Print or glog + return region + ":\x00:" + zone +} diff --git a/staging/src/k8s.io/component-helpers/node/topology/helpers_test.go b/staging/src/k8s.io/component-helpers/node/topology/helpers_test.go new file mode 100644 index 00000000000..6d3c3873fa6 --- /dev/null +++ b/staging/src/k8s.io/component-helpers/node/topology/helpers_test.go @@ -0,0 +1,104 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package topology + +import ( + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "testing" +) + +func Test_GetZoneKey(t *testing.T) { + tests := []struct { + name string + node *v1.Node + zone string + }{ + { + name: "has no zone or region keys", + node: &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{}, + }, + }, + zone: "", + }, + { + name: "has beta zone and region keys", + node: &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + v1.LabelFailureDomainBetaZone: "zone1", + v1.LabelFailureDomainBetaRegion: "region1", + }, + }, + }, + zone: "region1:\x00:zone1", + }, + { + name: "has GA zone and region keys", + node: &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + v1.LabelTopologyZone: "zone1", + v1.LabelTopologyRegion: "region1", + }, + }, + }, + zone: "region1:\x00:zone1", + }, + { + name: "has both beta and GA zone and region keys", + node: &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + v1.LabelTopologyZone: "zone1", + v1.LabelTopologyRegion: "region1", + v1.LabelFailureDomainBetaZone: "zone1", + v1.LabelFailureDomainBetaRegion: "region1", + }, + }, + }, + zone: "region1:\x00:zone1", + }, + { + name: "has both beta and GA zone and region keys, beta labels take precedent", + node: &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + v1.LabelTopologyZone: "zone1", + v1.LabelTopologyRegion: "region1", + v1.LabelFailureDomainBetaZone: "zone2", + v1.LabelFailureDomainBetaRegion: "region2", + }, + }, + }, + zone: "region2:\x00:zone2", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + zone := GetZoneKey(test.node) + if zone != test.zone { + t.Logf("actual zone key: %q", zone) + t.Logf("expected zone key: %q", test.zone) + t.Errorf("unexpected zone key") + } + }) + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 69acd338682..4e7de081435 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2240,6 +2240,7 @@ k8s.io/component-base/version/verflag k8s.io/component-helpers/apimachinery/lease k8s.io/component-helpers/auth/rbac/reconciliation k8s.io/component-helpers/auth/rbac/validation +k8s.io/component-helpers/node/topology k8s.io/component-helpers/scheduling/corev1 k8s.io/component-helpers/scheduling/corev1/nodeaffinity k8s.io/component-helpers/storage/volume