diff --git a/cmd/pinniped/cmd/get_kubeconfig.go b/cmd/pinniped/cmd/get_kubeconfig.go deleted file mode 100644 index 9ed9bacf..00000000 --- a/cmd/pinniped/cmd/get_kubeconfig.go +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package cmd - -import ( - "bytes" - "context" - "encoding/base64" - "fmt" - "io" - "os" - "time" - - "github.com/ghodss/yaml" - "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - v1 "k8s.io/client-go/tools/clientcmd/api/v1" - - configv1alpha1 "go.pinniped.dev/generated/1.19/apis/concierge/config/v1alpha1" - pinnipedclientset "go.pinniped.dev/generated/1.19/client/concierge/clientset/versioned" - "go.pinniped.dev/internal/constable" - "go.pinniped.dev/internal/here" - "go.pinniped.dev/internal/plog" -) - -//nolint: gochecknoinits -func init() { - rootCmd.AddCommand(newGetKubeConfigCommand().Command()) -} - -type getKubeConfigFlags struct { - token string - kubeconfig string - contextOverride string - namespace string - authenticatorName string - authenticatorType string -} - -type getKubeConfigCommand struct { - flags getKubeConfigFlags - // Test mocking points - getPathToSelf func() (string, error) - kubeClientCreator func(restConfig *rest.Config) (pinnipedclientset.Interface, error) -} - -func newGetKubeConfigCommand() *getKubeConfigCommand { - return &getKubeConfigCommand{ - flags: getKubeConfigFlags{ - namespace: "pinniped-concierge", - }, - getPathToSelf: os.Executable, - kubeClientCreator: func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { - return pinnipedclientset.NewForConfig(restConfig) - }, - } -} - -func (c *getKubeConfigCommand) Command() *cobra.Command { - cmd := &cobra.Command{ - RunE: c.run, - Args: cobra.NoArgs, // do not accept positional arguments for this command - Use: "get-kubeconfig", - Short: "Print a kubeconfig for authenticating into a cluster via Pinniped", - Long: here.Doc(` - Print a kubeconfig for authenticating into a cluster via Pinniped. - - Requires admin-like access to the cluster using the current - kubeconfig context in order to access Pinniped's metadata. - The current kubeconfig is found similar to how kubectl finds it: - using the value of the --kubeconfig option, or if that is not - specified then from the value of the KUBECONFIG environment - variable, or if that is not specified then it defaults to - .kube/config in your home directory. - - Prints a kubeconfig which is suitable to access the cluster using - Pinniped as the authentication mechanism. This kubeconfig output - can be saved to a file and used with future kubectl commands, e.g.: - pinniped get-kubeconfig --token $MY_TOKEN > $HOME/mycluster-kubeconfig - kubectl --kubeconfig $HOME/mycluster-kubeconfig get pods - `), - } - cmd.Flags().StringVar(&c.flags.token, "token", "", "Credential to include in the resulting kubeconfig output (Required)") - cmd.Flags().StringVar(&c.flags.kubeconfig, "kubeconfig", c.flags.kubeconfig, "Path to the kubeconfig file") - cmd.Flags().StringVar(&c.flags.contextOverride, "kubeconfig-context", c.flags.contextOverride, "Kubeconfig context override") - cmd.Flags().StringVar(&c.flags.namespace, "pinniped-namespace", c.flags.namespace, "Namespace in which Pinniped was installed") - cmd.Flags().StringVar(&c.flags.authenticatorType, "authenticator-type", c.flags.authenticatorType, "Authenticator type (e.g., 'webhook', 'jwt')") - cmd.Flags().StringVar(&c.flags.authenticatorName, "authenticator-name", c.flags.authenticatorType, "Authenticator name") - mustMarkRequired(cmd, "token") - plog.RemoveKlogGlobalFlags() - return cmd -} - -func (c *getKubeConfigCommand) run(cmd *cobra.Command, args []string) error { - fullPathToSelf, err := c.getPathToSelf() - if err != nil { - return fmt.Errorf("could not find path to self: %w", err) - } - - clientConfig := newClientConfig(c.flags.kubeconfig, c.flags.contextOverride) - - currentKubeConfig, err := clientConfig.RawConfig() - if err != nil { - return err - } - - restConfig, err := clientConfig.ClientConfig() - if err != nil { - return err - } - clientset, err := c.kubeClientCreator(restConfig) - if err != nil { - return err - } - - authenticatorType, authenticatorName := c.flags.authenticatorType, c.flags.authenticatorName - if authenticatorType == "" || authenticatorName == "" { - authenticatorType, authenticatorName, err = getDefaultAuthenticator(clientset, c.flags.namespace) - if err != nil { - return err - } - } - - credentialIssuer, err := fetchPinnipedCredentialIssuer(clientset, c.flags.namespace) - if err != nil { - return err - } - - if credentialIssuer.Status.KubeConfigInfo == nil { - return constable.Error(`CredentialIssuer "pinniped-config" was missing KubeConfigInfo`) - } - - v1Cluster, err := copyCurrentClusterFromExistingKubeConfig(currentKubeConfig, c.flags.contextOverride) - if err != nil { - return err - } - - err = issueWarningForNonMatchingServerOrCA(v1Cluster, credentialIssuer, cmd.ErrOrStderr()) - if err != nil { - return err - } - - config := newPinnipedKubeconfig(v1Cluster, fullPathToSelf, c.flags.token, c.flags.namespace, authenticatorType, authenticatorName) - - err = writeConfigAsYAML(cmd.OutOrStdout(), config) - if err != nil { - return err - } - - return nil -} - -func issueWarningForNonMatchingServerOrCA(v1Cluster v1.Cluster, credentialIssuer *configv1alpha1.CredentialIssuer, warningsWriter io.Writer) error { - credentialIssuerCA, err := base64.StdEncoding.DecodeString(credentialIssuer.Status.KubeConfigInfo.CertificateAuthorityData) - if err != nil { - return err - } - if v1Cluster.Server != credentialIssuer.Status.KubeConfigInfo.Server || - !bytes.Equal(v1Cluster.CertificateAuthorityData, credentialIssuerCA) { - _, err := warningsWriter.Write([]byte("WARNING: Server and certificate authority did not match between local kubeconfig and Pinniped's CredentialIssuer on the cluster. Using local kubeconfig values.\n")) - if err != nil { - return fmt.Errorf("output write error: %w", err) - } - } - return nil -} - -type noAuthenticatorError struct{ Namespace string } - -func (e noAuthenticatorError) Error() string { - return fmt.Sprintf(`no authenticators were found in namespace %q`, e.Namespace) -} - -type indeterminateAuthenticatorError struct{ Namespace string } - -func (e indeterminateAuthenticatorError) Error() string { - return fmt.Sprintf( - `multiple authenticators were found in namespace %q, so --authenticator-name/--authenticator-type must be specified`, - e.Namespace, - ) -} - -func getDefaultAuthenticator(clientset pinnipedclientset.Interface, namespace string) (string, string, error) { - ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*20) - defer cancelFunc() - - webhooks, err := clientset.AuthenticationV1alpha1().WebhookAuthenticators(namespace).List(ctx, metav1.ListOptions{}) - if err != nil { - return "", "", err - } - - type ref struct{ authenticatorType, authenticatorName string } - authenticators := make([]ref, 0, len(webhooks.Items)) - for _, webhook := range webhooks.Items { - authenticators = append(authenticators, ref{authenticatorType: "webhook", authenticatorName: webhook.Name}) - } - - if len(authenticators) == 0 { - return "", "", noAuthenticatorError{namespace} - } - if len(authenticators) > 1 { - return "", "", indeterminateAuthenticatorError{namespace} - } - return authenticators[0].authenticatorType, authenticators[0].authenticatorName, nil -} - -func fetchPinnipedCredentialIssuer(clientset pinnipedclientset.Interface, pinnipedInstallationNamespace string) (*configv1alpha1.CredentialIssuer, error) { - ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*20) - defer cancelFunc() - - credentialIssuers, err := clientset.ConfigV1alpha1().CredentialIssuers(pinnipedInstallationNamespace).List(ctx, metav1.ListOptions{}) - if err != nil { - return nil, err - } - - if len(credentialIssuers.Items) == 0 { - return nil, constable.Error(fmt.Sprintf( - `No CredentialIssuer was found in namespace "%s". Is Pinniped installed on this cluster in namespace "%s"?`, - pinnipedInstallationNamespace, - pinnipedInstallationNamespace, - )) - } - - if len(credentialIssuers.Items) > 1 { - return nil, constable.Error(fmt.Sprintf( - `More than one CredentialIssuer was found in namespace "%s"`, - pinnipedInstallationNamespace, - )) - } - - return &credentialIssuers.Items[0], nil -} - -func newClientConfig(kubeconfigPathOverride string, currentContextName string) clientcmd.ClientConfig { - loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - loadingRules.ExplicitPath = kubeconfigPathOverride - clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{ - CurrentContext: currentContextName, - }) - return clientConfig -} - -func writeConfigAsYAML(outputWriter io.Writer, config v1.Config) error { - output, err := yaml.Marshal(&config) - if err != nil { - return fmt.Errorf("YAML serialization error: %w", err) - } - - _, err = outputWriter.Write(output) - if err != nil { - return fmt.Errorf("output write error: %w", err) - } - - return nil -} - -func copyCurrentClusterFromExistingKubeConfig(currentKubeConfig clientcmdapi.Config, currentContextNameOverride string) (v1.Cluster, error) { - v1Cluster := v1.Cluster{} - - contextName := currentKubeConfig.CurrentContext - if currentContextNameOverride != "" { - contextName = currentContextNameOverride - } - - err := v1.Convert_api_Cluster_To_v1_Cluster( - currentKubeConfig.Clusters[currentKubeConfig.Contexts[contextName].Cluster], - &v1Cluster, - nil, - ) - if err != nil { - return v1.Cluster{}, err - } - - return v1Cluster, nil -} - -func newPinnipedKubeconfig(v1Cluster v1.Cluster, fullPathToSelf string, token string, namespace string, authenticatorType string, authenticatorName string) v1.Config { - clusterName := "pinniped-cluster" - userName := "pinniped-user" - - return v1.Config{ - Kind: "Config", - APIVersion: v1.SchemeGroupVersion.Version, - Preferences: v1.Preferences{}, - Clusters: []v1.NamedCluster{ - { - Name: clusterName, - Cluster: v1Cluster, - }, - }, - Contexts: []v1.NamedContext{ - { - Name: clusterName, - Context: v1.Context{ - Cluster: clusterName, - AuthInfo: userName, - }, - }, - }, - AuthInfos: []v1.NamedAuthInfo{ - { - Name: userName, - AuthInfo: v1.AuthInfo{ - Exec: &v1.ExecConfig{ - Command: fullPathToSelf, - Args: []string{"exchange-credential"}, - Env: []v1.ExecEnvVar{ - { - Name: "PINNIPED_K8S_API_ENDPOINT", - Value: v1Cluster.Server, - }, - { - Name: "PINNIPED_CA_BUNDLE", - Value: string(v1Cluster.CertificateAuthorityData)}, - { - Name: "PINNIPED_NAMESPACE", - Value: namespace, - }, - { - Name: "PINNIPED_TOKEN", - Value: token, - }, - { - Name: "PINNIPED_AUTHENTICATOR_TYPE", - Value: authenticatorType, - }, - { - Name: "PINNIPED_AUTHENTICATOR_NAME", - Value: authenticatorName, - }, - }, - APIVersion: clientauthenticationv1beta1.SchemeGroupVersion.String(), - InstallHint: "The Pinniped CLI is required to authenticate to the current cluster.\n" + - "For more information, please visit https://pinniped.dev", - }, - }, - }, - }, - CurrentContext: clusterName, - } -} diff --git a/cmd/pinniped/cmd/get_kubeconfig_test.go b/cmd/pinniped/cmd/get_kubeconfig_test.go deleted file mode 100644 index 40a88b90..00000000 --- a/cmd/pinniped/cmd/get_kubeconfig_test.go +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package cmd - -import ( - "bytes" - "encoding/base64" - "fmt" - "strings" - "testing" - - "github.com/spf13/cobra" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/rest" - coretesting "k8s.io/client-go/testing" - - authv1alpha "go.pinniped.dev/generated/1.19/apis/concierge/authentication/v1alpha1" - configv1alpha1 "go.pinniped.dev/generated/1.19/apis/concierge/config/v1alpha1" - pinnipedclientset "go.pinniped.dev/generated/1.19/client/concierge/clientset/versioned" - pinnipedfake "go.pinniped.dev/generated/1.19/client/concierge/clientset/versioned/fake" - "go.pinniped.dev/internal/here" -) - -var ( - knownGoodUsageForGetKubeConfig = here.Doc(` - Usage: - get-kubeconfig [flags] - - Flags: - --authenticator-name string Authenticator name - --authenticator-type string Authenticator type (e.g., 'webhook', 'jwt') - -h, --help help for get-kubeconfig - --kubeconfig string Path to the kubeconfig file - --kubeconfig-context string Kubeconfig context override - --pinniped-namespace string Namespace in which Pinniped was installed (default "pinniped-concierge") - --token string Credential to include in the resulting kubeconfig output (Required) - - `) - - knownGoodHelpForGetKubeConfig = here.Doc(` - Print a kubeconfig for authenticating into a cluster via Pinniped. - - Requires admin-like access to the cluster using the current - kubeconfig context in order to access Pinniped's metadata. - The current kubeconfig is found similar to how kubectl finds it: - using the value of the --kubeconfig option, or if that is not - specified then from the value of the KUBECONFIG environment - variable, or if that is not specified then it defaults to - .kube/config in your home directory. - - Prints a kubeconfig which is suitable to access the cluster using - Pinniped as the authentication mechanism. This kubeconfig output - can be saved to a file and used with future kubectl commands, e.g.: - pinniped get-kubeconfig --token $MY_TOKEN > $HOME/mycluster-kubeconfig - kubectl --kubeconfig $HOME/mycluster-kubeconfig get pods - - Usage: - get-kubeconfig [flags] - - Flags: - --authenticator-name string Authenticator name - --authenticator-type string Authenticator type (e.g., 'webhook', 'jwt') - -h, --help help for get-kubeconfig - --kubeconfig string Path to the kubeconfig file - --kubeconfig-context string Kubeconfig context override - --pinniped-namespace string Namespace in which Pinniped was installed (default "pinniped-concierge") - --token string Credential to include in the resulting kubeconfig output (Required) - `) -) - -func TestNewGetKubeConfigCmd(t *testing.T) { - tests := []struct { - name string - args []string - wantError bool - wantStdout string - wantStderr string - }{ - { - name: "help flag passed", - args: []string{"--help"}, - wantStdout: knownGoodHelpForGetKubeConfig, - }, - { - name: "missing required flag", - args: []string{}, - wantError: true, - wantStdout: `Error: required flag(s) "token" not set` + "\n" + knownGoodUsageForGetKubeConfig, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - cmd := newGetKubeConfigCommand().Command() - require.NotNil(t, cmd) - - var stdout, stderr bytes.Buffer - cmd.SetOut(&stdout) - cmd.SetErr(&stderr) - cmd.SetArgs(tt.args) - err := cmd.Execute() - if tt.wantError { - require.Error(t, err) - } else { - require.NoError(t, err) - } - require.Equal(t, tt.wantStdout, stdout.String(), "unexpected stdout") - require.Equal(t, tt.wantStderr, stderr.String(), "unexpected stderr") - }) - } -} - -type expectedKubeconfigYAML struct { - clusterCAData string - clusterServer string - command string - token string - pinnipedEndpoint string - pinnipedCABundle string - namespace string - authenticatorType string - authenticatorName string -} - -func (e expectedKubeconfigYAML) String() string { - return here.Docf(` - apiVersion: v1 - clusters: - - cluster: - certificate-authority-data: %s - server: %s - name: pinniped-cluster - contexts: - - context: - cluster: pinniped-cluster - user: pinniped-user - name: pinniped-cluster - current-context: pinniped-cluster - kind: Config - preferences: {} - users: - - name: pinniped-user - user: - exec: - apiVersion: client.authentication.k8s.io/v1beta1 - args: - - exchange-credential - command: %s - env: - - name: PINNIPED_K8S_API_ENDPOINT - value: %s - - name: PINNIPED_CA_BUNDLE - value: %s - - name: PINNIPED_NAMESPACE - value: %s - - name: PINNIPED_TOKEN - value: %s - - name: PINNIPED_AUTHENTICATOR_TYPE - value: %s - - name: PINNIPED_AUTHENTICATOR_NAME - value: %s - installHint: |- - The Pinniped CLI is required to authenticate to the current cluster. - For more information, please visit https://pinniped.dev - `, e.clusterCAData, e.clusterServer, e.command, e.pinnipedEndpoint, e.pinnipedCABundle, e.namespace, e.token, e.authenticatorType, e.authenticatorName) -} - -func newCredentialIssuer(name, namespace, server, certificateAuthorityData string) *configv1alpha1.CredentialIssuer { - return &configv1alpha1.CredentialIssuer{ - TypeMeta: metav1.TypeMeta{ - Kind: "CredentialIssuer", - APIVersion: configv1alpha1.SchemeGroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Status: configv1alpha1.CredentialIssuerStatus{ - KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{ - Server: server, - CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(certificateAuthorityData)), - }, - }, - } -} - -func TestRun(t *testing.T) { - t.Parallel() - tests := []struct { - name string - mocks func(*getKubeConfigCommand) - wantError string - wantStdout string - wantStderr string - }{ - { - name: "failure to get path to self", - mocks: func(cmd *getKubeConfigCommand) { - cmd.getPathToSelf = func() (string, error) { - return "", fmt.Errorf("some error getting path to self") - } - }, - wantError: "could not find path to self: some error getting path to self", - }, - { - name: "kubeconfig does not exist", - mocks: func(cmd *getKubeConfigCommand) { - cmd.flags.kubeconfig = "./testdata/does-not-exist.yaml" - }, - wantError: "stat ./testdata/does-not-exist.yaml: no such file or directory", - }, - { - name: "fail to get client", - mocks: func(cmd *getKubeConfigCommand) { - cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { - return nil, fmt.Errorf("some error configuring clientset") - } - }, - wantError: "some error configuring clientset", - }, - { - name: "fail to get authenticators", - mocks: func(cmd *getKubeConfigCommand) { - cmd.flags.authenticatorName = "" - cmd.flags.authenticatorType = "" - clientset := pinnipedfake.NewSimpleClientset() - clientset.PrependReactor("*", "*", func(_ coretesting.Action) (bool, runtime.Object, error) { - return true, nil, fmt.Errorf("some error getting authenticators") - }) - cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { - return clientset, nil - } - }, - wantError: "some error getting authenticators", - }, - { - name: "zero authenticators", - mocks: func(cmd *getKubeConfigCommand) { - cmd.flags.authenticatorName = "" - cmd.flags.authenticatorType = "" - cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { - return pinnipedfake.NewSimpleClientset(), nil - } - }, - wantError: `no authenticators were found in namespace "test-namespace"`, - }, - { - name: "multiple authenticators", - mocks: func(cmd *getKubeConfigCommand) { - cmd.flags.authenticatorName = "" - cmd.flags.authenticatorType = "" - cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { - return pinnipedfake.NewSimpleClientset( - &authv1alpha.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Namespace: "test-namespace", Name: "webhook-one"}}, - &authv1alpha.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Namespace: "test-namespace", Name: "webhook-two"}}, - ), nil - } - }, - wantError: `multiple authenticators were found in namespace "test-namespace", so --authenticator-name/--authenticator-type must be specified`, - }, - { - name: "fail to get CredentialIssuers", - mocks: func(cmd *getKubeConfigCommand) { - clientset := pinnipedfake.NewSimpleClientset() - clientset.PrependReactor("*", "*", func(_ coretesting.Action) (bool, runtime.Object, error) { - return true, nil, fmt.Errorf("some error getting CredentialIssuers") - }) - cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { - return clientset, nil - } - }, - wantError: "some error getting CredentialIssuers", - }, - { - name: "zero CredentialIssuers found", - mocks: func(cmd *getKubeConfigCommand) { - cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { - return pinnipedfake.NewSimpleClientset( - newCredentialIssuer("pinniped-config-1", "not-the-test-namespace", "", ""), - ), nil - } - }, - wantError: `No CredentialIssuer was found in namespace "test-namespace". Is Pinniped installed on this cluster in namespace "test-namespace"?`, - }, - { - name: "multiple CredentialIssuers found", - mocks: func(cmd *getKubeConfigCommand) { - cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { - return pinnipedfake.NewSimpleClientset( - newCredentialIssuer("pinniped-config-1", "test-namespace", "", ""), - newCredentialIssuer("pinniped-config-2", "test-namespace", "", ""), - ), nil - } - }, - wantError: `More than one CredentialIssuer was found in namespace "test-namespace"`, - }, - { - name: "CredentialIssuer missing KubeConfigInfo", - mocks: func(cmd *getKubeConfigCommand) { - ci := newCredentialIssuer("pinniped-config", "test-namespace", "", "") - ci.Status.KubeConfigInfo = nil - cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { - return pinnipedfake.NewSimpleClientset(ci), nil - } - }, - wantError: `CredentialIssuer "pinniped-config" was missing KubeConfigInfo`, - }, - { - name: "KubeConfigInfo has invalid base64", - mocks: func(cmd *getKubeConfigCommand) { - ci := newCredentialIssuer("pinniped-config", "test-namespace", "https://example.com", "") - ci.Status.KubeConfigInfo.CertificateAuthorityData = "invalid-base64-test-ca" - cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { - return pinnipedfake.NewSimpleClientset(ci), nil - } - }, - wantError: `illegal base64 data at input byte 7`, - }, - { - name: "success using remote CA data", - mocks: func(cmd *getKubeConfigCommand) { - ci := newCredentialIssuer("pinniped-config", "test-namespace", "https://fake-server-url-value", "fake-certificate-authority-data-value") - cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { - return pinnipedfake.NewSimpleClientset(ci), nil - } - }, - wantStdout: expectedKubeconfigYAML{ - clusterCAData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==", - clusterServer: "https://fake-server-url-value", - command: "/path/to/pinniped", - token: "test-token", - pinnipedEndpoint: "https://fake-server-url-value", - pinnipedCABundle: "fake-certificate-authority-data-value", - namespace: "test-namespace", - authenticatorType: "test-authenticator-type", - authenticatorName: "test-authenticator-name", - }.String(), - }, - { - name: "success using local CA data and discovered authenticator", - mocks: func(cmd *getKubeConfigCommand) { - cmd.flags.authenticatorName = "" - cmd.flags.authenticatorType = "" - - cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { - return pinnipedfake.NewSimpleClientset( - &authv1alpha.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Namespace: "test-namespace", Name: "discovered-authenticator"}}, - newCredentialIssuer("pinniped-config", "test-namespace", "https://example.com", "test-ca"), - ), nil - } - }, - wantStderr: `WARNING: Server and certificate authority did not match between local kubeconfig and Pinniped's CredentialIssuer on the cluster. Using local kubeconfig values.`, - wantStdout: expectedKubeconfigYAML{ - clusterCAData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==", - clusterServer: "https://fake-server-url-value", - command: "/path/to/pinniped", - token: "test-token", - pinnipedEndpoint: "https://fake-server-url-value", - pinnipedCABundle: "fake-certificate-authority-data-value", - namespace: "test-namespace", - authenticatorType: "webhook", - authenticatorName: "discovered-authenticator", - }.String(), - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - // Start with a default getKubeConfigCommand, set some defaults, then apply any mocks. - c := newGetKubeConfigCommand() - c.flags.token = "test-token" - c.flags.namespace = "test-namespace" - c.flags.authenticatorName = "test-authenticator-name" - c.flags.authenticatorType = "test-authenticator-type" - c.getPathToSelf = func() (string, error) { return "/path/to/pinniped", nil } - c.flags.kubeconfig = "./testdata/kubeconfig.yaml" - tt.mocks(c) - - cmd := &cobra.Command{} - var stdout, stderr bytes.Buffer - cmd.SetOut(&stdout) - cmd.SetErr(&stderr) - cmd.SetArgs([]string{}) - err := c.run(cmd, []string{}) - if tt.wantError != "" { - require.EqualError(t, err, tt.wantError) - } else { - require.NoError(t, err) - } - require.Equal(t, strings.TrimSpace(tt.wantStdout), strings.TrimSpace(stdout.String()), "unexpected stdout") - require.Equal(t, strings.TrimSpace(tt.wantStderr), strings.TrimSpace(stderr.String()), "unexpected stderr") - }) - } -} diff --git a/go.mod b/go.mod index 84237fd7..9d3d98c6 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/blang/semver v3.5.1+incompatible // indirect github.com/coreos/go-oidc v2.2.1+incompatible github.com/davecgh/go-spew v1.1.1 - github.com/ghodss/yaml v1.0.0 github.com/go-logr/logr v0.2.1 github.com/go-logr/stdr v0.2.0 github.com/gofrs/flock v0.8.0