Skip to content

Commit f00a841

Browse files
author
Germano Fronza
committed
Support multiple images update
1 parent 3f282cd commit f00a841

File tree

3 files changed

+73
-43
lines changed

3 files changed

+73
-43
lines changed

README.rst

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,13 @@ To run the script::
3737
-r | --region AWS Region Name. May also be set as environment variable AWS_DEFAULT_REGION
3838
-p | --profile AWS Profile to use - If you set this aws-access-key, aws-secret-key and region are needed
3939
-c | --cluster Name of ECS cluster
40-
-i | --image Name of Docker image to run, ex: repo/image:latest
41-
Format: [domain][:port][/repo][/][image][:tag]
42-
Examples: mariadb, mariadb:latest, silintl/mariadb, silintl/mariadb:latest, private.registry.com:8000/repo/image:tag
40+
-l |--container-image CONTAINER_IMAGE
41+
Name of the container with the name of Docker image to
42+
run Format: [container_name]=[domain][:port][/repo][/]
43+
[image][:tag] Examples: db=mariadb,
44+
database=mariadb:latest, db=silintl/mariadb:latest,
45+
my_container|private.registry.com:8000/repo/image:tag.
46+
You can use this argument multiple times to replace
4347
Optional arguments:
4448
-D | --desired-count The number of instantiations of the task to place and keep running in your service.
4549
-m | --min minumumHealthyPercent: The lower limit on the number of running tasks during a deployment.

