@@ -21,10 +21,15 @@ import (
21
21
"fmt"
22
22
23
23
"github.com/pkg/errors"
24
+ corev1 "k8s.io/api/core/v1"
24
25
apierrors "k8s.io/apimachinery/pkg/api/errors"
26
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27
+ "k8s.io/apimachinery/pkg/runtime"
25
28
"k8s.io/apimachinery/pkg/types"
29
+ kerrors "k8s.io/apimachinery/pkg/util/errors"
26
30
"k8s.io/client-go/tools/record"
27
31
"k8s.io/klog/v2"
32
+ "k8s.io/utils/ptr"
28
33
ctrl "sigs.k8s.io/controller-runtime"
29
34
"sigs.k8s.io/controller-runtime/pkg/client"
30
35
"sigs.k8s.io/controller-runtime/pkg/controller"
@@ -35,9 +40,15 @@ import (
35
40
infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
36
41
rosacontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/rosa/api/v1beta2"
37
42
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"
38
47
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
48
+ "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/rosa"
39
49
"sigs.k8s.io/cluster-api-provider-aws/v2/util/paused"
40
50
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
51
+ expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
41
52
"sigs.k8s.io/cluster-api/util"
42
53
"sigs.k8s.io/cluster-api/util/patch"
43
54
"sigs.k8s.io/cluster-api/util/predicates"
@@ -48,16 +59,20 @@ type ROSAClusterReconciler struct {
48
59
client.Client
49
60
Recorder record.EventRecorder
50
61
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 )
51
64
}
52
65
53
66
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=rosaclusters,verbs=get;list;watch;update;patch;delete
54
67
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=rosaclusters/status,verbs=get;update;patch
55
68
// +kubebuilder:rbac:groups=controlplane.cluster.x-k8s.io,resources=rosacontrolplanes;rosacontrolplanes/status,verbs=get;list;watch
56
69
// +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
57
72
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete
58
73
59
74
func (r * ROSAClusterReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (_ ctrl.Result , reterr error ) {
60
- log := ctrl . LoggerFrom (ctx )
75
+ log := logger . FromContext (ctx )
61
76
log .Info ("Reconciling ROSACluster" )
62
77
63
78
// Fetch the ROSACluster instance
@@ -70,11 +85,17 @@ func (r *ROSAClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
70
85
return reconcile.Result {}, err
71
86
}
72
87
88
+ if ! rosaCluster .DeletionTimestamp .IsZero () {
89
+ log .Info ("Deleting ROSACluster." )
90
+ return reconcile.Result {}, nil
91
+ }
92
+
73
93
// Fetch the Cluster.
74
94
cluster , err := util .GetOwnerCluster (ctx , r .Client , rosaCluster .ObjectMeta )
75
95
if err != nil {
76
96
return reconcile.Result {}, err
77
97
}
98
+
78
99
if cluster == nil {
79
100
log .Info ("Cluster Controller has not yet set OwnerRef" )
80
101
return reconcile.Result {}, nil
@@ -111,13 +132,122 @@ func (r *ROSAClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
111
132
return reconcile.Result {}, fmt .Errorf ("failed to patch ROSACluster: %w" , err )
112
133
}
113
134
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
+
114
242
log .Info ("Successfully reconciled ROSACluster" )
115
243
116
244
return reconcile.Result {}, nil
117
245
}
118
246
119
247
func (r * ROSAClusterReconciler ) SetupWithManager (ctx context.Context , mgr ctrl.Manager , options controller.Options ) error {
120
248
log := logger .FromContext (ctx )
249
+ r .NewOCMClient = rosa .NewWrappedOCMClient
250
+ r .NewStsClient = scope .NewSTSClient
121
251
122
252
rosaCluster := & expinfrav1.ROSACluster {}
123
253
@@ -196,3 +326,26 @@ func (r *ROSAClusterReconciler) rosaControlPlaneToManagedCluster(log *logger.Log
196
326
}
197
327
}
198
328
}
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