Skip to content

Commit 4faeba3

Browse files
authored
Merge branch 'develop' into develop
2 parents b935852 + 04ab7c8 commit 4faeba3

File tree

100 files changed

+1058309
-316938
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

100 files changed

+1058309
-316938
lines changed

.cfnlintrc.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ ignore_templates:
107107
- tests/translator/output/**/function_with_snapstart.json # Snapstart intentionally not attached to a lambda version which causes lint issues
108108
- tests/translator/output/**/managed_policies_everything.json # intentionally contains wrong arns
109109
- tests/translator/output/**/function_with_metrics_config.json
110+
- tests/translator/output/**/function_with_self_managed_kafka_and_schema_registry.json # cfnlint is not updated to recognize the SchemaRegistryConfig property
111+
- tests/translator/output/**/function_with_msk_with_schema_registry_config.json # cfnlint is not updated to recognize the SchemaRegistryConfig property
112+
- tests/translator/output/**/function_with_logging_config.json # cfnlint is not updated to recognize the LoggingConfig property
110113
- tests/translator/output/aws-*/*capacity_provider*.json # Ignore Capacity Provider test format in non-aws partitions
111114

112115
ignore_checks:

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ fetch-schema-data:
6868
# aws-cdk updated where they store the cfn doc json files. See https://github.com/aws/aws-cdk/blob/main/packages/%40aws-cdk/cfnspec/README.md
6969
bin/git_lfs_download.sh "https://raw.githubusercontent.com/cdklabs/awscdk-service-spec/main/sources/CloudFormationDocumentation/CloudFormationDocumentation.json"
7070

71-
curl -o .tmp/cloudformation.schema.json https://raw.githubusercontent.com/awslabs/goformation/master/schema/cloudformation.schema.json
71+
# Generate fresh CloudFormation schema using Python generator
72+
python3 schema_source/cfn_schema_generator.py
7273

7374
update-schema-data:
7475
# Parse docs

docs/globals.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ Currently, the following resources and properties are being supported:
9898
TracingEnabled:
9999
OpenApiVersion:
100100
Domain:
101+
SecurityPolicy:
101102
102103
HttpApi:
103104
# Properties of AWS::Serverless::HttpApi

integration/combination/test_custom_http_api_domains_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from unittest.case import skipIf
22

33
from integration.config.service_names import CUSTOM_DOMAIN
4-
from integration.helpers.base_internal_test import BaseInternalTest
4+
from integration.helpers.base_internal_test import CUSTOM_DOMAIN_TOP_LEVEL, BaseInternalTest
55
from integration.helpers.base_test import nonblocking
66
from integration.helpers.resource import current_region_not_included
77

@@ -26,7 +26,7 @@ def test_custom_http_api_domains_regional(self):
2626
if "FeatureToggle" in self.pipeline_prefix:
2727
self.assertEqual("httpapi.ftl.sam-gamma-regional.com", result["DomainName"])
2828
else:
29-
self.assertEqual("httpapi.sam-gamma-regional.com", result["DomainName"])
29+
self.assertEqual(f"httpapi-sam-gamma-regional.{CUSTOM_DOMAIN_TOP_LEVEL}", result["DomainName"])
3030

3131
mtls_auth_config = result["MutualTlsAuthentication"]
3232
self.assertEqual(self.file_to_s3_uri_map["MTLSCert.pem"]["uri"], mtls_auth_config["TruststoreUri"])

integration/combination/test_custom_rest_api_domains.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from unittest.case import skipIf
22

33
from integration.config.service_names import CUSTOM_DOMAIN
4-
from integration.helpers.base_internal_test import BaseInternalTest
4+
from integration.helpers.base_internal_test import CUSTOM_DOMAIN_TOP_LEVEL, BaseInternalTest
55
from integration.helpers.base_test import nonblocking
66
from integration.helpers.resource import current_region_not_included
77

@@ -25,7 +25,7 @@ def test_custom_rest_api_domains_edge(self):
2525
if "FeatureToggle" in self.pipeline_prefix:
2626
self.assertEqual("ftl.sam-gamma-edge.com", result["domainName"])
2727
else:
28-
self.assertEqual("sam-gamma-edge.com", result["domainName"])
28+
self.assertEqual(f"sam-gamma-edge.{CUSTOM_DOMAIN_TOP_LEVEL}", result["domainName"])
2929

3030
end_point_config = result["endpointConfiguration"]
3131
end_point_types = end_point_config["types"]
@@ -46,7 +46,7 @@ def test_custom_rest_api_domains_regional(self):
4646
if "FeatureToggle" in self.pipeline_prefix:
4747
self.assertEqual("ftl.sam-gamma-regional.com", result["domainName"])
4848
else:
49-
self.assertEqual("sam-gamma-regional.com", result["domainName"])
49+
self.assertEqual(f"sam-gamma-regional.{CUSTOM_DOMAIN_TOP_LEVEL}", result["domainName"])
5050

5151
self.assertEqual("TLS_1_2", result["securityPolicy"])
5252

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
from unittest.case import skipIf
2+
3+
import pytest
4+
5+
from integration.config.service_names import LAMBDA_MANAGED_INSTANCES
6+
from integration.helpers.base_test import BaseTest
7+
from integration.helpers.resource import current_region_does_not_support
8+
9+
10+
@skipIf(
11+
current_region_does_not_support([LAMBDA_MANAGED_INSTANCES]),
12+
"LambdaManagedInstance is not supported in this testing region",
13+
)
14+
class TestFunctionWithCapacityProvider(BaseTest):
15+
@pytest.fixture(autouse=True)
16+
def companion_stack_outputs(self, get_companion_stack_outputs):
17+
self.companion_stack_outputs = get_companion_stack_outputs
18+
19+
def generate_lmi_parameters(self):
20+
return [
21+
self.generate_parameter("SubnetId", self.companion_stack_outputs["LMISubnetId"]),
22+
self.generate_parameter("SecurityGroup", self.companion_stack_outputs["LMISecurityGroupId"]),
23+
self.generate_parameter("KMSKeyArn", self.companion_stack_outputs["LMIKMSKeyArn"]),
24+
]
25+
26+
def verify_capacity_provider_basic_config(self, cp_config, cp_name):
27+
"""Verify basic capacity provider configuration (state, existence) and return ARN."""
28+
self.assertIsNotNone(cp_config, f"{cp_name} should have configuration")
29+
self.assertEqual(cp_config["State"], "Active", f"{cp_name} should be in Active state")
30+
capacity_provider_arn = cp_config.get("CapacityProviderArn")
31+
self.assertIsNotNone(capacity_provider_arn, f"{cp_name} should have a capacity provider ARN")
32+
return capacity_provider_arn
33+
34+
def verify_capacity_provider_vpc_config(self, cp_config, cp_name):
35+
"""Verify capacity provider VPC configuration matches companion stack outputs."""
36+
vpc_config = cp_config.get("VpcConfig")
37+
self.assertIsNotNone(vpc_config, f"{cp_name} should have VPC configuration")
38+
self.assertIn(
39+
self.companion_stack_outputs["LMISubnetId"],
40+
vpc_config["SubnetIds"],
41+
f"{cp_name} should use the correct subnet",
42+
)
43+
self.assertIn(
44+
self.companion_stack_outputs["LMISecurityGroupId"],
45+
vpc_config["SecurityGroupIds"],
46+
f"{cp_name} should use the correct security group",
47+
)
48+
49+
def verify_capacity_provider_permissions_config(self, cp_config, cp_name):
50+
"""Verify capacity provider has permissions configuration with operator role."""
51+
permissions_config = cp_config.get("PermissionsConfig")
52+
self.assertIsNotNone(permissions_config, f"{cp_name} should have permissions configuration")
53+
operator_role_arn = permissions_config.get("CapacityProviderOperatorRoleArn")
54+
self.assertIsNotNone(operator_role_arn, f"{cp_name} should have operator role ARN")
55+
return operator_role_arn
56+
57+
def verify_function_capacity_provider_config(self, function_capacity_provider_config):
58+
"""Verify and extract capacity provider ARN from function configuration."""
59+
self.assertIsNotNone(function_capacity_provider_config, "Function should have capacity provider configuration")
60+
self.assertIn(
61+
"LambdaManagedInstancesCapacityProviderConfig",
62+
function_capacity_provider_config,
63+
"Function LambdaManagedInstancesCapacityProviderConfig should have LambdaManagedInstancesCapacityProviderConfig",
64+
)
65+
66+
lmi_config = function_capacity_provider_config["LambdaManagedInstancesCapacityProviderConfig"]
67+
function_capacity_provider_arn = lmi_config.get("CapacityProviderArn")
68+
self.assertIsNotNone(
69+
function_capacity_provider_arn, "Function capacity provider config should have a capacity provider ARN"
70+
)
71+
return function_capacity_provider_arn
72+
73+
def verify_capacity_provider_arn_match(self, function_capacity_provider_arn, capacity_provider_arn):
74+
"""Verify that the function references the correct capacity provider ARN."""
75+
self.assertEqual(
76+
function_capacity_provider_arn,
77+
capacity_provider_arn,
78+
"Function should reference the correct capacity provider ARN",
79+
)
80+
81+
def test_function_with_capacity_provider_custom_role(self):
82+
"""Test Lambda function with CapacityProviderConfig using custom operator role."""
83+
parameters = self.generate_lmi_parameters()
84+
self.create_and_verify_stack("combination/function_lmi_custom", parameters)
85+
86+
lambda_resources = self.get_stack_resources("AWS::Lambda::Function")
87+
self.assertEqual(len(lambda_resources), 1, "Should create exactly one Lambda function")
88+
89+
capacity_provider_resources = self.get_stack_resources("AWS::Lambda::CapacityProvider")
90+
self.assertEqual(len(capacity_provider_resources), 1, "Should create exactly one CapacityProvider")
91+
92+
lambda_function = lambda_resources[0]
93+
function_capacity_provider_config = self.get_function_capacity_provider_config(
94+
lambda_function["PhysicalResourceId"]
95+
)
96+
function_capacity_provider_arn = self.verify_function_capacity_provider_config(
97+
function_capacity_provider_config
98+
)
99+
100+
capacity_provider_config = self.get_lambda_capacity_provider_config("MyCapacityProvider")
101+
actual_capacity_provider_arn = self.verify_capacity_provider_basic_config(
102+
capacity_provider_config, "MyCapacityProvider"
103+
)
104+
self.verify_capacity_provider_arn_match(function_capacity_provider_arn, actual_capacity_provider_arn)
105+
106+
capacity_provider_operator_role_arn = self.verify_capacity_provider_permissions_config(
107+
capacity_provider_config, "MyCapacityProvider"
108+
)
109+
110+
custom_operator_role_physical_id = self.get_physical_id_by_logical_id("MyCapacityProviderCustomRole")
111+
self.assertIsNotNone(custom_operator_role_physical_id, "MyCapacityProviderCustomRole should exist in the stack")
112+
113+
self.assertIn(
114+
custom_operator_role_physical_id,
115+
capacity_provider_operator_role_arn,
116+
f"Capacity provider should use the custom operator role with physical ID {custom_operator_role_physical_id}",
117+
)
118+
119+
def test_function_with_capacity_provider_default_role(self):
120+
"""Test Lambda function with CapacityProviderConfig using default operator role."""
121+
parameters = self.generate_lmi_parameters()
122+
self.create_and_verify_stack("combination/function_lmi_default", parameters)
123+
124+
lambda_resources = self.get_stack_resources("AWS::Lambda::Function")
125+
self.assertEqual(len(lambda_resources), 1, "Should create exactly one Lambda function")
126+
127+
capacity_provider_resources = self.get_stack_resources("AWS::Lambda::CapacityProvider")
128+
self.assertEqual(len(capacity_provider_resources), 2, "Should create exactly two CapacityProviders")
129+
130+
my_function = lambda_resources[0]
131+
function_config = self.get_function_capacity_provider_config(my_function["PhysicalResourceId"])
132+
function_capacity_provider_arn = self.verify_function_capacity_provider_config(function_config)
133+
134+
simple_cp_config = self.get_lambda_capacity_provider_config("SimpleCapacityProvider")
135+
simple_cp_arn = self.verify_capacity_provider_basic_config(simple_cp_config, "SimpleCapacityProvider")
136+
self.verify_capacity_provider_arn_match(function_capacity_provider_arn, simple_cp_arn)
137+
138+
self.verify_capacity_provider_vpc_config(simple_cp_config, "SimpleCapacityProvider")
139+
self.verify_capacity_provider_permissions_config(simple_cp_config, "SimpleCapacityProvider")
140+
141+
advanced_cp_config = self.get_lambda_capacity_provider_config("AdvancedCapacityProvider")
142+
self.verify_capacity_provider_basic_config(advanced_cp_config, "AdvancedCapacityProvider")
143+
self.verify_capacity_provider_vpc_config(advanced_cp_config, "AdvancedCapacityProvider")
144+
self.verify_capacity_provider_permissions_config(advanced_cp_config, "AdvancedCapacityProvider")
145+
146+
instance_requirements = advanced_cp_config.get("InstanceRequirements")
147+
self.assertIsNotNone(instance_requirements, "AdvancedCapacityProvider should have instance requirements")
148+
self.assertIn(
149+
"x86_64",
150+
instance_requirements.get("Architectures", []),
151+
"AdvancedCapacityProvider should have x86_64 architecture",
152+
)
153+
allowed_types = instance_requirements.get("AllowedInstanceTypes", [])
154+
self.assertIn("m5.large", allowed_types, "AdvancedCapacityProvider should allow m5.large")
155+
self.assertIn("m5.xlarge", allowed_types, "AdvancedCapacityProvider should allow m5.xlarge")
156+
self.assertIn("m5.2xlarge", allowed_types, "AdvancedCapacityProvider should allow m5.2xlarge")
157+
158+
scaling_config = advanced_cp_config.get("CapacityProviderScalingConfig")
159+
self.assertIsNotNone(scaling_config, "AdvancedCapacityProvider should have scaling configuration")
160+
self.assertEqual(
161+
scaling_config.get("MaxVCpuCount"), 64, "AdvancedCapacityProvider should have MaxVCpuCount of 64"
162+
)
163+
scaling_policies = scaling_config.get("ScalingPolicies", [])
164+
self.assertTrue(len(scaling_policies) > 0, "AdvancedCapacityProvider should have scaling policies")
165+
cpu_policy = next(
166+
(
167+
p
168+
for p in scaling_policies
169+
if p.get("PredefinedMetricType") == "LambdaCapacityProviderAverageCPUUtilization"
170+
),
171+
None,
172+
)
173+
self.assertIsNotNone(cpu_policy, "AdvancedCapacityProvider should have CPU utilization scaling policy")
174+
self.assertEqual(
175+
cpu_policy.get("TargetValue"), 70.0, "AdvancedCapacityProvider should have CPU utilization target of 70"
176+
)
177+
178+
self.assertEqual(
179+
advanced_cp_config.get("KmsKeyArn"),
180+
self.companion_stack_outputs["LMIKMSKeyArn"],
181+
"AdvancedCapacityProvider should use the correct KMS key",
182+
)
183+
184+
def get_function_capacity_provider_config(self, function_name, alias_name=None):
185+
lambda_client = self.client_provider.lambda_client
186+
187+
try:
188+
# Build the function identifier - include alias if provided
189+
function_identifier = f"{function_name}:{alias_name}" if alias_name else function_name
190+
# Get the function configuration
191+
response = lambda_client.get_function_configuration(FunctionName=function_identifier)
192+
# Return the CapacityProviderConfig if it exists
193+
return response.get("CapacityProviderConfig")
194+
except Exception as e:
195+
# Log the error and return None for graceful handling
196+
print(f"Error getting function capacity provider config: {e}")
197+
return None
198+
199+
def get_lambda_capacity_provider_config(self, capacity_provider_logical_id):
200+
lambda_client = self.client_provider.lambda_client
201+
202+
try:
203+
# Get the physical ID from the logical ID
204+
capacity_provider_name = self.get_physical_id_by_logical_id(capacity_provider_logical_id)
205+
# Get the capacity provider configuration
206+
response = lambda_client.get_capacity_provider(CapacityProviderName=capacity_provider_name)
207+
return response.get("CapacityProvider")
208+
except Exception as e:
209+
# Log the error and return None for graceful handling
210+
print(f"Error getting capacity provider config for {capacity_provider_logical_id}: {e}")
211+
return None

integration/config/service_names.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
STATE_MACHINE_CWE_CWS = "StateMachineCweCws"
3131
STATE_MACHINE_WITH_APIS = "StateMachineWithApis"
3232
LAMBDA_URL = "LambdaUrl"
33+
LAMBDA_MANAGED_INSTANCES = "LambdaManagedInstances"
3334
LAMBDA_ENV_VARS = "LambdaEnvVars"
3435
EVENT_INVOKE_CONFIG = "EventInvokeConfig"
3536
API_KEY = "ApiKey"

integration/helpers/base_internal_test.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
from integration.helpers.base_test import BaseTest
66

7+
CUSTOM_DOMAIN_TOP_LEVEL = "tooling.lambda.aws.dev"
8+
79

810
class BaseInternalTest(BaseTest):
911
"""

integration/resources/expected/combination/api_with_custom_domains_edge.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
[
22
{
3-
"LogicalResourceId": "RecordSetGroup1b7eeb359e",
3+
"LogicalResourceId": "RecordSetGroup303b65d470",
44
"ResourceType": "AWS::Route53::RecordSetGroup"
55
},
66
{
77
"LogicalResourceId": "MyApiDeployment",
88
"ResourceType": "AWS::ApiGateway::Deployment"
99
},
1010
{
11-
"LogicalResourceId": "ApiGatewayDomainName1101a94567",
11+
"LogicalResourceId": "ApiGatewayDomainNameed93f7bdb0",
1212
"ResourceType": "AWS::ApiGateway::DomainName"
1313
},
1414
{

integration/resources/expected/combination/api_with_custom_domains_regional.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"ResourceType": "AWS::ApiGateway::RestApi"
1717
},
1818
{
19-
"LogicalResourceId": "RecordSetGroupddfc299be2",
19+
"LogicalResourceId": "RecordSetGroup303b65d470",
2020
"ResourceType": "AWS::Route53::RecordSetGroup"
2121
},
2222
{
@@ -28,7 +28,7 @@
2828
"ResourceType": "AWS::ApiGateway::BasePathMapping"
2929
},
3030
{
31-
"LogicalResourceId": "ApiGatewayDomainName7898169271",
31+
"LogicalResourceId": "ApiGatewayDomainNamec65e226806",
3232
"ResourceType": "AWS::ApiGateway::DomainName"
3333
},
3434
{

0 commit comments

Comments
 (0)