From e245fb04cfb2703950e458cf3a81ac6e50db1886 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Wed, 23 May 2018 16:28:41 -0700 Subject: [PATCH] Allow all Compose commands to retrieve and handle legacy-name containers Signed-off-by: Joffrey F --- compose/container.py | 8 ++++++++ compose/project.py | 19 ++++++++++++++++--- compose/service.py | 40 ++++++++++++++++++++++------------------ 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/compose/container.py b/compose/container.py index 0c2ca9902..8dac8cacd 100644 --- a/compose/container.py +++ b/compose/container.py @@ -9,6 +9,8 @@ from docker.errors import ImageNotFound from .const import LABEL_CONTAINER_NUMBER from .const import LABEL_PROJECT from .const import LABEL_SERVICE +from .const import LABEL_VERSION +from .version import ComposeVersion class Container(object): @@ -283,6 +285,12 @@ class Container(object): def attach(self, *args, **kwargs): return self.client.attach(self.id, *args, **kwargs) + def has_legacy_proj_name(self, project_name): + return ( + ComposeVersion(self.labels.get(LABEL_VERSION)) < ComposeVersion('1.21.0') and + self.project != project_name + ) + def __repr__(self): return '' % (self.name, self.id[:6]) diff --git a/compose/project.py b/compose/project.py index c27794fc3..005b7e240 100644 --- a/compose/project.py +++ b/compose/project.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import datetime import logging import operator +import re from functools import reduce import enum @@ -70,8 +71,11 @@ class Project(object): self.networks = networks or ProjectNetworks({}, False) self.config_version = config_version - def labels(self, one_off=OneOffFilter.exclude): - labels = ['{0}={1}'.format(LABEL_PROJECT, self.name)] + def labels(self, one_off=OneOffFilter.exclude, legacy=False): + name = self.name + if legacy: + name = re.sub(r'[_-]', '', name) + labels = ['{0}={1}'.format(LABEL_PROJECT, name)] OneOffFilter.update_labels(one_off, labels) return labels @@ -571,12 +575,21 @@ class Project(object): service.push(ignore_push_failures) def _labeled_containers(self, stopped=False, one_off=OneOffFilter.exclude): - return list(filter(None, [ + ctnrs = list(filter(None, [ Container.from_ps(self.client, container) for container in self.client.containers( all=stopped, filters={'label': self.labels(one_off=one_off)})]) ) + if ctnrs: + return ctnrs + + return list(filter(lambda c: c.has_legacy_proj_name(self.name), filter(None, [ + Container.from_ps(self.client, container) + for container in self.client.containers( + all=stopped, + filters={'label': self.labels(one_off=one_off, legacy=True)})]) + )) def containers(self, service_names=None, stopped=False, one_off=OneOffFilter.exclude): if service_names: diff --git a/compose/service.py b/compose/service.py index 932ed8b34..48cbc1702 100644 --- a/compose/service.py +++ b/compose/service.py @@ -1,6 +1,7 @@ from __future__ import absolute_import from __future__ import unicode_literals +import itertools import logging import os import re @@ -51,7 +52,6 @@ from .progress_stream import StreamOutputError from .utils import json_hash from .utils import parse_bytes from .utils import parse_seconds_float -from .version import ComposeVersion log = logging.getLogger(__name__) @@ -192,8 +192,8 @@ class Service(object): def __repr__(self): return ''.format(self.name) - def containers(self, stopped=False, one_off=False, filters={}): - filters.update({'label': self.labels(one_off=one_off)}) + def containers(self, stopped=False, one_off=False, filters={}, labels=None): + filters.update({'label': self.labels(one_off=one_off) + (labels or [])}) result = list(filter(None, [ Container.from_ps(self.client, container) @@ -204,10 +204,10 @@ class Service(object): if result: return result - filters.update({'label': self.labels(one_off=one_off, legacy=True)}) + filters.update({'label': self.labels(one_off=one_off, legacy=True) + (labels or [])}) return list( filter( - self.has_legacy_proj_name, filter(None, [ + lambda c: c.has_legacy_proj_name(self.project), filter(None, [ Container.from_ps(self.client, container) for container in self.client.containers( all=stopped, @@ -219,9 +219,9 @@ class Service(object): """Return a :class:`compose.container.Container` for this service. The container must be active, and match `number`. """ - labels = self.labels() + ['{0}={1}'.format(LABEL_CONTAINER_NUMBER, number)] - for container in self.client.containers(filters={'label': labels}): - return Container.from_ps(self.client, container) + + for container in self.containers(labels=['{0}={1}'.format(LABEL_CONTAINER_NUMBER, number)]): + return container raise ValueError("No container found for %s_%s" % (self.name, number)) @@ -258,6 +258,11 @@ class Service(object): running_containers = self.containers(stopped=False) num_running = len(running_containers) + for c in running_containers: + if not c.has_legacy_proj_name(self.project): + continue + log.info('Recreating container with legacy name %s' % c.name) + self.recreate_container(c, timeout, start_new_container=False) if desired_num == num_running: # do nothing as we already have the desired number @@ -404,7 +409,7 @@ class Service(object): has_diverged = False for c in containers: - if self.has_legacy_proj_name(c): + if c.has_legacy_proj_name(self.project): log.debug('%s has diverged: Legacy project name' % c.name) has_diverged = True continue @@ -713,9 +718,14 @@ class Service(object): # TODO: this would benefit from github.com/docker/docker/pull/14699 # to remove the need to inspect every container def _next_container_number(self, one_off=False): - containers = self._fetch_containers( - all=True, - filters={'label': self.labels(one_off=one_off)} + containers = itertools.chain( + self._fetch_containers( + all=True, + filters={'label': self.labels(one_off=one_off)} + ), self._fetch_containers( + all=True, + filters={'label': self.labels(one_off=one_off, legacy=True)} + ) ) numbers = [c.number for c in containers] return 1 if not numbers else max(numbers) + 1 @@ -1243,12 +1253,6 @@ class Service(object): return result - def has_legacy_proj_name(self, ctnr): - return ( - ComposeVersion(ctnr.labels.get(LABEL_VERSION)) < ComposeVersion('1.21.0') and - ctnr.project != self.project - ) - def short_id_alias_exists(container, network): aliases = container.get(