Implement "kops edit cluster --set" and "--unset"

This commit is contained in:
John Gardiner Myers 2021-07-13 13:54:41 -07:00
parent 7122e4d1c7
commit 8be10e96d0
10 changed files with 84 additions and 318 deletions

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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]

1
docs/cli/kops_set.md generated
View File

@ -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.

View File

@ -1,57 +0,0 @@
<!--- This file is automatically generated by make gen-cli-docs; changes should be made in the go CLI command code (under cmd/kops) -->
## 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.

View File

@ -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.

View File

@ -1,56 +0,0 @@
<!--- This file is automatically generated by make gen-cli-docs; changes should be made in the go CLI command code (under cmd/kops) -->
## 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.