diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index 00a245b9b3e..bdc50194b70 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -576,13 +576,12 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, stopCh <-chan // make a separate client for heartbeat with throttling disabled and a timeout attached heartbeatClientConfig := *clientConfig heartbeatClientConfig.Timeout = s.KubeletConfiguration.NodeStatusUpdateFrequency.Duration - // if the NodeLease feature is enabled, the timeout is the minimum of the lease duration and status update frequency - if utilfeature.DefaultFeatureGate.Enabled(features.NodeLease) { - leaseTimeout := time.Duration(s.KubeletConfiguration.NodeLeaseDurationSeconds) * time.Second - if heartbeatClientConfig.Timeout > leaseTimeout { - heartbeatClientConfig.Timeout = leaseTimeout - } + // The timeout is the minimum of the lease duration and status update frequency + leaseTimeout := time.Duration(s.KubeletConfiguration.NodeLeaseDurationSeconds) * time.Second + if heartbeatClientConfig.Timeout > leaseTimeout { + heartbeatClientConfig.Timeout = leaseTimeout } + heartbeatClientConfig.QPS = float32(-1) kubeDeps.HeartbeatClient, err = clientset.NewForConfig(&heartbeatClientConfig) if err != nil { diff --git a/cmd/kubemark/BUILD b/cmd/kubemark/BUILD index 1366a5866be..82426b25573 100644 --- a/cmd/kubemark/BUILD +++ b/cmd/kubemark/BUILD @@ -17,7 +17,6 @@ go_library( importpath = "k8s.io/kubernetes/cmd/kubemark", deps = [ "//pkg/api/legacyscheme:go_default_library", - "//pkg/features:go_default_library", "//pkg/kubelet/cadvisor/testing:go_default_library", "//pkg/kubelet/cm:go_default_library", "//pkg/kubelet/dockershim:go_default_library", @@ -28,7 +27,6 @@ go_library( "//pkg/util/sysctl/testing:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library", "//staging/src/k8s.io/client-go/rest:go_default_library", "//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library", diff --git a/cmd/kubemark/hollow-node.go b/cmd/kubemark/hollow-node.go index 9b0568e8637..567a930839a 100644 --- a/cmd/kubemark/hollow-node.go +++ b/cmd/kubemark/hollow-node.go @@ -30,7 +30,6 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/sets" - utilfeature "k8s.io/apiserver/pkg/util/feature" clientset "k8s.io/client-go/kubernetes" restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -42,7 +41,6 @@ import ( "k8s.io/component-base/version" "k8s.io/component-base/version/verflag" "k8s.io/kubernetes/pkg/api/legacyscheme" - "k8s.io/kubernetes/pkg/features" cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing" "k8s.io/kubernetes/pkg/kubelet/cm" "k8s.io/kubernetes/pkg/kubelet/dockershim" @@ -180,13 +178,12 @@ func run(config *hollowNodeConfig) { heartbeatClientConfig := *clientConfig heartbeatClientConfig.Timeout = c.NodeStatusUpdateFrequency.Duration - // if the NodeLease feature is enabled, the timeout is the minimum of the lease duration and status update frequency - if utilfeature.DefaultFeatureGate.Enabled(features.NodeLease) { - leaseTimeout := time.Duration(c.NodeLeaseDurationSeconds) * time.Second - if heartbeatClientConfig.Timeout > leaseTimeout { - heartbeatClientConfig.Timeout = leaseTimeout - } + // The timeout is the minimum of the lease duration and status update frequency + leaseTimeout := time.Duration(c.NodeLeaseDurationSeconds) * time.Second + if heartbeatClientConfig.Timeout > leaseTimeout { + heartbeatClientConfig.Timeout = leaseTimeout } + heartbeatClientConfig.QPS = float32(-1) heartbeatClient, err := clientset.NewForConfig(&heartbeatClientConfig) if err != nil { diff --git a/pkg/controller/nodelifecycle/node_lifecycle_controller.go b/pkg/controller/nodelifecycle/node_lifecycle_controller.go index 151cd724b83..3a8256657b6 100644 --- a/pkg/controller/nodelifecycle/node_lifecycle_controller.go +++ b/pkg/controller/nodelifecycle/node_lifecycle_controller.go @@ -55,7 +55,6 @@ import ( "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller/nodelifecycle/scheduler" nodeutil "k8s.io/kubernetes/pkg/controller/util/node" - "k8s.io/kubernetes/pkg/features" kubefeatures "k8s.io/kubernetes/pkg/features" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" utilnode "k8s.io/kubernetes/pkg/util/node" @@ -537,12 +536,7 @@ func NewNodeLifecycleController( }) nc.leaseLister = leaseInformer.Lister() - if utilfeature.DefaultFeatureGate.Enabled(features.NodeLease) { - nc.leaseInformerSynced = leaseInformer.Informer().HasSynced - } else { - // Always indicate that lease is synced to prevent syncing lease. - nc.leaseInformerSynced = func() bool { return true } - } + nc.leaseInformerSynced = leaseInformer.Informer().HasSynced nc.nodeLister = nodeInformer.Lister() nc.nodeInformerSynced = nodeInformer.Informer().HasSynced @@ -1103,17 +1097,14 @@ func (nc *Controller) tryUpdateNodeHealth(node *v1.Node) (time.Duration, v1.Node readyTransitionTimestamp: transitionTime, } } - var observedLease *coordv1.Lease - if utilfeature.DefaultFeatureGate.Enabled(features.NodeLease) { - // Always update the probe time if node lease is renewed. - // Note: If kubelet never posted the node status, but continues renewing the - // heartbeat leases, the node controller will assume the node is healthy and - // take no action. - observedLease, _ = nc.leaseLister.Leases(v1.NamespaceNodeLease).Get(node.Name) - if observedLease != nil && (savedLease == nil || savedLease.Spec.RenewTime.Before(observedLease.Spec.RenewTime)) { - nodeHealth.lease = observedLease - nodeHealth.probeTimestamp = nc.now() - } + // Always update the probe time if node lease is renewed. + // Note: If kubelet never posted the node status, but continues renewing the + // heartbeat leases, the node controller will assume the node is healthy and + // take no action. + observedLease, _ := nc.leaseLister.Leases(v1.NamespaceNodeLease).Get(node.Name) + if observedLease != nil && (savedLease == nil || savedLease.Spec.RenewTime.Before(observedLease.Spec.RenewTime)) { + nodeHealth.lease = observedLease + nodeHealth.probeTimestamp = nc.now() } if nc.now().After(nodeHealth.probeTimestamp.Add(gracePeriod)) { diff --git a/pkg/controller/nodelifecycle/node_lifecycle_controller_test.go b/pkg/controller/nodelifecycle/node_lifecycle_controller_test.go index ea0f75eaec2..a194b6cb6d7 100644 --- a/pkg/controller/nodelifecycle/node_lifecycle_controller_test.go +++ b/pkg/controller/nodelifecycle/node_lifecycle_controller_test.go @@ -1747,8 +1747,6 @@ func TestMonitorNodeHealthUpdateStatus(t *testing.T) { } func TestMonitorNodeHealthUpdateNodeAndPodStatusWithLease(t *testing.T) { - defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeLease, true)() - nodeCreationTime := metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC) fakeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC) testcases := []struct { diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 976595cf7f5..09dfe1efc37 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -335,6 +335,7 @@ const ( // owner: @mtaufen // alpha: v1.12 // beta: v1.14 + // GA: v1.17 // // Kubelet uses the new Lease API to report node heartbeats, // (Kube) Node Lifecycle Controller uses these heartbeats as a node health signal. @@ -555,7 +556,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS CSIBlockVolume: {Default: true, PreRelease: featuregate.Beta}, CSIInlineVolume: {Default: true, PreRelease: featuregate.Beta}, RuntimeClass: {Default: true, PreRelease: featuregate.Beta}, - NodeLease: {Default: true, PreRelease: featuregate.Beta}, + NodeLease: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, SCTPSupport: {Default: false, PreRelease: featuregate.Alpha}, VolumeSnapshotDataSource: {Default: false, PreRelease: featuregate.Alpha}, ProcMountType: {Default: false, PreRelease: featuregate.Alpha}, diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index 40486e829a8..eda637b338e 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -873,13 +873,10 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration, klet.appArmorValidator = apparmor.NewValidator(containerRuntime) klet.softAdmitHandlers.AddPodAdmitHandler(lifecycle.NewAppArmorAdmitHandler(klet.appArmorValidator)) klet.softAdmitHandlers.AddPodAdmitHandler(lifecycle.NewNoNewPrivsAdmitHandler(klet.containerRuntime)) - - if utilfeature.DefaultFeatureGate.Enabled(features.NodeLease) { - klet.nodeLeaseController = nodelease.NewController(klet.clock, klet.heartbeatClient, string(klet.nodeName), kubeCfg.NodeLeaseDurationSeconds, klet.onRepeatedHeartbeatFailure) - } - klet.softAdmitHandlers.AddPodAdmitHandler(lifecycle.NewProcMountAdmitHandler(klet.containerRuntime)) + klet.nodeLeaseController = nodelease.NewController(klet.clock, klet.heartbeatClient, string(klet.nodeName), kubeCfg.NodeLeaseDurationSeconds, klet.onRepeatedHeartbeatFailure) + // Finally, put the most recent version of the config on the Kubelet, so // people can see how it was configured. klet.kubeletConfiguration = *kubeCfg @@ -1420,9 +1417,7 @@ func (kl *Kubelet) Run(updates <-chan kubetypes.PodUpdate) { go kl.fastStatusUpdateOnce() // start syncing lease - if utilfeature.DefaultFeatureGate.Enabled(features.NodeLease) { - go kl.nodeLeaseController.Run(wait.NeverStop) - } + go kl.nodeLeaseController.Run(wait.NeverStop) } go wait.Until(kl.updateRuntimeUp, 5*time.Second, wait.NeverStop) diff --git a/pkg/kubelet/kubelet_node_status.go b/pkg/kubelet/kubelet_node_status.go index bdec539b837..6bd44ebd49a 100644 --- a/pkg/kubelet/kubelet_node_status.go +++ b/pkg/kubelet/kubelet_node_status.go @@ -434,7 +434,7 @@ func (kl *Kubelet) tryUpdateNodeStatus(tryNumber int) error { kl.setNodeStatus(node) now := kl.clock.Now() - if utilfeature.DefaultFeatureGate.Enabled(features.NodeLease) && now.Before(kl.lastStatusReportTime.Add(kl.nodeStatusReportFrequency)) { + if now.Before(kl.lastStatusReportTime.Add(kl.nodeStatusReportFrequency)) { if !podCIDRChanged && !nodeStatusHasChanged(&originalNode.Status, &node.Status) { // We must mark the volumes as ReportedInUse in volume manager's dsw even // if no changes were made to the node status (no volumes were added or removed diff --git a/pkg/kubelet/kubelet_node_status_test.go b/pkg/kubelet/kubelet_node_status_test.go index 4c66b2c00b9..a44f7e334f1 100644 --- a/pkg/kubelet/kubelet_node_status_test.go +++ b/pkg/kubelet/kubelet_node_status_test.go @@ -43,14 +43,11 @@ import ( "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/util/wait" - utilfeature "k8s.io/apiserver/pkg/util/feature" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/rest" core "k8s.io/client-go/testing" - featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/component-base/version" - "k8s.io/kubernetes/pkg/features" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing" "k8s.io/kubernetes/pkg/kubelet/cm" @@ -786,8 +783,6 @@ func TestUpdateNodeStatusError(t *testing.T) { } func TestUpdateNodeStatusWithLease(t *testing.T) { - defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeLease, true)() - testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) defer testKubelet.Cleanup() clock := testKubelet.fakeClock @@ -1020,116 +1015,7 @@ func TestUpdateNodeStatusWithLease(t *testing.T) { assert.IsType(t, core.GetActionImpl{}, actions[9]) } -func TestUpdateNodeStatusAndVolumesInUseWithoutNodeLease(t *testing.T) { - defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeLease, false)() - - cases := []struct { - desc string - existingVolumes []v1.UniqueVolumeName // volumes to initially populate volumeManager - existingNode *v1.Node // existing node object - expectedNode *v1.Node // new node object after patch - expectedReportedInUse []v1.UniqueVolumeName // expected volumes reported in use in volumeManager - }{ - { - desc: "no volumes and no update", - existingNode: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname}}, - expectedNode: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname}}, - }, - { - desc: "volumes inuse on node and volumeManager", - existingVolumes: []v1.UniqueVolumeName{"vol1"}, - existingNode: &v1.Node{ - ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname}, - Status: v1.NodeStatus{ - VolumesInUse: []v1.UniqueVolumeName{"vol1"}, - }, - }, - expectedNode: &v1.Node{ - ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname}, - Status: v1.NodeStatus{ - VolumesInUse: []v1.UniqueVolumeName{"vol1"}, - }, - }, - expectedReportedInUse: []v1.UniqueVolumeName{"vol1"}, - }, - { - desc: "volumes inuse on node but not in volumeManager", - existingNode: &v1.Node{ - ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname}, - Status: v1.NodeStatus{ - VolumesInUse: []v1.UniqueVolumeName{"vol1"}, - }, - }, - expectedNode: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname}}, - }, - { - desc: "volumes inuse in volumeManager but not on node", - existingVolumes: []v1.UniqueVolumeName{"vol1"}, - existingNode: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname}}, - expectedNode: &v1.Node{ - ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname}, - Status: v1.NodeStatus{ - VolumesInUse: []v1.UniqueVolumeName{"vol1"}, - }, - }, - expectedReportedInUse: []v1.UniqueVolumeName{"vol1"}, - }, - } - - for _, tc := range cases { - t.Run(tc.desc, func(t *testing.T) { - // Setup - testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) - defer testKubelet.Cleanup() - - kubelet := testKubelet.kubelet - kubelet.kubeClient = nil // ensure only the heartbeat client is used - kubelet.containerManager = &localCM{ContainerManager: cm.NewStubContainerManager()} - kubelet.lastStatusReportTime = kubelet.clock.Now() - kubelet.nodeStatusReportFrequency = time.Hour - kubelet.machineInfo = &cadvisorapi.MachineInfo{} - - // override test volumeManager - fakeVolumeManager := kubeletvolume.NewFakeVolumeManager(tc.existingVolumes) - kubelet.volumeManager = fakeVolumeManager - - // Only test VolumesInUse setter - kubelet.setNodeStatusFuncs = []func(*v1.Node) error{ - nodestatus.VolumesInUse(kubelet.volumeManager.ReconcilerStatesHasBeenSynced, - kubelet.volumeManager.GetVolumesInUse), - } - - kubeClient := testKubelet.fakeKubeClient - kubeClient.ReactionChain = fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{*tc.existingNode}}).ReactionChain - - // Execute - assert.NoError(t, kubelet.updateNodeStatus()) - - // Validate - actions := kubeClient.Actions() - if tc.expectedNode != nil { - assert.Len(t, actions, 2) - assert.IsType(t, core.GetActionImpl{}, actions[0]) - assert.IsType(t, core.PatchActionImpl{}, actions[1]) - patchAction := actions[1].(core.PatchActionImpl) - - updatedNode, err := applyNodeStatusPatch(tc.existingNode, patchAction.GetPatch()) - require.NoError(t, err) - assert.True(t, apiequality.Semantic.DeepEqual(tc.expectedNode, updatedNode), "%s", diff.ObjectDiff(tc.expectedNode, updatedNode)) - } else { - assert.Len(t, actions, 1) - assert.IsType(t, core.GetActionImpl{}, actions[0]) - } - - reportedInUse := fakeVolumeManager.GetVolumesReportedInUse() - assert.True(t, apiequality.Semantic.DeepEqual(tc.expectedReportedInUse, reportedInUse), "%s", diff.ObjectDiff(tc.expectedReportedInUse, reportedInUse)) - }) - } -} - func TestUpdateNodeStatusAndVolumesInUseWithNodeLease(t *testing.T) { - defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeLease, true)() - cases := []struct { desc string existingVolumes []v1.UniqueVolumeName // volumes to initially populate volumeManager diff --git a/pkg/master/controller.go b/pkg/master/controller.go index 4d9f35d5cd0..c3adb1dac32 100644 --- a/pkg/master/controller.go +++ b/pkg/master/controller.go @@ -30,11 +30,9 @@ import ( "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" genericapiserver "k8s.io/apiserver/pkg/server" - utilfeature "k8s.io/apiserver/pkg/util/feature" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/rest" "k8s.io/klog" - "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/master/reconcilers" "k8s.io/kubernetes/pkg/registry/core/rangeallocation" corerest "k8s.io/kubernetes/pkg/registry/core/rest" @@ -92,10 +90,7 @@ func (c *completedConfig) NewBootstrapController(legacyRESTStorage corerest.Lega klog.Fatalf("failed to get listener address: %v", err) } - systemNamespaces := []string{metav1.NamespaceSystem, metav1.NamespacePublic} - if utilfeature.DefaultFeatureGate.Enabled(features.NodeLease) { - systemNamespaces = append(systemNamespaces, corev1.NamespaceNodeLease) - } + systemNamespaces := []string{metav1.NamespaceSystem, metav1.NamespacePublic, corev1.NamespaceNodeLease} return &Controller{ ServiceClient: serviceClient, diff --git a/plugin/pkg/admission/noderestriction/admission.go b/plugin/pkg/admission/noderestriction/admission.go index c38b644ae9b..6c896f60fb0 100644 --- a/plugin/pkg/admission/noderestriction/admission.go +++ b/plugin/pkg/admission/noderestriction/admission.go @@ -151,10 +151,7 @@ func (p *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission. return nil case leaseResource: - if p.features.Enabled(features.NodeLease) { - return p.admitLease(nodeName, a) - } - return admission.NewForbidden(a, fmt.Errorf("disabled by feature gate %s", features.NodeLease)) + return p.admitLease(nodeName, a) case csiNodeResource: if p.features.Enabled(features.CSINodeInfo) { diff --git a/plugin/pkg/admission/noderestriction/admission_test.go b/plugin/pkg/admission/noderestriction/admission_test.go index 551dee24f6e..d93a4bf5b90 100644 --- a/plugin/pkg/admission/noderestriction/admission_test.go +++ b/plugin/pkg/admission/noderestriction/admission_test.go @@ -48,8 +48,6 @@ import ( var ( trEnabledFeature = featuregate.NewFeatureGate() trDisabledFeature = featuregate.NewFeatureGate() - leaseEnabledFeature = featuregate.NewFeatureGate() - leaseDisabledFeature = featuregate.NewFeatureGate() csiNodeInfoEnabledFeature = featuregate.NewFeatureGate() csiNodeInfoDisabledFeature = featuregate.NewFeatureGate() ) @@ -61,12 +59,6 @@ func init() { if err := trDisabledFeature.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.TokenRequest: {Default: false}}); err != nil { panic(err) } - if err := leaseEnabledFeature.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.NodeLease: {Default: true}}); err != nil { - panic(err) - } - if err := leaseDisabledFeature.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.NodeLease: {Default: false}}); err != nil { - panic(err) - } if err := csiNodeInfoEnabledFeature.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.CSINodeInfo: {Default: true}}); err != nil { panic(err) } @@ -1148,64 +1140,49 @@ func Test_nodePlugin_Admit(t *testing.T) { err: "", }, // Node leases - { - name: "disallowed create lease - feature disabled", - attributes: admission.NewAttributesRecord(lease, nil, leaseKind, lease.Namespace, lease.Name, leaseResource, "", admission.Create, &metav1.DeleteOptions{}, false, mynode), - features: leaseDisabledFeature, - err: "forbidden: disabled by feature gate NodeLease", - }, { name: "disallowed create lease in namespace other than kube-node-lease - feature enabled", attributes: admission.NewAttributesRecord(leaseWrongNS, nil, leaseKind, leaseWrongNS.Namespace, leaseWrongNS.Name, leaseResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), - features: leaseEnabledFeature, err: "forbidden: ", }, { name: "disallowed update lease in namespace other than kube-node-lease - feature enabled", attributes: admission.NewAttributesRecord(leaseWrongNS, leaseWrongNS, leaseKind, leaseWrongNS.Namespace, leaseWrongNS.Name, leaseResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), - features: leaseEnabledFeature, err: "forbidden: ", }, { name: "disallowed delete lease in namespace other than kube-node-lease - feature enabled", attributes: admission.NewAttributesRecord(nil, nil, leaseKind, leaseWrongNS.Namespace, leaseWrongNS.Name, leaseResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), - features: leaseEnabledFeature, err: "forbidden: ", }, { name: "disallowed create another node's lease - feature enabled", attributes: admission.NewAttributesRecord(leaseWrongName, nil, leaseKind, leaseWrongName.Namespace, leaseWrongName.Name, leaseResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), - features: leaseEnabledFeature, err: "forbidden: ", }, { name: "disallowed update another node's lease - feature enabled", attributes: admission.NewAttributesRecord(leaseWrongName, leaseWrongName, leaseKind, leaseWrongName.Namespace, leaseWrongName.Name, leaseResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), - features: leaseEnabledFeature, err: "forbidden: ", }, { name: "disallowed delete another node's lease - feature enabled", attributes: admission.NewAttributesRecord(nil, nil, leaseKind, leaseWrongName.Namespace, leaseWrongName.Name, leaseResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), - features: leaseEnabledFeature, err: "forbidden: ", }, { name: "allowed create node lease - feature enabled", attributes: admission.NewAttributesRecord(lease, nil, leaseKind, lease.Namespace, lease.Name, leaseResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), - features: leaseEnabledFeature, err: "", }, { name: "allowed update node lease - feature enabled", attributes: admission.NewAttributesRecord(lease, lease, leaseKind, lease.Namespace, lease.Name, leaseResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), - features: leaseEnabledFeature, err: "", }, { name: "allowed delete node lease - feature enabled", attributes: admission.NewAttributesRecord(nil, nil, leaseKind, lease.Namespace, lease.Name, leaseResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), - features: leaseEnabledFeature, err: "", }, // CSINode diff --git a/plugin/pkg/auth/authorizer/node/node_authorizer.go b/plugin/pkg/auth/authorizer/node/node_authorizer.go index d7c6e545528..55546d21ab6 100644 --- a/plugin/pkg/auth/authorizer/node/node_authorizer.go +++ b/plugin/pkg/auth/authorizer/node/node_authorizer.go @@ -116,10 +116,7 @@ func (r *NodeAuthorizer) Authorize(ctx context.Context, attrs authorizer.Attribu } return authorizer.DecisionNoOpinion, fmt.Sprintf("disabled by feature gate %s", features.TokenRequest), nil case leaseResource: - if r.features.Enabled(features.NodeLease) { - return r.authorizeLease(nodeName, attrs) - } - return authorizer.DecisionNoOpinion, fmt.Sprintf("disabled by feature gate %s", features.NodeLease), nil + return r.authorizeLease(nodeName, attrs) case csiNodeResource: if r.features.Enabled(features.CSINodeInfo) { return r.authorizeCSINode(nodeName, attrs) diff --git a/plugin/pkg/auth/authorizer/node/node_authorizer_test.go b/plugin/pkg/auth/authorizer/node/node_authorizer_test.go index f7b829dca5d..dd708f07468 100644 --- a/plugin/pkg/auth/authorizer/node/node_authorizer_test.go +++ b/plugin/pkg/auth/authorizer/node/node_authorizer_test.go @@ -43,8 +43,6 @@ import ( var ( trEnabledFeature = featuregate.NewFeatureGate() trDisabledFeature = featuregate.NewFeatureGate() - leaseEnabledFeature = featuregate.NewFeatureGate() - leaseDisabledFeature = featuregate.NewFeatureGate() csiNodeInfoEnabledFeature = featuregate.NewFeatureGate() csiNodeInfoDisabledFeature = featuregate.NewFeatureGate() ) @@ -56,12 +54,6 @@ func init() { if err := trDisabledFeature.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.TokenRequest: {Default: false}}); err != nil { panic(err) } - if err := leaseEnabledFeature.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.NodeLease: {Default: true}}); err != nil { - panic(err) - } - if err := leaseDisabledFeature.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.NodeLease: {Default: false}}); err != nil { - panic(err) - } if err := csiNodeInfoEnabledFeature.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.CSINodeInfo: {Default: true}}); err != nil { panic(err) } @@ -226,106 +218,84 @@ func TestAuthorizer(t *testing.T) { expect: authorizer.DecisionNoOpinion, }, { - name: "disallowed node lease - feature disabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease}, - features: leaseDisabledFeature, - expect: authorizer.DecisionNoOpinion, + name: "disallowed get lease in namespace other than kube-node-lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"}, + expect: authorizer.DecisionNoOpinion, }, { - name: "disallowed get lease in namespace other than kube-node-lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"}, - features: leaseEnabledFeature, - expect: authorizer.DecisionNoOpinion, + name: "disallowed create lease in namespace other than kube-node-lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"}, + expect: authorizer.DecisionNoOpinion, }, { - name: "disallowed create lease in namespace other than kube-node-lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"}, - features: leaseEnabledFeature, - expect: authorizer.DecisionNoOpinion, + name: "disallowed update lease in namespace other than kube-node-lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"}, + expect: authorizer.DecisionNoOpinion, }, { - name: "disallowed update lease in namespace other than kube-node-lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"}, - features: leaseEnabledFeature, - expect: authorizer.DecisionNoOpinion, + name: "disallowed patch lease in namespace other than kube-node-lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "patch", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"}, + expect: authorizer.DecisionNoOpinion, }, { - name: "disallowed patch lease in namespace other than kube-node-lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "patch", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"}, - features: leaseEnabledFeature, - expect: authorizer.DecisionNoOpinion, + name: "disallowed delete lease in namespace other than kube-node-lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "delete", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"}, + expect: authorizer.DecisionNoOpinion, }, { - name: "disallowed delete lease in namespace other than kube-node-lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "delete", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"}, - features: leaseEnabledFeature, - expect: authorizer.DecisionNoOpinion, + name: "disallowed get another node's lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node1", Namespace: corev1.NamespaceNodeLease}, + expect: authorizer.DecisionNoOpinion, }, { - name: "disallowed get another node's lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node1", Namespace: corev1.NamespaceNodeLease}, - features: leaseEnabledFeature, - expect: authorizer.DecisionNoOpinion, + name: "disallowed update another node's lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node1", Namespace: corev1.NamespaceNodeLease}, + expect: authorizer.DecisionNoOpinion, }, { - name: "disallowed update another node's lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node1", Namespace: corev1.NamespaceNodeLease}, - features: leaseEnabledFeature, - expect: authorizer.DecisionNoOpinion, + name: "disallowed patch another node's lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "patch", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node1", Namespace: corev1.NamespaceNodeLease}, + expect: authorizer.DecisionNoOpinion, }, { - name: "disallowed patch another node's lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "patch", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node1", Namespace: corev1.NamespaceNodeLease}, - features: leaseEnabledFeature, - expect: authorizer.DecisionNoOpinion, + name: "disallowed delete another node's lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "delete", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node1", Namespace: corev1.NamespaceNodeLease}, + expect: authorizer.DecisionNoOpinion, }, { - name: "disallowed delete another node's lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "delete", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node1", Namespace: corev1.NamespaceNodeLease}, - features: leaseEnabledFeature, - expect: authorizer.DecisionNoOpinion, + name: "disallowed list node leases - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "leases", APIGroup: "coordination.k8s.io", Namespace: corev1.NamespaceNodeLease}, + expect: authorizer.DecisionNoOpinion, }, { - name: "disallowed list node leases - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "leases", APIGroup: "coordination.k8s.io", Namespace: corev1.NamespaceNodeLease}, - features: leaseEnabledFeature, - expect: authorizer.DecisionNoOpinion, + name: "disallowed watch node leases - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "leases", APIGroup: "coordination.k8s.io", Namespace: corev1.NamespaceNodeLease}, + expect: authorizer.DecisionNoOpinion, }, { - name: "disallowed watch node leases - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "leases", APIGroup: "coordination.k8s.io", Namespace: corev1.NamespaceNodeLease}, - features: leaseEnabledFeature, - expect: authorizer.DecisionNoOpinion, + name: "allowed get node lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease}, + expect: authorizer.DecisionAllow, }, { - name: "allowed get node lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease}, - features: leaseEnabledFeature, - expect: authorizer.DecisionAllow, + name: "allowed create node lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease}, + expect: authorizer.DecisionAllow, }, { - name: "allowed create node lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease}, - features: leaseEnabledFeature, - expect: authorizer.DecisionAllow, + name: "allowed update node lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease}, + expect: authorizer.DecisionAllow, }, { - name: "allowed update node lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease}, - features: leaseEnabledFeature, - expect: authorizer.DecisionAllow, + name: "allowed patch node lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "patch", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease}, + expect: authorizer.DecisionAllow, }, { - name: "allowed patch node lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "patch", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease}, - features: leaseEnabledFeature, - expect: authorizer.DecisionAllow, - }, - { - name: "allowed delete node lease - feature enabled", - attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "delete", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease}, - features: leaseEnabledFeature, - expect: authorizer.DecisionAllow, + name: "allowed delete node lease - feature enabled", + attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "delete", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease}, + expect: authorizer.DecisionAllow, }, // CSINode { diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go index 44d39c9d9c3..2b6a0e42f47 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go @@ -143,6 +143,9 @@ func NodeRules() []rbacv1.PolicyRule { // for it to be signed. This allows the kubelet to rotate it's own certificate. rbacv1helpers.NewRule("create", "get", "list", "watch").Groups(certificatesGroup).Resources("certificatesigningrequests").RuleOrDie(), + // Leases + rbacv1helpers.NewRule("get", "create", "update", "patch", "delete").Groups("coordination.k8s.io").Resources("leases").RuleOrDie(), + // CSI rbacv1helpers.NewRule("get").Groups(storageGroup).Resources("volumeattachments").RuleOrDie(), } @@ -171,11 +174,6 @@ func NodeRules() []rbacv1.PolicyRule { nodePolicyRules = append(nodePolicyRules, csiNodeInfoRule) } - // Node leases - if utilfeature.DefaultFeatureGate.Enabled(features.NodeLease) { - nodePolicyRules = append(nodePolicyRules, rbacv1helpers.NewRule("get", "create", "update", "patch", "delete").Groups(coordinationGroup).Resources("leases").RuleOrDie()) - } - // RuntimeClass if utilfeature.DefaultFeatureGate.Enabled(features.RuntimeClass) { nodePolicyRules = append(nodePolicyRules, rbacv1helpers.NewRule("get", "list", "watch").Groups("node.k8s.io").Resources("runtimeclasses").RuleOrDie()) diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml index 6d4c4c9f356..d0860cf9c85 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml @@ -910,6 +910,16 @@ items: - get - list - watch + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - get + - patch + - update - apiGroups: - storage.k8s.io resources: @@ -948,16 +958,6 @@ items: - get - patch - update - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - delete - - get - - patch - - update - apiGroups: - node.k8s.io resources: diff --git a/test/integration/auth/node_test.go b/test/integration/auth/node_test.go index d0e851b1424..6370aee186c 100644 --- a/test/integration/auth/node_test.go +++ b/test/integration/auth/node_test.go @@ -56,9 +56,6 @@ func TestNodeAuthorizer(t *testing.T) { // Enable DynamicKubeletConfig feature so that Node.Spec.ConfigSource can be set defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DynamicKubeletConfig, true)() - // Enable NodeLease feature so that nodes can create leases - defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeLease, true)() - // Enable CSINodeInfo feature so that nodes can create CSINode objects. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSINodeInfo, true)()