Skip to content

Commit d5914bf

Browse files
authored
CFn: validate during get template (#13139)
1 parent 3fdbb29 commit d5914bf

File tree

4 files changed

+147
-0
lines changed

4 files changed

+147
-0
lines changed

localstack-core/localstack/services/cloudformation/v2/provider.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,10 +1295,19 @@ def get_template(
12951295
) -> GetTemplateOutput:
12961296
state = get_cloudformation_store(context.account_id, context.region)
12971297
if change_set_name:
1298+
if not is_changeset_arn(change_set_name) and not stack_name:
1299+
raise ValidationError("StackName is a required parameter.")
1300+
12981301
change_set = find_change_set_v2(state, change_set_name, stack_name=stack_name)
1302+
if not change_set:
1303+
raise ChangeSetNotFoundException(f"ChangeSet [{change_set_name}] does not exist")
12991304
stack = change_set.stack
13001305
elif stack_name:
13011306
stack = find_stack_v2(state, stack_name)
1307+
if not stack:
1308+
raise StackNotFoundError(
1309+
stack_name, message_override=f"Stack with id {stack_name} does not exist"
1310+
)
13021311
else:
13031312
raise StackNotFoundError(stack_name)
13041313

tests/aws/services/cloudformation/api/test_templates.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,30 @@ def test_validate_invalid_json_template_should_fail(aws_client, snapshot):
152152
aws_client.cloudformation.validate_template(TemplateBody=invalid_json)
153153

154154
snapshot.match("validate-invalid-json", ctx.value.response)
155+
156+
157+
@markers.aws.validated
158+
def test_get_template_missing_resources_stack(aws_client, snapshot):
159+
with pytest.raises(ClientError) as exc_info:
160+
aws_client.cloudformation.get_template(StackName="does-not-exist")
161+
snapshot.match("stack-error", exc_info.value.response)
162+
163+
164+
@skip_if_v1_provider("Not supported in legacy engine")
165+
@markers.aws.validated
166+
def test_get_template_missing_resources_change_set(aws_client, snapshot):
167+
with pytest.raises(ClientError) as exc_info:
168+
aws_client.cloudformation.get_template(ChangeSetName="does-not-exist")
169+
snapshot.match("change-set-error", exc_info.value.response)
170+
171+
172+
@skip_if_v1_provider("Not supported in legacy engine")
173+
@markers.aws.validated
174+
def test_get_template_missing_resources_change_set_id(aws_client, snapshot):
175+
change_set_id = (
176+
"arn:aws:cloudformation:us-east-1:000000000000:changeSet/change-set-926829fe/d065e78c"
177+
)
178+
snapshot.add_transformer(snapshot.transform.regex(change_set_id, "<change-set-id>"))
179+
with pytest.raises(ClientError) as exc_info:
180+
aws_client.cloudformation.get_template(ChangeSetName=change_set_id)
181+
snapshot.match("change-set-error", exc_info.value.response)

tests/aws/services/cloudformation/api/test_templates.snapshot.json

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,80 @@
125125
}
126126
}
127127
}
128+
},
129+
"tests/aws/services/cloudformation/api/test_templates.py::test_get_template_missing_resources": {
130+
"recorded-date": "12-09-2025, 16:08:17",
131+
"recorded-content": {
132+
"stack-error": {
133+
"Error": {
134+
"Code": "ValidationError",
135+
"Message": "Stack with id does-not-exist does not exist",
136+
"Type": "Sender"
137+
},
138+
"ResponseMetadata": {
139+
"HTTPHeaders": {},
140+
"HTTPStatusCode": 400
141+
}
142+
},
143+
"change-set-error": {
144+
"Error": {
145+
"Code": "ValidationError",
146+
"Message": "StackName is a required parameter.",
147+
"Type": "Sender"
148+
},
149+
"ResponseMetadata": {
150+
"HTTPHeaders": {},
151+
"HTTPStatusCode": 400
152+
}
153+
}
154+
}
155+
},
156+
"tests/aws/services/cloudformation/api/test_templates.py::test_get_template_missing_resources_stack": {
157+
"recorded-date": "12-09-2025, 16:08:42",
158+
"recorded-content": {
159+
"stack-error": {
160+
"Error": {
161+
"Code": "ValidationError",
162+
"Message": "Stack with id does-not-exist does not exist",
163+
"Type": "Sender"
164+
},
165+
"ResponseMetadata": {
166+
"HTTPHeaders": {},
167+
"HTTPStatusCode": 400
168+
}
169+
}
170+
}
171+
},
172+
"tests/aws/services/cloudformation/api/test_templates.py::test_get_template_missing_resources_change_set": {
173+
"recorded-date": "12-09-2025, 16:08:55",
174+
"recorded-content": {
175+
"change-set-error": {
176+
"Error": {
177+
"Code": "ValidationError",
178+
"Message": "StackName is a required parameter.",
179+
"Type": "Sender"
180+
},
181+
"ResponseMetadata": {
182+
"HTTPHeaders": {},
183+
"HTTPStatusCode": 400
184+
}
185+
}
186+
}
187+
},
188+
"tests/aws/services/cloudformation/api/test_templates.py::test_get_template_missing_resources_change_set_id": {
189+
"recorded-date": "12-09-2025, 17:32:31",
190+
"recorded-content": {
191+
"change-set-error": {
192+
"Error": {
193+
"Code": "ChangeSetNotFound",
194+
"Message": "ChangeSet [<change-set-id>] does not exist",
195+
"Type": "Sender"
196+
},
197+
"ResponseMetadata": {
198+
"HTTPHeaders": {},
199+
"HTTPStatusCode": 404
200+
}
201+
}
202+
}
128203
}
129204
}

