diff --git a/pkg/apis/clientauthentication/install/install.go b/pkg/apis/clientauthentication/install/install.go index 1b7b5f94..9040bb9a 100644 --- a/pkg/apis/clientauthentication/install/install.go +++ b/pkg/apis/clientauthentication/install/install.go @@ -22,12 +22,15 @@ import ( "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/pkg/apis/clientauthentication" + "k8s.io/client-go/pkg/apis/clientauthentication/v1" "k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1" + "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" ) // Install registers the API group and adds types to a scheme func Install(scheme *runtime.Scheme) { utilruntime.Must(clientauthentication.AddToScheme(scheme)) + utilruntime.Must(v1.AddToScheme(scheme)) + utilruntime.Must(v1beta1.AddToScheme(scheme)) utilruntime.Must(v1alpha1.AddToScheme(scheme)) - utilruntime.Must(scheme.SetVersionPriority(v1alpha1.SchemeGroupVersion)) } diff --git a/plugin/pkg/client/auth/exec/exec.go b/plugin/pkg/client/auth/exec/exec.go index 0964f1d8..7a098462 100644 --- a/plugin/pkg/client/auth/exec/exec.go +++ b/plugin/pkg/client/auth/exec/exec.go @@ -35,15 +35,15 @@ import ( "github.com/davecgh/go-spew/spew" "golang.org/x/term" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/util/clock" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/pkg/apis/clientauthentication" - "k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1" - "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" + "k8s.io/client-go/pkg/apis/clientauthentication/install" + clientauthenticationv1 "k8s.io/client-go/pkg/apis/clientauthentication/v1" + clientauthenticationv1alpha1 "k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1" + clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" "k8s.io/client-go/tools/clientcmd/api" "k8s.io/client-go/tools/metrics" "k8s.io/client-go/transport" @@ -63,10 +63,7 @@ var scheme = runtime.NewScheme() var codecs = serializer.NewCodecFactory(scheme) func init() { - v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) - utilruntime.Must(v1alpha1.AddToScheme(scheme)) - utilruntime.Must(v1beta1.AddToScheme(scheme)) - utilruntime.Must(clientauthentication.AddToScheme(scheme)) + install.Install(scheme) } var ( @@ -75,8 +72,9 @@ var ( globalCache = newCache() // The list of API versions we accept. apiVersions = map[string]schema.GroupVersion{ - v1alpha1.SchemeGroupVersion.String(): v1alpha1.SchemeGroupVersion, - v1beta1.SchemeGroupVersion.String(): v1beta1.SchemeGroupVersion, + clientauthenticationv1alpha1.SchemeGroupVersion.String(): clientauthenticationv1alpha1.SchemeGroupVersion, + clientauthenticationv1beta1.SchemeGroupVersion.String(): clientauthenticationv1beta1.SchemeGroupVersion, + clientauthenticationv1.SchemeGroupVersion.String(): clientauthenticationv1.SchemeGroupVersion, } ) diff --git a/plugin/pkg/client/auth/exec/exec_test.go b/plugin/pkg/client/auth/exec/exec_test.go index ca40eb0d..e55e845d 100644 --- a/plugin/pkg/client/auth/exec/exec_test.go +++ b/plugin/pkg/client/auth/exec/exec_test.go @@ -980,6 +980,36 @@ func TestRefreshCreds(t *testing.T) { }`, wantCreds: credentials{token: "foo-bar"}, }, + { + name: "v1-basic-request", + config: api.ExecConfig{ + APIVersion: "client.authentication.k8s.io/v1", + InteractiveMode: api.IfAvailableExecInteractiveMode, + }, + wantInput: `{ + "kind": "ExecCredential", + "apiVersion": "client.authentication.k8s.io/v1", + "spec": { + "interactive": false + } + }`, + output: `{ + "kind": "ExecCredential", + "apiVersion": "client.authentication.k8s.io/v1", + "status": { + "token": "foo-bar" + } + }`, + wantCreds: credentials{token: "foo-bar"}, + }, + { + name: "v1-with-missing-interactive-mode", + config: api.ExecConfig{ + APIVersion: "client.authentication.k8s.io/v1", + }, + wantErr: true, + wantErrSubstr: `exec plugin cannot support interactive mode: unknown interactiveMode: ""`, + }, } for _, test := range tests { diff --git a/tools/auth/exec/exec.go b/tools/auth/exec/exec.go index 246de2ef..87947b0c 100644 --- a/tools/auth/exec/exec.go +++ b/tools/auth/exec/exec.go @@ -22,14 +22,11 @@ import ( "fmt" "os" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/pkg/apis/clientauthentication" - "k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1" - "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" + "k8s.io/client-go/pkg/apis/clientauthentication/install" "k8s.io/client-go/rest" ) @@ -39,10 +36,7 @@ var scheme = runtime.NewScheme() var codecs = serializer.NewCodecFactory(scheme) func init() { - metav1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) - utilruntime.Must(v1alpha1.AddToScheme(scheme)) - utilruntime.Must(v1beta1.AddToScheme(scheme)) - utilruntime.Must(clientauthentication.AddToScheme(scheme)) + install.Install(scheme) } // LoadExecCredentialFromEnv is a helper-wrapper around LoadExecCredential that loads from the @@ -68,7 +62,7 @@ func LoadExecCredentialFromEnv() (runtime.Object, *rest.Config, error) { // value. // // If the provided data is successfully unmarshalled, but it does not contain cluster information -// (i.e., ExecCredential.Spec.Cluster == nil), then the returned rest.Config and error will be nil. +// (i.e., ExecCredential.Spec.Cluster == nil), then an error will be returned. // // Note that the returned rest.Config will use anonymous authentication, since the exec plugin has // not returned credentials for this cluster yet. diff --git a/tools/auth/exec/exec_test.go b/tools/auth/exec/exec_test.go index fd1b1b0b..e1a37f70 100644 --- a/tools/auth/exec/exec_test.go +++ b/tools/auth/exec/exec_test.go @@ -24,6 +24,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + clientauthenticationv1 "k8s.io/client-go/pkg/apis/clientauthentication/v1" clientauthenticationv1alpha1 "k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1" clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" "k8s.io/client-go/rest" @@ -46,6 +47,50 @@ func TestLoadExecCredential(t *testing.T) { wantRESTInfo restInfo wantErrorPrefix string }{ + { + name: "v1 happy path", + data: marshal(t, clientauthenticationv1.SchemeGroupVersion, &clientauthenticationv1.ExecCredential{ + Spec: clientauthenticationv1.ExecCredentialSpec{ + Cluster: &clientauthenticationv1.Cluster{ + Server: "https://some-server/some/path", + TLSServerName: "some-server-name", + InsecureSkipTLSVerify: true, + CertificateAuthorityData: []byte("some-ca-data"), + ProxyURL: "https://some-proxy-url:12345", + Config: runtime.RawExtension{ + Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"names":["marshmallow","zelda"]}}`), + }, + }, + }, + }), + wantExecCredential: &clientauthenticationv1.ExecCredential{ + TypeMeta: metav1.TypeMeta{ + Kind: "ExecCredential", + APIVersion: clientauthenticationv1.SchemeGroupVersion.String(), + }, + Spec: clientauthenticationv1.ExecCredentialSpec{ + Cluster: &clientauthenticationv1.Cluster{ + Server: "https://some-server/some/path", + TLSServerName: "some-server-name", + InsecureSkipTLSVerify: true, + CertificateAuthorityData: []byte("some-ca-data"), + ProxyURL: "https://some-proxy-url:12345", + Config: runtime.RawExtension{ + Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"names":["marshmallow","zelda"]}}`), + }, + }, + }, + }, + wantRESTInfo: restInfo{ + host: "https://some-server/some/path", + tlsClientConfig: rest.TLSClientConfig{ + Insecure: true, + ServerName: "some-server-name", + CAData: []byte("some-ca-data"), + }, + proxyURL: "https://some-proxy-url:12345", + }, + }, { name: "v1beta1 happy path", data: marshal(t, clientauthenticationv1beta1.SchemeGroupVersion, &clientauthenticationv1beta1.ExecCredential{ @@ -90,6 +135,44 @@ func TestLoadExecCredential(t *testing.T) { proxyURL: "https://some-proxy-url:12345", }, }, + { + name: "v1 nil config", + data: marshal(t, clientauthenticationv1.SchemeGroupVersion, &clientauthenticationv1.ExecCredential{ + Spec: clientauthenticationv1.ExecCredentialSpec{ + Cluster: &clientauthenticationv1.Cluster{ + Server: "https://some-server/some/path", + TLSServerName: "some-server-name", + InsecureSkipTLSVerify: true, + CertificateAuthorityData: []byte("some-ca-data"), + ProxyURL: "https://some-proxy-url:12345", + }, + }, + }), + wantExecCredential: &clientauthenticationv1.ExecCredential{ + TypeMeta: metav1.TypeMeta{ + Kind: "ExecCredential", + APIVersion: clientauthenticationv1.SchemeGroupVersion.String(), + }, + Spec: clientauthenticationv1.ExecCredentialSpec{ + Cluster: &clientauthenticationv1.Cluster{ + Server: "https://some-server/some/path", + TLSServerName: "some-server-name", + InsecureSkipTLSVerify: true, + CertificateAuthorityData: []byte("some-ca-data"), + ProxyURL: "https://some-proxy-url:12345", + }, + }, + }, + wantRESTInfo: restInfo{ + host: "https://some-server/some/path", + tlsClientConfig: rest.TLSClientConfig{ + Insecure: true, + ServerName: "some-server-name", + CAData: []byte("some-ca-data"), + }, + proxyURL: "https://some-proxy-url:12345", + }, + }, { name: "v1beta1 nil config", data: marshal(t, clientauthenticationv1beta1.SchemeGroupVersion, &clientauthenticationv1beta1.ExecCredential{ @@ -128,6 +211,17 @@ func TestLoadExecCredential(t *testing.T) { proxyURL: "https://some-proxy-url:12345", }, }, + { + name: "v1 invalid cluster", + data: marshal(t, clientauthenticationv1.SchemeGroupVersion, &clientauthenticationv1.ExecCredential{ + Spec: clientauthenticationv1.ExecCredentialSpec{ + Cluster: &clientauthenticationv1.Cluster{ + ProxyURL: "invalid- url\n", + }, + }, + }), + wantErrorPrefix: "cannot create rest.Config", + }, { name: "v1beta1 invalid cluster", data: marshal(t, clientauthenticationv1beta1.SchemeGroupVersion, &clientauthenticationv1beta1.ExecCredential{ @@ -139,6 +233,11 @@ func TestLoadExecCredential(t *testing.T) { }), wantErrorPrefix: "cannot create rest.Config", }, + { + name: "v1 nil cluster", + data: marshal(t, clientauthenticationv1.SchemeGroupVersion, &clientauthenticationv1.ExecCredential{}), + wantErrorPrefix: "ExecCredential does not contain cluster information", + }, { name: "v1beta1 nil cluster", data: marshal(t, clientauthenticationv1beta1.SchemeGroupVersion, &clientauthenticationv1beta1.ExecCredential{}), diff --git a/tools/auth/exec/types_test.go b/tools/auth/exec/types_test.go index e4b95a34..3e1938ca 100644 --- a/tools/auth/exec/types_test.go +++ b/tools/auth/exec/types_test.go @@ -17,26 +17,40 @@ limitations under the License. package exec import ( + "fmt" "reflect" "testing" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/sets" + clientauthenticationv1 "k8s.io/client-go/pkg/apis/clientauthentication/v1" clientauthenticationv1alpha1 "k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1" clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" clientcmdv1 "k8s.io/client-go/tools/clientcmd/api/v1" ) -// TestV1beta1ClusterTypesAreSynced ensures that clientauthenticationv1beta1.Cluster stays in sync -// with clientcmdv1.Cluster. -// -// We want clientauthenticationv1beta1.Cluster to offer the same knobs as clientcmdv1.Cluster to -// allow someone to connect to the kubernetes API. This test should fail if a new field is added to -// one of the structs without updating the other. -func TestV1beta1ClusterTypesAreSynced(t *testing.T) { +func TestClientAuthenticationClusterTypesAreSynced(t *testing.T) { t.Parallel() - execType := reflect.TypeOf(clientauthenticationv1beta1.Cluster{}) + for _, cluster := range []interface{}{ + clientauthenticationv1beta1.Cluster{}, + clientauthenticationv1.Cluster{}, + } { + t.Run(fmt.Sprintf("%T", cluster), func(t *testing.T) { + t.Parallel() + testClientAuthenticationClusterTypesAreSynced(t, cluster) + }) + } +} + +// testClusterTypesAreSynced ensures that the provided cluster type stays in sync +// with clientcmdv1.Cluster. +// +// We want clientauthentication*.Cluster types to offer the same knobs as clientcmdv1.Cluster to +// allow someone to connect to the kubernetes API. This test should fail if a new field is added to +// one of the structs without updating the other. +func testClientAuthenticationClusterTypesAreSynced(t *testing.T, cluster interface{}) { + execType := reflect.TypeOf(cluster) clientcmdType := reflect.TypeOf(clientcmdv1.Cluster{}) t.Run("exec cluster fields match clientcmd cluster fields", func(t *testing.T) { @@ -136,6 +150,8 @@ func TestAllClusterTypesAreSynced(t *testing.T) { clientauthenticationv1alpha1.SchemeGroupVersion.Version, // We have a test for v1beta1 above. clientauthenticationv1beta1.SchemeGroupVersion.Version, + // We have a test for v1 above. + clientauthenticationv1.SchemeGroupVersion.Version, ) for gvk := range scheme.AllKnownTypes() { if gvk.Group == clientauthenticationv1beta1.SchemeGroupVersion.Group &&