mirror of
https://github.com/docker/compose.git
synced 2025-06-27 17:04:26 +02:00
Check volume config against remote and error out if diverged
Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
parent
867ad1550b
commit
0112c740ad
@ -6,8 +6,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run:
|
- run:
|
||||||
name: install python3
|
name: setup script
|
||||||
command: brew update > /dev/null && brew upgrade python
|
command: ./script/setup/osx
|
||||||
- run:
|
- run:
|
||||||
name: install tox
|
name: install tox
|
||||||
command: sudo pip install --upgrade tox==2.1.1
|
command: sudo pip install --upgrade tox==2.1.1
|
||||||
|
@ -124,19 +124,7 @@ class ProjectVolumes(object):
|
|||||||
)
|
)
|
||||||
volume.create()
|
volume.create()
|
||||||
else:
|
else:
|
||||||
driver = volume.inspect()['Driver']
|
check_remote_volume_config(volume.inspect(), volume)
|
||||||
if volume.driver is not None and driver != volume.driver:
|
|
||||||
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']
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except NotFound:
|
except NotFound:
|
||||||
raise ConfigurationError(
|
raise ConfigurationError(
|
||||||
'Volume %s specifies nonexistent driver %s' % (volume.name, volume.driver)
|
'Volume %s specifies nonexistent driver %s' % (volume.name, volume.driver)
|
||||||
@ -152,3 +140,43 @@ class ProjectVolumes(object):
|
|||||||
else:
|
else:
|
||||||
volume_spec.source = self.volumes[volume_spec.source].full_name
|
volume_spec.source = self.volumes[volume_spec.source].full_name
|
||||||
return volume_spec
|
return volume_spec
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeConfigChangedError(ConfigurationError):
|
||||||
|
def __init__(self, local, property_name, local_value, remote_value):
|
||||||
|
super(VolumeConfigChangedError, self).__init__(
|
||||||
|
'Configuration for volume {vol_name} specifies {property_name} '
|
||||||
|
'{local_value}, but a volume with the same name uses a different '
|
||||||
|
'{property_name} ({remote_value}). If you wish to use the new '
|
||||||
|
'configuration, please remove the existing volume "{full_name}" '
|
||||||
|
'first:\n$ docker volume rm {full_name}'.format(
|
||||||
|
vol_name=local.name, property_name=property_name,
|
||||||
|
local_value=local_value, remote_value=remote_value,
|
||||||
|
full_name=local.full_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def check_remote_volume_config(remote, local):
|
||||||
|
if local.driver and remote.get('Driver') != local.driver:
|
||||||
|
raise VolumeConfigChangedError(local, 'driver', local.driver, remote.get('Driver'))
|
||||||
|
local_opts = local.driver_opts or {}
|
||||||
|
remote_opts = remote.get('Options') or {}
|
||||||
|
for k in set.union(set(remote_opts.keys()), set(local_opts.keys())):
|
||||||
|
if k.startswith('com.docker.'): # These options are set internally
|
||||||
|
continue
|
||||||
|
if remote_opts.get(k) != local_opts.get(k):
|
||||||
|
raise VolumeConfigChangedError(
|
||||||
|
local, '"{}" driver_opt'.format(k), local_opts.get(k), remote_opts.get(k),
|
||||||
|
)
|
||||||
|
|
||||||
|
local_labels = local.labels or {}
|
||||||
|
remote_labels = remote.get('Labels') or {}
|
||||||
|
for k in set.union(set(remote_labels.keys()), set(local_labels.keys())):
|
||||||
|
if k.startswith('com.docker.'): # We are only interested in user-specified labels
|
||||||
|
continue
|
||||||
|
if remote_labels.get(k) != local_labels.get(k):
|
||||||
|
log.warn(
|
||||||
|
'Volume {}: label "{}" has changed. It may need to be'
|
||||||
|
' recreated.'.format(local.name, k)
|
||||||
|
)
|
||||||
|
@ -4,6 +4,7 @@ from __future__ import unicode_literals
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import py
|
import py
|
||||||
@ -1537,6 +1538,52 @@ class ProjectTest(DockerClientTestCase):
|
|||||||
vol_name
|
vol_name
|
||||||
) in str(e.value)
|
) in str(e.value)
|
||||||
|
|
||||||
|
@v2_only()
|
||||||
|
@no_cluster('inspect volume by name defect on Swarm Classic')
|
||||||
|
def test_initialize_volumes_updated_driver_opts(self):
|
||||||
|
vol_name = '{0:x}'.format(random.getrandbits(32))
|
||||||
|
full_vol_name = 'composetest_{0}'.format(vol_name)
|
||||||
|
tmpdir = tempfile.mkdtemp(prefix='compose_test_')
|
||||||
|
self.addCleanup(shutil.rmtree, tmpdir)
|
||||||
|
driver_opts = {'o': 'bind', 'device': tmpdir, 'type': 'none'}
|
||||||
|
|
||||||
|
config_data = build_config(
|
||||||
|
version=V2_0,
|
||||||
|
services=[{
|
||||||
|
'name': 'web',
|
||||||
|
'image': 'busybox:latest',
|
||||||
|
'command': 'top'
|
||||||
|
}],
|
||||||
|
volumes={
|
||||||
|
vol_name: {
|
||||||
|
'driver': 'local',
|
||||||
|
'driver_opts': driver_opts
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
project = Project.from_config(
|
||||||
|
name='composetest',
|
||||||
|
config_data=config_data, client=self.client
|
||||||
|
)
|
||||||
|
project.volumes.initialize()
|
||||||
|
|
||||||
|
volume_data = self.get_volume_data(full_vol_name)
|
||||||
|
assert volume_data['Name'].split('/')[-1] == full_vol_name
|
||||||
|
assert volume_data['Driver'] == 'local'
|
||||||
|
assert volume_data['Options'] == driver_opts
|
||||||
|
|
||||||
|
driver_opts['device'] = '/opt/data/localdata'
|
||||||
|
project = Project.from_config(
|
||||||
|
name='composetest',
|
||||||
|
config_data=config_data,
|
||||||
|
client=self.client
|
||||||
|
)
|
||||||
|
with pytest.raises(config.ConfigurationError) as e:
|
||||||
|
project.volumes.initialize()
|
||||||
|
assert 'Configuration for volume {0} specifies "device" driver_opt {1}'.format(
|
||||||
|
vol_name, driver_opts['device']
|
||||||
|
) in str(e.value)
|
||||||
|
|
||||||
@v2_only()
|
@v2_only()
|
||||||
def test_initialize_volumes_updated_blank_driver(self):
|
def test_initialize_volumes_updated_blank_driver(self):
|
||||||
vol_name = '{0:x}'.format(random.getrandbits(32))
|
vol_name = '{0:x}'.format(random.getrandbits(32))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user