Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: New opt-in property to update Lambda version whenever a property changes #2838

Merged
merged 23 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from 17 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
36 changes: 36 additions & 0 deletions integration/combination/test_function_with_alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,42 @@ def test_updating_version_by_changing_property_value(self):
self.assertEqual(len(alias), 1)
hoffa marked this conversation as resolved.
Show resolved Hide resolved
self.assertEqual(len(versions), 1)

def test_updating_version_by_changing_property_value_additional_properties(self):
self.create_and_verify_stack("combination/function_with_alias_and_all_properties_property")
alias_name = "Live"
function_name = self.get_physical_id_by_type("AWS::Lambda::Function")
version_ids = self.get_function_version_by_name(function_name)
self.assertEqual(["1"], version_ids)

alias = self.get_alias(function_name, alias_name)
self.assertEqual("1", alias["FunctionVersion"])

# Changing Handler should create a new version, and leave the existing version intact
self.set_template_resource_property("MyLambdaFunction", "Handler", "not_index.handler")
self.update_stack()

version_ids = self.get_function_version_by_name(function_name)
self.assertEqual(["1", "2"], version_ids)

alias = self.get_alias(function_name, alias_name)
self.assertEqual("2", alias["FunctionVersion"])

# Changing Description should create a new version, and leave the existing version intact
self.set_template_resource_property("MyLambdaFunction", "Description", "bar")
self.update_stack()

version_ids = self.get_function_version_by_name(function_name)
self.assertEqual(["1", "2", "3"], version_ids)

alias = self.get_alias(function_name, alias_name)
self.assertEqual("3", alias["FunctionVersion"])

# Make sure the stack has only One Version & One Alias resource
alias = self.get_stack_resources("AWS::Lambda::Alias")
versions = self.get_stack_resources("AWS::Lambda::Version")
self.assertEqual(len(alias), 1)
self.assertEqual(len(versions), 1)

