5
5
"fmt"
6
6
"time"
7
7
8
+ "github.com/aws/aws-sdk-go/aws"
9
+ "github.com/aws/aws-sdk-go/service/ec2"
8
10
"github.com/blang/semver"
9
11
"github.com/google/go-cmp/cmp"
10
12
cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"
@@ -15,6 +17,7 @@ import (
15
17
"k8s.io/apimachinery/pkg/runtime/schema"
16
18
"k8s.io/client-go/tools/record"
17
19
"k8s.io/klog/v2"
20
+ "k8s.io/utils/ptr"
18
21
ctrl "sigs.k8s.io/controller-runtime"
19
22
"sigs.k8s.io/controller-runtime/pkg/client"
20
23
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
@@ -130,6 +133,7 @@ func (r *ROSAMachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Requ
130
133
MachinePool : machinePool ,
131
134
RosaMachinePool : rosaMachinePool ,
132
135
Logger : log ,
136
+ Endpoints : r .Endpoints ,
133
137
})
134
138
if err != nil {
135
139
return ctrl.Result {}, errors .Wrap (err , "failed to create scope" )
@@ -198,6 +202,17 @@ func (r *ROSAMachinePoolReconciler) reconcileNormal(ctx context.Context,
198
202
}
199
203
200
204
rosaMachinePool := machinePoolScope .RosaMachinePool
205
+ machinePool := machinePoolScope .MachinePool
206
+
207
+ if rosaMachinePool .Spec .Autoscaling != nil && ! annotations .ReplicasManagedByExternalAutoscaler (machinePool ) {
208
+ // make sure cluster.x-k8s.io/replicas-managed-by annotation is set on CAPI MachinePool when autoscaling is enabled.
209
+ annotations .AddAnnotations (machinePool , map [string ]string {
210
+ clusterv1 .ReplicasManagedByAnnotation : "rosa" ,
211
+ })
212
+ if err := machinePoolScope .PatchCAPIMachinePoolObject (ctx ); err != nil {
213
+ return ctrl.Result {}, err
214
+ }
215
+ }
201
216
202
217
nodePool , found , err := ocmClient .GetNodePool (machinePoolScope .ControlPlane .Status .ID , rosaMachinePool .Spec .NodePoolName )
203
218
if err != nil {
@@ -210,9 +225,25 @@ func (r *ROSAMachinePoolReconciler) reconcileNormal(ctx context.Context,
210
225
return ctrl.Result {}, fmt .Errorf ("failed to ensure rosaMachinePool: %w" , err )
211
226
}
212
227
213
- // TODO (alberto): discover and store providerIDs from aws so the CAPI controller can match then to Nodes and report readiness.
214
- rosaMachinePool .Status .Replicas = int32 (nodePool .Status ().CurrentReplicas ())
215
- if nodePool .Replicas () == nodePool .Status ().CurrentReplicas () && nodePool .Status ().Message () == "" {
228
+ currentReplicas := int32 (nodePool .Status ().CurrentReplicas ())
229
+ if annotations .ReplicasManagedByExternalAutoscaler (machinePool ) {
230
+ // Set MachinePool replicas to rosa autoscaling replicas
231
+ if * machinePool .Spec .Replicas != currentReplicas {
232
+ machinePoolScope .Info ("Setting MachinePool replicas to rosa autoscaling replicas" ,
233
+ "local" , * machinePool .Spec .Replicas ,
234
+ "external" , currentReplicas )
235
+ machinePool .Spec .Replicas = & currentReplicas
236
+ if err := machinePoolScope .PatchCAPIMachinePoolObject (ctx ); err != nil {
237
+ return ctrl.Result {}, err
238
+ }
239
+ }
240
+ }
241
+ if err := r .reconcileProviderIDList (ctx , machinePoolScope , nodePool ); err != nil {
242
+ return ctrl.Result {}, fmt .Errorf ("failed to reconcile ProviderIDList: %w" , err )
243
+ }
244
+
245
+ rosaMachinePool .Status .Replicas = currentReplicas
246
+ if rosa .IsNodePoolReady (nodePool ) {
216
247
conditions .MarkTrue (rosaMachinePool , expinfrav1 .RosaMachinePoolReadyCondition )
217
248
rosaMachinePool .Status .Ready = true
218
249
@@ -234,7 +265,7 @@ func (r *ROSAMachinePoolReconciler) reconcileNormal(ctx context.Context,
234
265
return ctrl.Result {RequeueAfter : time .Second * 60 }, nil
235
266
}
236
267
237
- npBuilder := nodePoolBuilder (rosaMachinePool .Spec , machinePoolScope . MachinePool .Spec )
268
+ npBuilder := nodePoolBuilder (rosaMachinePool .Spec , machinePool .Spec )
238
269
nodePoolSpec , err := npBuilder .Build ()
239
270
if err != nil {
240
271
return ctrl.Result {}, fmt .Errorf ("failed to build rosa nodepool: %w" , err )
@@ -294,20 +325,7 @@ func (r *ROSAMachinePoolReconciler) reconcileMachinePoolVersion(machinePoolScope
294
325
}
295
326
296
327
if scheduledUpgrade == nil {
297
- policy , err := ocmClient .BuildNodeUpgradePolicy (version , nodePool .ID (), ocm.UpgradeScheduling {
298
- AutomaticUpgrades : false ,
299
- // The OCM API places guardrails around the minimum and maximum delay that a user can request,
300
- // for the next run of the upgrade, which is [5min,6mo]. Set our next run request to something
301
- // slightly longer than 5min to make sure we account for the latency between when we send this
302
- // request and when the server processes it.
303
- // https://gitlab.cee.redhat.com/service/uhc-clusters-service/-/blob/master/cmd/clusters-service/servecmd/apiserver/upgrade_policy_handlers.go
304
- NextRun : time .Now ().Add (6 * time .Minute ),
305
- })
306
- if err != nil {
307
- return fmt .Errorf ("failed to create nodePool upgrade schedule to version %s: %w" , version , err )
308
- }
309
-
310
- scheduledUpgrade , err = ocmClient .ScheduleNodePoolUpgrade (clusterID , nodePool .ID (), policy )
328
+ scheduledUpgrade , err = rosa .ScheduleNodePoolUpgrade (ocmClient , clusterID , nodePool , version , time .Now ())
311
329
if err != nil {
312
330
return fmt .Errorf ("failed to schedule nodePool upgrade to version %s: %w" , version , err )
313
331
}
@@ -453,6 +471,47 @@ func nodePoolToRosaMachinePoolSpec(nodePool *cmv1.NodePool) expinfrav1.RosaMachi
453
471
return spec
454
472
}
455
473
474
+ func (r * ROSAMachinePoolReconciler ) reconcileProviderIDList (ctx context.Context , machinePoolScope * scope.RosaMachinePoolScope , nodePool * cmv1.NodePool ) error {
475
+ tags := nodePool .AWSNodePool ().Tags ()
476
+ if len (tags ) == 0 {
477
+ // can't identify EC2 instances belonging to this NodePool without tags.
478
+ return nil
479
+ }
480
+
481
+ ec2Svc := scope .NewEC2Client (machinePoolScope , machinePoolScope , & machinePoolScope .Logger , machinePoolScope .InfraCluster ())
482
+ response , err := ec2Svc .DescribeInstancesWithContext (ctx , & ec2.DescribeInstancesInput {
483
+ Filters : buildEC2FiltersFromTags (tags ),
484
+ })
485
+ if err != nil {
486
+ return err
487
+ }
488
+
489
+ var providerIDList []string
490
+ for _ , reservation := range response .Reservations {
491
+ for _ , instance := range reservation .Instances {
492
+ providerID := scope .GenerateProviderID (* instance .Placement .AvailabilityZone , * instance .InstanceId )
493
+ providerIDList = append (providerIDList , providerID )
494
+ }
495
+ }
496
+
497
+ machinePoolScope .RosaMachinePool .Spec .ProviderIDList = providerIDList
498
+ return nil
499
+ }
500
+
501
+ func buildEC2FiltersFromTags (tags map [string ]string ) []* ec2.Filter {
502
+ filters := make ([]* ec2.Filter , len (tags ))
503
+ for key , value := range tags {
504
+ filters = append (filters , & ec2.Filter {
505
+ Name : ptr .To (fmt .Sprintf ("tag:%s" , key )),
506
+ Values : aws .StringSlice ([]string {
507
+ value ,
508
+ }),
509
+ })
510
+ }
511
+
512
+ return filters
513
+ }
514
+
456
515
func rosaControlPlaneToRosaMachinePoolMapFunc (c client.Client , gvk schema.GroupVersionKind , log logger.Wrapper ) handler.MapFunc {
457
516
return func (ctx context.Context , o client.Object ) []reconcile.Request {
458
517
rosaControlPlane , ok := o .(* rosacontrolplanev1.ROSAControlPlane )
0 commit comments