@@ -24,9 +24,7 @@ import (
2424 "strings"
2525 "time"
2626
27- sdk "github.com/openshift-online/ocm-sdk-go"
2827 cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"
29- ocmerrors "github.com/openshift-online/ocm-sdk-go/errors"
3028 apierrors "k8s.io/apimachinery/pkg/api/errors"
3129 "k8s.io/apimachinery/pkg/types"
3230 ctrl "sigs.k8s.io/controller-runtime"
@@ -40,6 +38,7 @@ import (
4038 expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
4139 "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope"
4240 "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
41+ "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/rosa"
4342 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
4443 "sigs.k8s.io/cluster-api/util"
4544 capiannotations "sigs.k8s.io/cluster-api/util/annotations"
@@ -171,9 +170,47 @@ func (r *ROSAControlPlaneReconciler) reconcileNormal(ctx context.Context, rosaSc
171170 }
172171 }
173172
173+ // TODO: token should be read from a secret: https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues/4460
174+ token := os .Getenv ("OCM_TOKEN" )
175+ rosaClient , err := rosa .NewRosaClient (token )
176+ if err != nil {
177+ return ctrl.Result {}, fmt .Errorf ("failed to create a rosa client: %w" , err )
178+ }
179+
180+ defer func () {
181+ rosaClient .Close ()
182+ }()
183+
184+ cluster , err := rosaClient .GetCluster (rosaScope .RosaClusterName (), rosaScope .ControlPlane .Spec .CreatorARN )
185+ if err != nil {
186+ return ctrl.Result {}, err
187+ }
188+
189+ if clusterID := cluster .ID (); clusterID != "" {
190+ rosaScope .ControlPlane .Status .ID = & clusterID
191+ if cluster .Status ().State () == "ready" {
192+ conditions .MarkTrue (rosaScope .ControlPlane , rosacontrolplanev1 .ROSAControlPlaneReadyCondition )
193+ rosaScope .ControlPlane .Status .Ready = true
194+ // TODO: distinguish when controlPlane is ready vs initialized
195+ rosaScope .ControlPlane .Status .Initialized = true
196+
197+ return ctrl.Result {}, nil
198+ }
199+
200+ conditions .MarkFalse (rosaScope .ControlPlane ,
201+ rosacontrolplanev1 .ROSAControlPlaneReadyCondition ,
202+ string (cluster .Status ().State ()),
203+ clusterv1 .ConditionSeverityInfo ,
204+ "" )
205+
206+ rosaScope .Info ("waiting for cluster to become ready" , "state" , cluster .Status ().State ())
207+ // Requeue so that status.ready is set to true when the cluster is fully created.
208+ return ctrl.Result {RequeueAfter : time .Second * 60 }, nil
209+ }
210+
174211 // Create the cluster:
175212 clusterBuilder := cmv1 .NewCluster ().
176- Name (rosaScope .ControlPlane . Name [: 15 ] ).
213+ Name (rosaScope .RosaClusterName () ).
177214 MultiAZ (true ).
178215 Product (
179216 cmv1 .NewProduct ().
@@ -283,86 +320,46 @@ func (r *ROSAControlPlaneReconciler) reconcileNormal(ctx context.Context, rosaSc
283320 return ctrl.Result {}, fmt .Errorf ("failed to create description of cluster: %v" , err )
284321 }
285322
286- // create OCM NodePool
287- ocmClient , err := newOCMClient ()
323+ newCluster , err := rosaClient .CreateCluster (clusterSpec )
288324 if err != nil {
289- return ctrl.Result {}, err
290- }
291- defer func () {
292- ocmClient .ocm .Close ()
293- }()
294-
295- cluster , err := ocmClient .GetCluster (rosaScope )
296- if err != nil {
297- return ctrl.Result {}, err
298- }
299-
300- log := logger .FromContext (ctx )
301- if cluster .ID () != "" {
302- clusterID := cluster .ID ()
303- rosaScope .ControlPlane .Status .ID = & clusterID
304- conditions .MarkFalse (rosaScope .ControlPlane ,
305- rosacontrolplanev1 .ROSAControlPlaneReadyCondition ,
306- string (cluster .Status ().State ()),
307- clusterv1 .ConditionSeverityInfo ,
308- "" )
309-
310- if cluster .Status ().State () == "ready" {
311- conditions .MarkTrue (rosaScope .ControlPlane , rosacontrolplanev1 .ROSAControlPlaneReadyCondition )
312- rosaScope .ControlPlane .Status .Ready = true
313- }
314-
315- if err := rosaScope .PatchObject (); err != nil {
316- return ctrl.Result {}, err
317- }
318-
319- log .Info ("cluster exists" , "state" , cluster .Status ().State ())
320- return ctrl.Result {}, nil
321- }
322-
323- newCluster , err := ocmClient .CreateCluster (clusterSpec )
324- if err != nil {
325- log .Info ("error" , "error" , err )
325+ rosaScope .Info ("error" , "error" , err )
326326 return ctrl.Result {RequeueAfter : 10 * time .Second }, nil
327327 }
328328
329- log .Info ("cluster created" , "state" , newCluster .Status ().State ())
329+ rosaScope .Info ("cluster created" , "state" , newCluster .Status ().State ())
330330 clusterID := newCluster .ID ()
331331 rosaScope .ControlPlane .Status .ID = & clusterID
332- if err := rosaScope .PatchObject (); err != nil {
333- return ctrl.Result {}, err
334- }
335332
336333 return ctrl.Result {}, nil
337334}
338335
339336func (r * ROSAControlPlaneReconciler ) reconcileDelete (_ context.Context , rosaScope * scope.ROSAControlPlaneScope ) (res ctrl.Result , reterr error ) {
340337 rosaScope .Info ("Reconciling ROSAControlPlane delete" )
341338
342- // create OCM NodePool
343- ocmClient , err := newOCMClient ()
339+ // Create the connection, and remember to close it:
340+ // TODO: token should be read from a secret: https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues/4460
341+ token := os .Getenv ("OCM_TOKEN" )
342+ rosaClient , err := rosa .NewRosaClient (token )
344343 if err != nil {
345- return ctrl.Result {}, err
344+ return ctrl.Result {}, fmt . Errorf ( "failed to create a rosa client: %w" , err )
346345 }
346+
347347 defer func () {
348- ocmClient . ocm .Close ()
348+ rosaClient .Close ()
349349 }()
350350
351- cluster , err := ocmClient .GetCluster (rosaScope )
351+ cluster , err := rosaClient .GetCluster (rosaScope . RosaClusterName (), rosaScope . ControlPlane . Spec . CreatorARN )
352352 if err != nil {
353353 return ctrl.Result {}, err
354354 }
355355
356356 if cluster != nil {
357- if _ , err := ocmClient .DeleteCluster (cluster .ID ()); err != nil {
357+ if err := rosaClient .DeleteCluster (cluster .ID ()); err != nil {
358358 return ctrl.Result {}, err
359359 }
360360 }
361361
362362 controllerutil .RemoveFinalizer (rosaScope .ControlPlane , ROSAControlPlaneFinalizer )
363- if err := rosaScope .PatchObject (); err != nil {
364- return ctrl.Result {}, err
365- }
366363
367364 return ctrl.Result {}, nil
368365}
@@ -406,119 +403,3 @@ func (r *ROSAControlPlaneReconciler) rosaClusterToROSAControlPlane(log *logger.L
406403 }
407404 }
408405}
409-
410- // OCMClient is a temporary helper to talk to OCM API.
411- // TODO(alberto): vendor this from https://github.com/openshift/rosa/tree/master/pkg/ocm or build its own package here.
412- type OCMClient struct {
413- ocm * sdk.Connection
414- }
415-
416- func newOCMClient () (* OCMClient , error ) {
417- // Create the connection, and remember to close it:
418- token := os .Getenv ("OCM_TOKEN" )
419- ocmAPIUrl := os .Getenv ("OCM_API_URL" )
420- if ocmAPIUrl == "" {
421- ocmAPIUrl = "https://api.openshift.com"
422- }
423-
424- // Create a logger that has the debug level enabled:
425- ocmLogger , err := sdk .NewGoLoggerBuilder ().
426- Debug (false ).
427- Build ()
428- if err != nil {
429- return nil , fmt .Errorf ("failed to build logger: %w" , err )
430- }
431-
432- connection , err := sdk .NewConnectionBuilder ().
433- Logger (ocmLogger ).
434- Tokens (token ).
435- URL (ocmAPIUrl ).
436- Build ()
437- if err != nil {
438- return nil , fmt .Errorf ("failed to ocm client: %w" , err )
439- }
440- ocmClient := OCMClient {ocm : connection }
441-
442- return & ocmClient , nil
443- }
444-
445- func (client * OCMClient ) Close () error {
446- return client .ocm .Close ()
447- }
448-
449- func (client * OCMClient ) CreateCluster (clusterSpec * cmv1.Cluster ) (* cmv1.Cluster , error ) {
450- cluster , err := client .ocm .ClustersMgmt ().V1 ().Clusters ().
451- Add ().
452- Body (clusterSpec ).
453- Send ()
454- if err != nil {
455- return nil , handleErr (cluster .Error (), err )
456- }
457-
458- clusterObject := cluster .Body ()
459-
460- return clusterObject , nil
461- }
462-
463- func (client * OCMClient ) GetCluster (rosaScope * scope.ROSAControlPlaneScope ) (* cmv1.Cluster , error ) {
464- clusterKey := rosaScope .ControlPlane .Name [:15 ]
465- query := fmt .Sprintf ("%s AND (id = '%s' OR name = '%s' OR external_id = '%s')" ,
466- getClusterFilter (rosaScope ),
467- clusterKey , clusterKey , clusterKey ,
468- )
469- response , err := client .ocm .ClustersMgmt ().V1 ().Clusters ().List ().
470- Search (query ).
471- Page (1 ).
472- Size (1 ).
473- Send ()
474- if err != nil {
475- return nil , err
476- }
477-
478- switch response .Total () {
479- case 0 :
480- return nil , nil
481- case 1 :
482- return response .Items ().Slice ()[0 ], nil
483- default :
484- return nil , fmt .Errorf ("there are %d clusters with identifier or name '%s'" , response .Total (), clusterKey )
485- }
486- }
487-
488- func (client * OCMClient ) DeleteCluster (clusterID string ) (* cmv1.Cluster , error ) {
489- response , err := client .ocm .ClustersMgmt ().V1 ().Clusters ().
490- Cluster (clusterID ).
491- Delete ().
492- Send ()
493- if err != nil {
494- return nil , handleErr (response .Error (), err )
495- }
496-
497- return nil , nil
498- }
499-
500- // Generate a query that filters clusters running on the current AWS session account.
501- func getClusterFilter (rosaScope * scope.ROSAControlPlaneScope ) string {
502- filter := "product.id = 'rosa'"
503- if rosaScope .ControlPlane .Spec .CreatorARN != nil {
504- filter = fmt .Sprintf ("%s AND (properties.%s = '%s')" ,
505- filter ,
506- rosaCreatorArnProperty ,
507- * rosaScope .ControlPlane .Spec .CreatorARN )
508- }
509- return filter
510- }
511-
512- func handleErr (res * ocmerrors.Error , err error ) error {
513- msg := res .Reason ()
514- if msg == "" {
515- msg = err .Error ()
516- }
517- // Hack to always display the correct terms and conditions message
518- if res .Code () == "CLUSTERS-MGMT-451" {
519- msg = "You must accept the Terms and Conditions in order to continue.\n " +
520- "Go to https://www.redhat.com/wapps/tnc/ackrequired?site=ocm&event=register\n " +
521- "Once you accept the terms, you will need to retry the action that was blocked."
522- }
523- return fmt .Errorf (msg )
524- }
0 commit comments