@@ -242,8 +242,9 @@ def create_container(self,
242242
243243 def recreate_containers (self , insecure_registry = False , do_build = True , ** override_options ):
244244 """
245- If a container for this service doesn't exist, create and start one. If there are
246- any, stop them, create+start new ones, and remove the old containers.
245+ If a container for this service doesn't exist, create and start one. If
246+ there are any, stop them, create+start new ones, and remove the old
247+ containers.
247248 """
248249 containers = self .containers (stopped = True )
249250 if not containers :
@@ -253,21 +254,22 @@ def recreate_containers(self, insecure_registry=False, do_build=True, **override
253254 do_build = do_build ,
254255 ** override_options )
255256 self .start_container (container )
256- return [( None , container ) ]
257+ return [container ]
257258 else :
258- tuples = []
259-
260- for c in containers :
261- log . info ( "Recreating %s..." % c . name )
262- tuples . append ( self . recreate_container ( c , insecure_registry = insecure_registry , ** override_options ) )
263-
264- return tuples
259+ return [
260+ self . recreate_container (
261+ container ,
262+ insecure_registry = insecure_registry ,
263+ ** override_options )
264+ for container in containers
265+ ]
265266
266267 def recreate_container (self , container , ** override_options ):
267268 """Recreate a container. An intermediate container is created so that
268269 the new container has the same name, while still supporting
269270 `volumes-from` the original container.
270271 """
272+ log .info ("Recreating %s..." % container .name )
271273 try :
272274 container .stop ()
273275 except APIError as e :
@@ -278,24 +280,30 @@ def recreate_container(self, container, **override_options):
278280 else :
279281 raise
280282
283+ intermediate_options = dict (self .options , ** override_options )
281284 intermediate_container = Container .create (
282285 self .client ,
283286 image = container .image ,
284287 entrypoint = ['/bin/echo' ],
285288 command = [],
286289 detach = True ,
287290 )
288- intermediate_container .start (volumes_from = container .id )
291+ intermediate_container .start (
292+ binds = get_container_data_volumes (
293+ container , intermediate_options .get ('volumes' )))
289294 intermediate_container .wait ()
290295 container .remove ()
291296
297+ # TODO: volumes are being passed to both start and create, this is
298+ # probably unnecessary
292299 options = dict (override_options )
293300 new_container = self .create_container (do_build = False , ** options )
294- self .start_container (new_container , intermediate_container = intermediate_container )
301+ self .start_container (
302+ new_container ,
303+ intermediate_container = intermediate_container )
295304
296305 intermediate_container .remove ()
297-
298- return (intermediate_container , new_container )
306+ return new_container
299307
300308 def start_container_if_stopped (self , container , ** options ):
301309 if container .is_running :
@@ -307,12 +315,6 @@ def start_container_if_stopped(self, container, **options):
307315 def start_container (self , container , intermediate_container = None , ** override_options ):
308316 options = dict (self .options , ** override_options )
309317 port_bindings = build_port_bindings (options .get ('ports' ) or [])
310-
311- volume_bindings = dict (
312- build_volume_binding (parse_volume_spec (volume ))
313- for volume in options .get ('volumes' ) or []
314- if ':' in volume )
315-
316318 privileged = options .get ('privileged' , False )
317319 net = options .get ('net' , 'bridge' )
318320 dns = options .get ('dns' , None )
@@ -321,12 +323,14 @@ def start_container(self, container, intermediate_container=None, **override_opt
321323 cap_drop = options .get ('cap_drop' , None )
322324
323325 restart = parse_restart_spec (options .get ('restart' , None ))
326+ binds = get_volume_bindings (
327+ options .get ('volumes' ), intermediate_container )
324328
325329 container .start (
326330 links = self ._get_links (link_to_self = options .get ('one_off' , False )),
327331 port_bindings = port_bindings ,
328- binds = volume_bindings ,
329- volumes_from = self ._get_volumes_from (intermediate_container ),
332+ binds = binds ,
333+ volumes_from = self ._get_volumes_from (),
330334 privileged = privileged ,
331335 network_mode = net ,
332336 dns = dns ,
@@ -388,7 +392,7 @@ def _get_links(self, link_to_self):
388392 links .append ((external_link , link_name ))
389393 return links
390394
391- def _get_volumes_from (self , intermediate_container = None ):
395+ def _get_volumes_from (self ):
392396 volumes_from = []
393397 for volume_source in self .volumes_from :
394398 if isinstance (volume_source , Service ):
@@ -402,9 +406,6 @@ def _get_volumes_from(self, intermediate_container=None):
402406 elif isinstance (volume_source , Container ):
403407 volumes_from .append (volume_source .id )
404408
405- if intermediate_container :
406- volumes_from .append (intermediate_container .id )
407-
408409 return volumes_from
409410
410411 def _get_container_create_options (self , override_options , one_off = False ):
@@ -519,6 +520,45 @@ def pull(self, insecure_registry=False):
519520 )
520521
521522
523+ def get_container_data_volumes (container , volumes_option ):
524+ """Find the container data volumes that are in `volumes_option`, and return
525+ a mapping of volume bindings for those volumes.
526+ """
527+ volumes = []
528+ for volume in volumes_option or []:
529+ volume = parse_volume_spec (volume )
530+ # No need to preserve host volumes
531+ if volume .external :
532+ continue
533+
534+ volume_path = (container .get ('Volumes' ) or {}).get (volume .internal )
535+ # New volume, doesn't exist in the old container
536+ if not volume_path :
537+ continue
538+
539+ # Copy existing volume from old container
540+ volume = volume ._replace (external = volume_path )
541+ volumes .append (build_volume_binding (volume ))
542+
543+ return dict (volumes )
544+
545+
546+ def get_volume_bindings (volumes_option , intermediate_container ):
547+ """Return a list of volume bindings for a container. Container data volume
548+ bindings are replaced by those in the intermediate container.
549+ """
550+ volume_bindings = dict (
551+ build_volume_binding (parse_volume_spec (volume ))
552+ for volume in volumes_option or []
553+ if ':' in volume )
554+
555+ if intermediate_container :
556+ volume_bindings .update (
557+ get_container_data_volumes (intermediate_container , volumes_option ))
558+
559+ return volume_bindings
560+
561+
522562NAME_RE = re .compile (r'^([^_]+)_([^_]+)_(run_)?(\d+)$' )
523563
524564
0 commit comments