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/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",
],
)

View File

@ -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
}

View File

@ -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)
}
})
}