Skip to content

Commit dea604c

Browse files
committed
[run] wait command to finish
1 parent f9b53e1 commit dea604c

File tree

4 files changed

+93
-59
lines changed

4 files changed

+93
-59
lines changed

ecs_deploy/cli.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,11 +310,13 @@ def scale(cluster, service, desired_count, access_key_id, secret_access_key, reg
310310
@click.option('--access-key-id', help='AWS access key id')
311311
@click.option('--secret-access-key', help='AWS secret access key')
312312
@click.option('--profile', help='AWS configuration profile name')
313+
@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')
314+
@click.option('--sleep-time', default=1, type=int, help='Amount of seconds to wait between each check of the service (default: 1)')
313315
@click.option('--diff/--no-diff', default=True, help='Print what values were changed in the task definition')
314316
@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')
315317
@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')
316318
@click.option('--deregister/--no-deregister', default=True, help='Deregister or keep the old task definition (default: --deregister)')
317-
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):
319+
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):
318320
"""
319321
Run a one-off task.
320322
@@ -355,9 +357,18 @@ def run(cluster, task, count, tag, image, command, env, secret, role, launchtype
355357
click.secho('- %s' % started_task['taskArn'], fg='green')
356358
click.secho(' ')
357359

360+
exit_code = wait_for_task(
361+
action=action,
362+
timeout=timeout,
363+
title='Running task',
364+
sleep_time=sleep_time,
365+
)
366+
358367
if should_create_task_definition and deregister:
359368
deregister_task_definition(action, td_old)
360369

370+
exit(exit_code)
371+
361372
except EcsError as e:
362373
click.secho('%s\n' % str(e), fg='red', err=True)
363374
exit(1)
@@ -409,6 +420,34 @@ def diff(task, revision_a, revision_b, region, access_key_id, secret_access_key,
409420
exit(1)
410421

411422

423+
def wait_for_task(action, timeout, title, sleep_time=1):
424+
click.secho(title, nl=False)
425+
waiting_timeout = datetime.now() + timedelta(seconds=timeout)
426+
427+
if timeout == -1:
428+
waiting = False
429+
else:
430+
waiting = True
431+
432+
exit_code = 0
433+
434+
while waiting and datetime.now() < waiting_timeout:
435+
click.secho('.', nl=False)
436+
waiting = False
437+
438+
for started_task in action.started_tasks:
439+
task = action.get_task(started_task[u'taskArn'])
440+
if task[u'lastStatus'] != u'STOPPED':
441+
waiting = True
442+
else:
443+
for container in task[u'containers']:
444+
exit_code = exit_code or container[u'exitCode']
445+
if waiting:
446+
sleep(sleep_time)
447+
448+
return exit_code
449+
450+
412451
def wait_for_finish(action, timeout, title, success_message, failure_message,
413452
ignore_warnings, sleep_time=1):
414453
click.secho(title, nl=False)

ecs_deploy/ecs.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,13 @@ def get_service(self):
572572
service_definition=services_definition[u'services'][0]
573573
)
574574

575+
def get_task(self, task_arn):
576+
tasks_details = self._client.describe_tasks(
577+
cluster_name=self._cluster_name,
578+
task_arns=[task_arn]
579+
)
580+
return tasks_details[u'tasks'][0]
581+
575582
def get_current_task_definition(self, service):
576583
return self.get_task_definition(service.task_definition)
577584

tests/test_cli.py

Lines changed: 37 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -616,11 +616,10 @@ def test_scale_without_credentials(get_client, runner):
616616

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

622-
assert not result.exception
623-
assert result.exit_code == 0
622+
assert result.exit_code == 123
624623

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

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

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

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

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

694689
@patch('ecs_deploy.cli.get_client')
695690
def test_run_two_new_images(get_client, runner):
696-
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
691+
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
697692
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-i', 'application', 'application:latest',
698693
'-i', 'webserver', 'webserver:latest'))
699-
assert result.exit_code == 0
700-
assert not result.exception
694+
assert result.exit_code == 123
701695
assert u"Using task definition: test-task" in result.output
702696
assert u"Creating new task definition revision" in result.output
703697
assert u'Changed image of container "webserver" to: "webserver:latest" (was: "webserver:123")' in result.output
@@ -709,10 +703,9 @@ def test_run_two_new_images(get_client, runner):
709703

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

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

728-
assert result.exit_code == 0
729-
assert not result.exception
721+
assert result.exit_code == 123
730722

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

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

743-
assert result.exit_code == 0
744-
assert not result.exception
735+
assert result.exit_code == 123
745736

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

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

758-
assert result.exit_code == 0
759-
assert not result.exception
749+
assert result.exit_code == 123
760750

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

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

773-
assert result.exit_code == 0
774-
assert not result.exception
763+
assert result.exit_code == 123
775764

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

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

788-
assert result.exit_code == 0
789-
assert not result.exception
777+
assert result.exit_code == 123
790778

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

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

803-
assert result.exit_code == 0
804-
assert not result.exception
791+
assert result.exit_code == 123
805792

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

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

824-
assert result.exit_code == 0
825-
assert not result.exception
811+
assert result.exit_code == 123
826812

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

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

847-
assert result.exit_code == 0
848-
assert not result.exception
833+
assert result.exit_code == 123
849834

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

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

864-
assert result.exit_code == 0
865-
assert not result.exception
849+
assert result.exit_code == 123
866850

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

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

879-
assert result.exit_code == 0
880-
assert not result.exception
863+
assert result.exit_code == 123
881864

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

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

894-
assert not result.exception
895-
assert result.exit_code == 0
877+
assert result.exit_code == 123
896878

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

921903
@patch('ecs_deploy.cli.get_client')
922904
def test_run_task_with_invalid_cluster(get_client, runner):
923-
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
905+
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
924906
result = runner.invoke(cli.run, ('unknown-cluster', 'test-task'))
925907
assert result.exit_code == 1
926908
assert result.output == u'An error occurred (ClusterNotFoundException) when calling the RunTask operation: Cluster not found.\n\n'

tests/test_ecs.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@
109109
u'overrides': {u'containerOverrides': []},
110110
u'lastStatus': u'RUNNING',
111111
u'desiredStatus': u'RUNNING',
112-
u'containers': TASK_DEFINITION_CONTAINERS_1,
112+
u'containers': [{
113+
u'exitCode': 123,
114+
}],
113115
u'startedBy': SERVICE_ARN
114116
}
115117

@@ -898,7 +900,7 @@ def test_ecs_server_get_warnings():
898900
class EcsTestClient(object):
899901
def __init__(self, access_key_id=None, secret_access_key=None, region=None,
900902
profile=None, deployment_errors=False, client_errors=False,
901-
wait=0):
903+
wait=0, task_status=u'RUNNING'):
902904
super(EcsTestClient, self).__init__()
903905
self.access_key_id = access_key_id
904906
self.secret_access_key = secret_access_key
@@ -907,6 +909,7 @@ def __init__(self, access_key_id=None, secret_access_key=None, region=None,
907909
self.deployment_errors = deployment_errors
908910
self.client_errors = client_errors
909911
self.wait_until = datetime.now() + timedelta(seconds=wait)
912+
self.task_status = task_status
910913

911914
def describe_services(self, cluster_name, service_name):
912915
if not self.access_key_id or not self.secret_access_key:
@@ -939,7 +942,10 @@ def list_tasks(self, cluster_name, service_name):
939942
return deepcopy(RESPONSE_LIST_TASKS_0)
940943

941944
def describe_tasks(self, cluster_name, task_arns):
942-
return deepcopy(RESPONSE_DESCRIBE_TASKS)
945+
tasks = deepcopy(RESPONSE_DESCRIBE_TASKS)
946+
for task in tasks['tasks']:
947+
task[u'lastStatus'] = self.task_status
948+
return tasks
943949

944950
def register_task_definition(self, family, containers, volumes, role_arn,
945951
execution_role_arn, additional_properties):

0 commit comments

Comments
 (0)