Skip to content

Commit

Permalink
[run] wait command to finish
Browse files Browse the repository at this point in the history
  • Loading branch information
Maks3w committed Sep 28, 2019
1 parent dcc3fa7 commit c408c39
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 59 deletions.
41 changes: 40 additions & 1 deletion ecs_deploy/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,11 +310,13 @@ def scale(cluster, service, desired_count, access_key_id, secret_access_key, reg
@click.option('--access-key-id', help='AWS access key id')
@click.option('--secret-access-key', help='AWS secret access key')
@click.option('--profile', help='AWS configuration profile name')
@click.option('--timeout', default=300, type=int, help='Amount of seconds to wait for task to finish before command fails (default: 300). To disable timeout (fire and forget) set to -1')
@click.option('--sleep-time', default=1, type=int, help='Amount of seconds to wait between each check of the service (default: 1)')
@click.option('--diff/--no-diff', default=True, help='Print what values were changed in the task definition')
@click.option('--exclusive-env', is_flag=True, default=False, help='Set the given environment variables exclusively and remove all other pre-existing env variables from all containers')
@click.option('--exclusive-secrets', is_flag=True, default=False, help='Set the given secrets exclusively and remove all other pre-existing secrets from all containers')
@click.option('--deregister/--no-deregister', default=True, help='Deregister or keep the old task definition (default: --deregister)')
def run(cluster, task, count, tag, image, command, env, secret, role, launchtype, subnet, securitygroup, public_ip, region, access_key_id, secret_access_key, profile, diff, exclusive_env, exclusive_secrets, deregister):
def run(cluster, task, count, tag, image, command, env, secret, role, launchtype, subnet, securitygroup, public_ip, region, access_key_id, secret_access_key, profile, timeout, sleep_time, diff, exclusive_env, exclusive_secrets, deregister):
"""
Run a one-off task.
Expand Down Expand Up @@ -355,9 +357,18 @@ def run(cluster, task, count, tag, image, command, env, secret, role, launchtype
click.secho('- %s' % started_task['taskArn'], fg='green')
click.secho(' ')

exit_code = wait_for_task(
action=action,
timeout=timeout,
title='Running task',
sleep_time=sleep_time,
)

if should_create_task_definition and deregister:
deregister_task_definition(action, td_old)

exit(exit_code)

except EcsError as e:
click.secho('%s\n' % str(e), fg='red', err=True)
exit(1)
Expand Down Expand Up @@ -408,6 +419,34 @@ def diff(task, revision_a, revision_b, region, access_key_id, secret_access_key,
exit(1)


def wait_for_task(action, timeout, title, sleep_time=1):
click.secho(title, nl=False)
waiting_timeout = datetime.now() + timedelta(seconds=timeout)

if timeout == -1:
waiting = False
else:
waiting = True

exit_code = 0

while waiting and datetime.now() < waiting_timeout:
click.secho('.', nl=False)
waiting = False

for started_task in action.started_tasks:
task = action.get_task(started_task[u'taskArn'])
if task[u'lastStatus'] != u'STOPPED':
waiting = True
else:
for container in task[u'containers']:
exit_code = exit_code or container[u'exitCode']
if waiting:
sleep(sleep_time)

return exit_code


def wait_for_finish(action, timeout, title, success_message, failure_message,
ignore_warnings, sleep_time=1):
click.secho(title, nl=False)
Expand Down
7 changes: 7 additions & 0 deletions ecs_deploy/ecs.py
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,13 @@ def get_service(self):
service_definition=services_definition[u'services'][0]
)

def get_task(self, task_arn):
tasks_details = self._client.describe_tasks(
cluster_name=self._cluster_name,
task_arns=[task_arn]
)
return tasks_details[u'tasks'][0]

def get_current_task_definition(self, service):
return self.get_task_definition(service.task_definition)

Expand Down
92 changes: 37 additions & 55 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,11 +616,10 @@ def test_scale_without_credentials(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_task(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task'))

assert not result.exception
assert result.exit_code == 0
assert result.exit_code == 123

assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u'Successfully deregistered revision: 2' not in result.output
Expand All @@ -630,10 +629,9 @@ def test_run_task(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_with_role_arn_deregister_old_task_definiion(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task:1', '2', '-r', 'arn:new:role'))
assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123
assert u"Using task definition: test-task" in result.output
assert u'Changed role_arn to: "arn:new:role" (was: "arn:test:role:1")' in result.output
assert u"Creating new task definition revision" in result.output
Expand All @@ -646,10 +644,9 @@ def test_run_with_role_arn_deregister_old_task_definiion(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_with_role_arn_keep_old_task_definiion(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task:1', '2', '-r', 'arn:new:role', '--no-deregister'))
assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123
assert u"Using task definition: test-task" in result.output
assert u'Changed role_arn to: "arn:new:role" (was: "arn:test:role:1")' in result.output
assert u"Creating new task definition revision" in result.output
Expand All @@ -662,10 +659,9 @@ def test_run_with_role_arn_keep_old_task_definiion(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_new_tag(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-t', 'latest'))
assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123
assert u"Using task definition: test-task" in result.output
assert u"Creating new task definition revision" in result.output
assert u'Changed image of container "webserver" to: "webserver:latest" (was: "webserver:123")' in result.output
Expand All @@ -678,10 +674,9 @@ def test_run_new_tag(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_one_new_image(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-i', 'application', 'application:latest'))
assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123
assert u"Using task definition: test-task" in result.output
assert u"Creating new task definition revision" in result.output
assert u'Changed image of container "application" to: "application:latest" (was: "application:123")' in result.output
Expand All @@ -693,11 +688,10 @@ def test_run_one_new_image(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_two_new_images(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-i', 'application', 'application:latest',
'-i', 'webserver', 'webserver:latest'))
assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123
assert u"Using task definition: test-task" in result.output
assert u"Creating new task definition revision" in result.output
assert u'Changed image of container "webserver" to: "webserver:latest" (was: "webserver:123")' in result.output
Expand All @@ -709,10 +703,9 @@ def test_run_two_new_images(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_one_new_command(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-c', 'application', 'date'))
assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123
assert u"Using task definition: test-task" in result.output
assert u'Changed command of container "application" to: "date" (was: "run")' in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
Expand All @@ -722,11 +715,10 @@ def test_run_one_new_command(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_one_new_environment_variable(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'application', 'foo', 'bar'))

assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123

assert u"Using task definition: test-task" in result.output
assert u'Changed environment "foo" of container "application" to: "bar"' in result.output
Expand All @@ -737,11 +729,10 @@ def test_run_one_new_environment_variable(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_change_environment_variable_empty_string(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'application', 'foo', ''))

assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123

assert u"Using task definition: test-task" in result.output
assert u'Changed environment "foo" of container "application" to: ""' in result.output
Expand All @@ -752,11 +743,10 @@ def test_run_change_environment_variable_empty_string(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_new_empty_environment_variable(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'application', 'new', ''))

assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123

assert u"Using task definition: test-task" in result.output
assert u'Changed environment "new" of container "application" to: ""' in result.output
Expand All @@ -767,11 +757,10 @@ def test_run_new_empty_environment_variable(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_empty_environment_variable_again(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'webserver', 'empty', ''))

assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123

assert u"Using task definition: test-task" not in result.output
assert u'Changed environment' not in result.output
Expand All @@ -782,11 +771,10 @@ def test_run_empty_environment_variable_again(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_previously_empty_environment_variable_with_value(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'webserver', 'empty', 'not-empty'))

assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123

assert u"Using task definition: test-task" in result.output
assert u'Changed environment "empty" of container "webserver" to: "not-empty"' in result.output
Expand All @@ -797,11 +785,10 @@ def test_run_previously_empty_environment_variable_with_value(get_client, runner

@patch('ecs_deploy.cli.get_client')
def test_run_exclusive_environment(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'webserver', 'new-env', 'new-value', '--exclusive-env'))

assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123

assert u"Using task definition: test-task" in result.output
assert u'Changed environment "new-env" of container "webserver" to: "new-value"' in result.output
Expand All @@ -818,11 +805,10 @@ def test_run_exclusive_environment(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_exclusive_secret(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-s', 'webserver', 'new-secret', 'new-place', '--exclusive-secrets'))

assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123

assert u"Using task definition: test-task" in result.output
assert u'Changed secret "new-secret" of container "webserver" to: "new-place"' in result.output
Expand All @@ -839,13 +825,12 @@ def test_run_exclusive_secret(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_one_new_secret_variable(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2',
'-s', 'application', 'baz', 'qux',
'-s', 'webserver', 'baz', 'quux'))

assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123

assert u"Using task definition: test-task" in result.output
assert u'Changed secret "baz" of container "application" to: "qux"' in result.output
Expand All @@ -858,11 +843,10 @@ def test_run_one_new_secret_variable(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_without_changing_environment_value(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'webserver', 'foo', 'bar'))

assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123

assert u"Using task definition: test-task" not in result.output
assert u'Changed environment' not in result.output
Expand All @@ -873,11 +857,10 @@ def test_run_without_changing_environment_value(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_without_changing_secrets_value(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-s', 'webserver', 'baz', 'qux'))

assert result.exit_code == 0
assert not result.exception
assert result.exit_code == 123

assert u"Using task definition: test-task" not in result.output
assert u'Changed secrets' not in result.output
Expand All @@ -888,11 +871,10 @@ def test_run_without_changing_secrets_value(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_task_without_diff(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'application', 'foo', 'bar', '--no-diff'))

assert not result.exception
assert result.exit_code == 0
assert result.exit_code == 123

assert u"Using task definition: test-task" not in result.output
assert u'Changed environment' not in result.output
Expand Down Expand Up @@ -920,7 +902,7 @@ def test_run_task_without_credentials(get_client, runner):

@patch('ecs_deploy.cli.get_client')
def test_run_task_with_invalid_cluster(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
result = runner.invoke(cli.run, ('unknown-cluster', 'test-task'))
assert result.exit_code == 1
assert result.output == u'An error occurred (ClusterNotFoundException) when calling the RunTask operation: Cluster not found.\n\n'
Expand Down
12 changes: 9 additions & 3 deletions tests/test_ecs.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@
u'overrides': {u'containerOverrides': []},
u'lastStatus': u'RUNNING',
u'desiredStatus': u'RUNNING',
u'containers': TASK_DEFINITION_CONTAINERS_1,
u'containers': [{
u'exitCode': 123,
}],
u'startedBy': SERVICE_ARN
}

Expand Down Expand Up @@ -898,7 +900,7 @@ def test_ecs_server_get_warnings():
class EcsTestClient(object):
def __init__(self, access_key_id=None, secret_access_key=None, region=None,
profile=None, deployment_errors=False, client_errors=False,
wait=0):
wait=0, task_status=u'RUNNING'):
super(EcsTestClient, self).__init__()
self.access_key_id = access_key_id
self.secret_access_key = secret_access_key
Expand All @@ -907,6 +909,7 @@ def __init__(self, access_key_id=None, secret_access_key=None, region=None,
self.deployment_errors = deployment_errors
self.client_errors = client_errors
self.wait_until = datetime.now() + timedelta(seconds=wait)
self.task_status = task_status

def describe_services(self, cluster_name, service_name):
if not self.access_key_id or not self.secret_access_key:
Expand Down Expand Up @@ -939,7 +942,10 @@ def list_tasks(self, cluster_name, service_name):
return deepcopy(RESPONSE_LIST_TASKS_0)

def describe_tasks(self, cluster_name, task_arns):
return deepcopy(RESPONSE_DESCRIBE_TASKS)
tasks = deepcopy(RESPONSE_DESCRIBE_TASKS)
for task in tasks['tasks']:
task[u'lastStatus'] = self.task_status
return tasks

def register_task_definition(self, family, containers, volumes, role_arn,
execution_role_arn, additional_properties):
Expand Down

0 comments on commit c408c39

Please sign in to comment.