@@ -21,10 +21,15 @@ import (
2121 "fmt"
2222
2323 "github.com/pkg/errors"
24+ corev1 "k8s.io/api/core/v1"
2425 apierrors "k8s.io/apimachinery/pkg/api/errors"
26+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+ "k8s.io/apimachinery/pkg/runtime"
2528 "k8s.io/apimachinery/pkg/types"
29+ kerrors "k8s.io/apimachinery/pkg/util/errors"
2630 "k8s.io/client-go/tools/record"
2731 "k8s.io/klog/v2"
32+ "k8s.io/utils/ptr"
2833 ctrl "sigs.k8s.io/controller-runtime"
2934 "sigs.k8s.io/controller-runtime/pkg/client"
3035 "sigs.k8s.io/controller-runtime/pkg/controller"
@@ -35,9 +40,15 @@ import (
3540 infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
3641 rosacontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/rosa/api/v1beta2"
3742 expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
43+ "sigs.k8s.io/cluster-api-provider-aws/v2/exp/utils"
44+ "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud"
45+ "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope"
46+ stsservice "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/sts"
3847 "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
48+ "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/rosa"
3949 "sigs.k8s.io/cluster-api-provider-aws/v2/util/paused"
4050 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
51+ expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
4152 "sigs.k8s.io/cluster-api/util"
4253 "sigs.k8s.io/cluster-api/util/patch"
4354 "sigs.k8s.io/cluster-api/util/predicates"
@@ -48,16 +59,20 @@ type ROSAClusterReconciler struct {
4859 client.Client
4960 Recorder record.EventRecorder
5061 WatchFilterValue string
62+ NewStsClient func (cloud.ScopeUsage , cloud.Session , logger.Wrapper , runtime.Object ) stsservice.STSClient
63+ NewOCMClient func (ctx context.Context , rosaScope * scope.ROSAControlPlaneScope ) (rosa.OCMClient , error )
5164}
5265
5366// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=rosaclusters,verbs=get;list;watch;update;patch;delete
5467// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=rosaclusters/status,verbs=get;update;patch
5568// +kubebuilder:rbac:groups=controlplane.cluster.x-k8s.io,resources=rosacontrolplanes;rosacontrolplanes/status,verbs=get;list;watch
5669// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters;clusters/status,verbs=get;list;watch
70+ // +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machinepools;machinepools/status,verbs=get;list;watch;create
71+ // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=rosamachinepools;rosamachinepools/status,verbs=get;list;watch;create
5772// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete
5873
5974func (r * ROSAClusterReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (_ ctrl.Result , reterr error ) {
60- log := ctrl . LoggerFrom (ctx )
75+ log := logger . FromContext (ctx )
6176 log .Info ("Reconciling ROSACluster" )
6277
6378 // Fetch the ROSACluster instance
@@ -70,11 +85,17 @@ func (r *ROSAClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
7085 return reconcile.Result {}, err
7186 }
7287
88+ if ! rosaCluster .DeletionTimestamp .IsZero () {
89+ log .Info ("Deleting ROSACluster." )
90+ return reconcile.Result {}, nil
91+ }
92+
7393 // Fetch the Cluster.
7494 cluster , err := util .GetOwnerCluster (ctx , r .Client , rosaCluster .ObjectMeta )
7595 if err != nil {
7696 return reconcile.Result {}, err
7797 }
98+
7899 if cluster == nil {
79100 log .Info ("Cluster Controller has not yet set OwnerRef" )
80101 return reconcile.Result {}, nil
@@ -111,13 +132,122 @@ func (r *ROSAClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
111132 return reconcile.Result {}, fmt .Errorf ("failed to patch ROSACluster: %w" , err )
112133 }
113134
135+ if controlPlane .Status .Ready {
136+ // List the ROSA-HCP nodePools and ROSA MachinePools
137+ rosaScope , err := scope .NewROSAControlPlaneScope (scope.ROSAControlPlaneScopeParams {
138+ Client : r .Client ,
139+ Cluster : cluster ,
140+ ControlPlane : controlPlane ,
141+ ControllerName : "" ,
142+ Logger : log ,
143+ NewStsClient : r .NewStsClient ,
144+ })
145+ if err != nil {
146+ return ctrl.Result {}, fmt .Errorf ("failed to create rosa controlplane scope: %w" , err )
147+ }
148+
149+ if r .NewOCMClient == nil {
150+ return ctrl.Result {}, fmt .Errorf ("failed to create OCM client: NewOCMClient is nil" )
151+ }
152+
153+ ocmClient , err := r .NewOCMClient (ctx , rosaScope )
154+ if err != nil || ocmClient == nil {
155+ return ctrl.Result {}, fmt .Errorf ("failed to create OCM client: %w" , err )
156+ }
157+
158+ nodePools , err := ocmClient .GetNodePools (rosaScope .ControlPlane .Status .ID )
159+ if err != nil {
160+ return ctrl.Result {}, fmt .Errorf ("failed to get nodePools: %w" , err )
161+ }
162+
163+ rosaMPNames , err := r .getRosaMachinePoolNames (ctx , cluster )
164+ if err != nil {
165+ return ctrl.Result {}, fmt .Errorf ("failed to get Rosa machinePool names: %w" , err )
166+ }
167+
168+ var errs []error
169+ for _ , nodePool := range nodePools {
170+ // continue if nodePool is not in ready state.
171+ if ! rosa .IsNodePoolReady (nodePool ) {
172+ continue
173+ }
174+ // continue if nodePool exist
175+ if rosaMPNames [nodePool .ID ()] {
176+ continue
177+ }
178+
179+ // create RosaMachinePool
180+ rosaMPSpec := utils .NodePoolToRosaMachinePoolSpec (nodePool )
181+ rosaMachinePool := & expinfrav1.ROSAMachinePool {
182+ TypeMeta : metav1.TypeMeta {
183+ APIVersion : expinfrav1 .GroupVersion .String (),
184+ Kind : "ROSAMachinePool" ,
185+ },
186+ ObjectMeta : metav1.ObjectMeta {
187+ Name : nodePool .ID (),
188+ Namespace : controlPlane .Namespace ,
189+ Labels : map [string ]string {
190+ clusterv1 .ClusterNameLabel : cluster .Name ,
191+ },
192+ },
193+ Spec : rosaMPSpec ,
194+ }
195+ log .Info (fmt .Sprintf ("Create ROSAMachinePool %s" , rosaMachinePool .Name ))
196+ if err = r .Client .Create (ctx , rosaMachinePool ); err != nil {
197+ errs = append (errs , err )
198+ }
199+
200+ // create MachinePool
201+ machinePool := & expclusterv1.MachinePool {
202+ TypeMeta : metav1.TypeMeta {
203+ APIVersion : expclusterv1 .GroupVersion .String (),
204+ Kind : "MachinePool" ,
205+ },
206+ ObjectMeta : metav1.ObjectMeta {
207+ Name : nodePool .ID (),
208+ Namespace : cluster .Namespace ,
209+ Labels : map [string ]string {
210+ clusterv1 .ClusterNameLabel : cluster .Name ,
211+ },
212+ },
213+ Spec : expclusterv1.MachinePoolSpec {
214+ ClusterName : cluster .Name ,
215+ Replicas : ptr .To (int32 (1 )),
216+ Template : clusterv1.MachineTemplateSpec {
217+ Spec : clusterv1.MachineSpec {
218+ ClusterName : cluster .Name ,
219+ Bootstrap : clusterv1.Bootstrap {
220+ DataSecretName : ptr .To (string ("" )),
221+ },
222+ InfrastructureRef : corev1.ObjectReference {
223+ APIVersion : expinfrav1 .GroupVersion .String (),
224+ Kind : "ROSAMachinePool" ,
225+ Name : rosaMachinePool .Name ,
226+ },
227+ },
228+ },
229+ },
230+ }
231+ log .Info (fmt .Sprintf ("Create MachinePool %s" , machinePool .Name ))
232+ if err = r .Client .Create (ctx , machinePool ); err != nil {
233+ errs = append (errs , err )
234+ }
235+ }
236+
237+ if len (errs ) > 0 {
238+ return ctrl.Result {}, kerrors .NewAggregate (errs )
239+ }
240+ }
241+
114242 log .Info ("Successfully reconciled ROSACluster" )
115243
116244 return reconcile.Result {}, nil
117245}
118246
119247func (r * ROSAClusterReconciler ) SetupWithManager (ctx context.Context , mgr ctrl.Manager , options controller.Options ) error {
120248 log := logger .FromContext (ctx )
249+ r .NewOCMClient = rosa .NewWrappedOCMClient
250+ r .NewStsClient = scope .NewSTSClient
121251
122252 rosaCluster := & expinfrav1.ROSACluster {}
123253
@@ -196,3 +326,26 @@ func (r *ROSAClusterReconciler) rosaControlPlaneToManagedCluster(log *logger.Log
196326 }
197327 }
198328}
329+
330+ // GetRosMachinePools get map of RosaMachinePool names associatd with the cluster.
331+ func (r * ROSAClusterReconciler ) getRosaMachinePoolNames (ctx context.Context , cluster * clusterv1.Cluster ) (map [string ]bool , error ) {
332+ selectors := []client.ListOption {
333+ client .InNamespace (cluster .GetNamespace ()),
334+ client.MatchingLabels {
335+ clusterv1 .ClusterNameLabel : cluster .GetName (),
336+ },
337+ }
338+
339+ rosaMachinePoolList := & expinfrav1.ROSAMachinePoolList {}
340+ err := r .Client .List (ctx , rosaMachinePoolList , selectors ... )
341+ if err != nil {
342+ return nil , err
343+ }
344+
345+ rosaMPNames := make (map [string ]bool )
346+ for _ , rosaMP := range rosaMachinePoolList .Items {
347+ rosaMPNames [rosaMP .Spec .NodePoolName ] = true
348+ }
349+
350+ return rosaMPNames , nil
351+ }
0 commit comments