@@ -18,6 +18,7 @@ package controllers
1818
1919import  (
2020	"context" 
21+ 	"strings" 
2122
2223	"github.com/aws/aws-sdk-go-v2/service/ec2" 
2324	ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" 
@@ -67,8 +68,8 @@ func (r *AWSMachineTemplateReconciler) Reconcile(ctx context.Context, req ctrl.R
6768		return  ctrl.Result {}, err 
6869	}
6970
70- 	// Skip if capacity is  already set 
71- 	if  len (awsMachineTemplate .Status .Capacity ) >  0  {
71+ 	// Skip if capacity and nodeInfo are  already set 
72+ 	if  len (awsMachineTemplate .Status .Capacity ) >  0  &&   awsMachineTemplate . Status . NodeInfo   !=   nil   {
7273		return  ctrl.Result {}, nil 
7374	}
7475
@@ -98,21 +99,22 @@ func (r *AWSMachineTemplateReconciler) Reconcile(ctx context.Context, req ctrl.R
9899		return  ctrl.Result {}, nil 
99100	}
100101
101- 	// Query instance type capacity 
102- 	capacity , err  :=  r .getInstanceTypeCapacity (ctx , globalScope , instanceType )
102+ 	// Query instance type capacity and node info  
103+ 	capacity , nodeInfo ,  err  :=  r .getInstanceTypeInfo (ctx , globalScope ,  awsMachineTemplate , instanceType )
103104	if  err  !=  nil  {
104105		record .Warnf (awsMachineTemplate , "CapacityQueryFailed" , "Failed to query capacity for instance type %q: %v" , instanceType , err )
105106		return  ctrl.Result {}, nil 
106107	}
107108
108- 	// Update status with capacity 
109+ 	// Update status with capacity and nodeInfo  
109110	awsMachineTemplate .Status .Capacity  =  capacity 
111+ 	awsMachineTemplate .Status .NodeInfo  =  nodeInfo 
110112
111113	if  err  :=  r .Status ().Update (ctx , awsMachineTemplate ); err  !=  nil  {
112114		return  ctrl.Result {}, errors .Wrap (err , "failed to update AWSMachineTemplate status" )
113115	}
114116
115- 	log .Info ("Successfully populated capacity information " , "instanceType" , instanceType , "region" , region , "capacity" , capacity )
117+ 	log .Info ("Successfully populated capacity and nodeInfo " , "instanceType" , instanceType , "region" , region , "capacity" , capacity ,  "nodeInfo" ,  nodeInfo )
116118	return  ctrl.Result {}, nil 
117119}
118120
@@ -145,8 +147,8 @@ func (r *AWSMachineTemplateReconciler) getRegion(ctx context.Context, template *
145147	return  "" , nil 
146148}
147149
148- // getInstanceTypeCapacity  queries AWS EC2 API for instance type capacity. 
149- func  (r  * AWSMachineTemplateReconciler ) getInstanceTypeCapacity (ctx  context.Context , globalScope  * scope.GlobalScope , instanceType  string ) (corev1.ResourceList , error ) {
150+ // getInstanceTypeInfo  queries AWS EC2 API for instance type capacity and node info . 
151+ func  (r  * AWSMachineTemplateReconciler ) getInstanceTypeInfo (ctx  context.Context , globalScope  * scope.GlobalScope , template   * infrav1. AWSMachineTemplate ,  instanceType  string ) (corev1.ResourceList ,  * infrav1. NodeInfo , error ) {
150152	// Create EC2 client from global scope 
151153	ec2Client  :=  ec2 .NewFromConfig (globalScope .Session ())
152154
@@ -157,11 +159,11 @@ func (r *AWSMachineTemplateReconciler) getInstanceTypeCapacity(ctx context.Conte
157159
158160	result , err  :=  ec2Client .DescribeInstanceTypes (ctx , input )
159161	if  err  !=  nil  {
160- 		return  nil , errors .Wrapf (err , "failed to describe instance type %q" , instanceType )
162+ 		return  nil , nil ,  errors .Wrapf (err , "failed to describe instance type %q" , instanceType )
161163	}
162164
163165	if  len (result .InstanceTypes ) ==  0  {
164- 		return  nil , errors .Errorf ("no information found for instance type %q" , instanceType )
166+ 		return  nil , nil ,  errors .Errorf ("no information found for instance type %q" , instanceType )
165167	}
166168
167169	// Extract capacity information 
@@ -178,7 +180,71 @@ func (r *AWSMachineTemplateReconciler) getInstanceTypeCapacity(ctx context.Conte
178180		memoryBytes  :=  * info .MemoryInfo .SizeInMiB  *  1024  *  1024 
179181		resourceList [corev1 .ResourceMemory ] =  * resource .NewQuantity (memoryBytes , resource .BinarySI )
180182	}
181- 	return  resourceList , nil 
183+ 
184+ 	// Extract node info from AMI if available 
185+ 	nodeInfo  :=  & infrav1.NodeInfo {}
186+ 	amiID  :=  template .Spec .Template .Spec .AMI .ID 
187+ 	if  amiID  !=  nil  &&  * amiID  !=  ""  {
188+ 		arch , os , err  :=  r .getNodeInfoFromAMI (ctx , ec2Client , * amiID )
189+ 		if  err  ==  nil  {
190+ 			if  arch  !=  ""  {
191+ 				nodeInfo .Architecture  =  arch 
192+ 			}
193+ 			if  os  !=  ""  {
194+ 				nodeInfo .OperatingSystem  =  os 
195+ 			}
196+ 		}
197+ 	}
198+ 
199+ 	return  resourceList , nodeInfo , nil 
200+ }
201+ 
202+ // getNodeInfoFromAMI queries the AMI to determine architecture and operating system. 
203+ func  (r  * AWSMachineTemplateReconciler ) getNodeInfoFromAMI (ctx  context.Context , ec2Client  * ec2.Client , amiID  string ) (infrav1.Architecture , string , error ) {
204+ 	input  :=  & ec2.DescribeImagesInput {
205+ 		ImageIds : []string {amiID },
206+ 	}
207+ 
208+ 	result , err  :=  ec2Client .DescribeImages (ctx , input )
209+ 	if  err  !=  nil  {
210+ 		return  "" , "" , errors .Wrapf (err , "failed to describe AMI %q" , amiID )
211+ 	}
212+ 
213+ 	if  len (result .Images ) ==  0  {
214+ 		return  "" , "" , errors .Errorf ("no information found for AMI %q" , amiID )
215+ 	}
216+ 
217+ 	image  :=  result .Images [0 ]
218+ 
219+ 	// Get architecture from AMI 
220+ 	var  arch  infrav1.Architecture 
221+ 	switch  image .Architecture  {
222+ 	case  ec2types .ArchitectureValuesX8664 :
223+ 		arch  =  infrav1 .ArchitectureAmd64 
224+ 	case  ec2types .ArchitectureValuesArm64 :
225+ 		arch  =  infrav1 .ArchitectureArm64 
226+ 	}
227+ 
228+ 	// Determine OS - check Platform field first (specifically for Windows identification) 
229+ 	var  os  string 
230+ 
231+ 	// 1. Check Platform field (most reliable for Windows detection) 
232+ 	if  image .Platform  ==  ec2types .PlatformValuesWindows  {
233+ 		os  =  "windows" 
234+ 	}
235+ 
236+ 	// 2. Check PlatformDetails field (provides more detailed information) 
237+ 	if  os  ==  ""  &&  image .PlatformDetails  !=  nil  {
238+ 		platformDetails  :=  strings .ToLower (* image .PlatformDetails )
239+ 		switch  {
240+ 		case  strings .Contains (platformDetails , "windows" ):
241+ 			os  =  "windows" 
242+ 		case  strings .Contains (platformDetails , "linux" ), strings .Contains (platformDetails , "unix" ):
243+ 			os  =  "linux" 
244+ 		}
245+ 	}
246+ 
247+ 	return  arch , os , nil 
182248}
183249
184250// SetupWithManager sets up the controller with the Manager. 
0 commit comments