From 36eb74a803cafd6837cd114d695d9360d9d1b07f Mon Sep 17 00:00:00 2001 From: knight42 Date: Fri, 18 Sep 2020 15:55:37 +0800 Subject: [PATCH] refactor(kubeadm): make `alpha kubeconfig user` command accpet --config Signed-off-by: knight42 --- cmd/kubeadm/app/cmd/alpha/BUILD | 2 + cmd/kubeadm/app/cmd/alpha/kubeconfig.go | 37 +++------ cmd/kubeadm/app/cmd/alpha/kubeconfig_test.go | 86 +++++++++++++++----- 3 files changed, 81 insertions(+), 44 deletions(-) diff --git a/cmd/kubeadm/app/cmd/alpha/BUILD b/cmd/kubeadm/app/cmd/alpha/BUILD index e260e9d0e5a..6aa1659f536 100644 --- a/cmd/kubeadm/app/cmd/alpha/BUILD +++ b/cmd/kubeadm/app/cmd/alpha/BUILD @@ -67,10 +67,12 @@ go_test( "//cmd/kubeadm/test:go_default_library", "//cmd/kubeadm/test/cmd:go_default_library", "//cmd/kubeadm/test/kubeconfig:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library", "//vendor/github.com/spf13/pflag:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/github.com/stretchr/testify/require:go_default_library", + "//vendor/sigs.k8s.io/yaml:go_default_library", ], ) diff --git a/cmd/kubeadm/app/cmd/alpha/kubeconfig.go b/cmd/kubeadm/app/cmd/alpha/kubeconfig.go index 389f3a72d70..aa168b85850 100644 --- a/cmd/kubeadm/app/cmd/alpha/kubeconfig.go +++ b/cmd/kubeadm/app/cmd/alpha/kubeconfig.go @@ -19,9 +19,8 @@ package alpha import ( "io" - "github.com/pkg/errors" "github.com/spf13/cobra" - kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" + kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/options" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" @@ -39,8 +38,8 @@ var ( ` + cmdutil.AlphaDisclaimer) userKubeconfigExample = cmdutil.Examples(` - # Output a kubeconfig file for an additional user named foo - kubeadm alpha kubeconfig user --client-name=foo + # Output a kubeconfig file for an additional user named foo using a kubeadm config file bar + kubeadm alpha kubeconfig user --client-name=foo --config=bar `) ) @@ -62,12 +61,10 @@ func newCmdUserKubeConfig(out io.Writer) *cobra.Command { initCfg := &kubeadmapiv1beta2.InitConfiguration{} clusterCfg := &kubeadmapiv1beta2.ClusterConfiguration{} - // Default values for the cobra help text - kubeadmscheme.Scheme.Default(initCfg) - kubeadmscheme.Scheme.Default(clusterCfg) - - var token, clientName string - var organizations []string + var ( + token, clientName, cfgPath string + organizations []string + ) // Creates the UX Command cmd := &cobra.Command{ @@ -76,39 +73,31 @@ func newCmdUserKubeConfig(out io.Writer) *cobra.Command { Long: userKubeconfigLongDesc, Example: userKubeconfigExample, RunE: func(cmd *cobra.Command, args []string) error { - if clientName == "" { - return errors.New("missing required argument --client-name") - } - // This call returns the ready-to-use configuration based on the defaults populated by flags - internalcfg, err := configutil.DefaultedInitConfiguration(initCfg, clusterCfg) + internalCfg, err := configutil.LoadOrDefaultInitConfiguration(cfgPath, initCfg, clusterCfg) if err != nil { return err } // if the kubeconfig file for an additional user has to use a token, use it if token != "" { - return kubeconfigphase.WriteKubeConfigWithToken(out, internalcfg, clientName, token) + return kubeconfigphase.WriteKubeConfigWithToken(out, internalCfg, clientName, token) } // Otherwise, write a kubeconfig file with a generate client cert - return kubeconfigphase.WriteKubeConfigWithClientCert(out, internalcfg, clientName, organizations) + return kubeconfigphase.WriteKubeConfigWithClientCert(out, internalCfg, clientName, organizations) }, Args: cobra.NoArgs, } - // Add ClusterConfiguration backed flags to the command - cmd.Flags().StringVar(&clusterCfg.CertificatesDir, options.CertificatesDir, clusterCfg.CertificatesDir, "The path where certificates are stored") - cmd.Flags().StringVar(&clusterCfg.ClusterName, "cluster-name", clusterCfg.ClusterName, "Cluster name to be used in kubeconfig") - - // Add InitConfiguration backed flags to the command - cmd.Flags().StringVar(&initCfg.LocalAPIEndpoint.AdvertiseAddress, options.APIServerAdvertiseAddress, initCfg.LocalAPIEndpoint.AdvertiseAddress, "The IP address the API server is accessible on") - cmd.Flags().Int32Var(&initCfg.LocalAPIEndpoint.BindPort, options.APIServerBindPort, initCfg.LocalAPIEndpoint.BindPort, "The port the API server is accessible on") + options.AddConfigFlag(cmd.Flags(), &cfgPath) // Add command specific flags cmd.Flags().StringVar(&token, options.TokenStr, token, "The token that should be used as the authentication mechanism for this kubeconfig, instead of client certificates") cmd.Flags().StringVar(&clientName, "client-name", clientName, "The name of user. It will be used as the CN if client certificates are created") cmd.Flags().StringSliceVar(&organizations, "org", organizations, "The orgnizations of the client certificate. It will be used as the O if client certificates are created") + cmd.MarkFlagRequired(options.CfgPath) + cmd.MarkFlagRequired("client-name") return cmd } diff --git a/cmd/kubeadm/app/cmd/alpha/kubeconfig_test.go b/cmd/kubeadm/app/cmd/alpha/kubeconfig_test.go index 5477e72ec82..132ca498c90 100644 --- a/cmd/kubeadm/app/cmd/alpha/kubeconfig_test.go +++ b/cmd/kubeadm/app/cmd/alpha/kubeconfig_test.go @@ -19,16 +19,61 @@ package alpha import ( "bytes" "fmt" + "io/ioutil" "os" + "path/filepath" "testing" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/yaml" + + kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil" testutil "k8s.io/kubernetes/cmd/kubeadm/test" kubeconfigtestutil "k8s.io/kubernetes/cmd/kubeadm/test/kubeconfig" ) +func generateTestKubeadmConfig(dir, id, certDir, clusterName string) (string, error) { + cfgPath := filepath.Join(dir, id) + initCfg := kubeadmapiv1beta2.InitConfiguration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "kubeadm.k8s.io/v1beta2", + Kind: "InitConfiguration", + }, + LocalAPIEndpoint: kubeadmapiv1beta2.APIEndpoint{ + AdvertiseAddress: "1.2.3.4", + BindPort: 1234, + }, + } + clusterCfg := kubeadmapiv1beta2.ClusterConfiguration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "kubeadm.k8s.io/v1beta2", + Kind: "ClusterConfiguration", + }, + CertificatesDir: certDir, + ClusterName: clusterName, + KubernetesVersion: "v1.19.0", + } + + var buf bytes.Buffer + data, err := yaml.Marshal(&initCfg) + if err != nil { + return "", err + } + buf.Write(data) + buf.WriteString("---\n") + data, err = yaml.Marshal(&clusterCfg) + if err != nil { + return "", err + } + buf.Write(data) + + err = ioutil.WriteFile(cfgPath, buf.Bytes(), 0644) + return cfgPath, err +} + func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) { // Temporary folders for the test case @@ -44,19 +89,12 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) { t.Fatalf("couldn't retrieve ca cert: %v", err) } - commonFlags := []string{ - "--apiserver-advertise-address=1.2.3.4", - "--apiserver-bind-port=1234", - "--client-name=myUser", - fmt.Sprintf("--cert-dir=%s", pkidir), - } - var tests = []struct { name string command string + clusterName string withClientCert bool withToken bool - withClusterName bool additionalFlags []string }{ { @@ -65,11 +103,10 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) { withClientCert: true, }, { - name: "user subCommand withClientCert", - command: "user", - withClientCert: true, - withClusterName: true, - additionalFlags: []string{"--cluster-name=my-cluster"}, + name: "user subCommand withClientCert", + command: "user", + withClientCert: true, + clusterName: "my-cluster", }, { name: "user subCommand withToken", @@ -81,8 +118,8 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) { name: "user subCommand withToken", withToken: true, command: "user", - withClusterName: true, - additionalFlags: []string{"--token=123456", "--cluster-name=my-cluster"}, + clusterName: "my-cluster-with-token", + additionalFlags: []string{"--token=123456"}, }, } @@ -93,18 +130,27 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) { // Get subcommands working in the temporary directory cmd := newCmdUserKubeConfig(buf) + cfgPath, err := generateTestKubeadmConfig(tmpdir, test.name, pkidir, test.clusterName) + if err != nil { + t.Fatalf("Failed to generate kubeadm config: %v", err) + } + + commonFlags := []string{ + "--client-name=myUser", + fmt.Sprintf("--config=%s", cfgPath), + } + // Execute the subcommand allFlags := append(commonFlags, test.additionalFlags...) cmd.SetArgs(allFlags) if err := cmd.Execute(); err != nil { - t.Fatal("Could not execute subcommand") + t.Fatalf("Could not execute subcommand: %v", err) } // reads kubeconfig written to stdout config, err := clientcmd.Load(buf.Bytes()) if err != nil { - t.Errorf("couldn't read kubeconfig file from buffer: %v", err) - return + t.Fatalf("couldn't read kubeconfig file from buffer: %v", err) } // checks that CLI flags are properly propagated @@ -120,9 +166,9 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) { kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithToken(t, config, "myUser", "123456") } - if test.withClusterName { + if len(test.clusterName) > 0 { // checks that kubeconfig files have expected cluster name - kubeconfigtestutil.AssertKubeConfigCurrentContextWithClusterName(t, config, "my-cluster") + kubeconfigtestutil.AssertKubeConfigCurrentContextWithClusterName(t, config, test.clusterName) } }) }