Skip to content

Commit 80cbf54

Browse files
committed
Fix RosaControlPlane sync defaulet RosaMachinePool
Signed-off-by: serngawy <[email protected]>
1 parent 645f38e commit 80cbf54

File tree

10 files changed

+728
-61
lines changed

10 files changed

+728
-61
lines changed

config/rbac/role.yaml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ rules:
8383
- machinepools
8484
- machinepools/status
8585
verbs:
86+
- create
8687
- get
8788
- list
8889
- patch
@@ -161,7 +162,6 @@ rules:
161162
- awsmanagedclusters
162163
- awsmanagedmachinepools
163164
- rosaclusters
164-
- rosamachinepools
165165
verbs:
166166
- delete
167167
- get
@@ -175,7 +175,6 @@ rules:
175175
- awsclusters/status
176176
- awsfargateprofiles/status
177177
- rosaclusters/status
178-
- rosamachinepools/status
179178
verbs:
180179
- get
181180
- patch
@@ -197,6 +196,7 @@ rules:
197196
- infrastructure.cluster.x-k8s.io
198197
resources:
199198
- awsmachines
199+
- rosamachinepools
200200
verbs:
201201
- create
202202
- delete
@@ -211,3 +211,14 @@ rules:
211211
- rosamachinepools/finalizers
212212
verbs:
213213
- update
214+
- apiGroups:
215+
- infrastructure.cluster.x-k8s.io
216+
resources:
217+
- rosamachinepools/status
218+
verbs:
219+
- create
220+
- get
221+
- list
222+
- patch
223+
- update
224+
- watch

