diff --git a/cmd/kops/create_cluster.go b/cmd/kops/create_cluster.go index 6aa82731eb..999ce1e3b3 100644 --- a/cmd/kops/create_cluster.go +++ b/cmd/kops/create_cluster.go @@ -245,9 +245,12 @@ func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command { // TODO complete VFS paths } - cmd.Flags().StringVar(&options.KubernetesVersion, "kubernetes-version", options.KubernetesVersion, "Version of kubernetes to run (defaults to version in channel)") + cmd.Flags().StringVar(&options.KubernetesVersion, "kubernetes-version", options.KubernetesVersion, "Version of Kubernetes to run (defaults to version in channel)") cmd.RegisterFlagCompletionFunc("kubernetes-version", completeKubernetesVersion) + cmd.Flags().StringSliceVar(&options.KubernetesFeatureGates, "kubernetes-feature-gates", options.KubernetesFeatureGates, "List of Kubernetes feature gates to enable/disable") + cmd.RegisterFlagCompletionFunc("kubernetes-version", completeKubernetesFeatureGates) + cmd.Flags().StringVar(&options.ContainerRuntime, "container-runtime", options.ContainerRuntime, "Container runtime to use: containerd, docker") cmd.RegisterFlagCompletionFunc("container-runtime", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return []string{"containerd", "docker"}, cobra.ShellCompDirectiveNoFileComp @@ -432,7 +435,7 @@ func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command { cmd.RegisterFlagCompletionFunc("os-ext-net", completeOpenstackExternalNet) cmd.Flags().StringVar(&options.OpenstackExternalSubnet, "os-ext-subnet", options.OpenstackExternalSubnet, "External floating subnet to use with the openstack router") cmd.RegisterFlagCompletionFunc("os-ext-subnet", completeOpenstackExternalSubnet) - cmd.Flags().StringVar(&options.OpenstackLBSubnet, "os-lb-floating-subnet", options.OpenstackLBSubnet, "External subnet to use with the kubernetes api") + cmd.Flags().StringVar(&options.OpenstackLBSubnet, "os-lb-floating-subnet", options.OpenstackLBSubnet, "External subnet to use with the Kubernetes API") cmd.RegisterFlagCompletionFunc("os-lb-floating-subnet", completeOpenstackLBSubnet) cmd.Flags().BoolVar(&options.OpenstackStorageIgnoreAZ, "os-kubelet-ignore-az", options.OpenstackStorageIgnoreAZ, "Attach volumes across availability zones") cmd.Flags().BoolVar(&options.OpenstackLBOctavia, "os-octavia", options.OpenstackLBOctavia, "Use octavia load balancer API") @@ -908,6 +911,11 @@ func completeKubernetesVersion(cmd *cobra.Command, args []string, toComplete str return versions.List(), cobra.ShellCompDirectiveNoFileComp } +func completeKubernetesFeatureGates(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO check if there's a way to get the full list of feature gates from k8s libs + return nil, cobra.ShellCompDirectiveNoFileComp +} + func completeInstanceImage(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { // TODO call into cloud provider(s) to get list of valid images return nil, cobra.ShellCompDirectiveNoFileComp diff --git a/upup/pkg/fi/cloudup/new_cluster.go b/upup/pkg/fi/cloudup/new_cluster.go index 11df97412d..986e52ec33 100644 --- a/upup/pkg/fi/cloudup/new_cluster.go +++ b/upup/pkg/fi/cloudup/new_cluster.go @@ -63,6 +63,8 @@ type NewClusterOptions struct { DiscoveryStore string // KubernetesVersion is the version of Kubernetes to deploy. It defaults to the version recommended by the channel. KubernetesVersion string + // KubernetesFeatureGates is the list of Kubernetes feature gates to enable/disable. + KubernetesFeatureGates []string // AdminAccess is the set of CIDR blocks permitted to connect to the Kubernetes API. It defaults to "0.0.0.0/0" and "::/0". AdminAccess []string // SSHAccess is the set of CIDR blocks permitted to connect to SSH on the nodes. It defaults to the value of AdminAccess. @@ -236,6 +238,38 @@ func NewCluster(opt *NewClusterOptions, clientset simple.Clientset) (*NewCluster AnonymousAuth: fi.PtrTo(false), } + if len(opt.KubernetesFeatureGates) > 0 { + cluster.Spec.Kubelet.FeatureGates = make(map[string]string) + cluster.Spec.KubeAPIServer = &api.KubeAPIServerConfig{ + FeatureGates: make(map[string]string), + } + cluster.Spec.KubeControllerManager = &api.KubeControllerManagerConfig{ + FeatureGates: make(map[string]string), + } + cluster.Spec.KubeProxy = &api.KubeProxyConfig{ + FeatureGates: make(map[string]string), + } + cluster.Spec.KubeScheduler = &api.KubeSchedulerConfig{ + FeatureGates: make(map[string]string), + } + + for _, featureGate := range opt.KubernetesFeatureGates { + enabled := true + if featureGate[0] == '+' { + featureGate = featureGate[1:] + } + if featureGate[0] == '-' { + enabled = false + featureGate = featureGate[1:] + } + cluster.Spec.Kubelet.FeatureGates[featureGate] = strconv.FormatBool(enabled) + cluster.Spec.KubeAPIServer.FeatureGates[featureGate] = strconv.FormatBool(enabled) + cluster.Spec.KubeControllerManager.FeatureGates[featureGate] = strconv.FormatBool(enabled) + cluster.Spec.KubeProxy.FeatureGates[featureGate] = strconv.FormatBool(enabled) + cluster.Spec.KubeScheduler.FeatureGates[featureGate] = strconv.FormatBool(enabled) + } + } + if len(opt.AdminAccess) == 0 { opt.AdminAccess = []string{"0.0.0.0/0", "::/0"} }