ecs_deploy.py

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#!/usr/bin/env python
12
# -*- coding: utf-8 -*-
23
"""
34
ecs-deploy.py
@@ -26,7 +27,9 @@ def __init__(self):
2627
'aws_access_key_id')
2728
credentials = self._arg_kwargs(credentials, 'aws_secret_key',
2829
'aws_secret_access_key')
29-
credentials = self._arg_kwargs(credentials, 'aws_region', 'region')
30+
credentials = self._arg_kwargs(credentials, 'aws_region',
31+
'region_name')
32+
3033
# init boto3 ecs client
3134
self.client = boto3.client('ecs', **credentials)
3235
except ClientError as err:
@@ -77,7 +80,7 @@ def _init_parser(self):
7780

7881
parser.add_argument(
7982
'-r',
80-
'--region',
83+
'--aws-region',
8184
help='AWS Region Name. May also be set as environment variable \
8285
AWS_DEFAULT_REGION')
8386

@@ -103,13 +106,17 @@ def _init_parser(self):
103106
help='Name of ECS cluster')
104107

105108
parser.add_argument(
106-
'-i',
107-
'--image',
109+
'-l',
110+
'--container-image',
111+
action='append',
108112
required=True,
109-
help='Name of Docker image to run, ex: repo/image:latest\nFormat: \
110-
[domain][:port][/repo][/][image][:tag]\nExamples: mariadb, \
111-
mariadb:latest, silintl/mariadb,\nsilintl/mariadb:latest, \
112-
private.registry.com:8000/repo/image:tag')
113+
help='Name of the container with the name of Docker image to run\
114+
\nFormat: \
115+
[container_name]=[domain][:port][/repo][/][image][:tag]\
116+
\nExamples: db=mariadb, database=mariadb:latest, \
117+
db=silintl/mariadb:latest, \
118+
my_container|private.registry.com:8000/repo/image:tag. \
119+
You can use this argument multiple times to replace images.')
113120

114121
# OPTIONAL ARGUMENTS
115122

@@ -166,26 +173,40 @@ def _run_parser(self):
166173
self.task_definition_name = self._task_definition_name()
167174
self.service_name = self._service_name()
168175

169-
self.task_definition = \
170-
self.client_fn('describe_task_definition')['taskDefinition']
171-
self.new_task_definition = \
172-
self.client_fn('register_task_definition')['taskDefinition']
176+
self.task_definition = self.client_fn(
177+
'describe_task_definition')['taskDefinition']
178+
print('Current task definition: %s' %
179+
self.task_definition['taskDefinitionArn'])
180+
181+
self.new_task_definition = self.client_fn(
182+
'register_task_definition')['taskDefinition']
183+
print('New task definition: %s' %
184+
self.new_task_definition['taskDefinitionArn'])
173185

174186
if self.task_definition:
175187
if not self.client_fn('update_service'):
176188
sys.exit(1)
177189

178190
# loop for desired timeout
179-
timeout = self.args.get('timeout') or time.time() + 90
191+
timeout = self.args.get('timeout') or 90
192+
started_at = time.time()
180193
while True:
181-
updated = False
182-
running_tasks = self.client_fn('describe_tasks')['tasks']
183-
for task in running_tasks:
194+
self.running_tasks = self.client_fn('list_tasks')['taskArns']
195+
196+
if len(self.running_tasks) > 0:
197+
described_tasks = self.client_fn('describe_tasks')['tasks']
198+
else:
199+
described_tasks = []
200+
201+
for task in described_tasks:
184202
if task['taskDefinitionArn'] == \
185203
self.new_task_definition['taskDefinitionArn']:
186-
print('SUCCESS')
187-
updated = True
188-
if updated or time.time() > timeout:
204+
print('Service updated successfully, '
205+
'new task definition running.')
206+
return
207+
if time.time() > started_at + timeout:
208+
print('ERROR: New task definition not running'
209+
'within %d seconds' % timeout)
189210
sys.exit(1)
190211
time.sleep(1)
191212

@@ -235,10 +256,22 @@ def client_kwargs(self, fn):
235256
kwargs['family'] = self.task_definition['family']
236257
kwargs['containerDefinitions'] = \
237258
self.task_definition['containerDefinitions']
259+
238260
# optional kwargs from args
239-
if self.args.get('image'):
240-
kwargs['containerDefinitions'][0]['image'] = \
241-
self.args.get('image')
261+
for ci in self.args.get('container_image'):
262+
ci_container_name, ci_image = ci.split('=')
263+
container_found = False
264+
265+
for cd in kwargs['containerDefinitions']:
266+
if cd['name'] == ci_container_name:
267+
cd['image'] = ci_image
268+
container_found = True
269+
270+
if not container_found:
271+
print('Container %s not found in the task definition %s' %
272+
(ci_container_name,
273+
self.task_definition['taskDefinitionArn']))
274+
sys.exit(1)
242275

243276
elif fn == 'update_service':
244277
kwargs['cluster'] = self.cluster
@@ -260,7 +293,7 @@ def client_kwargs(self, fn):
260293

261294
elif fn == 'describe_tasks':
262295
kwargs['cluster'] = self.cluster
263-
kwargs['tasks'] = self.client_fn('list_tasks')['taskArns']
296+
kwargs['tasks'] = self.running_tasks
264297

265298
return kwargs
266299

tests/test_script.py

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import pytest
21
import mock
32
import boto3
43
from moto import mock_ecs
@@ -126,7 +125,7 @@ def setUp(self):
126125
'cluster': mock_cluster['cluster']['clusterName'],
127126
'task_definition': mock_task['taskDefinition']['family'],
128127
'service_name': mock_service['service']['serviceName'],
129-
'image': 'mock_image'
128+
'container_image': ['string=mock_image']
130129
}
131130

132131
self.mock_cli.cluster = mock_cluster['cluster']
@@ -231,7 +230,7 @@ def test_client_kwargs_with_register_task_definition(self):
231230
assert 'family' in mock_kwargs
232231
assert 'containerDefinitions' in mock_kwargs
233232
assert mock_kwargs['containerDefinitions'][0]['image'] == \
234-
mock_cli.args['image']
233+
mock_cli.args['container_image'][0].split('=')[1]
235234
assert mock_kwargs['family'] == mock_cli.task_definition['family']
236235

237236
def test_client_kwargs_with_update_service(self):
@@ -343,23 +342,17 @@ def test_client_kwargs_with_list_tasks(self):
343342
def test_client_kwargs_with_describe_tasks(self):
344343
mock_cli, client = self.setUp()
345344
mock_cli.service_name = mock_cli.args['service_name']
345+
mock_cli.running_tasks = [""]
346346

347-
with mock.patch.object(mock_cli, 'client_fn') as mock_fn:
348-
mock_list_tasks_response = {
349-
'taskArns': [
350-
mock_cli.task_definition['taskDefinitionArn']
351-
]
352-
}
353-
mock_fn.return_value = mock_list_tasks_response
354-
mock_kwargs = mock_cli.client_kwargs('describe_tasks')
347+
mock_kwargs = mock_cli.client_kwargs('describe_tasks')
355348

356-
assert 'cluster' in mock_kwargs
357-
assert 'tasks' in mock_kwargs
358-
assert mock_kwargs['cluster']['clusterName'] == \
359-
mock_cli.args['cluster']
360-
assert mock_kwargs['tasks'] == mock_list_tasks_response['taskArns']
349+
assert 'cluster' in mock_kwargs
350+
assert 'tasks' in mock_kwargs
351+
assert mock_kwargs['cluster']['clusterName'] == \
352+
mock_cli.args['cluster']
353+
assert mock_kwargs['tasks'] == mock_cli.running_tasks
361354

362-
def test_client_kwargs_with_describe_tasks(self):
355+
def test_client_kwargs_with_list_services_once(self):
363356
mock_cli, client = self.setUp()
364357
mock_cli.client = client
365358

0 commit comments

Comments
 (0)