kubeadm: Introduce config print init/join-defaults

In order to improve the UX of kubeadm, it was decided to introduce the
following subcommands:

- `kubeadm config print` - this is currently only a placeholder for subcommands
  that deal printing of some kind of configuration.
- `kubeadm config print init-defaults` - prints the default combination of
  InitConfiguration and ClusterConfiguration. Selected component configs can be
  printed too if the `--component-configs` command line switch is used.
- `kubeadm config print join-defaults` - prints the default JoinConfiguration.
  This command also supports the use of `--component-configs`.
- `kubeadm config print-defaults` is deprecated in favor of
  `kubeadm config print init/join-defaults`.

Signed-off-by: Rostislav M. Georgiev <rostislavg@vmware.com>
pull/58/head
Rostislav M. Georgiev 2018-10-04 13:02:56 +03:00
parent 0ab29b4852
commit 91049ef77b
10 changed files with 215 additions and 15 deletions

View File

@ -95,9 +95,11 @@ go_test(
"//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1beta1:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/v1beta1:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
"//cmd/kubeadm/app/componentconfigs:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/features:go_default_library", "//cmd/kubeadm/app/features:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library", "//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/config:go_default_library", "//cmd/kubeadm/app/util/config:go_default_library",
"//cmd/kubeadm/app/util/runtime:go_default_library", "//cmd/kubeadm/app/util/runtime:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",

View File

@ -50,8 +50,8 @@ import (
) )
var ( var (
// sillyToken is only set statically to make kubeadm not randomize the token on every run // placeholderToken is only set statically to make kubeadm not randomize the token on every run
sillyToken = kubeadmapiv1beta1.BootstrapToken{ placeholderToken = kubeadmapiv1beta1.BootstrapToken{
Token: &kubeadmapiv1beta1.BootstrapTokenString{ Token: &kubeadmapiv1beta1.BootstrapTokenString{
ID: "abcdef", ID: "abcdef",
Secret: "0123456789abcdef", Secret: "0123456789abcdef",
@ -83,6 +83,7 @@ func NewCmdConfig(out io.Writer) *cobra.Command {
options.AddKubeConfigFlag(cmd.PersistentFlags(), &kubeConfigFile) options.AddKubeConfigFlag(cmd.PersistentFlags(), &kubeConfigFile)
kubeConfigFile = cmdutil.FindExistingKubeConfig(kubeConfigFile) kubeConfigFile = cmdutil.FindExistingKubeConfig(kubeConfigFile)
cmd.AddCommand(NewCmdConfigPrint(out))
cmd.AddCommand(NewCmdConfigPrintDefault(out)) cmd.AddCommand(NewCmdConfigPrintDefault(out))
cmd.AddCommand(NewCmdConfigMigrate(out)) cmd.AddCommand(NewCmdConfigMigrate(out))
cmd.AddCommand(NewCmdConfigUpload(out, &kubeConfigFile)) cmd.AddCommand(NewCmdConfigUpload(out, &kubeConfigFile))
@ -91,6 +92,63 @@ func NewCmdConfig(out io.Writer) *cobra.Command {
return cmd return cmd
} }
// NewCmdConfigPrint returns cobra.Command for "kubeadm config print" command
func NewCmdConfigPrint(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "print",
Short: "Print configuration",
Long: "This command prints configurations for subcommands provided.",
RunE: cmdutil.SubCmdRunE("print"),
}
cmd.AddCommand(NewCmdConfigPrintInitDefaults(out))
cmd.AddCommand(NewCmdConfigPrintJoinDefaults(out))
return cmd
}
// NewCmdConfigPrintInitDefaults returns cobra.Command for "kubeadm config print init-defaults" command
func NewCmdConfigPrintInitDefaults(out io.Writer) *cobra.Command {
return newCmdConfigPrintActionDefaults(out, "init", getDefaultInitConfigBytes)
}
// NewCmdConfigPrintJoinDefaults returns cobra.Command for "kubeadm config print join-defaults" command
func NewCmdConfigPrintJoinDefaults(out io.Writer) *cobra.Command {
return newCmdConfigPrintActionDefaults(out, "join", getDefaultNodeConfigBytes)
}
func newCmdConfigPrintActionDefaults(out io.Writer, action string, configBytesProc func() ([]byte, error)) *cobra.Command {
componentConfigs := []string{}
cmd := &cobra.Command{
Use: fmt.Sprintf("%s-defaults", action),
Short: fmt.Sprintf("Print default %s configuration, that can be used for 'kubeadm %s'", action, action),
Long: fmt.Sprintf(dedent.Dedent(`
This command prints objects such as the default %s configuration that is used for 'kubeadm %s'.
Note that sensitive values like the Bootstrap Token fields are replaced with placeholder values like %q in order to pass validation but
not perform the real computation for creating a token.
`), action, action, placeholderToken),
Run: func(cmd *cobra.Command, args []string) {
runConfigPrintActionDefaults(out, componentConfigs, configBytesProc)
},
}
cmd.Flags().StringSliceVar(&componentConfigs, "component-configs", componentConfigs,
fmt.Sprintf("A comma-separated list for component config API objects to print the default values for. Available values: %v. If this flag is not set, no component configs will be printed.", getSupportedComponentConfigAPIObjects()))
return cmd
}
func runConfigPrintActionDefaults(out io.Writer, componentConfigs []string, configBytesProc func() ([]byte, error)) {
initialConfig, err := configBytesProc()
kubeadmutil.CheckErr(err)
allBytes := [][]byte{initialConfig}
for _, componentConfig := range componentConfigs {
cfgBytes, err := getDefaultComponentConfigAPIObjectBytes(componentConfig)
kubeadmutil.CheckErr(err)
allBytes = append(allBytes, cfgBytes)
}
fmt.Fprint(out, string(bytes.Join(allBytes, []byte(constants.YAMLDocumentSeparator))))
}
// NewCmdConfigPrintDefault returns cobra.Command for "kubeadm config print-default" command // NewCmdConfigPrintDefault returns cobra.Command for "kubeadm config print-default" command
func NewCmdConfigPrintDefault(out io.Writer) *cobra.Command { func NewCmdConfigPrintDefault(out io.Writer) *cobra.Command {
apiObjects := []string{} apiObjects := []string{}
@ -104,9 +162,10 @@ func NewCmdConfigPrintDefault(out io.Writer) *cobra.Command {
For documentation visit: https://godoc.org/k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha3 For documentation visit: https://godoc.org/k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha3
Note that sensitive values like the Bootstrap Token fields are replaced with silly values like %q in order to pass validation but Note that sensitive values like the Bootstrap Token fields are replaced with placeholder values like %q in order to pass validation but
not perform the real computation for creating a token. not perform the real computation for creating a token.
`), sillyToken), `), placeholderToken),
Deprecated: "Please, use `kubeadm config print` instead.",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if len(apiObjects) == 0 { if len(apiObjects) == 0 {
apiObjects = getSupportedAPIObjects() apiObjects = getSupportedAPIObjects()
@ -125,13 +184,21 @@ func NewCmdConfigPrintDefault(out io.Writer) *cobra.Command {
return cmd return cmd
} }
func getDefaultComponentConfigAPIObjectBytes(apiObject string) ([]byte, error) {
registration, ok := componentconfigs.Known[componentconfigs.RegistrationKind(apiObject)]
if !ok {
return []byte{}, fmt.Errorf("--component-configs needs to contain some of %v", getSupportedComponentConfigAPIObjects())
}
return getDefaultComponentConfigBytes(registration)
}
func getDefaultAPIObjectBytes(apiObject string) ([]byte, error) { func getDefaultAPIObjectBytes(apiObject string) ([]byte, error) {
switch apiObject { switch apiObject {
case constants.InitConfigurationKind: case constants.InitConfigurationKind:
return getDefaultInitConfigBytes(constants.InitConfigurationKind) return getDefaultInitConfigBytesByKind(constants.InitConfigurationKind)
case constants.ClusterConfigurationKind: case constants.ClusterConfigurationKind:
return getDefaultInitConfigBytes(constants.ClusterConfigurationKind) return getDefaultInitConfigBytesByKind(constants.ClusterConfigurationKind)
case constants.JoinConfigurationKind: case constants.JoinConfigurationKind:
return getDefaultNodeConfigBytes() return getDefaultNodeConfigBytes()
@ -146,15 +213,23 @@ func getDefaultAPIObjectBytes(apiObject string) ([]byte, error) {
} }
} }
// getSupportedAPIObjects returns all currently supported API object names // getSupportedComponentConfigAPIObjects returns all currently supported component config API object names
func getSupportedAPIObjects() []string { func getSupportedComponentConfigAPIObjects() []string {
objects := []string{constants.InitConfigurationKind, constants.ClusterConfigurationKind, constants.JoinConfigurationKind} objects := []string{}
for componentType := range componentconfigs.Known { for componentType := range componentconfigs.Known {
objects = append(objects, string(componentType)) objects = append(objects, string(componentType))
} }
return objects return objects
} }
// getSupportedAPIObjects returns all currently supported API object names
func getSupportedAPIObjects() []string {
baseObjects := []string{constants.InitConfigurationKind, constants.ClusterConfigurationKind, constants.JoinConfigurationKind}
objects := getSupportedComponentConfigAPIObjects()
objects = append(objects, baseObjects...)
return objects
}
// getAllAPIObjectNames returns currently supported API object names and their historical aliases // getAllAPIObjectNames returns currently supported API object names and their historical aliases
// NB. currently there is no historical supported API objects, but we keep this function for future changes // NB. currently there is no historical supported API objects, but we keep this function for future changes
func getAllAPIObjectNames() []string { func getAllAPIObjectNames() []string {
@ -171,18 +246,21 @@ func getDefaultedInitConfig() (*kubeadmapi.InitConfiguration, error) {
ClusterConfiguration: kubeadmapiv1beta1.ClusterConfiguration{ ClusterConfiguration: kubeadmapiv1beta1.ClusterConfiguration{
KubernetesVersion: fmt.Sprintf("v1.%d.0", constants.MinimumControlPlaneVersion.Minor()+1), KubernetesVersion: fmt.Sprintf("v1.%d.0", constants.MinimumControlPlaneVersion.Minor()+1),
}, },
BootstrapTokens: []kubeadmapiv1beta1.BootstrapToken{sillyToken}, BootstrapTokens: []kubeadmapiv1beta1.BootstrapToken{placeholderToken},
}) })
} }
// TODO: This is now invoked for both InitConfiguration and ClusterConfiguration, we should make separate versions of it func getDefaultInitConfigBytes() ([]byte, error) {
func getDefaultInitConfigBytes(kind string) ([]byte, error) {
internalcfg, err := getDefaultedInitConfig() internalcfg, err := getDefaultedInitConfig()
if err != nil { if err != nil {
return []byte{}, err return []byte{}, err
} }
b, err := configutil.MarshalKubeadmConfigObject(internalcfg) return configutil.MarshalKubeadmConfigObject(internalcfg)
}
func getDefaultInitConfigBytesByKind(kind string) ([]byte, error) {
b, err := getDefaultInitConfigBytes()
if err != nil { if err != nil {
return []byte{}, err return []byte{}, err
} }
@ -197,7 +275,7 @@ func getDefaultNodeConfigBytes() ([]byte, error) {
internalcfg, err := configutil.JoinConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1beta1.JoinConfiguration{ internalcfg, err := configutil.JoinConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1beta1.JoinConfiguration{
Discovery: kubeadmapiv1beta1.Discovery{ Discovery: kubeadmapiv1beta1.Discovery{
BootstrapToken: &kubeadmapiv1beta1.BootstrapTokenDiscovery{ BootstrapToken: &kubeadmapiv1beta1.BootstrapTokenDiscovery{
Token: sillyToken.Token.String(), Token: placeholderToken.Token.String(),
APIServerEndpoints: []string{"kube-apiserver:6443"}, APIServerEndpoints: []string{"kube-apiserver:6443"},
UnsafeSkipCAVerification: true, // TODO: UnsafeSkipCAVerification: true needs to be set for validation to pass, but shouldn't be recommended as the default UnsafeSkipCAVerification: true, // TODO: UnsafeSkipCAVerification: true needs to be set for validation to pass, but shouldn't be recommended as the default
}, },

View File

@ -18,17 +18,24 @@ package cmd_test
import ( import (
"bytes" "bytes"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"sort"
"strings" "strings"
"testing" "testing"
"github.com/renstrom/dedent" "github.com/renstrom/dedent"
"github.com/spf13/cobra"
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1" kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd" "k8s.io/kubernetes/cmd/kubeadm/app/cmd"
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features" "k8s.io/kubernetes/cmd/kubeadm/app/features"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
utilruntime "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime" utilruntime "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime"
"k8s.io/utils/exec" "k8s.io/utils/exec"
@ -272,3 +279,96 @@ func tempConfig(t *testing.T, config []byte) (string, func()) {
os.RemoveAll(tmpDir) os.RemoveAll(tmpDir)
} }
} }
func TestNewCmdConfigPrintActionDefaults(t *testing.T) {
tests := []struct {
name string
expectedKinds []string // need to be sorted
componentConfigs string
cmdProc func(out io.Writer) *cobra.Command
}{
{
name: "InitConfiguration: No component configs",
expectedKinds: []string{
constants.ClusterConfigurationKind,
constants.InitConfigurationKind,
},
cmdProc: cmd.NewCmdConfigPrintInitDefaults,
},
{
name: "InitConfiguration: KubeProxyConfiguration",
expectedKinds: []string{
constants.ClusterConfigurationKind,
constants.InitConfigurationKind,
string(componentconfigs.KubeProxyConfigurationKind),
},
componentConfigs: "KubeProxyConfiguration",
cmdProc: cmd.NewCmdConfigPrintInitDefaults,
},
{
name: "InitConfiguration: KubeProxyConfiguration and KubeletConfiguration",
expectedKinds: []string{
constants.ClusterConfigurationKind,
constants.InitConfigurationKind,
string(componentconfigs.KubeProxyConfigurationKind),
string(componentconfigs.KubeletConfigurationKind),
},
componentConfigs: "KubeProxyConfiguration,KubeletConfiguration",
cmdProc: cmd.NewCmdConfigPrintInitDefaults,
},
{
name: "JoinConfiguration: No component configs",
expectedKinds: []string{
constants.JoinConfigurationKind,
},
cmdProc: cmd.NewCmdConfigPrintJoinDefaults,
},
{
name: "JoinConfiguration: KubeProxyConfiguration",
expectedKinds: []string{
constants.JoinConfigurationKind,
string(componentconfigs.KubeProxyConfigurationKind),
},
componentConfigs: "KubeProxyConfiguration",
cmdProc: cmd.NewCmdConfigPrintJoinDefaults,
},
{
name: "JoinConfiguration: KubeProxyConfiguration and KubeletConfiguration",
expectedKinds: []string{
constants.JoinConfigurationKind,
string(componentconfigs.KubeProxyConfigurationKind),
string(componentconfigs.KubeletConfigurationKind),
},
componentConfigs: "KubeProxyConfiguration,KubeletConfiguration",
cmdProc: cmd.NewCmdConfigPrintJoinDefaults,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var output bytes.Buffer
command := test.cmdProc(&output)
if err := command.Flags().Set("component-configs", test.componentConfigs); err != nil {
t.Fatalf("failed to set component-configs flag")
}
command.Run(nil, nil)
gvkmap, err := kubeadmutil.SplitYAMLDocuments(output.Bytes())
if err != nil {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
}
gotKinds := []string{}
for gvk := range gvkmap {
gotKinds = append(gotKinds, gvk.Kind)
}
sort.Strings(gotKinds)
if !reflect.DeepEqual(gotKinds, test.expectedKinds) {
t.Fatalf("kinds not matching:\n\texpectedKinds: %v\n\tgotKinds: %v\n", test.expectedKinds, gotKinds)
}
})
}
}

View File

@ -75,7 +75,9 @@ docs/admin/kubeadm_config_images.md
docs/admin/kubeadm_config_images_list.md docs/admin/kubeadm_config_images_list.md
docs/admin/kubeadm_config_images_pull.md docs/admin/kubeadm_config_images_pull.md
docs/admin/kubeadm_config_migrate.md docs/admin/kubeadm_config_migrate.md
docs/admin/kubeadm_config_print-default.md docs/admin/kubeadm_config_print.md
docs/admin/kubeadm_config_print_init-defaults.md
docs/admin/kubeadm_config_print_join-defaults.md
docs/admin/kubeadm_config_upload.md docs/admin/kubeadm_config_upload.md
docs/admin/kubeadm_config_upload_from-file.md docs/admin/kubeadm_config_upload_from-file.md
docs/admin/kubeadm_config_upload_from-flags.md docs/admin/kubeadm_config_upload_from-flags.md
@ -172,6 +174,9 @@ docs/man/man1/kubeadm-config-images-pull.1
docs/man/man1/kubeadm-config-images.1 docs/man/man1/kubeadm-config-images.1
docs/man/man1/kubeadm-config-migrate.1 docs/man/man1/kubeadm-config-migrate.1
docs/man/man1/kubeadm-config-print-default.1 docs/man/man1/kubeadm-config-print-default.1
docs/man/man1/kubeadm-config-print-init-defaults.1
docs/man/man1/kubeadm-config-print-join-defaults.1
docs/man/man1/kubeadm-config-print.1
docs/man/man1/kubeadm-config-upload-from-file.1 docs/man/man1/kubeadm-config-upload-from-file.1
docs/man/man1/kubeadm-config-upload-from-flags.1 docs/man/man1/kubeadm-config-upload-from-flags.1
docs/man/man1/kubeadm-config-upload.1 docs/man/man1/kubeadm-config-upload.1

View File

@ -0,0 +1,3 @@
This file is autogenerated, but we've stopped checking such files into the
repository to reduce the need for rebases. Please run hack/generate-docs.sh to
populate this file.

View File

@ -0,0 +1,3 @@
This file is autogenerated, but we've stopped checking such files into the
repository to reduce the need for rebases. Please run hack/generate-docs.sh to
populate this file.

View File

@ -0,0 +1,3 @@
This file is autogenerated, but we've stopped checking such files into the
repository to reduce the need for rebases. Please run hack/generate-docs.sh to
populate this file.

View File

@ -0,0 +1,3 @@
This file is autogenerated, but we've stopped checking such files into the
repository to reduce the need for rebases. Please run hack/generate-docs.sh to
populate this file.

View File

@ -0,0 +1,3 @@
This file is autogenerated, but we've stopped checking such files into the
repository to reduce the need for rebases. Please run hack/generate-docs.sh to
populate this file.