@@ -20,10 +20,12 @@ import (
20
20
"context"
21
21
"fmt"
22
22
"strings"
23
+ "sync"
23
24
"time"
24
25
25
26
"github.com/pkg/errors"
26
27
28
+ corev1 "k8s.io/api/core/v1"
27
29
apierrors "k8s.io/apimachinery/pkg/api/errors"
28
30
"k8s.io/apimachinery/pkg/api/meta"
29
31
"k8s.io/apimachinery/pkg/runtime"
@@ -114,6 +116,116 @@ func (r *IBMPowerVSClusterReconciler) Reconcile(ctx context.Context, req ctrl.Re
114
116
return r .reconcile (clusterScope )
115
117
}
116
118
119
+ type powerVSCluster struct {
120
+ cluster * infrav1beta2.IBMPowerVSCluster
121
+ mu sync.Mutex
122
+ }
123
+
124
+ type reconcileResult struct {
125
+ reconcile.Result
126
+ error
127
+ }
128
+
129
+ func (update * powerVSCluster ) updateCondition (condition bool , conditionArgs ... interface {}) {
130
+ update .mu .Lock ()
131
+ defer update .mu .Unlock ()
132
+ if condition {
133
+ conditions .MarkTrue (update .cluster , conditionArgs [0 ].(capiv1beta1.ConditionType ))
134
+ return
135
+ }
136
+
137
+ conditions .MarkFalse (update .cluster , conditionArgs [0 ].(capiv1beta1.ConditionType ), conditionArgs [1 ].(string ), conditionArgs [2 ].(capiv1beta1.ConditionSeverity ), conditionArgs [3 ].(string ), conditionArgs [4 :]... )
138
+ }
139
+
140
+ func (r * IBMPowerVSClusterReconciler ) reconcilePowerVSResources (clusterScope * scope.PowerVSClusterScope , powerVSCluster * powerVSCluster , ch chan reconcileResult , wg * sync.WaitGroup ) {
141
+ defer wg .Done ()
142
+ powerVSLog := clusterScope .WithName ("powervs" )
143
+ // reconcile PowerVS service instance
144
+ powerVSLog .Info ("Reconciling PowerVS service instance" )
145
+ if requeue , err := clusterScope .ReconcilePowerVSServiceInstance (); err != nil {
146
+ powerVSLog .Error (err , "failed to reconcile PowerVS service instance" )
147
+ powerVSCluster .updateCondition (false , infrav1beta2 .ServiceInstanceReadyCondition , infrav1beta2 .ServiceInstanceReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
148
+ ch <- reconcileResult {reconcile.Result {}, err }
149
+ return
150
+ } else if requeue {
151
+ powerVSLog .Info ("PowerVS service instance creation is pending, requeuing" )
152
+ ch <- reconcileResult {reconcile.Result {Requeue : true }, nil }
153
+ return
154
+ }
155
+ powerVSCluster .updateCondition (true , infrav1beta2 .ServiceInstanceReadyCondition )
156
+
157
+ clusterScope .IBMPowerVSClient .WithClients (powervs.ServiceOptions {CloudInstanceID : clusterScope .GetServiceInstanceID ()})
158
+
159
+ // reconcile network
160
+ powerVSLog .Info ("Reconciling network" )
161
+ if dhcpServerActive , err := clusterScope .ReconcileNetwork (); err != nil {
162
+ powerVSLog .Error (err , "failed to reconcile PowerVS network" )
163
+ powerVSCluster .updateCondition (false , infrav1beta2 .NetworkReadyCondition , infrav1beta2 .NetworkReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
164
+ ch <- reconcileResult {reconcile.Result {}, err }
165
+ return
166
+ } else if dhcpServerActive {
167
+ powerVSCluster .updateCondition (true , infrav1beta2 .NetworkReadyCondition )
168
+ return
169
+ }
170
+ // Do not want to block the reconciliation of other resources like setting up TG and COS, so skipping the requeue and only logging the info.
171
+ powerVSLog .Info ("PowerVS network creation is pending" )
172
+ }
173
+
174
+ func (r * IBMPowerVSClusterReconciler ) reconcileVPCResources (clusterScope * scope.PowerVSClusterScope , powerVSCluster * powerVSCluster , ch chan reconcileResult , wg * sync.WaitGroup ) {
175
+ defer wg .Done ()
176
+ vpcLog := clusterScope .WithName ("vpc" )
177
+ vpcLog .Info ("Reconciling VPC" )
178
+ if requeue , err := clusterScope .ReconcileVPC (); err != nil {
179
+ clusterScope .Error (err , "failed to reconcile VPC" )
180
+ powerVSCluster .updateCondition (false , infrav1beta2 .VPCReadyCondition , infrav1beta2 .VPCReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
181
+ ch <- reconcileResult {reconcile.Result {}, err }
182
+ return
183
+ } else if requeue {
184
+ vpcLog .Info ("VPC creation is pending, requeuing" )
185
+ ch <- reconcileResult {reconcile.Result {Requeue : true }, nil }
186
+ return
187
+ }
188
+ powerVSCluster .updateCondition (true , infrav1beta2 .VPCReadyCondition )
189
+
190
+ // reconcile VPC Subnet
191
+ vpcLog .Info ("Reconciling VPC subnets" )
192
+ if requeue , err := clusterScope .ReconcileVPCSubnets (); err != nil {
193
+ vpcLog .Error (err , "failed to reconcile VPC subnets" )
194
+ powerVSCluster .updateCondition (false , infrav1beta2 .VPCSubnetReadyCondition , infrav1beta2 .VPCSubnetReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
195
+ ch <- reconcileResult {reconcile.Result {}, err }
196
+ return
197
+ } else if requeue {
198
+ vpcLog .Info ("VPC subnet creation is pending, requeuing" )
199
+ ch <- reconcileResult {reconcile.Result {Requeue : true }, nil }
200
+ return
201
+ }
202
+ powerVSCluster .updateCondition (true , infrav1beta2 .VPCSubnetReadyCondition )
203
+
204
+ // reconcile VPC security group
205
+ vpcLog .Info ("Reconciling VPC security group" )
206
+ if err := clusterScope .ReconcileVPCSecurityGroups (); err != nil {
207
+ vpcLog .Error (err , "failed to reconcile VPC security groups" )
208
+ powerVSCluster .updateCondition (false , infrav1beta2 .VPCSecurityGroupReadyCondition , infrav1beta2 .VPCSecurityGroupReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
209
+ ch <- reconcileResult {reconcile.Result {}, err }
210
+ return
211
+ }
212
+ powerVSCluster .updateCondition (true , infrav1beta2 .VPCSecurityGroupReadyCondition )
213
+
214
+ // reconcile LoadBalancer
215
+ vpcLog .Info ("Reconciling VPC load balancers" )
216
+ if loadBalancerReady , err := clusterScope .ReconcileLoadBalancers (); err != nil {
217
+ vpcLog .Error (err , "failed to reconcile VPC load balancers" )
218
+ powerVSCluster .updateCondition (false , infrav1beta2 .LoadBalancerReadyCondition , infrav1beta2 .LoadBalancerReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
219
+ ch <- reconcileResult {reconcile.Result {}, err }
220
+ return
221
+ } else if loadBalancerReady {
222
+ powerVSCluster .updateCondition (true , infrav1beta2 .LoadBalancerReadyCondition )
223
+ return
224
+ }
225
+ // Do not want to block the reconciliation of other resources like setting up TG and COS, so skipping the requeue and only logging the info.
226
+ vpcLog .Info ("VPC load balancer creation is pending" )
227
+ }
228
+
117
229
func (r * IBMPowerVSClusterReconciler ) reconcile (clusterScope * scope.PowerVSClusterScope ) (ctrl.Result , error ) { //nolint:gocyclo
118
230
if controllerutil .AddFinalizer (clusterScope .IBMPowerVSCluster , infrav1beta2 .IBMPowerVSClusterFinalizer ) {
119
231
return ctrl.Result {}, nil
@@ -140,116 +252,96 @@ func (r *IBMPowerVSClusterReconciler) reconcile(clusterScope *scope.PowerVSClust
140
252
return reconcile.Result {}, err
141
253
}
142
254
143
- powerVSCluster := clusterScope .IBMPowerVSCluster
144
- // reconcile PowerVS service instance
145
- clusterScope .Info ("Reconciling PowerVS service instance" )
146
- if requeue , err := clusterScope .ReconcilePowerVSServiceInstance (); err != nil {
147
- clusterScope .Error (err , "failed to reconcile PowerVS service instance" )
148
- conditions .MarkFalse (powerVSCluster , infrav1beta2 .ServiceInstanceReadyCondition , infrav1beta2 .ServiceInstanceReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
149
- return reconcile.Result {}, err
150
- } else if requeue {
151
- clusterScope .Info ("PowerVS service instance creation is pending, requeuing" )
152
- return reconcile.Result {RequeueAfter : 1 * time .Minute }, nil
255
+ powerVSCluster := & powerVSCluster {
256
+ cluster : clusterScope .IBMPowerVSCluster ,
153
257
}
154
- conditions .MarkTrue (powerVSCluster , infrav1beta2 .ServiceInstanceReadyCondition )
155
258
156
- clusterScope .IBMPowerVSClient .WithClients (powervs.ServiceOptions {CloudInstanceID : clusterScope .GetServiceInstanceID ()})
259
+ var wg sync.WaitGroup
260
+ ch := make (chan reconcileResult )
157
261
158
- // reconcile network
159
- clusterScope .Info ("Reconciling network" )
160
- if requeue , err := clusterScope .ReconcileNetwork (); err != nil {
161
- clusterScope .Error (err , "failed to reconcile PowerVS network" )
162
- conditions .MarkFalse (powerVSCluster , infrav1beta2 .NetworkReadyCondition , infrav1beta2 .NetworkReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
163
- return reconcile.Result {}, err
164
- } else if requeue {
165
- clusterScope .Info ("PowerVS network creation is pending, requeuing" )
166
- return reconcile.Result {RequeueAfter : 1 * time .Minute }, nil
167
- }
168
- conditions .MarkTrue (powerVSCluster , infrav1beta2 .NetworkReadyCondition )
262
+ // reconcile PowerVS resources
263
+ wg .Add (1 )
264
+ go r .reconcilePowerVSResources (clusterScope , powerVSCluster , ch , & wg )
169
265
170
266
// reconcile VPC
171
- clusterScope .Info ("Reconciling VPC" )
172
- if requeue , err := clusterScope .ReconcileVPC (); err != nil {
173
- clusterScope .Error (err , "failed to reconcile VPC" )
174
- conditions .MarkFalse (powerVSCluster , infrav1beta2 .VPCReadyCondition , infrav1beta2 .VPCReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
175
- return reconcile.Result {}, err
176
- } else if requeue {
177
- clusterScope .Info ("VPC creation is pending, requeuing" )
178
- return reconcile.Result {RequeueAfter : 15 * time .Second }, nil
179
- }
180
- conditions .MarkTrue (powerVSCluster , infrav1beta2 .VPCReadyCondition )
267
+ wg .Add (1 )
268
+ go r .reconcileVPCResources (clusterScope , powerVSCluster , ch , & wg )
181
269
182
- // reconcile VPC Subnet
183
- clusterScope .Info ("Reconciling VPC subnets" )
184
- if requeue , err := clusterScope .ReconcileVPCSubnets (); err != nil {
185
- clusterScope .Error (err , "failed to reconcile VPC subnets" )
186
- conditions .MarkFalse (powerVSCluster , infrav1beta2 .VPCSubnetReadyCondition , infrav1beta2 .VPCSubnetReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
187
- return reconcile.Result {}, err
188
- } else if requeue {
189
- clusterScope .Info ("VPC subnet creation is pending, requeuing" )
190
- return reconcile.Result {RequeueAfter : 15 * time .Second }, nil
270
+ // wait for above reconcile to complete and close the channel
271
+ go func () {
272
+ wg .Wait ()
273
+ close (ch )
274
+ }()
275
+
276
+ var requeue bool
277
+ var errList []error
278
+ // receive return values from the channel and decide the requeue
279
+ for val := range ch {
280
+ if val .Requeue {
281
+ requeue = true
282
+ }
283
+ if val .error != nil {
284
+ errList = append (errList , val .error )
285
+ }
191
286
}
192
- conditions .MarkTrue (powerVSCluster , infrav1beta2 .VPCSubnetReadyCondition )
193
287
194
- // reconcile VPC security group
195
- clusterScope . Info ( "Reconciling VPC security group" )
196
- if err := clusterScope . ReconcileVPCSecurityGroups (); err != nil {
197
- clusterScope . Error ( err , "failed to reconcile VPC security groups" )
198
- conditions . MarkFalse ( powerVSCluster , infrav1beta2 . VPCSecurityGroupReadyCondition , infrav1beta2 . VPCSecurityGroupReconciliationFailedReason , capiv1beta1 . ConditionSeverityError , err . Error ())
199
- return reconcile .Result {}, err
288
+ if requeue && len ( errList ) > 1 {
289
+ return ctrl. Result { RequeueAfter : 30 * time . Second }, kerrors . NewAggregate ( errList )
290
+ } else if requeue {
291
+ return ctrl. Result { RequeueAfter : 30 * time . Second }, nil
292
+ } else if len ( errList ) > 1 {
293
+ return ctrl .Result {}, kerrors . NewAggregate ( errList )
200
294
}
201
- conditions .MarkTrue (powerVSCluster , infrav1beta2 .VPCSecurityGroupReadyCondition )
202
295
203
296
// reconcile Transit Gateway
204
297
clusterScope .Info ("Reconciling Transit Gateway" )
205
298
if requeue , err := clusterScope .ReconcileTransitGateway (); err != nil {
206
299
clusterScope .Error (err , "failed to reconcile transit gateway" )
207
- conditions .MarkFalse (powerVSCluster , infrav1beta2 .TransitGatewayReadyCondition , infrav1beta2 .TransitGatewayReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
300
+ conditions .MarkFalse (powerVSCluster . cluster , infrav1beta2 .TransitGatewayReadyCondition , infrav1beta2 .TransitGatewayReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
208
301
return reconcile.Result {}, err
209
302
} else if requeue {
210
303
clusterScope .Info ("Transit gateway creation is pending, requeuing" )
211
304
return reconcile.Result {RequeueAfter : 1 * time .Minute }, nil
212
305
}
213
- conditions .MarkTrue (powerVSCluster , infrav1beta2 .TransitGatewayReadyCondition )
214
-
215
- // reconcile LoadBalancer
216
- clusterScope .Info ("Reconciling VPC load balancers" )
217
- if requeue , err := clusterScope .ReconcileLoadBalancers (); err != nil {
218
- clusterScope .Error (err , "failed to reconcile VPC load balancers" )
219
- conditions .MarkFalse (powerVSCluster , infrav1beta2 .LoadBalancerReadyCondition , infrav1beta2 .LoadBalancerReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
220
- return reconcile.Result {}, err
221
- } else if requeue {
222
- clusterScope .Info ("VPC load balancer creation is pending, requeuing" )
223
- return reconcile.Result {RequeueAfter : 1 * time .Minute }, nil
224
- }
306
+ conditions .MarkTrue (powerVSCluster .cluster , infrav1beta2 .TransitGatewayReadyCondition )
225
307
226
308
// reconcile COSInstance
227
309
if clusterScope .IBMPowerVSCluster .Spec .Ignition != nil {
228
310
clusterScope .Info ("Reconciling COS service instance" )
229
311
if err := clusterScope .ReconcileCOSInstance (); err != nil {
230
- conditions .MarkFalse (powerVSCluster , infrav1beta2 .COSInstanceReadyCondition , infrav1beta2 .COSInstanceReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
312
+ conditions .MarkFalse (powerVSCluster . cluster , infrav1beta2 .COSInstanceReadyCondition , infrav1beta2 .COSInstanceReconciliationFailedReason , capiv1beta1 .ConditionSeverityError , err .Error ())
231
313
return reconcile.Result {}, err
232
314
}
233
- conditions .MarkTrue (powerVSCluster , infrav1beta2 .COSInstanceReadyCondition )
315
+ conditions .MarkTrue (powerVSCluster .cluster , infrav1beta2 .COSInstanceReadyCondition )
316
+ }
317
+
318
+ var networkReady , loadBalancerReady bool
319
+ for _ , cond := range clusterScope .IBMPowerVSCluster .Status .Conditions {
320
+ if cond .Type == infrav1beta2 .NetworkReadyCondition && cond .Status == corev1 .ConditionTrue {
321
+ networkReady = true
322
+ }
323
+ if cond .Type == infrav1beta2 .LoadBalancerReadyCondition && cond .Status == corev1 .ConditionTrue {
324
+ loadBalancerReady = true
325
+ }
326
+ }
327
+
328
+ if ! networkReady || ! loadBalancerReady {
329
+ clusterScope .Info ("Network or LoadBalancer still not ready, requeuing" )
330
+ return ctrl.Result {RequeueAfter : 30 * time .Second }, nil
234
331
}
235
332
236
333
// update cluster object with loadbalancer host
237
334
loadBalancer := clusterScope .PublicLoadBalancer ()
238
335
if loadBalancer == nil {
239
336
return reconcile.Result {}, fmt .Errorf ("failed to fetch public loadbalancer" )
240
337
}
241
- if clusterScope .GetLoadBalancerState (loadBalancer .Name ) == nil || * clusterScope .GetLoadBalancerState (loadBalancer .Name ) != infrav1beta2 .VPCLoadBalancerStateActive {
242
- clusterScope .Info ("LoadBalancer state is not active" )
243
- return reconcile.Result {RequeueAfter : time .Minute }, nil
244
- }
245
338
246
339
clusterScope .Info ("Getting load balancer host" )
247
340
hostName := clusterScope .GetLoadBalancerHostName (loadBalancer .Name )
248
341
if hostName == nil || * hostName == "" {
249
342
clusterScope .Info ("LoadBalancer hostname is not yet available, requeuing" )
250
343
return reconcile.Result {RequeueAfter : time .Minute }, nil
251
344
}
252
- conditions .MarkTrue (powerVSCluster , infrav1beta2 .LoadBalancerReadyCondition )
253
345
254
346
clusterScope .IBMPowerVSCluster .Spec .ControlPlaneEndpoint .Host = * clusterScope .GetLoadBalancerHostName (loadBalancer .Name )
255
347
clusterScope .IBMPowerVSCluster .Spec .ControlPlaneEndpoint .Port = clusterScope .APIServerPort ()
0 commit comments