Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .cfnlintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ ignore_templates:
- tests/translator/output/**/function_with_metrics_config.json
- tests/translator/output/**/function_with_self_managed_kafka_and_schema_registry.json # cfnlint is not updated to recognize the SchemaRegistryConfig property
- tests/translator/output/**/function_with_msk_with_schema_registry_config.json # cfnlint is not updated to recognize the SchemaRegistryConfig property
- tests/translator/output/**/function_with_tenancy_config.json # cfnlint is not updated to recognize the TenancyConfig property
- tests/translator/output/**/function_with_tenancy_and_api_event.json # cfnlint is not updated to recognize the TenancyConfig property
- tests/translator/output/**/function_with_tenancy_and_httpapi_event.json # cfnlint is not updated to recognize the TenancyConfig property
- tests/translator/output/**/function_with_tenancy_config_global.json # cfnlint is not updated to recognize the TenancyConfig property

ignore_checks:
- E2531 # Deprecated runtime; not relevant for transform tests
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"LogicalResourceId": "MyLambdaFunction",
"ResourceType": "AWS::Lambda::Function"
},
{
"LogicalResourceId": "MyLambdaFunctionRole",
"ResourceType": "AWS::IAM::Role"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Resources:
MyLambdaFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs18.x
CodeUri: ${codeuri}
MemorySize: 128
TenancyConfig:
TenantIsolationMode: PER_TENANT
Policies:
- AWSLambdaRole
- AmazonS3ReadOnlyAccess
Metadata:
SamTransformTest: true
21 changes: 21 additions & 0 deletions integration/single/test_basic_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,3 +392,24 @@ def _assert_invoke(self, lambda_client, function_name, qualifier=None, expected_
def _verify_get_request(self, url, expected_text):
response = self.verify_get_request_response(url, 200)
self.assertEqual(response.text, expected_text)

# @skipIf(current_region_does_not_support([MULTI_TENANCY]), "Multi-tenancy is not supported in this testing region")
@skipIf(True, "Multi-tenancy feature is not available to test yet")
def test_basic_function_with_tenancy_config(self):
"""
Creates a basic lambda function with TenancyConfig
"""
self.create_and_verify_stack("single/basic_function_with_tenancy_config")

lambda_client = self.client_provider.lambda_client
function_name = self.get_physical_id_by_type("AWS::Lambda::Function")

# Get function configuration
function_config = lambda_client.get_function_configuration(FunctionName=function_name)

# Verify TenancyConfig is set correctly
self.assertEqual(
function_config["TenancyConfig"]["TenantIsolationMode"],
"PER_TENANT",
"Expected TenantIsolationMode to be PER_TENANT",
)
2 changes: 1 addition & 1 deletion samtranslator/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.101.0"
__version__ = "1.102.0"
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ class ScheduleV2Event(BaseModel):
LoggingConfig = Optional[PassThroughProp] # TODO: add documentation
RecursiveLoop = Optional[PassThroughProp]
SourceKMSKeyArn = Optional[PassThroughProp]
TenancyConfig = Optional[PassThroughProp]


class Properties(BaseModel):
Expand Down Expand Up @@ -650,6 +651,7 @@ class Properties(BaseModel):
LoggingConfig: Optional[PassThroughProp] # TODO: add documentation
RecursiveLoop: Optional[PassThroughProp] # TODO: add documentation
SourceKMSKeyArn: Optional[PassThroughProp] # TODO: add documentation
TenancyConfig: Optional[PassThroughProp] # TODO: add documentation


class Globals(BaseModel):
Expand Down Expand Up @@ -710,6 +712,7 @@ class Globals(BaseModel):
LoggingConfig: Optional[PassThroughProp] # TODO: add documentation
RecursiveLoop: Optional[PassThroughProp] # TODO: add documentation
SourceKMSKeyArn: Optional[PassThroughProp] # TODO: add documentation
TenancyConfig: Optional[PassThroughProp] # TODO: add documentation


class Resource(ResourceAttributes):
Expand Down
2 changes: 2 additions & 0 deletions samtranslator/model/lambda_.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class LambdaFunction(Resource):
"RuntimeManagementConfig": GeneratedProperty(),
"LoggingConfig": GeneratedProperty(),
"RecursiveLoop": GeneratedProperty(),
"TenancyConfig": GeneratedProperty(),
}

Code: Dict[str, Any]
Expand Down Expand Up @@ -64,6 +65,7 @@ class LambdaFunction(Resource):
RuntimeManagementConfig: Optional[Dict[str, Any]]
LoggingConfig: Optional[Dict[str, Any]]
RecursiveLoop: Optional[str]
TenancyConfig: Optional[Dict[str, Any]]

runtime_attrs = {"name": lambda self: ref(self.logical_id), "arn": lambda self: fnGetAtt(self.logical_id, "Arn")}

Expand Down
36 changes: 36 additions & 0 deletions samtranslator/model/sam_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ class SamFunction(SamResourceMacro):
"LoggingConfig": PassThroughProperty(False),
"RecursiveLoop": PassThroughProperty(False),
"SourceKMSKeyArn": PassThroughProperty(False),
"TenancyConfig": PassThroughProperty(False),
}

