Merge pull request #94879 from knight42/refactor/kubeadm-alpha-kubeconfig

kubeadm: make "alpha kubeconfig user" accept --config
This commit is contained in:
Kubernetes Prow Robot 2020-09-22 09:16:48 -07:00 committed by GitHub
commit 9f32854099
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 44 deletions

View File

@ -67,10 +67,12 @@ go_test(
"//cmd/kubeadm/test:go_default_library", "//cmd/kubeadm/test:go_default_library",
"//cmd/kubeadm/test/cmd:go_default_library", "//cmd/kubeadm/test/cmd:go_default_library",
"//cmd/kubeadm/test/kubeconfig: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", "//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/github.com/spf13/pflag: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/assert:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library", "//vendor/github.com/stretchr/testify/require:go_default_library",
"//vendor/sigs.k8s.io/yaml:go_default_library",
], ],
) )

View File

@ -19,9 +19,8 @@ package alpha
import ( import (
"io" "io"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2" kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
@ -39,8 +38,8 @@ var (
` + cmdutil.AlphaDisclaimer) ` + cmdutil.AlphaDisclaimer)
userKubeconfigExample = cmdutil.Examples(` userKubeconfigExample = cmdutil.Examples(`
# Output a kubeconfig file for an additional user named foo # Output a kubeconfig file for an additional user named foo using a kubeadm config file bar
kubeadm alpha kubeconfig user --client-name=foo kubeadm alpha kubeconfig user --client-name=foo --config=bar
`) `)
) )
@ -62,12 +61,10 @@ func newCmdUserKubeConfig(out io.Writer) *cobra.Command {
initCfg := &kubeadmapiv1beta2.InitConfiguration{} initCfg := &kubeadmapiv1beta2.InitConfiguration{}
clusterCfg := &kubeadmapiv1beta2.ClusterConfiguration{} clusterCfg := &kubeadmapiv1beta2.ClusterConfiguration{}
// Default values for the cobra help text var (
kubeadmscheme.Scheme.Default(initCfg) token, clientName, cfgPath string
kubeadmscheme.Scheme.Default(clusterCfg) organizations []string
)
var token, clientName string
var organizations []string
// Creates the UX Command // Creates the UX Command
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -76,39 +73,31 @@ func newCmdUserKubeConfig(out io.Writer) *cobra.Command {
Long: userKubeconfigLongDesc, Long: userKubeconfigLongDesc,
Example: userKubeconfigExample, Example: userKubeconfigExample,
RunE: func(cmd *cobra.Command, args []string) error { 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 // 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 { if err != nil {
return err return err
} }
// if the kubeconfig file for an additional user has to use a token, use it // if the kubeconfig file for an additional user has to use a token, use it
if token != "" { 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 // 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, Args: cobra.NoArgs,
} }
// Add ClusterConfiguration backed flags to the command options.AddConfigFlag(cmd.Flags(), &cfgPath)
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")
// Add command specific flags // 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(&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().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.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 return cmd
} }

View File

@ -19,16 +19,61 @@ package alpha
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath"
"testing" "testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/clientcmd" "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" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil" "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
testutil "k8s.io/kubernetes/cmd/kubeadm/test" testutil "k8s.io/kubernetes/cmd/kubeadm/test"
kubeconfigtestutil "k8s.io/kubernetes/cmd/kubeadm/test/kubeconfig" 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) { func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) {
// Temporary folders for the test case // Temporary folders for the test case
@ -44,19 +89,12 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) {
t.Fatalf("couldn't retrieve ca cert: %v", err) 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 { var tests = []struct {
name string name string
command string command string
clusterName string
withClientCert bool withClientCert bool
withToken bool withToken bool
withClusterName bool
additionalFlags []string additionalFlags []string
}{ }{
{ {
@ -65,11 +103,10 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) {
withClientCert: true, withClientCert: true,
}, },
{ {
name: "user subCommand withClientCert", name: "user subCommand withClientCert",
command: "user", command: "user",
withClientCert: true, withClientCert: true,
withClusterName: true, clusterName: "my-cluster",
additionalFlags: []string{"--cluster-name=my-cluster"},
}, },
{ {
name: "user subCommand withToken", name: "user subCommand withToken",
@ -81,8 +118,8 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) {
name: "user subCommand withToken", name: "user subCommand withToken",
withToken: true, withToken: true,
command: "user", command: "user",
withClusterName: true, clusterName: "my-cluster-with-token",
additionalFlags: []string{"--token=123456", "--cluster-name=my-cluster"}, additionalFlags: []string{"--token=123456"},
}, },
} }
@ -93,18 +130,27 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) {
// Get subcommands working in the temporary directory // Get subcommands working in the temporary directory
cmd := newCmdUserKubeConfig(buf) 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 // Execute the subcommand
allFlags := append(commonFlags, test.additionalFlags...) allFlags := append(commonFlags, test.additionalFlags...)
cmd.SetArgs(allFlags) cmd.SetArgs(allFlags)
if err := cmd.Execute(); err != nil { 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 // reads kubeconfig written to stdout
config, err := clientcmd.Load(buf.Bytes()) config, err := clientcmd.Load(buf.Bytes())
if err != nil { if err != nil {
t.Errorf("couldn't read kubeconfig file from buffer: %v", err) t.Fatalf("couldn't read kubeconfig file from buffer: %v", err)
return
} }
// checks that CLI flags are properly propagated // checks that CLI flags are properly propagated
@ -120,9 +166,9 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) {
kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithToken(t, config, "myUser", "123456") kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithToken(t, config, "myUser", "123456")
} }
if test.withClusterName { if len(test.clusterName) > 0 {
// checks that kubeconfig files have expected cluster name // checks that kubeconfig files have expected cluster name
kubeconfigtestutil.AssertKubeConfigCurrentContextWithClusterName(t, config, "my-cluster") kubeconfigtestutil.AssertKubeConfigCurrentContextWithClusterName(t, config, test.clusterName)
} }
}) })
} }