Completed
Pull Request — develop (#1)
by Fabian
01:17
created

wait_for_finish()   B

Complexity

Conditions 5

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 0
loc 16
ccs 14
cts 14
cp 1
c 2
b 0
f 0
rs 8.5454
cc 5
crap 5
1 1
from __future__ import print_function, absolute_import
2 1
from time import sleep
3
4 1
import click
5 1
import getpass
6 1
from datetime import datetime, timedelta
7
8 1
from ecs_deploy.ecs import DeployAction, ScaleAction, EcsClient
9 1
from ecs_deploy.newrelic import Deployment
10
11
12 1
@click.group()
13
def ecs(): # pragma: no cover
14
    pass
15
16
17 1
def get_client(access_key_id, secret_access_key, region, profile):
18 1
    return EcsClient(access_key_id, secret_access_key, region, profile)
19
20
21 1
@click.command()
22 1
@click.argument('cluster')
23 1
@click.argument('service')
24 1
@click.option('-t', '--tag', help='Changes the tag for ALL container images')
25 1
@click.option('-i', '--image', type=(str, str), multiple=True, help='Overwrites the image for a container')
26 1
@click.option('-c', '--command', type=(str, str), multiple=True, help='Overwrites the command in a container')
27 1
@click.option('--region', required=False, help='AWS region')
28 1
@click.option('--access-key-id', required=False, help='AWS access key id')
29 1
@click.option('--secret-access-key', required=False, help='AWS secret access yey')
30 1
@click.option('--profile', required=False, help='AWS configuration profile')
31 1
@click.option('--timeout', required=False, default=300, type=int, help='AWS configuration profile')
32 1
@click.option('--newrelic-apikey', required=False, help='New Relic API Key for recording the deployment')
33 1
@click.option('--newrelic-appid', required=False, help='New Relic App ID for recording the deployment')
34 1
@click.option('--comment', required=False, help='Description/comment for recording the deployment')
35 1
@click.option('--user', required=False, help='User who executes the deployment (used for recording)')
36
def deploy(cluster, service, tag, image, command, access_key_id, secret_access_key, region, profile, timeout,
37
           newrelic_apikey, newrelic_appid, comment, user):
38
    """
39
    Redeploy or modify a service.
40
41
    \b
42
    CLUSTER is the name of your cluster (e.g. 'my-custer') within ECS.
43
    SERVICE is the name of your service (e.g. 'my-app') within ECS.
44
45
    When not giving any other options, the task definition will not be changed. It will just be duplicated, so that
46
    all container images will be pulled and redeployed.
47
    """
48
49 1
    try:
50 1
        client = get_client(access_key_id, secret_access_key, region, profile)
51 1
        deployment = DeployAction(client, cluster, service)
52 1
        task_definition = deployment.get_current_task_definition(deployment.service)
53
54 1
        task_definition.set_images(tag, **{key: value for (key, value) in image})
55 1
        task_definition.set_commands(**{key: value for (key, value) in command})
56 1
        print_diff(task_definition)
57
58 1
        click.secho('Creating new task definition revision')
59 1
        new_task_definition = deployment.update_task_definition(task_definition)
60 1
        click.secho('Successfully created revision: %d' % new_task_definition.revision, fg='green')
61 1
        click.secho('Successfully deregistered revision: %d\n' % task_definition.revision, fg='green')
62 1
        click.secho('Updating service')
63 1
        deployment.deploy(new_task_definition)
64 1
        click.secho('Successfully changed task definition to: %s:%s\n' %
65
                    (new_task_definition.family, new_task_definition.revision), fg='green')
66
67 1
        record_deployment(tag, newrelic_apikey, newrelic_appid, comment, user);
68
69 1
        wait_for_finish(deployment, timeout, 'Deploying task definition', 'Deployment successful', 'Deployment failed')
70
71 1
    except Exception as e:
72 1
        click.secho('%s\n' % str(e), fg='red', err=True)
73 1
        exit(1)
74
75
76 1
@click.command()
77 1
@click.argument('cluster')
78 1
@click.argument('service')
79 1
@click.argument('desired_count', type=int)
80 1
@click.option('--region', help='AWS region')
81 1
@click.option('--access-key-id', help='AWS access key id')
82 1
@click.option('--secret-access-key', help='AWS secret access yey')
83 1
@click.option('--profile', help='AWS configuration profile')
84 1
@click.option('--timeout', default=300, type=int, help='AWS configuration profile')
85
def scale(cluster, service, desired_count, access_key_id, secret_access_key, region, profile, timeout):
86
    """
87
    Scale a service up or down.
88
89
    \b
90
    CLUSTER is the name of your cluster (e.g. 'my-custer') within ECS.
91
    SERVICE is the name of your service (e.g. 'my-app') within ECS.
92
    DESIRED_COUNT is the number of tasks your service should run.
93
    """
94 1
    try:
95 1
        client = get_client(access_key_id, secret_access_key, region, profile)
96 1
        scaling = ScaleAction(client, cluster, service)
97 1
        click.secho('Updating service')
98 1
        scaling.scale(desired_count)
99 1
        click.secho('Successfully changed desired count to: %s\n' % desired_count, fg='green')
100 1
        wait_for_finish(scaling, timeout, 'Scaling service', 'Scaling successful', 'Scaling failed')
101
102 1
    except Exception as e:
103 1
        click.secho('%s\n' % str(e), fg='red', err=True)
104 1
        exit(1)
105
106
107 1
def wait_for_finish(action, timeout, title, success_message, failure_message):
108 1
    click.secho(title, nl=False)
109 1
    waiting = True
110 1
    waiting_timeout = datetime.now() + timedelta(seconds=timeout)
111 1
    while waiting and datetime.now() < waiting_timeout:
112 1
        sleep(1)
113 1
        click.secho('.', nl=False)
114 1
        service = action.get_service()
115 1
        waiting = not action.is_deployed(service) and not service.errors
116
117 1
    if waiting or service.errors:
118 1
        print_errors(service, waiting, failure_message)
119 1
        exit(1)
120
121 1
    click.secho('\n%s\n' % success_message, fg='green')
122 1
    exit(0)
123
124
125 1
def record_deployment(revision, newrelic_apikey, newrelic_appid, comment, user):
126 1
    if not revision or not newrelic_apikey or not newrelic_appid:
127 1
        return False
128
129 1
    user = user or getpass.getuser()
130
131 1
    click.secho('Recording deployment in New Relic', nl=False)
132
133 1
    deployment = Deployment(newrelic_apikey, newrelic_appid, user)
134 1
    deployment.deploy(revision, '', comment)
135
136 1
    click.secho('\nDone\n', fg='green')
137
138 1
    return True
139
140
141 1
def print_diff(task_definition):
142 1
    if task_definition.diff:
143 1
        click.secho('Updating task definition')
144 1
        for diff in task_definition.diff:
145 1
            click.secho(str(diff), fg='blue')
146 1
        click.secho('')
147
148
149 1
def print_errors(service, was_timeout=False, message=''):
150 1
    if was_timeout:
151 1
        click.secho('\n%s (timeout)\n' % message, fg='red', err=True)
152
    else:
153 1
        click.secho('\n%s\n' % message, fg='red', err=True)
154
155 1
    for timestamp in service.errors:
156 1
        click.secho('%s\n%s\n' % (timestamp, service.errors[timestamp]), fg='red', err=True)
157
158 1
    if service.older_errors:
159 1
        click.secho('Older errors', fg='yellow', err=True)
160 1
        for timestamp in service.older_errors:
161 1
            click.secho('%s\n%s\n' % (timestamp, service.older_errors[timestamp]), fg='yellow', err=True)
162
163
164 1
ecs.add_command(deploy)
165 1
ecs.add_command(scale)
166
167
if __name__ == '__main__':  # pragma: no cover
168
    ecs()
169