def test_alias_deletion_must_retain_version(self):
self.create_and_verify_stack("combination/function_with_alias")
alias_name = "Live"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"LogicalResourceId": "MyLambdaFunction",
"ResourceType": "AWS::Lambda::Function"
},
{
"LogicalResourceId": "MyLambdaFunctionRole",
"ResourceType": "AWS::IAM::Role"
},
{
"LogicalResourceId": "MyLambdaFunctionAliasLive",
"ResourceType": "AWS::Lambda::Alias"
},
{
"LogicalResourceId": "MyLambdaFunctionVersion",
"ResourceType": "AWS::Lambda::Version"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Resources:
MyLambdaFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs14.x
CodeUri: ${codeuri}
AutoPublishAlias: Live
AutoPublishAliasAllProperties: true
Description: foo
Metadata:
SamTransformTest: true
32 changes: 21 additions & 11 deletions samtranslator/model/sam_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from samtranslator.metrics.method_decorator import cw_timer
from samtranslator.model import (
PassThroughProperty,
Property,
PropertyType,
Resource,
ResourceResolver,
Expand Down Expand Up @@ -121,6 +122,7 @@ class SamFunction(SamResourceMacro):
# Intrinsic functions in value of Alias property are not supported, yet
"AutoPublishAlias": PropertyType(False, one_of(IS_STR)),
"AutoPublishCodeSha256": PropertyType(False, one_of(IS_STR)),
"AutoPublishAliasAllProperties": Property(False, is_type(bool)),
"VersionDescription": PropertyType(False, IS_STR),
"ProvisionedConcurrencyConfig": PropertyType(False, IS_DICT),
"FileSystemConfigs": PropertyType(False, list_of(IS_DICT)),
Expand Down Expand Up @@ -161,6 +163,7 @@ class SamFunction(SamResourceMacro):
EphemeralStorage: Optional[Dict[str, Any]]
AutoPublishAlias: Optional[Intrinsicable[str]]
AutoPublishCodeSha256: Optional[Intrinsicable[str]]
AutoPublishAliasAllProperties: Optional[bool]
VersionDescription: Optional[Intrinsicable[str]]
ProvisionedConcurrencyConfig: Optional[Dict[str, Any]]
FileSystemConfigs: Optional[Dict[str, Any]]
Expand Down Expand Up @@ -874,18 +877,25 @@ def _construct_version(
# and next hashes. The chances that two subsequent hashes collide is fairly low.
prefix = "{id}Version".format(id=self.logical_id)
logical_dict = {}
try:
logical_dict = code_dict.copy()
except (AttributeError, UnboundLocalError):
pass # noqa: try-except-pass
# We can't directly change AutoPublishAlias as that would be a breaking change, so we have to add this opt-in
# property that when set to true would change the lambda version whenever a property in the lambda function changes
if self.AutoPublishAliasAllProperties:
properties = function._generate_resource_dict().get("Properties", {})
logical_dict = properties
else:
if function.Environment:
logical_dict.update(function.Environment)
if function.MemorySize:
logical_dict.update({"MemorySize": function.MemorySize})
# If SnapStart is enabled we want to publish a new version, to have the corresponding snapshot
if function.SnapStart and function.SnapStart.get("ApplyOn", "None") != "None":
logical_dict.update({"SnapStart": function.SnapStart})
try:
logical_dict = code_dict.copy()
except (AttributeError, UnboundLocalError):
pass # noqa: try-except-pass
aaythapa marked this conversation as resolved.
Show resolved Hide resolved
else:
if function.Environment:
logical_dict.update(function.Environment)
if function.MemorySize:
logical_dict.update({"MemorySize": function.MemorySize})
# If SnapStart is enabled we want to publish a new version, to have the corresponding snapshot
if function.SnapStart and function.SnapStart.get("ApplyOn", "None") != "None":
logical_dict.update({"SnapStart": function.SnapStart})

hoffa marked this conversation as resolved.
Show resolved Hide resolved
logical_id = logical_id_generator.LogicalIdGenerator(prefix, logical_dict, code_sha256).gen()

attributes = self.get_passthrough_resource_attributes()
Expand Down
1 change: 1 addition & 0 deletions samtranslator/plugins/globals/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Globals:
"Tracing",
"KmsKeyArn",
"AutoPublishAlias",
"AutoPublishAliasAllProperties",
"Layers",
"DeploymentPreference",
"RolePath",
Expand Down
4 changes: 4 additions & 0 deletions samtranslator/schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -189313,6 +189313,10 @@
"markdownDescription": "The name of the Lambda alias\\. For more information about Lambda aliases, see [Lambda function aliases](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) in the *AWS Lambda Developer Guide*\\. For examples that use this property, see [Deploying serverless applications gradually](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html)\\. \nAWS SAM generates [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html) and [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-alias.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-alias.html) resources when this property is set\\. For information about this scenario, see [AutoPublishAlias property is specified](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources-function.html#sam-specification-generated-resources-function-autopublishalias)\\. For general information about generated AWS CloudFormation resources, see [Generated AWS CloudFormation resources](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources.html)\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
"title": "AutoPublishAlias"
},
"AutoPublishAliasAllProperties": {
"title": "Autopublishaliasallproperties",
"type": "boolean"
},
"AutoPublishCodeSha256": {
"anyOf": [
{
Expand Down
2 changes: 2 additions & 0 deletions schema_source/aws_serverless_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ class ScheduleV2Event(BaseModel):
KmsKeyArn = Optional[PassThroughProp]
Layers = Optional[PassThroughProp]
AutoPublishAlias = Optional[SamIntrinsicable[str]]
AutoPublishAliasAllProperties = Optional[bool]
RolePath = Optional[PassThroughProp]
PermissionsBoundary = Optional[PassThroughProp]
ReservedConcurrentExecutions = Optional[PassThroughProp]
Expand All @@ -463,6 +464,7 @@ class Properties(BaseModel):
Architectures: Optional[Architectures] = prop("Architectures")
AssumeRolePolicyDocument: Optional[AssumeRolePolicyDocument] = prop("AssumeRolePolicyDocument")
AutoPublishAlias: Optional[AutoPublishAlias] = prop("AutoPublishAlias")
AutoPublishAliasAllProperties: Optional[AutoPublishAliasAllProperties] # TODO: add docs
AutoPublishCodeSha256: Optional[SamIntrinsicable[str]] = prop("AutoPublishCodeSha256")
CodeSigningConfigArn: Optional[SamIntrinsicable[str]] = prop("CodeSigningConfigArn")
CodeUri: Optional[CodeUriType] = prop("CodeUri")
Expand Down
4 changes: 4 additions & 0 deletions schema_source/sam.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -4692,6 +4692,10 @@
"markdownDescription": "The name of the Lambda alias\\. For more information about Lambda aliases, see [Lambda function aliases](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) in the *AWS Lambda Developer Guide*\\. For examples that use this property, see [Deploying serverless applications gradually](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html)\\. \nAWS SAM generates [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html) and [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-alias.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-alias.html) resources when this property is set\\. For information about this scenario, see [AutoPublishAlias property is specified](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources-function.html#sam-specification-generated-resources-function-autopublishalias)\\. For general information about generated AWS CloudFormation resources, see [Generated AWS CloudFormation resources](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources.html)\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
"title": "AutoPublishAlias"
},
"AutoPublishAliasAllProperties": {
"title": "Autopublishaliasallproperties",
"type": "boolean"
},
"AutoPublishCodeSha256": {
"anyOf": [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# AutoPublishCodeSha256 should take precedence when making the hash for the logical id of the Lambda version even if
# AutoPublishAliasAllProperties is set to true
Resources:
MinimalFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: s3://sam-demo-bucket/hello.zip
Handler: hello.handler
Runtime: python2.7
AutoPublishAlias: live
AutoPublishAliasAllProperties: true
AutoPublishCodeSha256: 6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b
VersionDescription: sam-testing
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Resources:
MinimalFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: s3://sam-demo-bucket/hello.zip
Handler: hello.handler
Runtime: python2.7
AutoPublishAlias: live
AutoPublishAliasAllProperties: true
aaythapa marked this conversation as resolved.
Show resolved Hide resolved
VersionDescription: sam-testing

OtherMinimalFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: s3://sam-demo-bucket/hello.zip
Handler: hello.handler
Runtime: python2.7
AutoPublishAlias: live
AutoPublishAliasAllProperties: false
VersionDescription: sam-testing
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"Resources": {
"MinimalFunction": {
"Properties": {
"Code": {
"S3Bucket": "sam-demo-bucket",
"S3Key": "hello.zip"
},
"Handler": "hello.handler",
"Role": {
"Fn::GetAtt": [
"MinimalFunctionRole",
"Arn"
]
},
"Runtime": "python2.7",
"Tags": [
{
"Key": "lambda:createdBy",
"Value": "SAM"
}
]
},
"Type": "AWS::Lambda::Function"
},
"MinimalFunctionAliaslive": {
"Properties": {
"FunctionName": {
"Ref": "MinimalFunction"
},
"FunctionVersion": {
"Fn::GetAtt": [
"MinimalFunctionVersion6b86b273ff",
"Version"
]
},
"Name": "live"
},
"Type": "AWS::Lambda::Alias"
},
"MinimalFunctionRole": {
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": [
"sts:AssumeRole"
],
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
}
}
],
"Version": "2012-10-17"
},
"ManagedPolicyArns": [
"arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
],
"Tags": [
{
"Key": "lambda:createdBy",
"Value": "SAM"
}
]
},
"Type": "AWS::IAM::Role"
},
"MinimalFunctionVersion6b86b273ff": {
"DeletionPolicy": "Retain",
"Properties": {
"Description": "sam-testing",
"FunctionName": {
"Ref": "MinimalFunction"
}
},
"Type": "AWS::Lambda::Version"
}
}
}
Loading