diff --git a/pkg/controller/service/service_controller.go b/pkg/controller/service/service_controller.go index 08f02d8b559..e9c51c557d0 100644 --- a/pkg/controller/service/service_controller.go +++ b/pkg/controller/service/service_controller.go @@ -58,23 +58,38 @@ const ( minRetryDelay = 5 * time.Second maxRetryDelay = 300 * time.Second - // LabelNodeRoleMaster specifies that a node is a master - // It's copied over to kubeadm until it's merged in core: https://github.com/kubernetes/kubernetes/pull/39112 - LabelNodeRoleMaster = "node-role.kubernetes.io/master" + // labelNodeRoleMaster specifies that a node is a master. The use of this label within the + // controller is deprecated and only considered when the LegacyNodeRoleBehavior feature gate + // is on. + labelNodeRoleMaster = "node-role.kubernetes.io/master" - // LabelNodeRoleExcludeBalancer specifies that the node should be - // exclude from load balancers created by a cloud provider. - LabelNodeRoleExcludeBalancer = "alpha.service-controller.kubernetes.io/exclude-balancer" + // labelNodeRoleExcludeBalancer specifies that the node should not be considered as a target + // for external load-balancers which use nodes as a second hop (e.g. many cloud LBs which only + // understand nodes). For services that use externalTrafficPolicy=Local, this may mean that + // any backends on excluded nodes are not reachable by those external load-balancers. + // Implementations of this exclusion may vary based on provider. This label is honored starting + // in 1.16 when the ServiceNodeExclusion gate is on. + labelNodeRoleExcludeBalancer = "node.kubernetes.io/exclude-from-external-load-balancers" + + // labelAlphaNodeRoleExcludeBalancer specifies that the node should be + // exclude from load balancers created by a cloud provider. This label is deprecated and will + // be removed in 1.17. + labelAlphaNodeRoleExcludeBalancer = "alpha.service-controller.kubernetes.io/exclude-balancer" // serviceNodeExclusionFeature is the feature gate name that // enables nodes to exclude themselves from service load balancers // originated from: https://github.com/kubernetes/kubernetes/blob/28e800245e/pkg/features/kube_features.go#L178 serviceNodeExclusionFeature = "ServiceNodeExclusion" - // ServiceLoadBalancerFinalizerFeature is the feature gate name that + // serviceLoadBalancerFinalizerFeature is the feature gate name that // enables Finalizer Protection for Service LoadBalancers. // orginated from: https://github.com/kubernetes/kubernetes/blob/28e800245e/pkg/features/kube_features.go#L433 serviceLoadBalancerFinalizerFeature = "ServiceLoadBalancerFinalizer" + + // legacyNodeRoleBehaviro is the feature gate name that enables legacy + // behavior to vary cluster functionality on the node-role.kubernetes.io + // labels. + legacyNodeRoleBehaviorFeature = "LegacyNodeRoleBehavior" ) type cachedService struct { @@ -625,14 +640,19 @@ func getNodeConditionPredicate() corelisters.NodeConditionPredicate { return false } - // As of 1.6, we will taint the master, but not necessarily mark it unschedulable. - // Recognize nodes labeled as master, and filter them also, as we were doing previously. - if _, hasMasterRoleLabel := node.Labels[LabelNodeRoleMaster]; hasMasterRoleLabel { - return false + if utilfeature.DefaultFeatureGate.Enabled(legacyNodeRoleBehaviorFeature) { + // As of 1.6, we will taint the master, but not necessarily mark it unschedulable. + // Recognize nodes labeled as master, and filter them also, as we were doing previously. + if _, hasMasterRoleLabel := node.Labels[labelNodeRoleMaster]; hasMasterRoleLabel { + return false + } } - if utilfeature.DefaultFeatureGate.Enabled(serviceNodeExclusionFeature) { - if _, hasExcludeBalancerLabel := node.Labels[LabelNodeRoleExcludeBalancer]; hasExcludeBalancerLabel { + // Will be removed in 1.17 + if _, hasExcludeBalancerLabel := node.Labels[labelAlphaNodeRoleExcludeBalancer]; hasExcludeBalancerLabel { + return false + } + if _, hasExcludeBalancerLabel := node.Labels[labelNodeRoleExcludeBalancer]; hasExcludeBalancerLabel { return false } } diff --git a/pkg/controller/service/service_controller_test.go b/pkg/controller/service/service_controller_test.go index 1c605cffa85..93d8c482889 100644 --- a/pkg/controller/service/service_controller_test.go +++ b/pkg/controller/service/service_controller_test.go @@ -1396,3 +1396,37 @@ func TestPatchStatus(t *testing.T) { }) } } + +func Test_getNodeConditionPredicate(t *testing.T) { + validNodeStatus := v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: "Test"}}} + tests := []struct { + name string + + enableExclusion bool + enableLegacy bool + input *v1.Node + want bool + }{ + {want: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}}}, + {want: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeRoleMaster: ""}}}}, + {want: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeRoleExcludeBalancer: ""}}}}, + {want: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelAlphaNodeRoleExcludeBalancer: ""}}}}, + + {want: true, enableExclusion: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeRoleMaster: ""}}}}, + {want: true, enableLegacy: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeRoleExcludeBalancer: ""}}}}, + + {want: false, enableLegacy: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeRoleMaster: ""}}}}, + {want: false, enableExclusion: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelAlphaNodeRoleExcludeBalancer: ""}}}}, + {want: false, enableExclusion: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeRoleExcludeBalancer: ""}}}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, serviceNodeExclusionFeature, tt.enableExclusion)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, legacyNodeRoleBehaviorFeature, tt.enableLegacy)() + + if result := getNodeConditionPredicate()(tt.input); result != tt.want { + t.Errorf("getNodeConditionPredicate() = %v, want %v", result, tt.want) + } + }) + } +}