Skip to content

Commit 6b68c43

Browse files
authored
add support for Lambda Managed Instance (LMI) functions for terraform (#8501)
1 parent b7c3692 commit 6b68c43

2 files changed

Lines changed: 155 additions & 0 deletions

File tree

samcli/hook_packages/terraform/hooks/prepare/property_builder.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,72 @@ def _check_image_config_value(image_config: Any) -> bool:
227227
return True
228228

229229

230+
def _build_capacity_provider_config_property(tf_properties: dict, resource: TFResource) -> Optional[dict]:
231+
"""
232+
Builds the CapacityProviderConfig property of a CloudFormation AWS Lambda Function out of the
233+
properties of the equivalent terraform resource
234+
235+
Terraform structure:
236+
capacity_provider_config {
237+
lambda_managed_instances_capacity_provider_config {
238+
capacity_provider_arn = "arn:..."
239+
per_execution_environment_max_concurrency = 10 # optional, default 4
240+
execution_environment_memory_gib_per_vcpu = 2.0 # optional
241+
}
242+
}
243+
244+
Maps to CloudFormation:
245+
CapacityProviderConfig:
246+
LambdaManagedInstancesCapacityProviderConfig:
247+
CapacityProviderArn: "arn:..."
248+
PerExecutionEnvironmentMaxConcurrency: 10
249+
ExecutionEnvironmentMemoryGiBPerVCpu: 2.0
250+
251+
Parameters
252+
----------
253+
tf_properties: dict
254+
Properties of the terraform AWS Lambda function resource
255+
resource: TFResource
256+
Configuration terraform resource
257+
258+
Returns
259+
-------
260+
Optional[dict]
261+
The built CapacityProviderConfig property of a CloudFormation AWS Lambda Function resource,
262+
or None if not present
263+
"""
264+
capacity_provider_config = tf_properties.get("capacity_provider_config")
265+
if not capacity_provider_config:
266+
return None
267+
268+
# Terraform stores blocks as lists, extract first item
269+
config_block = capacity_provider_config[0] if capacity_provider_config else None
270+
if not config_block:
271+
return None
272+
273+
lmi_config = config_block.get("lambda_managed_instances_capacity_provider_config")
274+
if not lmi_config:
275+
return None
276+
277+
# Extract nested config (also a list in Terraform JSON)
278+
lmi_block = lmi_config[0] if lmi_config else None
279+
if not lmi_block:
280+
return None
281+
282+
# Build CloudFormation structure with required field
283+
cfn_config = {"CapacityProviderArn": lmi_block.get("capacity_provider_arn", "")}
284+
285+
# Add optional fields using helper function
286+
def _add_optional_field(cfn_key: str, tf_key: str) -> None:
287+
if tf_key in lmi_block:
288+
cfn_config[cfn_key] = lmi_block[tf_key]
289+
290+
_add_optional_field("PerExecutionEnvironmentMaxConcurrency", "per_execution_environment_max_concurrency")
291+
_add_optional_field("ExecutionEnvironmentMemoryGiBPerVCpu", "execution_environment_memory_gib_per_vcpu")
292+
293+
return {"LambdaManagedInstancesCapacityProviderConfig": cfn_config}
294+
295+
230296
def _get_json_body(tf_properties: dict, resource: TFResource) -> Any:
231297
"""
232298
Gets the JSON formatted body value from the API Gateway if there is one
@@ -321,6 +387,7 @@ def _add_property(cfn_prop, tf_prop):
321387
"Timeout": _get_property_extractor("timeout"),
322388
"MemorySize": _get_property_extractor("memory_size"),
323389
"ImageConfig": _build_lambda_function_image_config_property,
390+
"CapacityProviderConfig": _build_capacity_provider_config_property,
324391
}
325392

326393
AWS_LAMBDA_LAYER_VERSION_PROPERTY_BUILDER_MAPPING: PropertyBuilderMapping = {

tests/unit/hook_packages/terraform/hooks/prepare/test_property_builder.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
_build_lambda_function_image_config_property,
1313
_check_image_config_value,
1414
_get_cors_v2_api,
15+
_build_capacity_provider_config_property,
1516
)
1617
from samcli.hook_packages.terraform.hooks.prepare.constants import REMOTE_DUMMY_VALUE
1718

@@ -248,3 +249,90 @@ def test_get_json_body_invalid(self, invalid_value):
248249
def test_get_cors_v2_api(self, tf_properties, expected):
249250
response = _get_cors_v2_api(tf_properties, Mock())
250251
self.assertEqual(response, expected)
252+
253+
@parameterized.expand(
254+
[
255+
(
256+
# Full capacity provider config with all optional fields
257+
{
258+
"capacity_provider_config": [
259+
{
260+
"lambda_managed_instances_capacity_provider_config": [
261+
{
262+
"capacity_provider_arn": "arn:aws:lambda:us-east-1:123456789012:capacity-provider/test",
263+
"per_execution_environment_max_concurrency": 10,
264+
"execution_environment_memory_gib_per_vcpu": 2.0,
265+
}
266+
]
267+
}
268+
]
269+
},
270+
{
271+
"LambdaManagedInstancesCapacityProviderConfig": {
272+
"CapacityProviderArn": "arn:aws:lambda:us-east-1:123456789012:capacity-provider/test",
273+
"PerExecutionEnvironmentMaxConcurrency": 10,
274+
"ExecutionEnvironmentMemoryGiBPerVCpu": 2.0,
275+
}
276+
},
277+
),
278+
(
279+
# Minimal capacity provider config with only required field
280+
{
281+
"capacity_provider_config": [
282+
{
283+
"lambda_managed_instances_capacity_provider_config": [
284+
{
285+
"capacity_provider_arn": "arn:aws:lambda:us-east-1:123456789012:capacity-provider/test",
286+
}
287+
]
288+
}
289+
]
290+
},
291+
{
292+
"LambdaManagedInstancesCapacityProviderConfig": {
293+
"CapacityProviderArn": "arn:aws:lambda:us-east-1:123456789012:capacity-provider/test",
294+
}
295+
},
296+
),
297+
(
298+
# Capacity provider config with only max concurrency
299+
{
300+
"capacity_provider_config": [
301+
{
302+
"lambda_managed_instances_capacity_provider_config": [
303+
{
304+
"capacity_provider_arn": "arn:aws:lambda:us-east-1:123456789012:capacity-provider/test",
305+
"per_execution_environment_max_concurrency": 100,
306+
}
307+
]
308+
}
309+
]
310+
},
311+
{
312+
"LambdaManagedInstancesCapacityProviderConfig": {
313+
"CapacityProviderArn": "arn:aws:lambda:us-east-1:123456789012:capacity-provider/test",
314+
"PerExecutionEnvironmentMaxConcurrency": 100,
315+
}
316+
},
317+
),
318+
# No capacity provider config
319+
({}, None),
320+
({"capacity_provider_config": None}, None),
321+
({"capacity_provider_config": []}, None),
322+
# Empty nested config
323+
({"capacity_provider_config": [{}]}, None),
324+
(
325+
{"capacity_provider_config": [{"lambda_managed_instances_capacity_provider_config": None}]},
326+
None,
327+
),
328+
(
329+
{"capacity_provider_config": [{"lambda_managed_instances_capacity_provider_config": []}]},
330+
None,
331+
),
332+
]
333+
)
334+
def test_build_capacity_provider_config_property(self, tf_properties, expected):
335+
"""Test building CapacityProviderConfig property from Terraform properties"""
336+
resource_mock = Mock()
337+
result = _build_capacity_provider_config_property(tf_properties, resource_mock)
338+
self.assertEqual(result, expected)

0 commit comments

Comments
 (0)