diff --git a/go.mod b/go.mod index 4c53bb8e6..19dc8159e 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( k8s.io/api v0.0.0-20260227163613-d93bd39adfb7 k8s.io/apimachinery v0.0.0-20260227163238-b6415d345084 k8s.io/cli-runtime v0.0.0-20260227172404-5d8163f22d8c - k8s.io/client-go v0.0.0-20260227164107-f037d681ac6d + k8s.io/client-go v0.0.0-20260227204202-2f21ae3478f8 k8s.io/component-base v0.0.0-20260227165336-7a57b97c7f91 k8s.io/component-helpers v0.0.0-20260227165526-69b095369cfd k8s.io/klog/v2 v2.130.1 diff --git a/go.sum b/go.sum index 17bed7ef6..9e23a040a 100644 --- a/go.sum +++ b/go.sum @@ -198,8 +198,8 @@ k8s.io/apimachinery v0.0.0-20260227163238-b6415d345084 h1:vRyPwew4b3Yiq7mwOpqX+o k8s.io/apimachinery v0.0.0-20260227163238-b6415d345084/go.mod h1:zqp0Kd80VeQZHyQ4j0VW/sn9zTHYeXFd/brJe5Po20w= k8s.io/cli-runtime v0.0.0-20260227172404-5d8163f22d8c h1:PGSLHLUjVEZ4P2tgUzMmygoTqKHpiw6Aj8CGDsaB8Kw= k8s.io/cli-runtime v0.0.0-20260227172404-5d8163f22d8c/go.mod h1:KMPzbSuEmf0sYtlQb62CmYpPK5dAdHzHf0l5jqjIKhI= -k8s.io/client-go v0.0.0-20260227164107-f037d681ac6d h1:LXoO77UtpPsXm3Esw5+lF09jhyhKUeHkC8o6yjWylZg= -k8s.io/client-go v0.0.0-20260227164107-f037d681ac6d/go.mod h1:C3PeNJlRoXdZV687zm39WAzBKG9z9lGdJJp3QCoGXFQ= +k8s.io/client-go v0.0.0-20260227204202-2f21ae3478f8 h1:IcsjKdDcmQzd6ZjCrJ/0TUnEzGOleP+rEElYSMbquxI= +k8s.io/client-go v0.0.0-20260227204202-2f21ae3478f8/go.mod h1:C3PeNJlRoXdZV687zm39WAzBKG9z9lGdJJp3QCoGXFQ= k8s.io/component-base v0.0.0-20260227165336-7a57b97c7f91 h1:Q9dYHfnrOKaVd4BW9CWEGq8ICO81qOFapzf6uI6176U= k8s.io/component-base v0.0.0-20260227165336-7a57b97c7f91/go.mod h1:mZrr4Xz0IvgSYoY3ikmtftuJFNyU34HLw2q0mzSyob0= k8s.io/component-helpers v0.0.0-20260227165526-69b095369cfd h1:+UaL85CkWcxE4trdWzTglCzye+y4PomeKKyLbwCUa1g= diff --git a/pkg/config/types.go b/pkg/config/types.go index b310ceae4..3fecc679a 100644 --- a/pkg/config/types.go +++ b/pkg/config/types.go @@ -115,12 +115,12 @@ const ( // the logical AND of all checks corresponding to the specified fields within // the entry. type AllowlistEntry struct { - // Name matching is performed by first resolving the absolute path of both + // Command matching is performed by first resolving the absolute path of both // the plugin and the name in the allowlist entry using `exec.LookPath`. It // will be called on both, and the resulting strings must be equal. If - // either call to `exec.LookPath` results in an error, the `Name` check + // either call to `exec.LookPath` results in an error, the `Command` check // will be considered a failure. - Name string + Command string } // AliasOverride stores the alias definitions. diff --git a/pkg/config/v1beta1/conversion.go b/pkg/config/v1beta1/conversion.go new file mode 100644 index 000000000..a862605cc --- /dev/null +++ b/pkg/config/v1beta1/conversion.go @@ -0,0 +1,49 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "fmt" + + conversion "k8s.io/apimachinery/pkg/conversion" + config "k8s.io/kubectl/pkg/config" +) + +func Convert_config_AllowlistEntry_To_v1beta1_AllowlistEntry(in *config.AllowlistEntry, out *AllowlistEntry, s conversion.Scope) error { + return autoConvert_config_AllowlistEntry_To_v1beta1_AllowlistEntry(in, out, s) +} + +// The internal AllowlistEntry type does not have the `Name` field, which is deprecated as of v1.36. Convert `Name` to `Command` where possible, and return an error if both `Name` and `Command` are supplied. +func Convert_v1beta1_AllowlistEntry_To_config_AllowlistEntry(in *AllowlistEntry, out *config.AllowlistEntry, s conversion.Scope) error { + if err := autoConvert_v1beta1_AllowlistEntry_To_config_AllowlistEntry(in, out, s); err != nil { + return err + } + + switch { + case len(in.Name) != 0 && len(in.Command) != 0 && in.Name != in.Command: + return fmt.Errorf("both `Name` and `Command` were supplied with different values. `Name` is deprecated, use `Command` instead") + case len(in.Command) != 0: + out.Command = in.Command + case len(in.Name) != 0: + out.Command = in.Name + default: + // both `Name` and `Command` are empty, propagate the empty value and + // allow validation to catch it later since it's a validation error, not a + // conversion error + } + return nil +} diff --git a/pkg/config/v1beta1/types.go b/pkg/config/v1beta1/types.go index e1d5409a1..f019bbb37 100644 --- a/pkg/config/v1beta1/types.go +++ b/pkg/config/v1beta1/types.go @@ -121,7 +121,15 @@ type AllowlistEntry struct { // will be called on both, and the resulting strings must be equal. If // either call to `exec.LookPath` results in an error, the `Name` check // will be considered a failure. - Name string `json:"name"` + // + // Deprecated: use Command instead. + Name string `json:"name,omitempty"` + // Command matching is performed by first resolving the absolute path of both + // the plugin and the name in the allowlist entry using `exec.LookPath`. It + // will be called on both, and the resulting strings must be equal. If + // either call to `exec.LookPath` results in an error, the `Command` check + // will be considered a failure. + Command string `json:"command,omitempty"` } // AliasOverride stores the alias definitions. diff --git a/pkg/config/v1beta1/zz_generated.conversion.go b/pkg/config/v1beta1/zz_generated.conversion.go index dcbe99481..f0516ce95 100644 --- a/pkg/config/v1beta1/zz_generated.conversion.go +++ b/pkg/config/v1beta1/zz_generated.conversion.go @@ -46,16 +46,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*AllowlistEntry)(nil), (*config.AllowlistEntry)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_AllowlistEntry_To_config_AllowlistEntry(a.(*AllowlistEntry), b.(*config.AllowlistEntry), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*config.AllowlistEntry)(nil), (*AllowlistEntry)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_config_AllowlistEntry_To_v1beta1_AllowlistEntry(a.(*config.AllowlistEntry), b.(*AllowlistEntry), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*CommandDefaults)(nil), (*config.CommandDefaults)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_CommandDefaults_To_config_CommandDefaults(a.(*CommandDefaults), b.(*config.CommandDefaults), scope) }); err != nil { @@ -86,6 +76,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*config.AllowlistEntry)(nil), (*AllowlistEntry)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_config_AllowlistEntry_To_v1beta1_AllowlistEntry(a.(*config.AllowlistEntry), b.(*AllowlistEntry), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*AllowlistEntry)(nil), (*config.AllowlistEntry)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_AllowlistEntry_To_config_AllowlistEntry(a.(*AllowlistEntry), b.(*config.AllowlistEntry), scope) + }); err != nil { + return err + } return nil } @@ -118,25 +118,16 @@ func Convert_config_AliasOverride_To_v1beta1_AliasOverride(in *config.AliasOverr } func autoConvert_v1beta1_AllowlistEntry_To_config_AllowlistEntry(in *AllowlistEntry, out *config.AllowlistEntry, s conversion.Scope) error { - out.Name = in.Name + // WARNING: in.Name requires manual conversion: does not exist in peer-type + out.Command = in.Command return nil } -// Convert_v1beta1_AllowlistEntry_To_config_AllowlistEntry is an autogenerated conversion function. -func Convert_v1beta1_AllowlistEntry_To_config_AllowlistEntry(in *AllowlistEntry, out *config.AllowlistEntry, s conversion.Scope) error { - return autoConvert_v1beta1_AllowlistEntry_To_config_AllowlistEntry(in, out, s) -} - func autoConvert_config_AllowlistEntry_To_v1beta1_AllowlistEntry(in *config.AllowlistEntry, out *AllowlistEntry, s conversion.Scope) error { - out.Name = in.Name + out.Command = in.Command return nil } -// Convert_config_AllowlistEntry_To_v1beta1_AllowlistEntry is an autogenerated conversion function. -func Convert_config_AllowlistEntry_To_v1beta1_AllowlistEntry(in *config.AllowlistEntry, out *AllowlistEntry, s conversion.Scope) error { - return autoConvert_config_AllowlistEntry_To_v1beta1_AllowlistEntry(in, out, s) -} - func autoConvert_v1beta1_CommandDefaults_To_config_CommandDefaults(in *CommandDefaults, out *config.CommandDefaults, s conversion.Scope) error { out.Command = in.Command out.Options = *(*[]config.CommandOptionDefault)(unsafe.Pointer(&in.Options)) @@ -185,7 +176,17 @@ func autoConvert_v1beta1_Preference_To_config_Preference(in *Preference, out *co out.Defaults = *(*[]config.CommandDefaults)(unsafe.Pointer(&in.Defaults)) out.Aliases = *(*[]config.AliasOverride)(unsafe.Pointer(&in.Aliases)) out.CredentialPluginPolicy = config.CredentialPluginPolicy(in.CredentialPluginPolicy) - out.CredentialPluginAllowlist = *(*[]config.AllowlistEntry)(unsafe.Pointer(&in.CredentialPluginAllowlist)) + if in.CredentialPluginAllowlist != nil { + in, out := &in.CredentialPluginAllowlist, &out.CredentialPluginAllowlist + *out = make([]config.AllowlistEntry, len(*in)) + for i := range *in { + if err := Convert_v1beta1_AllowlistEntry_To_config_AllowlistEntry(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.CredentialPluginAllowlist = nil + } return nil } @@ -198,7 +199,17 @@ func autoConvert_config_Preference_To_v1beta1_Preference(in *config.Preference, out.Defaults = *(*[]CommandDefaults)(unsafe.Pointer(&in.Defaults)) out.Aliases = *(*[]AliasOverride)(unsafe.Pointer(&in.Aliases)) out.CredentialPluginPolicy = CredentialPluginPolicy(in.CredentialPluginPolicy) - out.CredentialPluginAllowlist = *(*[]AllowlistEntry)(unsafe.Pointer(&in.CredentialPluginAllowlist)) + if in.CredentialPluginAllowlist != nil { + in, out := &in.CredentialPluginAllowlist, &out.CredentialPluginAllowlist + *out = make([]AllowlistEntry, len(*in)) + for i := range *in { + if err := Convert_config_AllowlistEntry_To_v1beta1_AllowlistEntry(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.CredentialPluginAllowlist = nil + } return nil } diff --git a/pkg/kuberc/kuberc_test.go b/pkg/kuberc/kuberc_test.go index 01afec489..567ea5871 100644 --- a/pkg/kuberc/kuberc_test.go +++ b/pkg/kuberc/kuberc_test.go @@ -2978,8 +2978,8 @@ users: return &config.Preference{ CredentialPluginPolicy: config.CredentialPluginPolicy("Allowlist"), CredentialPluginAllowlist: []config.AllowlistEntry{ - {Name: "bar"}, - {Name: "baz"}, + {Command: "bar"}, + {Command: "baz"}, }, }, nil } @@ -2992,8 +2992,8 @@ users: require.NotNil(t, cfg, "rest config") require.NotNil(t, cfg.ExecProvider, "exec config") require.Equal(t, clientcmdapi.PolicyType("Allowlist"), cfg.ExecProvider.PluginPolicy.PolicyType) - require.Equal(t, "bar", cfg.ExecProvider.PluginPolicy.Allowlist[0].Name) - require.Equal(t, "baz", cfg.ExecProvider.PluginPolicy.Allowlist[1].Name) + require.Equal(t, "bar", cfg.ExecProvider.PluginPolicy.Allowlist[0].Command) + require.Equal(t, "baz", cfg.ExecProvider.PluginPolicy.Allowlist[1].Command) }) type pluginPolicyTest struct { @@ -3028,7 +3028,7 @@ kind: Preference apiVersion: kubectl.config.k8s.io/v1beta1 credentialPluginPolicy: "foo" credentialPluginAllowlist: -- name: "bar" +- command: "bar" `, }, { @@ -3057,8 +3057,8 @@ credentialPluginAllowlist: [] kind: Preference apiVersion: kubectl.config.k8s.io/v1beta1 credentialPluginAllowlist: -- name: "bar" -- name: "baz" +- command: "bar" +- command: "baz" `, }, { @@ -3069,8 +3069,8 @@ kind: Preference apiVersion: kubectl.config.k8s.io/v1beta1 credentialPluginPolicy: "AllowAll" credentialPluginAllowlist: []clientcmdapi.AllowlistEntry{ -- name: "bar" -- name: "baz" +- command: "bar" +- command: "baz" `, }, { @@ -3081,8 +3081,8 @@ kind: Preference apiVersion: kubectl.config.k8s.io/v1beta1 credentialPluginPolicy: "DenyAll" credentialPluginAllowlist: -- name: "bar" -- name: "baz" +- command: "bar" +- command: "baz" `, }, { @@ -3103,8 +3103,8 @@ kind: Preference apiVersion: kubectl.config.k8s.io/v1beta1 credentialPluginPolicy: "Allowlist" credentialPluginAllowlist: -- name: "foo" -- name: "" +- command: "foo" +- command: "" `, }, { @@ -3114,7 +3114,39 @@ credentialPluginAllowlist: kind: Preference credentialPluginPolicy: "Allowlist" credentialPluginAllowlist: +- command: "foo" +`, + }, + { + name: "allowlist-policy-name-converts-to-command", + shouldErr: false, + kuberc: `apiVersion: kubectl.config.k8s.io/v1beta1 +kind: Preference +credentialPluginPolicy: "Allowlist" +credentialPluginAllowlist: - name: "foo" +`, + }, + { + name: "allowlist-policy-with-both-name-and-command-having-different-values", + shouldErr: true, + kuberc: `apiVersion: kubectl.config.k8s.io/v1beta1 +kind: Preference +credentialPluginPolicy: "Allowlist" +credentialPluginAllowlist: +- name: "foo" + command: "bar" +`, + }, + { + name: "allowlist-policy-with-both-name-and-command-having-the-same-value", + shouldErr: false, + kuberc: `apiVersion: kubectl.config.k8s.io/v1beta1 +kind: Preference +credentialPluginPolicy: "Allowlist" +credentialPluginAllowlist: +- name: "foo" + command: "foo" `, }, {