Skip to content

Commit 249dc32

Browse files
authored
fix: Add the property UseAliasAsEventTarget to AWS::Serverless::StateMachine and make associated events use the state machine alias as a target. (#3627)
1 parent 174f42a commit 249dc32

12 files changed

+1388
-6
lines changed

DEVELOPMENT_GUIDE.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ We format our code using [Black](https://github.com/python/black) and verify the
5959
during PR checks. Black will be installed automatically with `make init`.
6060

6161
After installing, you can run our formatting through our Makefile by `make format` or integrating Black directly in your favorite IDE (instructions
62-
can be found [here](https://black.readthedocs.io/en/stable/editor_integration.html))
62+
can be found [here](https://black.readthedocs.io/en/stable/integrations/editors.html))
6363

6464
##### (Workaround) Integrating Black directly in your favorite IDE
65-
Since black is installed in virtualenv, when you follow [this instruction](https://black.readthedocs.io/en/stable/editor_integration.html), `which black` might give you this
65+
Since black is installed in virtualenv, when you follow [this instruction](https://black.readthedocs.io/en/stable/integrations/editors.html), `which black` might give you this
6666

6767
```bash
6868
(sam38) $ where black

bin/add_transform_test.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,13 @@ def get_input_file_path() -> Path:
6868

6969

7070
def copy_input_file_to_transform_test_dir(input_file_path: Path, transform_test_input_path: Path) -> None:
71-
shutil.copyfile(input_file_path, transform_test_input_path)
72-
print(f"Transform Test input file generated {transform_test_input_path}")
71+
try:
72+
shutil.copyfile(input_file_path, transform_test_input_path)
73+
print(f"Transform Test input file generated {transform_test_input_path}")
74+
except shutil.SameFileError:
75+
print(f"Source and destination are the same file: {input_file_path}")
76+
except Exception as e:
77+
raise e
7378

7479

7580
def verify_input_template(input_file_path: Path) -> None:
@@ -99,7 +104,9 @@ def main() -> None:
99104
verify_input_template(input_file_path)
100105

101106
transform_test_input_path = TRANSFORM_TEST_DIR / "input" / (file_basename + ".yaml")
102-
copy_input_file_to_transform_test_dir(input_file_path, transform_test_input_path)
107+
# check if the template is not already in the transform test input dir
108+
if input_file_path != transform_test_input_path:
109+
copy_input_file_to_transform_test_dir(input_file_path, transform_test_input_path)
103110

104111
generate_transform_test_output_files(transform_test_input_path, file_basename)
105112

samtranslator/internal/schema_source/aws_serverless_statemachine.py

+1
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ class Properties(BaseModel):
174174
Type: Optional[PassThroughProp] = properties("Type")
175175
AutoPublishAlias: Optional[PassThroughProp]
176176
DeploymentPreference: Optional[PassThroughProp]
177+
UseAliasAsEventTarget: Optional[bool]
177178

178179

179180
class Resource(ResourceAttributes):

samtranslator/model/sam_resources.py

+3
Original file line numberDiff line numberDiff line change
@@ -1778,6 +1778,7 @@ class SamStateMachine(SamResourceMacro):
17781778
"PermissionsBoundary": PropertyType(False, IS_STR),
17791779
"AutoPublishAlias": PassThroughProperty(False),
17801780
"DeploymentPreference": MutatedPassThroughProperty(False),
1781+
"UseAliasAsEventTarget": Property(False, IS_BOOL),
17811782
}
17821783

17831784
Definition: Optional[Dict[str, Any]]
@@ -1796,6 +1797,7 @@ class SamStateMachine(SamResourceMacro):
17961797
PermissionsBoundary: Optional[Intrinsicable[str]]
17971798
AutoPublishAlias: Optional[PassThrough]
17981799
DeploymentPreference: Optional[PassThrough]
1800+
UseAliasAsEventTarget: Optional[bool]
17991801

18001802
event_resolver = ResourceTypeResolver(
18011803
samtranslator.model.stepfunctions.events,
@@ -1834,6 +1836,7 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def]
18341836
get_managed_policy_map=get_managed_policy_map,
18351837
auto_publish_alias=self.AutoPublishAlias,
18361838
deployment_preference=self.DeploymentPreference,
1839+
use_alias_as_event_target=self.UseAliasAsEventTarget,
18371840
)
18381841

18391842
generated_resources = state_machine_generator.to_cloudformation()

samtranslator/model/stepfunctions/generators.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def __init__( # type: ignore[no-untyped-def] # noqa: PLR0913
5454
get_managed_policy_map=None,
5555
auto_publish_alias=None,
5656
deployment_preference=None,
57+
use_alias_as_event_target=None,
5758
):
5859
"""
5960
Constructs an State Machine Generator class that generates a State Machine resource
@@ -81,6 +82,7 @@ def __init__( # type: ignore[no-untyped-def] # noqa: PLR0913
8182
:param passthrough_resource_attributes: Attributes such as `Condition` that are added to derived resources
8283
:param auto_publish_alias: Name of the state machine alias to automatically create and update
8384
:deployment_preference: Settings to enable gradual state machine deployments
85+
:param use_alias_as_event_target: Whether to use the state machine alias as the event target
8486
"""
8587
self.logical_id = logical_id
8688
self.depends_on = depends_on
@@ -110,6 +112,7 @@ def __init__( # type: ignore[no-untyped-def] # noqa: PLR0913
110112
self.get_managed_policy_map = get_managed_policy_map
111113
self.auto_publish_alias = auto_publish_alias
112114
self.deployment_preference = deployment_preference
115+
self.use_alias_as_event_target = use_alias_as_event_target
113116

114117
@cw_timer(prefix="Generator", name="StateMachine")
115118
def to_cloudformation(self): # type: ignore[no-untyped-def]
@@ -300,6 +303,8 @@ def _construct_alias(self, version: StepFunctionsStateMachineVersion) -> StepFun
300303
deployment_preference["StateMachineVersionArn"] = state_machine_version_arn
301304
state_machine_alias.DeploymentPreference = deployment_preference
302305

306+
self.state_machine_alias = state_machine_alias
307+
303308
return state_machine_alias
304309

305310
def _generate_managed_traffic_shifting_resources(
@@ -310,6 +315,10 @@ def _generate_managed_traffic_shifting_resources(
310315
:returns: a list containing the state machine's version and alias resources
311316
:rtype: list
312317
"""
318+
if not self.auto_publish_alias and self.use_alias_as_event_target:
319+
raise InvalidResourceException(
320+
self.logical_id, "'UseAliasAsEventTarget' requires 'AutoPublishAlias' property to be specified."
321+
)
313322
if not self.auto_publish_alias and not self.deployment_preference:
314323
return []
315324
if not self.auto_publish_alias and self.deployment_preference:
@@ -341,7 +350,12 @@ def _generate_event_resources(self) -> List[Dict[str, Any]]:
341350
kwargs[name] = resource
342351
except (TypeError, AttributeError) as e:
343352
raise InvalidEventException(logical_id, str(e)) from e
344-
resources += eventsource.to_cloudformation(resource=self.state_machine, **kwargs)
353+
target_resource = (
354+
(self.state_machine_alias or self.state_machine)
355+
if self.use_alias_as_event_target
356+
else self.state_machine
357+
)
358+
resources += eventsource.to_cloudformation(resource=target_resource, **kwargs)
345359

346360
return resources
347361

samtranslator/schema/schema.json

+4
Original file line numberDiff line numberDiff line change
@@ -281000,6 +281000,10 @@
281000281000
],
281001281001
"markdownDescription": "The type of the state machine\\. \n*Valid values*: `STANDARD` or `EXPRESS` \n*Type*: String \n*Required*: No \n*Default*: `STANDARD` \n*AWS CloudFormation compatibility*: This property is passed directly to the [`StateMachineType`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-stepfunctions-statemachine.html#cfn-stepfunctions-statemachine-statemachinetype) property of an `AWS::StepFunctions::StateMachine` resource\\.",
281002281002
"title": "Type"
281003+
},
281004+
"UseAliasAsEventTarget": {
281005+
"title": "Usealiasaseventtarget",
281006+
"type": "boolean"
281003281007
}
281004281008
},
281005281009
"title": "Properties",

schema_source/sam.schema.json

+4
Original file line numberDiff line numberDiff line change
@@ -8020,6 +8020,10 @@
80208020
],
80218021
"markdownDescription": "The type of the state machine\\. \n*Valid values*: `STANDARD` or `EXPRESS` \n*Type*: String \n*Required*: No \n*Default*: `STANDARD` \n*AWS CloudFormation compatibility*: This property is passed directly to the [`StateMachineType`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-stepfunctions-statemachine.html#cfn-stepfunctions-statemachine-statemachinetype) property of an `AWS::StepFunctions::StateMachine` resource\\.",
80228022
"title": "Type"
8023+
},
8024+
"UseAliasAsEventTarget": {
8025+
"title": "Usealiasaseventtarget",
8026+
"type": "boolean"
80238027
}
80248028
},
80258029
"title": "Properties",

tests/model/stepfunctions/test_state_machine_generator.py

+31
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,37 @@ def test_state_machine_with_unsupported_event_source(self):
148148
with self.assertRaises(InvalidEventException):
149149
StateMachineGenerator(**self.kwargs).to_cloudformation()
150150

151+
def test_state_machine_with_alias_as_event_source_target(self):
152+
self.kwargs["definition_uri"] = "s3://mybucket/myASLfile"
153+
self.kwargs["role"] = "my-test-role-arn"
154+
self.kwargs["use_alias_as_event_target"] = True
155+
self.kwargs["auto_publish_alias"] = "live"
156+
event_resolver = Mock()
157+
event_resolver.resolve_resource_type = Mock(return_value=CloudWatchEvent)
158+
self.kwargs["event_resolver"] = event_resolver
159+
self.kwargs["events"] = {
160+
"CWEEvent": {"Type": "CloudWatchEvent", "Properties": {"Pattern": {"detail": {"state": ["terminated"]}}}}
161+
}
162+
self.kwargs["event_resources"] = {"CWEEvent": {}}
163+
state_machine_generator = StateMachineGenerator(**self.kwargs)
164+
state_machine_generator._generate_managed_traffic_shifting_resources()
165+
generated_event_resources = state_machine_generator._generate_event_resources()
166+
self.assertEqual(generated_event_resources[0].Targets[0]["Arn"], {"Ref": "StateMachineIdAliaslive"})
167+
168+
def test_state_machine_with_alias_as_event_source_target_requires_alias(self):
169+
self.kwargs["definition_uri"] = "s3://mybucket/myASLfile"
170+
self.kwargs["role"] = "my-test-role-arn"
171+
self.kwargs["use_alias_as_event_target"] = True
172+
self.kwargs["deployment_preference"] = {"Type": "ALL_AT_ONCE"}
173+
# Missing property
174+
# self.kwargs["auto_publish_alias"] = "live"
175+
with self.assertRaises(InvalidResourceException) as error:
176+
StateMachineGenerator(**self.kwargs).to_cloudformation()
177+
self.assertEqual(
178+
error.exception.message,
179+
"Resource with id [StateMachineId] is invalid. 'UseAliasAsEventTarget' requires 'AutoPublishAlias' property to be specified.",
180+
)
181+
151182
def test_state_machine_with_managed_traffic_shifting_properties(self):
152183
self.kwargs["definition_uri"] = "s3://mybucket/myASLfile"
153184
self.kwargs["role"] = "my-test-role-arn"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
Transform: AWS::Serverless-2016-10-31
2+
Resources:
3+
MyStateMachine:
4+
Type: AWS::Serverless::StateMachine
5+
Properties:
6+
Type: STANDARD
7+
Definition:
8+
StartAt: HelloWorld
9+
States:
10+
HelloWorld:
11+
Type: Pass
12+
Result: 1
13+
End: true
14+
Role: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/doesNotExist"
15+
AutoPublishAlias: test
16+
UseAliasAsEventTarget: true
17+
Events:
18+
CWEvent:
19+
Type: CloudWatchEvent
20+
Properties:
21+
Pattern:
22+
detail:
23+
state:
24+
- terminated
25+
EBEvent:
26+
Type: EventBridgeRule
27+
Properties:
28+
Pattern:
29+
source: [aws.tag]
30+
ApiEvent:
31+
Type: Api
32+
Properties:
33+
Path: /path
34+
Method: get
35+
CWSchedule:
36+
Type: Schedule
37+
Properties:
38+
Schedule: rate(1 minute)
39+
Name: TestSchedule
40+
Description: test schedule
41+
Enabled: false
42+
ScheduleEvent:
43+
Type: ScheduleV2
44+
Properties:
45+
ScheduleExpression: rate(1 minute)

0 commit comments

Comments
 (0)