mirror of https://github.com/docker/compose.git
Extract volume init and removal from project.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
parent
3b1276bd44
commit
61d00ebee4
|
@ -6,7 +6,6 @@ import logging
|
|||
from functools import reduce
|
||||
|
||||
from docker.errors import APIError
|
||||
from docker.errors import NotFound
|
||||
|
||||
from . import parallel
|
||||
from .config import ConfigurationError
|
||||
|
@ -28,7 +27,7 @@ from .service import NetworkMode
|
|||
from .service import Service
|
||||
from .service import ServiceNetworkMode
|
||||
from .utils import microseconds_from_time_nano
|
||||
from .volume import Volume
|
||||
from .volume import ProjectVolumes
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -42,7 +41,7 @@ class Project(object):
|
|||
self.name = name
|
||||
self.services = services
|
||||
self.client = client
|
||||
self.volumes = volumes or {}
|
||||
self.volumes = volumes or ProjectVolumes({})
|
||||
self.networks = networks or ProjectNetworks({}, False)
|
||||
|
||||
def labels(self, one_off=False):
|
||||
|
@ -62,16 +61,8 @@ class Project(object):
|
|||
config_data.services,
|
||||
networks,
|
||||
use_networking)
|
||||
project = cls(name, [], client, project_networks)
|
||||
|
||||
if config_data.volumes:
|
||||
for vol_name, data in config_data.volumes.items():
|
||||
project.volumes[vol_name] = Volume(
|
||||
client=client, project=name, name=vol_name,
|
||||
driver=data.get('driver'),
|
||||
driver_opts=data.get('driver_opts'),
|
||||
external_name=data.get('external_name')
|
||||
)
|
||||
volumes = ProjectVolumes.from_config(name, config_data, client)
|
||||
project = cls(name, [], client, project_networks, volumes)
|
||||
|
||||
for service_dict in config_data.services:
|
||||
service_dict = dict(service_dict)
|
||||
|
@ -86,13 +77,10 @@ class Project(object):
|
|||
volumes_from = get_volumes_from(project, service_dict)
|
||||
|
||||
if config_data.version != V1:
|
||||
service_volumes = service_dict.get('volumes', [])
|
||||
for volume_spec in service_volumes:
|
||||
if volume_spec.is_named_volume:
|
||||
declared_volume = project.volumes[volume_spec.external]
|
||||
service_volumes[service_volumes.index(volume_spec)] = (
|
||||
volume_spec._replace(external=declared_volume.full_name)
|
||||
)
|
||||
service_dict['volumes'] = [
|
||||
volumes.namespace_spec(volume_spec)
|
||||
for volume_spec in service_dict.get('volumes', [])
|
||||
]
|
||||
|
||||
project.services.append(
|
||||
Service(
|
||||
|
@ -233,49 +221,13 @@ class Project(object):
|
|||
def remove_stopped(self, service_names=None, **options):
|
||||
parallel.parallel_remove(self.containers(service_names, stopped=True), options)
|
||||
|
||||
def initialize_volumes(self):
|
||||
try:
|
||||
for volume in self.volumes.values():
|
||||
if volume.external:
|
||||
log.debug(
|
||||
'Volume {0} declared as external. No new '
|
||||
'volume will be created.'.format(volume.name)
|
||||
)
|
||||
if not volume.exists():
|
||||
raise ConfigurationError(
|
||||
'Volume {name} declared as external, but could'
|
||||
' not be found. Please create the volume manually'
|
||||
' using `{command}{name}` and try again.'.format(
|
||||
name=volume.full_name,
|
||||
command='docker volume create --name='
|
||||
)
|
||||
)
|
||||
continue
|
||||
volume.create()
|
||||
except NotFound:
|
||||
raise ConfigurationError(
|
||||
'Volume %s specifies nonexistent driver %s' % (volume.name, volume.driver)
|
||||
)
|
||||
except APIError as e:
|
||||
if 'Choose a different volume name' in str(e):
|
||||
raise ConfigurationError(
|
||||
'Configuration for volume {0} specifies driver {1}, but '
|
||||
'a volume with the same name uses a different driver '
|
||||
'({3}). If you wish to use the new configuration, please '
|
||||
'remove the existing volume "{2}" first:\n'
|
||||
'$ docker volume rm {2}'.format(
|
||||
volume.name, volume.driver, volume.full_name,
|
||||
volume.inspect()['Driver']
|
||||
)
|
||||
)
|
||||
|
||||
def down(self, remove_image_type, include_volumes):
|
||||
self.stop()
|
||||
self.remove_stopped(v=include_volumes)
|
||||
self.networks.remove()
|
||||
|
||||
if include_volumes:
|
||||
self.remove_volumes()
|
||||
self.volumes.remove()
|
||||
|
||||
self.remove_images(remove_image_type)
|
||||
|
||||
|
@ -283,10 +235,6 @@ class Project(object):
|
|||
for service in self.get_services():
|
||||
service.remove_image(remove_image_type)
|
||||
|
||||
def remove_volumes(self):
|
||||
for volume in self.volumes.values():
|
||||
volume.remove()
|
||||
|
||||
def restart(self, service_names=None, **options):
|
||||
containers = self.containers(service_names, stopped=True)
|
||||
parallel.parallel_restart(containers, options)
|
||||
|
@ -371,7 +319,7 @@ class Project(object):
|
|||
|
||||
def initialize(self):
|
||||
self.networks.initialize()
|
||||
self.initialize_volumes()
|
||||
self.volumes.initialize()
|
||||
|
||||
def _get_convergence_plans(self, services, strategy):
|
||||
plans = {}
|
||||
|
|
|
@ -3,8 +3,10 @@ from __future__ import unicode_literals
|
|||
|
||||
import logging
|
||||
|
||||
from docker.errors import APIError
|
||||
from docker.errors import NotFound
|
||||
|
||||
from .config import ConfigurationError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -50,3 +52,71 @@ class Volume(object):
|
|||
if self.external_name:
|
||||
return self.external_name
|
||||
return '{0}_{1}'.format(self.project, self.name)
|
||||
|
||||
|
||||
class ProjectVolumes(object):
|
||||
|
||||
def __init__(self, volumes):
|
||||
self.volumes = volumes
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, name, config_data, client):
|
||||
config_volumes = config_data.volumes or {}
|
||||
volumes = {
|
||||
vol_name: Volume(
|
||||
client=client,
|
||||
project=name,
|
||||
name=vol_name,
|
||||
driver=data.get('driver'),
|
||||
driver_opts=data.get('driver_opts'),
|
||||
external_name=data.get('external_name'))
|
||||
for vol_name, data in config_volumes.items()
|
||||
}
|
||||
return cls(volumes)
|
||||
|
||||
def remove(self):
|
||||
for volume in self.volumes.values():
|
||||
volume.remove()
|
||||
|
||||
def initialize(self):
|
||||
try:
|
||||
for volume in self.volumes.values():
|
||||
if volume.external:
|
||||
log.debug(
|
||||
'Volume {0} declared as external. No new '
|
||||
'volume will be created.'.format(volume.name)
|
||||
)
|
||||
if not volume.exists():
|
||||
raise ConfigurationError(
|
||||
'Volume {name} declared as external, but could'
|
||||
' not be found. Please create the volume manually'
|
||||
' using `{command}{name}` and try again.'.format(
|
||||
name=volume.full_name,
|
||||
command='docker volume create --name='
|
||||
)
|
||||
)
|
||||
continue
|
||||
volume.create()
|
||||
except NotFound:
|
||||
raise ConfigurationError(
|
||||
'Volume %s specifies nonexistent driver %s' % (volume.name, volume.driver)
|
||||
)
|
||||
except APIError as e:
|
||||
if 'Choose a different volume name' in str(e):
|
||||
raise ConfigurationError(
|
||||
'Configuration for volume {0} specifies driver {1}, but '
|
||||
'a volume with the same name uses a different driver '
|
||||
'({3}). If you wish to use the new configuration, please '
|
||||
'remove the existing volume "{2}" first:\n'
|
||||
'$ docker volume rm {2}'.format(
|
||||
volume.name, volume.driver, volume.full_name,
|
||||
volume.inspect()['Driver']
|
||||
)
|
||||
)
|
||||
|
||||
def namespace_spec(self, volume_spec):
|
||||
if not volume_spec.is_named_volume:
|
||||
return volume_spec
|
||||
|
||||
volume = self.volumes[volume_spec.external]
|
||||
return volume_spec._replace(external=volume.full_name)
|
||||
|
|
|
@ -749,7 +749,7 @@ class ProjectTest(DockerClientTestCase):
|
|||
name='composetest',
|
||||
config_data=config_data, client=self.client
|
||||
)
|
||||
project.initialize_volumes()
|
||||
project.volumes.initialize()
|
||||
|
||||
volume_data = self.client.inspect_volume(full_vol_name)
|
||||
self.assertEqual(volume_data['Name'], full_vol_name)
|
||||
|
@ -800,7 +800,7 @@ class ProjectTest(DockerClientTestCase):
|
|||
config_data=config_data, client=self.client
|
||||
)
|
||||
with self.assertRaises(config.ConfigurationError):
|
||||
project.initialize_volumes()
|
||||
project.volumes.initialize()
|
||||
|
||||
@v2_only()
|
||||
def test_initialize_volumes_updated_driver(self):
|
||||
|
@ -821,7 +821,7 @@ class ProjectTest(DockerClientTestCase):
|
|||
name='composetest',
|
||||
config_data=config_data, client=self.client
|
||||
)
|
||||
project.initialize_volumes()
|
||||
project.volumes.initialize()
|
||||
|
||||
volume_data = self.client.inspect_volume(full_vol_name)
|
||||
self.assertEqual(volume_data['Name'], full_vol_name)
|
||||
|
@ -836,7 +836,7 @@ class ProjectTest(DockerClientTestCase):
|
|||
client=self.client
|
||||
)
|
||||
with self.assertRaises(config.ConfigurationError) as e:
|
||||
project.initialize_volumes()
|
||||
project.volumes.initialize()
|
||||
assert 'Configuration for volume {0} specifies driver smb'.format(
|
||||
vol_name
|
||||
) in str(e.exception)
|
||||
|
@ -863,7 +863,7 @@ class ProjectTest(DockerClientTestCase):
|
|||
name='composetest',
|
||||
config_data=config_data, client=self.client
|
||||
)
|
||||
project.initialize_volumes()
|
||||
project.volumes.initialize()
|
||||
|
||||
with self.assertRaises(NotFound):
|
||||
self.client.inspect_volume(full_vol_name)
|
||||
|
@ -889,7 +889,7 @@ class ProjectTest(DockerClientTestCase):
|
|||
config_data=config_data, client=self.client
|
||||
)
|
||||
with self.assertRaises(config.ConfigurationError) as e:
|
||||
project.initialize_volumes()
|
||||
project.volumes.initialize()
|
||||
assert 'Volume {0} declared as external'.format(
|
||||
vol_name
|
||||
) in str(e.exception)
|
||||
|
|
Loading…
Reference in New Issue