controllers/rosacluster_controller.go

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,17 @@ import (
2020
"context"
2121
"fmt"
2222

23+
cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"
2324
"github.com/pkg/errors"
25+
corev1 "k8s.io/api/core/v1"
2426
apierrors "k8s.io/apimachinery/pkg/api/errors"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/apimachinery/pkg/runtime"
2529
"k8s.io/apimachinery/pkg/types"
30+
kerrors "k8s.io/apimachinery/pkg/util/errors"
2631
"k8s.io/client-go/tools/record"
2732
"k8s.io/klog/v2"
33+
"k8s.io/utils/ptr"
2834
ctrl "sigs.k8s.io/controller-runtime"
2935
"sigs.k8s.io/controller-runtime/pkg/client"
3036
"sigs.k8s.io/controller-runtime/pkg/controller"
@@ -35,9 +41,15 @@ import (
3541
infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
3642
rosacontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/rosa/api/v1beta2"
3743
expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
44+
"sigs.k8s.io/cluster-api-provider-aws/v2/exp/utils"
45+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud"
46+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope"
47+
stsservice "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/sts"
3848
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
49+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/rosa"
3950
"sigs.k8s.io/cluster-api-provider-aws/v2/util/paused"
4051
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
52+
expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
4153
"sigs.k8s.io/cluster-api/util"
4254
"sigs.k8s.io/cluster-api/util/patch"
4355
"sigs.k8s.io/cluster-api/util/predicates"
@@ -48,16 +60,20 @@ type ROSAClusterReconciler struct {
4860
client.Client
4961
Recorder record.EventRecorder
5062
WatchFilterValue string
63+
NewStsClient func(cloud.ScopeUsage, cloud.Session, logger.Wrapper, runtime.Object) stsservice.STSClient
64+
NewOCMClient func(ctx context.Context, rosaScope *scope.ROSAControlPlaneScope) (rosa.OCMClient, error)
5165
}
5266

5367
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=rosaclusters,verbs=get;list;watch;update;patch;delete
5468
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=rosaclusters/status,verbs=get;update;patch
5569
// +kubebuilder:rbac:groups=controlplane.cluster.x-k8s.io,resources=rosacontrolplanes;rosacontrolplanes/status,verbs=get;list;watch
5670
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters;clusters/status,verbs=get;list;watch
71+
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machinepools;machinepools/status,verbs=get;list;watch;create
72+
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=rosamachinepools;rosamachinepools/status,verbs=get;list;watch;create
5773
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete
5874

5975
func (r *ROSAClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) {
60-
log := ctrl.LoggerFrom(ctx)
76+
log := logger.FromContext(ctx)
6177
log.Info("Reconciling ROSACluster")
6278

6379
// Fetch the ROSACluster instance
@@ -70,11 +86,17 @@ func (r *ROSAClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
7086
return reconcile.Result{}, err
7187
}
7288

89+
if !rosaCluster.DeletionTimestamp.IsZero() {
90+
log.Info("Deleting ROSACluster.")
91+
return reconcile.Result{}, nil
92+
}
93+
7394
// Fetch the Cluster.
7495
cluster, err := util.GetOwnerCluster(ctx, r.Client, rosaCluster.ObjectMeta)
7596
if err != nil {
7697
return reconcile.Result{}, err
7798
}
99+
78100
if cluster == nil {
79101
log.Info("Cluster Controller has not yet set OwnerRef")
80102
return reconcile.Result{}, nil
@@ -111,13 +133,78 @@ func (r *ROSAClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
111133
return reconcile.Result{}, fmt.Errorf("failed to patch ROSACluster: %w", err)
112134
}
113135

136+
if controlPlane.Status.Ready {
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+
// List the ROSA-HCP nodePools and MachinePools
159+
nodePools, err := ocmClient.GetNodePools(rosaScope.ControlPlane.Status.ID)
160+
if err != nil {
161+
return ctrl.Result{}, fmt.Errorf("failed to get nodePools: %w", err)
162+
}
163+
164+
rosaMPNames, err := r.getRosaMachinePoolNames(ctx, cluster)
165+
if err != nil {
166+
return ctrl.Result{}, fmt.Errorf("failed to get Rosa machinePool names: %w", err)
167+
}
168+
169+
// Ensure every NodePool has a MachinePool and create a corresponding MachinePool if it does not exist.
170+
var errs []error
171+
for _, nodePool := range nodePools {
172+
// continue if nodePool is not in ready state.
173+
if !rosa.IsNodePoolReady(nodePool) {
174+
continue
175+
}
176+
// continue if nodePool exist
177+
if rosaMPNames[nodePool.ID()] {
178+
continue
179+
}
180+
// create ROSAMachinePool & MachinePool
181+
rosaMachinePool, machinePool := r.buildROSAMachinePool(nodePool.ID(), cluster.Name, cluster.Namespace, nodePool)
182+
183+
log.Info(fmt.Sprintf("Create ROSAMachinePool %s", rosaMachinePool.Name))
184+
if err = r.Client.Create(ctx, rosaMachinePool); err != nil {
185+
errs = append(errs, err)
186+
}
187+
188+
log.Info(fmt.Sprintf("Create MachinePool %s", machinePool.Name))
189+
if err = r.Client.Create(ctx, machinePool); err != nil {
190+
errs = append(errs, err)
191+
}
192+
}
193+
194+
if len(errs) > 0 {
195+
return ctrl.Result{}, kerrors.NewAggregate(errs)
196+
}
197+
}
198+
114199
log.Info("Successfully reconciled ROSACluster")
115200

116201
return reconcile.Result{}, nil
117202
}
118203

119204
func (r *ROSAClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
120205
log := logger.FromContext(ctx)
206+
r.NewOCMClient = rosa.NewWrappedOCMClient
207+
r.NewStsClient = scope.NewSTSClient
121208

122209
rosaCluster := &expinfrav1.ROSACluster{}
123210

@@ -196,3 +283,77 @@ func (r *ROSAClusterReconciler) rosaControlPlaneToManagedCluster(log *logger.Log
196283
}
197284
}
198285
}
286+
287+
// getRosMachinePools returns a map of RosaMachinePool names associatd with the cluster.
288+
func (r *ROSAClusterReconciler) getRosaMachinePoolNames(ctx context.Context, cluster *clusterv1.Cluster) (map[string]bool, error) {
289+
selectors := []client.ListOption{
290+
client.InNamespace(cluster.GetNamespace()),
291+
client.MatchingLabels{
292+
clusterv1.ClusterNameLabel: cluster.GetName(),
293+
},
294+
}
295+
296+
rosaMachinePoolList := &expinfrav1.ROSAMachinePoolList{}
297+
err := r.Client.List(ctx, rosaMachinePoolList, selectors...)
298+
if err != nil {
299+
return nil, err
300+
}
301+
302+
rosaMPNames := make(map[string]bool)
303+
for _, rosaMP := range rosaMachinePoolList.Items {
304+
rosaMPNames[rosaMP.Spec.NodePoolName] = true
305+
}
306+
307+
return rosaMPNames, nil
308+
}
309+
310+
// buildROSAMachinePool returns a ROSAMachinePool and its corresponding MachinePool.
311+
func (r *ROSAClusterReconciler) buildROSAMachinePool(nodePoolName string, clusterName string, namespace string, nodePool *cmv1.NodePool) (*expinfrav1.ROSAMachinePool, *expclusterv1.MachinePool) {
312+
rosaMPSpec := utils.NodePoolToRosaMachinePoolSpec(nodePool)
313+
rosaMachinePool := &expinfrav1.ROSAMachinePool{
314+
TypeMeta: metav1.TypeMeta{
315+
APIVersion: expinfrav1.GroupVersion.String(),
316+
Kind: "ROSAMachinePool",
317+
},
318+
ObjectMeta: metav1.ObjectMeta{
319+
Name: nodePoolName,
320+
Namespace: namespace,
321+
Labels: map[string]string{
322+
clusterv1.ClusterNameLabel: clusterName,
323+
},
324+
},
325+
Spec: rosaMPSpec,
326+
}
327+
machinePool := &expclusterv1.MachinePool{
328+
TypeMeta: metav1.TypeMeta{
329+
APIVersion: expclusterv1.GroupVersion.String(),
330+
Kind: "MachinePool",
331+
},
332+
ObjectMeta: metav1.ObjectMeta{
333+
Name: nodePoolName,
334+
Namespace: namespace,
335+
Labels: map[string]string{
336+
clusterv1.ClusterNameLabel: clusterName,
337+
},
338+
},
339+
Spec: expclusterv1.MachinePoolSpec{
340+
ClusterName: clusterName,
341+
Replicas: ptr.To(int32(1)),
342+
Template: clusterv1.MachineTemplateSpec{
343+
Spec: clusterv1.MachineSpec{
344+
ClusterName: clusterName,
345+
Bootstrap: clusterv1.Bootstrap{
346+
DataSecretName: ptr.To(string("")),
347+
},
348+
InfrastructureRef: corev1.ObjectReference{
349+
APIVersion: expinfrav1.GroupVersion.String(),
350+
Kind: "ROSAMachinePool",
351+
Name: rosaMachinePool.Name,
352+
},
353+
},
354+
},
355+
},
356+
}
357+
358+
return rosaMachinePool, machinePool
359+
}

0 commit comments

Comments
 (0)