Skip to content

Commit

Permalink
clusterctl: Add move --to-folder and --from-folder flags
Browse files Browse the repository at this point in the history
These flags perform the same actions clusterctl backup and restore
commands. Also adds deprecation warning to clusterctl backup and restore.
  • Loading branch information
oscr committed Aug 11, 2022
1 parent 2fc48a8 commit 1d2c370
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 70 deletions.
100 changes: 55 additions & 45 deletions cmd/clusterctl/client/move.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package client
import (
"os"

"github.com/pkg/errors"

"sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
)

Expand All @@ -36,11 +38,21 @@ type MoveOptions struct {
// namespace will be used.
Namespace string

// Directory defines the local directory to store or restore cluster objects from
Directory string

// RestoreFromFolder restore configuration from folder. Used with Directory
RestoreFromFolder bool

// BackupToFolder save configuration to folder. Used with Directory
BackupToFolder bool

// DryRun means the move action is a dry run, no real action will be performed
DryRun bool
}

// BackupOptions holds options supported by backup.
// BackupOptions holds options supported by backup. Deprecated and will be removed in a future release.
// Please use MoveOptions.
type BackupOptions struct {
// FromKubeconfig defines the kubeconfig to use for accessing the source management cluster. If empty,
// default rules for kubeconfig discovery will be used.
Expand All @@ -54,7 +66,8 @@ type BackupOptions struct {
Directory string
}

// RestoreOptions holds options supported by restore.
// RestoreOptions holds options supported by restore. Deprecated and will be removed in a future release.
// Please use MoveOptions.
type RestoreOptions struct {
// FromKubeconfig defines the kubeconfig to use for accessing the target management cluster. If empty,
// default rules for kubeconfig discovery will be used.
Expand All @@ -65,37 +78,38 @@ type RestoreOptions struct {
}

func (c *clusterctlClient) Move(options MoveOptions) error {
// Get the client for interacting with the source management cluster.
fromCluster, err := c.clusterClientFactory(ClusterClientFactoryInput{Kubeconfig: options.FromKubeconfig})
if err != nil {
return err
// Both backup and restore makes no sense. It's a complete move.
if options.RestoreFromFolder && options.BackupToFolder {
return errors.Errorf("can't set both BackupFromFolder and RestoreFromFolder")
}

// Ensure this command only runs against management clusters with the current Cluster API contract.
if err := fromCluster.ProviderInventory().CheckCAPIContract(); err != nil {
return err
if options.BackupToFolder {
return c.Backup(BackupOptions{
FromKubeconfig: options.FromKubeconfig,
Namespace: options.Namespace,
Directory: options.Directory,
})
} else if options.RestoreFromFolder {
return c.Restore(RestoreOptions{
ToKubeconfig: options.ToKubeconfig,
Directory: options.Directory,
})
} else {
return c.moveInternal(options)
}
}

// Ensures the custom resource definitions required by clusterctl are in place.
if err := fromCluster.ProviderInventory().EnsureCustomResourceDefinitions(); err != nil {
func (c *clusterctlClient) moveInternal(options MoveOptions) error {
// Get the client for interacting with the source management cluster.
fromCluster, err := c.getClusterClient(options.FromKubeconfig)
if err != nil {
return err
}

var toCluster cluster.Client
if !options.DryRun {
// Get the client for interacting with the target management cluster.
toCluster, err = c.clusterClientFactory(ClusterClientFactoryInput{Kubeconfig: options.ToKubeconfig})
if err != nil {
return err
}

// Ensure this command only runs against management clusters with the current Cluster API contract.
if err := toCluster.ProviderInventory().CheckCAPIContract(); err != nil {
return err
}

// Ensures the custom resource definitions required by clusterctl are in place
if err := toCluster.ProviderInventory().EnsureCustomResourceDefinitions(); err != nil {
if toCluster, err = c.getClusterClient(options.ToKubeconfig); err != nil {
return err
}
}
Expand All @@ -113,22 +127,11 @@ func (c *clusterctlClient) Move(options MoveOptions) error {
}

func (c *clusterctlClient) Backup(options BackupOptions) error {
// Get the client for interacting with the source management cluster.
fromCluster, err := c.clusterClientFactory(ClusterClientFactoryInput{Kubeconfig: options.FromKubeconfig})
fromCluster, err := c.getClusterClient(options.FromKubeconfig)
if err != nil {
return err
}

// Ensure this command only runs against management clusters with the current Cluster API contract.
if err := fromCluster.ProviderInventory().CheckCAPIContract(); err != nil {
return err
}

// Ensures the custom resource definitions required by clusterctl are in place.
if err := fromCluster.ProviderInventory().EnsureCustomResourceDefinitions(); err != nil {
return err
}

// If the option specifying the Namespace is empty, try to detect it.
if options.Namespace == "" {
currentNamespace, err := fromCluster.Proxy().CurrentNamespace()
Expand All @@ -146,25 +149,32 @@ func (c *clusterctlClient) Backup(options BackupOptions) error {
}

func (c *clusterctlClient) Restore(options RestoreOptions) error {
// Get the client for interacting with the source management cluster.
toCluster, err := c.clusterClientFactory(ClusterClientFactoryInput{Kubeconfig: options.ToKubeconfig})
toCluster, err := c.getClusterClient(options.ToKubeconfig)
if err != nil {
return err
}

// Ensure this command only runs against management clusters with the current Cluster API contract.
if err := toCluster.ProviderInventory().CheckCAPIContract(); err != nil {
if _, err := os.Stat(options.Directory); os.IsNotExist(err) {
return err
}

// Ensures the custom resource definitions required by clusterctl are in place.
if err := toCluster.ProviderInventory().EnsureCustomResourceDefinitions(); err != nil {
return err
return toCluster.ObjectMover().Restore(toCluster, options.Directory)
}

func (c *clusterctlClient) getClusterClient(kubeconfig Kubeconfig) (cluster.Client, error) {
cluster, err := c.clusterClientFactory(ClusterClientFactoryInput{Kubeconfig: kubeconfig})
if err != nil {
return nil, err
}

if _, err := os.Stat(options.Directory); os.IsNotExist(err) {
return err
// Ensure this command only runs against management clusters with the current Cluster API contract.
if err := cluster.ProviderInventory().CheckCAPIContract(); err != nil {
return nil, err
}

return toCluster.ObjectMover().Restore(toCluster, options.Directory)
// Ensures the custom resource definitions required by clusterctl are in place.
if err := cluster.ProviderInventory().EnsureCustomResourceDefinitions(); err != nil {
return nil, err
}
return cluster, nil
}
37 changes: 37 additions & 0 deletions cmd/clusterctl/client/move_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,43 @@ func Test_clusterctlClient_Move(t *testing.T) {
},
wantErr: true,
},
{
name: "returns an error if both move to folder and from folder are set",
fields: fields{
client: fakeClientForMove(),
},
args: args{
options: MoveOptions{
BackupToFolder: true,
RestoreFromFolder: true,
},
},
wantErr: true,
},
{
name: "returns an error if only BackupToFolder is set",
fields: fields{
client: fakeClientForMove(),
},
args: args{
options: MoveOptions{
BackupToFolder: true,
},
},
wantErr: true,
},
{
name: "returns an error if only RestoreFromFolder is set",
fields: fields{
client: fakeClientForMove(),
},
args: args{
options: MoveOptions{
RestoreFromFolder: true,
},
},
wantErr: true,
},
}

for _, tt := range tests {
Expand Down
1 change: 1 addition & 0 deletions cmd/clusterctl/cmd/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ var backupCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error {
return runBackup()
},
Deprecated: "use 'clusterctl move --to-folder' instead.",
}

func init() {
Expand Down
38 changes: 33 additions & 5 deletions cmd/clusterctl/cmd/move.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ type moveOptions struct {
toKubeconfig string
toKubeconfigContext string
namespace string
directory string
restoreFromFolder bool
backupToFolder bool
dryRun bool
}

Expand All @@ -46,6 +49,14 @@ var moveCmd = &cobra.Command{
Move Cluster API objects and all dependencies between management clusters.
clusterctl move --to-kubeconfig=target-kubeconfig.yaml`),
Args: cobra.NoArgs,
PreRunE: func(cmd *cobra.Command, args []string) error {
toFolder, _ := cmd.Flags().GetBool("to-folder")
fromFolder, _ := cmd.Flags().GetBool("from-folder")
if toFolder || fromFolder {
return cmd.MarkFlagRequired("directory")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return runMove()
},
Expand All @@ -64,13 +75,27 @@ func init() {
"The namespace where the workload cluster is hosted. If unspecified, the current context's namespace is used.")
moveCmd.Flags().BoolVar(&mo.dryRun, "dry-run", false,
"Enable dry run, don't really perform the move actions")
moveCmd.Flags().BoolVar(&mo.backupToFolder, "to-folder", false,
"Write Cluster API objects and all dependencies from a management cluster to folder.")
moveCmd.Flags().BoolVar(&mo.restoreFromFolder, "from-folder", false,
"Restore Cluster API objects from folder.")
moveCmd.Flags().StringVar(&mo.directory, "directory", "",
"The directory to save Cluster API objects to as yaml files")

// Flags mutually exclusive
moveCmd.MarkFlagsMutuallyExclusive("from-folder", "kubeconfig")
moveCmd.MarkFlagsMutuallyExclusive("to-folder", "to-kubeconfig")
moveCmd.MarkFlagsMutuallyExclusive("from-folder", "to-folder")

RootCmd.AddCommand(moveCmd)
}

func runMove() error {
// if no to kubeconfig provided and it's not a dry run, return error
if mo.toKubeconfig == "" && !mo.dryRun {
if !mo.backupToFolder &&
!mo.restoreFromFolder &&
mo.toKubeconfig == "" &&
!mo.dryRun {
return errors.New("please specify a target cluster using the --to-kubeconfig flag")
}

Expand All @@ -80,9 +105,12 @@ func runMove() error {
}

return c.Move(client.MoveOptions{
FromKubeconfig: client.Kubeconfig{Path: mo.fromKubeconfig, Context: mo.fromKubeconfigContext},
ToKubeconfig: client.Kubeconfig{Path: mo.toKubeconfig, Context: mo.toKubeconfigContext},
Namespace: mo.namespace,
DryRun: mo.dryRun,
FromKubeconfig: client.Kubeconfig{Path: mo.fromKubeconfig, Context: mo.fromKubeconfigContext},
ToKubeconfig: client.Kubeconfig{Path: mo.toKubeconfig, Context: mo.toKubeconfigContext},
RestoreFromFolder: mo.restoreFromFolder,
BackupToFolder: mo.backupToFolder,
Directory: mo.directory,
Namespace: mo.namespace,
DryRun: mo.dryRun,
})
}
1 change: 1 addition & 0 deletions cmd/clusterctl/cmd/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ var restoreCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error {
return runRestore()
},
Deprecated: "use 'clusterctl move --from-folder' instead.",
}

func init() {
Expand Down
4 changes: 4 additions & 0 deletions docs/book/src/clusterctl/commands/additional-commands.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# clusterctl backup

**DEPRECATED. Please use `clusterctl move --to-folder` instead.**

Backup Cluster API objects and all dependencies from a management cluster.

# clusterctl config repositories
Expand All @@ -16,6 +18,8 @@ Simply type `clusterctl help [command]` for full details.

# clusterctl restore

**DEPRECATED. Please use `clusterctl move --from-folder` instead.**

Restore Cluster API objects from file by glob. Object files are searched in the default config directory
or in the provided directory.

Expand Down
40 changes: 20 additions & 20 deletions docs/book/src/clusterctl/commands/commands.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
# clusterctl commands

| Command | Description |
|------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------|
| [`clusterctl alpha rollout`](alpha-rollout.md) | Manages the rollout of Cluster API resources. For example: MachineDeployments. |
| [`clusterctl alpha topology plan`](alpha-topology-plan.md) | Describes the changes to a cluster topology for a given input. |
| [`clusterctl backup`](additional-commands.md#clusterctl-backup) | Backup Cluster API objects and all their dependencies from a management cluster. |
| [`clusterctl completion`](completion.md) | Output shell completion code for the specified shell (bash or zsh). |
| [`clusterctl config`](additional-commands.md#clusterctl-config-repositories) | Display clusterctl configuration. |
| [`clusterctl delete`](delete.md) | Delete one or more providers from the management cluster. |
| [`clusterctl describe cluster`](describe-cluster.md) | Describe workload clusters. |
| [`clusterctl generate cluster`](generate-cluster.md) | Generate templates for creating workload clusters. |
| [`clusterctl generate provider`](generate-provider.md) | Generate templates for provider components. |
| [`clusterctl generate yaml`](generate-yaml.md) | Process yaml using clusterctl's yaml processor. |
| [`clusterctl get kubeconfig`](get-kubeconfig.md) | Gets the kubeconfig file for accessing a workload cluster. |
| [`clusterctl help`](additional-commands.md#clusterctl-help) | Help about any command. |
| [`clusterctl init`](init.md) | Initialize a management cluster. |
| [`clusterctl move`](move.md) | Move Cluster API objects and all their dependencies between management clusters. |
| [`clusterctl restore`](additional-commands.md#clusterctl-restore) | Restore Cluster API objects from file by glob. |
| [`clusterctl upgrade plan`](upgrade.md#upgrade-plan) | Provide a list of recommended target versions for upgrading Cluster API providers in a management cluster. |
| [`clusterctl upgrade apply`](upgrade.md#upgrade-apply) | Apply new versions of Cluster API core and providers in a management cluster. |
| [`clusterctl version`](additional-commands.md#clusterctl-version) | Print clusterctl version. |
| Command | Description |
|------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
| [`clusterctl alpha rollout`](alpha-rollout.md) | Manages the rollout of Cluster API resources. For example: MachineDeployments. |
| [`clusterctl alpha topology plan`](alpha-topology-plan.md) | Describes the changes to a cluster topology for a given input. |
| [`clusterctl backup`](additional-commands.md#clusterctl-backup) | Backup Cluster API objects and all their dependencies from a management cluster. **DEPRECATED. Please use `clusterctl move --to-folder` instead.** |
| [`clusterctl completion`](completion.md) | Output shell completion code for the specified shell (bash or zsh). |
| [`clusterctl config`](additional-commands.md#clusterctl-config-repositories) | Display clusterctl configuration. |
| [`clusterctl delete`](delete.md) | Delete one or more providers from the management cluster. |
| [`clusterctl describe cluster`](describe-cluster.md) | Describe workload clusters. |
| [`clusterctl generate cluster`](generate-cluster.md) | Generate templates for creating workload clusters. |
| [`clusterctl generate provider`](generate-provider.md) | Generate templates for provider components. |
| [`clusterctl generate yaml`](generate-yaml.md) | Process yaml using clusterctl's yaml processor. |
| [`clusterctl get kubeconfig`](get-kubeconfig.md) | Gets the kubeconfig file for accessing a workload cluster. |
| [`clusterctl help`](additional-commands.md#clusterctl-help) | Help about any command. |
| [`clusterctl init`](init.md) | Initialize a management cluster. |
| [`clusterctl move`](move.md) | Move Cluster API objects and all their dependencies between management clusters. |
| [`clusterctl restore`](additional-commands.md#clusterctl-restore) | Restore Cluster API objects from file by glob. **DEPRECATED. Please use `clusterctl move --from-folder` instead.** |
| [`clusterctl upgrade plan`](upgrade.md#upgrade-plan) | Provide a list of recommended target versions for upgrading Cluster API providers in a management cluster. |
| [`clusterctl upgrade apply`](upgrade.md#upgrade-apply) | Apply new versions of Cluster API core and providers in a management cluster. |
| [`clusterctl version`](additional-commands.md#clusterctl-version) | Print clusterctl version. |
2 changes: 2 additions & 0 deletions docs/book/src/developer/providers/v1.2-to-v1.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ in Cluster API are kept in sync with the versions used by `sigs.k8s.io/controlle
### Deprecation

- `sigs.k8s.io/cluster-api/controllers/external.CloneTemplate` has been deprecated and will be removed in a future release. Please use `sigs.k8s.io/cluster-api/controllers/external.CreateFromTemplate` instead.
- `clusterctl backup` has been deprecated. Please use `clusterctl move --to-folder` instead.
- `clusterctl restore` has been deprecated. Please use `clusterctl move --from-folder` instead.

### Removals

Expand Down

0 comments on commit 1d2c370

Please sign in to comment.