Skip to content

Commit 518f257

Browse files
authored
Merge pull request #5691 from k8s-infra-cherrypick-robot/cherry-pick-5629-to-release-2.9
[release-2.9] 🐛 ROSA: Fix RosaControlPlane sync default RosaMachinePool
2 parents ecf83e5 + ed62651 commit 518f257

File tree

10 files changed

+737
-61
lines changed

10 files changed

+737
-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
@@ -162,7 +163,6 @@ rules:
162163
- awsmanagedclusters
163164
- awsmanagedmachinepools
164165
- rosaclusters
165-
- rosamachinepools
166166
verbs:
167167
- delete
168168
- get
@@ -176,7 +176,6 @@ rules:
176176
- awsclusters/status
177177
- awsfargateprofiles/status
178178
- rosaclusters/status
179-
- rosamachinepools/status
180179
verbs:
181180
- get
182181
- patch
@@ -198,6 +197,7 @@ rules:
198197
- infrastructure.cluster.x-k8s.io
199198
resources:
200199
- awsmachines
200+
- rosamachinepools
201201
verbs:
202202
- create
203203
- delete
@@ -212,3 +212,14 @@ rules:
212212
- rosamachinepools/finalizers
213213
verbs:
214214
- update
215+
- apiGroups:
216+
- infrastructure.cluster.x-k8s.io
217+
resources:
218+
- rosamachinepools/status
219+
verbs:
220+
- create
221+
- get
222+
- list
223+
- patch
224+
- update
225+
- watch

controllers/rosacluster_controller.go

Lines changed: 171 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,32 @@ 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+
rosaScope, err := scope.NewROSAControlPlaneScope(scope.ROSAControlPlaneScopeParams{
137+
Client: r.Client,
138+
Cluster: cluster,
139+
ControlPlane: controlPlane,
140+
ControllerName: "",
141+
Logger: log,
142+
NewStsClient: r.NewStsClient,
143+
})
144+
if err != nil {
145+
return ctrl.Result{}, fmt.Errorf("failed to create rosa controlplane scope: %w", err)
146+
}
147+
148+
err = r.syncROSAClusterNodePools(ctx, controlPlane, rosaScope)
149+
if err != nil {
150+
return ctrl.Result{}, fmt.Errorf("failed to sync ROSA cluster nodePools: %w", err)
151+
}
152+
114153
log.Info("Successfully reconciled ROSACluster")
115154

116155
return reconcile.Result{}, nil
117156
}
118157