tests/aws/services/cloudformation/api/test_templates.validation.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,42 @@
1111
"tests/aws/services/cloudformation/api/test_templates.py::test_create_stack_from_s3_template_url[s3_url]": {
1212
"last_validated_date": "2023-10-10T22:03:44+00:00"
1313
},
14+
"tests/aws/services/cloudformation/api/test_templates.py::test_get_template_missing_resources": {
15+
"last_validated_date": "2025-09-12T16:08:17+00:00",
16+
"durations_in_seconds": {
17+
"setup": 0.81,
18+
"call": 0.23,
19+
"teardown": 0.0,
20+
"total": 1.04
21+
}
22+
},
23+
"tests/aws/services/cloudformation/api/test_templates.py::test_get_template_missing_resources_change_set": {
24+
"last_validated_date": "2025-09-12T16:08:55+00:00",
25+
"durations_in_seconds": {
26+
"setup": 0.73,
27+
"call": 0.18,
28+
"teardown": 0.0,
29+
"total": 0.91
30+
}
31+
},
32+
"tests/aws/services/cloudformation/api/test_templates.py::test_get_template_missing_resources_change_set_id": {
33+
"last_validated_date": "2025-09-12T17:32:31+00:00",
34+
"durations_in_seconds": {
35+
"setup": 0.94,
36+
"call": 0.22,
37+
"teardown": 0.0,
38+
"total": 1.16
39+
}
40+
},
41+
"tests/aws/services/cloudformation/api/test_templates.py::test_get_template_missing_resources_stack": {
42+
"last_validated_date": "2025-09-12T16:08:42+00:00",
43+
"durations_in_seconds": {
44+
"setup": 0.99,
45+
"call": 0.2,
46+
"teardown": 0.0,
47+
"total": 1.19
48+
}
49+
},
1450
"tests/aws/services/cloudformation/api/test_templates.py::test_get_template_summary": {
1551
"last_validated_date": "2023-05-24T13:05:00+00:00"
1652
},

0 commit comments

Comments
 (0)