// ProxyConfigFromNodeConfig builds the kube-proxy configuration from the already-parsed nodeconfig.
519033
func ProxyConfigFromNodeConfig(options configapi.NodeConfig) (*kubeproxyconfig.KubeProxyConfiguration, error) {
	proxyOptions := kubeproxyoptions.NewOptions()
	// get default config
	proxyconfig := proxyOptions.GetConfig()
	defaultedProxyConfig, err := proxyOptions.ApplyDefaults(proxyconfig)
	if err != nil {
		return nil, err
	}
	*proxyconfig = *defaultedProxyConfig

	proxyconfig.HostnameOverride = options.NodeName

	// BindAddress - Override default bind address from our config
	addr := options.ServingInfo.BindAddress
	host, _, err := net.SplitHostPort(addr)
	if err != nil {
		return nil, fmt.Errorf("The provided value to bind to must be an ip:port %q", addr)
	}
	ip := net.ParseIP(host)
	if ip == nil {
		return nil, fmt.Errorf("The provided value to bind to must be an ip:port: %q", addr)
	}
	proxyconfig.BindAddress = ip.String()
	proxyconfig.MetricsBindAddress = "0.0.0.0:10253"
	if arg := options.ProxyArguments["metrics-bind-address"]; len(arg) > 0 {
		proxyconfig.MetricsBindAddress = arg[0]
	}
	delete(options.ProxyArguments, "metrics-bind-address")

	// OOMScoreAdj, ResourceContainer - clear, we don't run in a container
	oomScoreAdj := int32(0)
	proxyconfig.OOMScoreAdj = &oomScoreAdj
	proxyconfig.ResourceContainer = ""

	// use the same client as the node
	proxyconfig.ClientConnection.Kubeconfig = options.MasterKubeConfig

	// ProxyMode, set to iptables
	proxyconfig.Mode = "iptables"

	// IptablesSyncPeriod, set to our config value
	syncPeriod, err := time.ParseDuration(options.IPTablesSyncPeriod)
	if err != nil {
		return nil, fmt.Errorf("Cannot parse the provided ip-tables sync period (%s) : %v", options.IPTablesSyncPeriod, err)
	}
	proxyconfig.IPTables.SyncPeriod = metav1.Duration{
		Duration: syncPeriod,
	}
	masqueradeBit := int32(0)
	proxyconfig.IPTables.MasqueradeBit = &masqueradeBit

	// PortRange, use default
	// HostnameOverride, use default
	// ConfigSyncPeriod, use default
	// MasqueradeAll, use default
	// CleanupAndExit, use default
	// KubeAPIQPS, use default, doesn't apply until we build a separate client
	// KubeAPIBurst, use default, doesn't apply until we build a separate client
	// UDPIdleTimeout, use default

	// Resolve cmd flags to add any user overrides
	fss := apiserverflag.NamedFlagSets{}
	proxyOptions.AddFlags(fss.FlagSet("proxy"))
	if err := cmdflags.Resolve(options.ProxyArguments, fss); len(err) > 0 {
		return nil, kerrors.NewAggregate(err)
	}

	if err := proxyOptions.Complete(); err != nil {
		return nil, err
	}

	return proxyconfig, nil
}
136987
func ConfigFromBackend(coreBackend ...core.ConfigBackend) (MetricsConfig, error) {
	//create default metrics config
	config := &MetricsConfigImpl{backend: lookup.New(coreBackend...)}
	//operationsConfig
	if err := config.loadMetricsConfiguration(); err != nil {
		return nil, errors.WithMessage(err, "metrics configuration load failed")
	}
	return config, nil
}
97286
func applyAuthConfig(fc *FileConfig, cfg *service.Config) error {
	var err error

	if fc.Auth.KubeconfigFile != "" {
		warningMessage := "The auth_service no longer needs kubeconfig_file. It has " +
			"been moved to proxy_service section. This setting is ignored."
		log.Warning(warningMessage)
	}
	cfg.Auth.EnableProxyProtocol, err = utils.ParseOnOff("proxy_protocol", fc.Auth.ProxyProtocol, true)
	if err != nil {
		return trace.Wrap(err)
	}
	if fc.Auth.ListenAddress != "" {
		addr, err := utils.ParseHostPortAddr(fc.Auth.ListenAddress, int(defaults.AuthListenPort))
		if err != nil {
			return trace.Wrap(err)
		}
		cfg.Auth.SSHAddr = *addr
		cfg.AuthServers = append(cfg.AuthServers, *addr)
	}

	// DELETE IN: 2.7.0
	// We have converted this warning to error
	if fc.Auth.DynamicConfig != nil {
		warningMessage := "Dynamic configuration is no longer supported. Refer to " +
			"Teleport documentation for more details: " +
			"http://gravitational.com/teleport/docs/admin-guide"
		return trace.BadParameter(warningMessage)
	}
	// INTERNAL: Authorities (plus Roles) and ReverseTunnels don't follow the
	// same pattern as the rest of the configuration (they are not configuration
	// singletons). However, we need to keep them around while Telekube uses them.
	for _, authority := range fc.Auth.Authorities {
		ca, role, err := authority.Parse()
		if err != nil {
			return trace.Wrap(err)
		}
		cfg.Auth.Authorities = append(cfg.Auth.Authorities, ca)
		cfg.Auth.Roles = append(cfg.Auth.Roles, role)
	}
	for _, t := range fc.Auth.ReverseTunnels {
		tun, err := t.ConvertAndValidate()
		if err != nil {
			return trace.Wrap(err)
		}
		cfg.ReverseTunnels = append(cfg.ReverseTunnels, tun)
	}
	if len(fc.Auth.PublicAddr) != 0 {
		addrs, err := fc.Auth.PublicAddr.Addrs(defaults.AuthListenPort)
		if err != nil {
			return trace.Wrap(err)
		}
		cfg.Auth.PublicAddrs = addrs
	}
	// DELETE IN: 2.4.0
	if len(fc.Auth.TrustedClusters) > 0 {
		warningMessage := "Configuring Trusted Clusters using file configuration has " +
			"been deprecated, Trusted Clusters must now be configured using resources. " +
			"Existing Trusted Cluster relationships will be maintained, but you will " +
			"not be able to add or remove Trusted Clusters using file configuration " +
			"anymore. Refer to Teleport documentation for more details: " +
			"http://gravitational.com/teleport/docs/admin-guide"
		log.Warnf(warningMessage)
	}
	// read in cluster name from file configuration and create services.ClusterName
	cfg.Auth.ClusterName, err = fc.Auth.ClusterName.Parse()
	if err != nil {
		return trace.Wrap(err)
	}
	// read in static tokens from file configuration and create services.StaticTokens
	if fc.Auth.StaticTokens != nil {
		cfg.Auth.StaticTokens, err = fc.Auth.StaticTokens.Parse()
		if err != nil {
			return trace.Wrap(err)
		}
	}
	// read in and set authentication preferences
	if fc.Auth.Authentication != nil {
		authPreference, oidcConnector, err := fc.Auth.Authentication.Parse()
		if err != nil {
			return trace.Wrap(err)
		}
		cfg.Auth.Preference = authPreference

		// DELETE IN: 2.4.0
		if oidcConnector != nil {
			warningMessage := "Configuring OIDC connectors using file configuration is " +
				"no longer supported, OIDC connectors must be configured using resources. " +
				"Existing OIDC connectors will not be removed but you will not be able to " +
				"add, remove, or update them using file configuration. Refer to Teleport " +
				"documentation for more details: " +
				"http://gravitational.com/teleport/docs/admin-guide"
			log.Warnf(warningMessage)
		}
	}

	auditConfig, err := services.AuditConfigFromObject(fc.Storage.Params)
	if err != nil {
		return trace.Wrap(err)
	}
	auditConfig.Type = fc.Storage.Type

	// Set cluster-wide configuration from file configuration.
	cfg.Auth.ClusterConfig, err = services.NewClusterConfig(services.ClusterConfigSpecV3{
		SessionRecording:      fc.Auth.SessionRecording,
		ProxyChecksHostKeys:   fc.Auth.ProxyChecksHostKeys,
		Audit:                 *auditConfig,
		ClientIdleTimeout:     fc.Auth.ClientIdleTimeout,
		DisconnectExpiredCert: fc.Auth.DisconnectExpiredCert,
		KeepAliveInterval:     fc.Auth.KeepAliveInterval,
		KeepAliveCountMax:     fc.Auth.KeepAliveCountMax,
	})
	if err != nil {
		return trace.Wrap(err)
	}

	// read in and set the license file path (not used in open-source version)
	licenseFile := fc.Auth.LicenseFile
	if licenseFile != "" {
		if filepath.IsAbs(licenseFile) {
			cfg.Auth.LicenseFile = licenseFile
		} else {
			cfg.Auth.LicenseFile = filepath.Join(cfg.DataDir, licenseFile)
		}
	}

	return nil
}
1279147
func DefaultConfig() (*Config, error) {
	conf, err := lib.DefaultConfig()
	if err != nil {
		return nil, err
	}
	return &Config{
		Config: *conf,
		APIConfig: APIConfig{
			Listen:             CrioSocketPath,
			StreamAddress:      "127.0.0.1",
			StreamPort:         "0",
			GRPCMaxSendMsgSize: DefaultGRPCMaxMsgSize,
			GRPCMaxRecvMsgSize: DefaultGRPCMaxMsgSize,
		},
	}, nil
}
599890
func (c *Client) NodeBalancerConfigUpdate(confID int, conf NodeBalancerConfigUpdateOpts) error {
	args, err := c.argMarshal(conf)
	if err != nil {
		return err
	}
	args["ConfigID"] = confID

	_, err = c.apiCall("nodebalancer.config.update", args)
	if err != nil {
		return err
	}

	return nil
}
1040952
func (c *Client) NodeBalancerConfigList(nbID int, confID *int) ([]NodeBalancerConfig, error) {
	args := make(map[string]interface{})
	args["NodeBalancerID"] = nbID
	args["ConfigID"] = confID

	data, err := c.apiCall("nodebalancer.config.list", args)
	if err != nil {
		return nil, err
	}

	var ret []NodeBalancerConfig
	err = unmarshalMultiMap(data, &ret)
	if err != nil {
		return nil, err
	}

	return ret, nil
}
439505
func buildKubeletClientConfig(s *options.KubeletServer, nodeName types.NodeName) (*restclient.Config, func(), error) {
	if s.RotateCertificates && utilfeature.DefaultFeatureGate.Enabled(features.RotateKubeletClientCertificate) {
		// Rules for client rotation and the handling of kube config files:
		//
		// 1. If the client provides only a kubeconfig file, we must use that as the initial client
		//    kubeadm needs the initial data in the kubeconfig to be placed into the cert store
		// 2. If the client provides only an initial bootstrap kubeconfig file, we must create a
		//    kubeconfig file at the target location that points to the cert store, but until
		//    the file is present the client config will have no certs
		// 3. If the client provides both and the kubeconfig is valid, we must ignore the bootstrap
		//    kubeconfig.
		// 4. If the client provides both and the kubeconfig is expired or otherwise invalid, we must
		//    replace the kubeconfig with a new file that points to the cert dir
		//
		// The desired configuration for bootstrapping is to use a bootstrap kubeconfig and to have
		// the kubeconfig file be managed by this process. For backwards compatibility with kubeadm,
		// which provides a high powered kubeconfig on the master with cert/key data, we must
		// bootstrap the cert manager with the contents of the initial client config.

		klog.Infof("Client rotation is on, will bootstrap in background")
		certConfig, clientConfig, err := bootstrap.LoadClientConfig(s.KubeConfig, s.BootstrapKubeconfig, s.CertDirectory)
		if err != nil {
			return nil, nil, err
		}

		// use the correct content type for cert rotation, but don't set QPS
		setContentTypeForClient(certConfig, s.ContentType)

		kubeClientConfigOverrides(s, clientConfig)

		clientCertificateManager, err := buildClientCertificateManager(certConfig, clientConfig, s.CertDirectory, nodeName)
		if err != nil {
			return nil, nil, err
		}

		// the rotating transport will use the cert from the cert manager instead of these files
		transportConfig := restclient.AnonymousClientConfig(clientConfig)

		// we set exitAfter to five minutes because we use this client configuration to request new certs - if we are unable
		// to request new certs, we will be unable to continue normal operation. Exiting the process allows a wrapper
		// or the bootstrapping credentials to potentially lay down new initial config.
		closeAllConns, err := kubeletcertificate.UpdateTransport(wait.NeverStop, transportConfig, clientCertificateManager, 5*time.Minute)
		if err != nil {
			return nil, nil, err
		}

		klog.V(2).Info("Starting client certificate rotation.")
		clientCertificateManager.Start()

		return transportConfig, closeAllConns, nil
	}

	if len(s.BootstrapKubeconfig) > 0 {
		if err := bootstrap.LoadClientCert(s.KubeConfig, s.BootstrapKubeconfig, s.CertDirectory, nodeName); err != nil {
			return nil, nil, err
		}
	}

	clientConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		&clientcmd.ClientConfigLoadingRules{ExplicitPath: s.KubeConfig},
		&clientcmd.ConfigOverrides{},
	).ClientConfig()
	if err != nil {
		return nil, nil, fmt.Errorf("invalid kubeconfig: %v", err)
	}

	kubeClientConfigOverrides(s, clientConfig)

	return clientConfig, nil, nil
}
1007214
func CreateKubeAPIServerConfig(
	s completedServerRunOptions,
	nodeTunneler tunneler.Tunneler,
	proxyTransport *http.Transport,
) (
	config *master.Config,
	insecureServingInfo *genericapiserver.DeprecatedInsecureServingInfo,
	serviceResolver aggregatorapiserver.ServiceResolver,
	pluginInitializers []admission.PluginInitializer,
	admissionPostStartHook genericapiserver.PostStartHookFunc,
	lastErr error,
) {
	var genericConfig *genericapiserver.Config
	var storageFactory *serverstorage.DefaultStorageFactory
	var versionedInformers clientgoinformers.SharedInformerFactory
	genericConfig, versionedInformers, insecureServingInfo, serviceResolver, pluginInitializers, admissionPostStartHook, storageFactory, lastErr = buildGenericConfig(s.ServerRunOptions, proxyTransport)
	if lastErr != nil {
		return
	}

	if _, port, err := net.SplitHostPort(s.Etcd.StorageConfig.Transport.ServerList[0]); err == nil && port != "0" && len(port) != 0 {
		if err := utilwait.PollImmediate(etcdRetryInterval, etcdRetryLimit*etcdRetryInterval, preflight.EtcdConnection{ServerList: s.Etcd.StorageConfig.Transport.ServerList}.CheckEtcdServers); err != nil {
			lastErr = fmt.Errorf("error waiting for etcd connection: %v", err)
			return
		}
	}

	capabilities.Initialize(capabilities.Capabilities{
		AllowPrivileged: s.AllowPrivileged,
		// TODO(vmarmol): Implement support for HostNetworkSources.
		PrivilegedSources: capabilities.PrivilegedSources{
			HostNetworkSources: []string{},
			HostPIDSources:     []string{},
			HostIPCSources:     []string{},
		},
		PerConnectionBandwidthLimitBytesPerSec: s.MaxConnectionBytesPerSec,
	})

	serviceIPRange, apiServerServiceIP, lastErr := master.DefaultServiceIPRange(s.ServiceClusterIPRange)
	if lastErr != nil {
		return
	}

	clientCA, lastErr := readCAorNil(s.Authentication.ClientCert.ClientCA)
	if lastErr != nil {
		return
	}
	requestHeaderProxyCA, lastErr := readCAorNil(s.Authentication.RequestHeader.ClientCAFile)
	if lastErr != nil {
		return
	}

	config = &master.Config{
		GenericConfig: genericConfig,
		ExtraConfig: master.ExtraConfig{
			ClientCARegistrationHook: master.ClientCARegistrationHook{
				ClientCA:                         clientCA,
				RequestHeaderUsernameHeaders:     s.Authentication.RequestHeader.UsernameHeaders,
				RequestHeaderGroupHeaders:        s.Authentication.RequestHeader.GroupHeaders,
				RequestHeaderExtraHeaderPrefixes: s.Authentication.RequestHeader.ExtraHeaderPrefixes,
				RequestHeaderCA:                  requestHeaderProxyCA,
				RequestHeaderAllowedNames:        s.Authentication.RequestHeader.AllowedNames,
			},

			APIResourceConfigSource: storageFactory.APIResourceConfigSource,
			StorageFactory:          storageFactory,
			EventTTL:                s.EventTTL,
			KubeletClientConfig:     s.KubeletConfig,
			EnableLogsSupport:       s.EnableLogsHandler,
			ProxyTransport:          proxyTransport,

			Tunneler: nodeTunneler,

			ServiceIPRange:       serviceIPRange,
			APIServerServiceIP:   apiServerServiceIP,
			APIServerServicePort: 443,

			ServiceNodePortRange:      s.ServiceNodePortRange,
			KubernetesServiceNodePort: s.KubernetesServiceNodePort,

			EndpointReconcilerType: reconcilers.Type(s.EndpointReconcilerType),
			MasterCount:            s.MasterCount,

			ServiceAccountIssuer:        s.ServiceAccountIssuer,
			ServiceAccountMaxExpiration: s.ServiceAccountTokenMaxExpiration,

			VersionedInformers: versionedInformers,
		},
	}

	if nodeTunneler != nil {
		// Use the nodeTunneler's dialer to connect to the kubelet
		config.ExtraConfig.KubeletClientConfig.Dial = nodeTunneler.Dial
	}

	return
}
298537
func ReadProxyConfig(path string) (c ProxyConfig, err error) {
	f, err := os.Open(path)
	if err != nil {
		return c, err
	}
	defer f.Close()
	c, err = readProxyConfig(f)
	c.applyDefaults()
	return
}
1319010
func (cfg *centOSCloudConfig) PackageProxy() string {
	proxy, _ := cfg.attrs["package_proxy"].(string)
	return proxy
}
424345
func (s *Server) GetNodeConfig(c context.Context, n *pb.Node) (*pb.RingConf, error) {
	s.RLock()
	defer s.RUnlock()
	node := s.r.Node(n.Id)
	if node == nil {
		return &pb.RingConf{}, fmt.Errorf("Node %d not found", n.Id)
	}

	config := &pb.RingConf{
		Status: &pb.RingStatus{Status: true, Version: s.r.Version()},
		Conf:   &pb.Conf{Conf: node.Config(), RestartRequired: false},
	}
	s.ctxlog.Println(config)
	return config, nil
}
758524
func (g *gatewayFlags) addFlagsToConfig(loadedConfig *config.GatewayConfig) {
	if g.operation.IsSet() {
		loadedConfig.ClusterOperation = pointer.String(g.operation.String())
	}
}
945713
func MergeConfig(a, b *Config) *Config {
	var result Config = *a

	// Copy the strings if they're set
	if b.NodeName != "" {
		result.NodeName = b.NodeName
	}
	if b.Role != "" {
		result.Role = b.Role
	}
	if b.DisableCoordinates == true {
		result.DisableCoordinates = true
	}
	if b.Tags != nil {
		if result.Tags == nil {
			result.Tags = make(map[string]string)
		}
		for name, value := range b.Tags {
			result.Tags[name] = value
		}
	}
	if b.BindAddr != "" {
		result.BindAddr = b.BindAddr
	}
	if b.AdvertiseAddr != "" {
		result.AdvertiseAddr = b.AdvertiseAddr
	}
	if b.EncryptKey != "" {
		result.EncryptKey = b.EncryptKey
	}
	if b.LogLevel != "" {
		result.LogLevel = b.LogLevel
	}
	if b.Protocol > 0 {
		result.Protocol = b.Protocol
	}
	if b.RPCAddr != "" {
		result.RPCAddr = b.RPCAddr
	}
	if b.RPCAuthKey != "" {
		result.RPCAuthKey = b.RPCAuthKey
	}
	if b.ReplayOnJoin != false {
		result.ReplayOnJoin = b.ReplayOnJoin
	}
	if b.Profile != "" {
		result.Profile = b.Profile
	}
	if b.SnapshotPath != "" {
		result.SnapshotPath = b.SnapshotPath
	}
	if b.LeaveOnTerm == true {
		result.LeaveOnTerm = true
	}
	if b.SkipLeaveOnInt == true {
		result.SkipLeaveOnInt = true
	}
	if b.Discover != "" {
		result.Discover = b.Discover
	}
	if b.Interface != "" {
		result.Interface = b.Interface
	}
	if b.ReconnectInterval != 0 {
		result.ReconnectInterval = b.ReconnectInterval
	}
	if b.ReconnectTimeout != 0 {
		result.ReconnectTimeout = b.ReconnectTimeout
	}
	if b.TombstoneTimeout != 0 {
		result.TombstoneTimeout = b.TombstoneTimeout
	}
	if b.DisableNameResolution {
		result.DisableNameResolution = true
	}
	if b.TagsFile != "" {
		result.TagsFile = b.TagsFile
	}
	if b.KeyringFile != "" {
		result.KeyringFile = b.KeyringFile
	}
	if b.EnableSyslog {
		result.EnableSyslog = true
	}
	if b.RetryMaxAttempts != 0 {
		result.RetryMaxAttempts = b.RetryMaxAttempts
	}
	if b.RetryInterval != 0 {
		result.RetryInterval = b.RetryInterval
	}
	if b.RejoinAfterLeave {
		result.RejoinAfterLeave = true
	}
	if b.SyslogFacility != "" {
		result.SyslogFacility = b.SyslogFacility
	}
	if b.StatsiteAddr != "" {
		result.StatsiteAddr = b.StatsiteAddr
	}
	if b.StatsdAddr != "" {
		result.StatsdAddr = b.StatsdAddr
	}
	if b.QueryResponseSizeLimit != 0 {
		result.QueryResponseSizeLimit = b.QueryResponseSizeLimit
	}
	if b.QuerySizeLimit != 0 {
		result.QuerySizeLimit = b.QuerySizeLimit
	}
	if b.UserEventSizeLimit != 0 {
		result.UserEventSizeLimit = b.UserEventSizeLimit
	}
	if b.BroadcastTimeout != 0 {
		result.BroadcastTimeout = b.BroadcastTimeout
	}
	result.EnableCompression = b.EnableCompression

	// Copy the event handlers
	result.EventHandlers = make([]string, 0, len(a.EventHandlers)+len(b.EventHandlers))
	result.EventHandlers = append(result.EventHandlers, a.EventHandlers...)
	result.EventHandlers = append(result.EventHandlers, b.EventHandlers...)

	// Copy the start join addresses
	result.StartJoin = make([]string, 0, len(a.StartJoin)+len(b.StartJoin))
	result.StartJoin = append(result.StartJoin, a.StartJoin...)
	result.StartJoin = append(result.StartJoin, b.StartJoin...)

	// Copy the retry join addresses
	result.RetryJoin = make([]string, 0, len(a.RetryJoin)+len(b.RetryJoin))
	result.RetryJoin = append(result.RetryJoin, a.RetryJoin...)
	result.RetryJoin = append(result.RetryJoin, b.RetryJoin...)

	return &result
}
290493
func DefaultConfig() *Config {
	return &Config{
		MetadataExtractor: nil,
		NetclsProgrammer:  nil,
		EnableHostPods:    false,
		Kubeconfig:        "",
		Nodename:          "",
	}
}
587823
func NetworkingEnvironFromModelConfig(configGetter environs.EnvironConfigGetter) (environs.NetworkingEnviron, error) {
	modelConfig, err := configGetter.ModelConfig()
	if err != nil {
		return nil, errors.Annotate(err, "failed to get model config")
	}
	cloudSpec, err := configGetter.CloudSpec()
	if err != nil {
		return nil, errors.Annotate(err, "failed to get cloudspec")
	}
	if cloudSpec.Type == cloud.CloudTypeCAAS {
		return nil, errors.NotSupportedf("CAAS model %q networking", modelConfig.Name())
	}

	env, err := environs.GetEnviron(configGetter, environs.New)
	if err != nil {
		return nil, errors.Annotate(err, "failed to construct a model from config")
	}
	netEnviron, supported := environs.SupportsNetworking(env)
	if !supported {
		// " not supported" will be appended to the message below.
		return nil, errors.NotSupportedf("model %q networking", modelConfig.Name())
	}
	return netEnviron, nil
}
1120370
func (manager *configManager) GetNodeConf(nodeID string) (*NodeConfig, error) {
	if len(nodeID) == 0 {
		return nil, fmt.Errorf("input cannot be nil")
	}

	// get json from kvdb and unmarshal into config
	kvPair, err := manager.kv.Get(getNodeKeyFromNodeID(nodeID))
	if err != nil {
		return nil, err
	}

	config := new(NodeConfig)
	if err = json.Unmarshal(kvPair.Value, config); err != nil {
		return nil, err
	}

	return config, nil
}
909978
func NewCmdUpgradeNodeConfig() *cobra.Command {
	flags := &nodeUpgradeFlags{
		kubeConfigPath:    constants.GetKubeletKubeConfigPath(),
		kubeletVersionStr: "",
		dryRun:            false,
	}

	cmd := &cobra.Command{
		Use:     "config",
		Short:   "Download the kubelet configuration from the cluster ConfigMap kubelet-config-1.X, where X is the minor version of the kubelet",
		Long:    upgradeNodeConfigLongDesc,
		Example: upgradeNodeConfigExample,
		Run: func(cmd *cobra.Command, args []string) {
			err := RunUpgradeNodeConfig(flags)
			kubeadmutil.CheckErr(err)
		},
	}

	options.AddKubeConfigFlag(cmd.Flags(), &flags.kubeConfigPath)
	cmd.Flags().BoolVar(&flags.dryRun, options.DryRun, flags.dryRun, "Do not change any state, just output the actions that would be performed.")
	cmd.Flags().StringVar(&flags.kubeletVersionStr, "kubelet-version", flags.kubeletVersionStr, "The *desired* version for the kubelet after the upgrade.")
	return cmd
}