119158
func (r *ROSAClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
120159
log := logger.FromContext(ctx)
160+
r.NewOCMClient = rosa.NewWrappedOCMClient
161+
r.NewStsClient = scope.NewSTSClient
121162

122163
rosaCluster := &expinfrav1.ROSACluster{}
123164

@@ -196,3 +237,132 @@ func (r *ROSAClusterReconciler) rosaControlPlaneToManagedCluster(log *logger.Log
196237
}
197238
}
198239
}
240+
241+
// getRosMachinePools returns a map of RosaMachinePool names associatd with the cluster.
242+
func (r *ROSAClusterReconciler) getRosaMachinePoolNames(ctx context.Context, cluster *clusterv1.Cluster) (map[string]bool, error) {
243+
selectors := []client.ListOption{
244+
client.InNamespace(cluster.GetNamespace()),
245+
client.MatchingLabels{
246+
clusterv1.ClusterNameLabel: cluster.GetName(),
247+
},
248+
}
249+
250+
rosaMachinePoolList := &expinfrav1.ROSAMachinePoolList{}
251+
err := r.Client.List(ctx, rosaMachinePoolList, selectors...)
252+
if err != nil {
253+
return nil, err
254+
}
255+
256+
rosaMPNames := make(map[string]bool)
257+
for _, rosaMP := range rosaMachinePoolList.Items {
258+
rosaMPNames[rosaMP.Spec.NodePoolName] = true
259+
}
260+
261+
return rosaMPNames, nil
262+
}
263+
264+
// buildROSAMachinePool returns a ROSAMachinePool and its corresponding MachinePool.
265+
func (r *ROSAClusterReconciler) buildROSAMachinePool(nodePoolName string, clusterName string, namespace string, nodePool *cmv1.NodePool) (*expinfrav1.ROSAMachinePool, *expclusterv1.MachinePool) {
266+
rosaMPSpec := utils.NodePoolToRosaMachinePoolSpec(nodePool)
267+
rosaMachinePool := &expinfrav1.ROSAMachinePool{
268+
TypeMeta: metav1.TypeMeta{
269+
APIVersion: expinfrav1.GroupVersion.String(),
270+
Kind: "ROSAMachinePool",
271+
},
272+
ObjectMeta: metav1.ObjectMeta{
273+
Name: nodePoolName,
274+
Namespace: namespace,
275+
Labels: map[string]string{
276+
clusterv1.ClusterNameLabel: clusterName,
277+
},
278+
},
279+
Spec: rosaMPSpec,
280+
}
281+
machinePool := &expclusterv1.MachinePool{
282+
TypeMeta: metav1.TypeMeta{
283+
APIVersion: expclusterv1.GroupVersion.String(),
284+
Kind: "MachinePool",
285+
},
286+
ObjectMeta: metav1.ObjectMeta{
287+
Name: nodePoolName,
288+
Namespace: namespace,
289+
Labels: map[string]string{
290+
clusterv1.ClusterNameLabel: clusterName,
291+
},
292+
},
293+
Spec: expclusterv1.MachinePoolSpec{
294+
ClusterName: clusterName,
295+
Replicas: ptr.To(int32(1)),
296+
Template: clusterv1.MachineTemplateSpec{
297+
Spec: clusterv1.MachineSpec{
298+
ClusterName: clusterName,
299+
Bootstrap: clusterv1.Bootstrap{
300+
DataSecretName: ptr.To(string("")),
301+
},
302+
InfrastructureRef: corev1.ObjectReference{
303+
APIVersion: expinfrav1.GroupVersion.String(),
304+
Kind: "ROSAMachinePool",
305+
Name: rosaMachinePool.Name,
306+
},
307+
},
308+
},
309+
},
310+
}
311+
312+
return rosaMachinePool, machinePool
313+
}
314+
315+
// syncROSAClusterNodePools ensure every NodePool has a MachinePool and create a corresponding MachinePool if it does not exist.
316+
func (r *ROSAClusterReconciler) syncROSAClusterNodePools(ctx context.Context, controlPlane *rosacontrolplanev1.ROSAControlPlane, rosaScope *scope.ROSAControlPlaneScope) error {
317+
if controlPlane.Status.Ready {
318+
if r.NewOCMClient == nil {
319+
return fmt.Errorf("failed to create OCM client: NewOCMClient is nil")
320+
}
321+
322+
ocmClient, err := r.NewOCMClient(ctx, rosaScope)
323+
if err != nil || ocmClient == nil {
324+
return fmt.Errorf("failed to create OCM client: %w", err)
325+
}
326+
327+
// List the ROSA-HCP nodePools and MachinePools
328+
nodePools, err := ocmClient.GetNodePools(rosaScope.ControlPlane.Status.ID)
329+
if err != nil {
330+
return fmt.Errorf("failed to get nodePools: %w", err)
331+
}
332+
333+
rosaMPNames, err := r.getRosaMachinePoolNames(ctx, rosaScope.Cluster)
334+
if err != nil {
335+
return fmt.Errorf("failed to get Rosa machinePool names: %w", err)
336+
}
337+
338+
// Ensure every NodePool has a MachinePool and create a corresponding MachinePool if it does not exist.
339+
var errs []error
340+
for _, nodePool := range nodePools {
341+
// continue if nodePool is not in ready state.
342+
if !rosa.IsNodePoolReady(nodePool) {
343+
continue
344+
}
345+
// continue if nodePool exist
346+
if rosaMPNames[nodePool.ID()] {
347+
continue
348+
}
349+
// create ROSAMachinePool & MachinePool
350+
rosaMachinePool, machinePool := r.buildROSAMachinePool(nodePool.ID(), rosaScope.Cluster.Name, rosaScope.Cluster.Namespace, nodePool)
351+
352+
rosaScope.Logger.Info(fmt.Sprintf("Create ROSAMachinePool %s", rosaMachinePool.Name))
353+
if err = r.Client.Create(ctx, rosaMachinePool); err != nil {
354+
errs = append(errs, err)
355+
}
356+
357+
rosaScope.Logger.Info(fmt.Sprintf("Create MachinePool %s", machinePool.Name))
358+
if err = r.Client.Create(ctx, machinePool); err != nil {
359+
errs = append(errs, err)
360+
}
361+
}
362+
363+
if len(errs) > 0 {
364+
return kerrors.NewAggregate(errs)
365+
}
366+
}
367+
return nil
368+
}

0 commit comments

Comments
 (0)