Merge pull request #112643 from SergeyKanzhelev/removeDynamicKubeletConfig

remove DynamicKubeletConfig feature gate from the code
This commit is contained in:
Kubernetes Prow Robot 2022-10-12 01:33:00 -07:00 committed by GitHub
commit 525280d285
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 59 additions and 363 deletions

View File

@ -6322,7 +6322,7 @@
"properties": { "properties": {
"configSource": { "configSource": {
"$ref": "#/definitions/io.k8s.api.core.v1.NodeConfigSource", "$ref": "#/definitions/io.k8s.api.core.v1.NodeConfigSource",
"description": "Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed from Kubelets as of 1.24 and will be fully removed in 1.26." "description": "Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed."
}, },
"externalID": { "externalID": {
"description": "Deprecated. Not all kubelets will set this field. Remove field after 1.13. see: https://issues.k8s.io/61966", "description": "Deprecated. Not all kubelets will set this field. Remove field after 1.13. see: https://issues.k8s.io/61966",

View File

@ -3594,7 +3594,7 @@
"$ref": "#/components/schemas/io.k8s.api.core.v1.NodeConfigSource" "$ref": "#/components/schemas/io.k8s.api.core.v1.NodeConfigSource"
} }
], ],
"description": "Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed from Kubelets as of 1.24 and will be fully removed in 1.26." "description": "Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed."
}, },
"externalID": { "externalID": {
"description": "Deprecated. Not all kubelets will set this field. Remove field after 1.13. see: https://issues.k8s.io/61966", "description": "Deprecated. Not all kubelets will set this field. Remove field after 1.13. see: https://issues.k8s.io/61966",

View File

@ -235,13 +235,6 @@ HTTP server: The kubelet can also listen for HTTP and respond to a simple API
klog.InfoS("unsupported configuration:KubeletCgroups is not within KubeReservedCgroup") klog.InfoS("unsupported configuration:KubeletCgroups is not within KubeReservedCgroup")
} }
// The features.DynamicKubeletConfig is locked to false,
// feature gate is not locked using the LockedToDefault flag
// to make sure node authorizer can keep working with the older nodes
if utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) {
return fmt.Errorf("cannot set feature gate %v to %v, feature is locked to %v", features.DynamicKubeletConfig, true, false)
}
// construct a KubeletServer from kubeletFlags and kubeletConfig // construct a KubeletServer from kubeletFlags and kubeletConfig
kubeletServer := &options.KubeletServer{ kubeletServer := &options.KubeletServer{
KubeletFlags: *kubeletFlags, KubeletFlags: *kubeletFlags,

View File

@ -4160,7 +4160,7 @@ type NodeSpec struct {
// +optional // +optional
Taints []Taint Taints []Taint
// Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed from Kubelets as of 1.24 and will be fully removed in 1.26. // Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed.
// +optional // +optional
ConfigSource *NodeConfigSource ConfigSource *NodeConfigSource

View File

@ -5259,6 +5259,8 @@ func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList {
} }
// validation specific to Node.Spec.ConfigSource // validation specific to Node.Spec.ConfigSource
// The field ConfigSource is deprecated and will not be used. The validation is kept in place
// for the backward compatibility
func validateNodeConfigSourceSpec(source *core.NodeConfigSource, fldPath *field.Path) field.ErrorList { func validateNodeConfigSourceSpec(source *core.NodeConfigSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
count := int(0) count := int(0)
@ -5276,6 +5278,8 @@ func validateNodeConfigSourceSpec(source *core.NodeConfigSource, fldPath *field.
} }
// validation specific to Node.Spec.ConfigSource.ConfigMap // validation specific to Node.Spec.ConfigSource.ConfigMap
// The field ConfigSource is deprecated and will not be used. The validation is kept in place
// for the backward compatibility
func validateConfigMapNodeConfigSourceSpec(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList { func validateConfigMapNodeConfigSourceSpec(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
// uid and resourceVersion must not be set in spec // uid and resourceVersion must not be set in spec

View File

@ -256,12 +256,6 @@ const (
// Enables usage of hugepages-<size> in downward API. // Enables usage of hugepages-<size> in downward API.
DownwardAPIHugePages featuregate.Feature = "DownwardAPIHugePages" DownwardAPIHugePages featuregate.Feature = "DownwardAPIHugePages"
// owner: @mtaufen
// alpha: v1.4
// beta: v1.11
// deprecated: 1.22
DynamicKubeletConfig featuregate.Feature = "DynamicKubeletConfig"
// owner: @andrewsykim // owner: @andrewsykim
// kep: https://kep.k8s.io/1672 // kep: https://kep.k8s.io/1672
// alpha: v1.20 // alpha: v1.20
@ -863,8 +857,6 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
DownwardAPIHugePages: {Default: true, PreRelease: featuregate.Beta}, // on by default in 1.22 DownwardAPIHugePages: {Default: true, PreRelease: featuregate.Beta}, // on by default in 1.22
DynamicKubeletConfig: {Default: false, PreRelease: featuregate.Deprecated}, // feature gate is deprecated in 1.22, kubelet logic is removed in 1.24, api server logic can be removed in 1.26
EndpointSliceTerminatingCondition: {Default: true, PreRelease: featuregate.Beta}, EndpointSliceTerminatingCondition: {Default: true, PreRelease: featuregate.Beta},
EphemeralContainers: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.27 EphemeralContainers: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.27

View File

@ -19913,7 +19913,7 @@ func schema_k8sio_api_core_v1_NodeSpec(ref common.ReferenceCallback) common.Open
}, },
"configSource": { "configSource": {
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed from Kubelets as of 1.24 and will be fully removed in 1.26.", Description: "Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed.",
Ref: ref("k8s.io/api/core/v1.NodeConfigSource"), Ref: ref("k8s.io/api/core/v1.NodeConfigSource"),
}, },
}, },

View File

@ -34,11 +34,9 @@ import (
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
pkgstorage "k8s.io/apiserver/pkg/storage" pkgstorage "k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/kubelet/client" "k8s.io/kubernetes/pkg/kubelet/client"
proxyutil "k8s.io/kubernetes/pkg/proxy/util" proxyutil "k8s.io/kubernetes/pkg/proxy/util"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath" "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
@ -94,13 +92,13 @@ func (nodeStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Objec
func dropDisabledFields(node *api.Node, oldNode *api.Node) { func dropDisabledFields(node *api.Node, oldNode *api.Node) {
// Nodes allow *all* fields, including status, to be set on create. // Nodes allow *all* fields, including status, to be set on create.
// for create // for create
if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) && oldNode == nil { if oldNode == nil {
node.Spec.ConfigSource = nil node.Spec.ConfigSource = nil
node.Status.Config = nil node.Status.Config = nil
} }
// for update // for update
if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) && !nodeConfigSourceInUse(oldNode) && oldNode != nil { if !nodeConfigSourceInUse(oldNode) && oldNode != nil {
node.Spec.ConfigSource = nil node.Spec.ConfigSource = nil
} }
@ -170,7 +168,7 @@ func (nodeStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime
oldNode := old.(*api.Node) oldNode := old.(*api.Node)
newNode.Spec = oldNode.Spec newNode.Spec = oldNode.Spec
if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) && !nodeStatusConfigInUse(oldNode) { if !nodeStatusConfigInUse(oldNode) {
newNode.Status.Config = nil newNode.Status.Config = nil
} }
} }
@ -274,7 +272,7 @@ func dynamicKubeletConfigIsDeprecatedWarning(obj runtime.Object) []string {
if newNode.Spec.ConfigSource != nil { if newNode.Spec.ConfigSource != nil {
var warnings []string var warnings []string
// KEP https://github.com/kubernetes/enhancements/issues/281 // KEP https://github.com/kubernetes/enhancements/issues/281
warnings = append(warnings, "spec.configSource: deprecated in v1.22, support removal is planned in v1.23") warnings = append(warnings, "spec.configSource: the feature is removed")
return warnings return warnings
} }
return nil return nil

View File

@ -27,11 +27,8 @@ import (
"k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/diff"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
apitesting "k8s.io/kubernetes/pkg/api/testing" apitesting "k8s.io/kubernetes/pkg/api/testing"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
// ensure types are installed // ensure types are installed
_ "k8s.io/kubernetes/pkg/apis/core/install" _ "k8s.io/kubernetes/pkg/apis/core/install"
@ -88,11 +85,10 @@ func makeNode(podCIDRs []string, addSpecDynamicConfig bool, addStatusDynamicConf
func TestDropFields(t *testing.T) { func TestDropFields(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
node *api.Node node *api.Node
oldNode *api.Node oldNode *api.Node
compareNode *api.Node compareNode *api.Node
enableNodeDynamicConfig bool
}{ }{
{ {
name: "nil pod cidrs", name: "nil pod cidrs",
@ -127,60 +123,45 @@ func TestDropFields(t *testing.T) {
compareNode: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false), compareNode: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
}, },
{ {
name: "new with no Spec.ConfigSource and no Status.Config , enableNodeDynamicConfig disabled", name: "new with no Spec.ConfigSource and no Status.Config",
enableNodeDynamicConfig: false, node: makeNode(nil, false, false),
node: makeNode(nil, false, false), oldNode: nil,
oldNode: nil, compareNode: makeNode(nil, false, false),
compareNode: makeNode(nil, false, false),
}, },
{ {
name: "new with Spec.ConfigSource and no Status.Config, enableNodeDynamicConfig disabled", name: "new with Spec.ConfigSource and no Status.Config",
enableNodeDynamicConfig: false, node: makeNode(nil, true, false),
node: makeNode(nil, true, false), oldNode: nil,
oldNode: nil, compareNode: makeNode(nil, false, false),
compareNode: makeNode(nil, false, false),
}, },
{ {
name: "new with Spec.ConfigSource and Status.Config, enableNodeDynamicConfig disabled", name: "new with Spec.ConfigSource and Status.Config",
enableNodeDynamicConfig: false, node: makeNode(nil, true, true),
node: makeNode(nil, true, true), oldNode: nil,
oldNode: nil, compareNode: makeNode(nil, false, false),
compareNode: makeNode(nil, false, false),
}, },
{ {
name: "update with Spec.ConfigSource and Status.Config (old has none), enableNodeDynamicConfig disabled", name: "update with Spec.ConfigSource and Status.Config (old has none)",
enableNodeDynamicConfig: false, node: makeNode(nil, true, true),
node: makeNode(nil, true, true), oldNode: makeNode(nil, false, false),
oldNode: makeNode(nil, false, false), compareNode: makeNode(nil, false, true),
compareNode: makeNode(nil, false, true),
}, },
{ {
name: "update with Spec.ConfigSource and Status.Config (old has them), enableNodeDynamicConfig disabled", name: "update with Spec.ConfigSource and Status.Config (old has them)",
enableNodeDynamicConfig: false, node: makeNode(nil, true, true),
node: makeNode(nil, true, true), oldNode: makeNode(nil, true, true),
oldNode: makeNode(nil, true, true), compareNode: makeNode(nil, true, true),
compareNode: makeNode(nil, true, true),
}, },
{ {
name: "update with Spec.ConfigSource and Status.Config (old has Status.Config), enableNodeDynamicConfig disabled", name: "update with Spec.ConfigSource and Status.Config (old has Status.Config)",
enableNodeDynamicConfig: false, node: makeNode(nil, true, true),
node: makeNode(nil, true, true), oldNode: makeNode(nil, false, true),
oldNode: makeNode(nil, false, true), compareNode: makeNode(nil, false, true),
compareNode: makeNode(nil, false, true),
},
{
name: "new with Spec.ConfigSource and Status.Config, enableNodeDynamicConfig enabled",
enableNodeDynamicConfig: true,
node: makeNode(nil, true, true),
oldNode: nil,
compareNode: makeNode(nil, true, true),
}, },
} }
for _, tc := range testCases { for _, tc := range testCases {
func() { func() {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DynamicKubeletConfig, tc.enableNodeDynamicConfig)()
dropDisabledFields(tc.node, tc.oldNode) dropDisabledFields(tc.node, tc.oldNode)
old := tc.oldNode.DeepCopy() old := tc.oldNode.DeepCopy()

View File

@ -476,31 +476,3 @@ func (g *Graph) DeleteVolumeAttachment(name string) {
defer g.lock.Unlock() defer g.lock.Unlock()
g.deleteVertex_locked(vaVertexType, "", name) g.deleteVertex_locked(vaVertexType, "", name)
} }
// SetNodeConfigMap sets up edges for the Node.Spec.ConfigSource.ConfigMap relationship:
//
// configmap -> node
func (g *Graph) SetNodeConfigMap(nodeName, configMapName, configMapNamespace string) {
start := time.Now()
defer func() {
graphActionsDuration.WithLabelValues("SetNodeConfigMap").Observe(time.Since(start).Seconds())
}()
g.lock.Lock()
defer g.lock.Unlock()
// TODO(mtaufen): ensure len(nodeName) > 0 in all cases (would sure be nice to have a dependently-typed language here...)
// clear edges configmaps -> node where the destination is the current node *only*
// at present, a node can only have one *direct* configmap reference at a time
g.deleteEdges_locked(configMapVertexType, nodeVertexType, "", nodeName)
// establish new edges if we have a real ConfigMap to reference
if len(configMapName) > 0 && len(configMapNamespace) > 0 {
configmapVertex := g.getOrCreateVertex_locked(configMapVertexType, configMapNamespace, configMapName)
nodeVertex := g.getOrCreateVertex_locked(nodeVertexType, "", nodeName)
e := newDestinationEdge(configmapVertex, nodeVertex, nodeVertex)
g.graph.SetEdge(e)
g.addEdgeToDestinationIndex_locked(e)
}
}

View File

@ -17,7 +17,6 @@ limitations under the License.
package node package node
import ( import (
"fmt"
"time" "time"
"k8s.io/klog/v2" "k8s.io/klog/v2"
@ -25,11 +24,9 @@ import (
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
storagev1 "k8s.io/api/storage/v1" storagev1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
corev1informers "k8s.io/client-go/informers/core/v1" corev1informers "k8s.io/client-go/informers/core/v1"
storageinformers "k8s.io/client-go/informers/storage/v1" storageinformers "k8s.io/client-go/informers/storage/v1"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/kubernetes/pkg/features"
) )
type graphPopulator struct { type graphPopulator struct {
@ -49,15 +46,6 @@ func AddGraphEventHandlers(
var hasSynced []cache.InformerSynced var hasSynced []cache.InformerSynced
if utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) {
nodes.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: g.addNode,
UpdateFunc: g.updateNode,
DeleteFunc: g.deleteNode,
})
hasSynced = append(hasSynced, nodes.Informer().HasSynced)
}
pods.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ pods.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: g.addPod, AddFunc: g.addPod,
UpdateFunc: g.updatePod, UpdateFunc: g.updatePod,
@ -82,62 +70,6 @@ func AddGraphEventHandlers(
go cache.WaitForNamedCacheSync("node_authorizer", wait.NeverStop, hasSynced...) go cache.WaitForNamedCacheSync("node_authorizer", wait.NeverStop, hasSynced...)
} }
func (g *graphPopulator) addNode(obj interface{}) {
g.updateNode(nil, obj)
}
func (g *graphPopulator) updateNode(oldObj, obj interface{}) {
node := obj.(*corev1.Node)
var oldNode *corev1.Node
if oldObj != nil {
oldNode = oldObj.(*corev1.Node)
}
// we only set up rules for ConfigMap today, because that is the only reference type
var name, namespace string
if source := node.Spec.ConfigSource; source != nil && source.ConfigMap != nil {
name = source.ConfigMap.Name
namespace = source.ConfigMap.Namespace
}
var oldName, oldNamespace string
if oldNode != nil {
if oldSource := oldNode.Spec.ConfigSource; oldSource != nil && oldSource.ConfigMap != nil {
oldName = oldSource.ConfigMap.Name
oldNamespace = oldSource.ConfigMap.Namespace
}
}
// if Node.Spec.ConfigSource wasn't updated, nothing for us to do
if name == oldName && namespace == oldNamespace {
return
}
path := "nil"
if node.Spec.ConfigSource != nil {
path = fmt.Sprintf("%s/%s", namespace, name)
}
klog.V(4).Infof("updateNode configSource reference to %s for node %s", path, node.Name)
g.graph.SetNodeConfigMap(node.Name, name, namespace)
}
func (g *graphPopulator) deleteNode(obj interface{}) {
if tombstone, ok := obj.(cache.DeletedFinalStateUnknown); ok {
obj = tombstone.Obj
}
node, ok := obj.(*corev1.Node)
if !ok {
klog.Infof("unexpected type %T", obj)
return
}
// NOTE: We don't remove the node, because if the node is re-created not all pod -> node
// links are re-established (we don't get relevant events because the no mutations need
// to happen in the API; the state is already there).
g.graph.SetNodeConfigMap(node.Name, "", "")
}
func (g *graphPopulator) addPod(obj interface{}) { func (g *graphPopulator) addPod(obj interface{}) {
g.updatePod(nil, obj) g.updatePod(nil, obj)
} }

View File

@ -343,74 +343,4 @@ func TestIndex(t *testing.T) {
"configmap:ns/cm3": {"node:node1=1", "node:node2=1", "node:node3=1"}, "configmap:ns/cm3": {"node:node1=1", "node:node2=1", "node:node3=1"},
"serviceAccount:ns/sa1": {"node:node1=1", "node:node2=1", "node:node3=1"}, "serviceAccount:ns/sa1": {"node:node1=1", "node:node2=1", "node:node3=1"},
}) })
// Set node->configmap references
g.SetNodeConfigMap("node1", "cm1", "ns")
g.SetNodeConfigMap("node2", "cm1", "ns")
g.SetNodeConfigMap("node3", "cm1", "ns")
g.SetNodeConfigMap("node4", "cm1", "ns")
expectGraph(map[string][]string{
"node:node1": {},
"node:node2": {},
"node:node3": {},
"node:node4": {},
"pod:ns/pod2": {"node:node2"},
"pod:ns/pod3": {"node:node3"},
"pod:ns/pod4": {"node:node1"},
"configmap:ns/cm1": {"node:node1", "node:node2", "node:node3", "node:node4", "pod:ns/pod2", "pod:ns/pod3", "pod:ns/pod4"},
"configmap:ns/cm2": {"pod:ns/pod2", "pod:ns/pod3", "pod:ns/pod4"},
"configmap:ns/cm3": {"pod:ns/pod2", "pod:ns/pod3", "pod:ns/pod4"},
"serviceAccount:ns/sa1": {"pod:ns/pod2", "pod:ns/pod3", "pod:ns/pod4"},
})
expectIndex(map[string][]string{
"configmap:ns/cm1": {"node:node1=2", "node:node2=2", "node:node3=2", "node:node4=1"},
"configmap:ns/cm2": {"node:node1=1", "node:node2=1", "node:node3=1"},
"configmap:ns/cm3": {"node:node1=1", "node:node2=1", "node:node3=1"},
"serviceAccount:ns/sa1": {"node:node1=1", "node:node2=1", "node:node3=1"},
})
// Update node->configmap reference
g.SetNodeConfigMap("node1", "cm2", "ns")
expectGraph(map[string][]string{
"node:node1": {},
"node:node2": {},
"node:node3": {},
"node:node4": {},
"pod:ns/pod2": {"node:node2"},
"pod:ns/pod3": {"node:node3"},
"pod:ns/pod4": {"node:node1"},
"configmap:ns/cm1": {"node:node2", "node:node3", "node:node4", "pod:ns/pod2", "pod:ns/pod3", "pod:ns/pod4"},
"configmap:ns/cm2": {"node:node1", "pod:ns/pod2", "pod:ns/pod3", "pod:ns/pod4"},
"configmap:ns/cm3": {"pod:ns/pod2", "pod:ns/pod3", "pod:ns/pod4"},
"serviceAccount:ns/sa1": {"pod:ns/pod2", "pod:ns/pod3", "pod:ns/pod4"},
})
expectIndex(map[string][]string{
"configmap:ns/cm1": {"node:node1=1", "node:node2=2", "node:node3=2", "node:node4=1"},
"configmap:ns/cm2": {"node:node1=2", "node:node2=1", "node:node3=1"},
"configmap:ns/cm3": {"node:node1=1", "node:node2=1", "node:node3=1"},
"serviceAccount:ns/sa1": {"node:node1=1", "node:node2=1", "node:node3=1"},
})
// Remove node->configmap reference
g.SetNodeConfigMap("node1", "", "")
g.SetNodeConfigMap("node4", "", "")
expectGraph(map[string][]string{
"node:node1": {},
"node:node2": {},
"node:node3": {},
"node:node4": {},
"pod:ns/pod2": {"node:node2"},
"pod:ns/pod3": {"node:node3"},
"pod:ns/pod4": {"node:node1"},
"configmap:ns/cm1": {"node:node2", "node:node3", "pod:ns/pod2", "pod:ns/pod3", "pod:ns/pod4"},
"configmap:ns/cm2": {"pod:ns/pod2", "pod:ns/pod3", "pod:ns/pod4"},
"configmap:ns/cm3": {"pod:ns/pod2", "pod:ns/pod3", "pod:ns/pod4"},
"serviceAccount:ns/sa1": {"pod:ns/pod2", "pod:ns/pod3", "pod:ns/pod4"},
})
expectIndex(map[string][]string{
"configmap:ns/cm1": {"node:node1=1", "node:node2=2", "node:node3=2"},
"configmap:ns/cm2": {"node:node1=1", "node:node2=1", "node:node3=1"},
"configmap:ns/cm3": {"node:node1=1", "node:node2=1", "node:node3=1"},
"serviceAccount:ns/sa1": {"node:node1=1", "node:node2=1", "node:node3=1"},
})
} }

View File

@ -30,7 +30,6 @@ import (
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
storagev1 "k8s.io/api/storage/v1" storagev1 "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/authorization/authorizer"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
@ -68,11 +67,6 @@ func TestAuthorizer(t *testing.T) {
expect authorizer.Decision expect authorizer.Decision
features featuregate.FeatureGate features featuregate.FeatureGate
}{ }{
{
name: "allowed node configmap",
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "node0-configmap", Namespace: "ns0"},
expect: authorizer.DecisionAllow,
},
{ {
name: "allowed configmap", name: "allowed configmap",
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node0", Namespace: "ns0"}, attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node0", Namespace: "ns0"},
@ -128,12 +122,6 @@ func TestAuthorizer(t *testing.T) {
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumes", Name: "pv0-pod0-node0-ns0", Namespace: ""}, attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumes", Name: "pv0-pod0-node0-ns0", Namespace: ""},
expect: authorizer.DecisionAllow, expect: authorizer.DecisionAllow,
}, },
{
name: "disallowed node configmap",
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "node1-configmap", Namespace: "ns0"},
expect: authorizer.DecisionNoOpinion,
},
{ {
name: "disallowed configmap", name: "disallowed configmap",
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node1", Namespace: "ns0"}, attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node1", Namespace: "ns0"},
@ -385,36 +373,23 @@ func TestAuthorizerSharedResources(t *testing.T) {
} }
g.AddPod(pod3) g.AddPod(pod3)
g.SetNodeConfigMap("node1", "shared-configmap", "ns1")
g.SetNodeConfigMap("node2", "shared-configmap", "ns1")
g.SetNodeConfigMap("node3", "configmap", "ns1")
testcases := []struct { testcases := []struct {
User user.Info User user.Info
Secret string Secret string
ConfigMap string ConfigMap string
ExpectAllowed bool Decision authorizer.Decision
}{ }{
{User: node1, ExpectAllowed: true, Secret: "node1-only"}, {User: node1, Decision: authorizer.DecisionAllow, Secret: "node1-only"},
{User: node1, ExpectAllowed: true, Secret: "node1-node2-only"}, {User: node1, Decision: authorizer.DecisionAllow, Secret: "node1-node2-only"},
{User: node1, ExpectAllowed: true, Secret: "shared-all"}, {User: node1, Decision: authorizer.DecisionAllow, Secret: "shared-all"},
{User: node2, ExpectAllowed: false, Secret: "node1-only"}, {User: node2, Decision: authorizer.DecisionNoOpinion, Secret: "node1-only"},
{User: node2, ExpectAllowed: true, Secret: "node1-node2-only"}, {User: node2, Decision: authorizer.DecisionAllow, Secret: "node1-node2-only"},
{User: node2, ExpectAllowed: true, Secret: "shared-all"}, {User: node2, Decision: authorizer.DecisionAllow, Secret: "shared-all"},
{User: node3, ExpectAllowed: false, Secret: "node1-only"}, {User: node3, Decision: authorizer.DecisionNoOpinion, Secret: "node1-only"},
{User: node3, ExpectAllowed: false, Secret: "node1-node2-only"}, {User: node3, Decision: authorizer.DecisionNoOpinion, Secret: "node1-node2-only"},
{User: node3, ExpectAllowed: true, Secret: "shared-all"}, {User: node3, Decision: authorizer.DecisionAllow, Secret: "shared-all"},
{User: node1, ExpectAllowed: true, ConfigMap: "shared-configmap"},
{User: node1, ExpectAllowed: false, ConfigMap: "configmap"},
{User: node2, ExpectAllowed: true, ConfigMap: "shared-configmap"},
{User: node2, ExpectAllowed: false, ConfigMap: "configmap"},
{User: node3, ExpectAllowed: false, ConfigMap: "shared-configmap"},
{User: node3, ExpectAllowed: true, ConfigMap: "configmap"},
} }
for i, tc := range testcases { for i, tc := range testcases {
@ -439,8 +414,8 @@ func TestAuthorizerSharedResources(t *testing.T) {
t.Fatalf("test case must include a request for a Secret or ConfigMap") t.Fatalf("test case must include a request for a Secret or ConfigMap")
} }
if (decision == authorizer.DecisionAllow) != tc.ExpectAllowed { if decision != tc.Decision {
t.Errorf("%d: expected %v, got %v", i, tc.ExpectAllowed, decision) t.Errorf("%d: expected %v, got %v", i, tc.Decision, decision)
} }
} }
@ -629,11 +604,6 @@ func BenchmarkAuthorization(b *testing.B) {
expect authorizer.Decision expect authorizer.Decision
features featuregate.FeatureGate features featuregate.FeatureGate
}{ }{
{
name: "allowed node configmap",
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "node0-configmap", Namespace: "ns0"},
expect: authorizer.DecisionAllow,
},
{ {
name: "allowed configmap", name: "allowed configmap",
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node0", Namespace: "ns0"}, attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node0", Namespace: "ns0"},
@ -649,12 +619,6 @@ func BenchmarkAuthorization(b *testing.B) {
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-shared", Namespace: "ns0"}, attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-shared", Namespace: "ns0"},
expect: authorizer.DecisionAllow, expect: authorizer.DecisionAllow,
}, },
{
name: "disallowed node configmap",
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "node1-configmap", Namespace: "ns0"},
expect: authorizer.DecisionNoOpinion,
},
{ {
name: "disallowed configmap", name: "disallowed configmap",
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node1", Namespace: "ns0"}, attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node1", Namespace: "ns0"},
@ -779,9 +743,6 @@ func BenchmarkAuthorization(b *testing.B) {
func populate(graph *Graph, nodes []*corev1.Node, pods []*corev1.Pod, pvs []*corev1.PersistentVolume, attachments []*storagev1.VolumeAttachment) { func populate(graph *Graph, nodes []*corev1.Node, pods []*corev1.Pod, pvs []*corev1.PersistentVolume, attachments []*storagev1.VolumeAttachment) {
p := &graphPopulator{} p := &graphPopulator{}
p.graph = graph p.graph = graph
for _, node := range nodes {
p.addNode(node)
}
for _, pod := range pods { for _, pod := range pods {
p.addPod(pod) p.addPod(pod)
} }
@ -830,19 +791,9 @@ func generate(opts *sampleDataOpts) ([]*corev1.Node, []*corev1.Pod, []*corev1.Pe
attachments = append(attachments, attachment) attachments = append(attachments, attachment)
} }
name := fmt.Sprintf("%s-configmap", nodeName)
nodes = append(nodes, &corev1.Node{ nodes = append(nodes, &corev1.Node{
ObjectMeta: metav1.ObjectMeta{Name: nodeName}, ObjectMeta: metav1.ObjectMeta{Name: nodeName},
Spec: corev1.NodeSpec{ Spec: corev1.NodeSpec{},
ConfigSource: &corev1.NodeConfigSource{
ConfigMap: &corev1.ConfigMapNodeConfigSource{
Name: name,
Namespace: "ns0",
UID: types.UID(fmt.Sprintf("ns0-%s", name)),
KubeletConfigKey: "kubelet",
},
},
},
}) })
} }
return nodes, pods, pvs, attachments return nodes, pods, pvs, attachments

View File

@ -2413,7 +2413,7 @@ message NodeSpec {
// +optional // +optional
repeated Taint taints = 5; repeated Taint taints = 5;
// Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed from Kubelets as of 1.24 and will be fully removed in 1.26. // Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed.
// +optional // +optional
optional NodeConfigSource configSource = 6; optional NodeConfigSource configSource = 6;

View File

@ -4898,7 +4898,7 @@ type NodeSpec struct {
// +optional // +optional
Taints []Taint `json:"taints,omitempty" protobuf:"bytes,5,opt,name=taints"` Taints []Taint `json:"taints,omitempty" protobuf:"bytes,5,opt,name=taints"`
// Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed from Kubelets as of 1.24 and will be fully removed in 1.26. // Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed.
// +optional // +optional
ConfigSource *NodeConfigSource `json:"configSource,omitempty" protobuf:"bytes,6,opt,name=configSource"` ConfigSource *NodeConfigSource `json:"configSource,omitempty" protobuf:"bytes,6,opt,name=configSource"`

View File

@ -1189,7 +1189,7 @@ var map_NodeSpec = map[string]string{
"providerID": "ID of the node assigned by the cloud provider in the format: <ProviderName>://<ProviderSpecificNodeID>", "providerID": "ID of the node assigned by the cloud provider in the format: <ProviderName>://<ProviderSpecificNodeID>",
"unschedulable": "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: https://kubernetes.io/docs/concepts/nodes/node/#manual-node-administration", "unschedulable": "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: https://kubernetes.io/docs/concepts/nodes/node/#manual-node-administration",
"taints": "If specified, the node's taints.", "taints": "If specified, the node's taints.",
"configSource": "Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed from Kubelets as of 1.24 and will be fully removed in 1.26.", "configSource": "Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed.",
"externalID": "Deprecated. Not all kubelets will set this field. Remove field after 1.13. see: https://issues.k8s.io/61966", "externalID": "Deprecated. Not all kubelets will set this field. Remove field after 1.13. see: https://issues.k8s.io/61966",
} }

View File

@ -33,11 +33,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
featuregatetesting "k8s.io/component-base/featuregate/testing"
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/test/integration/framework" "k8s.io/kubernetes/test/integration/framework"
"k8s.io/utils/pointer" "k8s.io/utils/pointer"
) )
@ -52,9 +49,6 @@ func TestNodeAuthorizer(t *testing.T) {
tokenNode2 = "node2-token" tokenNode2 = "node2-token"
) )
// Enable DynamicKubeletConfig feature so that Node.Spec.ConfigSource can be set
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DynamicKubeletConfig, true)()
tokenFile, err := os.CreateTemp("", "kubeconfig") tokenFile, err := os.CreateTemp("", "kubeconfig")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -106,9 +100,6 @@ func TestNodeAuthorizer(t *testing.T) {
if _, err := superuserClient.CoreV1().ConfigMaps("ns").Create(context.TODO(), &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "myconfigmap"}}, metav1.CreateOptions{}); err != nil { if _, err := superuserClient.CoreV1().ConfigMaps("ns").Create(context.TODO(), &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "myconfigmap"}}, metav1.CreateOptions{}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if _, err := superuserClient.CoreV1().ConfigMaps("ns").Create(context.TODO(), &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "myconfigmapconfigsource"}}, metav1.CreateOptions{}); err != nil {
t.Fatal(err)
}
pvName := "mypv" pvName := "mypv"
if _, err := superuserClientExternal.StorageV1().VolumeAttachments().Create(context.TODO(), &storagev1.VolumeAttachment{ if _, err := superuserClientExternal.StorageV1().VolumeAttachments().Create(context.TODO(), &storagev1.VolumeAttachment{
ObjectMeta: metav1.ObjectMeta{Name: "myattachment"}, ObjectMeta: metav1.ObjectMeta{Name: "myattachment"},
@ -160,12 +151,6 @@ func TestNodeAuthorizer(t *testing.T) {
return err return err
} }
} }
getConfigMapConfigSource := func(client clientset.Interface) func() error {
return func() error {
_, err := client.CoreV1().ConfigMaps("ns").Get(context.TODO(), "myconfigmapconfigsource", metav1.GetOptions{})
return err
}
}
getPVC := func(client clientset.Interface) func() error { getPVC := func(client clientset.Interface) func() error {
return func() error { return func() error {
_, err := client.CoreV1().PersistentVolumeClaims("ns").Get(context.TODO(), "mypvc", metav1.GetOptions{}) _, err := client.CoreV1().PersistentVolumeClaims("ns").Get(context.TODO(), "mypvc", metav1.GetOptions{})
@ -260,34 +245,6 @@ func TestNodeAuthorizer(t *testing.T) {
return err return err
} }
} }
setNode2ConfigSource := func(client clientset.Interface) func() error {
return func() error {
node2, err := client.CoreV1().Nodes().Get(context.TODO(), "node2", metav1.GetOptions{})
if err != nil {
return err
}
node2.Spec.ConfigSource = &corev1.NodeConfigSource{
ConfigMap: &corev1.ConfigMapNodeConfigSource{
Namespace: "ns",
Name: "myconfigmapconfigsource",
KubeletConfigKey: "kubelet",
},
}
_, err = client.CoreV1().Nodes().Update(context.TODO(), node2, metav1.UpdateOptions{})
return err
}
}
unsetNode2ConfigSource := func(client clientset.Interface) func() error {
return func() error {
node2, err := client.CoreV1().Nodes().Get(context.TODO(), "node2", metav1.GetOptions{})
if err != nil {
return err
}
node2.Spec.ConfigSource = nil
_, err = client.CoreV1().Nodes().Update(context.TODO(), node2, metav1.UpdateOptions{})
return err
}
}
updateNode2Status := func(client clientset.Interface) func() error { updateNode2Status := func(client clientset.Interface) func() error {
return func() error { return func() error {
_, err := client.CoreV1().Nodes().UpdateStatus(context.TODO(), &corev1.Node{ _, err := client.CoreV1().Nodes().UpdateStatus(context.TODO(), &corev1.Node{
@ -578,20 +535,6 @@ func TestNodeAuthorizer(t *testing.T) {
// create node2 again // create node2 again
expectAllowed(t, createNode2(node2Client)) expectAllowed(t, createNode2(node2Client))
// node2 can not set its own config source
expectForbidden(t, setNode2ConfigSource(node2Client))
// node2 can not access the configmap config source yet
expectForbidden(t, getConfigMapConfigSource(node2Client))
// superuser can access the configmap config source
expectAllowed(t, getConfigMapConfigSource(superuserClient))
// superuser can set node2's config source
expectAllowed(t, setNode2ConfigSource(superuserClient))
// node2 can now get the configmap assigned as its config source
expectAllowed(t, getConfigMapConfigSource(node2Client))
// superuser can unset node2's config source
expectAllowed(t, unsetNode2ConfigSource(superuserClient))
// node2 can no longer get the configmap after it is unassigned as its config source
expectForbidden(t, getConfigMapConfigSource(node2Client))
// clean up node2 // clean up node2
expectAllowed(t, deleteNode2(superuserClient)) expectAllowed(t, deleteNode2(superuserClient))