mirror of https://github.com/docker/compose.git
Retrieve objects using legacy (< 1.21) project names
Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
parent
1315b51e44
commit
385b65032d
|
@ -2,6 +2,7 @@ from __future__ import absolute_import
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from docker.errors import NotFound
|
from docker.errors import NotFound
|
||||||
|
@ -10,9 +11,11 @@ from docker.types import IPAMPool
|
||||||
from docker.utils import version_gte
|
from docker.utils import version_gte
|
||||||
from docker.utils import version_lt
|
from docker.utils import version_lt
|
||||||
|
|
||||||
|
from . import __version__
|
||||||
from .config import ConfigurationError
|
from .config import ConfigurationError
|
||||||
from .const import LABEL_NETWORK
|
from .const import LABEL_NETWORK
|
||||||
from .const import LABEL_PROJECT
|
from .const import LABEL_PROJECT
|
||||||
|
from .const import LABEL_VERSION
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -39,6 +42,7 @@ class Network(object):
|
||||||
self.enable_ipv6 = enable_ipv6
|
self.enable_ipv6 = enable_ipv6
|
||||||
self.labels = labels
|
self.labels = labels
|
||||||
self.custom_name = custom_name
|
self.custom_name = custom_name
|
||||||
|
self.legacy = False
|
||||||
|
|
||||||
def ensure(self):
|
def ensure(self):
|
||||||
if self.external:
|
if self.external:
|
||||||
|
@ -68,6 +72,14 @@ class Network(object):
|
||||||
data = self.inspect()
|
data = self.inspect()
|
||||||
check_remote_network_config(data, self)
|
check_remote_network_config(data, self)
|
||||||
except NotFound:
|
except NotFound:
|
||||||
|
try:
|
||||||
|
data = self.inspect(legacy=True)
|
||||||
|
self.legacy = True
|
||||||
|
check_remote_network_config(data, self)
|
||||||
|
return
|
||||||
|
except NotFound:
|
||||||
|
pass
|
||||||
|
|
||||||
driver_name = 'the default driver'
|
driver_name = 'the default driver'
|
||||||
if self.driver:
|
if self.driver:
|
||||||
driver_name = 'driver "{}"'.format(self.driver)
|
driver_name = 'driver "{}"'.format(self.driver)
|
||||||
|
@ -94,18 +106,37 @@ class Network(object):
|
||||||
log.info("Network %s is external, skipping", self.full_name)
|
log.info("Network %s is external, skipping", self.full_name)
|
||||||
return
|
return
|
||||||
|
|
||||||
log.info("Removing network {}".format(self.full_name))
|
log.info("Removing network {}".format(self.true_name))
|
||||||
self.client.remove_network(self.full_name)
|
try:
|
||||||
|
self.client.remove_network(self.full_name)
|
||||||
|
except NotFound:
|
||||||
|
self.client.remove_network(self.legacy_full_name)
|
||||||
|
|
||||||
def inspect(self):
|
def inspect(self, legacy=False):
|
||||||
|
if legacy:
|
||||||
|
return self.client.inspect_network(self.legacy_full_name)
|
||||||
return self.client.inspect_network(self.full_name)
|
return self.client.inspect_network(self.full_name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def legacy_full_name(self):
|
||||||
|
if self.custom_name:
|
||||||
|
return self.name
|
||||||
|
return '{0}_{1}'.format(
|
||||||
|
re.sub(r'[_-]', '', self.project), self.name
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_name(self):
|
def full_name(self):
|
||||||
if self.custom_name:
|
if self.custom_name:
|
||||||
return self.name
|
return self.name
|
||||||
return '{0}_{1}'.format(self.project, self.name)
|
return '{0}_{1}'.format(self.project, self.name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def true_name(self):
|
||||||
|
if self.legacy:
|
||||||
|
return self.legacy_full_name
|
||||||
|
return self.full_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _labels(self):
|
def _labels(self):
|
||||||
if version_lt(self.client._version, '1.23'):
|
if version_lt(self.client._version, '1.23'):
|
||||||
|
@ -114,6 +145,7 @@ class Network(object):
|
||||||
labels.update({
|
labels.update({
|
||||||
LABEL_PROJECT: self.project,
|
LABEL_PROJECT: self.project,
|
||||||
LABEL_NETWORK: self.name,
|
LABEL_NETWORK: self.name,
|
||||||
|
LABEL_VERSION: __version__,
|
||||||
})
|
})
|
||||||
return labels
|
return labels
|
||||||
|
|
||||||
|
@ -150,49 +182,49 @@ def check_remote_ipam_config(remote, local):
|
||||||
remote_ipam = remote.get('IPAM')
|
remote_ipam = remote.get('IPAM')
|
||||||
ipam_dict = create_ipam_config_from_dict(local.ipam)
|
ipam_dict = create_ipam_config_from_dict(local.ipam)
|
||||||
if local.ipam.get('driver') and local.ipam.get('driver') != remote_ipam.get('Driver'):
|
if local.ipam.get('driver') and local.ipam.get('driver') != remote_ipam.get('Driver'):
|
||||||
raise NetworkConfigChangedError(local.full_name, 'IPAM driver')
|
raise NetworkConfigChangedError(local.true_name, 'IPAM driver')
|
||||||
if len(ipam_dict['Config']) != 0:
|
if len(ipam_dict['Config']) != 0:
|
||||||
if len(ipam_dict['Config']) != len(remote_ipam['Config']):
|
if len(ipam_dict['Config']) != len(remote_ipam['Config']):
|
||||||
raise NetworkConfigChangedError(local.full_name, 'IPAM configs')
|
raise NetworkConfigChangedError(local.true_name, 'IPAM configs')
|
||||||
remote_configs = sorted(remote_ipam['Config'], key='Subnet')
|
remote_configs = sorted(remote_ipam['Config'], key='Subnet')
|
||||||
local_configs = sorted(ipam_dict['Config'], key='Subnet')
|
local_configs = sorted(ipam_dict['Config'], key='Subnet')
|
||||||
while local_configs:
|
while local_configs:
|
||||||
lc = local_configs.pop()
|
lc = local_configs.pop()
|
||||||
rc = remote_configs.pop()
|
rc = remote_configs.pop()
|
||||||
if lc.get('Subnet') != rc.get('Subnet'):
|
if lc.get('Subnet') != rc.get('Subnet'):
|
||||||
raise NetworkConfigChangedError(local.full_name, 'IPAM config subnet')
|
raise NetworkConfigChangedError(local.true_name, 'IPAM config subnet')
|
||||||
if lc.get('Gateway') is not None and lc.get('Gateway') != rc.get('Gateway'):
|
if lc.get('Gateway') is not None and lc.get('Gateway') != rc.get('Gateway'):
|
||||||
raise NetworkConfigChangedError(local.full_name, 'IPAM config gateway')
|
raise NetworkConfigChangedError(local.true_name, 'IPAM config gateway')
|
||||||
if lc.get('IPRange') != rc.get('IPRange'):
|
if lc.get('IPRange') != rc.get('IPRange'):
|
||||||
raise NetworkConfigChangedError(local.full_name, 'IPAM config ip_range')
|
raise NetworkConfigChangedError(local.true_name, 'IPAM config ip_range')
|
||||||
if sorted(lc.get('AuxiliaryAddresses')) != sorted(rc.get('AuxiliaryAddresses')):
|
if sorted(lc.get('AuxiliaryAddresses')) != sorted(rc.get('AuxiliaryAddresses')):
|
||||||
raise NetworkConfigChangedError(local.full_name, 'IPAM config aux_addresses')
|
raise NetworkConfigChangedError(local.true_name, 'IPAM config aux_addresses')
|
||||||
|
|
||||||
remote_opts = remote_ipam.get('Options') or {}
|
remote_opts = remote_ipam.get('Options') or {}
|
||||||
local_opts = local.ipam.get('Options') or {}
|
local_opts = local.ipam.get('Options') or {}
|
||||||
for k in set.union(set(remote_opts.keys()), set(local_opts.keys())):
|
for k in set.union(set(remote_opts.keys()), set(local_opts.keys())):
|
||||||
if remote_opts.get(k) != local_opts.get(k):
|
if remote_opts.get(k) != local_opts.get(k):
|
||||||
raise NetworkConfigChangedError(local.full_name, 'IPAM option "{}"'.format(k))
|
raise NetworkConfigChangedError(local.true_name, 'IPAM option "{}"'.format(k))
|
||||||
|
|
||||||
|
|
||||||
def check_remote_network_config(remote, local):
|
def check_remote_network_config(remote, local):
|
||||||
if local.driver and remote.get('Driver') != local.driver:
|
if local.driver and remote.get('Driver') != local.driver:
|
||||||
raise NetworkConfigChangedError(local.full_name, 'driver')
|
raise NetworkConfigChangedError(local.true_name, 'driver')
|
||||||
local_opts = local.driver_opts or {}
|
local_opts = local.driver_opts or {}
|
||||||
remote_opts = remote.get('Options') or {}
|
remote_opts = remote.get('Options') or {}
|
||||||
for k in set.union(set(remote_opts.keys()), set(local_opts.keys())):
|
for k in set.union(set(remote_opts.keys()), set(local_opts.keys())):
|
||||||
if k in OPTS_EXCEPTIONS:
|
if k in OPTS_EXCEPTIONS:
|
||||||
continue
|
continue
|
||||||
if remote_opts.get(k) != local_opts.get(k):
|
if remote_opts.get(k) != local_opts.get(k):
|
||||||
raise NetworkConfigChangedError(local.full_name, 'option "{}"'.format(k))
|
raise NetworkConfigChangedError(local.true_name, 'option "{}"'.format(k))
|
||||||
|
|
||||||
if local.ipam is not None:
|
if local.ipam is not None:
|
||||||
check_remote_ipam_config(remote, local)
|
check_remote_ipam_config(remote, local)
|
||||||
|
|
||||||
if local.internal is not None and local.internal != remote.get('Internal', False):
|
if local.internal is not None and local.internal != remote.get('Internal', False):
|
||||||
raise NetworkConfigChangedError(local.full_name, 'internal')
|
raise NetworkConfigChangedError(local.true_name, 'internal')
|
||||||
if local.enable_ipv6 is not None and local.enable_ipv6 != remote.get('EnableIPv6', False):
|
if local.enable_ipv6 is not None and local.enable_ipv6 != remote.get('EnableIPv6', False):
|
||||||
raise NetworkConfigChangedError(local.full_name, 'enable_ipv6')
|
raise NetworkConfigChangedError(local.true_name, 'enable_ipv6')
|
||||||
|
|
||||||
local_labels = local.labels or {}
|
local_labels = local.labels or {}
|
||||||
remote_labels = remote.get('Labels', {})
|
remote_labels = remote.get('Labels', {})
|
||||||
|
@ -202,7 +234,7 @@ def check_remote_network_config(remote, local):
|
||||||
if remote_labels.get(k) != local_labels.get(k):
|
if remote_labels.get(k) != local_labels.get(k):
|
||||||
log.warn(
|
log.warn(
|
||||||
'Network {}: label "{}" has changed. It may need to be'
|
'Network {}: label "{}" has changed. It may need to be'
|
||||||
' recreated.'.format(local.full_name, k)
|
' recreated.'.format(local.true_name, k)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -257,7 +289,7 @@ class ProjectNetworks(object):
|
||||||
try:
|
try:
|
||||||
network.remove()
|
network.remove()
|
||||||
except NotFound:
|
except NotFound:
|
||||||
log.warn("Network %s not found.", network.full_name)
|
log.warn("Network %s not found.", network.true_name)
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
if not self.use_networking:
|
if not self.use_networking:
|
||||||
|
@ -286,7 +318,7 @@ def get_networks(service_dict, network_definitions):
|
||||||
for name, netdef in get_network_defs_for_service(service_dict).items():
|
for name, netdef in get_network_defs_for_service(service_dict).items():
|
||||||
network = network_definitions.get(name)
|
network = network_definitions.get(name)
|
||||||
if network:
|
if network:
|
||||||
networks[network.full_name] = netdef
|
networks[network.true_name] = netdef
|
||||||
else:
|
else:
|
||||||
raise ConfigurationError(
|
raise ConfigurationError(
|
||||||
'Service "{}" uses an undefined network "{}"'
|
'Service "{}" uses an undefined network "{}"'
|
||||||
|
|
|
@ -51,6 +51,7 @@ 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,11 +193,25 @@ class Service(object):
|
||||||
def containers(self, stopped=False, one_off=False, filters={}):
|
def containers(self, stopped=False, one_off=False, filters={}):
|
||||||
filters.update({'label': self.labels(one_off=one_off)})
|
filters.update({'label': self.labels(one_off=one_off)})
|
||||||
|
|
||||||
return list(filter(None, [
|
result = 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=filters)]))
|
filters=filters)])
|
||||||
|
)
|
||||||
|
if result:
|
||||||
|
return result
|
||||||
|
|
||||||
|
filters.update({'label': self.labels(one_off=one_off, legacy=True)})
|
||||||
|
return list(
|
||||||
|
filter(
|
||||||
|
self.has_legacy_proj_name, filter(None, [
|
||||||
|
Container.from_ps(self.client, container)
|
||||||
|
for container in self.client.containers(
|
||||||
|
all=stopped,
|
||||||
|
filters=filters)])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def get_container(self, number=1):
|
def get_container(self, number=1):
|
||||||
"""Return a :class:`compose.container.Container` for this service. The
|
"""Return a :class:`compose.container.Container` for this service. The
|
||||||
|
@ -380,6 +395,10 @@ class Service(object):
|
||||||
has_diverged = False
|
has_diverged = False
|
||||||
|
|
||||||
for c in containers:
|
for c in containers:
|
||||||
|
if self.has_legacy_proj_name(c):
|
||||||
|
log.debug('%s has diverged: Legacy project name' % c.name)
|
||||||
|
has_diverged = True
|
||||||
|
continue
|
||||||
container_config_hash = c.labels.get(LABEL_CONFIG_HASH, None)
|
container_config_hash = c.labels.get(LABEL_CONFIG_HASH, None)
|
||||||
if container_config_hash != config_hash:
|
if container_config_hash != config_hash:
|
||||||
log.debug(
|
log.debug(
|
||||||
|
@ -1053,11 +1072,12 @@ class Service(object):
|
||||||
def can_be_built(self):
|
def can_be_built(self):
|
||||||
return 'build' in self.options
|
return 'build' in self.options
|
||||||
|
|
||||||
def labels(self, one_off=False):
|
def labels(self, one_off=False, legacy=False):
|
||||||
|
proj_name = self.project if not legacy else re.sub(r'[_-]', '', self.project)
|
||||||
return [
|
return [
|
||||||
'{0}={1}'.format(LABEL_PROJECT, self.project),
|
'{0}={1}'.format(LABEL_PROJECT, proj_name),
|
||||||
'{0}={1}'.format(LABEL_SERVICE, self.name),
|
'{0}={1}'.format(LABEL_SERVICE, self.name),
|
||||||
'{0}={1}'.format(LABEL_ONE_OFF, "True" if one_off else "False")
|
'{0}={1}'.format(LABEL_ONE_OFF, "True" if one_off else "False"),
|
||||||
]
|
]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -1214,6 +1234,12 @@ 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(
|
||||||
|
|
|
@ -2,15 +2,19 @@ from __future__ import absolute_import
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
from docker.errors import NotFound
|
from docker.errors import NotFound
|
||||||
from docker.utils import version_lt
|
from docker.utils import version_lt
|
||||||
|
|
||||||
|
from . import __version__
|
||||||
from .config import ConfigurationError
|
from .config import ConfigurationError
|
||||||
from .config.types import VolumeSpec
|
from .config.types import VolumeSpec
|
||||||
from .const import LABEL_PROJECT
|
from .const import LABEL_PROJECT
|
||||||
|
from .const import LABEL_VERSION
|
||||||
from .const import LABEL_VOLUME
|
from .const import LABEL_VOLUME
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,6 +29,7 @@ class Volume(object):
|
||||||
self.external = external
|
self.external = external
|
||||||
self.labels = labels
|
self.labels = labels
|
||||||
self.custom_name = custom_name
|
self.custom_name = custom_name
|
||||||
|
self.legacy = False
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
return self.client.create_volume(
|
return self.client.create_volume(
|
||||||
|
@ -36,15 +41,26 @@ class Volume(object):
|
||||||
log.info("Volume %s is external, skipping", self.full_name)
|
log.info("Volume %s is external, skipping", self.full_name)
|
||||||
return
|
return
|
||||||
log.info("Removing volume %s", self.full_name)
|
log.info("Removing volume %s", self.full_name)
|
||||||
return self.client.remove_volume(self.full_name)
|
try:
|
||||||
|
return self.client.remove_volume(self.full_name)
|
||||||
|
except NotFound:
|
||||||
|
self.client.remove_volume(self.legacy_full_name)
|
||||||
|
|
||||||
def inspect(self):
|
def inspect(self, legacy=False):
|
||||||
|
if legacy:
|
||||||
|
return self.client.inspect_volume(self.legacy_full_name)
|
||||||
return self.client.inspect_volume(self.full_name)
|
return self.client.inspect_volume(self.full_name)
|
||||||
|
|
||||||
def exists(self):
|
def exists(self):
|
||||||
try:
|
try:
|
||||||
self.inspect()
|
self.inspect()
|
||||||
except NotFound:
|
except NotFound:
|
||||||
|
try:
|
||||||
|
self.inspect(legacy=True)
|
||||||
|
self.legacy = True
|
||||||
|
return True
|
||||||
|
except NotFound:
|
||||||
|
pass
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -54,6 +70,20 @@ class Volume(object):
|
||||||
return self.name
|
return self.name
|
||||||
return '{0}_{1}'.format(self.project, self.name)
|
return '{0}_{1}'.format(self.project, self.name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def legacy_full_name(self):
|
||||||
|
if self.custom_name:
|
||||||
|
return self.name
|
||||||
|
return '{0}_{1}'.format(
|
||||||
|
re.sub(r'[_-]', '', self.project), self.name
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def true_name(self):
|
||||||
|
if self.legacy:
|
||||||
|
return self.legacy_full_name
|
||||||
|
return self.full_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _labels(self):
|
def _labels(self):
|
||||||
if version_lt(self.client._version, '1.23'):
|
if version_lt(self.client._version, '1.23'):
|
||||||
|
@ -62,6 +92,7 @@ class Volume(object):
|
||||||
labels.update({
|
labels.update({
|
||||||
LABEL_PROJECT: self.project,
|
LABEL_PROJECT: self.project,
|
||||||
LABEL_VOLUME: self.name,
|
LABEL_VOLUME: self.name,
|
||||||
|
LABEL_VERSION: __version__,
|
||||||
})
|
})
|
||||||
return labels
|
return labels
|
||||||
|
|
||||||
|
@ -94,7 +125,7 @@ class ProjectVolumes(object):
|
||||||
try:
|
try:
|
||||||
volume.remove()
|
volume.remove()
|
||||||
except NotFound:
|
except NotFound:
|
||||||
log.warn("Volume %s not found.", volume.full_name)
|
log.warn("Volume %s not found.", volume.true_name)
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
try:
|
try:
|
||||||
|
@ -136,9 +167,9 @@ class ProjectVolumes(object):
|
||||||
|
|
||||||
if isinstance(volume_spec, VolumeSpec):
|
if isinstance(volume_spec, VolumeSpec):
|
||||||
volume = self.volumes[volume_spec.external]
|
volume = self.volumes[volume_spec.external]
|
||||||
return volume_spec._replace(external=volume.full_name)
|
return volume_spec._replace(external=volume.true_name)
|
||||||
else:
|
else:
|
||||||
volume_spec.source = self.volumes[volume_spec.source].full_name
|
volume_spec.source = self.volumes[volume_spec.source].true_name
|
||||||
return volume_spec
|
return volume_spec
|
||||||
|
|
||||||
|
|
||||||
|
@ -152,7 +183,7 @@ class VolumeConfigChangedError(ConfigurationError):
|
||||||
'first:\n$ docker volume rm {full_name}'.format(
|
'first:\n$ docker volume rm {full_name}'.format(
|
||||||
vol_name=local.name, property_name=property_name,
|
vol_name=local.name, property_name=property_name,
|
||||||
local_value=local_value, remote_value=remote_value,
|
local_value=local_value, remote_value=remote_value,
|
||||||
full_name=local.full_name
|
full_name=local.true_name
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue