3
3
from collections import namedtuple
4
4
import logging
5
5
import re
6
- from operator import attrgetter
7
6
import sys
8
- import six
7
+ from operator import attrgetter
9
8
9
+ import six
10
10
from docker .errors import APIError
11
11
from docker .utils import create_host_config , LogConfig
12
12
13
- from .config import DOCKER_CONFIG_KEYS
13
+ from .config import DOCKER_CONFIG_KEYS , merge_environment
14
14
from .container import Container , get_container_name
15
15
from .progress_stream import stream_output , StreamOutputError
16
16
@@ -183,10 +183,10 @@ def create_container(self,
183
183
Create a container for this service. If the image doesn't exist, attempt to pull
184
184
it.
185
185
"""
186
- override_options ['volumes_from' ] = self ._get_volumes_from (previous_container )
187
186
container_options = self ._get_container_create_options (
188
187
override_options ,
189
188
one_off = one_off ,
189
+ previous_container = previous_container ,
190
190
)
191
191
192
192
if (do_build and
@@ -247,6 +247,12 @@ def recreate_container(self, container, **override_options):
247
247
self .client .rename (
248
248
container .id ,
249
249
'%s_%s' % (container .short_id , container .name ))
250
+
251
+ override_options = dict (
252
+ override_options ,
253
+ environment = merge_environment (
254
+ override_options .get ('environment' ),
255
+ {'affinity:container' : '=' + container .id }))
250
256
new_container = self .create_container (
251
257
do_build = False ,
252
258
previous_container = container ,
@@ -326,7 +332,7 @@ def _get_links(self, link_to_self):
326
332
links .append ((external_link , link_name ))
327
333
return links
328
334
329
- def _get_volumes_from (self , previous_container = None ):
335
+ def _get_volumes_from (self ):
330
336
volumes_from = []
331
337
for volume_source in self .volumes_from :
332
338
if isinstance (volume_source , Service ):
@@ -339,9 +345,6 @@ def _get_volumes_from(self, previous_container=None):
339
345
elif isinstance (volume_source , Container ):
340
346
volumes_from .append (volume_source .id )
341
347
342
- if previous_container :
343
- volumes_from .append (previous_container .id )
344
-
345
348
return volumes_from
346
349
347
350
def _get_net (self ):
@@ -363,7 +366,11 @@ def _get_net(self):
363
366
364
367
return net
365
368
366
- def _get_container_create_options (self , override_options , one_off = False ):
369
+ def _get_container_create_options (
370
+ self ,
371
+ override_options ,
372
+ one_off = False ,
373
+ previous_container = None ):
367
374
container_options = dict (
368
375
(k , self .options [k ])
369
376
for k in DOCKER_CONFIG_KEYS if k in self .options )
@@ -396,11 +403,19 @@ def _get_container_create_options(self, override_options, one_off=False):
396
403
ports .append (port )
397
404
container_options ['ports' ] = ports
398
405
406
+ override_options ['binds' ] = merge_volume_bindings (
407
+ container_options .get ('volumes' ) or [],
408
+ previous_container )
409
+
399
410
if 'volumes' in container_options :
400
411
container_options ['volumes' ] = dict (
401
412
(parse_volume_spec (v ).internal , {})
402
413
for v in container_options ['volumes' ])
403
414
415
+ container_options ['environment' ] = merge_environment (
416
+ self .options .get ('environment' ),
417
+ override_options .get ('environment' ))
418
+
404
419
if self .can_be_built ():
405
420
container_options ['image' ] = self .full_name
406
421
@@ -418,11 +433,6 @@ def _get_container_host_config(self, override_options, one_off=False):
418
433
options = dict (self .options , ** override_options )
419
434
port_bindings = build_port_bindings (options .get ('ports' ) or [])
420
435
421
- volume_bindings = dict (
422
- build_volume_binding (parse_volume_spec (volume ))
423
- for volume in options .get ('volumes' ) or []
424
- if ':' in volume )
425
-
426
436
privileged = options .get ('privileged' , False )
427
437
cap_add = options .get ('cap_add' , None )
428
438
cap_drop = options .get ('cap_drop' , None )
@@ -447,8 +457,8 @@ def _get_container_host_config(self, override_options, one_off=False):
447
457
return create_host_config (
448
458
links = self ._get_links (link_to_self = one_off ),
449
459
port_bindings = port_bindings ,
450
- binds = volume_bindings ,
451
- volumes_from = options . get ( 'volumes_from' ),
460
+ binds = options . get ( 'binds' ) ,
461
+ volumes_from = self . _get_volumes_from ( ),
452
462
privileged = privileged ,
453
463
network_mode = self ._get_net (),
454
464
devices = devices ,
@@ -531,6 +541,50 @@ def pull(self, insecure_registry=False):
531
541
stream_output (output , sys .stdout )
532
542
533
543
544
+ def get_container_data_volumes (container , volumes_option ):
545
+ """Find the container data volumes that are in `volumes_option`, and return
546
+ a mapping of volume bindings for those volumes.
547
+ """
548
+ volumes = []
549
+
550
+ volumes_option = volumes_option or []
551
+ container_volumes = container .get ('Volumes' ) or {}
552
+ image_volumes = container .image_config ['ContainerConfig' ].get ('Volumes' ) or {}
553
+
554
+ for volume in set (volumes_option + image_volumes .keys ()):
555
+ volume = parse_volume_spec (volume )
556
+ # No need to preserve host volumes
557
+ if volume .external :
558
+ continue
559
+
560
+ volume_path = container_volumes .get (volume .internal )
561
+ # New volume, doesn't exist in the old container
562
+ if not volume_path :
563
+ continue
564
+
565
+ # Copy existing volume from old container
566
+ volume = volume ._replace (external = volume_path )
567
+ volumes .append (build_volume_binding (volume ))
568
+
569
+ return dict (volumes )
570
+
571
+
572
+ def merge_volume_bindings (volumes_option , previous_container ):
573
+ """Return a list of volume bindings for a container. Container data volumes
574
+ are replaced by those from the previous container.
575
+ """
576
+ volume_bindings = dict (
577
+ build_volume_binding (parse_volume_spec (volume ))
578
+ for volume in volumes_option or []
579
+ if ':' in volume )
580
+
581
+ if previous_container :
582
+ volume_bindings .update (
583
+ get_container_data_volumes (previous_container , volumes_option ))
584
+
585
+ return volume_bindings
586
+
587
+
534
588
NAME_RE = re .compile (r'^([^_]+)_([^_]+)_(run_)?(\d+)$' )
535
589
536
590
0 commit comments