From f24bf00d172c0e58fc71513fdaf8ca57d1e5696f Mon Sep 17 00:00:00 2001 From: Kostiantyn Kalynovskyi Date: Tue, 16 Mar 2021 18:24:03 +0000 Subject: [PATCH] Get-kubeconfig return all cluster contexts Now if we run `airshipctl cluster get-kubeconfig` without args it will return kubeconfig for the entire site, which will have contexts for every cluster defined in cluster map. Relates-To: #460 Closes: #460 Change-Id: Icf1f09724a5c60ac520b1dbcbda69c3280ac4c7e --- cmd/cluster/get_kubeconfig.go | 27 ++++++++---- cmd/cluster/get_kubeconfig_test.go | 42 +++++++++++++++++++ ...luster-get-kubeconfig-cmd-with-help.golden | 9 +++- .../cli/airshipctl_cluster_get-kubeconfig.md | 9 +++- pkg/cluster/command.go | 11 +++-- 5 files changed, 85 insertions(+), 13 deletions(-) diff --git a/cmd/cluster/get_kubeconfig.go b/cmd/cluster/get_kubeconfig.go index c67673f88..8e215218b 100644 --- a/cmd/cluster/get_kubeconfig.go +++ b/cmd/cluster/get_kubeconfig.go @@ -24,31 +24,42 @@ import ( const ( getKubeconfigLong = ` Retrieve cluster kubeconfig and print it to stdout +If you specify clusterName, kubeconfig will have a CurrentContext set to clusterName and +will have this context defined +If you don't specify clusterName, kubeconfig will have multiple contexts for every cluster +in the airship site. Context names will correspond to cluster names. CurrentContext will be empty ` getKubeconfigExample = ` # Retrieve target-cluster kubeconfig airshipctl cluster get-kubeconfig target-cluster + +# Retrieve kubeconfig for the entire site; the kubeconfig will have context for every cluster +airshipctl cluster get-kubeconfig ` ) // NewGetKubeconfigCommand creates a command which retrieves cluster kubeconfig func NewGetKubeconfigCommand(cfgFactory config.Factory) *cobra.Command { + opts := &cluster.GetKubeconfigCommand{} cmd := &cobra.Command{ - Use: "get-kubeconfig [cluster_name]", + Use: "get-kubeconfig [clusterName]", Short: "Retrieve kubeconfig for a desired cluster", Long: getKubeconfigLong[1:], + Args: GetKubeconfArgs(opts), Example: getKubeconfigExample[1:], - Args: cobra.ExactArgs(1), - RunE: getKubeconfigRunE(cfgFactory), + RunE: func(cmd *cobra.Command, args []string) error { + return opts.RunE(cfgFactory, cmd.OutOrStdout()) + }, } - return cmd } -// getKubeconfigRunE returns a function to cobra command to be executed in runtime -func getKubeconfigRunE(cfgFactory config.Factory) func( - cmd *cobra.Command, args []string) error { +// GetKubeconfArgs extracts one or less arguments from command line, and saves it as name +func GetKubeconfArgs(opts *cluster.GetKubeconfigCommand) cobra.PositionalArgs { return func(cmd *cobra.Command, args []string) error { - return cluster.GetKubeconfig(cfgFactory, args[0], cmd.OutOrStdout()) + if len(args) == 1 { + opts.ClusterName = args[0] + } + return cobra.MaximumNArgs(1)(cmd, args) } } diff --git a/cmd/cluster/get_kubeconfig_test.go b/cmd/cluster/get_kubeconfig_test.go index 0ad252961..9a6accd13 100644 --- a/cmd/cluster/get_kubeconfig_test.go +++ b/cmd/cluster/get_kubeconfig_test.go @@ -17,7 +17,11 @@ package cluster_test import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "opendev.org/airship/airshipctl/cmd/cluster" + pkgcluster "opendev.org/airship/airshipctl/pkg/cluster" "opendev.org/airship/airshipctl/testutil" ) @@ -33,3 +37,41 @@ func TestNewKubeConfigCommandCmd(t *testing.T) { testutil.RunTest(t, testcase) } } + +func TestGetKubeconfArgs(t *testing.T) { + tests := []struct { + name string + args []string + expectedErrStr string + expectedClusterName string + }{ + { + name: "success one cluster specified", + args: []string{"cluster01"}, + expectedClusterName: "cluster01", + }, + { + name: "success no cluster specified", + }, + { + name: "error two cluster specified", + expectedErrStr: "accepts at most 1 arg(s)", + args: []string{"cluster01", "cluster02"}, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + cmd := &pkgcluster.GetKubeconfigCommand{} + args := cluster.GetKubeconfArgs(cmd) + err := args(cluster.NewGetKubeconfigCommand(nil), tt.args) + if tt.expectedErrStr != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.expectedErrStr) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedClusterName, cmd.ClusterName) + } + }) + } +} diff --git a/cmd/cluster/testdata/TestNewKubeConfigCommandCmdGoldenOutput/cluster-get-kubeconfig-cmd-with-help.golden b/cmd/cluster/testdata/TestNewKubeConfigCommandCmdGoldenOutput/cluster-get-kubeconfig-cmd-with-help.golden index 6b7af28a2..5006793d0 100644 --- a/cmd/cluster/testdata/TestNewKubeConfigCommandCmdGoldenOutput/cluster-get-kubeconfig-cmd-with-help.golden +++ b/cmd/cluster/testdata/TestNewKubeConfigCommandCmdGoldenOutput/cluster-get-kubeconfig-cmd-with-help.golden @@ -1,12 +1,19 @@ Retrieve cluster kubeconfig and print it to stdout +If you specify clusterName, kubeconfig will have a CurrentContext set to clusterName and +will have this context defined +If you don't specify clusterName, kubeconfig will have multiple contexts for every cluster +in the airship site. Context names will correspond to cluster names. CurrentContext will be empty Usage: - get-kubeconfig [cluster_name] [flags] + get-kubeconfig [clusterName] [flags] Examples: # Retrieve target-cluster kubeconfig airshipctl cluster get-kubeconfig target-cluster +# Retrieve kubeconfig for the entire site; the kubeconfig will have context for every cluster +airshipctl cluster get-kubeconfig + Flags: -h, --help help for get-kubeconfig diff --git a/docs/source/cli/airshipctl_cluster_get-kubeconfig.md b/docs/source/cli/airshipctl_cluster_get-kubeconfig.md index 641f76e83..dfe55b4ca 100644 --- a/docs/source/cli/airshipctl_cluster_get-kubeconfig.md +++ b/docs/source/cli/airshipctl_cluster_get-kubeconfig.md @@ -5,10 +5,14 @@ Retrieve kubeconfig for a desired cluster ### Synopsis Retrieve cluster kubeconfig and print it to stdout +If you specify clusterName, kubeconfig will have a CurrentContext set to clusterName and +will have this context defined +If you don't specify clusterName, kubeconfig will have multiple contexts for every cluster +in the airship site. Context names will correspond to cluster names. CurrentContext will be empty ``` -airshipctl cluster get-kubeconfig [cluster_name] [flags] +airshipctl cluster get-kubeconfig [clusterName] [flags] ``` ### Examples @@ -17,6 +21,9 @@ airshipctl cluster get-kubeconfig [cluster_name] [flags] # Retrieve target-cluster kubeconfig airshipctl cluster get-kubeconfig target-cluster +# Retrieve kubeconfig for the entire site; the kubeconfig will have context for every cluster +airshipctl cluster get-kubeconfig + ``` ### Options diff --git a/pkg/cluster/command.go b/pkg/cluster/command.go index 5cfab2f07..153cae77c 100755 --- a/pkg/cluster/command.go +++ b/pkg/cluster/command.go @@ -56,8 +56,13 @@ func StatusRunner(o StatusOptions, w io.Writer) error { return nil } -// GetKubeconfig creates new kubeconfig interface object from secret and prints its content to writer -func GetKubeconfig(cfgFactory config.Factory, clusterName string, writer io.Writer) error { +// GetKubeconfigCommand holds options for get kubeconfig command +type GetKubeconfigCommand struct { + ClusterName string +} + +// RunE creates new kubeconfig interface object from secret and prints its content to writer +func (cmd *GetKubeconfigCommand) RunE(cfgFactory config.Factory, writer io.Writer) error { cfg, err := cfgFactory() if err != nil { return err @@ -87,7 +92,7 @@ func GetKubeconfig(cfgFactory config.Factory, clusterName string, writer io.Writ WithBundle(helper.PhaseBundleRoot()). WithClusterctClient(client). WithClusterMap(cMap). - WithClusterName(clusterName). + WithClusterName(cmd.ClusterName). WithTempRoot(wd). Build()