@@ -16,15 +16,18 @@ class TestFunctionWithCapacityProvider(BaseTest):
1616 def companion_stack_outputs (self , get_companion_stack_outputs ):
1717 self .companion_stack_outputs = get_companion_stack_outputs
1818
19- def test_function_with_capacity_provider_custom_role (self ):
20- """Test Lambda function with CapacityProviderConfig using custom operator role."""
21- # Phase 1: Prepare parameters from companion stack
22- parameters = [
19+ def generate_lmi_parameters (self ):
20+ return [
2321 self .generate_parameter ("SubnetId" , self .companion_stack_outputs ["LMISubnetId" ]),
2422 self .generate_parameter ("SecurityGroup" , self .companion_stack_outputs ["LMISecurityGroupId" ]),
2523 self .generate_parameter ("KMSKeyArn" , self .companion_stack_outputs ["LMIKMSKeyArn" ]),
2624 ]
2725
26+ def test_function_with_capacity_provider_custom_role (self ):
27+ """Test Lambda function with CapacityProviderConfig using custom operator role."""
28+ # Phase 1: Prepare parameters from companion stack
29+ parameters = self .generate_lmi_parameters ()
30+
2831 # Phase 2: Deploy and verify against expected JSON
2932 self .create_and_verify_stack ("combination/function_lmi_custom" , parameters )
3033
@@ -35,17 +38,61 @@ def test_function_with_capacity_provider_custom_role(self):
3538 capacity_provider_resources = self .get_stack_resources ("AWS::Lambda::CapacityProvider" )
3639 self .assertEqual (len (capacity_provider_resources ), 1 , "Should create exactly one CapacityProvider" )
3740
38- iam_role_resources = self .get_stack_resources ("AWS::IAM::Role" )
39- self .assertEqual (len (iam_role_resources ), 2 , "Should create exactly two IAM roles" )
41+ # Phase 4: Validate function capacity provider configuration
42+ lambda_function = lambda_resources [0 ]
43+ capacity_provider = capacity_provider_resources [0 ]
44+
45+ function_capacity_provider_config = self .get_function_capacity_provider_config (
46+ lambda_function ["PhysicalResourceId" ]
47+ )
48+ self .assertIsNotNone (function_capacity_provider_config , "Function should have capacity provider configuration" )
49+ self .assertIn (
50+ "LambdaManagedInstancesCapacityProviderConfig" ,
51+ function_capacity_provider_config ,
52+ "Function should have LambdaManagedInstancesCapacityProviderConfig" ,
53+ )
54+
55+ lmi_config = function_capacity_provider_config ["LambdaManagedInstancesCapacityProviderConfig" ]
56+ function_capacity_provider_arn = lmi_config .get ("CapacityProviderArn" )
57+ self .assertIsNotNone (function_capacity_provider_arn , "Function should reference a capacity provider ARN" )
58+
59+ # Phase 5: Validate capacity provider details
60+ capacity_provider_config = self .get_lambda_capacity_provider_config (capacity_provider )
61+ self .assertIsNotNone (capacity_provider_config , "Capacity provider should have configuration" )
62+ self .assertEqual (capacity_provider_config ["State" ], "Active" , "Capacity provider should be in Active state" )
63+
64+ # Verify the function uses the correct capacity provider ARN
65+ actual_capacity_provider_arn = capacity_provider_config .get ("CapacityProviderArn" )
66+ self .assertEqual (
67+ function_capacity_provider_arn ,
68+ actual_capacity_provider_arn ,
69+ "Function should reference the correct capacity provider ARN" ,
70+ )
71+
72+ # Phase 6: Verify capacity provider uses custom operator role
73+ permissions_config = capacity_provider_config .get ("PermissionsConfig" )
74+ self .assertIsNotNone (permissions_config , "Capacity provider should have permissions configuration" )
75+
76+ capacity_provider_operator_role_arn = permissions_config .get ("CapacityProviderOperatorRoleArn" )
77+ self .assertIsNotNone (
78+ capacity_provider_operator_role_arn , "Capacity provider should have custom operator role ARN"
79+ )
80+
81+ # Get the physical ID of the custom role using its logical ID
82+ custom_operator_role_physical_id = self .get_physical_id_by_logical_id ("MyCapacityProviderCustomRole" )
83+ self .assertIsNotNone (custom_operator_role_physical_id , "MyCapacityProviderCustomRole should exist in the stack" )
84+
85+ # Verify the capacity provider uses the custom role by comparing physical IDs
86+ self .assertIn (
87+ custom_operator_role_physical_id ,
88+ capacity_provider_operator_role_arn ,
89+ f"Capacity provider should use the custom operator role with physical ID { custom_operator_role_physical_id } " ,
90+ )
4091
4192 def test_function_with_capacity_provider_default_role (self ):
4293 """Test Lambda function with CapacityProviderConfig using default operator role."""
4394 # Phase 1: Prepare parameters from companion stack
44- parameters = [
45- self .generate_parameter ("SubnetId" , self .companion_stack_outputs ["LMISubnetId" ]),
46- self .generate_parameter ("SecurityGroup" , self .companion_stack_outputs ["LMISecurityGroupId" ]),
47- self .generate_parameter ("KMSKeyArn" , self .companion_stack_outputs ["LMIKMSKeyArn" ]),
48- ]
95+ parameters = self .generate_lmi_parameters ()
4996
5097 # Phase 2: Deploy and verify against expected JSON
5198 self .create_and_verify_stack ("combination/function_lmi_default" , parameters )
@@ -57,5 +104,150 @@ def test_function_with_capacity_provider_default_role(self):
57104 capacity_provider_resources = self .get_stack_resources ("AWS::Lambda::CapacityProvider" )
58105 self .assertEqual (len (capacity_provider_resources ), 2 , "Should create exactly two CapacityProviders" )
59106
60- iam_role_resources = self .get_stack_resources ("AWS::IAM::Role" )
61- self .assertEqual (len (iam_role_resources ), 3 , "Should create exactly three IAM roles" )
107+ # Phase 4: Validate function capacity provider configuration
108+ simple_capacity_provider = next (
109+ r for r in capacity_provider_resources if r ["LogicalResourceId" ] == "SimpleCapacityProvider"
110+ )
111+ advanced_capacity_provider = next (
112+ r for r in capacity_provider_resources if r ["LogicalResourceId" ] == "AdvancedCapacityProvider"
113+ )
114+ my_function = lambda_resources [0 ]
115+
116+ function_config = self .get_function_capacity_provider_config (my_function ["PhysicalResourceId" ])
117+ self .assertIsNotNone (function_config , "Function should have capacity provider configuration" )
118+ self .assertIn (
119+ "LambdaManagedInstancesCapacityProviderConfig" ,
120+ function_config ,
121+ "Function should have LambdaManagedInstancesCapacityProviderConfig" ,
122+ )
123+
124+ lmi_config = function_config ["LambdaManagedInstancesCapacityProviderConfig" ]
125+ function_capacity_provider_arn = lmi_config .get ("CapacityProviderArn" )
126+ self .assertIsNotNone (function_capacity_provider_arn , "Function should reference a capacity provider ARN" )
127+
128+ # Phase 5: Validate SimpleCapacityProvider configuration
129+ simple_cp_config = self .get_lambda_capacity_provider_config (simple_capacity_provider )
130+ self .assertIsNotNone (simple_cp_config , "SimpleCapacityProvider should have configuration" )
131+ self .assertEqual (simple_cp_config ["State" ], "Active" , "SimpleCapacityProvider should be in Active state" )
132+
133+ # Verify the function uses SimpleCapacityProvider
134+ simple_cp_arn = simple_cp_config .get ("CapacityProviderArn" )
135+ self .assertEqual (
136+ function_capacity_provider_arn , simple_cp_arn , "Function should reference SimpleCapacityProvider ARN"
137+ )
138+
139+ # Verify SimpleCapacityProvider VPC configuration
140+ simple_vpc_config = simple_cp_config .get ("VpcConfig" )
141+ self .assertIsNotNone (simple_vpc_config , "SimpleCapacityProvider should have VPC configuration" )
142+ self .assertIn (
143+ self .companion_stack_outputs ["LMISubnetId" ],
144+ simple_vpc_config ["SubnetIds" ],
145+ "SimpleCapacityProvider should use the correct subnet" ,
146+ )
147+ self .assertIn (
148+ self .companion_stack_outputs ["LMISecurityGroupId" ],
149+ simple_vpc_config ["SecurityGroupIds" ],
150+ "SimpleCapacityProvider should use the correct security group" ,
151+ )
152+
153+ # Verify SimpleCapacityProvider uses SAM-generated default operator role
154+ simple_permissions_config = simple_cp_config .get ("PermissionsConfig" )
155+ self .assertIsNotNone (simple_permissions_config , "SimpleCapacityProvider should have permissions configuration" )
156+ simple_operator_arn = simple_permissions_config .get ("CapacityProviderOperatorRoleArn" )
157+ self .assertIsNotNone (simple_operator_arn , "SimpleCapacityProvider should have operator role ARN" )
158+
159+ # Phase 6: Validate AdvancedCapacityProvider configuration
160+ advanced_cp_config = self .get_lambda_capacity_provider_config (advanced_capacity_provider )
161+ self .assertIsNotNone (advanced_cp_config , "AdvancedCapacityProvider should have configuration" )
162+ self .assertEqual (advanced_cp_config ["State" ], "Active" , "AdvancedCapacityProvider should be in Active state" )
163+
164+ # Verify AdvancedCapacityProvider VPC configuration
165+ advanced_vpc_config = advanced_cp_config .get ("VpcConfig" )
166+ self .assertIsNotNone (advanced_vpc_config , "AdvancedCapacityProvider should have VPC configuration" )
167+ self .assertIn (
168+ self .companion_stack_outputs ["LMISubnetId" ],
169+ advanced_vpc_config ["SubnetIds" ],
170+ "AdvancedCapacityProvider should use the correct subnet" ,
171+ )
172+ self .assertIn (
173+ self .companion_stack_outputs ["LMISecurityGroupId" ],
174+ advanced_vpc_config ["SecurityGroupIds" ],
175+ "AdvancedCapacityProvider should use the correct security group" ,
176+ )
177+
178+ # Verify AdvancedCapacityProvider permissions configuration
179+ advanced_permissions_config = advanced_cp_config .get ("PermissionsConfig" )
180+ self .assertIsNotNone (
181+ advanced_permissions_config ,
182+ "AdvancedCapacityProvider should have permissions configuration" ,
183+ )
184+
185+ # Verify AdvancedCapacityProvider instance requirements
186+ instance_requirements = advanced_cp_config .get ("InstanceRequirements" )
187+ self .assertIsNotNone (instance_requirements , "AdvancedCapacityProvider should have instance requirements" )
188+ self .assertIn (
189+ "x86_64" ,
190+ instance_requirements .get ("Architectures" , []),
191+ "AdvancedCapacityProvider should have x86_64 architecture" ,
192+ )
193+ allowed_types = instance_requirements .get ("AllowedInstanceTypes" , [])
194+ self .assertIn ("m5.large" , allowed_types , "AdvancedCapacityProvider should allow m5.large" )
195+ self .assertIn ("m5.xlarge" , allowed_types , "AdvancedCapacityProvider should allow m5.xlarge" )
196+ self .assertIn ("m5.2xlarge" , allowed_types , "AdvancedCapacityProvider should allow m5.2xlarge" )
197+
198+ # Verify AdvancedCapacityProvider scaling configuration
199+ scaling_config = advanced_cp_config .get ("CapacityProviderScalingConfig" )
200+ self .assertIsNotNone (scaling_config , "AdvancedCapacityProvider should have scaling configuration" )
201+ self .assertEqual (
202+ scaling_config .get ("MaxVCpuCount" ), 64 , "AdvancedCapacityProvider should have MaxVCpuCount of 64"
203+ )
204+ scaling_policies = scaling_config .get ("ScalingPolicies" , [])
205+ self .assertTrue (len (scaling_policies ) > 0 , "AdvancedCapacityProvider should have scaling policies" )
206+ cpu_policy = next (
207+ (
208+ p
209+ for p in scaling_policies
210+ if p .get ("PredefinedMetricType" ) == "LambdaCapacityProviderAverageCPUUtilization"
211+ ),
212+ None ,
213+ )
214+ self .assertIsNotNone (cpu_policy , "AdvancedCapacityProvider should have CPU utilization scaling policy" )
215+ self .assertEqual (
216+ cpu_policy .get ("TargetValue" ), 70.0 , "AdvancedCapacityProvider should have CPU utilization target of 70"
217+ )
218+
219+ # Verify AdvancedCapacityProvider KMS key
220+ self .assertEqual (
221+ advanced_cp_config .get ("KmsKeyArn" ),
222+ self .companion_stack_outputs ["LMIKMSKeyArn" ],
223+ "AdvancedCapacityProvider should use the correct KMS key" ,
224+ )
225+
226+ def get_function_capacity_provider_config (self , function_name , alias_name = None ):
227+ lambda_client = self .client_provider .lambda_client
228+
229+ try :
230+ # Build the function identifier - include alias if provided
231+ function_identifier = f"{ function_name } :{ alias_name } " if alias_name else function_name
232+ # Get the function configuration
233+ response = lambda_client .get_function_configuration (FunctionName = function_identifier )
234+ # Return the CapacityProviderConfig if it exists
235+ return response .get ("CapacityProviderConfig" )
236+ except Exception as e :
237+ # Log the error and return None for graceful handling
238+ print (f"Error getting function capacity provider config: { e } " )
239+ return None
240+
241+ def get_lambda_capacity_provider_config (self , capacity_provider_resource ):
242+ lambda_client = self .client_provider .lambda_client
243+
244+ try :
245+ # Extract capacity provider name from resource dict
246+ capacity_provider_name = capacity_provider_resource ["PhysicalResourceId" ]
247+ # Get the capacity provider configuration
248+ response = lambda_client .get_capacity_provider (CapacityProviderName = capacity_provider_name )
249+ return response .get ("CapacityProvider" )
250+ except Exception as e :
251+ # Log the error and return None for graceful handling
252+ print (f"Error getting capacity provider config for { capacity_provider_resource } : { e } " )
253+ return None
0 commit comments