Skip to content

Commit 3e5a5b0

Browse files
committed
[run] wait command to finish
1 parent 9dee06b commit 3e5a5b0

4 files changed

Lines changed: 88 additions & 56 deletions

File tree

ecs_deploy/cli.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,11 @@ def scale(cluster, service, desired_count, access_key_id, secret_access_key, reg
161161
@click.option('--access-key-id', required=False, help='AWS access key id')
162162
@click.option('--secret-access-key', required=False, help='AWS secret access key')
163163
@click.option('--profile', required=False, help='AWS configuration profile name')
164+
@click.option('--timeout', required=False, default=300, type=int, help='Amount of seconds to wait for deployment before command fails (default: 300). To disable timeout (fire and forget) set to -1')
164165
@click.option('--diff/--no-diff', default=True, help='Print which values were changed in the task definition (default: --diff)')
165166
@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')
166167
@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')
167-
def run(cluster, task, count, tag, image, command, env, secret, role, region, access_key_id, secret_access_key, profile, diff, exclusive_env, exclusive_secrets):
168+
def run(cluster, task, count, tag, image, command, env, secret, role, region, access_key_id, secret_access_key, profile, timeout, diff, exclusive_env, exclusive_secrets):
168169
"""
169170
Run a one-off task.
170171
@@ -204,11 +205,46 @@ def run(cluster, task, count, tag, image, command, env, secret, role, region, ac
204205
click.secho('- %s' % started_task['taskArn'], fg='green')
205206
click.secho(' ')
206207

208+
exit_code = wait_for_task(
209+
action=action,
210+
timeout=timeout,
211+
title='Running task'
212+
)
213+
exit(exit_code)
214+
207215
except EcsError as e:
208216
click.secho('%s\n' % str(e), fg='red', err=True)
209217
exit(1)
210218

211219

220+
def wait_for_task(action, timeout, title):
221+
click.secho(title, nl=False)
222+
waiting_timeout = datetime.now() + timedelta(seconds=timeout)
223+
224+
if timeout == -1:
225+
waiting = False
226+
else:
227+
waiting = True
228+
229+
exit_code = 0
230+
231+
while waiting and datetime.now() < waiting_timeout:
232+
click.secho('.', nl=False)
233+
waiting = False
234+
235+
for started_task in action.started_tasks:
236+
task = action.get_task(started_task[u'taskArn'])
237+
if task[u'lastStatus'] != u'STOPPED':
238+
waiting = True
239+
else:
240+
for container in task[u'containers']:
241+
exit_code = exit_code or container[u'exitCode']
242+
if waiting:
243+
sleep(1)
244+
245+
return exit_code
246+
247+
212248
def wait_for_finish(action, timeout, title, success_message, failure_message,
213249
ignore_warnings):
214250
click.secho(title, nl=False)

ecs_deploy/ecs.py

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

456+
def get_task(self, task_arn):
457+
tasks_details = self._client.describe_tasks(
458+
cluster_name=self._cluster_name,
459+
task_arns=[task_arn]
460+
)
461+
return tasks_details[u'tasks'][0]
462+
456463
def get_current_task_definition(self, service):
457464
return self.get_task_definition(service.task_definition)
458465

tests/test_cli.py

Lines changed: 35 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -602,11 +602,10 @@ def test_scale_without_credentials(get_client, runner):
602602

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

608-
assert not result.exception
609-
assert result.exit_code == 0
608+
assert result.exit_code == 123
610609

611610
assert u"Successfully started 2 instances of task: test-task:2" in result.output
612611
assert u"- arn:foo:bar" in result.output
@@ -615,10 +614,9 @@ def test_run_task(get_client, runner):
615614

616615
@patch('ecs_deploy.cli.get_client')
617616
def test_run_with_role_arn(get_client, runner):
618-
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
617+
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
619618
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task:1', '2', '-r', 'arn:new:role'))
620-
assert result.exit_code == 0
621-
assert not result.exception
619+
assert result.exit_code == 123
622620
assert u"Using task definition: test-task" in result.output
623621
assert u'Changed role_arn to: "arn:new:role" (was: "arn:test:role:1")' in result.output
624622
assert u"Creating new task definition revision" in result.output
@@ -630,10 +628,9 @@ def test_run_with_role_arn(get_client, runner):
630628

631629
@patch('ecs_deploy.cli.get_client')
632630
def test_run_new_tag(get_client, runner):
633-
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
631+
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
634632
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-t', 'latest'))
635-
assert result.exit_code == 0
636-
assert not result.exception
633+
assert result.exit_code == 123
637634
assert u"Using task definition: test-task" in result.output
638635
assert u"Creating new task definition revision" in result.output
639636
assert u'Changed image of container "webserver" to: "webserver:latest" (was: "webserver:123")' in result.output
@@ -646,10 +643,9 @@ def test_run_new_tag(get_client, runner):
646643

647644
@patch('ecs_deploy.cli.get_client')
648645
def test_run_one_new_image(get_client, runner):
649-
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
646+
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
650647
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-i', 'application', 'application:latest'))
651-
assert result.exit_code == 0
652-
assert not result.exception
648+
assert result.exit_code == 123
653649
assert u"Using task definition: test-task" in result.output
654650
assert u"Creating new task definition revision" in result.output
655651
assert u'Changed image of container "application" to: "application:latest" (was: "application:123")' in result.output
@@ -661,11 +657,10 @@ def test_run_one_new_image(get_client, runner):
661657

662658
@patch('ecs_deploy.cli.get_client')
663659
def test_run_two_new_images(get_client, runner):
664-
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
660+
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
665661
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-i', 'application', 'application:latest',
666662
'-i', 'webserver', 'webserver:latest'))
667-
assert result.exit_code == 0
668-
assert not result.exception
663+
assert result.exit_code == 123
669664
assert u"Using task definition: test-task" in result.output
670665
assert u"Creating new task definition revision" in result.output
671666
assert u'Changed image of container "webserver" to: "webserver:latest" (was: "webserver:123")' in result.output
@@ -677,10 +672,9 @@ def test_run_two_new_images(get_client, runner):
677672

678673
@patch('ecs_deploy.cli.get_client')
679674
def test_run_one_new_command(get_client, runner):
680-
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
675+
get_client.return_value = EcsTestClient('acces_key', 'secret_key', task_status=u'STOPPED')
681676
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-c', 'application', 'date'))
682-
assert result.exit_code == 0
683-
assert not result.exception
677+
assert result.exit_code == 123
684678
assert u"Using task definition: test-task" in result.output
685679
assert u'Changed command of container "application" to: "date" (was: "run")' in result.output
686680
assert u"Successfully started 2 instances of task: test-task:2" in result.output
@@ -690,11 +684,10 @@ def test_run_one_new_command(get_client, runner):
690684

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

696-
assert result.exit_code == 0
697-
assert not result.exception
690+
assert result.exit_code == 123
698691

699692
assert u"Using task definition: test-task" in result.output
700693
assert u'Changed environment "foo" of container "application" to: "bar"' in result.output
@@ -705,11 +698,10 @@ def test_run_one_new_environment_variable(get_client, runner):
705698

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

711-
assert result.exit_code == 0
712-
assert not result.exception
704+
assert result.exit_code == 123
713705

714706
assert u"Using task definition: test-task" in result.output
715707
assert u'Changed environment "foo" of container "application" to: ""' in result.output
@@ -720,11 +712,10 @@ def test_run_change_environment_variable_empty_string(get_client, runner):
720712

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

726-
assert result.exit_code == 0
727-
assert not result.exception
718+
assert result.exit_code == 123
728719

729720
assert u"Using task definition: test-task" in result.output
730721
assert u'Changed environment "new" of container "application" to: ""' in result.output
@@ -735,11 +726,10 @@ def test_run_new_empty_environment_variable(get_client, runner):
735726

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

741-
assert result.exit_code == 0
742-
assert not result.exception
732+
assert result.exit_code == 123
743733

744734
assert u"Using task definition: test-task" not in result.output
745735
assert u'Changed environment' not in result.output
@@ -750,11 +740,10 @@ def test_run_empty_environment_variable_again(get_client, runner):
750740

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

756-
assert result.exit_code == 0
757-
assert not result.exception
746+
assert result.exit_code == 123
758747

759748
assert u"Using task definition: test-task" in result.output
760749
assert u'Changed environment "empty" of container "webserver" to: "not-empty"' in result.output
@@ -765,11 +754,10 @@ def test_run_previously_empty_environment_variable_with_value(get_client, runner
765754

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

771-
assert result.exit_code == 0
772-
assert not result.exception
760+
assert result.exit_code == 123
773761

774762
assert u"Using task definition: test-task" in result.output
775763
assert u'Changed environment "new-env" of container "webserver" to: "new-value"' in result.output
@@ -786,11 +774,10 @@ def test_run_exclusive_environment(get_client, runner):
786774

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

792-
assert result.exit_code == 0
793-
assert not result.exception
780+
assert result.exit_code == 123
794781

795782
assert u"Using task definition: test-task" in result.output
796783
assert u'Changed secret "new-secret" of container "webserver" to: "new-place"' in result.output
@@ -807,13 +794,12 @@ def test_run_exclusive_secret(get_client, runner):
807794

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

815-
assert result.exit_code == 0
816-
assert not result.exception
802+
assert result.exit_code == 123
817803

818804
assert u"Using task definition: test-task" in result.output
819805
assert u'Changed secret "baz" of container "application" to: "qux"' in result.output
@@ -826,11 +812,10 @@ def test_run_one_new_secret_variable(get_client, runner):
826812

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

832-
assert result.exit_code == 0
833-
assert not result.exception
818+
assert result.exit_code == 123
834819

835820
assert u"Using task definition: test-task" not in result.output
836821
assert u'Changed environment' not in result.output
@@ -841,11 +826,10 @@ def test_run_without_changing_environment_value(get_client, runner):
841826

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

847-
assert result.exit_code == 0
848-
assert not result.exception
832+
assert result.exit_code == 123
849833

850834
assert u"Using task definition: test-task" not in result.output
851835
assert u'Changed secrets' not in result.output
@@ -856,11 +840,10 @@ def test_run_without_changing_secrets_value(get_client, runner):
856840

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

862-
assert not result.exception
863-
assert result.exit_code == 0
846+
assert result.exit_code == 123
864847

865848
assert u"Using task definition: test-task" not in result.output
866849
assert u'Changed environment' not in result.output
@@ -888,7 +871,7 @@ def test_run_task_without_credentials(get_client, runner):
888871

889872
@patch('ecs_deploy.cli.get_client')
890873
def test_run_task_with_invalid_cluster(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, ('unknown-cluster', 'test-task'))
893876
assert result.exit_code == 1
894877
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
@@ -79,7 +79,9 @@
7979
u'overrides': {u'containerOverrides': []},
8080
u'lastStatus': u'RUNNING',
8181
u'desiredStatus': u'RUNNING',
82-
u'containers': TASK_DEFINITION_CONTAINERS_1,
82+
u'containers': [{
83+
u'exitCode': 123,
84+
}],
8385
u'startedBy': SERVICE_ARN
8486
}
8587

@@ -802,7 +804,7 @@ def test_run_action_run(client, task_definition):
802804
class EcsTestClient(object):
803805
def __init__(self, access_key_id=None, secret_access_key=None, region=None,
804806
profile=None, deployment_errors=False, client_errors=False,
805-
wait=0):
807+
wait=0, task_status=u'RUNNING'):
806808
super(EcsTestClient, self).__init__()
807809
self.access_key_id = access_key_id
808810
self.secret_access_key = secret_access_key
@@ -811,6 +813,7 @@ def __init__(self, access_key_id=None, secret_access_key=None, region=None,
811813
self.deployment_errors = deployment_errors
812814
self.client_errors = client_errors
813815
self.wait_until = datetime.now() + timedelta(seconds=wait)
816+
self.task_status = task_status
814817

815818
def describe_services(self, cluster_name, service_name):
816819
if not self.access_key_id or not self.secret_access_key:
@@ -841,7 +844,10 @@ def list_tasks(self, cluster_name, service_name):
841844
return deepcopy(RESPONSE_LIST_TASKS_0)
842845

843846
def describe_tasks(self, cluster_name, task_arns):
844-
return deepcopy(RESPONSE_DESCRIBE_TASKS)
847+
tasks = deepcopy(RESPONSE_DESCRIBE_TASKS)
848+
for task in tasks['tasks']:
849+
task[u'lastStatus'] = self.task_status
850+
return tasks
845851

846852
def register_task_definition(self, family, containers, volumes, role_arn, additional_properties):
847853
return deepcopy(RESPONSE_TASK_DEFINITION_2)

0 commit comments

Comments
 (0)