diff --git a/cmd/kubeadm/app/cmd/BUILD b/cmd/kubeadm/app/cmd/BUILD index b5b5d1158cf..091a0119466 100644 --- a/cmd/kubeadm/app/cmd/BUILD +++ b/cmd/kubeadm/app/cmd/BUILD @@ -55,7 +55,6 @@ go_library( "//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/util/i18n:go_default_library", "//pkg/util/initsystem:go_default_library", - "//pkg/util/node:go_default_library", "//pkg/version:go_default_library", "//vendor/github.com/ghodss/yaml:go_default_library", "//vendor/github.com/golang/glog:go_default_library", @@ -65,7 +64,6 @@ go_library( "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/duration:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/version:go_default_library", diff --git a/cmd/kubeadm/app/cmd/config.go b/cmd/kubeadm/app/cmd/config.go index cdcee23204c..481a9b6c748 100644 --- a/cmd/kubeadm/app/cmd/config.go +++ b/cmd/kubeadm/app/cmd/config.go @@ -42,6 +42,14 @@ import ( utilsexec "k8s.io/utils/exec" ) +const ( + masterConfig = "MasterConfiguration" + nodeConfig = "NodeConfiguration" + sillyToken = "abcdef.0123456789abcdef" +) + +var availableAPIObjects = []string{masterConfig, nodeConfig} + // NewCmdConfig returns cobra.Command for "kubeadm config" command func NewCmdConfig(out io.Writer) *cobra.Command { @@ -65,12 +73,71 @@ func NewCmdConfig(out io.Writer) *cobra.Command { cmd.PersistentFlags().StringVar(&kubeConfigFile, "kubeconfig", "/etc/kubernetes/admin.conf", "The KubeConfig file to use when talking to the cluster.") + cmd.AddCommand(NewCmdConfigPrintDefault(out)) cmd.AddCommand(NewCmdConfigUpload(out, &kubeConfigFile)) cmd.AddCommand(NewCmdConfigView(out, &kubeConfigFile)) cmd.AddCommand(NewCmdConfigImages(out)) return cmd } +// NewCmdConfigPrintDefault returns cobra.Command for "kubeadm config print-default" command +func NewCmdConfigPrintDefault(out io.Writer) *cobra.Command { + apiObjects := []string{} + cmd := &cobra.Command{ + Use: "print-default", + Aliases: []string{"print-defaults"}, + Short: "Print the default values for a kubeadm configuration object.", + Long: fmt.Sprintf(dedent.Dedent(` + This command prints the default MasterConfiguration object that is used for 'kubeadm init' and 'kubeadm upgrade', + and the default NodeConfiguration object that is used for 'kubeadm join'. + + Note that sensitive values like the Bootstrap Token fields are replaced with silly values like %q in order to pass validation but + not perform the real computation for creating a token. + `), sillyToken), + Run: func(cmd *cobra.Command, args []string) { + if len(apiObjects) == 0 { + apiObjects = availableAPIObjects + } + for i, apiObject := range apiObjects { + if i > 0 { + fmt.Fprintln(out, "---") + } + + cfgBytes, err := getDefaultAPIObjectBytes(apiObject) + kubeadmutil.CheckErr(err) + // Print the API object byte array + fmt.Fprintf(out, "%s", cfgBytes) + } + }, + } + cmd.Flags().StringSliceVar(&apiObjects, "api-objects", apiObjects, + fmt.Sprintf("A comma-separated list for API objects to print the default values for. Available values: %v. This flag unset means 'print all known objects'", availableAPIObjects)) + return cmd +} + +func getDefaultAPIObjectBytes(apiObject string) ([]byte, error) { + if apiObject == masterConfig { + + internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1alpha2.MasterConfiguration{ + Token: sillyToken, + }) + kubeadmutil.CheckErr(err) + + return kubeadmutil.MarshalToYamlForCodecs(internalcfg, kubeadmapiv1alpha2.SchemeGroupVersion, kubeadmscheme.Codecs) + } + if apiObject == nodeConfig { + internalcfg, err := configutil.NodeConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1alpha2.NodeConfiguration{ + Token: sillyToken, + DiscoveryTokenAPIServers: []string{"kube-apiserver:6443"}, + DiscoveryTokenUnsafeSkipCAVerification: true, + }) + kubeadmutil.CheckErr(err) + + return kubeadmutil.MarshalToYamlForCodecs(internalcfg, kubeadmapiv1alpha2.SchemeGroupVersion, kubeadmscheme.Codecs) + } + return []byte{}, fmt.Errorf("--api-object needs to be one of %v", availableAPIObjects) +} + // NewCmdConfigUpload returns cobra.Command for "kubeadm config upload" command func NewCmdConfigUpload(out io.Writer, kubeConfigFile *string) *cobra.Command { cmd := &cobra.Command{ diff --git a/cmd/kubeadm/app/cmd/join.go b/cmd/kubeadm/app/cmd/join.go index 20b74f0d66d..ce6566cd6e8 100644 --- a/cmd/kubeadm/app/cmd/join.go +++ b/cmd/kubeadm/app/cmd/join.go @@ -19,7 +19,6 @@ package cmd import ( "fmt" "io" - "io/ioutil" "path/filepath" "strings" @@ -28,7 +27,6 @@ import ( "github.com/spf13/cobra" flag "github.com/spf13/pflag" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/sets" certutil "k8s.io/client-go/util/cert" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" @@ -41,8 +39,8 @@ import ( kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet" "k8s.io/kubernetes/cmd/kubeadm/app/preflight" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" + configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" - nodeutil "k8s.io/kubernetes/pkg/util/node" utilsexec "k8s.io/utils/exec" ) @@ -115,9 +113,8 @@ func NewCmdJoin(out io.Writer) *cobra.Command { Short: "Run this on any machine you wish to join an existing cluster", Long: joinLongDescription, Run: func(cmd *cobra.Command, args []string) { - j, err := NewValidJoin(cfg, args, skipPreFlight, cfgPath, featureGatesString, ignorePreflightErrors) + j, err := NewValidJoin(cmd.PersistentFlags(), cfg, args, skipPreFlight, cfgPath, featureGatesString, ignorePreflightErrors) kubeadmutil.CheckErr(err) - kubeadmutil.CheckErr(j.Validate(cmd)) kubeadmutil.CheckErr(j.Run(out)) }, } @@ -129,7 +126,7 @@ func NewCmdJoin(out io.Writer) *cobra.Command { } // NewValidJoin validates the command line that are passed to the cobra command -func NewValidJoin(cfg *kubeadmapiv1alpha2.NodeConfiguration, args []string, skipPreFlight bool, cfgPath, featureGatesString string, ignorePreflightErrors []string) (*Join, error) { +func NewValidJoin(flagSet *flag.FlagSet, cfg *kubeadmapiv1alpha2.NodeConfiguration, args []string, skipPreFlight bool, cfgPath, featureGatesString string, ignorePreflightErrors []string) (*Join, error) { cfg.DiscoveryTokenAPIServers = args var err error @@ -137,16 +134,16 @@ func NewValidJoin(cfg *kubeadmapiv1alpha2.NodeConfiguration, args []string, skip return nil, err } - kubeadmscheme.Scheme.Default(cfg) - internalcfg := &kubeadmapi.NodeConfiguration{} - kubeadmscheme.Scheme.Convert(cfg, internalcfg, nil) + if err := validation.ValidateMixedArguments(flagSet); err != nil { + return nil, err + } ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(ignorePreflightErrors, skipPreFlight) if err != nil { return nil, err } - return NewJoin(cfgPath, args, internalcfg, ignorePreflightErrorsSet) + return NewJoin(cfgPath, args, cfg, ignorePreflightErrorsSet) } // AddJoinConfigFlags adds join flags bound to the config to the specified flagset @@ -205,31 +202,23 @@ type Join struct { } // NewJoin instantiates Join struct with given arguments -func NewJoin(cfgPath string, args []string, cfg *kubeadmapi.NodeConfiguration, ignorePreflightErrors sets.String) (*Join, error) { +func NewJoin(cfgPath string, args []string, defaultcfg *kubeadmapiv1alpha2.NodeConfiguration, ignorePreflightErrors sets.String) (*Join, error) { - if cfg.NodeName == "" { + if defaultcfg.NodeName == "" { glog.V(1).Infoln("[join] found NodeName empty") glog.V(1).Infoln("[join] considered OS hostname as NodeName") - cfg.NodeName = nodeutil.GetHostname("") } - if cfgPath != "" { - glog.V(1).Infoln("[join] reading configuration from", cfgPath) - b, err := ioutil.ReadFile(cfgPath) - if err != nil { - return nil, fmt.Errorf("unable to read config from %q [%v]", cfgPath, err) - } - glog.V(1).Infoln("[join] decoding configuration information") - if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(), b, cfg); err != nil { - return nil, fmt.Errorf("unable to decode config from %q [%v]", cfgPath, err) - } + internalcfg, err := configutil.NodeConfigFileAndDefaultsToInternalConfig(cfgPath, defaultcfg) + if err != nil { + return nil, err } glog.Infoln("[preflight] running pre-flight checks") // Then continue with the others... glog.V(1).Infoln("[preflight] running various checks on all nodes") - if err := preflight.RunJoinNodeChecks(utilsexec.New(), cfg, ignorePreflightErrors); err != nil { + if err := preflight.RunJoinNodeChecks(utilsexec.New(), internalcfg, ignorePreflightErrors); err != nil { return nil, err } @@ -237,15 +226,7 @@ func NewJoin(cfgPath string, args []string, cfg *kubeadmapi.NodeConfiguration, i glog.V(1).Infoln("[preflight] starting kubelet service if it's inactive") preflight.TryStartKubelet(ignorePreflightErrors) - return &Join{cfg: cfg}, nil -} - -// Validate validates mixed arguments passed to cobra.Command -func (j *Join) Validate(cmd *cobra.Command) error { - if err := validation.ValidateMixedArguments(cmd.PersistentFlags()); err != nil { - return err - } - return validation.ValidateNodeConfiguration(j.cfg).ToAggregate() + return &Join{cfg: internalcfg}, nil } // Run executes worker node provisioning and tries to join an existing cluster. diff --git a/cmd/kubeadm/app/cmd/join_test.go b/cmd/kubeadm/app/cmd/join_test.go index 3283e505912..ee4d06fdf03 100644 --- a/cmd/kubeadm/app/cmd/join_test.go +++ b/cmd/kubeadm/app/cmd/join_test.go @@ -156,14 +156,15 @@ func TestNewValidJoin(t *testing.T) { t.Fatalf("Unable to write file %q: %v", tc.cfgPath, err) } - join, err := NewValidJoin(cfg, tc.args, tc.skipPreFlight, tc.cfgPath, tc.featureGatesString, tc.ignorePreflightErrors) cmd := NewCmdJoin(&out) - if tc.cmdPersistentFlags != nil { for key, value := range tc.cmdPersistentFlags { cmd.PersistentFlags().Set(key, value) } } + + join, err := NewValidJoin(cmd.PersistentFlags(), cfg, tc.args, tc.skipPreFlight, tc.cfgPath, tc.featureGatesString, tc.ignorePreflightErrors) + if tc.nodeConfig != nil { join.cfg = tc.nodeConfig } @@ -174,11 +175,6 @@ func TestNewValidJoin(t *testing.T) { if (err != nil) != tc.expectedError { t.Fatalf(errorFormat, tc.name, tc.expectedError, (err != nil), err) } - // test Join.Validate() - } else if err == nil && tc.testJoinValidate { - if err = join.Validate(cmd); (err != nil) != tc.expectedError { - t.Fatalf(errorFormat, tc.name, tc.expectedError, (err != nil), err) - } // check error for NewValidJoin() } else if (err != nil) != tc.expectedError { t.Fatalf(errorFormat, tc.name, tc.expectedError, (err != nil), err) diff --git a/cmd/kubeadm/app/util/config/BUILD b/cmd/kubeadm/app/util/config/BUILD index 1a790e57363..6637bf87aab 100644 --- a/cmd/kubeadm/app/util/config/BUILD +++ b/cmd/kubeadm/app/util/config/BUILD @@ -8,7 +8,10 @@ load( go_library( name = "go_default_library", - srcs = ["masterconfig.go"], + srcs = [ + "masterconfig.go", + "nodeconfig.go", + ], importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/config", deps = [ "//cmd/kubeadm/app/apis/kubeadm:go_default_library", diff --git a/cmd/kubeadm/app/util/config/masterconfig.go b/cmd/kubeadm/app/util/config/masterconfig.go index b2b4a975f85..ec2f84dec46 100644 --- a/cmd/kubeadm/app/util/config/masterconfig.go +++ b/cmd/kubeadm/app/util/config/masterconfig.go @@ -37,7 +37,7 @@ import ( "k8s.io/kubernetes/pkg/util/version" ) -// SetInitDynamicDefaults checks and sets configuration values for Master node +// SetInitDynamicDefaults checks and sets configuration values for the MasterConfiguration object func SetInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error { // validate cfg.API.AdvertiseAddress. diff --git a/cmd/kubeadm/app/util/config/nodeconfig.go b/cmd/kubeadm/app/util/config/nodeconfig.go new file mode 100644 index 00000000000..b8ffc64d932 --- /dev/null +++ b/cmd/kubeadm/app/util/config/nodeconfig.go @@ -0,0 +1,71 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "fmt" + "io/ioutil" + + "github.com/golang/glog" + + "k8s.io/apimachinery/pkg/runtime" + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" + kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" + kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2" + "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" + "k8s.io/kubernetes/pkg/util/node" +) + +// SetJoinDynamicDefaults checks and sets configuration values for the NodeConfiguration object +func SetJoinDynamicDefaults(cfg *kubeadmapi.NodeConfiguration) error { + cfg.NodeName = node.GetHostname(cfg.NodeName) + return nil +} + +// NodeConfigFileAndDefaultsToInternalConfig +func NodeConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedcfg *kubeadmapiv1alpha2.NodeConfiguration) (*kubeadmapi.NodeConfiguration, error) { + internalcfg := &kubeadmapi.NodeConfiguration{} + + if cfgPath != "" { + // Loads configuration from config file, if provided + // Nb. --config overrides command line flags + glog.V(1).Infoln("loading configuration from the given file") + + b, err := ioutil.ReadFile(cfgPath) + if err != nil { + return nil, fmt.Errorf("unable to read config from %q [%v]", cfgPath, err) + } + runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(kubeadmapiv1alpha1.SchemeGroupVersion, kubeadmapiv1alpha2.SchemeGroupVersion), b, internalcfg) + } else { + // Takes passed flags into account; the defaulting is executed once again enforcing assignement of + // static default values to cfg only for values not provided with flags + kubeadmscheme.Scheme.Default(defaultversionedcfg) + kubeadmscheme.Scheme.Convert(defaultversionedcfg, internalcfg, nil) + } + + // Applies dynamic defaults to settings not provided with flags + if err := SetJoinDynamicDefaults(internalcfg); err != nil { + return nil, err + } + // Validates cfg (flags/configs + defaults) + if err := validation.ValidateNodeConfiguration(internalcfg).ToAggregate(); err != nil { + return nil, err + } + + return internalcfg, nil +} diff --git a/docs/.generated_docs b/docs/.generated_docs index 75fef06ed88..7a4aefa192b 100644 --- a/docs/.generated_docs +++ b/docs/.generated_docs @@ -61,6 +61,7 @@ docs/admin/kubeadm_config.md docs/admin/kubeadm_config_images.md docs/admin/kubeadm_config_images_list.md docs/admin/kubeadm_config_images_pull.md +docs/admin/kubeadm_config_print-default.md docs/admin/kubeadm_config_upload.md docs/admin/kubeadm_config_upload_from-file.md docs/admin/kubeadm_config_upload_from-flags.md @@ -139,6 +140,7 @@ docs/man/man1/kubeadm-completion.1 docs/man/man1/kubeadm-config-images-list.1 docs/man/man1/kubeadm-config-images-pull.1 docs/man/man1/kubeadm-config-images.1 +docs/man/man1/kubeadm-config-print-default.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-default.md new file mode 100644 index 00000000000..b6fd7a0f989 --- /dev/null +++ b/docs/admin/kubeadm_config_print-default.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-default.1 b/docs/man/man1/kubeadm-config-print-default.1 new file mode 100644 index 00000000000..b6fd7a0f989 --- /dev/null +++ b/docs/man/man1/kubeadm-config-print-default.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.