Skip to content
This repository was archived by the owner on Aug 14, 2024. It is now read-only.

Commit 2d9b4e8

Browse files
committed
minor fixes for CloudFormation upstream changes (localstack#2922)
1 parent 6d8ebc6 commit 2d9b4e8

6 files changed

Lines changed: 36 additions & 15 deletions

File tree

localstack/services/cloudformation/cloudformation_starter.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ def add_default_resource_props(resource_props, stack_name, resource_name=None):
209209
if res_type == 'AWS::Lambda::EventSourceMapping' and not props.get('StartingPosition'):
210210
props['StartingPosition'] = 'LATEST'
211211

212+
if res_type == 'AWS::Lambda::Function' and not props.get('FunctionName'):
213+
props['FunctionName'] = '{}-lambda-{}'.format(stack_name[:45], short_uid())
214+
212215
if res_type == 'AWS::SNS::Topic' and not props.get('TopicName'):
213216
props['TopicName'] = 'topic-%s' % short_uid()
214217

@@ -248,8 +251,15 @@ def clean_json(resource_json, resources_map):
248251
LOG.info('Potential circular dependency detected when resolving Ref "%s"' % resource_json['Ref'])
249252
return resource_json['Ref']
250253
raise
251-
if isinstance(result, BaseModel):
252-
if isinstance(resource_json, dict) and 'Ref' in resource_json:
254+
if isinstance(resource_json, dict):
255+
if isinstance(resource_json.get('Fn::GetAtt'), list) and result == resource_json:
256+
# If the attribute cannot be resolved (i.e., result == resource_json), then return
257+
# an empty value, to avoid returning the original JSON struct (which otherwise
258+
# results in downstream issues, e.g., when concatenating template values).
259+
# TODO: Note that this workaround could point towards a general issue with
260+
# dependency resolution - in fact, this case should never be happening (but it does).
261+
return ''
262+
if 'Ref' in resource_json and isinstance(result, BaseModel):
253263
entity_id = get_entity_id(result, resource_json)
254264
if entity_id:
255265
return entity_id
@@ -294,7 +304,7 @@ def _parse_and_create_resource(logical_id, resource_json, resources_map, region_
294304
return None
295305

296306
# parse and get final resource JSON
297-
resource_tuple = parsing.parse_resource(logical_id, resource_json, resources_map)
307+
resource_tuple = parsing.parse_resource_and_generate_name(logical_id, resource_json, resources_map)
298308
if not resource_tuple:
299309
return None
300310
_, resource_json, resource_name = resource_tuple
@@ -617,7 +627,9 @@ def SQS_Queue_physical_resource_id(self):
617627
result = SQS_Queue_physical_resource_id_orig.fget(self)
618628
if '://' not in result:
619629
# convert ID to queue URL
620-
return aws_stack.get_sqs_queue_url(result)
630+
self._physical_resource_id = (getattr(self, '_physical_resource_id', None) or
631+
aws_stack.get_sqs_queue_url(result))
632+
return self._physical_resource_id
621633
return result
622634

623635
SQS_Queue_physical_resource_id_orig = sqs_models.Queue.physical_resource_id
@@ -793,7 +805,7 @@ def Role_update_from_cloudformation_json(cls,
793805
if not hasattr(iam_models.Role, 'update_from_cloudformation_json'):
794806
iam_models.Role.update_from_cloudformation_json = Role_update_from_cloudformation_json
795807

796-
# patch ApiGateway Deployment
808+
# patch ApiGateway Deployment deletion
797809
@staticmethod
798810
def depl_delete_from_cloudformation_json(resource_name, resource_json, region_name):
799811
properties = resource_json['Properties']
@@ -802,7 +814,7 @@ def depl_delete_from_cloudformation_json(resource_name, resource_json, region_na
802814
if not hasattr(apigw_models.Deployment, 'delete_from_cloudformation_json'):
803815
apigw_models.Deployment.delete_from_cloudformation_json = depl_delete_from_cloudformation_json
804816

805-
# patch Lambda Version
817+
# patch Lambda Version deletion
806818
@staticmethod
807819
def vers_delete_from_cloudformation_json(resource_name, resource_json, region_name):
808820
properties = resource_json['Properties']

localstack/utils/bootstrap.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,11 @@ def run(self):
523523
LOG.warning('Thread run method %s(%s) failed: %s %s' %
524524
(self.func, self.params, e, traceback.format_exc()))
525525
finally:
526-
self.result_future.set_result(result)
526+
try:
527+
self.result_future.set_result(result)
528+
except Exception:
529+
# this can happen as InvalidStateError on shutdown, if the task is already canceled
530+
pass
527531

528532
def stop(self, quiet=False):
529533
if not quiet and not self.quiet:

localstack/utils/cloudformation/template_deployer.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -747,9 +747,8 @@ def retrieve_resource_details(resource_id, resource_status, resources, stack_nam
747747
resource_props = resource.get('Properties')
748748
try:
749749
if resource_type == 'Lambda::Function':
750-
resource_props['FunctionName'] = (resource_props.get('FunctionName') or
751-
'{}-lambda-{}'.format(stack_name[:45], common.short_uid()))
752-
resource_id = resource_props['FunctionName'] if resource else resource_id
750+
func_name = resolve_refs_recursively(stack_name, resource_props['FunctionName'], resources)
751+
resource_id = func_name if resource else resource_id
753752
return aws_stack.connect_to_service('lambda').get_function(FunctionName=resource_id)
754753
elif resource_type == 'Lambda::Version':
755754
name = resource_props.get('FunctionName')

tests/integration/serverless/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
},
88
"devDependencies": {
99
"serverless": "^1.67.1",
10-
"serverless-localstack": "^0.4.24"
10+
"serverless-localstack": "^0.4.24",
11+
"serverless-deployment-bucket": "^1.1.2"
1112
}
1213
}

tests/integration/serverless/serverless.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ provider:
77
versionFunctions: false
88
timeout: 900
99
runtime: "nodejs12.x"
10+
deploymentBucket:
11+
name: custom-sls-depl-bucket-123
1012

1113
functions:
1214
test:
@@ -25,8 +27,10 @@ functions:
2527
path: /test/v1
2628
method: get
2729
integration: lambda-proxy
30+
2831
plugins:
29-
- "serverless-localstack"
32+
- serverless-deployment-bucket
33+
- serverless-localstack
3034

3135
custom:
3236
localstack:

tests/integration/test_cloudformation.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,8 @@ def test_deploy_stack_with_iam_role(self):
11471147
role_name = 'role-%s' % short_uid()
11481148

11491149
cloudformation = aws_stack.connect_to_service('cloudformation')
1150+
iam_client = aws_stack.connect_to_service('iam')
1151+
roles_before = iam_client.list_roles()['Roles']
11501152

11511153
try:
11521154
cloudformation.describe_stacks(
@@ -1188,11 +1190,10 @@ def test_deploy_stack_with_iam_role(self):
11881190
stack = rs['Stacks'][0]
11891191
self.assertEqual(stack['StackName'], stack_name)
11901192

1191-
iam_client = aws_stack.connect_to_service('iam')
11921193
rs = iam_client.list_roles()
11931194

1194-
self.assertEqual(len(rs['Roles']), 1)
1195-
self.assertEqual(rs['Roles'][0]['RoleName'], role_name)
1195+
self.assertEqual(len(rs['Roles']), len(roles_before) + 1)
1196+
self.assertEqual(rs['Roles'][-1]['RoleName'], role_name)
11961197

11971198
rs = iam_client.list_role_policies(
11981199
RoleName=role_name

0 commit comments

Comments
 (0)