Passed
Pull Request — master (#151)
by Fabian
18:38 queued 08:37
created

ecs_deploy.ecs.EcsTaskDefinition.__init__()   A

Complexity

Conditions 1

Size

Total Lines 21
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 17
nop 13
dl 0
loc 21
ccs 11
cts 11
cp 1
crap 1
rs 9.55
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1 1
from datetime import datetime
2 1
import json
3 1
import re
4
5 1
from boto3.session import Session
6 1
from botocore.exceptions import ClientError, NoCredentialsError
7 1
from dateutil.tz.tz import tzlocal
8 1
from dictdiffer import diff
9
10 1
JSON_LIST_REGEX = re.compile(r'^\[.*\]$')
11
12
# Python2 raises ValueError
13 1
try:
14 1
    JSONDecodeError = json.JSONDecodeError
15 1
except AttributeError:
16 1
    JSONDecodeError = ValueError
17
18
LAUNCH_TYPE_EC2 = 'EC2'
19 1
LAUNCH_TYPE_FARGATE = 'FARGATE'
20 1
21
22
def read_env_file(container_name,file):
23 1
    env_vars = []
24 1
    try:
25
        with open(file) as f:
26 1
            for line in f:
27
                if line.startswith('#') or not line.strip() or '=' not in line:
28
                    continue
29
                key, value = line.strip().split('=', 1)
30
                env_vars.append((container_name,key,value))
31 1
    except Exception as e:
32 1
        raise EcsTaskDefinitionCommandError(str(e))
33
    return tuple(env_vars)
34 1
35 1
36
class EcsClient(object):
37
    def __init__(self, access_key_id=None, secret_access_key=None,
38
                 region=None, profile=None, session_token=None):
39
        session = Session(aws_access_key_id=access_key_id,
40 1
                          aws_secret_access_key=secret_access_key,
41 1
                          aws_session_token=session_token,
42 1
                          region_name=region,
43
                          profile_name=profile)
44
        self.boto = session.client(u'ecs')
45
        self.events = session.client(u'events')
46
47
    def describe_services(self, cluster_name, service_name):
48 1
        return self.boto.describe_services(
49 1
            cluster=cluster_name,
50
            services=[service_name]
51
        )
52
53 1
    def describe_task_definition(self, task_definition_arn):
54 1
        try:
55
            return self.boto.describe_task_definition(
56
                taskDefinition=task_definition_arn,
57
                include=[
58
                    'TAGS',
59 1
                ]
60 1
            )
61
        except ClientError:
62 1
            raise UnknownTaskDefinitionError(
63
                u'Unknown task definition arn: %s' % task_definition_arn
64 1
            )
65 1
66
    def list_tasks(self, cluster_name, service_name):
67 1
        return self.boto.list_tasks(
68
            cluster=cluster_name,
69
            serviceName=service_name
70
        )
71
72
    def describe_tasks(self, cluster_name, task_arns):
73
        return self.boto.describe_tasks(cluster=cluster_name, tasks=task_arns)
74
75
    def register_task_definition(self, family, containers, volumes, role_arn,
76 1
                                 execution_role_arn, tags, additional_properties):
77 1
        if tags:
78
            additional_properties['tags'] = tags
79
80
        return self.boto.register_task_definition(
81 1
            family=family,
82 1
            containerDefinitions=containers,
83 1
            volumes=volumes,
84
            taskRoleArn=role_arn,
85
            executionRoleArn=execution_role_arn,
86
            **additional_properties
87
        )
88 1
89
    def deregister_task_definition(self, task_definition_arn):
90
        return self.boto.deregister_task_definition(
91
            taskDefinition=task_definition_arn
92
        )
93
94
    def update_service(self, cluster, service, desired_count, task_definition):
95 1
        if desired_count is None:
96
            return self.boto.update_service(
97
                cluster=cluster,
98
                service=service,
99 1
                taskDefinition=task_definition
100
            )
101
        return self.boto.update_service(
102
            cluster=cluster,
103
            service=service,
104
            desiredCount=desired_count,
105
            taskDefinition=task_definition
106
        )
107
108
    def run_task(self, cluster, task_definition, count, started_by, overrides,
109
                 launchtype='EC2', subnets=(), security_groups=(),
110
                 public_ip=False, platform_version=None):
111
112
        if launchtype == LAUNCH_TYPE_FARGATE:
113
            if not subnets or not security_groups:
114
                msg = 'At least one subnet (--subnet) and one security ' \
115
                      'group (--securitygroup) definition are required ' \
116
                      'for launch type FARGATE'
117
                raise TaskPlacementError(msg)
118
119
            network_configuration = {
120
                "awsvpcConfiguration": {
121
                    "subnets": subnets,
122
                    "securityGroups": security_groups,
123
                    "assignPublicIp": "ENABLED" if public_ip else "DISABLED"
124
                }
125
            }
126
127
            if platform_version is None:
128 1
                platform_version = 'LATEST'
129
130
            return self.boto.run_task(
131
                cluster=cluster,
132
                taskDefinition=task_definition,
133
                count=count,
134
                startedBy=started_by,
135
                overrides=overrides,
136 1
                launchType=launchtype,
137
                networkConfiguration=network_configuration,
138
                platformVersion=platform_version,
139
            )
140
141
        return self.boto.run_task(
142
            cluster=cluster,
143
            taskDefinition=task_definition,
144 1
            count=count,
145 1
            startedBy=started_by,
146 1
            overrides=overrides
147 1
        )
148
149 1
    def update_rule(self, cluster, rule, task_definition):
150 1
        target = self.events.list_targets_by_rule(Rule=rule)['Targets'][0]
151
        target['Arn'] = task_definition.arn.partition('task-definition')[0] + 'cluster/' + cluster
152 1
        target['EcsParameters']['TaskDefinitionArn'] = task_definition.arn
153
        self.events.put_targets(Rule=rule, Targets=[target])
154 1
        return target['Id']
155
156 1
157
class EcsService(dict):
158 1
    def __init__(self, cluster, service_definition=None, **kwargs):
159
        self._cluster = cluster
160 1
        super(EcsService, self).__init__(service_definition, **kwargs)
161
162 1
    def set_task_definition(self, task_definition):
163
        self[u'taskDefinition'] = task_definition.arn
164 1
165
    @property
166 1
    def cluster(self):
167
        return self._cluster
168 1
169
    @property
170 1
    def name(self):
171 1
        return self.get(u'serviceName')
172 1
173 1
    @property
174
    def task_definition(self):
175 1
        return self.get(u'taskDefinition')
176
177 1
    @property
178 1
    def desired_count(self):
179 1
        return self.get(u'desiredCount')
180 1
181
    @property
182 1
    def deployment_created_at(self):
183
        for deployment in self.get(u'deployments'):
184 1
            if deployment.get(u'status') == u'PRIMARY':
185
                return deployment.get(u'createdAt')
186
        return datetime.now()
187
188 1
    @property
189
    def deployment_updated_at(self):
190 1
        for deployment in self.get(u'deployments'):
191
            if deployment.get(u'status') == u'PRIMARY':
192
                return deployment.get(u'updatedAt')
193
        return datetime.now()
194
195 1
    @property
196 1
    def errors(self):
197 1
        return self.get_warnings(
198 1
            since=self.deployment_updated_at
199 1
        )
200 1
201 1
    @property
202 1
    def older_errors(self):
203 1
        return self.get_warnings(
204 1
            since=self.deployment_created_at,
205
            until=self.deployment_updated_at
206
        )
207 1
208 1
    def get_warnings(self, since=None, until=None):
209
        since = since or self.deployment_created_at
210
        until = until or datetime.now(tz=tzlocal())
211
        errors = {}
212 1
        for event in self.get(u'events'):
213 1
            if u'unable' not in event[u'message']:
214 1
                continue
215 1
            if since < event[u'createdAt'] < until:
216 1
                errors[event[u'createdAt']] = event[u'message']
217 1
        return errors
218 1
219 1
220 1
class EcsTaskDefinition(object):
221 1
    def __init__(self, containerDefinitions, volumes, family, revision,
222 1
                 status, taskDefinitionArn, requiresAttributes=None,
223 1
                 taskRoleArn=None, executionRoleArn=None, compatibilities=None,
224
                 tags=None, **kwargs):
225
        self.containers = containerDefinitions
226
        self.volumes = volumes
227
        self.family = family
228 1
        self.revision = revision
229
        self.status = status
230 1
        self.arn = taskDefinitionArn
231
        self.requires_attributes = requiresAttributes or {}
232 1
        self.role_arn = taskRoleArn or u''
233 1
        self.execution_role_arn = executionRoleArn or u''
234
        self.tags = tags
235 1
        self.additional_properties = kwargs
236
        self._diff = []
237 1
238
        # the compatibilities parameter is returned from the ECS API, when
239 1
        # describing a task, but may not be included, when registering a new
240
        # task definition. Just storing it for now.
241 1
        self.compatibilities = compatibilities
242
243 1
    @property
244 1
    def container_names(self):
245 1
        for container in self.containers:
246
            yield container[u'name']
247 1
248 1
    @property
249
    def family_revision(self):
250 1
        return '%s:%d' % (self.family, self.revision)
251 1
252
    @property
253 1
    def diff(self):
254 1
        return self._diff
255
256 1
    def diff_raw(self, task_b):
257 1
        containers_a = {c['name']: c for c in self.containers}
258
        containers_b = {c['name']: c for c in task_b.containers}
259 1
260 1
        requirements_a = sorted([r['name'] for r in self.requires_attributes])
261
        requirements_b = sorted([r['name'] for r in task_b.requires_attributes])
262 1
263
        for container in containers_a:
264
            containers_a[container]['environment'] = {e['name']: e['value'] for e in
265
                                                      containers_a[container].get('environment', {})}
266
267
        for container in containers_b:
268
            containers_b[container]['environment'] = {e['name']: e['value'] for e in
269
                                                      containers_b[container].get('environment', {})}
270
271
        for container in containers_a:
272 1
            containers_a[container]['secrets'] = {e['name']: e['valueFrom'] for e in
273
                                                  containers_a[container].get('secrets', {})}
274
275
        for container in containers_b:
276
            containers_b[container]['secrets'] = {e['name']: e['valueFrom'] for e in
277
                                                  containers_b[container].get('secrets', {})}
278
279
        composite_a = {
280
            'containers': containers_a,
281
            'volumes': self.volumes,
282 1
            'requires_attributes': requirements_a,
283
            'role_arn': self.role_arn,
284 1
            'execution_role_arn': self.execution_role_arn,
285 1
            'compatibilities': self.compatibilities,
286 1
            'additional_properties': self.additional_properties,
287 1
        }
288 1
289 1
        composite_b = {
290 1
            'containers': containers_b,
291 1
            'volumes': task_b.volumes,
292 1
            'requires_attributes': requirements_b,
293 1
            'role_arn': task_b.role_arn,
294 1
            'execution_role_arn': task_b.execution_role_arn,
295 1
            'compatibilities': task_b.compatibilities,
296 1
            'additional_properties': task_b.additional_properties,
297 1
        }
298
299 1
        return list(diff(composite_a, composite_b))
300
301 1
    def get_overrides(self):
302 1
        override = dict()
303 1
        overrides = []
304 1
        for diff in self.diff:
305 1
            if override.get('name') != diff.container:
306
                override = dict(name=diff.container)
307
                overrides.append(override)
308
            if diff.field == 'command':
309
                override['command'] = self.get_overrides_command(diff.value)
310 1
            elif diff.field == 'environment':
311
                override['environment'] = self.get_overrides_env(diff.value)
312 1
            elif diff.field == 'secrets':
313
                override['secrets'] = self.get_overrides_secrets(diff.value)
314 1
        return overrides
315
316 1
    @staticmethod
317
    def parse_command(command):
318 1
        if re.match(JSON_LIST_REGEX, command):
319
            try:
320 1
                return json.loads(command)
321
            except JSONDecodeError as e:
322 1
                raise EcsTaskDefinitionCommandError(
323
                    "command should be valid JSON list. Got following "
324 1
                    "command: {} resulting in error: {}"
325 1
                        .format(command, str(e)))
326 1
327 1
        return command.split()
328 1
329 1
    @staticmethod
330
    def get_overrides_command(command):
331
        return EcsTaskDefinition.parse_command(command)
332
333
    @staticmethod
334
    def get_overrides_env(env):
335 1
        return [{"name": e, "value": env[e]} for e in env]
336 1
337 1
    @staticmethod
338 1
    def get_overrides_secrets(secrets):
339 1
        return [{"name": s, "valueFrom": secrets[s]} for s in secrets]
340 1
341
    def set_images(self, tag=None, **images):
342
        self.validate_container_options(**images)
343
        for container in self.containers:
344
            if container[u'name'] in images:
345
                new_image = images[container[u'name']]
346 1
                diff = EcsTaskDefinitionDiff(
347 1
                    container=container[u'name'],
348
                    field=u'image',
349 1
                    value=new_image,
350 1
                    old_value=container[u'image']
351 1
                )
352 1
                self._diff.append(diff)
353 1
                container[u'image'] = new_image
354 1
            elif tag:
355
                image_definition = container[u'image'].rsplit(u':', 1)
356
                new_image = u'%s:%s' % (image_definition[0], tag.strip())
357
                diff = EcsTaskDefinitionDiff(
358
                    container=container[u'name'],
359
                    field=u'image',
360 1
                    value=new_image,
361 1
                    old_value=container[u'image']
362
                )
363 1
                self._diff.append(diff)
364 1
                container[u'image'] = new_image
365
366 1
    def set_commands(self, **commands):
367 1
        self.validate_container_options(**commands)
368 1
        for container in self.containers:
369
            if container[u'name'] in commands:
370 1
                new_command = commands[container[u'name']]
371 1
                diff = EcsTaskDefinitionDiff(
372 1
                    container=container[u'name'],
373 1
                    field=u'command',
374
                    value=new_command,
375
                    old_value=container.get(u'command')
376
                )
377
                self._diff.append(diff)
378 1
                container[u'command'] = self.parse_command(new_command)
379 1
380
    def set_environment(self, environment_list, exclusive=False, env_file=((None, None),)):
381
        environment = {}
382
        if None not in env_file[0]:
383
            for env in env_file:
384
                l = read_env_file(env[0], env[1])
385 1
                environment_list = l + environment_list
386 1
        for env in environment_list:
387 1
            environment.setdefault(env[0], {})
388
            environment[env[0]][env[1]] = env[2]
389 1
390 1
        self.validate_container_options(**environment)
391
        for container in self.containers:
392 1
            if container[u'name'] in environment:
393 1
                self.apply_container_environment(
394
                    container=container,
395 1
                    new_environment=environment[container[u'name']],
396 1
                    exclusive=exclusive,
397
                )
398 1
            elif exclusive is True:
399
                self.apply_container_environment(
400
                    container=container,
401
                    new_environment={},
402
                    exclusive=exclusive,
403
                )
404 1
405
    def apply_container_environment(self, container, new_environment, exclusive=False):
406 1
        environment = container.get('environment', {})
407
        old_environment = {env['name']: env['value'] for env in environment}
408
409
        if exclusive is True:
410 1
            merged = new_environment
411 1
        else:
412
            merged = old_environment.copy()
413 1
            merged.update(new_environment)
414 1
415 1
        if old_environment == merged:
416
            return
417 1
418 1
        diff = EcsTaskDefinitionDiff(
419 1
            container=container[u'name'],
420 1
            field=u'environment',
421
            value=merged,
422
            old_value=old_environment
423
        )
424
        self._diff.append(diff)
425 1
426 1
        container[u'environment'] = [
427
            {"name": e, "value": merged[e]} for e in merged
428
        ]
429
430
    def set_secrets(self, secrets_list, exclusive=False):
431
        secrets = {}
432 1
433 1
        for secret in secrets_list:
434 1
            secrets.setdefault(secret[0], {})
435
            secrets[secret[0]][secret[1]] = secret[2]
436 1
437 1
        self.validate_container_options(**secrets)
438
        for container in self.containers:
439 1
            if container[u'name'] in secrets:
440 1
                self.apply_container_secrets(
441
                    container=container,
442 1
                    new_secrets=secrets[container[u'name']],
443 1
                    exclusive=exclusive,
444
                )
445 1
            elif exclusive is True:
446
                self.apply_container_secrets(
447
                    container=container,
448
                    new_secrets={},
449
                    exclusive=exclusive,
450
                )
451 1
452
    def apply_container_secrets(self, container, new_secrets, exclusive=False):
453 1
        secrets = container.get('secrets', {})
454
        old_secrets = {secret['name']: secret['valueFrom'] for secret in secrets}
455
456
        if exclusive is True:
457 1
            merged = new_secrets
458 1
        else:
459 1
            merged = old_secrets.copy()
460 1
            merged.update(new_secrets)
461
462
        if old_secrets == merged:
463
            return
464 1
465 1
        diff = EcsTaskDefinitionDiff(
466 1
            container=container[u'name'],
467
            field=u'secrets',
468
            value=merged,
469
            old_value=old_secrets
470
        )
471
        self._diff.append(diff)
472 1
473 1
        container[u'secrets'] = [
474
            {"name": s, "valueFrom": merged[s]} for s in merged
475 1
        ]
476 1
477 1
    def validate_container_options(self, **container_options):
478
        for container_name in container_options:
479
            if container_name not in self.container_names:
480
                raise UnknownContainerError(
481
                    u'Unknown container: %s' % container_name
482
                )
483 1
484 1
    def set_role_arn(self, role_arn):
485
        if role_arn:
486
            diff = EcsTaskDefinitionDiff(
487 1
                container=None,
488 1
                field=u'role_arn',
489 1
                value=role_arn,
490 1
                old_value=self.role_arn
491 1
            )
492 1
            self.role_arn = role_arn
493
            self._diff.append(diff)
494 1
495 1
    def set_execution_role_arn(self, execution_role_arn):
496 1
        if execution_role_arn:
497
            diff = EcsTaskDefinitionDiff(
498
                container=None,
499
                field=u'execution_role_arn',
500
                value=execution_role_arn,
501 1
                old_value=self.execution_role_arn
502 1
            )
503
            self.execution_role_arn = execution_role_arn
504
            self._diff.append(diff)
505
506
507 1
class EcsTaskDefinitionDiff(object):
508 1
    def __init__(self, container, field, value, old_value):
509
        self.container = container
510
        self.field = field
511
        self.value = value
512
        self.old_value = old_value
513
514
    def __repr__(self):
515 1
        if self.field == u'environment':
516
            return '\n'.join(self._get_environment_diffs(
517
                self.container,
518
                self.value,
519
                self.old_value,
520
            ))
521 1
        elif self.field == u'secrets':
522
            return '\n'.join(self._get_secrets_diffs(
523 1
                self.container,
524 1
                self.value,
525 1
                self.old_value,
526 1
            ))
527 1
        elif self.container:
528 1
            return u'Changed %s of container "%s" to: "%s" (was: "%s")' % (
529 1
                self.field,
530 1
                self.container,
531 1
                self.value,
532 1
                self.old_value
533 1
            )
534 1
        else:
535 1
            return u'Changed %s to: "%s" (was: "%s")' % (
536
                self.field,
537 1
                self.value,
538
                self.old_value
539 1
            )
540 1
541 1
    @staticmethod
542 1
    def _get_environment_diffs(container, env, old_env):
543 1
        msg = u'Changed environment "%s" of container "%s" to: "%s"'
544 1
        msg_removed = u'Removed environment "%s" of container "%s"'
545 1
        diffs = []
546 1
        for name, value in env.items():
547 1
            old_value = old_env.get(name)
548 1
            if value != old_value or value and not old_value:
549 1
                message = msg % (name, container, value)
550 1
                diffs.append(message)
551 1
        for old_name in old_env.keys():
552
            if old_name not in env.keys():
553
                message = msg_removed % (old_name, container)
554 1
                diffs.append(message)
555 1
        return diffs
556 1
557 1
    @staticmethod
558 1
    def _get_secrets_diffs(container, secrets, old_secrets):
559
        msg = u'Changed secret "%s" of container "%s" to: "%s"'
560 1
        msg_removed = u'Removed secret "%s" of container "%s"'
561 1
        diffs = []
562 1
        for name, value in secrets.items():
563 1
            old_value = old_secrets.get(name)
564 1
            if value != old_value or not old_value:
565
                message = msg % (name, container, value)
566
                diffs.append(message)
567
        for old_name in old_secrets.keys():
568 1
            if old_name not in secrets.keys():
569 1
                message = msg_removed % (old_name, container)
570 1
                diffs.append(message)
571 1
        return diffs
572
573
574
class EcsAction(object):
575
    def __init__(self, client, cluster_name, service_name):
576 1
        self._client = client
577 1
        self._cluster_name = cluster_name
578
        self._service_name = service_name
579
580
        try:
581 1
            if service_name:
582
                self._service = self.get_service()
583
        except IndexError:
584
            raise EcsConnectionError(
585
                u'An error occurred when calling the DescribeServices '
586 1
                u'operation: Service not found.'
587 1
            )
588
        except ClientError as e:
589 1
            raise EcsConnectionError(str(e))
590 1
        except NoCredentialsError:
591
            raise EcsConnectionError(
592
                u'Unable to locate credentials. Configure credentials '
593
                u'by running "aws configure".'
594 1
            )
595
596
    def get_service(self):
597
        services_definition = self._client.describe_services(
598 1
            cluster_name=self._cluster_name,
599
            service_name=self._service_name
600 1
        )
601 1
        return EcsService(
602
            cluster=self._cluster_name,
603
            service_definition=services_definition[u'services'][0]
604
        )
605
606
    def get_current_task_definition(self, service):
607
        return self.get_task_definition(service.task_definition)
608
609
    def get_task_definition(self, task_definition):
610 1
        task_definition_payload = self._client.describe_task_definition(
611 1
            task_definition_arn=task_definition
612
        )
613 1
614 1
        task_definition = EcsTaskDefinition(
615
            tags=task_definition_payload.get('tags', None),
616 1
            **task_definition_payload[u'taskDefinition']
617 1
        )
618
        return task_definition
619
620
    def update_task_definition(self, task_definition):
621
        response = self._client.register_task_definition(
622
            family=task_definition.family,
623 1
            containers=task_definition.containers,
624
            volumes=task_definition.volumes,
625 1
            role_arn=task_definition.role_arn,
626 1
            execution_role_arn=task_definition.execution_role_arn,
627 1
            tags=task_definition.tags,
628 1
            additional_properties=task_definition.additional_properties
629
        )
630
        new_task_definition = EcsTaskDefinition(**response[u'taskDefinition'])
631
        return new_task_definition
632 1
633 1
    def deregister_task_definition(self, task_definition):
634 1
        self._client.deregister_task_definition(task_definition.arn)
635
636
    def update_service(self, service, desired_count=None):
637
        response = self._client.update_service(
638 1
            cluster=service.cluster,
639
            service=service.name,
640 1
            desired_count=desired_count,
641 1
            task_definition=service.task_definition
642 1
        )
643
        return EcsService(self._cluster_name, response[u'service'])
644
645
    def is_deployed(self, service):
646 1
        if len(service[u'deployments']) != 1:
647 1
            return False
648 1
        running_tasks = self._client.list_tasks(
649 1
            cluster_name=service.cluster,
650 1
            service_name=service.name
651 1
        )
652
        if not running_tasks[u'taskArns']:
653 1
            return service.desired_count == 0
654
        running_count = self.get_running_tasks_count(
655 1
            service=service,
656
            task_arns=running_tasks[u'taskArns']
657 1
        )
658
        return service.desired_count == running_count
659 1
660
    def get_running_tasks_count(self, service, task_arns):
661 1
        running_count = 0
662
        tasks_details = self._client.describe_tasks(
663 1
            cluster_name=self._cluster_name,
664
            task_arns=task_arns
665 1
        )
666
        for task in tasks_details[u'tasks']:
667 1
            arn = task[u'taskDefinitionArn']
668
            status = task[u'lastStatus']
669
            if arn == service.task_definition and status == u'RUNNING':
670 1
                running_count += 1
671 1
        return running_count
672 1
673 1
    @property
674 1
    def client(self):
675 1
        return self._client
676 1
677
    @property
678
    def service(self):
679 1
        return self._service
680 1
681 1
    @property
682 1
    def cluster_name(self):
683 1
        return self._cluster_name
684 1
685
    @property
686
    def service_name(self):
687 1
        return self._service_name
688 1
689 1
690 1
class DeployAction(EcsAction):
691 1
    def deploy(self, task_definition):
692 1
        try:
693
            self._service.set_task_definition(task_definition)
694 1
            return self.update_service(self._service)
695
        except ClientError as e:
696 1
            raise EcsError(str(e))
697 1
698
699
class ScaleAction(EcsAction):
700
    def scale(self, desired_count):
701
        try:
702
            return self.update_service(self._service, desired_count)
703
        except ClientError as e:
704
            raise EcsError(str(e))
705
706
707
class RunAction(EcsAction):
708
    def __init__(self, client, cluster_name):
709 1
        super(RunAction, self).__init__(client, cluster_name, None)
710 1
        self._client = client
711 1
        self._cluster_name = cluster_name
712 1
        self.started_tasks = []
713
714
    def run(self, task_definition, count, started_by, launchtype, subnets,
715 1
            security_groups, public_ip, platform_version):
716 1
        try:
717 1
            result = self._client.run_task(
718
                cluster=self._cluster_name,
719
                task_definition=task_definition.family_revision,
720 1
                count=count,
721 1
                started_by=started_by,
722 1
                overrides=dict(containerOverrides=task_definition.get_overrides()),
723
                launchtype=launchtype,
724
                subnets=subnets,
725 1
                security_groups=security_groups,
726 1
                public_ip=public_ip,
727
                platform_version=platform_version,
728
            )
729 1
            self.started_tasks = result['tasks']
730 1
            return True
731
        except ClientError as e:
732
            raise EcsError(str(e))
733 1
734 1
735
class UpdateAction(EcsAction):
736
    def __init__(self, client):
737 1
        super(UpdateAction, self).__init__(client, None, None)
738 1
739
740
class DiffAction(EcsAction):
741 1
    def __init__(self, client):
742 1
        super(DiffAction, self).__init__(client, None, None)
743
744
745 1
class EcsError(Exception):
746 1
    pass
747
748
749
class EcsConnectionError(EcsError):
750
    pass
751
752
753
class UnknownContainerError(EcsError):
754
    pass
755
756
757
class TaskPlacementError(EcsError):
758
    pass
759
760
761
class UnknownTaskDefinitionError(EcsError):
762
    pass
763
764
765
class EcsTaskDefinitionCommandError(EcsError):
766
    pass
767