Merge pull request #63314 from mtaufen/dkcfg-structured-status
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Move to a structured status for dynamic kubelet config This PR updates dynamic Kubelet config to use a structured status, rather than a node condition. This makes the status machine-readable, and thus more useful for config orchestration. Fixes: #56896 ```release-note The status of dynamic Kubelet config is now reported via Node.Status.Config, rather than the KubeletConfigOk node condition. ```
This commit is contained in:
@@ -4000,9 +4000,14 @@ func ValidateNode(node *core.Node) field.ErrorList {
|
||||
// That said, if specified, we need to ensure they are valid.
|
||||
allErrs = append(allErrs, ValidateNodeResources(node)...)
|
||||
|
||||
// Only allow Node.Spec.ConfigSource to be set if the DynamicKubeletConfig feature gate is enabled
|
||||
if node.Spec.ConfigSource != nil && !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) {
|
||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "configSource"), "configSource may only be set if the DynamicKubeletConfig feature gate is enabled)"))
|
||||
// Only allow Spec.ConfigSource and Status.Config to be set if the DynamicKubeletConfig feature gate is enabled
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) {
|
||||
if node.Spec.ConfigSource != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "configSource"), "configSource may only be set if the DynamicKubeletConfig feature gate is enabled)"))
|
||||
}
|
||||
if node.Status.Config != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("status", "config"), "config may only be set if the DynamicKubeletConfig feature gate is enabled)"))
|
||||
}
|
||||
}
|
||||
|
||||
if len(node.Spec.PodCIDR) != 0 {
|
||||
@@ -4087,6 +4092,18 @@ func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList {
|
||||
}
|
||||
}
|
||||
|
||||
// Allow and validate updates to Node.Spec.ConfigSource and Node.Status.Config if DynamicKubeletConfig feature gate is enabled
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) {
|
||||
if node.Spec.ConfigSource != nil {
|
||||
allErrs = append(allErrs, validateNodeConfigSourceSpec(node.Spec.ConfigSource, field.NewPath("spec", "configSource"))...)
|
||||
}
|
||||
oldNode.Spec.ConfigSource = node.Spec.ConfigSource
|
||||
if node.Status.Config != nil {
|
||||
allErrs = append(allErrs, validateNodeConfigStatus(node.Status.Config, field.NewPath("status", "config"))...)
|
||||
}
|
||||
oldNode.Status.Config = node.Status.Config
|
||||
}
|
||||
|
||||
// TODO: move reset function to its own location
|
||||
// Ignore metadata changes now that they have been tested
|
||||
oldNode.ObjectMeta = node.ObjectMeta
|
||||
@@ -4103,14 +4120,6 @@ func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList {
|
||||
}
|
||||
oldNode.Spec.Taints = node.Spec.Taints
|
||||
|
||||
// Allow updates to Node.Spec.ConfigSource if DynamicKubeletConfig feature gate is enabled
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) {
|
||||
if node.Spec.ConfigSource != nil {
|
||||
allErrs = append(allErrs, validateNodeConfigSourceSpec(node.Spec.ConfigSource, field.NewPath("spec", "configSource"))...)
|
||||
}
|
||||
oldNode.Spec.ConfigSource = node.Spec.ConfigSource
|
||||
}
|
||||
|
||||
// We made allowed changes to oldNode, and now we compare oldNode to node. Any remaining differences indicate changes to protected fields.
|
||||
// TODO: Add a 'real' error type for this error and provide print actual diffs.
|
||||
if !apiequality.Semantic.DeepEqual(oldNode, node) {
|
||||
@@ -4152,12 +4161,56 @@ func validateConfigMapNodeConfigSourceSpec(source *core.ConfigMapNodeConfigSourc
|
||||
return append(allErrs, validateConfigMapNodeConfigSource(source, fldPath)...)
|
||||
}
|
||||
|
||||
// validation specififc to Node.Status.Config
|
||||
func validateNodeConfigStatus(status *core.NodeConfigStatus, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if status.Assigned != nil {
|
||||
allErrs = append(allErrs, validateNodeConfigSourceStatus(status.Assigned, fldPath.Child("assigned"))...)
|
||||
}
|
||||
if status.Active != nil {
|
||||
allErrs = append(allErrs, validateNodeConfigSourceStatus(status.Active, fldPath.Child("active"))...)
|
||||
}
|
||||
if status.LastKnownGood != nil {
|
||||
allErrs = append(allErrs, validateNodeConfigSourceStatus(status.LastKnownGood, fldPath.Child("lastKnownGood"))...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validation specific to Node.Status.Config.(Active|Assigned|LastKnownGood)
|
||||
func validateNodeConfigSourceStatus(source *core.NodeConfigSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
count := int(0)
|
||||
if source.ConfigMap != nil {
|
||||
count++
|
||||
allErrs = append(allErrs, validateConfigMapNodeConfigSourceStatus(source.ConfigMap, fldPath.Child("configMap"))...)
|
||||
}
|
||||
// add more subfields here in the future as they are added to NodeConfigSource
|
||||
|
||||
// exactly one reference subfield must be non-nil
|
||||
if count != 1 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, source, "exactly one reference subfield must be non-nil"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validation specific to Node.Status.Config.(Active|Assigned|LastKnownGood).ConfigMap
|
||||
func validateConfigMapNodeConfigSourceStatus(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if string(source.UID) == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("uid"), "uid must be set in status"))
|
||||
}
|
||||
// TODO(#63221): require ResourceVersion in status when we start respecting ConfigMap mutations (the Kubelet isn't tracking it internally until
|
||||
// that PR, which makes it difficult to report for now).
|
||||
return append(allErrs, validateConfigMapNodeConfigSource(source, fldPath)...)
|
||||
}
|
||||
|
||||
// common validation
|
||||
func validateConfigMapNodeConfigSource(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
// validate target configmap namespace
|
||||
if source.Namespace == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "namespace must be set in spec"))
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "namespace must be set"))
|
||||
} else {
|
||||
for _, msg := range ValidateNameFunc(ValidateNamespaceName)(source.Namespace, false) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), source.Namespace, msg))
|
||||
@@ -4165,7 +4218,7 @@ func validateConfigMapNodeConfigSource(source *core.ConfigMapNodeConfigSource, f
|
||||
}
|
||||
// validate target configmap name
|
||||
if source.Name == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name must be set in spec"))
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name must be set"))
|
||||
} else {
|
||||
for _, msg := range ValidateNameFunc(ValidateConfigMapName)(source.Name, false) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), source.Name, msg))
|
||||
@@ -4173,7 +4226,7 @@ func validateConfigMapNodeConfigSource(source *core.ConfigMapNodeConfigSource, f
|
||||
}
|
||||
// validate kubeletConfigKey against rules for configMap key names
|
||||
if source.KubeletConfigKey == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("kubeletConfigKey"), "kubeletConfigKey must be set in spec"))
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("kubeletConfigKey"), "kubeletConfigKey must be set"))
|
||||
} else {
|
||||
for _, msg := range validation.IsConfigMapKey(source.KubeletConfigKey) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("kubeletConfigKey"), source.KubeletConfigKey, msg))
|
||||
|
Reference in New Issue
Block a user