diff --git a/cmd/kubeadm/app/cmd/BUILD b/cmd/kubeadm/app/cmd/BUILD index 1900bc9280..6127f5fc15 100644 --- a/cmd/kubeadm/app/cmd/BUILD +++ b/cmd/kubeadm/app/cmd/BUILD @@ -95,9 +95,11 @@ go_test( "//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/v1beta1: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/features: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/runtime:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", diff --git a/cmd/kubeadm/app/cmd/config.go b/cmd/kubeadm/app/cmd/config.go index 0f5bea4b5f..c8cb8a8047 100644 --- a/cmd/kubeadm/app/cmd/config.go +++ b/cmd/kubeadm/app/cmd/config.go @@ -50,8 +50,8 @@ import ( ) var ( - // sillyToken is only set statically to make kubeadm not randomize the token on every run - sillyToken = kubeadmapiv1beta1.BootstrapToken{ + // placeholderToken is only set statically to make kubeadm not randomize the token on every run + placeholderToken = kubeadmapiv1beta1.BootstrapToken{ Token: &kubeadmapiv1beta1.BootstrapTokenString{ ID: "abcdef", Secret: "0123456789abcdef", @@ -83,6 +83,7 @@ func NewCmdConfig(out io.Writer) *cobra.Command { options.AddKubeConfigFlag(cmd.PersistentFlags(), &kubeConfigFile) kubeConfigFile = cmdutil.FindExistingKubeConfig(kubeConfigFile) + cmd.AddCommand(NewCmdConfigPrint(out)) cmd.AddCommand(NewCmdConfigPrintDefault(out)) cmd.AddCommand(NewCmdConfigMigrate(out)) cmd.AddCommand(NewCmdConfigUpload(out, &kubeConfigFile)) @@ -91,6 +92,63 @@ func NewCmdConfig(out io.Writer) *cobra.Command { 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 func NewCmdConfigPrintDefault(out io.Writer) *cobra.Command { 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 - 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. - `), sillyToken), + `), placeholderToken), + Deprecated: "Please, use `kubeadm config print` instead.", Run: func(cmd *cobra.Command, args []string) { if len(apiObjects) == 0 { apiObjects = getSupportedAPIObjects() @@ -125,13 +184,21 @@ func NewCmdConfigPrintDefault(out io.Writer) *cobra.Command { 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) { switch apiObject { case constants.InitConfigurationKind: - return getDefaultInitConfigBytes(constants.InitConfigurationKind) + return getDefaultInitConfigBytesByKind(constants.InitConfigurationKind) case constants.ClusterConfigurationKind: - return getDefaultInitConfigBytes(constants.ClusterConfigurationKind) + return getDefaultInitConfigBytesByKind(constants.ClusterConfigurationKind) case constants.JoinConfigurationKind: return getDefaultNodeConfigBytes() @@ -146,15 +213,23 @@ func getDefaultAPIObjectBytes(apiObject string) ([]byte, error) { } } -// getSupportedAPIObjects returns all currently supported API object names -func getSupportedAPIObjects() []string { - objects := []string{constants.InitConfigurationKind, constants.ClusterConfigurationKind, constants.JoinConfigurationKind} +// getSupportedComponentConfigAPIObjects returns all currently supported component config API object names +func getSupportedComponentConfigAPIObjects() []string { + objects := []string{} for componentType := range componentconfigs.Known { objects = append(objects, string(componentType)) } 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 // NB. currently there is no historical supported API objects, but we keep this function for future changes func getAllAPIObjectNames() []string { @@ -171,18 +246,21 @@ func getDefaultedInitConfig() (*kubeadmapi.InitConfiguration, error) { ClusterConfiguration: kubeadmapiv1beta1.ClusterConfiguration{ 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(kind string) ([]byte, error) { +func getDefaultInitConfigBytes() ([]byte, error) { internalcfg, err := getDefaultedInitConfig() if err != nil { return []byte{}, err } - b, err := configutil.MarshalKubeadmConfigObject(internalcfg) + return configutil.MarshalKubeadmConfigObject(internalcfg) +} + +func getDefaultInitConfigBytesByKind(kind string) ([]byte, error) { + b, err := getDefaultInitConfigBytes() if err != nil { return []byte{}, err } @@ -197,7 +275,7 @@ func getDefaultNodeConfigBytes() ([]byte, error) { internalcfg, err := configutil.JoinConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1beta1.JoinConfiguration{ Discovery: kubeadmapiv1beta1.Discovery{ BootstrapToken: &kubeadmapiv1beta1.BootstrapTokenDiscovery{ - Token: sillyToken.Token.String(), + Token: placeholderToken.Token.String(), 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 }, diff --git a/cmd/kubeadm/app/cmd/config_test.go b/cmd/kubeadm/app/cmd/config_test.go index 452b42d3d7..7af79bc3be 100644 --- a/cmd/kubeadm/app/cmd/config_test.go +++ b/cmd/kubeadm/app/cmd/config_test.go @@ -18,17 +18,24 @@ package cmd_test import ( "bytes" + "io" "io/ioutil" "os" "path/filepath" + "reflect" + "sort" "strings" "testing" "github.com/renstrom/dedent" + "github.com/spf13/cobra" kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1" "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" + kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" utilruntime "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime" "k8s.io/utils/exec" @@ -272,3 +279,96 @@ func tempConfig(t *testing.T, config []byte) (string, func()) { 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) + } + }) + } +} diff --git a/docs/.generated_docs b/docs/.generated_docs index 294c91fd39..9035112ac2 100644 --- a/docs/.generated_docs +++ b/docs/.generated_docs @@ -75,7 +75,9 @@ docs/admin/kubeadm_config_images.md docs/admin/kubeadm_config_images_list.md docs/admin/kubeadm_config_images_pull.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_from-file.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-migrate.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-flags.1 docs/man/man1/kubeadm-config-upload.1 diff --git a/docs/admin/kubeadm_config_print-default.md b/docs/admin/kubeadm_config_print.md similarity index 100% rename from docs/admin/kubeadm_config_print-default.md rename to docs/admin/kubeadm_config_print.md diff --git a/docs/admin/kubeadm_config_print_init-defaults.md b/docs/admin/kubeadm_config_print_init-defaults.md new file mode 100644 index 0000000000..b6fd7a0f98 --- /dev/null +++ b/docs/admin/kubeadm_config_print_init-defaults.md @@ -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. diff --git a/docs/admin/kubeadm_config_print_join-defaults.md b/docs/admin/kubeadm_config_print_join-defaults.md new file mode 100644 index 0000000000..b6fd7a0f98 --- /dev/null +++ b/docs/admin/kubeadm_config_print_join-defaults.md @@ -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. diff --git a/docs/man/man1/kubeadm-config-print-init-defaults.1 b/docs/man/man1/kubeadm-config-print-init-defaults.1 new file mode 100644 index 0000000000..b6fd7a0f98 --- /dev/null +++ b/docs/man/man1/kubeadm-config-print-init-defaults.1 @@ -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. diff --git a/docs/man/man1/kubeadm-config-print-join-defaults.1 b/docs/man/man1/kubeadm-config-print-join-defaults.1 new file mode 100644 index 0000000000..b6fd7a0f98 --- /dev/null +++ b/docs/man/man1/kubeadm-config-print-join-defaults.1 @@ -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. diff --git a/docs/man/man1/kubeadm-config-print.1 b/docs/man/man1/kubeadm-config-print.1 new file mode 100644 index 0000000000..b6fd7a0f98 --- /dev/null +++ b/docs/man/man1/kubeadm-config-print.1 @@ -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.