diff --git a/cmd/kops/edit_cluster.go b/cmd/kops/edit_cluster.go index cb3953a5eb..97b759a8d1 100644 --- a/cmd/kops/edit_cluster.go +++ b/cmd/kops/edit_cluster.go @@ -31,9 +31,11 @@ import ( api "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/apis/kops/validation" "k8s.io/kops/pkg/assets" + "k8s.io/kops/pkg/client/simple" "k8s.io/kops/pkg/commands" "k8s.io/kops/pkg/commands/commandutils" "k8s.io/kops/pkg/edit" + "k8s.io/kops/pkg/featureflag" "k8s.io/kops/pkg/kopscodecs" "k8s.io/kops/pkg/pretty" "k8s.io/kops/pkg/try" @@ -45,10 +47,15 @@ import ( type EditClusterOptions struct { ClusterName string + + // Sets allows setting values directly in the spec. + Sets []string + // Unsets allows unsetting values directly in the spec. + Unsets []string } var ( - editClusterLong = templates.LongDesc(i18n.T(`Edit a cluster configuration. + editClusterLong = pretty.LongDesc(i18n.T(`Edit a cluster configuration. This command changes the desired cluster configuration in the registry. @@ -78,6 +85,17 @@ func NewCmdEditCluster(f *util.Factory, out io.Writer) *cobra.Command { }, } + if featureflag.SpecOverrideFlag.Enabled() { + cmd.Flags().StringSliceVar(&options.Sets, "set", options.Sets, "Directly set values in the spec") + cmd.RegisterFlagCompletionFunc("set", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return nil, cobra.ShellCompDirectiveNoFileComp + }) + cmd.Flags().StringSliceVar(&options.Unsets, "unset", options.Unsets, "Directly unset values in the spec") + cmd.RegisterFlagCompletionFunc("unset", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return nil, cobra.ShellCompDirectiveNoFileComp + }) + } + return cmd } @@ -102,6 +120,25 @@ func RunEditCluster(ctx context.Context, f *util.Factory, out io.Writer, options return err } + if len(options.Unsets)+len(options.Sets) > 0 { + newCluster := oldCluster.DeepCopy() + if err := commands.UnsetClusterFields(options.Unsets, newCluster); err != nil { + return err + } + if err := commands.SetClusterFields(options.Sets, newCluster); err != nil { + return err + } + + failure, err := updateCluster(ctx, clientset, oldCluster, newCluster, instanceGroups) + if err != nil { + return err + } + if failure != "" { + return fmt.Errorf("%s", failure) + } + return nil + } + var ( editor = util_editor.NewDefaultEditor(commandutils.EditorEnvs) ) @@ -200,53 +237,56 @@ func RunEditCluster(ctx context.Context, f *util.Factory, out io.Writer, options continue } - cloud, err := cloudup.BuildCloud(newCluster) - if err != nil { - return err - } - - err = cloudup.PerformAssignments(newCluster, cloud) - if err != nil { - return preservedFile(fmt.Errorf("error populating configuration: %v", err), file, out) - } - - assetBuilder := assets.NewAssetBuilder(newCluster, false) - fullCluster, err := cloudup.PopulateClusterSpec(clientset, newCluster, cloud, assetBuilder) - if err != nil { - results = editResults{ - file: file, - } - results.header.addError(fmt.Sprintf("error populating cluster spec: %s", err)) - containsError = true - continue - } - - err = validation.DeepValidate(fullCluster, instanceGroups, true, cloud) - if err != nil { - results = editResults{ - file: file, - } - results.header.addError(fmt.Sprintf("validation failed: %s", err)) - containsError = true - continue - } - - // Retrieve the current status of the cluster. This will eventually be part of the cluster object. - status, err := cloud.FindClusterStatus(oldCluster) - if err != nil { - return err - } - - // Note we perform as much validation as we can, before writing a bad config - _, err = clientset.UpdateCluster(ctx, newCluster, status) + failure, err := updateCluster(ctx, clientset, oldCluster, newCluster, instanceGroups) if err != nil { return preservedFile(err, file, out) } + if failure != "" { + results = editResults{ + file: file, + } + results.header.addError(failure) + containsError = true + continue + } return nil } } +func updateCluster(ctx context.Context, clientset simple.Clientset, oldCluster, newCluster *api.Cluster, instanceGroups []*api.InstanceGroup) (string, error) { + cloud, err := cloudup.BuildCloud(newCluster) + if err != nil { + return "", err + } + + err = cloudup.PerformAssignments(newCluster, cloud) + if err != nil { + return "", fmt.Errorf("error populating configuration: %v", err) + } + + assetBuilder := assets.NewAssetBuilder(newCluster, false) + fullCluster, err := cloudup.PopulateClusterSpec(clientset, newCluster, cloud, assetBuilder) + if err != nil { + return fmt.Sprintf("error populating cluster spec: %s", err), nil + } + + err = validation.DeepValidate(fullCluster, instanceGroups, true, cloud) + if err != nil { + return fmt.Sprintf("validation failed: %s", err), nil + } + + // Retrieve the current status of the cluster. This will eventually be part of the cluster object. + status, err := cloud.FindClusterStatus(oldCluster) + if err != nil { + return "", err + } + + // Note we perform as much validation as we can, before writing a bad config + _, err = clientset.UpdateCluster(ctx, newCluster, status) + return "", err +} + type editResults struct { header editHeader file string diff --git a/cmd/kops/set.go b/cmd/kops/set.go index 31c4954470..d90939a81c 100644 --- a/cmd/kops/set.go +++ b/cmd/kops/set.go @@ -47,7 +47,6 @@ func NewCmdSet(f *util.Factory, out io.Writer) *cobra.Command { } // create subcommands - cmd.AddCommand(NewCmdSetCluster(f, out)) cmd.AddCommand(NewCmdSetInstancegroup(f, out)) return cmd diff --git a/cmd/kops/set_cluster.go b/cmd/kops/set_cluster.go deleted file mode 100644 index da92b856a4..0000000000 --- a/cmd/kops/set_cluster.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright 2019 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 main - -import ( - "context" - "fmt" - "io" - "strings" - - "github.com/spf13/cobra" - "k8s.io/kubectl/pkg/util/i18n" - "k8s.io/kubectl/pkg/util/templates" - - "k8s.io/kops/cmd/kops/util" - "k8s.io/kops/pkg/commands" -) - -var ( - setClusterLong = templates.LongDesc(i18n.T(`Set a cluster field value. - - This command changes the desired cluster configuration in the registry. - - kops set does not update the cloud resources; to apply the changes use "kops update cluster".`)) - - setClusterExample = templates.Examples(i18n.T(` - # Set cluster to run kubernetes version 1.17.0 - kops set cluster k8s.cluster.site spec.kubernetesVersion=1.17.0 - `)) -) - -// NewCmdSetCluster builds a cobra command for the kops set cluster command -func NewCmdSetCluster(f *util.Factory, out io.Writer) *cobra.Command { - options := &commands.SetClusterOptions{} - - cmd := &cobra.Command{ - Use: "cluster", - Short: i18n.T("Set cluster fields."), - Long: setClusterLong, - Example: setClusterExample, - Run: func(cmd *cobra.Command, args []string) { - ctx := context.TODO() - - for i, arg := range args { - index := strings.Index(arg, "=") - - if i == 0 && index == -1 { - options.ClusterName = arg - } else { - if index == -1 { - exitWithError(fmt.Errorf("unrecognized parameter %q (missing '=')", arg)) - return - } - options.Fields = append(options.Fields, arg) - } - } - - if options.ClusterName == "" { - options.ClusterName = rootCommand.ClusterName(true) - } - - if err := commands.RunSetCluster(ctx, f, cmd, out, options); err != nil { - exitWithError(err) - } - }, - } - - return cmd -} diff --git a/cmd/kops/unset.go b/cmd/kops/unset.go index b1eca48531..546d022849 100644 --- a/cmd/kops/unset.go +++ b/cmd/kops/unset.go @@ -46,7 +46,6 @@ func NewCmdUnset(f *util.Factory, out io.Writer) *cobra.Command { } // create subcommands - cmd.AddCommand(NewCmdUnsetCluster(f, out)) cmd.AddCommand(NewCmdUnsetInstancegroup(f, out)) return cmd diff --git a/cmd/kops/unset_cluster.go b/cmd/kops/unset_cluster.go deleted file mode 100644 index b52f74116f..0000000000 --- a/cmd/kops/unset_cluster.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2019 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 main - -import ( - "context" - "io" - "strings" - - "github.com/spf13/cobra" - "k8s.io/kubectl/pkg/util/i18n" - "k8s.io/kubectl/pkg/util/templates" - - "k8s.io/kops/cmd/kops/util" - "k8s.io/kops/pkg/commands" -) - -var ( - unsetClusterLong = templates.LongDesc(i18n.T(`Unset a cluster field value. - - This command changes the desired cluster configuration in the registry. - - kops unset does not update the cloud resources; to apply the changes use "kops update cluster".`)) - - unsetClusterExample = templates.Examples(i18n.T(` - kops unset cluster k8s-cluster.example.com spec.iam.allowContainerRegistry - `)) -) - -// NewCmdUnsetCluster builds a cobra command for the kops unset cluster command -func NewCmdUnsetCluster(f *util.Factory, out io.Writer) *cobra.Command { - options := &commands.UnsetClusterOptions{} - - cmd := &cobra.Command{ - Use: "cluster", - Short: i18n.T("Unset cluster fields."), - Long: unsetClusterLong, - Example: unsetClusterExample, - Run: func(cmd *cobra.Command, args []string) { - ctx := context.TODO() - - for i, arg := range args { - if i == 0 && !strings.HasPrefix(arg, "spec.") && !strings.HasPrefix(arg, "cluster.") { - options.ClusterName = arg - } else { - options.Fields = append(options.Fields, arg) - } - } - - if options.ClusterName == "" { - options.ClusterName = rootCommand.ClusterName(true) - } - - if err := commands.RunUnsetCluster(ctx, f, cmd, out, options); err != nil { - exitWithError(err) - } - }, - } - - return cmd -} diff --git a/docs/cli/kops_edit_cluster.md b/docs/cli/kops_edit_cluster.md index 45d1fae539..c581435606 100644 --- a/docs/cli/kops_edit_cluster.md +++ b/docs/cli/kops_edit_cluster.md @@ -9,11 +9,12 @@ Edit cluster. Edit a cluster configuration. - This command changes the desired cluster configuration in the registry. +This command changes the desired cluster configuration in the registry. - To set your preferred editor, you can define the EDITOR environment variable. When you have done this, kOps will use the editor that you have set. + To set your preferred editor, you can define the EDITOR environment variable. + When you have done this, kOps will use the editor that you have set. - kops edit does not update the cloud resources, to apply the changes usekops update cluster . +kops edit does not update the cloud resources, to apply the changes use `kops update cluster`. ``` kops edit cluster [CLUSTER] [flags] diff --git a/docs/cli/kops_set.md b/docs/cli/kops_set.md index c48660bfa4..9138d9178a 100644 --- a/docs/cli/kops_set.md +++ b/docs/cli/kops_set.md @@ -49,6 +49,5 @@ Set a configuration field. ### SEE ALSO * [kops](kops.md) - kOps is Kubernetes Operations. -* [kops set cluster](kops_set_cluster.md) - Set cluster fields. * [kops set instancegroup](kops_set_instancegroup.md) - Set instancegroup fields. diff --git a/docs/cli/kops_set_cluster.md b/docs/cli/kops_set_cluster.md deleted file mode 100644 index 74e3d541ce..0000000000 --- a/docs/cli/kops_set_cluster.md +++ /dev/null @@ -1,57 +0,0 @@ - - - -## kops set cluster - -Set cluster fields. - -### Synopsis - -Set a cluster field value. - - This command changes the desired cluster configuration in the registry. - - kops set does not update the cloud resources; to apply the changes use "kops update cluster". - -``` -kops set cluster [flags] -``` - -### Examples - -``` - # Set cluster to run kubernetes version 1.17.0 - kops set cluster k8s.cluster.site spec.kubernetesVersion=1.17.0 -``` - -### Options - -``` - -h, --help help for cluster -``` - -### Options inherited from parent commands - -``` - --add_dir_header If true, adds the file directory to the header of the log messages - --alsologtostderr log to standard error as well as files - --config string yaml config file (default is $HOME/.kops.yaml) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file - --log_file_max_size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) - --logtostderr log to standard error instead of files (default true) - --name string Name of cluster. Overrides KOPS_CLUSTER_NAME environment variable - --one_output If true, only write logs to their native severity level (vs also writing to each lower severity level) - --skip_headers If true, avoid header prefixes in the log messages - --skip_log_headers If true, avoid headers when opening log files - --state string Location of state storage (kops 'config' file). Overrides KOPS_STATE_STORE environment variable - --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level number for the log level verbosity - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging -``` - -### SEE ALSO - -* [kops set](kops_set.md) - Set fields on clusters and other resources. - diff --git a/docs/cli/kops_unset.md b/docs/cli/kops_unset.md index 9605a787ae..275d82e7f2 100644 --- a/docs/cli/kops_unset.md +++ b/docs/cli/kops_unset.md @@ -48,6 +48,5 @@ Unset a configuration field. ### SEE ALSO * [kops](kops.md) - kOps is Kubernetes Operations. -* [kops unset cluster](kops_unset_cluster.md) - Unset cluster fields. * [kops unset instancegroup](kops_unset_instancegroup.md) - Unset instancegroup fields. diff --git a/docs/cli/kops_unset_cluster.md b/docs/cli/kops_unset_cluster.md deleted file mode 100644 index 9acc46fc6b..0000000000 --- a/docs/cli/kops_unset_cluster.md +++ /dev/null @@ -1,56 +0,0 @@ - - - -## kops unset cluster - -Unset cluster fields. - -### Synopsis - -Unset a cluster field value. - - This command changes the desired cluster configuration in the registry. - - kops unset does not update the cloud resources; to apply the changes use "kops update cluster". - -``` -kops unset cluster [flags] -``` - -### Examples - -``` - kops unset cluster k8s-cluster.example.com spec.iam.allowContainerRegistry -``` - -### Options - -``` - -h, --help help for cluster -``` - -### Options inherited from parent commands - -``` - --add_dir_header If true, adds the file directory to the header of the log messages - --alsologtostderr log to standard error as well as files - --config string yaml config file (default is $HOME/.kops.yaml) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file - --log_file_max_size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) - --logtostderr log to standard error instead of files (default true) - --name string Name of cluster. Overrides KOPS_CLUSTER_NAME environment variable - --one_output If true, only write logs to their native severity level (vs also writing to each lower severity level) - --skip_headers If true, avoid header prefixes in the log messages - --skip_log_headers If true, avoid headers when opening log files - --state string Location of state storage (kops 'config' file). Overrides KOPS_STATE_STORE environment variable - --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level number for the log level verbosity - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging -``` - -### SEE ALSO - -* [kops unset](kops_unset.md) - Unset fields on clusters and other resources. -