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
@@ -182,10 +182,10 @@ def create_container(self,
182
182
Create a container for this service. If the image doesn't exist, attempt to pull
183
183
it.
184
184
"""
185
- override_options ['volumes_from' ] = self ._get_volumes_from (previous_container )
186
185
container_options = self ._get_container_create_options (
187
186
override_options ,
188
187
one_off = one_off ,
188
+ previous_container = previous_container ,
189
189
)
190
190
191
191
if (do_build and
@@ -325,7 +325,7 @@ def _get_links(self, link_to_self):
325
325
links .append ((external_link , link_name ))
326
326
return links
327
327
328
- def _get_volumes_from (self , previous_container = None ):
328
+ def _get_volumes_from (self ):
329
329
volumes_from = []
330
330
for volume_source in self .volumes_from :
331
331
if isinstance (volume_source , Service ):
@@ -338,9 +338,6 @@ def _get_volumes_from(self, previous_container=None):
338
338
elif isinstance (volume_source , Container ):
339
339
volumes_from .append (volume_source .id )
340
340
341
- if previous_container :
342
- volumes_from .append (previous_container .id )
343
-
344
341
return volumes_from
345
342
346
343
def _get_net (self ):
@@ -362,7 +359,11 @@ def _get_net(self):
362
359
363
360
return net
364
361
365
- def _get_container_create_options (self , override_options , one_off = False ):
362
+ def _get_container_create_options (
363
+ self ,
364
+ override_options ,
365
+ one_off = False ,
366
+ previous_container = None ):
366
367
container_options = dict (
367
368
(k , self .options [k ])
368
369
for k in DOCKER_CONFIG_KEYS if k in self .options )
@@ -395,6 +396,10 @@ def _get_container_create_options(self, override_options, one_off=False):
395
396
ports .append (port )
396
397
container_options ['ports' ] = ports
397
398
399
+ override_options ['binds' ] = merge_volume_bindings (
400
+ container_options .get ('volumes' ) or [],
401
+ previous_container )
402
+
398
403
if 'volumes' in container_options :
399
404
container_options ['volumes' ] = dict (
400
405
(parse_volume_spec (v ).internal , {})
@@ -417,11 +422,6 @@ def _get_container_host_config(self, override_options, one_off=False):
417
422
options = dict (self .options , ** override_options )
418
423
port_bindings = build_port_bindings (options .get ('ports' ) or [])
419
424
420
- volume_bindings = dict (
421
- build_volume_binding (parse_volume_spec (volume ))
422
- for volume in options .get ('volumes' ) or []
423
- if ':' in volume )
424
-
425
425
privileged = options .get ('privileged' , False )
426
426
cap_add = options .get ('cap_add' , None )
427
427
cap_drop = options .get ('cap_drop' , None )
@@ -444,8 +444,8 @@ def _get_container_host_config(self, override_options, one_off=False):
444
444
return create_host_config (
445
445
links = self ._get_links (link_to_self = one_off ),
446
446
port_bindings = port_bindings ,
447
- binds = volume_bindings ,
448
- volumes_from = options . get ( 'volumes_from' ),
447
+ binds = options . get ( 'binds' ) ,
448
+ volumes_from = self . _get_volumes_from ( ),
449
449
privileged = privileged ,
450
450
network_mode = self ._get_net (),
451
451
dns = dns ,
@@ -527,6 +527,50 @@ def pull(self, insecure_registry=False):
527
527
stream_output (output , sys .stdout )
528
528
529
529
530
+ def get_container_data_volumes (container , volumes_option ):
531
+ """Find the container data volumes that are in `volumes_option`, and return
532
+ a mapping of volume bindings for those volumes.
533
+ """
534
+ volumes = []
535
+
536
+ volumes_option = volumes_option or []
537
+ container_volumes = container .get ('Volumes' ) or {}
538
+ image_volumes = container .image_config ['ContainerConfig' ].get ('Volumes' ) or {}
539
+
540
+ for volume in set (volumes_option + image_volumes .keys ()):
541
+ volume = parse_volume_spec (volume )
542
+ # No need to preserve host volumes
543
+ if volume .external :
544
+ continue
545
+
546
+ volume_path = container_volumes .get (volume .internal )
547
+ # New volume, doesn't exist in the old container
548
+ if not volume_path :
549
+ continue
550
+
551
+ # Copy existing volume from old container
552
+ volume = volume ._replace (external = volume_path )
553
+ volumes .append (build_volume_binding (volume ))
554
+
555
+ return dict (volumes )
556
+
557
+
558
+ def merge_volume_bindings (volumes_option , previous_container ):
559
+ """Return a list of volume bindings for a container. Container data volumes
560
+ are replaced by those from the previous container.
561
+ """
562
+ volume_bindings = dict (
563
+ build_volume_binding (parse_volume_spec (volume ))
564
+ for volume in volumes_option or []
565
+ if ':' in volume )
566
+
567
+ if previous_container :
568
+ volume_bindings .update (
569
+ get_container_data_volumes (previous_container , volumes_option ))
570
+
571
+ return volume_bindings
572
+
573
+
530
574
NAME_RE = re .compile (r'^([^_]+)_([^_]+)_(run_)?(\d+)$' )
531
575
532
576
0 commit comments