Skip to content

Commit 0ece31f

Browse files
authored
fix CloudFormation updates for EC2::Instance with empty SecurityGroups property (localstack#5117)
1 parent 5d4f668 commit 0ece31f

File tree

3 files changed

+46
-29
lines changed

3 files changed

+46
-29
lines changed

localstack/services/cloudformation/models/ec2.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,10 +425,13 @@ def update_resource(self, new_resource, stack_name, resources):
425425
groups = props.get("SecurityGroups", props.get("SecurityGroupIds"))
426426

427427
client = aws_stack.connect_to_service("ec2")
428+
kwargs = {}
429+
if groups:
430+
kwargs["Groups"] = groups
428431
client.modify_instance_attribute(
429-
Groups=groups,
430432
InstanceId=instance_id,
431433
InstanceType={"Value": props["InstanceType"]},
434+
**kwargs,
432435
)
433436
return self._get_state(client)
434437

tests/integration/templates/template28.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ Resources:
8282
- InstanceProfile
8383
# </sensitive>
8484
Properties:
85-
InstanceType: 't3.small'
85+
InstanceType: "t3.small"
8686
ImageId: abc
8787
KeyName: "ec2-key-pair"
8888
IamInstanceProfile: !Ref InstanceProfile
@@ -91,6 +91,12 @@ Resources:
9191
SubnetId:
9292
Ref: PublicSubnetA
9393

94+
Ec2InstanceNoSGs:
95+
Type: AWS::EC2::Instance
96+
Properties:
97+
InstanceType: "t3.small"
98+
ImageId: "ami-c6f321bf"
99+
94100
CloudWatchAlarm:
95101
Type: AWS::CloudWatch::Alarm
96102
Properties:

tests/integration/test_cloudformation.py

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,43 +1958,51 @@ def test_deploy_stack_with_sub_select_and_sub_getaz(self):
19581958
cw_client = aws_stack.connect_to_service("cloudwatch")
19591959

19601960
# list resources before stack deployment
1961+
# TODO: avoid fetching resources before/after, to make tests parallelizable!
19611962
metric_alarms = cw_client.describe_alarms().get("MetricAlarms", [])
19621963
composite_alarms = cw_client.describe_alarms().get("CompositeAlarms", [])
19631964

1964-
# deploy stack
1965-
create_and_await_stack(StackName=stack_name, TemplateBody=template)
1966-
exports = cfn_client.list_exports()["Exports"]
1965+
def _assert_resources_created():
1966+
exports = cfn_client.list_exports()["Exports"]
1967+
# TODO: we should use named stack outputs/exports here, to make tests parallelizable!
1968+
subnets = [export for export in exports if export["Name"] == "public-sn-a"]
1969+
instances = [export for export in exports if export["Name"] == "RegmonEc2InstanceId"]
19671970

1968-
subnets = [export for export in exports if export["Name"] == "public-sn-a"]
1969-
instances = [export for export in exports if export["Name"] == "RegmonEc2InstanceId"]
1971+
self.assertEqual(1, len(subnets))
1972+
self.assertEqual(1, len(instances))
19701973

1971-
self.assertEqual(1, len(subnets))
1972-
self.assertEqual(1, len(instances))
1974+
subnet_id = subnets[0]["Value"]
1975+
instance_id = instances[0]["Value"]
19731976

1974-
subnet_id = subnets[0]["Value"]
1975-
instance_id = instances[0]["Value"]
1977+
ec2_client = aws_stack.connect_to_service("ec2")
1978+
resp = ec2_client.describe_subnets(SubnetIds=[subnet_id])
1979+
self.assertEqual(1, len(resp["Subnets"]))
19761980

1977-
ec2_client = aws_stack.connect_to_service("ec2")
1978-
resp = ec2_client.describe_subnets(SubnetIds=[subnet_id])
1979-
self.assertEqual(1, len(resp["Subnets"]))
1981+
resp = ec2_client.describe_instances(InstanceIds=[instance_id])
1982+
self.assertEqual(1, len(resp["Reservations"][0]["Instances"]))
19801983

1981-
resp = ec2_client.describe_instances(InstanceIds=[instance_id])
1982-
self.assertEqual(1, len(resp["Reservations"][0]["Instances"]))
1984+
# assert creation of further resources
1985+
resp = sns_client.list_topics()
1986+
topic_arns = [tp["TopicArn"] for tp in resp["Topics"]]
1987+
self.assertIn(aws_stack.sns_topic_arn("companyname-slack-topic"), topic_arns)
1988+
metric_alarms_after = cw_client.describe_alarms().get("MetricAlarms", [])
1989+
composite_alarms_after = cw_client.describe_alarms().get("CompositeAlarms", [])
1990+
self.assertEqual(len(metric_alarms) + 1, len(metric_alarms_after))
1991+
self.assertEqual(len(composite_alarms) + 1, len(composite_alarms_after))
19831992

1984-
# assert creation of further resources
1985-
resp = sns_client.list_topics()
1986-
topic_arns = [tp["TopicArn"] for tp in resp["Topics"]]
1987-
self.assertIn(aws_stack.sns_topic_arn("companyname-slack-topic"), topic_arns)
1988-
metric_alarms_after = cw_client.describe_alarms().get("MetricAlarms", [])
1989-
composite_alarms_after = cw_client.describe_alarms().get("CompositeAlarms", [])
1990-
self.assertEqual(len(metric_alarms) + 1, len(metric_alarms_after))
1991-
self.assertEqual(len(composite_alarms) + 1, len(composite_alarms_after))
1993+
iam_client = aws_stack.connect_to_service("iam")
1994+
profiles = iam_client.list_instance_profiles().get("InstanceProfiles", [])
1995+
assert len(profiles) > 0
1996+
profile = profiles[0]
1997+
assert len(profile["Roles"]) > 0
19921998

1993-
iam_client = aws_stack.connect_to_service("iam")
1994-
profiles = iam_client.list_instance_profiles().get("InstanceProfiles", [])
1995-
assert len(profiles) > 0
1996-
profile = profiles[0]
1997-
assert len(profile["Roles"]) > 0
1999+
# deploy stack
2000+
create_and_await_stack(StackName=stack_name, TemplateBody=template)
2001+
_assert_resources_created()
2002+
2003+
# update stack
2004+
update_and_await_stack(stack_name, TemplateBody=template)
2005+
_assert_resources_created()
19982006

19992007
# clean up
20002008
self.cleanup(stack_name)

0 commit comments

Comments
 (0)