FunctionName: Optional[Intrinsicable[str]]
Expand Down Expand Up @@ -228,6 +229,7 @@ class SamFunction(SamResourceMacro):
LoggingConfig: Optional[Dict[str, Any]]
RecursiveLoop: Optional[str]
SourceKMSKeyArn: Optional[str]
TenancyConfig: Optional[Dict[str, Any]]

event_resolver = ResourceTypeResolver(
samtranslator.model.eventsources,
Expand Down Expand Up @@ -277,6 +279,8 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] # noqa: P
if self.DeadLetterQueue:
self._validate_dlq(self.DeadLetterQueue)

self._validate_tenancy_config_compatibility()

lambda_function = self._construct_lambda_function(intrinsics_resolver)
resources.append(lambda_function)

Expand Down Expand Up @@ -572,6 +576,37 @@ def _get_resolved_alias_name(

return resolved_alias_name

def _validate_tenancy_config_compatibility(self) -> None:
if not self.TenancyConfig:
return

if self.ProvisionedConcurrencyConfig:
raise InvalidResourceException(
self.logical_id,
"Provisioned concurrency is not supported for functions enabled with tenancy configuration.",
)

if self.FunctionUrlConfig:
raise InvalidResourceException(
self.logical_id,
"Function URL is not supported for functions enabled with tenancy configuration.",
)

if self.SnapStart:
raise InvalidResourceException(
self.logical_id,
"SnapStart is not supported for functions enabled with tenancy configuration.",
)

if self.Events:
for event in self.Events.values():
event_type = event.get("Type")
if event_type not in ["Api", "HttpApi"]:
raise InvalidResourceException(
self.logical_id,
f"Event source '{event_type}' is not supported for functions enabled with tenancy configuration. Only Api and HttpApi event sources are supported.",
)

def _construct_lambda_function(self, intrinsics_resolver: IntrinsicsResolver) -> LambdaFunction:
"""Constructs and returns the Lambda function.

Expand Down Expand Up @@ -618,6 +653,7 @@ def _construct_lambda_function(self, intrinsics_resolver: IntrinsicsResolver) ->

lambda_function.RuntimeManagementConfig = self.RuntimeManagementConfig # type: ignore[attr-defined]
lambda_function.LoggingConfig = self.LoggingConfig
lambda_function.TenancyConfig = self.TenancyConfig
lambda_function.RecursiveLoop = self.RecursiveLoop
self._validate_package_type(lambda_function)
return lambda_function
Expand Down
8 changes: 7 additions & 1 deletion samtranslator/plugins/globals/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Globals:
"LoggingConfig",
"RecursiveLoop",
"SourceKMSKeyArn",
"TenancyConfig",
],
# Everything except
# DefinitionBody: because its hard to reason about merge of Swagger dictionaries
Expand Down Expand Up @@ -101,7 +102,12 @@ class Globals:
}
# unreleased_properties *must be* part of supported_properties too
unreleased_properties: Dict[str, List[str]] = {
SamResourceType.Function.value: ["RuntimeManagementConfig", "RecursiveLoop", "SourceKMSKeyArn"],
SamResourceType.Function.value: [
"RuntimeManagementConfig",
"RecursiveLoop",
"SourceKMSKeyArn",
"TenancyConfig",
],
}

def __init__(self, template: Dict[str, Any]) -> None:
Expand Down
6 changes: 6 additions & 0 deletions samtranslator/schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -278502,6 +278502,9 @@
"title": "Tags",
"type": "object"
},
"TenancyConfig": {
"$ref": "#/definitions/PassThroughProp"
},
"Timeout": {
"allOf": [
{
Expand Down Expand Up @@ -278899,6 +278902,9 @@
"title": "Tags",
"type": "object"
},
"TenancyConfig": {
"$ref": "#/definitions/PassThroughProp"
},
"Timeout": {
"allOf": [
{
Expand Down
6 changes: 6 additions & 0 deletions schema_source/sam.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -5609,6 +5609,9 @@
"title": "Tags",
"type": "object"
},
"TenancyConfig": {
"$ref": "#/definitions/PassThroughProp"
},
"Timeout": {
"allOf": [
{
Expand Down Expand Up @@ -6197,6 +6200,9 @@
"title": "Tags",
"type": "object"
},
"TenancyConfig": {
"$ref": "#/definitions/PassThroughProp"
},
"Timeout": {
"allOf": [
{
Expand Down
32 changes: 32 additions & 0 deletions tests/translator/input/error_tenancy_with_dynamodb_event.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Transform: AWS::Serverless-2016-10-31

Resources:
MyTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
BillingMode: PAY_PER_REQUEST
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES

MyFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: python3.11
Handler: index.handler
InlineCode: |
def handler(event, context):
return {}
TenancyConfig:
TenantIsolationMode: PER_TENANT
Events:
MyDynamoDBEvent:
Type: DynamoDB
Properties:
Stream: !GetAtt MyTable.StreamArn
StartingPosition: LATEST
15 changes: 15 additions & 0 deletions tests/translator/input/error_tenancy_with_function_url.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Transform: AWS::Serverless-2016-10-31

Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: python3.11
Handler: index.handler
InlineCode: |
def handler(event, context):
return {}
TenancyConfig:
TenantIsolationMode: PER_TENANT
FunctionUrlConfig:
AuthType: AWS_IAM
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Transform: AWS::Serverless-2016-10-31

Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: python3.11
Handler: index.handler
InlineCode: |
def handler(event, context):
return {}
TenancyConfig:
TenantIsolationMode: PER_TENANT
AutoPublishAlias: live
ProvisionedConcurrencyConfig:
ProvisionedConcurrentExecutions: 5
15 changes: 15 additions & 0 deletions tests/translator/input/error_tenancy_with_snapstart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Transform: AWS::Serverless-2016-10-31

Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: python3.11
Handler: index.handler
InlineCode: |
def handler(event, context):
return {}
TenancyConfig:
TenantIsolationMode: PER_TENANT
SnapStart:
ApplyOn: PublishedVersions
21 changes: 21 additions & 0 deletions tests/translator/input/error_tenancy_with_sns_event.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Transform: AWS::Serverless-2016-10-31

Resources:
MyTopic:
Type: AWS::SNS::Topic

MyFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: python3.11
Handler: index.handler
InlineCode: |
def handler(event, context):
return {}
TenancyConfig:
TenantIsolationMode: PER_TENANT
Events:
MySNSEvent:
Type: SNS
Properties:
Topic: !Ref MyTopic
21 changes: 21 additions & 0 deletions tests/translator/input/error_tenancy_with_sqs_event.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Transform: AWS::Serverless-2016-10-31

Resources:
MyQueue:
Type: AWS::SQS::Queue

MyFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: python3.11
Handler: index.handler
InlineCode: |
def handler(event, context):
return {}
TenancyConfig:
TenantIsolationMode: PER_TENANT
Events:
MySQSEvent:
Type: SQS
Properties:
Queue: !GetAtt MyQueue.Arn
19 changes: 19 additions & 0 deletions tests/translator/input/function_with_tenancy_and_api_event.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Transform: AWS::Serverless-2016-10-31

Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: python3.11
Handler: index.handler
InlineCode: |
def handler(event, context):
return {'statusCode': 200, 'body': 'Hello'}
TenancyConfig:
TenantIsolationMode: PER_TENANT
Events:
MyApiEvent:
Type: Api
Properties:
Path: /hello
Method: get
Loading
Loading