mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
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:
commit
d15985798e
@ -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",
|
||||
|
@ -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{
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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.
|
||||
|
71
cmd/kubeadm/app/util/config/nodeconfig.go
Normal file
71
cmd/kubeadm/app/util/config/nodeconfig.go
Normal 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
|
||||
}
|
@ -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
|
||||
|
3
docs/admin/kubeadm_config_print-default.md
Normal file
3
docs/admin/kubeadm_config_print-default.md
Normal 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.
|
3
docs/man/man1/kubeadm-config-print-default.1
Normal file
3
docs/man/man1/kubeadm-config-print-default.1
Normal 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.
|
Loading…
Reference in New Issue
Block a user