Merge pull request #63969 from luxas/kubeadm_config_print_defaults

Automatic merge from submit-queue (batch tested with PRs 63969, 63902, 63689, 63973, 63978). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Add a 'kubeadm config print-default' command

**What this PR does / why we need it**:
Improves the UX around creating config files.

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes https://github.com/kubernetes/kubeadm/issues/829

**Special notes for your reviewer**:

**Release note**:

```release-note
kubeadm: A `kubeadm config print-default` command has now been added that you can use as a starting point when writing your own kubeadm configuration files
```
@kubernetes/sig-cluster-lifecycle-pr-reviews @liztio
This commit is contained in:
Kubernetes Submit Queue 2018-05-18 15:59:12 -07:00 committed by GitHub
commit d15985798e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 168 additions and 44 deletions

View File

@ -55,7 +55,6 @@ go_library(
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/util/i18n:go_default_library", "//pkg/kubectl/util/i18n:go_default_library",
"//pkg/util/initsystem:go_default_library", "//pkg/util/initsystem:go_default_library",
"//pkg/util/node:go_default_library",
"//pkg/version:go_default_library", "//pkg/version:go_default_library",
"//vendor/github.com/ghodss/yaml:go_default_library", "//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/golang/glog: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/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/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/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/duration:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/version:go_default_library", "//vendor/k8s.io/apimachinery/pkg/version:go_default_library",

View File

@ -42,6 +42,14 @@ import (
utilsexec "k8s.io/utils/exec" 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 // NewCmdConfig returns cobra.Command for "kubeadm config" command
func NewCmdConfig(out io.Writer) *cobra.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.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(NewCmdConfigUpload(out, &kubeConfigFile))
cmd.AddCommand(NewCmdConfigView(out, &kubeConfigFile)) cmd.AddCommand(NewCmdConfigView(out, &kubeConfigFile))
cmd.AddCommand(NewCmdConfigImages(out)) cmd.AddCommand(NewCmdConfigImages(out))
return cmd 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 // NewCmdConfigUpload returns cobra.Command for "kubeadm config upload" command
func NewCmdConfigUpload(out io.Writer, kubeConfigFile *string) *cobra.Command { func NewCmdConfigUpload(out io.Writer, kubeConfigFile *string) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{

View File

@ -19,7 +19,6 @@ package cmd
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
"path/filepath" "path/filepath"
"strings" "strings"
@ -28,7 +27,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
certutil "k8s.io/client-go/util/cert" certutil "k8s.io/client-go/util/cert"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
@ -41,8 +39,8 @@ import (
kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet" kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight" "k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" 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" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
nodeutil "k8s.io/kubernetes/pkg/util/node"
utilsexec "k8s.io/utils/exec" 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", Short: "Run this on any machine you wish to join an existing cluster",
Long: joinLongDescription, Long: joinLongDescription,
Run: func(cmd *cobra.Command, args []string) { 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(err)
kubeadmutil.CheckErr(j.Validate(cmd))
kubeadmutil.CheckErr(j.Run(out)) 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 // 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 cfg.DiscoveryTokenAPIServers = args
var err error var err error
@ -137,16 +134,16 @@ func NewValidJoin(cfg *kubeadmapiv1alpha2.NodeConfiguration, args []string, skip
return nil, err return nil, err
} }
kubeadmscheme.Scheme.Default(cfg) if err := validation.ValidateMixedArguments(flagSet); err != nil {
internalcfg := &kubeadmapi.NodeConfiguration{} return nil, err
kubeadmscheme.Scheme.Convert(cfg, internalcfg, nil) }
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(ignorePreflightErrors, skipPreFlight) ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(ignorePreflightErrors, skipPreFlight)
if err != nil { if err != nil {
return nil, err 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 // 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 // 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] found NodeName empty")
glog.V(1).Infoln("[join] considered OS hostname as NodeName") glog.V(1).Infoln("[join] considered OS hostname as NodeName")
cfg.NodeName = nodeutil.GetHostname("")
} }
if cfgPath != "" { internalcfg, err := configutil.NodeConfigFileAndDefaultsToInternalConfig(cfgPath, defaultcfg)
glog.V(1).Infoln("[join] reading configuration from", cfgPath) if err != nil {
b, err := ioutil.ReadFile(cfgPath) return nil, err
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)
}
} }
glog.Infoln("[preflight] running pre-flight checks") glog.Infoln("[preflight] running pre-flight checks")
// Then continue with the others... // Then continue with the others...
glog.V(1).Infoln("[preflight] running various checks on all nodes") 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 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") glog.V(1).Infoln("[preflight] starting kubelet service if it's inactive")
preflight.TryStartKubelet(ignorePreflightErrors) preflight.TryStartKubelet(ignorePreflightErrors)
return &Join{cfg: cfg}, nil return &Join{cfg: internalcfg}, 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()
} }
// Run executes worker node provisioning and tries to join an existing cluster. // Run executes worker node provisioning and tries to join an existing cluster.

View File

@ -156,14 +156,15 @@ func TestNewValidJoin(t *testing.T) {
t.Fatalf("Unable to write file %q: %v", tc.cfgPath, err) 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) cmd := NewCmdJoin(&out)
if tc.cmdPersistentFlags != nil { if tc.cmdPersistentFlags != nil {
for key, value := range tc.cmdPersistentFlags { for key, value := range tc.cmdPersistentFlags {
cmd.PersistentFlags().Set(key, value) 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 { if tc.nodeConfig != nil {
join.cfg = tc.nodeConfig join.cfg = tc.nodeConfig
} }
@ -174,11 +175,6 @@ func TestNewValidJoin(t *testing.T) {
if (err != nil) != tc.expectedError { if (err != nil) != tc.expectedError {
t.Fatalf(errorFormat, tc.name, tc.expectedError, (err != nil), err) 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() // check error for NewValidJoin()
} else if (err != nil) != tc.expectedError { } else if (err != nil) != tc.expectedError {
t.Fatalf(errorFormat, tc.name, tc.expectedError, (err != nil), err) t.Fatalf(errorFormat, tc.name, tc.expectedError, (err != nil), err)

View File

@ -8,7 +8,10 @@ load(
go_library( go_library(
name = "go_default_library", name = "go_default_library",
srcs = ["masterconfig.go"], srcs = [
"masterconfig.go",
"nodeconfig.go",
],
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/config", importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/config",
deps = [ deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/apis/kubeadm:go_default_library",

View File

@ -37,7 +37,7 @@ import (
"k8s.io/kubernetes/pkg/util/version" "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 { func SetInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error {
// validate cfg.API.AdvertiseAddress. // validate cfg.API.AdvertiseAddress.

View File

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

View File

@ -61,6 +61,7 @@ docs/admin/kubeadm_config.md
docs/admin/kubeadm_config_images.md 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_print-default.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
@ -139,6 +140,7 @@ docs/man/man1/kubeadm-completion.1
docs/man/man1/kubeadm-config-images-list.1 docs/man/man1/kubeadm-config-images-list.1
docs/man/man1/kubeadm-config-images-pull.1 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-print-default.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.