From 381fd51e132cda458aadaee601218072de86f98f Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Mon, 21 Sep 2020 15:42:54 -0500 Subject: [PATCH] Refactor get_kubeconfig.go. Signed-off-by: Matt Moyer --- cmd/pinniped/cmd/get_kubeconfig.go | 174 ++---- cmd/pinniped/cmd/get_kubeconfig_test.go | 795 +++++++----------------- 2 files changed, 261 insertions(+), 708 deletions(-) diff --git a/cmd/pinniped/cmd/get_kubeconfig.go b/cmd/pinniped/cmd/get_kubeconfig.go index 2acb5282..7992f087 100644 --- a/cmd/pinniped/cmd/get_kubeconfig.go +++ b/cmd/pinniped/cmd/get_kubeconfig.go @@ -27,51 +27,40 @@ import ( "go.pinniped.dev/internal/here" ) -const ( - getKubeConfigCmdTokenFlagName = "token" - getKubeConfigCmdKubeconfigFlagName = "kubeconfig" - getKubeConfigCmdKubeconfigContextFlagName = "kubeconfig-context" - getKubeConfigCmdPinnipedNamespaceFlagName = "pinniped-namespace" -) - //nolint: gochecknoinits func init() { - rootCmd.AddCommand(newGetKubeConfigCmd(os.Args, os.Stdout, os.Stderr).cmd) + rootCmd.AddCommand(newGetKubeConfigCommand().Command()) +} + +type getKubeConfigFlags struct { + token string + kubeconfig string + contextOverride string + namespace string } type getKubeConfigCommand struct { - // runFunc is called by the cobra.Command.Run hook. It is included here for - // testability. - runFunc func( - stdout, stderr io.Writer, - token, kubeconfigPathOverride, currentContextOverride, pinnipedInstallationNamespace string, - ) - - // cmd is the cobra.Command for this CLI command. It is included here for - // testability. - cmd *cobra.Command + flags getKubeConfigFlags + // Test mocking points + getPathToSelf func() (string, error) + kubeClientCreator func(restConfig *rest.Config) (pinnipedclientset.Interface, error) } -func newGetKubeConfigCmd(args []string, stdout, stderr io.Writer) *getKubeConfigCommand { - c := &getKubeConfigCommand{ - runFunc: runGetKubeConfig, - } - - c.cmd = &cobra.Command{ - Run: func(cmd *cobra.Command, _ []string) { - token := cmd.Flag(getKubeConfigCmdTokenFlagName).Value.String() - kubeconfigPathOverride := cmd.Flag(getKubeConfigCmdKubeconfigFlagName).Value.String() - currentContextOverride := cmd.Flag(getKubeConfigCmdKubeconfigContextFlagName).Value.String() - pinnipedInstallationNamespace := cmd.Flag(getKubeConfigCmdPinnipedNamespaceFlagName).Value.String() - c.runFunc( - stdout, - stderr, - token, - kubeconfigPathOverride, - currentContextOverride, - pinnipedInstallationNamespace, - ) +func newGetKubeConfigCommand() *getKubeConfigCommand { + return &getKubeConfigCommand{ + flags: getKubeConfigFlags{ + namespace: "pinniped", }, + 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", @@ -93,94 +82,40 @@ func newGetKubeConfigCmd(args []string, stdout, stderr io.Writer) *getKubeConfig kubectl --kubeconfig $HOME/mycluster-kubeconfig get pods `), } - - c.cmd.SetArgs(args) - c.cmd.SetOut(stdout) - c.cmd.SetErr(stderr) - - c.cmd.Flags().StringP( - getKubeConfigCmdTokenFlagName, - "", - "", - "Credential to include in the resulting kubeconfig output (Required)", - ) - err := c.cmd.MarkFlagRequired(getKubeConfigCmdTokenFlagName) + cmd.Flags().StringVar(&c.flags.token, "token", "", "Credential to include in the resulting kubeconfig output (Required)") + err := cmd.MarkFlagRequired("token") if err != nil { panic(err) } - - c.cmd.Flags().StringP( - getKubeConfigCmdKubeconfigFlagName, - "", - "", - "Path to the kubeconfig file", - ) - - c.cmd.Flags().StringP( - getKubeConfigCmdKubeconfigContextFlagName, - "", - "", - "Kubeconfig context override", - ) - - c.cmd.Flags().StringP( - getKubeConfigCmdPinnipedNamespaceFlagName, - "", - "pinniped", - "Namespace in which Pinniped was installed", - ) - - return c + 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") + return cmd } -func runGetKubeConfig( - stdout, stderr io.Writer, - token, kubeconfigPathOverride, currentContextOverride, pinnipedInstallationNamespace string, -) { - err := getKubeConfig( - stdout, - stderr, - token, - kubeconfigPathOverride, - currentContextOverride, - pinnipedInstallationNamespace, - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { - return pinnipedclientset.NewForConfig(restConfig) - }, - ) - - if err != nil { - _, _ = fmt.Fprintf(os.Stderr, "error: %s\n", err.Error()) - os.Exit(1) - } -} - -func getKubeConfig( - outputWriter io.Writer, - warningsWriter io.Writer, - token string, - kubeconfigPathOverride string, - currentContextNameOverride string, - pinnipedInstallationNamespace string, - kubeClientCreator func(restConfig *rest.Config) (pinnipedclientset.Interface, error), -) error { - if token == "" { - return constable.Error("--" + getKubeConfigCmdTokenFlagName + " flag value cannot be empty") - } - - fullPathToSelf, err := os.Executable() +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(kubeconfigPathOverride, currentContextNameOverride) + clientConfig := newClientConfig(c.flags.kubeconfig, c.flags.contextOverride) currentKubeConfig, err := clientConfig.RawConfig() if err != nil { return err } - credentialIssuerConfig, err := fetchPinnipedCredentialIssuerConfig(clientConfig, kubeClientCreator, pinnipedInstallationNamespace) + restConfig, err := clientConfig.ClientConfig() + if err != nil { + return err + } + clientset, err := c.kubeClientCreator(restConfig) + if err != nil { + return err + } + + credentialIssuerConfig, err := fetchPinnipedCredentialIssuerConfig(clientset, c.flags.namespace) if err != nil { return err } @@ -189,19 +124,19 @@ func getKubeConfig( return constable.Error(`CredentialIssuerConfig "pinniped-config" was missing KubeConfigInfo`) } - v1Cluster, err := copyCurrentClusterFromExistingKubeConfig(currentKubeConfig, currentContextNameOverride) + v1Cluster, err := copyCurrentClusterFromExistingKubeConfig(currentKubeConfig, c.flags.contextOverride) if err != nil { return err } - err = issueWarningForNonMatchingServerOrCA(v1Cluster, credentialIssuerConfig, warningsWriter) + err = issueWarningForNonMatchingServerOrCA(v1Cluster, credentialIssuerConfig, cmd.ErrOrStderr()) if err != nil { return err } - config := newPinnipedKubeconfig(v1Cluster, fullPathToSelf, token, pinnipedInstallationNamespace) + config := newPinnipedKubeconfig(v1Cluster, fullPathToSelf, c.flags.token, c.flags.namespace) - err = writeConfigAsYAML(outputWriter, config) + err = writeConfigAsYAML(cmd.OutOrStdout(), config) if err != nil { return err } @@ -224,16 +159,7 @@ func issueWarningForNonMatchingServerOrCA(v1Cluster v1.Cluster, credentialIssuer return nil } -func fetchPinnipedCredentialIssuerConfig(clientConfig clientcmd.ClientConfig, kubeClientCreator func(restConfig *rest.Config) (pinnipedclientset.Interface, error), pinnipedInstallationNamespace string) (*configv1alpha1.CredentialIssuerConfig, error) { - restConfig, err := clientConfig.ClientConfig() - if err != nil { - return nil, err - } - clientset, err := kubeClientCreator(restConfig) - if err != nil { - return nil, err - } - +func fetchPinnipedCredentialIssuerConfig(clientset pinnipedclientset.Interface, pinnipedInstallationNamespace string) (*configv1alpha1.CredentialIssuerConfig, error) { ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*20) defer cancelFunc() diff --git a/cmd/pinniped/cmd/get_kubeconfig_test.go b/cmd/pinniped/cmd/get_kubeconfig_test.go index 817fbd4f..d8bb7232 100644 --- a/cmd/pinniped/cmd/get_kubeconfig_test.go +++ b/cmd/pinniped/cmd/get_kubeconfig_test.go @@ -7,16 +7,15 @@ import ( "bytes" "encoding/base64" "fmt" - "io" - "os" + "strings" "testing" - "github.com/sclevine/spec" - "github.com/sclevine/spec/report" + "github.com/spf13/cobra" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" + coretesting "k8s.io/client-go/testing" configv1alpha1 "go.pinniped.dev/generated/1.19/apis/config/v1alpha1" pinnipedclientset "go.pinniped.dev/generated/1.19/client/clientset/versioned" @@ -68,149 +67,60 @@ var ( ) func TestNewGetKubeConfigCmd(t *testing.T) { - spec.Run(t, "newGetKubeConfigCmd", func(t *testing.T, when spec.G, it spec.S) { - var r *require.Assertions - var stdout, stderr *bytes.Buffer + t.Parallel() + 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) { + t.Parallel() + cmd := newGetKubeConfigCommand().Command() + require.NotNil(t, cmd) - it.Before(func() { - r = require.New(t) - - stdout, stderr = bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}) + 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") }) - - it("passes all flags to runFunc", func() { - args := []string{ - "--token", "some-token", - "--kubeconfig", "some-kubeconfig", - "--kubeconfig-context", "some-kubeconfig-context", - "--pinniped-namespace", "some-pinniped-namespace", - } - c := newGetKubeConfigCmd(args, stdout, stderr) - - runFuncCalled := false - c.runFunc = func( - out, err io.Writer, - token, kubeconfigPathOverride, currentContextOverride, pinnipedInstallationNamespace string, - ) { - runFuncCalled = true - r.Equal("some-token", token) - r.Equal("some-kubeconfig", kubeconfigPathOverride) - r.Equal("some-kubeconfig-context", currentContextOverride) - r.Equal("some-pinniped-namespace", pinnipedInstallationNamespace) - } - - r.NoError(c.cmd.Execute()) - r.True(runFuncCalled) - r.Empty(stdout.String()) - r.Empty(stderr.String()) - }) - - it("requires the 'token' flag", func() { - args := []string{ - "--kubeconfig", "some-kubeconfig", - "--kubeconfig-context", "some-kubeconfig-context", - "--pinniped-namespace", "some-pinniped-namespace", - } - c := newGetKubeConfigCmd(args, stdout, stderr) - - runFuncCalled := false - c.runFunc = func( - out, err io.Writer, - token, kubeconfigPathOverride, currentContextOverride, pinnipedInstallationNamespace string, - ) { - runFuncCalled = true - } - - errorMessage := `required flag(s) "token" not set` - r.EqualError(c.cmd.Execute(), errorMessage) - r.False(runFuncCalled) - - output := "Error: " + errorMessage + "\n" + knownGoodUsageForGetKubeConfig - r.Equal(output, stdout.String()) - r.Empty(stderr.String()) - }) - - it("defaults the flags correctly", func() { - args := []string{ - "--token", "some-token", - } - c := newGetKubeConfigCmd(args, stdout, stderr) - - runFuncCalled := false - c.runFunc = func( - out, err io.Writer, - token, kubeconfigPathOverride, currentContextOverride, pinnipedInstallationNamespace string, - ) { - runFuncCalled = true - r.Equal("some-token", token) - r.Equal("", kubeconfigPathOverride) - r.Equal("", currentContextOverride) - r.Equal("pinniped", pinnipedInstallationNamespace) - } - - r.NoError(c.cmd.Execute()) - r.True(runFuncCalled) - r.Empty(stdout.String()) - r.Empty(stderr.String()) - }) - - it("fails when args are passed", func() { - args := []string{ - "--token", "some-token", - "some-arg", - } - c := newGetKubeConfigCmd(args, stdout, stderr) - - runFuncCalled := false - c.runFunc = func( - out, err io.Writer, - token, kubeconfigPathOverride, currentContextOverride, pinnipedInstallationNamespace string, - ) { - runFuncCalled = true - } - - errorMessage := `unknown command "some-arg" for "get-kubeconfig"` - r.EqualError(c.cmd.Execute(), errorMessage) - r.False(runFuncCalled) - - output := "Error: " + errorMessage + "\n" + knownGoodUsageForGetKubeConfig - r.Equal(output, stdout.String()) - r.Empty(stderr.String()) - }) - - it("prints a nice help message", func() { - args := []string{ - "--help", - } - c := newGetKubeConfigCmd(args, stdout, stderr) - - runFuncCalled := false - c.runFunc = func( - out, err io.Writer, - token, kubeconfigPathOverride, currentContextOverride, pinnipedInstallationNamespace string, - ) { - runFuncCalled = true - } - - r.NoError(c.cmd.Execute()) - r.False(runFuncCalled) - r.Equal(knownGoodHelpForGetKubeConfig, stdout.String()) - r.Empty(stderr.String()) - }) - }, spec.Parallel(), spec.Report(report.Terminal{})) + } } -func expectedKubeconfigYAML( - clusterCAData, - clusterServer, - command, - // nolint: unparam // Pass in the token even if it is always the same in practice - token, - pinnipedEndpoint, - pinnipedCABundle, - // nolint: unparam // Pass in the namespace even if it is always the same in practice - namespace string, -) string { +type expectedKubeconfigYAML struct { + clusterCAData string + clusterServer string + command string + token string + pinnipedEndpoint string + pinnipedCABundle string + namespace string +} + +func (e expectedKubeconfigYAML) String() string { return here.Docf(` apiVersion: v1 clusters: @@ -246,16 +156,10 @@ func expectedKubeconfigYAML( installHint: |- The Pinniped CLI is required to authenticate to the current cluster. For more information, please visit https://pinniped.dev - `, clusterCAData, clusterServer, command, pinnipedEndpoint, pinnipedCABundle, namespace, token) + `, e.clusterCAData, e.clusterServer, e.command, e.pinnipedEndpoint, e.pinnipedCABundle, e.namespace, e.token) } -func newCredentialIssuerConfig( - name, - //nolint: unparam // Pass in the namespace even if it is always the same in practice - namespace, - server, - certificateAuthorityData string, -) *configv1alpha1.CredentialIssuerConfig { +func newCredentialIssuerConfig(name, namespace, server, certificateAuthorityData string) *configv1alpha1.CredentialIssuerConfig { return &configv1alpha1.CredentialIssuerConfig{ TypeMeta: metav1.TypeMeta{ Kind: "CredentialIssuerConfig", @@ -274,439 +178,162 @@ func newCredentialIssuerConfig( } } -func TestGetKubeConfig(t *testing.T) { - spec.Run(t, "cmd.getKubeConfig", func(t *testing.T, when spec.G, it spec.S) { - var r *require.Assertions - var outputBuffer *bytes.Buffer - var warningsBuffer *bytes.Buffer - var fullPathToSelf string - var pinnipedClient *pinnipedfake.Clientset - const installationNamespace = "some-namespace" +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 CredentialIssuerConfigs", + mocks: func(cmd *getKubeConfigCommand) { + clientset := pinnipedfake.NewSimpleClientset() + clientset.PrependReactor("*", "*", func(_ coretesting.Action) (bool, runtime.Object, error) { + return true, nil, fmt.Errorf("some error getting CredentialIssuerConfigs") + }) + cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { + return clientset, nil + } + }, + wantError: "some error getting CredentialIssuerConfigs", + }, + { + name: "zero CredentialIssuerConfigs found", + mocks: func(cmd *getKubeConfigCommand) { + cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { + return pinnipedfake.NewSimpleClientset( + newCredentialIssuerConfig("pinniped-config-1", "not-the-test-namespace", "", ""), + ), nil + } + }, + wantError: `No CredentialIssuerConfig was found in namespace "test-namespace". Is Pinniped installed on this cluster in namespace "test-namespace"?`, + }, + { + name: "multiple CredentialIssuerConfigs found", + mocks: func(cmd *getKubeConfigCommand) { + cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { + return pinnipedfake.NewSimpleClientset( + newCredentialIssuerConfig("pinniped-config-1", "test-namespace", "", ""), + newCredentialIssuerConfig("pinniped-config-2", "test-namespace", "", ""), + ), nil + } + }, + wantError: `More than one CredentialIssuerConfig was found in namespace "test-namespace"`, + }, + { + name: "CredentialIssuerConfig missing KubeConfigInfo", + mocks: func(cmd *getKubeConfigCommand) { + cic := newCredentialIssuerConfig("pinniped-config", "test-namespace", "", "") + cic.Status.KubeConfigInfo = nil + cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { + return pinnipedfake.NewSimpleClientset(cic), nil + } + }, + wantError: `CredentialIssuerConfig "pinniped-config" was missing KubeConfigInfo`, + }, + { + name: "KubeConfigInfo has invalid base64", + mocks: func(cmd *getKubeConfigCommand) { + cic := newCredentialIssuerConfig("pinniped-config", "test-namespace", "https://example.com", "") + cic.Status.KubeConfigInfo.CertificateAuthorityData = "invalid-base64-test-ca" + cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { + return pinnipedfake.NewSimpleClientset(cic), nil + } + }, + wantError: `illegal base64 data at input byte 7`, + }, + { + name: "success using remote CA data", + mocks: func(cmd *getKubeConfigCommand) { + cic := newCredentialIssuerConfig("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(cic), 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", + }.String(), + }, + { + name: "success using local CA data", + mocks: func(cmd *getKubeConfigCommand) { + cic := newCredentialIssuerConfig("pinniped-config", "test-namespace", "https://example.com", "test-ca") + cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { + return pinnipedfake.NewSimpleClientset(cic), nil + } + }, + wantStderr: `WARNING: Server and certificate authority did not match between local kubeconfig and Pinniped's CredentialIssuerConfig 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", + }.String(), + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() - it.Before(func() { - r = require.New(t) + // 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.getPathToSelf = func() (string, error) { return "/path/to/pinniped", nil } + c.flags.kubeconfig = "./testdata/kubeconfig.yaml" + tt.mocks(c) - outputBuffer = new(bytes.Buffer) - warningsBuffer = new(bytes.Buffer) - - var err error - fullPathToSelf, err = os.Executable() - r.NoError(err) - - pinnipedClient = pinnipedfake.NewSimpleClientset() + 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") }) - - when("the CredentialIssuerConfig is found on the cluster with a configuration that matches the existing kubeconfig", func() { - it.Before(func() { - r.NoError(pinnipedClient.Tracker().Add( - newCredentialIssuerConfig( - "some-cic-name", - installationNamespace, - "https://fake-server-url-value", - "fake-certificate-authority-data-value", - ), - )) - }) - - it("writes the kubeconfig to the given writer", func() { - kubeClientCreatorFuncWasCalled := false - err := getKubeConfig(outputBuffer, - warningsBuffer, - "some-token", - "./testdata/kubeconfig.yaml", - "", - installationNamespace, - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { - kubeClientCreatorFuncWasCalled = true - r.Equal("https://fake-server-url-value", restConfig.Host) - r.Equal("fake-certificate-authority-data-value", string(restConfig.CAData)) - return pinnipedClient, nil - }, - ) - r.NoError(err) - r.True(kubeClientCreatorFuncWasCalled) - - r.Empty(warningsBuffer.String()) - r.Equal(expectedKubeconfigYAML( - base64.StdEncoding.EncodeToString([]byte("fake-certificate-authority-data-value")), - "https://fake-server-url-value", - fullPathToSelf, - "some-token", - "https://fake-server-url-value", - "fake-certificate-authority-data-value", - installationNamespace, - ), outputBuffer.String()) - }) - - when("the currentContextOverride is used to specify a context other than the default context", func() { - it.Before(func() { - // update the Server and CertificateAuthorityData to make them match the other kubeconfig context - r.NoError(pinnipedClient.Tracker().Update( - schema.GroupVersionResource{ - Group: configv1alpha1.GroupName, - Version: configv1alpha1.SchemeGroupVersion.Version, - Resource: "credentialissuerconfigs", - }, - newCredentialIssuerConfig( - "some-cic-name", - installationNamespace, - "https://some-other-fake-server-url-value", - "some-other-fake-certificate-authority-data-value", - ), - installationNamespace, - )) - }) - - when("that context exists", func() { - it("writes the kubeconfig to the given writer using the specified context", func() { - kubeClientCreatorFuncWasCalled := false - err := getKubeConfig(outputBuffer, - warningsBuffer, - "some-token", - "./testdata/kubeconfig.yaml", - "some-other-context", - installationNamespace, - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { - kubeClientCreatorFuncWasCalled = true - r.Equal("https://some-other-fake-server-url-value", restConfig.Host) - r.Equal("some-other-fake-certificate-authority-data-value", string(restConfig.CAData)) - return pinnipedClient, nil - }, - ) - r.NoError(err) - r.True(kubeClientCreatorFuncWasCalled) - - r.Empty(warningsBuffer.String()) - r.Equal(expectedKubeconfigYAML( - base64.StdEncoding.EncodeToString([]byte("some-other-fake-certificate-authority-data-value")), - "https://some-other-fake-server-url-value", - fullPathToSelf, - "some-token", - "https://some-other-fake-server-url-value", - "some-other-fake-certificate-authority-data-value", - installationNamespace, - ), outputBuffer.String()) - }) - }) - - when("that context does not exist the in the current kubeconfig", func() { - it("returns an error", func() { - err := getKubeConfig(outputBuffer, - warningsBuffer, - "some-token", - "./testdata/kubeconfig.yaml", - "this-context-name-does-not-exist-in-kubeconfig.yaml", - installationNamespace, - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { return pinnipedClient, nil }, - ) - r.EqualError(err, `context "this-context-name-does-not-exist-in-kubeconfig.yaml" does not exist`) - r.Empty(warningsBuffer.String()) - r.Empty(outputBuffer.String()) - }) - }) - }) - - when("the token passed in is empty", func() { - it("returns an error", func() { - err := getKubeConfig(outputBuffer, - warningsBuffer, - "", - "./testdata/kubeconfig.yaml", - "", - installationNamespace, - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { return pinnipedClient, nil }, - ) - r.EqualError(err, "--token flag value cannot be empty") - r.Empty(warningsBuffer.String()) - r.Empty(outputBuffer.String()) - }) - }) - - when("the kubeconfig path passed refers to a file that does not exist", func() { - it("returns an error", func() { - err := getKubeConfig(outputBuffer, - warningsBuffer, - "some-token", - "./testdata/this-file-does-not-exist.yaml", - "", - installationNamespace, - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { return pinnipedClient, nil }, - ) - r.EqualError(err, "stat ./testdata/this-file-does-not-exist.yaml: no such file or directory") - r.Empty(warningsBuffer.String()) - r.Empty(outputBuffer.String()) - }) - }) - - when("the kubeconfig path parameter is empty", func() { - it.Before(func() { - // Note that this is technically polluting other parallel tests in this file, but other tests - // are always specifying the kubeconfigPathOverride parameter, so they're not actually looking - // at the value of this environment variable. - r.NoError(os.Setenv("KUBECONFIG", "./testdata/kubeconfig.yaml")) - }) - - it.After(func() { - r.NoError(os.Unsetenv("KUBECONFIG")) - }) - - it("falls back to using the KUBECONFIG env var to find the kubeconfig file", func() { - kubeClientCreatorFuncWasCalled := false - err := getKubeConfig(outputBuffer, - warningsBuffer, - "some-token", - "", - "", - installationNamespace, - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { - kubeClientCreatorFuncWasCalled = true - r.Equal("https://fake-server-url-value", restConfig.Host) - r.Equal("fake-certificate-authority-data-value", string(restConfig.CAData)) - return pinnipedClient, nil - }, - ) - r.NoError(err) - r.True(kubeClientCreatorFuncWasCalled) - - r.Empty(warningsBuffer.String()) - r.Equal(expectedKubeconfigYAML( - base64.StdEncoding.EncodeToString([]byte("fake-certificate-authority-data-value")), - "https://fake-server-url-value", - fullPathToSelf, - "some-token", - "https://fake-server-url-value", - "fake-certificate-authority-data-value", - installationNamespace, - ), outputBuffer.String()) - }) - }) - - when("the wrong pinniped namespace is passed in", func() { - it("returns an error", func() { - kubeClientCreatorFuncWasCalled := false - err := getKubeConfig(outputBuffer, - warningsBuffer, - "some-token", - "./testdata/kubeconfig.yaml", - "", - "this-is-the-wrong-namespace", - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { - kubeClientCreatorFuncWasCalled = true - r.Equal("https://fake-server-url-value", restConfig.Host) - r.Equal("fake-certificate-authority-data-value", string(restConfig.CAData)) - return pinnipedClient, nil - }, - ) - r.EqualError(err, `No CredentialIssuerConfig was found in namespace "this-is-the-wrong-namespace". Is Pinniped installed on this cluster in namespace "this-is-the-wrong-namespace"?`) - r.True(kubeClientCreatorFuncWasCalled) - }) - }) - - when("there is more than one CredentialIssuerConfig is found on the cluster", func() { - it.Before(func() { - r.NoError(pinnipedClient.Tracker().Add( - newCredentialIssuerConfig( - "another-cic-name", - installationNamespace, - "https://fake-server-url-value", - "fake-certificate-authority-data-value", - ), - )) - }) - - it("returns an error", func() { - kubeClientCreatorFuncWasCalled := false - err := getKubeConfig(outputBuffer, - warningsBuffer, - "some-token", - "./testdata/kubeconfig.yaml", - "", - installationNamespace, - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { - kubeClientCreatorFuncWasCalled = true - r.Equal("https://fake-server-url-value", restConfig.Host) - r.Equal("fake-certificate-authority-data-value", string(restConfig.CAData)) - return pinnipedClient, nil - }, - ) - r.EqualError(err, `More than one CredentialIssuerConfig was found in namespace "some-namespace"`) - r.True(kubeClientCreatorFuncWasCalled) - }) - }) - }) - - when("the CredentialIssuerConfig is found on the cluster with a configuration that does not match the existing kubeconfig", func() { - when("the Server doesn't match", func() { - it.Before(func() { - r.NoError(pinnipedClient.Tracker().Add( - newCredentialIssuerConfig( - "some-cic-name", - installationNamespace, - "non-matching-pinniped-server-url", - "fake-certificate-authority-data-value", - ), - )) - }) - - it("writes the kubeconfig to the given writer using the values found in the local kubeconfig and issues a warning", func() { - kubeClientCreatorFuncWasCalled := false - err := getKubeConfig(outputBuffer, - warningsBuffer, - "some-token", - "./testdata/kubeconfig.yaml", - "", - installationNamespace, - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { - kubeClientCreatorFuncWasCalled = true - r.Equal("https://fake-server-url-value", restConfig.Host) - r.Equal("fake-certificate-authority-data-value", string(restConfig.CAData)) - return pinnipedClient, nil - }, - ) - r.NoError(err) - r.True(kubeClientCreatorFuncWasCalled) - - r.Equal( - "WARNING: Server and certificate authority did not match between local kubeconfig and Pinniped's CredentialIssuerConfig on the cluster. Using local kubeconfig values.\n", - warningsBuffer.String(), - ) - r.Equal(expectedKubeconfigYAML( - base64.StdEncoding.EncodeToString([]byte("fake-certificate-authority-data-value")), - "https://fake-server-url-value", - fullPathToSelf, - "some-token", - "https://fake-server-url-value", - "fake-certificate-authority-data-value", - installationNamespace, - ), outputBuffer.String()) - }) - }) - - when("the CA doesn't match", func() { - it.Before(func() { - r.NoError(pinnipedClient.Tracker().Add( - newCredentialIssuerConfig( - "some-cic-name", - installationNamespace, - "https://fake-server-url-value", - "non-matching-certificate-authority-data-value", - ), - )) - }) - - it("writes the kubeconfig to the given writer using the values found in the local kubeconfig and issues a warning", func() { - kubeClientCreatorFuncWasCalled := false - err := getKubeConfig(outputBuffer, - warningsBuffer, - "some-token", - "./testdata/kubeconfig.yaml", - "", - installationNamespace, - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { - kubeClientCreatorFuncWasCalled = true - r.Equal("https://fake-server-url-value", restConfig.Host) - r.Equal("fake-certificate-authority-data-value", string(restConfig.CAData)) - return pinnipedClient, nil - }, - ) - r.NoError(err) - r.True(kubeClientCreatorFuncWasCalled) - - r.Equal( - "WARNING: Server and certificate authority did not match between local kubeconfig and Pinniped's CredentialIssuerConfig on the cluster. Using local kubeconfig values.\n", - warningsBuffer.String(), - ) - r.Equal(expectedKubeconfigYAML( - base64.StdEncoding.EncodeToString([]byte("fake-certificate-authority-data-value")), - "https://fake-server-url-value", - fullPathToSelf, - "some-token", - "https://fake-server-url-value", - "fake-certificate-authority-data-value", - installationNamespace, - ), outputBuffer.String()) - }) - }) - }) - - when("the CredentialIssuerConfig is found on the cluster with an empty KubeConfigInfo", func() { - it.Before(func() { - r.NoError(pinnipedClient.Tracker().Add( - &configv1alpha1.CredentialIssuerConfig{ - TypeMeta: metav1.TypeMeta{ - Kind: "CredentialIssuerConfig", - APIVersion: configv1alpha1.SchemeGroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "pinniped-config", - Namespace: installationNamespace, - }, - Status: configv1alpha1.CredentialIssuerConfigStatus{}, - }, - )) - }) - - it("returns an error", func() { - kubeClientCreatorFuncWasCalled := false - err := getKubeConfig(outputBuffer, - warningsBuffer, - "some-token", - "./testdata/kubeconfig.yaml", - "", - installationNamespace, - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { - kubeClientCreatorFuncWasCalled = true - r.Equal("https://fake-server-url-value", restConfig.Host) - r.Equal("fake-certificate-authority-data-value", string(restConfig.CAData)) - return pinnipedClient, nil - }, - ) - r.True(kubeClientCreatorFuncWasCalled) - r.EqualError(err, `CredentialIssuerConfig "pinniped-config" was missing KubeConfigInfo`) - r.Empty(warningsBuffer.String()) - r.Empty(outputBuffer.String()) - }) - }) - - when("the CredentialIssuerConfig does not exist on the cluster", func() { - it("returns an error", func() { - kubeClientCreatorFuncWasCalled := false - err := getKubeConfig(outputBuffer, - warningsBuffer, - "some-token", - "./testdata/kubeconfig.yaml", - "", - installationNamespace, - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { - kubeClientCreatorFuncWasCalled = true - r.Equal("https://fake-server-url-value", restConfig.Host) - r.Equal("fake-certificate-authority-data-value", string(restConfig.CAData)) - return pinnipedClient, nil - }, - ) - r.True(kubeClientCreatorFuncWasCalled) - r.EqualError(err, `No CredentialIssuerConfig was found in namespace "some-namespace". Is Pinniped installed on this cluster in namespace "some-namespace"?`) - r.Empty(warningsBuffer.String()) - r.Empty(outputBuffer.String()) - }) - }) - - when("there is an error while getting the CredentialIssuerConfig from the cluster", func() { - it("returns an error", func() { - err := getKubeConfig(outputBuffer, - warningsBuffer, - "some-token", - "./testdata/kubeconfig.yaml", - "", - installationNamespace, - func(restConfig *rest.Config) (pinnipedclientset.Interface, error) { - return nil, fmt.Errorf("some error getting CredentialIssuerConfig") - }, - ) - r.EqualError(err, "some error getting CredentialIssuerConfig") - r.Empty(warningsBuffer.String()) - r.Empty(outputBuffer.String()) - }) - }) - }, spec.Parallel(), spec.Report(report.Terminal{})) + } }