Skip to content

Commit

Permalink
minor fixes for CloudFormation upstream changes (#2922)
Browse files Browse the repository at this point in the history
  • Loading branch information
whummer committed Sep 2, 2020
1 parent 6d8ebc6 commit b7709d5
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 15 deletions.
21 changes: 15 additions & 6 deletions localstack/services/cloudformation/cloudformation_starter.py
Expand Up @@ -209,6 +209,9 @@ def add_default_resource_props(resource_props, stack_name, resource_name=None):
if res_type == 'AWS::Lambda::EventSourceMapping' and not props.get('StartingPosition'):
props['StartingPosition'] = 'LATEST'

if res_type == 'AWS::Lambda::Function' and not props.get('FunctionName'):
props['FunctionName'] = '{}-lambda-{}'.format(stack_name[:45], short_uid())

if res_type == 'AWS::SNS::Topic' and not props.get('TopicName'):
props['TopicName'] = 'topic-%s' % short_uid()

Expand Down Expand Up @@ -248,8 +251,12 @@ def clean_json(resource_json, resources_map):
LOG.info('Potential circular dependency detected when resolving Ref "%s"' % resource_json['Ref'])
return resource_json['Ref']
raise
if isinstance(result, BaseModel):
if isinstance(resource_json, dict) and 'Ref' in resource_json:
if isinstance(resource_json, dict):
if isinstance(resource_json.get('Fn::GetAtt'), list) and result == resource_json:
# if the attribute cannot be resolved (i.e., result == resource_json), then return None,
# to avoid returning the original JSON struct (which otherwise results in downstream issues..)
return None
if 'Ref' in resource_json and isinstance(result, BaseModel):
entity_id = get_entity_id(result, resource_json)
if entity_id:
return entity_id
Expand Down Expand Up @@ -294,7 +301,7 @@ def _parse_and_create_resource(logical_id, resource_json, resources_map, region_
return None

# parse and get final resource JSON
resource_tuple = parsing.parse_resource(logical_id, resource_json, resources_map)
resource_tuple = parsing.parse_resource_and_generate_name(logical_id, resource_json, resources_map)
if not resource_tuple:
return None
_, resource_json, resource_name = resource_tuple
Expand Down Expand Up @@ -617,7 +624,9 @@ def SQS_Queue_physical_resource_id(self):
result = SQS_Queue_physical_resource_id_orig.fget(self)
if '://' not in result:
# convert ID to queue URL
return aws_stack.get_sqs_queue_url(result)
self._physical_resource_id = (getattr(self, '_physical_resource_id', None) or
aws_stack.get_sqs_queue_url(result))
return self._physical_resource_id
return result

SQS_Queue_physical_resource_id_orig = sqs_models.Queue.physical_resource_id
Expand Down Expand Up @@ -793,7 +802,7 @@ def Role_update_from_cloudformation_json(cls,
if not hasattr(iam_models.Role, 'update_from_cloudformation_json'):
iam_models.Role.update_from_cloudformation_json = Role_update_from_cloudformation_json

# patch ApiGateway Deployment
# patch ApiGateway Deployment deletion
@staticmethod
def depl_delete_from_cloudformation_json(resource_name, resource_json, region_name):
properties = resource_json['Properties']
Expand All @@ -802,7 +811,7 @@ def depl_delete_from_cloudformation_json(resource_name, resource_json, region_na
if not hasattr(apigw_models.Deployment, 'delete_from_cloudformation_json'):
apigw_models.Deployment.delete_from_cloudformation_json = depl_delete_from_cloudformation_json

# patch Lambda Version
# patch Lambda Version deletion
@staticmethod
def vers_delete_from_cloudformation_json(resource_name, resource_json, region_name):
properties = resource_json['Properties']
Expand Down
6 changes: 5 additions & 1 deletion localstack/utils/bootstrap.py
Expand Up @@ -523,7 +523,11 @@ def run(self):
LOG.warning('Thread run method %s(%s) failed: %s %s' %
(self.func, self.params, e, traceback.format_exc()))
finally:
self.result_future.set_result(result)
try:
self.result_future.set_result(result)
except Exception:
# this can happen as InvalidStateError on shutdown, if the task is already canceled
pass

def stop(self, quiet=False):
if not quiet and not self.quiet:
Expand Down
5 changes: 2 additions & 3 deletions localstack/utils/cloudformation/template_deployer.py
Expand Up @@ -747,9 +747,8 @@ def retrieve_resource_details(resource_id, resource_status, resources, stack_nam
resource_props = resource.get('Properties')
try:
if resource_type == 'Lambda::Function':
resource_props['FunctionName'] = (resource_props.get('FunctionName') or
'{}-lambda-{}'.format(stack_name[:45], common.short_uid()))
resource_id = resource_props['FunctionName'] if resource else resource_id
func_name = resolve_refs_recursively(stack_name, resource_props['FunctionName'], resources)
resource_id = func_name if resource else resource_id
return aws_stack.connect_to_service('lambda').get_function(FunctionName=resource_id)
elif resource_type == 'Lambda::Version':
name = resource_props.get('FunctionName')
Expand Down
3 changes: 2 additions & 1 deletion tests/integration/serverless/package.json
Expand Up @@ -7,6 +7,7 @@
},
"devDependencies": {
"serverless": "^1.67.1",
"serverless-localstack": "^0.4.24"
"serverless-localstack": "^0.4.24",
"serverless-deployment-bucket": "^1.1.2"
}
}
6 changes: 5 additions & 1 deletion tests/integration/serverless/serverless.yml
Expand Up @@ -7,6 +7,8 @@ provider:
versionFunctions: false
timeout: 900
runtime: "nodejs12.x"
deploymentBucket:
name: custom-sls-depl-bucket-123

functions:
test:
Expand All @@ -25,8 +27,10 @@ functions:
path: /test/v1
method: get
integration: lambda-proxy

plugins:
- "serverless-localstack"
- serverless-deployment-bucket
- serverless-localstack

custom:
localstack:
Expand Down
7 changes: 4 additions & 3 deletions tests/integration/test_cloudformation.py
Expand Up @@ -1147,6 +1147,8 @@ def test_deploy_stack_with_iam_role(self):
role_name = 'role-%s' % short_uid()

cloudformation = aws_stack.connect_to_service('cloudformation')
iam_client = aws_stack.connect_to_service('iam')
roles_before = iam_client.list_roles()['Roles']

try:
cloudformation.describe_stacks(
Expand Down Expand Up @@ -1188,11 +1190,10 @@ def test_deploy_stack_with_iam_role(self):
stack = rs['Stacks'][0]
self.assertEqual(stack['StackName'], stack_name)

iam_client = aws_stack.connect_to_service('iam')
rs = iam_client.list_roles()

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

rs = iam_client.list_role_policies(
RoleName=role_name
Expand Down

0 comments on commit b7709d5

Please sign in to comment.