@@ -41,6 +41,7 @@ import (
4141 clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
4242 clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
4343 logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log"
44+ "sigs.k8s.io/cluster-api/util/annotations"
4445 "sigs.k8s.io/cluster-api/util/conditions"
4546 "sigs.k8s.io/cluster-api/util/patch"
4647 "sigs.k8s.io/cluster-api/util/yaml"
@@ -232,8 +233,7 @@ func (o *objectMover) checkProvisioningCompleted(ctx context.Context, graph *obj
232233 // Checking all the clusters have infrastructure is ready
233234 readClusterBackoff := newReadBackoff ()
234235 clusters := graph .getClusters ()
235- for i := range clusters {
236- cluster := clusters [i ]
236+ for _ , cluster := range clusters {
237237 clusterObj := & clusterv1.Cluster {}
238238 if err := retryWithExponentialBackoff (ctx , readClusterBackoff , func (ctx context.Context ) error {
239239 return getClusterObj (ctx , o .fromProxy , cluster , clusterObj )
@@ -297,6 +297,25 @@ func getClusterObj(ctx context.Context, proxy Proxy, cluster *node, clusterObj *
297297 return nil
298298}
299299
300+ // getClusterClassObj retrieves the clusterClassObj corresponding to a node with type ClusterClass.
301+ func getClusterClassObj (ctx context.Context , proxy Proxy , clusterClass * node , clusterClassObj * clusterv1.ClusterClass ) error {
302+ c , err := proxy .NewClient (ctx )
303+ if err != nil {
304+ return err
305+ }
306+
307+ clusterClassObjKey := client.ObjectKey {
308+ Namespace : clusterClass .identity .Namespace ,
309+ Name : clusterClass .identity .Name ,
310+ }
311+
312+ if err := c .Get (ctx , clusterClassObjKey , clusterClassObj ); err != nil {
313+ return errors .Wrapf (err , "error reading ClusterClass %s/%s" ,
314+ clusterClass .identity .Namespace , clusterClass .identity .Name )
315+ }
316+ return nil
317+ }
318+
300319// getMachineObj retrieves the machineObj corresponding to a node with type Machine.
301320func getMachineObj (ctx context.Context , proxy Proxy , machine * node , machineObj * clusterv1.Machine ) error {
302321 c , err := proxy .NewClient (ctx )
@@ -320,9 +339,17 @@ func (o *objectMover) move(ctx context.Context, graph *objectGraph, toProxy Prox
320339 log := logf .Log
321340
322341 clusters := graph .getClusters ()
342+ if err := checkClustersNotPaused (ctx , o .fromProxy , clusters ); err != nil {
343+ return err
344+ }
345+
323346 log .Info ("Moving Cluster API objects" , "Clusters" , len (clusters ))
324347
325348 clusterClasses := graph .getClusterClasses ()
349+ if err := checkClusterClassesNotPaused (ctx , o .fromProxy , clusterClasses ); err != nil {
350+ return err
351+ }
352+
326353 log .Info ("Moving Cluster API objects" , "ClusterClasses" , len (clusterClasses ))
327354
328355 // Sets the pause field on the Cluster object in the source management cluster, so the controllers stop reconciling it.
@@ -395,9 +422,17 @@ func (o *objectMover) toDirectory(ctx context.Context, graph *objectGraph, direc
395422 log := logf .Log
396423
397424 clusters := graph .getClusters ()
425+ if err := checkClustersNotPaused (ctx , o .fromProxy , clusters ); err != nil {
426+ return err
427+ }
428+
398429 log .Info ("Starting move of Cluster API objects" , "Clusters" , len (clusters ))
399430
400431 clusterClasses := graph .getClusterClasses ()
432+ if err := checkClusterClassesNotPaused (ctx , o .fromProxy , clusterClasses ); err != nil {
433+ return err
434+ }
435+
401436 log .Info ("Moving Cluster API objects" , "ClusterClasses" , len (clusterClasses ))
402437
403438 // Sets the pause field on the Cluster object in the source management cluster, so the controllers stop reconciling it.
@@ -570,8 +605,7 @@ func setClusterPause(ctx context.Context, proxy Proxy, clusters []*node, value b
570605 patch := client .RawPatch (types .MergePatchType , []byte (fmt .Sprintf ("{\" spec\" :{\" paused\" :%s}}" , patchValue )))
571606
572607 setClusterPauseBackoff := newWriteBackoff ()
573- for i := range clusters {
574- cluster := clusters [i ]
608+ for _ , cluster := range clusters {
575609 log .V (5 ).Info ("Set Cluster.Spec.Paused" , "paused" , value , "Cluster" , klog .KRef (cluster .identity .Namespace , cluster .identity .Name ))
576610
577611 // Nb. The operation is wrapped in a retry loop to make setClusterPause more resilient to unexpected conditions.
@@ -593,8 +627,7 @@ func setClusterClassPause(ctx context.Context, proxy Proxy, clusterclasses []*no
593627 log := logf .Log
594628
595629 setClusterClassPauseBackoff := newWriteBackoff ()
596- for i := range clusterclasses {
597- clusterclass := clusterclasses [i ]
630+ for _ , clusterclass := range clusterclasses {
598631 if pause {
599632 log .V (5 ).Info ("Set Paused annotation" , "ClusterClass" , clusterclass .identity .Name , "Namespace" , clusterclass .identity .Namespace )
600633 } else {
@@ -611,6 +644,38 @@ func setClusterClassPause(ctx context.Context, proxy Proxy, clusterclasses []*no
611644 return nil
612645}
613646
647+ // checkClustersNotPaused checks that no cluster in the graph is paused before proceeding.
648+ func checkClustersNotPaused (ctx context.Context , proxy Proxy , clusters []* node ) error {
649+ for _ , cluster := range clusters {
650+ clusterObj := & clusterv1.Cluster {}
651+ if err := getClusterObj (ctx , proxy , cluster , clusterObj ); err != nil {
652+ return err
653+ }
654+
655+ if ptr .Deref (clusterObj .Spec .Paused , false ) || annotations .HasPaused (clusterObj ) {
656+ return errors .Errorf ("cannot start operation while Cluster %s/%s is paused" , clusterObj .Namespace , clusterObj .Name )
657+ }
658+ }
659+
660+ return nil
661+ }
662+
663+ // checkClusterClassesNotPaused checks that no clusterClass in the graph is paused before proceeding.
664+ func checkClusterClassesNotPaused (ctx context.Context , proxy Proxy , clusterClasses []* node ) error {
665+ for _ , clusterClass := range clusterClasses {
666+ clusterClassObj := & clusterv1.ClusterClass {}
667+ if err := getClusterClassObj (ctx , proxy , clusterClass , clusterClassObj ); err != nil {
668+ return err
669+ }
670+
671+ if annotations .HasPaused (clusterClassObj ) {
672+ return errors .Errorf ("cannot start operation while ClusterClass %s/%s is paused" , clusterClassObj .Namespace , clusterClassObj .Name )
673+ }
674+ }
675+
676+ return nil
677+ }
678+
614679func waitReadyForMove (ctx context.Context , proxy Proxy , nodes []* node , dryRun bool , backoff wait.Backoff ) error {
615680 if dryRun {
616681 return nil
@@ -723,7 +788,8 @@ func pauseClusterClass(ctx context.Context, proxy Proxy, n *node, pause bool, mu
723788 ObjectMeta : metav1.ObjectMeta {
724789 Name : n .identity .Name ,
725790 Namespace : n .identity .Namespace ,
726- }}, mutators ... )
791+ },
792+ }, mutators ... )
727793 if err != nil {
728794 return err
729795 }
@@ -1173,7 +1239,6 @@ func (o *objectMover) deleteGroup(ctx context.Context, group moveGroup) error {
11731239 err := retryWithExponentialBackoff (ctx , deleteSourceObjectBackoff , func (ctx context.Context ) error {
11741240 return o .deleteSourceObject (ctx , nodeToDelete )
11751241 })
1176-
11771242 if err != nil {
11781243 errList = append (errList , err )
11791244 }
0 commit comments