@@ -21,6 +21,7 @@ import (
2121 "fmt"
2222 "os"
2323 "path/filepath"
24+ "strings"
2425 "time"
2526
2627 "github.com/pkg/errors"
@@ -41,6 +42,7 @@ import (
4142 clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
4243 clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
4344 logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log"
45+ "sigs.k8s.io/cluster-api/util/annotations"
4446 "sigs.k8s.io/cluster-api/util/conditions"
4547 "sigs.k8s.io/cluster-api/util/patch"
4648 "sigs.k8s.io/cluster-api/util/yaml"
@@ -232,8 +234,7 @@ func (o *objectMover) checkProvisioningCompleted(ctx context.Context, graph *obj
232234 // Checking all the clusters have infrastructure is ready
233235 readClusterBackoff := newReadBackoff ()
234236 clusters := graph .getClusters ()
235- for i := range clusters {
236- cluster := clusters [i ]
237+ for _ , cluster := range clusters {
237238 clusterObj := & clusterv1.Cluster {}
238239 if err := retryWithExponentialBackoff (ctx , readClusterBackoff , func (ctx context.Context ) error {
239240 return getClusterObj (ctx , o .fromProxy , cluster , clusterObj )
@@ -297,6 +298,25 @@ func getClusterObj(ctx context.Context, proxy Proxy, cluster *node, clusterObj *
297298 return nil
298299}
299300
301+ // getClusterClassObj retrieves the clusterClassObj corresponding to a node with type ClusterClass.
302+ func getClusterClassObj (ctx context.Context , proxy Proxy , clusterClass * node , clusterClassObj * clusterv1.ClusterClass ) error {
303+ c , err := proxy .NewClient (ctx )
304+ if err != nil {
305+ return err
306+ }
307+
308+ clusterClassObjKey := client.ObjectKey {
309+ Namespace : clusterClass .identity .Namespace ,
310+ Name : clusterClass .identity .Name ,
311+ }
312+
313+ if err := c .Get (ctx , clusterClassObjKey , clusterClassObj ); err != nil {
314+ return errors .Wrapf (err , "error reading ClusterClass %s/%s" ,
315+ clusterClass .identity .Namespace , clusterClass .identity .Name )
316+ }
317+ return nil
318+ }
319+
300320// getMachineObj retrieves the machineObj corresponding to a node with type Machine.
301321func getMachineObj (ctx context.Context , proxy Proxy , machine * node , machineObj * clusterv1.Machine ) error {
302322 c , err := proxy .NewClient (ctx )
@@ -320,9 +340,17 @@ func (o *objectMover) move(ctx context.Context, graph *objectGraph, toProxy Prox
320340 log := logf .Log
321341
322342 clusters := graph .getClusters ()
343+ if err := checkClustersNotPaused (ctx , o .fromProxy , clusters ); err != nil {
344+ return err
345+ }
346+
323347 log .Info ("Moving Cluster API objects" , "Clusters" , len (clusters ))
324348
325349 clusterClasses := graph .getClusterClasses ()
350+ if err := checkClusterClassesNotPaused (ctx , o .fromProxy , clusterClasses ); err != nil {
351+ return err
352+ }
353+
326354 log .Info ("Moving Cluster API objects" , "ClusterClasses" , len (clusterClasses ))
327355
328356 // Sets the pause field on the Cluster object in the source management cluster, so the controllers stop reconciling it.
@@ -395,9 +423,17 @@ func (o *objectMover) toDirectory(ctx context.Context, graph *objectGraph, direc
395423 log := logf .Log
396424
397425 clusters := graph .getClusters ()
426+ if err := checkClustersNotPaused (ctx , o .fromProxy , clusters ); err != nil {
427+ return err
428+ }
429+
398430 log .Info ("Starting move of Cluster API objects" , "Clusters" , len (clusters ))
399431
400432 clusterClasses := graph .getClusterClasses ()
433+ if err := checkClusterClassesNotPaused (ctx , o .fromProxy , clusterClasses ); err != nil {
434+ return err
435+ }
436+
401437 log .Info ("Moving Cluster API objects" , "ClusterClasses" , len (clusterClasses ))
402438
403439 // Sets the pause field on the Cluster object in the source management cluster, so the controllers stop reconciling it.
@@ -570,8 +606,7 @@ func setClusterPause(ctx context.Context, proxy Proxy, clusters []*node, value b
570606 patch := client .RawPatch (types .MergePatchType , []byte (fmt .Sprintf ("{\" spec\" :{\" paused\" :%s}}" , patchValue )))
571607
572608 setClusterPauseBackoff := newWriteBackoff ()
573- for i := range clusters {
574- cluster := clusters [i ]
609+ for _ , cluster := range clusters {
575610 log .V (5 ).Info ("Set Cluster.Spec.Paused" , "paused" , value , "Cluster" , klog .KRef (cluster .identity .Namespace , cluster .identity .Name ))
576611
577612 // Nb. The operation is wrapped in a retry loop to make setClusterPause more resilient to unexpected conditions.
@@ -593,8 +628,7 @@ func setClusterClassPause(ctx context.Context, proxy Proxy, clusterclasses []*no
593628 log := logf .Log
594629
595630 setClusterClassPauseBackoff := newWriteBackoff ()
596- for i := range clusterclasses {
597- clusterclass := clusterclasses [i ]
631+ for _ , clusterclass := range clusterclasses {
598632 if pause {
599633 log .V (5 ).Info ("Set Paused annotation" , "ClusterClass" , clusterclass .identity .Name , "Namespace" , clusterclass .identity .Namespace )
600634 } else {
@@ -611,6 +645,44 @@ func setClusterClassPause(ctx context.Context, proxy Proxy, clusterclasses []*no
611645 return nil
612646}
613647
648+ // checkClustersNotPaused checks that no cluster in the graph is paused before proceeding.
649+ func checkClustersNotPaused (ctx context.Context , proxy Proxy , clusters []* node ) error {
650+ paused := []string {}
651+ for _ , cluster := range clusters {
652+ clusterObj := & clusterv1.Cluster {}
653+ if err := getClusterObj (ctx , proxy , cluster , clusterObj ); err != nil {
654+ return err
655+ }
656+
657+ if ptr .Deref (clusterObj .Spec .Paused , false ) || annotations .HasPaused (clusterObj ) {
658+ paused = append (paused , fmt .Sprintf ("%s/%s" , clusterObj .Namespace , clusterObj .Name ))
659+ }
660+ }
661+ if len (paused ) > 0 {
662+ return errors .Errorf ("cannot start operation while the following Clusters are paused: %s" , strings .Join (paused , ", " ))
663+ }
664+ return nil
665+ }
666+
667+ // checkClusterClassesNotPaused checks that no clusterClass in the graph is paused before proceeding.
668+ func checkClusterClassesNotPaused (ctx context.Context , proxy Proxy , clusterClasses []* node ) error {
669+ paused := []string {}
670+ for _ , clusterClass := range clusterClasses {
671+ clusterClassObj := & clusterv1.ClusterClass {}
672+ if err := getClusterClassObj (ctx , proxy , clusterClass , clusterClassObj ); err != nil {
673+ return err
674+ }
675+
676+ if annotations .HasPaused (clusterClassObj ) {
677+ paused = append (paused , fmt .Sprintf ("%s/%s" , clusterClassObj .Namespace , clusterClassObj .Name ))
678+ }
679+ }
680+ if len (paused ) > 0 {
681+ return errors .Errorf ("cannot start operation while the following ClusterClasses are paused: %s" , strings .Join (paused , ", " ))
682+ }
683+ return nil
684+ }
685+
614686func waitReadyForMove (ctx context.Context , proxy Proxy , nodes []* node , dryRun bool , backoff wait.Backoff ) error {
615687 if dryRun {
616688 return nil
@@ -723,7 +795,8 @@ func pauseClusterClass(ctx context.Context, proxy Proxy, n *node, pause bool, mu
723795 ObjectMeta : metav1.ObjectMeta {
724796 Name : n .identity .Name ,
725797 Namespace : n .identity .Namespace ,
726- }}, mutators ... )
798+ },
799+ }, mutators ... )
727800 if err != nil {
728801 return err
729802 }
@@ -1072,7 +1145,7 @@ func (o *objectMover) backupTargetObject(ctx context.Context, nodeToCreate *node
10721145 }
10731146 }
10741147
1075- err = os .WriteFile (objectFile , byObj , 0600 )
1148+ err = os .WriteFile (objectFile , byObj , 0o600 )
10761149 if err != nil {
10771150 return err
10781151 }
@@ -1173,7 +1246,6 @@ func (o *objectMover) deleteGroup(ctx context.Context, group moveGroup) error {
11731246 err := retryWithExponentialBackoff (ctx , deleteSourceObjectBackoff , func (ctx context.Context ) error {
11741247 return o .deleteSourceObject (ctx , nodeToDelete )
11751248 })
1176-
11771249 if err != nil {
11781250 errList = append (errList , err )
11791251 }
0 commit comments