diff --git a/cmd/kubelet/app/BUILD b/cmd/kubelet/app/BUILD index 963001058b..814a7ce430 100644 --- a/cmd/kubelet/app/BUILD +++ b/cmd/kubelet/app/BUILD @@ -85,6 +85,7 @@ go_library( "//pkg/kubelet/eviction:go_default_library", "//pkg/kubelet/eviction/api:go_default_library", "//pkg/kubelet/kubeletconfig:go_default_library", + "//pkg/kubelet/kubeletconfig/configfiles:go_default_library", "//pkg/kubelet/network:go_default_library", "//pkg/kubelet/network/cni:go_default_library", "//pkg/kubelet/network/kubenet:go_default_library", @@ -92,6 +93,7 @@ go_library( "//pkg/kubelet/server/streaming:go_default_library", "//pkg/kubelet/types:go_default_library", "//pkg/util/configz:go_default_library", + "//pkg/util/filesystem:go_default_library", "//pkg/util/flock:go_default_library", "//pkg/util/io:go_default_library", "//pkg/util/mount:go_default_library", @@ -131,6 +133,7 @@ go_library( "//pkg/volume/vsphere_volume:go_default_library", "//vendor/github.com/golang/glog: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/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index 25ad786809..4f5077e368 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -30,10 +30,12 @@ import ( "path" "path/filepath" "strconv" + "strings" "time" "github.com/golang/glog" "github.com/spf13/cobra" + "github.com/spf13/pflag" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" @@ -74,11 +76,13 @@ import ( dockerremote "k8s.io/kubernetes/pkg/kubelet/dockershim/remote" "k8s.io/kubernetes/pkg/kubelet/eviction" evictionapi "k8s.io/kubernetes/pkg/kubelet/eviction/api" - "k8s.io/kubernetes/pkg/kubelet/kubeletconfig" + dynamickubeletconfig "k8s.io/kubernetes/pkg/kubelet/kubeletconfig" + "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles" "k8s.io/kubernetes/pkg/kubelet/server" "k8s.io/kubernetes/pkg/kubelet/server/streaming" kubetypes "k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/util/configz" + utilfs "k8s.io/kubernetes/pkg/util/filesystem" "k8s.io/kubernetes/pkg/util/flock" kubeio "k8s.io/kubernetes/pkg/util/io" "k8s.io/kubernetes/pkg/util/mount" @@ -96,8 +100,9 @@ const ( // NewKubeletCommand creates a *cobra.Command object with default parameters func NewKubeletCommand() *cobra.Command { + cleanFlagSet := pflag.NewFlagSet(componentKubelet, pflag.ContinueOnError) kubeletFlags := options.NewKubeletFlags() - kubeletConfiguration, err := options.NewKubeletConfiguration() + kubeletConfig, err := options.NewKubeletConfiguration() // programmer error if err != nil { glog.Fatal(err) @@ -124,25 +129,79 @@ is checked every 20 seconds (also configurable with a flag). HTTP server: The kubelet can also listen for HTTP and respond to a simple API (underspec'd currently) to submit a new manifest.`, + // The Kubelet has special flag parsing requirements to enforce flag precedence rules, + // so we do all our parsing manually in Run, below. + // DisableFlagParsing=true provides the full set of flags passed to the kubelet in the + // `args` arg to Run, without Cobra's interference. + DisableFlagParsing: true, Run: func(cmd *cobra.Command, args []string) { + // initial flag parse, since we disable cobra's flag parsing + if err := cleanFlagSet.Parse(args); err != nil { + cmd.Usage() + glog.Fatal(err) + } + + // short-circuit on help + help, err := cleanFlagSet.GetBool("help") + if err != nil { + glog.Fatal(`"help" flag is non-bool, programmer error, please correct`) + } + if help { + cmd.Help() + return + } + // short-circuit on verflag verflag.PrintAndExitIfRequested() - // TODO(mtaufen): won't need this this once dynamic config is GA - // set feature gates so we can check if dynamic config is enabled - if err := utilfeature.DefaultFeatureGate.SetFromMap(kubeletConfiguration.FeatureGates); err != nil { + // log args (separate lines, so we don't overflow line-length limits in logging infrastructure) + glog.V(2).Infof("kubelet flags: %s", strings.Join(args, "\n")) + + // set feature gates from initial flags-based config + if err := utilfeature.DefaultFeatureGate.SetFromMap(kubeletConfig.FeatureGates); err != nil { glog.Fatal(err) } - // validate the initial KubeletFlags, to make sure the dynamic-config-related flags aren't used unless the feature gate is on + + // validate the initial KubeletFlags if err := options.ValidateKubeletFlags(kubeletFlags); err != nil { glog.Fatal(err) } - // bootstrap the kubelet config controller, app.BootstrapKubeletConfigController will check - // feature gates and only turn on relevant parts of the controller - kubeletConfig, kubeletConfigController, err := BootstrapKubeletConfigController( - kubeletConfiguration, kubeletFlags.KubeletConfigFile, kubeletFlags.DynamicConfigDir) - if err != nil { - glog.Fatal(err) + + // load kubelet config file, if provided + if configFile := kubeletFlags.KubeletConfigFile; len(configFile) > 0 { + kubeletConfig, err = loadConfigFile(configFile) + if err != nil { + glog.Fatal(err) + } + // We must enforce flag precedence by re-parsing the command line into the new object. + // This is necessary to preserve backwards-compatibility across binary upgrades. + // See issue #56171 for more details. + if err := kubeletConfigFlagPrecedence(kubeletConfig, args); err != nil { + glog.Fatal(err) + } + // update feature gates based on new config + if err := utilfeature.DefaultFeatureGate.SetFromMap(kubeletConfig.FeatureGates); err != nil { + glog.Fatal(err) + } + } + + // use dynamic kubelet config, if enabled + var kubeletConfigController *dynamickubeletconfig.Controller + if dynamicConfigDir := kubeletFlags.DynamicConfigDir.Value(); len(dynamicConfigDir) > 0 { + kubeletConfig, kubeletConfigController, err = BootstrapKubeletConfigController(kubeletConfig, dynamicConfigDir) + if err != nil { + glog.Fatal(err) + } + // We must enforce flag precedence by re-parsing the command line into the new object. + // This is necessary to preserve backwards-compatibility across binary upgrades. + // See issue #56171 for more details. + if err := kubeletConfigFlagPrecedence(kubeletConfig, args); err != nil { + glog.Fatal(err) + } + // update feature gates based on new config + if err := utilfeature.DefaultFeatureGate.SetFromMap(kubeletConfig.FeatureGates); err != nil { + glog.Fatal(err) + } } // construct a KubeletServer from kubeletFlags and kubeletConfig @@ -174,13 +233,92 @@ HTTP server: The kubelet can also listen for HTTP and respond to a simple API }, } - kubeletFlags.AddFlags(cmd.Flags()) - options.AddKubeletConfigFlags(cmd.Flags(), kubeletConfiguration) - options.AddGlobalFlags(cmd.Flags()) + // keep cleanFlagSet separate, so Cobra doesn't pollute it with the global flags + kubeletFlags.AddFlags(cleanFlagSet) + options.AddKubeletConfigFlags(cleanFlagSet, kubeletConfig) + options.AddGlobalFlags(cleanFlagSet) + cleanFlagSet.BoolP("help", "h", false, fmt.Sprintf("help for %s", cmd.Name())) + + // ugly, but necessary, because Cobra's default UsageFunc and HelpFunc pollute the flagset with global flags + const usageFmt = "Usage:\n %s\n\nFlags:\n%s" + cmd.SetUsageFunc(func(cmd *cobra.Command) error { + fmt.Fprintf(cmd.OutOrStderr(), usageFmt, cmd.UseLine(), cleanFlagSet.FlagUsagesWrapped(2)) + return nil + }) + cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { + fmt.Fprintf(cmd.OutOrStdout(), "%s\n\n"+usageFmt, cmd.Long, cmd.UseLine(), cleanFlagSet.FlagUsagesWrapped(2)) + }) return cmd } +// newFlagSetWithGlobals constructs a new pflag.FlagSet with global flags registered +// on it. +func newFlagSetWithGlobals() *pflag.FlagSet { + fs := pflag.NewFlagSet("", pflag.ExitOnError) + // set the normalize func, similar to k8s.io/apiserver/pkg/util/flag/flags.go:InitFlags + fs.SetNormalizeFunc(flag.WordSepNormalizeFunc) + // explicitly add flags from libs that register global flags + options.AddGlobalFlags(fs) + return fs +} + +// newFakeFlagSet constructs a pflag.FlagSet with the same flags as fs, but where +// all values have noop Set implementations +func newFakeFlagSet(fs *pflag.FlagSet) *pflag.FlagSet { + ret := pflag.NewFlagSet("", pflag.ExitOnError) + ret.SetNormalizeFunc(fs.GetNormalizeFunc()) + fs.VisitAll(func(f *pflag.Flag) { + ret.VarP(flag.NoOp{}, f.Name, f.Shorthand, f.Usage) + }) + return ret +} + +// kubeletConfigFlagPrecedence re-parses flags over the KubeletConfiguration object. +// We must enforce flag precedence by re-parsing the command line into the new object. +// This is necessary to preserve backwards-compatibility across binary upgrades. +// See issue #56171 for more details. +func kubeletConfigFlagPrecedence(kc *kubeletconfiginternal.KubeletConfiguration, args []string) error { + // We use a throwaway kubeletFlags and a fake global flagset to avoid double-parses, + // as some Set implementations accumulate values from multiple flag invocations. + fs := newFakeFlagSet(newFlagSetWithGlobals()) + // register throwaway KubeletFlags + options.NewKubeletFlags().AddFlags(fs) + // register new KubeletConfiguration + options.AddKubeletConfigFlags(fs, kc) + // Remember original feature gates, so we can merge with flag gates later + original := kc.FeatureGates + // re-parse flags + if err := fs.Parse(args); err != nil { + return err + } + // Add back feature gates that were set in the original kc, but not in flags + for k, v := range original { + if _, ok := kc.FeatureGates[k]; !ok { + kc.FeatureGates[k] = v + } + } + return nil +} + +func loadConfigFile(name string) (*kubeletconfiginternal.KubeletConfiguration, error) { + const errFmt = "failed to load Kubelet config file %s, error %v" + // compute absolute path based on current working dir + kubeletConfigFile, err := filepath.Abs(name) + if err != nil { + return nil, fmt.Errorf(errFmt, name, err) + } + loader, err := configfiles.NewFsLoader(utilfs.DefaultFs{}, kubeletConfigFile) + if err != nil { + return nil, fmt.Errorf(errFmt, name, err) + } + kc, err := loader.Load() + if err != nil { + return nil, fmt.Errorf(errFmt, name, err) + } + return kc, err +} + // UnsecuredDependencies returns a Dependencies suitable for being run, or an error if the server setup // is not valid. It will not start any background processes, and does not include authentication/authorization func UnsecuredDependencies(s *options.KubeletServer) (*kubelet.Dependencies, error) { @@ -438,9 +576,9 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies) (err error) { } } - // Alpha Dynamic Configuration Implementation; - // if the kubelet config controller is available, inject the latest to start the config and status sync loops - if utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) && kubeDeps.KubeletConfigController != nil && !standaloneMode && !s.RunOnce { + // If the kubelet config controller is available, and dynamic config is enabled, start the config and status sync loops + if utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) && len(s.DynamicConfigDir.Value()) > 0 && + kubeDeps.KubeletConfigController != nil && !standaloneMode && !s.RunOnce { kubeDeps.KubeletConfigController.StartSync(kubeDeps.KubeClient, kubeDeps.EventClient, string(nodeName)) } @@ -916,35 +1054,26 @@ func parseResourceList(m map[string]string) (v1.ResourceList, error) { // BootstrapKubeletConfigController constructs and bootstrap a configuration controller func BootstrapKubeletConfigController(defaultConfig *kubeletconfiginternal.KubeletConfiguration, - kubeletConfigFile string, - dynamicConfigDirFlag flag.StringFlag) (*kubeletconfiginternal.KubeletConfiguration, *kubeletconfig.Controller, error) { - var err error - // Alpha Dynamic Configuration Implementation; this section only loads config from disk, it does not contact the API server - // compute absolute paths based on current working dir - if len(kubeletConfigFile) > 0 { - kubeletConfigFile, err = filepath.Abs(kubeletConfigFile) - if err != nil { - return nil, nil, fmt.Errorf("failed to get absolute path for --config") - } + dynamicConfigDir string) (*kubeletconfiginternal.KubeletConfiguration, *dynamickubeletconfig.Controller, error) { + if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) { + return nil, nil, fmt.Errorf("failed to bootstrap Kubelet config controller, you must enable the DynamicKubeletConfig feature gate") } - dynamicConfigDir := "" - if utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) && dynamicConfigDirFlag.Provided() { - dynamicConfigDir, err = filepath.Abs(dynamicConfigDirFlag.Value()) - if err != nil { - return nil, nil, fmt.Errorf("failed to get absolute path for --dynamic-config-dir") - } + if len(dynamicConfigDir) == 0 { + return nil, nil, fmt.Errorf("cannot bootstrap Kubelet config controller, --dynamic-config-dir was not provided") } - // get the latest KubeletConfiguration checkpoint from disk, or load the kubelet config file or default config if no valid checkpoints exist - kubeletConfigController, err := kubeletconfig.NewController(defaultConfig, kubeletConfigFile, dynamicConfigDir) + // compute absolute path and bootstrap controller + dir, err := filepath.Abs(dynamicConfigDir) if err != nil { - return nil, nil, fmt.Errorf("failed to construct controller, error: %v", err) + return nil, nil, fmt.Errorf("failed to get absolute path for --dynamic-config-dir=%s", dynamicConfigDir) } - kubeletConfig, err := kubeletConfigController.Bootstrap() + // get the latest KubeletConfiguration checkpoint from disk, or return the default config if no valid checkpoints exist + c := dynamickubeletconfig.NewController(defaultConfig, dir) + kc, err := c.Bootstrap() if err != nil { return nil, nil, fmt.Errorf("failed to determine a valid configuration, error: %v", err) } - return kubeletConfig, kubeletConfigController, nil + return kc, c, nil } // RunDockershim only starts the dockershim in current process. This is only used for cri validate testing purpose diff --git a/pkg/kubelet/kubeletconfig/BUILD b/pkg/kubelet/kubeletconfig/BUILD index d5ea255591..ae3e99f674 100644 --- a/pkg/kubelet/kubeletconfig/BUILD +++ b/pkg/kubelet/kubeletconfig/BUILD @@ -18,7 +18,6 @@ go_library( "//pkg/kubelet/apis/kubeletconfig/validation:go_default_library", "//pkg/kubelet/kubeletconfig/checkpoint:go_default_library", "//pkg/kubelet/kubeletconfig/checkpoint/store:go_default_library", - "//pkg/kubelet/kubeletconfig/configfiles:go_default_library", "//pkg/kubelet/kubeletconfig/status:go_default_library", "//pkg/kubelet/kubeletconfig/util/equal:go_default_library", "//pkg/kubelet/kubeletconfig/util/log:go_default_library", diff --git a/pkg/kubelet/kubeletconfig/controller.go b/pkg/kubelet/kubeletconfig/controller.go index b8b344b95c..c91fcc6df3 100644 --- a/pkg/kubelet/kubeletconfig/controller.go +++ b/pkg/kubelet/kubeletconfig/controller.go @@ -31,7 +31,6 @@ import ( "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint" "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store" - "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles" "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/status" utillog "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log" utilpanic "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/panic" @@ -42,23 +41,12 @@ const ( checkpointsDir = "checkpoints" ) -// Controller is the controller which, among other things: -// - loads configuration from disk -// - checkpoints configuration to disk -// - downloads new configuration from the API server -// - validates configuration -// - tracks the last-known-good configuration, and rolls-back to last-known-good when necessary +// Controller manages syncing dynamic Kubelet configurations // For more information, see the proposal: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/node/dynamic-kubelet-configuration.md type Controller struct { - // dynamicConfig, if true, indicates that we should sync config from the API server - dynamicConfig bool - // defaultConfig is the configuration to use if no initConfig is provided defaultConfig *kubeletconfig.KubeletConfiguration - // fileLoader is for loading the Kubelet's local config files from disk - fileLoader configfiles.Loader - // pendingConfigSource; write to this channel to indicate that the config source needs to be synced from the API server pendingConfigSource chan bool @@ -73,37 +61,14 @@ type Controller struct { } // NewController constructs a new Controller object and returns it. Directory paths must be absolute. -// If the `kubeletConfigFile` is an empty string, skips trying to load the kubelet config file. -// If the `dynamicConfigDir` is an empty string, skips trying to load checkpoints or download new config, -// but will still sync the ConfigOK condition if you call StartSync with a non-nil client. -func NewController(defaultConfig *kubeletconfig.KubeletConfiguration, - kubeletConfigFile string, - dynamicConfigDir string) (*Controller, error) { - var err error - - fs := utilfs.DefaultFs{} - - var fileLoader configfiles.Loader - if len(kubeletConfigFile) > 0 { - fileLoader, err = configfiles.NewFsLoader(fs, kubeletConfigFile) - if err != nil { - return nil, err - } - } - dynamicConfig := false - if len(dynamicConfigDir) > 0 { - dynamicConfig = true - } - +func NewController(defaultConfig *kubeletconfig.KubeletConfiguration, dynamicConfigDir string) *Controller { return &Controller{ - dynamicConfig: dynamicConfig, defaultConfig: defaultConfig, // channels must have capacity at least 1, since we signal with non-blocking writes pendingConfigSource: make(chan bool, 1), configOK: status.NewConfigOKCondition(), - checkpointStore: store.NewFsStore(fs, filepath.Join(dynamicConfigDir, checkpointsDir)), - fileLoader: fileLoader, - }, nil + checkpointStore: store.NewFsStore(utilfs.DefaultFs{}, filepath.Join(dynamicConfigDir, checkpointsDir)), + } } // Bootstrap attempts to return a valid KubeletConfiguration based on the configuration of the Controller, @@ -111,28 +76,19 @@ func NewController(defaultConfig *kubeletconfig.KubeletConfiguration, func (cc *Controller) Bootstrap() (*kubeletconfig.KubeletConfiguration, error) { utillog.Infof("starting controller") - // Load and validate the local config (defaults + flags, file) - local, err := cc.loadLocalConfig() - if err != nil { - return nil, err - } // Assert: the default and file configs are both valid - - // if dynamic config is disabled, we just stop here - if !cc.dynamicConfig { - // NOTE(mtaufen): We still need to update the status. - // We expect to be able to disable dynamic config but still get a status update about the config. - // This is because the feature gate covers dynamic config AND config status reporting, while the - // --dynamic-config-dir flag just covers dynamic config. - cc.configOK.Set(status.NotDynamicLocalMessage, status.NotDynamicLocalReason, apiv1.ConditionTrue) - return local, nil - } // Assert: dynamic config is enabled + // ALWAYS validate the local config. This makes incorrectly provisioned nodes an error. + // It must be valid because it is the default last-known-good config. + utillog.Infof("validating local config") + if err := validation.ValidateKubeletConfiguration(cc.defaultConfig); err != nil { + return nil, fmt.Errorf("local config failed validation, error: %v", err) + } // ensure the filesystem is initialized if err := cc.initializeDynamicConfigDir(); err != nil { return nil, err } - assigned, curSource, reason, err := cc.loadAssignedConfig(local) + assigned, curSource, reason, err := cc.loadAssignedConfig(cc.defaultConfig) if err == nil { // set the status to indicate we will use the assigned config if curSource != nil { @@ -159,7 +115,7 @@ func (cc *Controller) Bootstrap() (*kubeletconfig.KubeletConfiguration, error) { utillog.Errorf(fmt.Sprintf("%s, error: %v", reason, err)) // load the last-known-good config - lkg, lkgSource, err := cc.loadLastKnownGoodConfig(local) + lkg, lkgSource, err := cc.loadLastKnownGoodConfig(cc.defaultConfig) if err != nil { return nil, err } @@ -195,57 +151,26 @@ func (cc *Controller) StartSync(client clientset.Interface, eventClient v1core.E }, 10*time.Second, 0.2, true, wait.NeverStop) })() - // only sync to new, remotely provided configurations if dynamic config was enabled - if cc.dynamicConfig { - cc.informer = newSharedNodeInformer(client, nodeName, - cc.onAddNodeEvent, cc.onUpdateNodeEvent, cc.onDeleteNodeEvent) - // start the informer loop - // Rather than use utilruntime.HandleCrash, which doesn't actually crash in the Kubelet, - // we use HandlePanic to manually call the panic handlers and then crash. - // We have a better chance of recovering normal operation if we just restart the Kubelet in the event - // of a Go runtime error. - go utilpanic.HandlePanic(func() { - utillog.Infof("starting Node informer sync loop") - cc.informer.Run(wait.NeverStop) - })() + cc.informer = newSharedNodeInformer(client, nodeName, + cc.onAddNodeEvent, cc.onUpdateNodeEvent, cc.onDeleteNodeEvent) + // start the informer loop + // Rather than use utilruntime.HandleCrash, which doesn't actually crash in the Kubelet, + // we use HandlePanic to manually call the panic handlers and then crash. + // We have a better chance of recovering normal operation if we just restart the Kubelet in the event + // of a Go runtime error. + go utilpanic.HandlePanic(func() { + utillog.Infof("starting Node informer sync loop") + cc.informer.Run(wait.NeverStop) + })() - // start the config source sync loop - go utilpanic.HandlePanic(func() { - utillog.Infof("starting config source sync loop") - wait.JitterUntil(func() { - cc.syncConfigSource(client, eventClient, nodeName) - }, 10*time.Second, 0.2, true, wait.NeverStop) - })() - } else { - utillog.Infof("dynamic config not enabled, will not sync to remote config") - } -} + // start the config source sync loop + go utilpanic.HandlePanic(func() { + utillog.Infof("starting config source sync loop") + wait.JitterUntil(func() { + cc.syncConfigSource(client, eventClient, nodeName) + }, 10*time.Second, 0.2, true, wait.NeverStop) + })() -// loadLocalConfig returns the local config: either the defaults provided to the controller or -// a local config file, if the Kubelet is configured to use the local file -func (cc *Controller) loadLocalConfig() (*kubeletconfig.KubeletConfiguration, error) { - // ALWAYS validate the local configs. This makes incorrectly provisioned nodes an error. - // These must be valid because they are the default last-known-good configs. - utillog.Infof("validating combination of defaults and flags") - if err := validation.ValidateKubeletConfiguration(cc.defaultConfig); err != nil { - return nil, fmt.Errorf("combination of defaults and flags failed validation, error: %v", err) - } - // only attempt to load and validate the Kubelet config file if the user provided a path - if cc.fileLoader != nil { - utillog.Infof("loading Kubelet config file") - kc, err := cc.fileLoader.Load() - if err != nil { - return nil, err - } - // validate the Kubelet config file config - utillog.Infof("validating Kubelet config file") - if err := validation.ValidateKubeletConfiguration(kc); err != nil { - return nil, fmt.Errorf("failed to validate the Kubelet config file, error: %v", err) - } - return kc, nil - } - // if no Kubelet config file config, just return the default - return cc.defaultConfig, nil } // loadAssignedConfig loads the Kubelet's currently assigned config, diff --git a/pkg/kubelet/kubeletconfig/status/status.go b/pkg/kubelet/kubeletconfig/status/status.go index 797f7bbcb5..ee761e0348 100644 --- a/pkg/kubelet/kubeletconfig/status/status.go +++ b/pkg/kubelet/kubeletconfig/status/status.go @@ -34,11 +34,6 @@ import ( // TODO(mtaufen): s/current/assigned, as this is more accurate e.g. if you are using lkg, you aren't currently using "current" :) const ( - // NotDynamicLocalMessage indicates that the Kubelet is using its local config - we send this when dynamic Kubelet config is disabled by omitting the --dynamic-config-dir flag - NotDynamicLocalMessage = "using local config" - // NotDynamicLocalReason indicates that the Kubelet is using its local config - we send this when dynamic Kubelet config is disabled by omitting the --dynamic-config-dir flag - NotDynamicLocalReason = "dynamic config is currently disabled by omission of --dynamic-config-dir Kubelet flag" - // CurLocalMessage indicates that the Kubelet is using its local config, which consists of defaults, flags, and/or local files CurLocalMessage = "using current: local" // LkgLocalMessage indicates that the Kubelet is using its local config, which consists of defaults, flags, and/or local files diff --git a/pkg/version/verflag/verflag.go b/pkg/version/verflag/verflag.go index 0c07805277..18c3ac4424 100644 --- a/pkg/version/verflag/verflag.go +++ b/pkg/version/verflag/verflag.go @@ -91,6 +91,8 @@ var ( versionFlag = Version(versionFlagName, VersionFalse, "Print version information and quit") ) +// AddFlags registers this package's flags on arbitrary FlagSets, such that they point to the +// same value as the global flags. func AddFlags(fs *flag.FlagSet) { fs.AddFlag(flag.Lookup(versionFlagName)) } diff --git a/staging/src/k8s.io/apiserver/pkg/util/flag/BUILD b/staging/src/k8s.io/apiserver/pkg/util/flag/BUILD index 25bf080aae..f6aae4b336 100644 --- a/staging/src/k8s.io/apiserver/pkg/util/flag/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/util/flag/BUILD @@ -32,6 +32,7 @@ go_library( "map_string_bool.go", "map_string_string.go", "namedcertkey_flag.go", + "noop.go", "omitempty.go", "string_flag.go", "tristate.go", diff --git a/staging/src/k8s.io/apiserver/pkg/util/flag/noop.go b/staging/src/k8s.io/apiserver/pkg/util/flag/noop.go new file mode 100644 index 0000000000..03f7f14c0b --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/util/flag/noop.go @@ -0,0 +1,41 @@ +/* +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 flag + +import ( + goflag "flag" + "github.com/spf13/pflag" +) + +// NoOp implements goflag.Value and plfag.Value, +// but has a noop Set implementation +type NoOp struct{} + +var _ goflag.Value = NoOp{} +var _ pflag.Value = NoOp{} + +func (NoOp) String() string { + return "" +} + +func (NoOp) Set(val string) error { + return nil +} + +func (NoOp) Type() string { + return "NoOp" +}