Step Functions - Execution methods
This commit is contained in:
parent
af4082f38e
commit
78254cc4f2
@ -6054,20 +6054,20 @@
|
||||
- [ ] delete_activity
|
||||
- [X] delete_state_machine
|
||||
- [ ] describe_activity
|
||||
- [ ] describe_execution
|
||||
- [X] describe_execution
|
||||
- [X] describe_state_machine
|
||||
- [ ] describe_state_machine_for_execution
|
||||
- [x] describe_state_machine_for_execution
|
||||
- [ ] get_activity_task
|
||||
- [ ] get_execution_history
|
||||
- [ ] list_activities
|
||||
- [ ] list_executions
|
||||
- [X] list_executions
|
||||
- [X] list_state_machines
|
||||
- [X] list_tags_for_resource
|
||||
- [ ] send_task_failure
|
||||
- [ ] send_task_heartbeat
|
||||
- [ ] send_task_success
|
||||
- [ ] start_execution
|
||||
- [ ] stop_execution
|
||||
- [X] start_execution
|
||||
- [X] stop_execution
|
||||
- [ ] tag_resource
|
||||
- [ ] untag_resource
|
||||
- [ ] update_state_machine
|
||||
|
@ -20,6 +20,11 @@ class AccessDeniedException(AWSError):
|
||||
STATUS = 400
|
||||
|
||||
|
||||
class ExecutionDoesNotExist(AWSError):
|
||||
CODE = 'ExecutionDoesNotExist'
|
||||
STATUS = 400
|
||||
|
||||
|
||||
class InvalidArn(AWSError):
|
||||
CODE = 'InvalidArn'
|
||||
STATUS = 400
|
||||
|
@ -4,7 +4,8 @@ import re
|
||||
from datetime import datetime
|
||||
from moto.core import BaseBackend
|
||||
from moto.core.utils import iso_8601_datetime_without_milliseconds
|
||||
from .exceptions import AccessDeniedException, InvalidArn, InvalidName, StateMachineDoesNotExist
|
||||
from uuid import uuid4
|
||||
from .exceptions import AccessDeniedException, ExecutionDoesNotExist, InvalidArn, InvalidName, StateMachineDoesNotExist
|
||||
|
||||
|
||||
class StateMachine():
|
||||
@ -17,6 +18,22 @@ class StateMachine():
|
||||
self.tags = tags
|
||||
|
||||
|
||||
class Execution():
|
||||
def __init__(self, region_name, account_id, state_machine_name, execution_name, state_machine_arn):
|
||||
execution_arn = 'arn:aws:states:{}:{}:execution:{}:{}'
|
||||
execution_arn = execution_arn.format(region_name, account_id, state_machine_name, execution_name)
|
||||
self.execution_arn = execution_arn
|
||||
self.name = execution_name
|
||||
self.start_date = iso_8601_datetime_without_milliseconds(datetime.now())
|
||||
self.state_machine_arn = state_machine_arn
|
||||
self.status = 'RUNNING'
|
||||
self.stop_date = None
|
||||
|
||||
def stop(self):
|
||||
self.status = 'SUCCEEDED'
|
||||
self.stop_date = iso_8601_datetime_without_milliseconds(datetime.now())
|
||||
|
||||
|
||||
class StepFunctionBackend(BaseBackend):
|
||||
|
||||
# https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/stepfunctions.html#SFN.Client.create_state_machine
|
||||
@ -44,9 +61,11 @@ class StepFunctionBackend(BaseBackend):
|
||||
u'\u009A', u'\u009B', u'\u009C', u'\u009D', u'\u009E', u'\u009F']
|
||||
accepted_role_arn_format = re.compile('arn:aws:iam:(?P<account_id>[0-9]{12}):role/.+')
|
||||
accepted_mchn_arn_format = re.compile('arn:aws:states:[-0-9a-zA-Z]+:(?P<account_id>[0-9]{12}):stateMachine:.+')
|
||||
accepted_exec_arn_format = re.compile('arn:aws:states:[-0-9a-zA-Z]+:(?P<account_id>[0-9]{12}):execution:.+')
|
||||
|
||||
def __init__(self, region_name):
|
||||
self.state_machines = []
|
||||
self.executions = []
|
||||
self.region_name = region_name
|
||||
self._account_id = None
|
||||
|
||||
@ -77,6 +96,29 @@ class StepFunctionBackend(BaseBackend):
|
||||
if sm:
|
||||
self.state_machines.remove(sm)
|
||||
|
||||
def start_execution(self, state_machine_arn):
|
||||
state_machine_name = self.describe_state_machine(state_machine_arn).name
|
||||
execution = Execution(region_name=self.region_name, account_id=self._get_account_id(), state_machine_name=state_machine_name, execution_name=str(uuid4()), state_machine_arn=state_machine_arn)
|
||||
self.executions.append(execution)
|
||||
return execution
|
||||
|
||||
def stop_execution(self, execution_arn):
|
||||
execution = next((x for x in self.executions if x.execution_arn == execution_arn), None)
|
||||
if not execution:
|
||||
raise ExecutionDoesNotExist("Execution Does Not Exist: '" + execution_arn + "'")
|
||||
execution.stop()
|
||||
return execution
|
||||
|
||||
def list_executions(self, state_machine_arn):
|
||||
return [execution for execution in self.executions if execution.state_machine_arn == state_machine_arn]
|
||||
|
||||
def describe_execution(self, arn):
|
||||
self._validate_execution_arn(arn)
|
||||
exctn = next((x for x in self.executions if x.execution_arn == arn), None)
|
||||
if not exctn:
|
||||
raise ExecutionDoesNotExist("Execution Does Not Exist: '" + arn + "'")
|
||||
return exctn
|
||||
|
||||
def reset(self):
|
||||
region_name = self.region_name
|
||||
self.__dict__ = {}
|
||||
@ -101,6 +143,12 @@ class StepFunctionBackend(BaseBackend):
|
||||
invalid_msg="Invalid Role Arn: '" + machine_arn + "'",
|
||||
access_denied_msg='User moto is not authorized to access this resource')
|
||||
|
||||
def _validate_execution_arn(self, execution_arn):
|
||||
self._validate_arn(arn=execution_arn,
|
||||
regex=self.accepted_exec_arn_format,
|
||||
invalid_msg="Execution Does Not Exist: '" + execution_arn + "'",
|
||||
access_denied_msg='User moto is not authorized to access this resource')
|
||||
|
||||
def _validate_arn(self, arn, regex, invalid_msg, access_denied_msg):
|
||||
match = regex.match(arn)
|
||||
if not arn or not match:
|
||||
|
@ -45,8 +45,12 @@ class StepFunctionResponse(BaseResponse):
|
||||
@amzn_request_id
|
||||
def describe_state_machine(self):
|
||||
arn = self._get_param('stateMachineArn')
|
||||
return self._describe_state_machine(arn)
|
||||
|
||||
@amzn_request_id
|
||||
def _describe_state_machine(self, state_machine_arn):
|
||||
try:
|
||||
state_machine = self.stepfunction_backend.describe_state_machine(arn)
|
||||
state_machine = self.stepfunction_backend.describe_state_machine(state_machine_arn)
|
||||
response = {
|
||||
'creationDate': state_machine.creation_date,
|
||||
'stateMachineArn': state_machine.arn,
|
||||
@ -78,3 +82,57 @@ class StepFunctionResponse(BaseResponse):
|
||||
tags = []
|
||||
response = {'tags': tags}
|
||||
return 200, {}, json.dumps(response)
|
||||
|
||||
@amzn_request_id
|
||||
def start_execution(self):
|
||||
arn = self._get_param('stateMachineArn')
|
||||
execution = self.stepfunction_backend.start_execution(arn)
|
||||
response = {'executionArn': execution.execution_arn,
|
||||
'startDate': execution.start_date}
|
||||
return 200, {}, json.dumps(response)
|
||||
|
||||
@amzn_request_id
|
||||
def list_executions(self):
|
||||
arn = self._get_param('stateMachineArn')
|
||||
state_machine = self.stepfunction_backend.describe_state_machine(arn)
|
||||
executions = self.stepfunction_backend.list_executions(arn)
|
||||
executions = [{'executionArn': execution.execution_arn,
|
||||
'name': execution.name,
|
||||
'startDate': execution.start_date,
|
||||
'stateMachineArn': state_machine.arn,
|
||||
'status': execution.status} for execution in executions]
|
||||
return 200, {}, json.dumps({'executions': executions})
|
||||
|
||||
@amzn_request_id
|
||||
def describe_execution(self):
|
||||
arn = self._get_param('executionArn')
|
||||
try:
|
||||
execution = self.stepfunction_backend.describe_execution(arn)
|
||||
response = {
|
||||
'executionArn': arn,
|
||||
'input': '{}',
|
||||
'name': execution.name,
|
||||
'startDate': execution.start_date,
|
||||
'stateMachineArn': execution.state_machine_arn,
|
||||
'status': execution.status,
|
||||
'stopDate': execution.stop_date
|
||||
}
|
||||
return 200, {}, json.dumps(response)
|
||||
except AWSError as err:
|
||||
return err.response()
|
||||
|
||||
@amzn_request_id
|
||||
def describe_state_machine_for_execution(self):
|
||||
arn = self._get_param('executionArn')
|
||||
try:
|
||||
execution = self.stepfunction_backend.describe_execution(arn)
|
||||
return self._describe_state_machine(execution.state_machine_arn)
|
||||
except AWSError as err:
|
||||
return err.response()
|
||||
|
||||
@amzn_request_id
|
||||
def stop_execution(self):
|
||||
arn = self._get_param('executionArn')
|
||||
execution = self.stepfunction_backend.stop_execution(arn)
|
||||
response = {'stopDate': execution.stop_date}
|
||||
return 200, {}, json.dumps(response)
|
||||
|
@ -157,7 +157,7 @@ def test_state_machine_creation_is_idempotent_by_name():
|
||||
|
||||
@mock_stepfunctions
|
||||
@mock_sts
|
||||
def test_state_machine_creation_can_be_described_by_name():
|
||||
def test_state_machine_creation_can_be_described():
|
||||
client = boto3.client('stepfunctions', region_name=region)
|
||||
#
|
||||
sm = client.create_state_machine(name='name', definition=str(simple_definition), roleArn=default_stepfunction_role)
|
||||
@ -274,3 +274,139 @@ def test_state_machine_list_tags_for_nonexisting_machine():
|
||||
response = client.list_tags_for_resource(resourceArn=non_existing_state_machine)
|
||||
tags = response['tags']
|
||||
tags.should.have.length_of(0)
|
||||
|
||||
|
||||
@mock_stepfunctions
|
||||
@mock_sts
|
||||
def test_state_machine_start_execution():
|
||||
client = boto3.client('stepfunctions', region_name=region)
|
||||
#
|
||||
sm = client.create_state_machine(name='name', definition=str(simple_definition), roleArn=default_stepfunction_role)
|
||||
execution = client.start_execution(stateMachineArn=sm['stateMachineArn'])
|
||||
#
|
||||
execution['ResponseMetadata']['HTTPStatusCode'].should.equal(200)
|
||||
expected_exec_name = 'arn:aws:states:' + region + ':' + str(DEFAULT_ACCOUNT_ID) + ':execution:name:[a-zA-Z0-9-]+'
|
||||
execution['executionArn'].should.match(expected_exec_name)
|
||||
execution['startDate'].should.be.a(datetime)
|
||||
|
||||
|
||||
@mock_stepfunctions
|
||||
@mock_sts
|
||||
def test_state_machine_list_executions():
|
||||
client = boto3.client('stepfunctions', region_name=region)
|
||||
#
|
||||
sm = client.create_state_machine(name='name', definition=str(simple_definition), roleArn=default_stepfunction_role)
|
||||
execution = client.start_execution(stateMachineArn=sm['stateMachineArn'])
|
||||
execution_arn = execution['executionArn']
|
||||
execution_name = execution_arn[execution_arn.rindex(':')+1:]
|
||||
executions = client.list_executions(stateMachineArn=sm['stateMachineArn'])
|
||||
#
|
||||
executions['ResponseMetadata']['HTTPStatusCode'].should.equal(200)
|
||||
executions['executions'].should.have.length_of(1)
|
||||
executions['executions'][0]['executionArn'].should.equal(execution_arn)
|
||||
executions['executions'][0]['name'].should.equal(execution_name)
|
||||
executions['executions'][0]['startDate'].should.equal(execution['startDate'])
|
||||
executions['executions'][0]['stateMachineArn'].should.equal(sm['stateMachineArn'])
|
||||
executions['executions'][0]['status'].should.equal('RUNNING')
|
||||
executions['executions'][0].shouldnt.have('stopDate')
|
||||
|
||||
|
||||
@mock_stepfunctions
|
||||
@mock_sts
|
||||
def test_state_machine_list_executions_when_none_exist():
|
||||
client = boto3.client('stepfunctions', region_name=region)
|
||||
#
|
||||
sm = client.create_state_machine(name='name', definition=str(simple_definition), roleArn=default_stepfunction_role)
|
||||
executions = client.list_executions(stateMachineArn=sm['stateMachineArn'])
|
||||
#
|
||||
executions['ResponseMetadata']['HTTPStatusCode'].should.equal(200)
|
||||
executions['executions'].should.have.length_of(0)
|
||||
|
||||
|
||||
@mock_stepfunctions
|
||||
@mock_sts
|
||||
def test_state_machine_describe_execution():
|
||||
client = boto3.client('stepfunctions', region_name=region)
|
||||
#
|
||||
sm = client.create_state_machine(name='name', definition=str(simple_definition), roleArn=default_stepfunction_role)
|
||||
execution = client.start_execution(stateMachineArn=sm['stateMachineArn'])
|
||||
description = client.describe_execution(executionArn=execution['executionArn'])
|
||||
#
|
||||
description['ResponseMetadata']['HTTPStatusCode'].should.equal(200)
|
||||
description['executionArn'].should.equal(execution['executionArn'])
|
||||
description['input'].should.equal("{}")
|
||||
description['name'].shouldnt.be.empty
|
||||
description['startDate'].should.equal(execution['startDate'])
|
||||
description['stateMachineArn'].should.equal(sm['stateMachineArn'])
|
||||
description['status'].should.equal('RUNNING')
|
||||
description.shouldnt.have('stopDate')
|
||||
|
||||
|
||||
@mock_stepfunctions
|
||||
@mock_sts
|
||||
def test_state_machine_throws_error_when_describing_unknown_machine():
|
||||
client = boto3.client('stepfunctions', region_name=region)
|
||||
#
|
||||
with assert_raises(ClientError) as exc:
|
||||
unknown_execution = 'arn:aws:states:' + region + ':' + str(DEFAULT_ACCOUNT_ID) + ':execution:unknown'
|
||||
client.describe_execution(executionArn=unknown_execution)
|
||||
exc.exception.response['Error']['Code'].should.equal('ExecutionDoesNotExist')
|
||||
exc.exception.response['Error']['Message'].should.equal("Execution Does Not Exist: '" + unknown_execution + "'")
|
||||
exc.exception.response['ResponseMetadata']['HTTPStatusCode'].should.equal(400)
|
||||
|
||||
|
||||
@mock_stepfunctions
|
||||
@mock_sts
|
||||
def test_state_machine_can_be_described_by_execution():
|
||||
client = boto3.client('stepfunctions', region_name=region)
|
||||
#
|
||||
sm = client.create_state_machine(name='name', definition=str(simple_definition), roleArn=default_stepfunction_role)
|
||||
execution = client.start_execution(stateMachineArn=sm['stateMachineArn'])
|
||||
desc = client.describe_state_machine_for_execution(executionArn=execution['executionArn'])
|
||||
desc['ResponseMetadata']['HTTPStatusCode'].should.equal(200)
|
||||
desc['definition'].should.equal(str(simple_definition))
|
||||
desc['name'].should.equal('name')
|
||||
desc['roleArn'].should.equal(default_stepfunction_role)
|
||||
desc['stateMachineArn'].should.equal(sm['stateMachineArn'])
|
||||
|
||||
|
||||
@mock_stepfunctions
|
||||
@mock_sts
|
||||
def test_state_machine_throws_error_when_describing_unknown_execution():
|
||||
client = boto3.client('stepfunctions', region_name=region)
|
||||
#
|
||||
with assert_raises(ClientError) as exc:
|
||||
unknown_execution = 'arn:aws:states:' + region + ':' + str(DEFAULT_ACCOUNT_ID) + ':execution:unknown'
|
||||
client.describe_state_machine_for_execution(executionArn=unknown_execution)
|
||||
exc.exception.response['Error']['Code'].should.equal('ExecutionDoesNotExist')
|
||||
exc.exception.response['Error']['Message'].should.equal("Execution Does Not Exist: '" + unknown_execution + "'")
|
||||
exc.exception.response['ResponseMetadata']['HTTPStatusCode'].should.equal(400)
|
||||
|
||||
|
||||
@mock_stepfunctions
|
||||
@mock_sts
|
||||
def test_state_machine_stop_execution():
|
||||
client = boto3.client('stepfunctions', region_name=region)
|
||||
#
|
||||
sm = client.create_state_machine(name='name', definition=str(simple_definition), roleArn=default_stepfunction_role)
|
||||
start = client.start_execution(stateMachineArn=sm['stateMachineArn'])
|
||||
stop = client.stop_execution(executionArn=start['executionArn'])
|
||||
print(stop)
|
||||
#
|
||||
stop['ResponseMetadata']['HTTPStatusCode'].should.equal(200)
|
||||
stop['stopDate'].should.be.a(datetime)
|
||||
|
||||
|
||||
@mock_stepfunctions
|
||||
@mock_sts
|
||||
def test_state_machine_describe_execution_after_stoppage():
|
||||
client = boto3.client('stepfunctions', region_name=region)
|
||||
#
|
||||
sm = client.create_state_machine(name='name', definition=str(simple_definition), roleArn=default_stepfunction_role)
|
||||
execution = client.start_execution(stateMachineArn=sm['stateMachineArn'])
|
||||
client.stop_execution(executionArn=execution['executionArn'])
|
||||
description = client.describe_execution(executionArn=execution['executionArn'])
|
||||
#
|
||||
description['ResponseMetadata']['HTTPStatusCode'].should.equal(200)
|
||||
description['status'].should.equal('SUCCEEDED')
|
||||
description['stopDate'].should.be.a(datetime)
|
||||
|
Loading…
Reference in New Issue
Block a user