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

View File

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

View File

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

View File

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

View File

@ -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",

View File

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

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_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

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.