kubeadm: add --allow-deprecated-api to 'config validate'

The command can have this additional flag so that a warning
is printed or not printed when a deprecated API is validated.

Additionally, this commit fixes missing UpgradeConfiguration
validation and strict errors. By default we call:
  documentMapToUpgradeConfiguration(gvkmap, false, false, false)

so there is no change in behavior outside of
'config migrate/validate'.
This commit is contained in:
Lubomir I. Ivanov
2025-11-05 19:04:38 +02:00
parent 26a2945d5d
commit e5665154f7
5 changed files with 42 additions and 11 deletions

View File

@@ -311,7 +311,7 @@ func newCmdConfigMigrate(out io.Writer) *cobra.Command {
// newCmdConfigValidate returns cobra.Command for the "kubeadm config validate" command
func newCmdConfigValidate(out io.Writer) *cobra.Command {
var cfgPath string
var allowExperimental bool
var allowDeprecated, allowExperimental bool
cmd := &cobra.Command{
Use: "validate",
@@ -336,7 +336,7 @@ func newCmdConfigValidate(out io.Writer) *cobra.Command {
return err
}
if err := configutil.ValidateConfig(cfgBytes, allowExperimental); err != nil {
if err := configutil.ValidateConfig(cfgBytes, allowDeprecated, allowExperimental); err != nil {
return err
}
fmt.Fprintln(out, "ok")
@@ -346,6 +346,7 @@ func newCmdConfigValidate(out io.Writer) *cobra.Command {
Args: cobra.NoArgs,
}
options.AddConfigFlag(cmd.Flags(), &cfgPath)
cmd.Flags().BoolVar(&allowDeprecated, options.AllowDeprecatedAPI, false, "Allow validation of deprecated APIs.")
cmd.Flags().BoolVar(&allowExperimental, options.AllowExperimentalAPI, false, "Allow validation of experimental, unreleased APIs.")
return cmd
}

View File

@@ -143,6 +143,9 @@ const (
// CleanupTmpDir flag indicates whether reset will cleanup the tmp dir
CleanupTmpDir = "cleanup-tmp-dir"
// AllowDeprecatedAPI flag can be used to allow deprecated APIs
AllowDeprecatedAPI = "allow-deprecated-api"
// AllowExperimentalAPI flag can be used to allow experimental / work in progress APIs
AllowExperimentalAPI = "allow-experimental-api"

View File

@@ -322,12 +322,28 @@ func MigrateOldConfig(oldConfig []byte, allowExperimental bool, mutators migrate
newConfig = append(newConfig, b)
}
// Migrate UpgradeConfiguration if there is any
if kubeadmutil.GroupVersionKindsHasUpgradeConfiguration(gvks...) {
o, err := documentMapToUpgradeConfiguration(gvkmap, true, allowExperimental, true)
if err != nil {
return []byte{}, err
}
if err := mutators.mutate([]any{o}); err != nil {
return []byte{}, err
}
b, err := MarshalKubeadmConfigObject(o, gv)
if err != nil {
return []byte{}, err
}
newConfig = append(newConfig, b)
}
return bytes.Join(newConfig, []byte(constants.YAMLDocumentSeparator)), nil
}
// ValidateConfig takes a byte slice containing a kubeadm configuration and performs conversion
// to internal types and validation.
func ValidateConfig(config []byte, allowExperimental bool) error {
func ValidateConfig(config []byte, allowDeprecated, allowExperimental bool) error {
gvkmap, err := kubeadmutil.SplitConfigDocuments(config)
if err != nil {
return err
@@ -344,21 +360,28 @@ func ValidateConfig(config []byte, allowExperimental bool) error {
// Validate InitConfiguration and ClusterConfiguration if there are any in the config
if kubeadmutil.GroupVersionKindsHasInitConfiguration(gvks...) || kubeadmutil.GroupVersionKindsHasClusterConfiguration(gvks...) {
if _, err := documentMapToInitConfiguration(gvkmap, true, allowExperimental, true, true); err != nil {
if _, err := documentMapToInitConfiguration(gvkmap, allowDeprecated, allowExperimental, true, true); err != nil {
return err
}
}
// Validate JoinConfiguration if there is any
if kubeadmutil.GroupVersionKindsHasJoinConfiguration(gvks...) {
if _, err := documentMapToJoinConfiguration(gvkmap, true, allowExperimental, true, true); err != nil {
if _, err := documentMapToJoinConfiguration(gvkmap, allowDeprecated, allowExperimental, true, true); err != nil {
return err
}
}
// Validate ResetConfiguration if there is any
if kubeadmutil.GroupVersionKindsHasResetConfiguration(gvks...) {
if _, err := documentMapToResetConfiguration(gvkmap, true, allowExperimental, true, true); err != nil {
if _, err := documentMapToResetConfiguration(gvkmap, allowDeprecated, allowExperimental, true, true); err != nil {
return err
}
}
// Validate UpgradeConfiguration if there is any
if kubeadmutil.GroupVersionKindsHasUpgradeConfiguration(gvks...) {
if _, err := documentMapToUpgradeConfiguration(gvkmap, allowDeprecated, allowExperimental, true); err != nil {
return err
}
}

View File

@@ -726,7 +726,7 @@ func TestValidateConfig(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
err := ValidateConfig([]byte(test.cfg), test.allowExperimental)
err := ValidateConfig([]byte(test.cfg), true, test.allowExperimental)
if (err != nil) != test.expectedError {
t.Fatalf("expected error: %v, got: %v, error: %v", test.expectedError, (err != nil), err)
}

View File

@@ -34,7 +34,7 @@ import (
// documentMapToUpgradeConfiguration takes a map between GVKs and YAML/JSON documents (as returned by SplitYAMLDocuments),
// finds a UpgradeConfiguration, decodes it, dynamically defaults it and then validates it prior to return.
func documentMapToUpgradeConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated bool) (*kubeadmapi.UpgradeConfiguration, error) {
func documentMapToUpgradeConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated, allowExperimental, strictErrors bool) (*kubeadmapi.UpgradeConfiguration, error) {
upgradeBytes := []byte{}
for gvk, bytes := range gvkmap {
@@ -44,13 +44,17 @@ func documentMapToUpgradeConfiguration(gvkmap kubeadmapi.DocumentMap, allowDepre
}
// check if this version is supported and possibly not deprecated
if err := validateSupportedVersion(gvk, allowDeprecated, true); err != nil {
if err := validateSupportedVersion(gvk, allowDeprecated, allowExperimental); err != nil {
return nil, err
}
// verify the validity of the YAML/JSON
if err := strict.VerifyUnmarshalStrict([]*runtime.Scheme{kubeadmscheme.Scheme}, gvk, bytes); err != nil {
klog.Warning(err.Error())
if !strictErrors {
klog.Warning(err.Error())
} else {
return nil, err
}
}
upgradeBytes = bytes
@@ -107,7 +111,7 @@ func BytesToUpgradeConfiguration(b []byte) (*kubeadmapi.UpgradeConfiguration, er
return nil, err
}
return documentMapToUpgradeConfiguration(gvkmap, false)
return documentMapToUpgradeConfiguration(gvkmap, false, false, false)
}
// LoadOrDefaultUpgradeConfiguration takes a path to a config file and a versioned configuration that can serve as the default config