Merge pull request #5982 from docker/5933-retrieve-legacy-containers

Allow all Compose commands to retrieve and handle legacy-name containers
This commit is contained in:
Joffrey F 2018-05-24 11:33:43 -07:00 committed by GitHub
commit 706164accd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 21 deletions

View File

@ -9,6 +9,8 @@ from docker.errors import ImageNotFound
from .const import LABEL_CONTAINER_NUMBER from .const import LABEL_CONTAINER_NUMBER
from .const import LABEL_PROJECT from .const import LABEL_PROJECT
from .const import LABEL_SERVICE from .const import LABEL_SERVICE
from .const import LABEL_VERSION
from .version import ComposeVersion
class Container(object): class Container(object):
@ -283,6 +285,12 @@ class Container(object):
def attach(self, *args, **kwargs): def attach(self, *args, **kwargs):
return self.client.attach(self.id, *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): def __repr__(self):
return '<Container: %s (%s)>' % (self.name, self.id[:6]) return '<Container: %s (%s)>' % (self.name, self.id[:6])

View File

@ -4,6 +4,7 @@ from __future__ import unicode_literals
import datetime import datetime
import logging import logging
import operator import operator
import re
from functools import reduce from functools import reduce
import enum import enum
@ -70,8 +71,11 @@ class Project(object):
self.networks = networks or ProjectNetworks({}, False) self.networks = networks or ProjectNetworks({}, False)
self.config_version = config_version self.config_version = config_version
def labels(self, one_off=OneOffFilter.exclude): def labels(self, one_off=OneOffFilter.exclude, legacy=False):
labels = ['{0}={1}'.format(LABEL_PROJECT, self.name)] name = self.name
if legacy:
name = re.sub(r'[_-]', '', name)
labels = ['{0}={1}'.format(LABEL_PROJECT, name)]
OneOffFilter.update_labels(one_off, labels) OneOffFilter.update_labels(one_off, labels)
return labels return labels
@ -571,12 +575,21 @@ class Project(object):
service.push(ignore_push_failures) service.push(ignore_push_failures)
def _labeled_containers(self, stopped=False, one_off=OneOffFilter.exclude): def _labeled_containers(self, stopped=False, one_off=OneOffFilter.exclude):
return list(filter(None, [ ctnrs = list(filter(None, [
Container.from_ps(self.client, container) Container.from_ps(self.client, container)
for container in self.client.containers( for container in self.client.containers(
all=stopped, all=stopped,
filters={'label': self.labels(one_off=one_off)})]) 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): def containers(self, service_names=None, stopped=False, one_off=OneOffFilter.exclude):
if service_names: if service_names:

View File

@ -1,6 +1,7 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import unicode_literals from __future__ import unicode_literals
import itertools
import logging import logging
import os import os
import re import re
@ -51,7 +52,6 @@ from .progress_stream import StreamOutputError
from .utils import json_hash from .utils import json_hash
from .utils import parse_bytes from .utils import parse_bytes
from .utils import parse_seconds_float from .utils import parse_seconds_float
from .version import ComposeVersion
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -192,8 +192,8 @@ class Service(object):
def __repr__(self): def __repr__(self):
return '<Service: {}>'.format(self.name) return '<Service: {}>'.format(self.name)
def containers(self, stopped=False, one_off=False, filters={}): def containers(self, stopped=False, one_off=False, filters={}, labels=None):
filters.update({'label': self.labels(one_off=one_off)}) filters.update({'label': self.labels(one_off=one_off) + (labels or [])})
result = list(filter(None, [ result = list(filter(None, [
Container.from_ps(self.client, container) Container.from_ps(self.client, container)
@ -204,10 +204,10 @@ class Service(object):
if result: if result:
return 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( return list(
filter( 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) Container.from_ps(self.client, container)
for container in self.client.containers( for container in self.client.containers(
all=stopped, all=stopped,
@ -219,9 +219,9 @@ class Service(object):
"""Return a :class:`compose.container.Container` for this service. The """Return a :class:`compose.container.Container` for this service. The
container must be active, and match `number`. 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}): for container in self.containers(labels=['{0}={1}'.format(LABEL_CONTAINER_NUMBER, number)]):
return Container.from_ps(self.client, container) return container
raise ValueError("No container found for %s_%s" % (self.name, number)) raise ValueError("No container found for %s_%s" % (self.name, number))
@ -258,6 +258,11 @@ class Service(object):
running_containers = self.containers(stopped=False) running_containers = self.containers(stopped=False)
num_running = len(running_containers) 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: if desired_num == num_running:
# do nothing as we already have the desired number # do nothing as we already have the desired number
@ -404,7 +409,7 @@ class Service(object):
has_diverged = False has_diverged = False
for c in containers: 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) log.debug('%s has diverged: Legacy project name' % c.name)
has_diverged = True has_diverged = True
continue continue
@ -713,9 +718,14 @@ class Service(object):
# TODO: this would benefit from github.com/docker/docker/pull/14699 # TODO: this would benefit from github.com/docker/docker/pull/14699
# to remove the need to inspect every container # to remove the need to inspect every container
def _next_container_number(self, one_off=False): def _next_container_number(self, one_off=False):
containers = self._fetch_containers( containers = itertools.chain(
all=True, self._fetch_containers(
filters={'label': self.labels(one_off=one_off)} 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] numbers = [c.number for c in containers]
return 1 if not numbers else max(numbers) + 1 return 1 if not numbers else max(numbers) + 1
@ -1243,12 +1253,6 @@ class Service(object):
return result 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): def short_id_alias_exists(container, network):
aliases = container.get( aliases = container.get(