Use mounts for secrets instead of volumes

Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
Joffrey F 2017-11-29 12:21:56 -08:00
parent 58dcfac853
commit 415fa6c59b
3 changed files with 71 additions and 10 deletions

View File

@ -133,6 +133,48 @@ def normalize_path_for_engine(path):
return path.replace('\\', '/') return path.replace('\\', '/')
class MountSpec(object):
options_map = {
'volume': {
'nocopy': 'no_copy'
},
'bind': {
'propagation': 'propagation'
}
}
_fields = ['type', 'source', 'target', 'read_only', 'consistency']
def __init__(self, type, source=None, target=None, read_only=None, consistency=None, **kwargs):
self.type = type
self.source = source
self.target = target
self.read_only = read_only
self.consistency = consistency
self.options = None
if self.type in kwargs:
self.options = kwargs[self.type]
def as_volume_spec(self):
mode = 'ro' if self.read_only else 'rw'
return VolumeSpec(external=self.source, internal=self.target, mode=mode)
def legacy_repr(self):
return self.as_volume_spec().repr()
def repr(self):
res = {}
for field in self._fields:
if getattr(self, field, None):
res[field] = getattr(self, field)
if self.options:
res[self.type] = self.options
return res
@property
def is_named_volume(self):
return self.type == 'volume' and self.source
class VolumeSpec(namedtuple('_VolumeSpec', 'external internal mode')): class VolumeSpec(namedtuple('_VolumeSpec', 'external internal mode')):
@classmethod @classmethod

View File

@ -14,6 +14,7 @@ from docker.errors import APIError
from docker.errors import ImageNotFound from docker.errors import ImageNotFound
from docker.errors import NotFound from docker.errors import NotFound
from docker.types import LogConfig from docker.types import LogConfig
from docker.types import Mount
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 docker.utils.ports import build_port_bindings from docker.utils.ports import build_port_bindings
@ -27,6 +28,7 @@ from .config import DOCKER_CONFIG_KEYS
from .config import merge_environment from .config import merge_environment
from .config import merge_labels from .config import merge_labels
from .config.errors import DependencyError from .config.errors import DependencyError
from .config.types import MountSpec
from .config.types import ServicePort from .config.types import ServicePort
from .config.types import VolumeSpec from .config.types import VolumeSpec
from .const import DEFAULT_TIMEOUT from .const import DEFAULT_TIMEOUT
@ -795,9 +797,13 @@ class Service(object):
secret_volumes = self.get_secret_volumes() secret_volumes = self.get_secret_volumes()
if secret_volumes: if secret_volumes:
override_options['binds'].extend(v.repr() for v in secret_volumes) if version_lt(self.client.api_version, '1.30'):
container_options['volumes'].update( override_options['binds'].extend(v.legacy_repr() for v in secret_volumes)
(v.internal, {}) for v in secret_volumes) container_options['volumes'].update(
(v.target, {}) for v in secret_volumes
)
else:
override_options['mounts'] = [build_mount(v) for v in secret_volumes]
container_options['image'] = self.image_name container_options['image'] = self.image_name
@ -891,6 +897,7 @@ class Service(object):
device_read_iops=blkio_config.get('device_read_iops'), device_read_iops=blkio_config.get('device_read_iops'),
device_write_bps=blkio_config.get('device_write_bps'), device_write_bps=blkio_config.get('device_write_bps'),
device_write_iops=blkio_config.get('device_write_iops'), device_write_iops=blkio_config.get('device_write_iops'),
mounts=options.get('mounts'),
) )
def get_secret_volumes(self): def get_secret_volumes(self):
@ -901,7 +908,7 @@ class Service(object):
elif not os.path.isabs(target): elif not os.path.isabs(target):
target = '{}/{}'.format(const.SECRETS_PATH, target) target = '{}/{}'.format(const.SECRETS_PATH, target)
return VolumeSpec(secret['file'], target, 'ro') return MountSpec('bind', secret['file'], target, read_only=True)
return [build_spec(secret) for secret in self.secrets] return [build_spec(secret) for secret in self.secrets]
@ -1346,6 +1353,18 @@ def build_volume_from(volume_from_spec):
return "{}:{}".format(volume_from_spec.source.id, volume_from_spec.mode) return "{}:{}".format(volume_from_spec.source.id, volume_from_spec.mode)
def build_mount(mount_spec):
kwargs = {}
if mount_spec.options:
for option, sdk_name in mount_spec.options_map[mount_spec.type].items():
if option in mount_spec.options:
kwargs[sdk_name] = mount_spec.options[option]
return Mount(
type=mount_spec.type, target=mount_spec.target, source=mount_spec.source,
read_only=mount_spec.read_only, consistency=mount_spec.consistency, **kwargs
)
# Labels # Labels

View File

@ -1133,8 +1133,8 @@ class ServiceSecretTest(unittest.TestCase):
) )
volumes = service.get_secret_volumes() volumes = service.get_secret_volumes()
assert volumes[0].external == secret1['file'] assert volumes[0].source == secret1['file']
assert volumes[0].internal == '{}/{}'.format(SECRETS_PATH, secret1['secret'].target) assert volumes[0].target == '{}/{}'.format(SECRETS_PATH, secret1['secret'].target)
def test_get_secret_volumes_abspath(self): def test_get_secret_volumes_abspath(self):
secret1 = { secret1 = {
@ -1149,8 +1149,8 @@ class ServiceSecretTest(unittest.TestCase):
) )
volumes = service.get_secret_volumes() volumes = service.get_secret_volumes()
assert volumes[0].external == secret1['file'] assert volumes[0].source == secret1['file']
assert volumes[0].internal == secret1['secret'].target assert volumes[0].target == secret1['secret'].target
def test_get_secret_volumes_no_target(self): def test_get_secret_volumes_no_target(self):
secret1 = { secret1 = {
@ -1165,5 +1165,5 @@ class ServiceSecretTest(unittest.TestCase):
) )
volumes = service.get_secret_volumes() volumes = service.get_secret_volumes()
assert volumes[0].external == secret1['file'] assert volumes[0].source == secret1['file']
assert volumes[0].internal == '{}/{}'.format(SECRETS_PATH, secret1['secret'].source) assert volumes[0].target == '{}/{}'.format(SECRETS_PATH, secret1['secret'].source)