| 
									
										
										
										
											2021-02-17 15:06:31 +00:00
										 |  |  | import boto3 | 
					
						
							|  |  |  | import json | 
					
						
							| 
									
										
										
										
											2021-10-18 19:44:29 +00:00
										 |  |  | import sure  # noqa # pylint: disable=unused-import | 
					
						
							| 
									
										
										
										
											2021-02-17 15:06:31 +00:00
										 |  |  | from botocore.exceptions import ClientError | 
					
						
							|  |  |  | import pytest | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from moto import mock_cloudformation, mock_stepfunctions | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_stepfunctions | 
					
						
							|  |  |  | @mock_cloudformation | 
					
						
							|  |  |  | def test_state_machine_cloudformation(): | 
					
						
							|  |  |  |     sf = boto3.client("stepfunctions", region_name="us-east-1") | 
					
						
							|  |  |  |     cf = boto3.resource("cloudformation", region_name="us-east-1") | 
					
						
							|  |  |  |     definition = '{"StartAt": "HelloWorld", "States": {"HelloWorld": {"Type": "Task", "Resource": "arn:aws:lambda:us-east-1:111122223333;:function:HelloFunction", "End": true}}}' | 
					
						
							|  |  |  |     role_arn = ( | 
					
						
							|  |  |  |         "arn:aws:iam::111122223333:role/service-role/StatesExecutionRole-us-east-1;" | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     template = { | 
					
						
							|  |  |  |         "AWSTemplateFormatVersion": "2010-09-09", | 
					
						
							|  |  |  |         "Description": "An example template for a Step Functions state machine.", | 
					
						
							|  |  |  |         "Resources": { | 
					
						
							|  |  |  |             "MyStateMachine": { | 
					
						
							|  |  |  |                 "Type": "AWS::StepFunctions::StateMachine", | 
					
						
							|  |  |  |                 "Properties": { | 
					
						
							|  |  |  |                     "StateMachineName": "HelloWorld-StateMachine", | 
					
						
							|  |  |  |                     "StateMachineType": "STANDARD", | 
					
						
							|  |  |  |                     "DefinitionString": definition, | 
					
						
							|  |  |  |                     "RoleArn": role_arn, | 
					
						
							|  |  |  |                     "Tags": [ | 
					
						
							|  |  |  |                         {"Key": "key1", "Value": "value1"}, | 
					
						
							|  |  |  |                         {"Key": "key2", "Value": "value2"}, | 
					
						
							|  |  |  |                     ], | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "Outputs": { | 
					
						
							|  |  |  |             "StateMachineArn": {"Value": {"Ref": "MyStateMachine"}}, | 
					
						
							|  |  |  |             "StateMachineName": {"Value": {"Fn::GetAtt": ["MyStateMachine", "Name"]}}, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     cf.create_stack(StackName="test_stack", TemplateBody=json.dumps(template)) | 
					
						
							|  |  |  |     outputs_list = cf.Stack("test_stack").outputs | 
					
						
							|  |  |  |     output = {item["OutputKey"]: item["OutputValue"] for item in outputs_list} | 
					
						
							|  |  |  |     state_machine = sf.describe_state_machine(stateMachineArn=output["StateMachineArn"]) | 
					
						
							|  |  |  |     state_machine["stateMachineArn"].should.equal(output["StateMachineArn"]) | 
					
						
							|  |  |  |     state_machine["name"].should.equal(output["StateMachineName"]) | 
					
						
							|  |  |  |     state_machine["roleArn"].should.equal(role_arn) | 
					
						
							|  |  |  |     state_machine["definition"].should.equal(definition) | 
					
						
							|  |  |  |     tags = sf.list_tags_for_resource(resourceArn=output["StateMachineArn"]).get("tags") | 
					
						
							|  |  |  |     for i, tag in enumerate(tags, 1): | 
					
						
							| 
									
										
										
										
											2022-11-17 21:41:08 -01:00
										 |  |  |         tag["key"].should.equal(f"key{i}") | 
					
						
							|  |  |  |         tag["value"].should.equal(f"value{i}") | 
					
						
							| 
									
										
										
										
											2021-02-17 15:06:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     cf.Stack("test_stack").delete() | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as ex: | 
					
						
							|  |  |  |         sf.describe_state_machine(stateMachineArn=output["StateMachineArn"]) | 
					
						
							|  |  |  |     ex.value.response["Error"]["Code"].should.equal("StateMachineDoesNotExist") | 
					
						
							|  |  |  |     ex.value.response["Error"]["Message"].should.contain("Does Not Exist") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_stepfunctions | 
					
						
							|  |  |  | @mock_cloudformation | 
					
						
							|  |  |  | def test_state_machine_cloudformation_update_with_replacement(): | 
					
						
							|  |  |  |     sf = boto3.client("stepfunctions", region_name="us-east-1") | 
					
						
							|  |  |  |     cf = boto3.resource("cloudformation", region_name="us-east-1") | 
					
						
							|  |  |  |     definition = '{"StartAt": "HelloWorld", "States": {"HelloWorld": {"Type": "Task", "Resource": "arn:aws:lambda:us-east-1:111122223333;:function:HelloFunction", "End": true}}}' | 
					
						
							|  |  |  |     role_arn = ( | 
					
						
							|  |  |  |         "arn:aws:iam::111122223333:role/service-role/StatesExecutionRole-us-east-1" | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     properties = { | 
					
						
							|  |  |  |         "StateMachineName": "HelloWorld-StateMachine", | 
					
						
							|  |  |  |         "DefinitionString": definition, | 
					
						
							|  |  |  |         "RoleArn": role_arn, | 
					
						
							|  |  |  |         "Tags": [ | 
					
						
							|  |  |  |             {"Key": "key1", "Value": "value1"}, | 
					
						
							|  |  |  |             {"Key": "key2", "Value": "value2"}, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     template = { | 
					
						
							|  |  |  |         "AWSTemplateFormatVersion": "2010-09-09", | 
					
						
							|  |  |  |         "Description": "An example template for a Step Functions state machine.", | 
					
						
							|  |  |  |         "Resources": { | 
					
						
							|  |  |  |             "MyStateMachine": { | 
					
						
							|  |  |  |                 "Type": "AWS::StepFunctions::StateMachine", | 
					
						
							|  |  |  |                 "Properties": {}, | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "Outputs": { | 
					
						
							|  |  |  |             "StateMachineArn": {"Value": {"Ref": "MyStateMachine"}}, | 
					
						
							|  |  |  |             "StateMachineName": {"Value": {"Fn::GetAtt": ["MyStateMachine", "Name"]}}, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     template["Resources"]["MyStateMachine"]["Properties"] = properties | 
					
						
							|  |  |  |     cf.create_stack(StackName="test_stack", TemplateBody=json.dumps(template)) | 
					
						
							|  |  |  |     outputs_list = cf.Stack("test_stack").outputs | 
					
						
							|  |  |  |     output = {item["OutputKey"]: item["OutputValue"] for item in outputs_list} | 
					
						
							|  |  |  |     state_machine = sf.describe_state_machine(stateMachineArn=output["StateMachineArn"]) | 
					
						
							|  |  |  |     original_machine_arn = state_machine["stateMachineArn"] | 
					
						
							|  |  |  |     original_creation_date = state_machine["creationDate"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Update State Machine, with replacement. | 
					
						
							|  |  |  |     updated_role = role_arn + "-updated" | 
					
						
							|  |  |  |     updated_definition = definition.replace("HelloWorld", "HelloWorld2") | 
					
						
							|  |  |  |     updated_properties = { | 
					
						
							|  |  |  |         "StateMachineName": "New-StateMachine-Name", | 
					
						
							|  |  |  |         "DefinitionString": updated_definition, | 
					
						
							|  |  |  |         "RoleArn": updated_role, | 
					
						
							|  |  |  |         "Tags": [ | 
					
						
							|  |  |  |             {"Key": "key3", "Value": "value3"}, | 
					
						
							|  |  |  |             {"Key": "key1", "Value": "updated_value"}, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     template["Resources"]["MyStateMachine"]["Properties"] = updated_properties | 
					
						
							|  |  |  |     cf.Stack("test_stack").update(TemplateBody=json.dumps(template)) | 
					
						
							|  |  |  |     outputs_list = cf.Stack("test_stack").outputs | 
					
						
							|  |  |  |     output = {item["OutputKey"]: item["OutputValue"] for item in outputs_list} | 
					
						
							|  |  |  |     state_machine = sf.describe_state_machine(stateMachineArn=output["StateMachineArn"]) | 
					
						
							|  |  |  |     state_machine["stateMachineArn"].should_not.equal(original_machine_arn) | 
					
						
							|  |  |  |     state_machine["name"].should.equal("New-StateMachine-Name") | 
					
						
							|  |  |  |     state_machine["creationDate"].should.be.greater_than(original_creation_date) | 
					
						
							|  |  |  |     state_machine["roleArn"].should.equal(updated_role) | 
					
						
							|  |  |  |     state_machine["definition"].should.equal(updated_definition) | 
					
						
							|  |  |  |     tags = sf.list_tags_for_resource(resourceArn=output["StateMachineArn"]).get("tags") | 
					
						
							|  |  |  |     tags.should.have.length_of(3) | 
					
						
							|  |  |  |     for tag in tags: | 
					
						
							|  |  |  |         if tag["key"] == "key1": | 
					
						
							|  |  |  |             tag["value"].should.equal("updated_value") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as ex: | 
					
						
							|  |  |  |         sf.describe_state_machine(stateMachineArn=original_machine_arn) | 
					
						
							|  |  |  |     ex.value.response["Error"]["Code"].should.equal("StateMachineDoesNotExist") | 
					
						
							|  |  |  |     ex.value.response["Error"]["Message"].should.contain("State Machine Does Not Exist") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_stepfunctions | 
					
						
							|  |  |  | @mock_cloudformation | 
					
						
							|  |  |  | def test_state_machine_cloudformation_update_with_no_interruption(): | 
					
						
							|  |  |  |     sf = boto3.client("stepfunctions", region_name="us-east-1") | 
					
						
							|  |  |  |     cf = boto3.resource("cloudformation", region_name="us-east-1") | 
					
						
							|  |  |  |     definition = '{"StartAt": "HelloWorld", "States": {"HelloWorld": {"Type": "Task", "Resource": "arn:aws:lambda:us-east-1:111122223333;:function:HelloFunction", "End": true}}}' | 
					
						
							|  |  |  |     role_arn = ( | 
					
						
							|  |  |  |         "arn:aws:iam::111122223333:role/service-role/StatesExecutionRole-us-east-1" | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     properties = { | 
					
						
							|  |  |  |         "StateMachineName": "HelloWorld-StateMachine", | 
					
						
							|  |  |  |         "DefinitionString": definition, | 
					
						
							|  |  |  |         "RoleArn": role_arn, | 
					
						
							|  |  |  |         "Tags": [ | 
					
						
							|  |  |  |             {"Key": "key1", "Value": "value1"}, | 
					
						
							|  |  |  |             {"Key": "key2", "Value": "value2"}, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     template = { | 
					
						
							|  |  |  |         "AWSTemplateFormatVersion": "2010-09-09", | 
					
						
							|  |  |  |         "Description": "An example template for a Step Functions state machine.", | 
					
						
							|  |  |  |         "Resources": { | 
					
						
							|  |  |  |             "MyStateMachine": { | 
					
						
							|  |  |  |                 "Type": "AWS::StepFunctions::StateMachine", | 
					
						
							|  |  |  |                 "Properties": {}, | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "Outputs": { | 
					
						
							|  |  |  |             "StateMachineArn": {"Value": {"Ref": "MyStateMachine"}}, | 
					
						
							|  |  |  |             "StateMachineName": {"Value": {"Fn::GetAtt": ["MyStateMachine", "Name"]}}, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     template["Resources"]["MyStateMachine"]["Properties"] = properties | 
					
						
							|  |  |  |     cf.create_stack(StackName="test_stack", TemplateBody=json.dumps(template)) | 
					
						
							|  |  |  |     outputs_list = cf.Stack("test_stack").outputs | 
					
						
							|  |  |  |     output = {item["OutputKey"]: item["OutputValue"] for item in outputs_list} | 
					
						
							|  |  |  |     state_machine = sf.describe_state_machine(stateMachineArn=output["StateMachineArn"]) | 
					
						
							|  |  |  |     machine_arn = state_machine["stateMachineArn"] | 
					
						
							|  |  |  |     creation_date = state_machine["creationDate"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Update State Machine in-place, no replacement. | 
					
						
							|  |  |  |     updated_role = role_arn + "-updated" | 
					
						
							|  |  |  |     updated_definition = definition.replace("HelloWorld", "HelloWorldUpdated") | 
					
						
							|  |  |  |     updated_properties = { | 
					
						
							|  |  |  |         "DefinitionString": updated_definition, | 
					
						
							|  |  |  |         "RoleArn": updated_role, | 
					
						
							|  |  |  |         "Tags": [ | 
					
						
							|  |  |  |             {"Key": "key3", "Value": "value3"}, | 
					
						
							|  |  |  |             {"Key": "key1", "Value": "updated_value"}, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     template["Resources"]["MyStateMachine"]["Properties"] = updated_properties | 
					
						
							|  |  |  |     cf.Stack("test_stack").update(TemplateBody=json.dumps(template)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     state_machine = sf.describe_state_machine(stateMachineArn=machine_arn) | 
					
						
							|  |  |  |     state_machine["name"].should.equal("HelloWorld-StateMachine") | 
					
						
							|  |  |  |     state_machine["creationDate"].should.equal(creation_date) | 
					
						
							|  |  |  |     state_machine["roleArn"].should.equal(updated_role) | 
					
						
							|  |  |  |     state_machine["definition"].should.equal(updated_definition) | 
					
						
							|  |  |  |     tags = sf.list_tags_for_resource(resourceArn=machine_arn).get("tags") | 
					
						
							|  |  |  |     tags.should.have.length_of(3) | 
					
						
							|  |  |  |     for tag in tags: | 
					
						
							|  |  |  |         if tag["key"] == "key1": | 
					
						
							|  |  |  |             tag["value"].should.equal("updated_value") |