kubeadm: Implement the 'kubeadm config' command

This commit is contained in:
Lucas Käldström 2017-08-22 18:49:34 +03:00
parent a4b719dcab
commit 28117323bc
No known key found for this signature in database
GPG Key ID: 3FA3783D77751514
5 changed files with 236 additions and 35 deletions

View File

@ -11,6 +11,7 @@ go_library(
srcs = [
"cmd.go",
"completion.go",
"config.go",
"init.go",
"join.go",
"reset.go",
@ -57,6 +58,7 @@ go_library(
"//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/renstrom/dedent:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/github.com/spf13/pflag: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/fields:go_default_library",

View File

@ -17,6 +17,7 @@ limitations under the License.
package cmd
import (
"fmt"
"io"
"github.com/renstrom/dedent"
@ -68,6 +69,7 @@ func NewKubeadmCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
cmds.SetGlobalNormalizationFunc(flag.WarnWordSepNormalizeFunc)
cmds.AddCommand(NewCmdCompletion(out, ""))
cmds.AddCommand(NewCmdConfig(out))
cmds.AddCommand(NewCmdInit(out))
cmds.AddCommand(NewCmdJoin(out))
cmds.AddCommand(NewCmdReset(out))
@ -84,3 +86,18 @@ func NewKubeadmCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
return cmds
}
// subCmdRunE returns a function that handles a case where a subcommand must be specified
// Without this callback, if a user runs just the command without a subcommand,
// or with an invalid subcommand, cobra will print usage information, but still exit cleanly.
// We want to return an error code in these cases so that the
// user knows that their command was invalid.
func subCmdRunE(name string) func(*cobra.Command, []string) error {
return func(_ *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("missing subcommand; %q is not meant to be run on its own", name)
}
return fmt.Errorf("invalid subcommand: %q", args[0])
}
}

View File

@ -0,0 +1,177 @@
/*
Copyright 2016 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 cmd
import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientset "k8s.io/client-go/kubernetes"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadconfig"
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"
"k8s.io/kubernetes/pkg/api"
)
func NewCmdConfig(out io.Writer) *cobra.Command {
var kubeConfigFile string
cmd := &cobra.Command{
Use: "config",
Short: "Manage configuration for a kubeadm cluster persisted in a ConfigMap in the cluster.",
Long: fmt.Sprintf(dedent.Dedent(`
There is a ConfigMap in the %s namespace called %q that kubeadm uses to store internal configuration about the
cluster. kubeadm CLI v1.8.0+ automatically creates this ConfigMap with used config on 'kubeadm init', but if you
initialized your cluster using kubeadm v1.7.x or lower, you must use the 'config upload' command to create this
ConfigMap in order for 'kubeadm upgrade' to be able to configure your upgraded cluster correctly.
`), metav1.NamespaceSystem, constants.MasterConfigurationConfigMap),
// Without this callback, if a user runs just the "upload"
// command without a subcommand, or with an invalid subcommand,
// cobra will print usage information, but still exit cleanly.
// We want to return an error code in these cases so that the
// user knows that their command was invalid.
RunE: subCmdRunE("config"),
}
cmd.PersistentFlags().StringVar(&kubeConfigFile, "kubeconfig", "/etc/kubernetes/admin.conf", "The KubeConfig file to use for talking to the cluster")
cmd.AddCommand(NewCmdConfigUpload(out, &kubeConfigFile))
cmd.AddCommand(NewCmdConfigView(out, &kubeConfigFile))
return cmd
}
func NewCmdConfigUpload(out io.Writer, kubeConfigFile *string) *cobra.Command {
cmd := &cobra.Command{
Use: "upload",
Short: "Upload configuration about the current state so 'kubeadm upgrade' later can know how to configure the upgraded cluster",
RunE: subCmdRunE("upload"),
}
cmd.AddCommand(NewCmdConfigUploadFromFile(out, kubeConfigFile))
cmd.AddCommand(NewCmdConfigUploadFromFlags(out, kubeConfigFile))
return cmd
}
func NewCmdConfigView(out io.Writer, kubeConfigFile *string) *cobra.Command {
return &cobra.Command{
Use: "view",
Short: "View the kubeadm configuration stored inside the cluster",
Long: fmt.Sprintf(dedent.Dedent(`
Using this command, you can view the ConfigMap in the cluster where the configuration for kubeadm is located
The configuration is located in the %q namespace in the %q ConfigMap
`), metav1.NamespaceSystem, constants.MasterConfigurationConfigMap),
Run: func(cmd *cobra.Command, args []string) {
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err)
err = RunConfigView(out, client)
kubeadmutil.CheckErr(err)
},
}
}
func NewCmdConfigUploadFromFile(out io.Writer, kubeConfigFile *string) *cobra.Command {
var cfgPath string
cmd := &cobra.Command{
Use: "from-file",
Short: "Upload a configuration file to the in-cluster ConfigMap for kubeadm configuration",
Long: fmt.Sprintf(dedent.Dedent(`
Using from-file, you can upload configuration to the ConfigMap in the cluster using the same config file you gave to kubeadm init.
If you initialized your cluster using a v1.7.x or lower kubeadm client and used the --config option; you need to run this command with the
same config file before upgrading to v1.8 using 'kubeadm upgrade'.
The configuration is located in the %q namespace in the %q ConfigMap
`), metav1.NamespaceSystem, constants.MasterConfigurationConfigMap),
Run: func(cmd *cobra.Command, args []string) {
if len(cfgPath) == 0 {
kubeadmutil.CheckErr(fmt.Errorf("The --config flag is mandatory"))
}
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err)
// The default configuration is empty; everything should come from the file on disk
defaultcfg := &kubeadmapiext.MasterConfiguration{}
// Upload the configuration using the file; don't care about the defaultcfg really
err = uploadConfiguration(client, cfgPath, defaultcfg)
kubeadmutil.CheckErr(err)
},
}
cmd.Flags().StringVar(&cfgPath, "config", "", "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
return cmd
}
func NewCmdConfigUploadFromFlags(out io.Writer, kubeConfigFile *string) *cobra.Command {
cfg := &kubeadmapiext.MasterConfiguration{}
api.Scheme.Default(cfg)
cmd := &cobra.Command{
Use: "from-flags",
Short: "Create the in-cluster configuration file for the first time from using flags",
Long: fmt.Sprintf(dedent.Dedent(`
Using from-flags, you can upload configuration to the ConfigMap in the cluster using the same flags you'd give to kubeadm init.
If you initialized your cluster using a v1.7.x or lower kubeadm client and set some flag; you need to run this command with the
same flags before upgrading to v1.8 using 'kubeadm upgrade'.
`), metav1.NamespaceSystem, constants.MasterConfigurationConfigMap),
Run: func(cmd *cobra.Command, args []string) {
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err)
// Default both statically and dynamically, convert to internal API type, and validate everything
// The cfgPath argument is unset here as we shouldn't load a config file from disk, just go with cfg
err = uploadConfiguration(client, "", cfg)
kubeadmutil.CheckErr(err)
},
}
AddInitConfigFlags(cmd.PersistentFlags(), cfg)
return cmd
}
// RunConfigView gets the configuration persisted in the cluster
func RunConfigView(out io.Writer, client clientset.Interface) error {
cfgConfigMap, err := client.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(constants.MasterConfigurationConfigMap, metav1.GetOptions{})
if err != nil {
return err
}
// No need to append \n as that already exists in the ConfigMap
fmt.Fprintf(out, "%s", cfgConfigMap.Data[constants.MasterConfigurationConfigMapKey])
return nil
}
// uploadConfiguration handles the uploading of the configuration internally
func uploadConfiguration(client clientset.Interface, cfgPath string, defaultcfg *kubeadmapiext.MasterConfiguration) error {
// Default both statically and dynamically, convert to internal API type, and validate everything
// First argument is unset here as we shouldn't load a config file from disk
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, defaultcfg)
if err != nil {
return err
}
// Then just call the uploadconfig phase to do the rest of the work
return uploadconfig.UploadConfiguration(internalcfg, client)
}

View File

@ -26,6 +26,7 @@ import (
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime"
clientset "k8s.io/client-go/kubernetes"
@ -109,70 +110,81 @@ func NewCmdInit(out io.Writer) *cobra.Command {
},
}
cmd.PersistentFlags().StringVar(
AddInitConfigFlags(cmd.PersistentFlags(), cfg)
AddInitOtherFlags(cmd.PersistentFlags(), &cfgPath, &skipPreFlight, &skipTokenPrint, &dryRun)
return cmd
}
// AddInitConfigFlags adds init flags bound to the config to the specified flagset
func AddInitConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiext.MasterConfiguration) {
flagSet.StringVar(
&cfg.API.AdvertiseAddress, "apiserver-advertise-address", cfg.API.AdvertiseAddress,
"The IP address the API Server will advertise it's listening on. 0.0.0.0 means the default network interface's address.",
)
cmd.PersistentFlags().Int32Var(
flagSet.Int32Var(
&cfg.API.BindPort, "apiserver-bind-port", cfg.API.BindPort,
"Port for the API Server to bind to",
)
cmd.PersistentFlags().StringVar(
flagSet.StringVar(
&cfg.Networking.ServiceSubnet, "service-cidr", cfg.Networking.ServiceSubnet,
"Use alternative range of IP address for service VIPs",
)
cmd.PersistentFlags().StringVar(
flagSet.StringVar(
&cfg.Networking.PodSubnet, "pod-network-cidr", cfg.Networking.PodSubnet,
"Specify range of IP addresses for the pod network; if set, the control plane will automatically allocate CIDRs for every node",
)
cmd.PersistentFlags().StringVar(
flagSet.StringVar(
&cfg.Networking.DNSDomain, "service-dns-domain", cfg.Networking.DNSDomain,
`Use alternative domain for services, e.g. "myorg.internal"`,
)
cmd.PersistentFlags().StringVar(
flagSet.StringVar(
&cfg.KubernetesVersion, "kubernetes-version", cfg.KubernetesVersion,
`Choose a specific Kubernetes version for the control plane`,
)
cmd.PersistentFlags().StringVar(
flagSet.StringVar(
&cfg.CertificatesDir, "cert-dir", cfg.CertificatesDir,
`The path where to save and store the certificates`,
)
cmd.PersistentFlags().StringSliceVar(
flagSet.StringSliceVar(
&cfg.APIServerCertSANs, "apiserver-cert-extra-sans", cfg.APIServerCertSANs,
`Optional extra altnames to use for the API Server serving cert. Can be both IP addresses and dns names.`,
)
cmd.PersistentFlags().StringVar(
flagSet.StringVar(
&cfg.NodeName, "node-name", cfg.NodeName,
`Specify the node name`,
)
flagSet.StringVar(
&cfg.Token, "token", cfg.Token,
"The token to use for establishing bidirectional trust between nodes and masters.",
)
flagSet.DurationVar(
&cfg.TokenTTL, "token-ttl", cfg.TokenTTL,
"The duration before the bootstrap token is automatically deleted. 0 means 'never expires'.",
)
}
cmd.PersistentFlags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
// AddInitOtherFlags adds init flags that are not bound to a configuration file to the given flagset
func AddInitOtherFlags(flagSet *flag.FlagSet, cfgPath *string, skipPreFlight, skipTokenPrint, dryRun *bool) {
flagSet.StringVar(
cfgPath, "config", *cfgPath,
"Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)",
)
// Note: All flags that are not bound to the cfg object should be whitelisted in cmd/kubeadm/app/apis/kubeadm/validation/validation.go
cmd.PersistentFlags().BoolVar(
&skipPreFlight, "skip-preflight-checks", skipPreFlight,
flagSet.BoolVar(
skipPreFlight, "skip-preflight-checks", *skipPreFlight,
"Skip preflight checks normally run before modifying the system",
)
// Note: All flags that are not bound to the cfg object should be whitelisted in cmd/kubeadm/app/apis/kubeadm/validation/validation.go
cmd.PersistentFlags().BoolVar(
&skipTokenPrint, "skip-token-print", skipTokenPrint,
flagSet.BoolVar(
skipTokenPrint, "skip-token-print", *skipTokenPrint,
"Skip printing of the default bootstrap token generated by 'kubeadm init'",
)
// Note: All flags that are not bound to the cfg object should be whitelisted in cmd/kubeadm/app/apis/kubeadm/validation/validation.go
cmd.PersistentFlags().BoolVar(
&dryRun, "dry-run", dryRun,
flagSet.BoolVar(
dryRun, "dry-run", *dryRun,
"Don't apply any changes; just output what would be done",
)
cmd.PersistentFlags().StringVar(
&cfg.Token, "token", cfg.Token,
"The token to use for establishing bidirectional trust between nodes and masters.")
cmd.PersistentFlags().DurationVar(
&cfg.TokenTTL, "token-ttl", cfg.TokenTTL,
"The duration before the bootstrap token is automatically deleted. 0 means 'never expires'.")
return cmd
}
func NewInit(cfgPath string, cfg *kubeadmapi.MasterConfiguration, skipPreFlight, skipTokenPrint, dryRun bool) (*Init, error) {

View File

@ -17,7 +17,6 @@ limitations under the License.
package cmd
import (
"errors"
"fmt"
"io"
"os"
@ -78,13 +77,7 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
// cobra will print usage information, but still exit cleanly.
// We want to return an error code in these cases so that the
// user knows that their command was invalid.
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("missing subcommand; 'token' is not meant to be run on its own")
} else {
return fmt.Errorf("invalid subcommand: %s", args[0])
}
},
RunE: subCmdRunE("token"),
}
tokenCmd.PersistentFlags().StringVar(&kubeConfigFile,