55	"fmt" 
66	"time" 
77
8+ 	"github.com/aws/aws-sdk-go/aws" 
9+ 	"github.com/aws/aws-sdk-go/service/ec2" 
810	"github.com/blang/semver" 
911	"github.com/google/go-cmp/cmp" 
1012	cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" 
@@ -15,6 +17,7 @@ import (
1517	"k8s.io/apimachinery/pkg/runtime/schema" 
1618	"k8s.io/client-go/tools/record" 
1719	"k8s.io/klog/v2" 
20+ 	"k8s.io/utils/ptr" 
1821	ctrl "sigs.k8s.io/controller-runtime" 
1922	"sigs.k8s.io/controller-runtime/pkg/client" 
2023	"sigs.k8s.io/controller-runtime/pkg/client/apiutil" 
@@ -130,6 +133,7 @@ func (r *ROSAMachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Requ
130133		MachinePool :     machinePool ,
131134		RosaMachinePool : rosaMachinePool ,
132135		Logger :          log ,
136+ 		Endpoints :       r .Endpoints ,
133137	})
134138	if  err  !=  nil  {
135139		return  ctrl.Result {}, errors .Wrap (err , "failed to create scope" )
@@ -198,6 +202,17 @@ func (r *ROSAMachinePoolReconciler) reconcileNormal(ctx context.Context,
198202	}
199203
200204	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+ 	}
201216
202217	nodePool , found , err  :=  ocmClient .GetNodePool (machinePoolScope .ControlPlane .Status .ID , rosaMachinePool .Spec .NodePoolName )
203218	if  err  !=  nil  {
@@ -210,9 +225,25 @@ func (r *ROSAMachinePoolReconciler) reconcileNormal(ctx context.Context,
210225			return  ctrl.Result {}, fmt .Errorf ("failed to ensure rosaMachinePool: %w" , err )
211226		}
212227
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 ) {
216247			conditions .MarkTrue (rosaMachinePool , expinfrav1 .RosaMachinePoolReadyCondition )
217248			rosaMachinePool .Status .Ready  =  true 
218249
@@ -234,7 +265,7 @@ func (r *ROSAMachinePoolReconciler) reconcileNormal(ctx context.Context,
234265		return  ctrl.Result {RequeueAfter : time .Second  *  60 }, nil 
235266	}
236267
237- 	npBuilder  :=  nodePoolBuilder (rosaMachinePool .Spec , machinePoolScope . MachinePool .Spec )
268+ 	npBuilder  :=  nodePoolBuilder (rosaMachinePool .Spec , machinePool .Spec )
238269	nodePoolSpec , err  :=  npBuilder .Build ()
239270	if  err  !=  nil  {
240271		return  ctrl.Result {}, fmt .Errorf ("failed to build rosa nodepool: %w" , err )
@@ -294,20 +325,7 @@ func (r *ROSAMachinePoolReconciler) reconcileMachinePoolVersion(machinePoolScope
294325	}
295326
296327	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 ())
311329		if  err  !=  nil  {
312330			return  fmt .Errorf ("failed to schedule nodePool upgrade to version %s: %w" , version , err )
313331		}
@@ -453,6 +471,47 @@ func nodePoolToRosaMachinePoolSpec(nodePool *cmv1.NodePool) expinfrav1.RosaMachi
453471	return  spec 
454472}
455473
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+ 
456515func  rosaControlPlaneToRosaMachinePoolMapFunc (c  client.Client , gvk  schema.GroupVersionKind , log  logger.Wrapper ) handler.MapFunc  {
457516	return  func (ctx  context.Context , o  client.Object ) []reconcile.Request  {
458517		rosaControlPlane , ok  :=  o .(* rosacontrolplanev1.ROSAControlPlane )
0 commit comments