Completed
Push — master ( 98e9e8...6ab974 )
by Fabian
8s
created

record_deployment()   A

Complexity

Conditions 4

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

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