Repository: fabfuel/ecs-deploy

Issue:
Deploy without timeout or wait
Hi,

I want to thank you guys for this fantastic utility program to simplify deployment on ECS. Are there plans to support deployments without a timeout option?

For certain deployments (especially launch type Fargate) it appears ECS Cluster manager takes a while to shutdown/deregister from ASG and reach steady state. The default 300s is not really enough, but it is hard to know when to arbitrarily increase it to

In certain projects, it may be better to not have a timeout. Is this an functionality being considered?

Discussion:

Patch:
diff --git a/README.rst b/README.rst
index c6602be..ccbd4a7 100644
--- a/README.rst
+++ b/README.rst
@@ -238,6 +238,13 @@ To ignore these warnings, you can run the deployment with the flag ``--ignore-wa
 In that case, the warning is printed, but the script continues and waits for a successful
 deployment until it times out.
 
+Deployment timeout
+==================
+The deploy and scale actions allow defining a timeout (in seconds) via the ``--timeout`` parameter.
+This instructs ecs-deploy to wait for ECS to finish the deployment for the given number of seconds.
+
+To run a deployment without waiting for the successful or failed result at all, set ``--timeout`` to the value of ``-1``.
+
 Scaling
 -------
 
diff --git a/ecs_deploy/cli.py b/ecs_deploy/cli.py
index 45cdfee..869a0bc 100644
--- a/ecs_deploy/cli.py
+++ b/ecs_deploy/cli.py
@@ -37,7 +37,7 @@ def get_client(access_key_id, secret_access_key, region, profile):
 @click.option('--access-key-id', required=False, help='AWS access key id')
 @click.option('--secret-access-key', required=False, help='AWS secret access key')
 @click.option('--profile', required=False, help='AWS configuration profile name')
-@click.option('--timeout', required=False, default=300, type=int, help='Amount of seconds to wait for deployment before command fails (default: 300)')
+@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')
 @click.option('--ignore-warnings', is_flag=True, help='Do not fail deployment on warnings (port already in use or insufficient memory/CPU)')
 @click.option('--newrelic-apikey', required=False, help='New Relic API Key for recording the deployment')
 @click.option('--newrelic-appid', required=False, help='New Relic App ID for recording the deployment')
@@ -113,7 +113,7 @@ def deploy(cluster, service, tag, image, command, env, secret, role, task, regio
 @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='AWS configuration profile')
+@click.option('--timeout', 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')
 @click.option('--ignore-warnings', is_flag=True, help='Do not fail deployment on warnings (port already in use or insufficient memory/CPU)')
 def scale(cluster, service, desired_count, access_key_id, secret_access_key, region, profile, timeout, ignore_warnings):
     """
@@ -202,10 +202,15 @@ def run(cluster, task, count, command, env, secret, region, access_key_id, secre
 def wait_for_finish(action, timeout, title, success_message, failure_message,
                     ignore_warnings):
     click.secho(title, nl=False)
-    waiting = True
     waiting_timeout = datetime.now() + timedelta(seconds=timeout)
     service = action.get_service()
     inspected_until = None
+
+    if timeout == -1:
+        waiting = False
+    else:
+        waiting = True
+
     while waiting and datetime.now() < waiting_timeout:
         click.secho('.', nl=False)
         service = action.get_service()


Test patch:
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 043a894..d29f1de 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1,3 +1,5 @@
+from datetime import datetime
+
 import pytest
 from click.testing import CliRunner
 from mock.mock import patch
@@ -484,6 +486,30 @@ def test_deploy_with_timeout(get_client, runner):
            u"https://github.com/fabfuel/ecs-deploy#timeout" in result.output
 
 
+@patch('ecs_deploy.cli.get_client')
+def test_deploy_with_wait_within_timeout(get_client, runner):
+    get_client.return_value = EcsTestClient('acces_key', 'secret_key', wait=2)
+    result = runner.invoke(cli.deploy, (CLUSTER_NAME, SERVICE_NAME, '--timeout', '10'))
+    assert result.exit_code == 0
+    assert u'Deploying new task definition...' in result.output
+
+
+@patch('ecs_deploy.cli.get_client')
+def test_deploy_without_timeout(get_client, runner):
+    get_client.return_value = EcsTestClient('acces_key', 'secret_key', wait=2)
+
+    start_time = datetime.now()
+    result = runner.invoke(cli.deploy, (CLUSTER_NAME, SERVICE_NAME, '--timeout', '-1'))
+    end_time = datetime.now()
+
+    assert result.exit_code == 0
+
+    # assert task is not waiting for deployment
+    assert u'Deploying new task definition\n' in result.output
+    assert u'...' not in result.output
+    assert (end_time - start_time).total_seconds() < 1
+
+
 @patch('ecs_deploy.cli.get_client')
 def test_deploy_unknown_task_definition_arn(get_client, runner):
     get_client.return_value = EcsTestClient('acces_key', 'secret_key')