Skip to content

Commit

Permalink
clusterctl: Add move --to-directory and --from-directory 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 Sep 20, 2022
1 parent f7fd599 commit 7a18359
Show file tree
Hide file tree
Showing 12 changed files with 239 additions and 109 deletions.
2 changes: 2 additions & 0 deletions cmd/clusterctl/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ type Client interface {
Move(options MoveOptions) error

// Backup saves all the Cluster API objects existing in a namespace (or from all the namespaces if empty) to a target management cluster.
// Deprecated: This will be dropped in a future release. Please use Move.
Backup(options BackupOptions) error

// Restore restores all the Cluster API objects existing in a configured directory based on a glob to a target management cluster.
// Deprecated: This will be dropped in a future release. Please use Move.
Restore(options RestoreOptions) error

// PlanUpgrade returns a set of suggested Upgrade plans for the cluster, and more specifically:
Expand Down
52 changes: 37 additions & 15 deletions cmd/clusterctl/client/cluster/mover.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,19 @@ import (
type ObjectMover interface {
// Move moves all the Cluster API objects existing in a namespace (or from all the namespaces if empty) to a target management cluster.
Move(namespace string, toCluster Client, dryRun bool) error
// Backup saves all the Cluster API objects existing in a namespace (or from all the namespaces if empty) to a target management cluster.

// ToDirectory writes all the Cluster API objects existing in a namespace (or from all the namespaces if empty) to a target directory.
ToDirectory(namespace string, directory string) error

// FromDirectory reads all the Cluster API objects existing in a configured directory to a target management cluster.
FromDirectory(toCluster Client, directory string) error

// Backup saves all the Cluster API objects existing in a namespace (or from all the namespaces if empty) to a target directory.
// Deprecated: This will be dropped in a future release. Please use ToDirectory.
Backup(namespace string, directory string) error

// Restore restores all the Cluster API objects existing in a configured directory to a target management cluster.
// Deprecated: This will be dropped in a future release. Please use FromDirectory.
Restore(toCluster Client, directory string) error
}

Expand Down Expand Up @@ -94,21 +104,33 @@ func (o *objectMover) Move(namespace string, toCluster Client, dryRun bool) erro

func (o *objectMover) Backup(namespace string, directory string) error {
log := logf.Log
log.Info("Performing backup...")
log.V(5).Info("Deprecated: This function will be dropped in a future release. Please use ToDirectory instead of Backup.")
return o.ToDirectory(namespace, directory)
}

func (o *objectMover) ToDirectory(namespace string, directory string) error {
log := logf.Log
log.Info("Moving to directory...")

objectGraph, err := o.getObjectGraph(namespace)
if err != nil {
return errors.Wrap(err, "failed to get object graph")
}

return o.backup(objectGraph, directory)
return o.toDirectory(objectGraph, directory)
}

func (o *objectMover) Restore(toCluster Client, directory string) error {
log := logf.Log
log.Info("Performing restore...")
log.V(5).Info("Deprecated: This function will be dropped in a future release. Please use FromDirectory instead of Restore.")
return o.FromDirectory(toCluster, directory)
}

// Build an empty object graph used for the restore sequence not tied to a specific namespace
func (o *objectMover) FromDirectory(toCluster Client, directory string) error {
log := logf.Log
log.Info("Moving from directory...")

// Build an empty object graph used for the fromDirectory sequence not tied to a specific namespace
objectGraph := newObjectGraph(o.fromProxy, o.fromProviderInventory)

// Gets all the types defined by the CRDs installed by clusterctl plus the ConfigMap/Secret core types.
Expand All @@ -135,13 +157,13 @@ func (o *objectMover) Restore(toCluster Client, directory string) error {
// Completes the graph by setting for each node the list of tenants the node belongs to.
objectGraph.setTenants()

// Check whether nodes are not included in GVK considered for restore.
// Check whether nodes are not included in GVK considered for fromDirectory.
objectGraph.checkVirtualNode()

// Restore the objects to the target cluster.
proxy := toCluster.Proxy()

return o.restore(objectGraph, proxy)
return o.fromDirectory(objectGraph, proxy)
}

func (o *objectMover) filesToObjs(dir string) ([]unstructured.Unstructured, error) {
Expand Down Expand Up @@ -191,7 +213,7 @@ func (o *objectMover) getObjectGraph(namespace string) (*objectGraph, error) {
return nil, errors.Wrap(err, "failed to discover the object graph")
}

// Checks if Cluster API has already completed the provisioning of the infrastructure for the objects involved in the move/backup operation.
// Checks if Cluster API has already completed the provisioning of the infrastructure for the objects involved in the move/toDirectory operation.
// This is required because if the infrastructure is provisioned, then we can reasonably assume that the objects we are moving/backing up are
// not currently waiting for long-running reconciliation loops, and so we can safely rely on the pause field on the Cluster object
// for blocking any further object reconciliation on the source objects.
Expand Down Expand Up @@ -354,7 +376,7 @@ func (o *objectMover) move(graph *objectGraph, toProxy Proxy) error {
return err
}
}

// Resume the ClusterClasses in the target management cluster, so the controllers start reconciling it.
log.V(1).Info("Resuming the target ClusterClasses")
if err := setClusterClassPause(toProxy, clusterClasses, false, o.dryRun); err != nil {
Expand All @@ -366,11 +388,11 @@ func (o *objectMover) move(graph *objectGraph, toProxy Proxy) error {
return setClusterPause(toProxy, clusters, false, o.dryRun)
}

func (o *objectMover) backup(graph *objectGraph, directory string) error {
func (o *objectMover) toDirectory(graph *objectGraph, directory string) error {
log := logf.Log

clusters := graph.getClusters()
log.Info("Starting backup of Cluster API objects", "Clusters", len(clusters))
log.Info("Starting move of Cluster API objects", "Clusters", len(clusters))

clusterClasses := graph.getClusterClasses()
log.Info("Moving Cluster API objects", "ClusterClasses", len(clusterClasses))
Expand Down Expand Up @@ -412,7 +434,7 @@ func (o *objectMover) backup(graph *objectGraph, directory string) error {
return setClusterPause(o.fromProxy, clusters, false, o.dryRun)
}

func (o *objectMover) restore(graph *objectGraph, toProxy Proxy) error {
func (o *objectMover) fromDirectory(graph *objectGraph, toProxy Proxy) error {
log := logf.Log

// Get clusters from graph
Expand Down Expand Up @@ -448,8 +470,8 @@ func (o *objectMover) restore(graph *objectGraph, toProxy Proxy) error {
return errors.Wrap(err, "error resuming ClusterClasses")
}

// Resume reconciling the Clusters after being restored from a backup.
// By default, during backup, Clusters are paused so they must be unpaused to be used again
// Resume reconciling the Clusters after being restored from a directory.
// By default, when moved to a directory , Clusters are paused, so they must be unpaused to be used again.
log.V(1).Info("Resuming the target cluster")
return setClusterPause(toProxy, clusters, false, o.dryRun)
}
Expand Down Expand Up @@ -972,7 +994,7 @@ func (o *objectMover) restoreTargetObject(nodeToCreate *node, toProxy Proxy) err
existingTargetObj.SetAPIVersion(nodeToCreate.restoreObject.GetAPIVersion())
existingTargetObj.SetKind(nodeToCreate.restoreObject.GetKind())
if err := cTo.Get(ctx, objKey, existingTargetObj); err == nil {
log.V(5).Info("Object already exists, skipping restore", nodeToCreate.identity.Kind, nodeToCreate.identity.Name, "Namespace", nodeToCreate.identity.Namespace)
log.V(5).Info("Object already exists, skipping moving from directory", nodeToCreate.identity.Kind, nodeToCreate.identity.Name, "Namespace", nodeToCreate.identity.Namespace)

// Update the nodes UID since it already exits. Any nodes owned by this existing node will be updated when the owner chain is rebuilt
nodeToCreate.newUID = existingTargetObj.GetUID()
Expand Down
12 changes: 6 additions & 6 deletions cmd/clusterctl/client/cluster/mover_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ func Test_objectMover_backupTargetObject(t *testing.T) {
// Add delay so we ensure the file ModTime of updated files is different from old ones in the original files
time.Sleep(time.Millisecond * 50)

// Running backupTargetObject should override any existing files since it represents a new backup
// Running backupTargetObject should override any existing files since it represents a new toDirectory
err = mover.backupTargetObject(node, dir)
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
Expand Down Expand Up @@ -855,7 +855,7 @@ func Test_objectMover_backup(t *testing.T) {
// trigger discovery the content of the source cluster
g.Expect(graph.Discovery("")).To(Succeed())

// Run backup
// Run toDirectory
mover := objectMover{
fromProxy: graph.proxy,
}
Expand All @@ -866,7 +866,7 @@ func Test_objectMover_backup(t *testing.T) {
}
defer os.RemoveAll(dir)

err = mover.backup(graph, dir)
err = mover.toDirectory(graph, dir)
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
return
Expand Down Expand Up @@ -996,7 +996,7 @@ func Test_objectMover_restore(t *testing.T) {
// gets a fakeProxy to an empty cluster with all the required CRDs
toProxy := getFakeProxyWithCRDs()

// Run restore
// Run fromDirectory
mover := objectMover{
fromProxy: graph.proxy,
}
Expand All @@ -1018,14 +1018,14 @@ func Test_objectMover_restore(t *testing.T) {
g.Expect(graph.addRestoredObj(&objs[i])).NotTo(HaveOccurred())
}

// restore works on the target cluster which does not yet have objs to discover
// fromDirectory works on the target cluster which does not yet have objs to discover
// instead set the owners and tenants correctly on object graph like how ObjectMover.Restore does
// https://github.com/kubernetes-sigs/cluster-api/blob/main/cmd/clusterctl/client/cluster/mover.go#L129-L132
graph.setSoftOwnership()
graph.setTenants()
graph.checkVirtualNode()

err = mover.restore(graph, toProxy)
err = mover.fromDirectory(graph, toProxy)
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
return
Expand Down
6 changes: 3 additions & 3 deletions cmd/clusterctl/client/cluster/objectgraph.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ type node struct {
// the node is linked to a object indirectly in the OwnerReference chain.
tenant map[*node]empty

// restoreObject holds the object that is referenced when creating a node during restore from file.
// restoreObject holds the object that is referenced when creating a node during fromDirectory from file.
// the object can then be referenced latter when restoring objects to a target management cluster
restoreObject *unstructured.Unstructured

Expand Down Expand Up @@ -179,8 +179,8 @@ func (o *objectGraph) addObj(obj *unstructured.Unstructured) error {
return nil
}

// addRestoredObj adds a Kubernetes object to the object graph from file that is generated during a restore
// Populates the restoredObject field to be referenced during restore
// addRestoredObj adds a Kubernetes object to the object graph from file that is generated during a fromDirectory
// Populates the restoredObject field to be referenced during fromDirectory
// During add, OwnerReferences are processed in order to create the dependency graph.
func (o *objectGraph) addRestoredObj(obj *unstructured.Unstructured) error {
// Add object to graph
Expand Down
Loading

0 comments on commit 7a18359

Please sign in to comment.