mirror of https://github.com/docker/docker-py.git
New docker.types subpackage containing advanced config dictionary types
Tests and docs updated to match docker.utils.types has been moved to docker.types Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
parent
02e99e4967
commit
97094e4ea3
|
|
@ -5,9 +5,6 @@ from .daemon import DaemonApiMixin
|
||||||
from .exec_api import ExecApiMixin
|
from .exec_api import ExecApiMixin
|
||||||
from .image import ImageApiMixin
|
from .image import ImageApiMixin
|
||||||
from .network import NetworkApiMixin
|
from .network import NetworkApiMixin
|
||||||
from .service import (
|
from .service import ServiceApiMixin
|
||||||
ServiceApiMixin, TaskTemplate, ContainerSpec, Mount, Resources,
|
|
||||||
RestartPolicy, UpdateConfig
|
|
||||||
)
|
|
||||||
from .swarm import SwarmApiMixin
|
from .swarm import SwarmApiMixin
|
||||||
from .volume import VolumeApiMixin
|
from .volume import VolumeApiMixin
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,17 @@
|
||||||
import six
|
|
||||||
|
|
||||||
from .. import errors
|
|
||||||
from .. import utils
|
from .. import utils
|
||||||
|
|
||||||
|
|
||||||
class ServiceApiMixin(object):
|
class ServiceApiMixin(object):
|
||||||
@utils.minimum_version('1.24')
|
|
||||||
def services(self, filters=None):
|
|
||||||
params = {
|
|
||||||
'filters': utils.convert_filters(filters) if filters else None
|
|
||||||
}
|
|
||||||
url = self._url('/services')
|
|
||||||
return self._result(self._get(url, params=params), True)
|
|
||||||
|
|
||||||
@utils.minimum_version('1.24')
|
@utils.minimum_version('1.24')
|
||||||
def create_service(
|
def create_service(
|
||||||
self, task_config, name=None, labels=None, mode=None,
|
self, task_template, name=None, labels=None, mode=None,
|
||||||
update_config=None, networks=None, endpoint_config=None
|
update_config=None, networks=None, endpoint_config=None
|
||||||
):
|
):
|
||||||
url = self._url('/services/create')
|
url = self._url('/services/create')
|
||||||
data = {
|
data = {
|
||||||
'Name': name,
|
'Name': name,
|
||||||
'Labels': labels,
|
'Labels': labels,
|
||||||
'TaskTemplate': task_config,
|
'TaskTemplate': task_template,
|
||||||
'Mode': mode,
|
'Mode': mode,
|
||||||
'UpdateConfig': update_config,
|
'UpdateConfig': update_config,
|
||||||
'Networks': networks,
|
'Networks': networks,
|
||||||
|
|
@ -44,6 +33,14 @@ class ServiceApiMixin(object):
|
||||||
self._raise_for_status(resp)
|
self._raise_for_status(resp)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@utils.minimum_version('1.24')
|
||||||
|
def services(self, filters=None):
|
||||||
|
params = {
|
||||||
|
'filters': utils.convert_filters(filters) if filters else None
|
||||||
|
}
|
||||||
|
url = self._url('/services')
|
||||||
|
return self._result(self._get(url, params=params), True)
|
||||||
|
|
||||||
@utils.minimum_version('1.24')
|
@utils.minimum_version('1.24')
|
||||||
@utils.check_resource
|
@utils.check_resource
|
||||||
def update_service(self, service, version, task_template=None, name=None,
|
def update_service(self, service, version, task_template=None, name=None,
|
||||||
|
|
@ -69,167 +66,3 @@ class ServiceApiMixin(object):
|
||||||
resp = self._post_json(url, data=data, params={'version': version})
|
resp = self._post_json(url, data=data, params={'version': version})
|
||||||
self._raise_for_status(resp)
|
self._raise_for_status(resp)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class TaskTemplate(dict):
|
|
||||||
def __init__(self, container_spec, resources=None, restart_policy=None,
|
|
||||||
placement=None, log_driver=None):
|
|
||||||
self['ContainerSpec'] = container_spec
|
|
||||||
if resources:
|
|
||||||
self['Resources'] = resources
|
|
||||||
if restart_policy:
|
|
||||||
self['RestartPolicy'] = restart_policy
|
|
||||||
if placement:
|
|
||||||
self['Placement'] = placement
|
|
||||||
if log_driver:
|
|
||||||
self['LogDriver'] = log_driver
|
|
||||||
|
|
||||||
@property
|
|
||||||
def container_spec(self):
|
|
||||||
return self.get('ContainerSpec')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def resources(self):
|
|
||||||
return self.get('Resources')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def restart_policy(self):
|
|
||||||
return self.get('RestartPolicy')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def placement(self):
|
|
||||||
return self.get('Placement')
|
|
||||||
|
|
||||||
|
|
||||||
class ContainerSpec(dict):
|
|
||||||
def __init__(self, image, command=None, args=None, env=None, workdir=None,
|
|
||||||
user=None, labels=None, mounts=None, stop_grace_period=None):
|
|
||||||
self['Image'] = image
|
|
||||||
self['Command'] = command
|
|
||||||
self['Args'] = args
|
|
||||||
|
|
||||||
if env is not None:
|
|
||||||
self['Env'] = env
|
|
||||||
if workdir is not None:
|
|
||||||
self['Dir'] = workdir
|
|
||||||
if user is not None:
|
|
||||||
self['User'] = user
|
|
||||||
if labels is not None:
|
|
||||||
self['Labels'] = labels
|
|
||||||
if mounts is not None:
|
|
||||||
for mount in mounts:
|
|
||||||
if isinstance(mount, six.string_types):
|
|
||||||
mounts.append(Mount.parse_mount_string(mount))
|
|
||||||
mounts.remove(mount)
|
|
||||||
self['Mounts'] = mounts
|
|
||||||
if stop_grace_period is not None:
|
|
||||||
self['StopGracePeriod'] = stop_grace_period
|
|
||||||
|
|
||||||
|
|
||||||
class Mount(dict):
|
|
||||||
def __init__(self, target, source, type='volume', read_only=False,
|
|
||||||
propagation=None, no_copy=False, labels=None,
|
|
||||||
driver_config=None):
|
|
||||||
self['Target'] = target
|
|
||||||
self['Source'] = source
|
|
||||||
if type not in ('bind', 'volume'):
|
|
||||||
raise errors.DockerError(
|
|
||||||
'Only acceptable mount types are `bind` and `volume`.'
|
|
||||||
)
|
|
||||||
self['Type'] = type
|
|
||||||
|
|
||||||
if type == 'bind':
|
|
||||||
if propagation is not None:
|
|
||||||
self['BindOptions'] = {
|
|
||||||
'Propagation': propagation
|
|
||||||
}
|
|
||||||
if any(labels, driver_config, no_copy):
|
|
||||||
raise errors.DockerError(
|
|
||||||
'Mount type is binding but volume options have been '
|
|
||||||
'provided.'
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
volume_opts = {}
|
|
||||||
if no_copy:
|
|
||||||
volume_opts['NoCopy'] = True
|
|
||||||
if labels:
|
|
||||||
volume_opts['Labels'] = labels
|
|
||||||
if driver_config:
|
|
||||||
volume_opts['driver_config'] = driver_config
|
|
||||||
if volume_opts:
|
|
||||||
self['VolumeOptions'] = volume_opts
|
|
||||||
if propagation:
|
|
||||||
raise errors.DockerError(
|
|
||||||
'Mount type is volume but `propagation` argument has been '
|
|
||||||
'provided.'
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def parse_mount_string(cls, string):
|
|
||||||
parts = string.split(':')
|
|
||||||
if len(parts) > 3:
|
|
||||||
raise errors.DockerError(
|
|
||||||
'Invalid mount format "{0}"'.format(string)
|
|
||||||
)
|
|
||||||
if len(parts) == 1:
|
|
||||||
return cls(target=parts[0])
|
|
||||||
else:
|
|
||||||
target = parts[1]
|
|
||||||
source = parts[0]
|
|
||||||
read_only = not (len(parts) == 3 or parts[2] == 'ro')
|
|
||||||
return cls(target, source, read_only=read_only)
|
|
||||||
|
|
||||||
|
|
||||||
class Resources(dict):
|
|
||||||
def __init__(self, cpu_limit=None, mem_limit=None, cpu_reservation=None,
|
|
||||||
mem_reservation=None):
|
|
||||||
limits = {}
|
|
||||||
reservation = {}
|
|
||||||
if cpu_limit is not None:
|
|
||||||
limits['NanoCPUs'] = cpu_limit
|
|
||||||
if mem_limit is not None:
|
|
||||||
limits['MemoryBytes'] = mem_limit
|
|
||||||
if cpu_reservation is not None:
|
|
||||||
reservation['NanoCPUs'] = cpu_reservation
|
|
||||||
if mem_reservation is not None:
|
|
||||||
reservation['MemoryBytes'] = mem_reservation
|
|
||||||
|
|
||||||
self['Limits'] = limits
|
|
||||||
self['Reservation'] = reservation
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateConfig(dict):
|
|
||||||
def __init__(self, parallelism=0, delay=None, failure_action='continue'):
|
|
||||||
self['Parallelism'] = parallelism
|
|
||||||
if delay is not None:
|
|
||||||
self['Delay'] = delay
|
|
||||||
if failure_action not in ('pause', 'continue'):
|
|
||||||
raise errors.DockerError(
|
|
||||||
'failure_action must be either `pause` or `continue`.'
|
|
||||||
)
|
|
||||||
self['FailureAction'] = failure_action
|
|
||||||
|
|
||||||
|
|
||||||
class RestartConditionTypesEnum(object):
|
|
||||||
_values = (
|
|
||||||
'none',
|
|
||||||
'on_failure',
|
|
||||||
'any',
|
|
||||||
)
|
|
||||||
NONE, ON_FAILURE, ANY = _values
|
|
||||||
|
|
||||||
|
|
||||||
class RestartPolicy(dict):
|
|
||||||
condition_types = RestartConditionTypesEnum
|
|
||||||
|
|
||||||
def __init__(self, condition=RestartConditionTypesEnum.NONE, delay=0,
|
|
||||||
attempts=0, window=0):
|
|
||||||
if condition not in self.condition_types._values:
|
|
||||||
raise TypeError(
|
|
||||||
'Invalid RestartPolicy condition {0}'.format(condition)
|
|
||||||
)
|
|
||||||
|
|
||||||
self['Condition'] = condition
|
|
||||||
self['Delay'] = delay
|
|
||||||
self['Attempts'] = attempts
|
|
||||||
self['Window'] = window
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
# flake8: noqa
|
||||||
|
from .containers import LogConfig, Ulimit
|
||||||
|
from .services import (
|
||||||
|
ContainerSpec, LogDriver, Mount, Resources, RestartPolicy, TaskTemplate,
|
||||||
|
UpdateConfig
|
||||||
|
)
|
||||||
|
from .swarm import SwarmSpec, SwarmExternalCA
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import six
|
||||||
|
|
||||||
|
|
||||||
|
class DictType(dict):
|
||||||
|
def __init__(self, init):
|
||||||
|
for k, v in six.iteritems(init):
|
||||||
|
self[k] = v
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from .base import DictType
|
||||||
|
|
||||||
|
|
||||||
class LogConfigTypesEnum(object):
|
class LogConfigTypesEnum(object):
|
||||||
_values = (
|
_values = (
|
||||||
|
|
@ -13,12 +15,6 @@ class LogConfigTypesEnum(object):
|
||||||
JSON, SYSLOG, JOURNALD, GELF, FLUENTD, NONE = _values
|
JSON, SYSLOG, JOURNALD, GELF, FLUENTD, NONE = _values
|
||||||
|
|
||||||
|
|
||||||
class DictType(dict):
|
|
||||||
def __init__(self, init):
|
|
||||||
for k, v in six.iteritems(init):
|
|
||||||
self[k] = v
|
|
||||||
|
|
||||||
|
|
||||||
class LogConfig(DictType):
|
class LogConfig(DictType):
|
||||||
types = LogConfigTypesEnum
|
types = LogConfigTypesEnum
|
||||||
|
|
||||||
|
|
@ -94,45 +90,3 @@ class Ulimit(DictType):
|
||||||
@hard.setter
|
@hard.setter
|
||||||
def hard(self, value):
|
def hard(self, value):
|
||||||
self['Hard'] = value
|
self['Hard'] = value
|
||||||
|
|
||||||
|
|
||||||
class SwarmSpec(DictType):
|
|
||||||
def __init__(self, task_history_retention_limit=None,
|
|
||||||
snapshot_interval=None, keep_old_snapshots=None,
|
|
||||||
log_entries_for_slow_followers=None, heartbeat_tick=None,
|
|
||||||
election_tick=None, dispatcher_heartbeat_period=None,
|
|
||||||
node_cert_expiry=None, external_ca=None, name=None):
|
|
||||||
if task_history_retention_limit is not None:
|
|
||||||
self['Orchestration'] = {
|
|
||||||
'TaskHistoryRetentionLimit': task_history_retention_limit
|
|
||||||
}
|
|
||||||
if any([snapshot_interval, keep_old_snapshots,
|
|
||||||
log_entries_for_slow_followers, heartbeat_tick, election_tick]):
|
|
||||||
self['Raft'] = {
|
|
||||||
'SnapshotInterval': snapshot_interval,
|
|
||||||
'KeepOldSnapshots': keep_old_snapshots,
|
|
||||||
'LogEntriesForSlowFollowers': log_entries_for_slow_followers,
|
|
||||||
'HeartbeatTick': heartbeat_tick,
|
|
||||||
'ElectionTick': election_tick
|
|
||||||
}
|
|
||||||
|
|
||||||
if dispatcher_heartbeat_period:
|
|
||||||
self['Dispatcher'] = {
|
|
||||||
'HeartbeatPeriod': dispatcher_heartbeat_period
|
|
||||||
}
|
|
||||||
|
|
||||||
if node_cert_expiry or external_ca:
|
|
||||||
self['CAConfig'] = {
|
|
||||||
'NodeCertExpiry': node_cert_expiry,
|
|
||||||
'ExternalCA': external_ca
|
|
||||||
}
|
|
||||||
|
|
||||||
if name is not None:
|
|
||||||
self['Name'] = name
|
|
||||||
|
|
||||||
|
|
||||||
class SwarmExternalCA(DictType):
|
|
||||||
def __init__(self, url, protocol=None, options=None):
|
|
||||||
self['URL'] = url
|
|
||||||
self['Protocol'] = protocol
|
|
||||||
self['Options'] = options
|
|
||||||
|
|
@ -0,0 +1,176 @@
|
||||||
|
import six
|
||||||
|
|
||||||
|
from .. import errors
|
||||||
|
|
||||||
|
|
||||||
|
class TaskTemplate(dict):
|
||||||
|
def __init__(self, container_spec, resources=None, restart_policy=None,
|
||||||
|
placement=None, log_driver=None):
|
||||||
|
self['ContainerSpec'] = container_spec
|
||||||
|
if resources:
|
||||||
|
self['Resources'] = resources
|
||||||
|
if restart_policy:
|
||||||
|
self['RestartPolicy'] = restart_policy
|
||||||
|
if placement:
|
||||||
|
self['Placement'] = placement
|
||||||
|
if log_driver:
|
||||||
|
self['LogDriver'] = log_driver
|
||||||
|
|
||||||
|
@property
|
||||||
|
def container_spec(self):
|
||||||
|
return self.get('ContainerSpec')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def resources(self):
|
||||||
|
return self.get('Resources')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def restart_policy(self):
|
||||||
|
return self.get('RestartPolicy')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def placement(self):
|
||||||
|
return self.get('Placement')
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerSpec(dict):
|
||||||
|
def __init__(self, image, command=None, args=None, env=None, workdir=None,
|
||||||
|
user=None, labels=None, mounts=None, stop_grace_period=None):
|
||||||
|
self['Image'] = image
|
||||||
|
self['Command'] = command
|
||||||
|
self['Args'] = args
|
||||||
|
|
||||||
|
if env is not None:
|
||||||
|
self['Env'] = env
|
||||||
|
if workdir is not None:
|
||||||
|
self['Dir'] = workdir
|
||||||
|
if user is not None:
|
||||||
|
self['User'] = user
|
||||||
|
if labels is not None:
|
||||||
|
self['Labels'] = labels
|
||||||
|
if mounts is not None:
|
||||||
|
for mount in mounts:
|
||||||
|
if isinstance(mount, six.string_types):
|
||||||
|
mounts.append(Mount.parse_mount_string(mount))
|
||||||
|
mounts.remove(mount)
|
||||||
|
self['Mounts'] = mounts
|
||||||
|
if stop_grace_period is not None:
|
||||||
|
self['StopGracePeriod'] = stop_grace_period
|
||||||
|
|
||||||
|
|
||||||
|
class Mount(dict):
|
||||||
|
def __init__(self, target, source, type='volume', read_only=False,
|
||||||
|
propagation=None, no_copy=False, labels=None,
|
||||||
|
driver_config=None):
|
||||||
|
self['Target'] = target
|
||||||
|
self['Source'] = source
|
||||||
|
if type not in ('bind', 'volume'):
|
||||||
|
raise errors.DockerError(
|
||||||
|
'Only acceptable mount types are `bind` and `volume`.'
|
||||||
|
)
|
||||||
|
self['Type'] = type
|
||||||
|
|
||||||
|
if type == 'bind':
|
||||||
|
if propagation is not None:
|
||||||
|
self['BindOptions'] = {
|
||||||
|
'Propagation': propagation
|
||||||
|
}
|
||||||
|
if any(labels, driver_config, no_copy):
|
||||||
|
raise errors.DockerError(
|
||||||
|
'Mount type is binding but volume options have been '
|
||||||
|
'provided.'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
volume_opts = {}
|
||||||
|
if no_copy:
|
||||||
|
volume_opts['NoCopy'] = True
|
||||||
|
if labels:
|
||||||
|
volume_opts['Labels'] = labels
|
||||||
|
if driver_config:
|
||||||
|
volume_opts['driver_config'] = driver_config
|
||||||
|
if volume_opts:
|
||||||
|
self['VolumeOptions'] = volume_opts
|
||||||
|
if propagation:
|
||||||
|
raise errors.DockerError(
|
||||||
|
'Mount type is volume but `propagation` argument has been '
|
||||||
|
'provided.'
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse_mount_string(cls, string):
|
||||||
|
parts = string.split(':')
|
||||||
|
if len(parts) > 3:
|
||||||
|
raise errors.DockerError(
|
||||||
|
'Invalid mount format "{0}"'.format(string)
|
||||||
|
)
|
||||||
|
if len(parts) == 1:
|
||||||
|
return cls(target=parts[0])
|
||||||
|
else:
|
||||||
|
target = parts[1]
|
||||||
|
source = parts[0]
|
||||||
|
read_only = not (len(parts) == 3 or parts[2] == 'ro')
|
||||||
|
return cls(target, source, read_only=read_only)
|
||||||
|
|
||||||
|
|
||||||
|
class Resources(dict):
|
||||||
|
def __init__(self, cpu_limit=None, mem_limit=None, cpu_reservation=None,
|
||||||
|
mem_reservation=None):
|
||||||
|
limits = {}
|
||||||
|
reservation = {}
|
||||||
|
if cpu_limit is not None:
|
||||||
|
limits['NanoCPUs'] = cpu_limit
|
||||||
|
if mem_limit is not None:
|
||||||
|
limits['MemoryBytes'] = mem_limit
|
||||||
|
if cpu_reservation is not None:
|
||||||
|
reservation['NanoCPUs'] = cpu_reservation
|
||||||
|
if mem_reservation is not None:
|
||||||
|
reservation['MemoryBytes'] = mem_reservation
|
||||||
|
|
||||||
|
if limits:
|
||||||
|
self['Limits'] = limits
|
||||||
|
if reservation:
|
||||||
|
self['Reservations'] = reservation
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateConfig(dict):
|
||||||
|
def __init__(self, parallelism=0, delay=None, failure_action='continue'):
|
||||||
|
self['Parallelism'] = parallelism
|
||||||
|
if delay is not None:
|
||||||
|
self['Delay'] = delay
|
||||||
|
if failure_action not in ('pause', 'continue'):
|
||||||
|
raise errors.DockerError(
|
||||||
|
'failure_action must be either `pause` or `continue`.'
|
||||||
|
)
|
||||||
|
self['FailureAction'] = failure_action
|
||||||
|
|
||||||
|
|
||||||
|
class RestartConditionTypesEnum(object):
|
||||||
|
_values = (
|
||||||
|
'none',
|
||||||
|
'on_failure',
|
||||||
|
'any',
|
||||||
|
)
|
||||||
|
NONE, ON_FAILURE, ANY = _values
|
||||||
|
|
||||||
|
|
||||||
|
class RestartPolicy(dict):
|
||||||
|
condition_types = RestartConditionTypesEnum
|
||||||
|
|
||||||
|
def __init__(self, condition=RestartConditionTypesEnum.NONE, delay=0,
|
||||||
|
max_attempts=0, window=0):
|
||||||
|
if condition not in self.condition_types._values:
|
||||||
|
raise TypeError(
|
||||||
|
'Invalid RestartPolicy condition {0}'.format(condition)
|
||||||
|
)
|
||||||
|
|
||||||
|
self['Condition'] = condition
|
||||||
|
self['Delay'] = delay
|
||||||
|
self['MaxAttempts'] = max_attempts
|
||||||
|
self['Window'] = window
|
||||||
|
|
||||||
|
|
||||||
|
class LogDriver(dict):
|
||||||
|
def __init__(self, name, options=None):
|
||||||
|
self['Name'] = name
|
||||||
|
if options:
|
||||||
|
self['Options'] = options
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
class SwarmSpec(dict):
|
||||||
|
def __init__(self, task_history_retention_limit=None,
|
||||||
|
snapshot_interval=None, keep_old_snapshots=None,
|
||||||
|
log_entries_for_slow_followers=None, heartbeat_tick=None,
|
||||||
|
election_tick=None, dispatcher_heartbeat_period=None,
|
||||||
|
node_cert_expiry=None, external_ca=None, name=None):
|
||||||
|
if task_history_retention_limit is not None:
|
||||||
|
self['Orchestration'] = {
|
||||||
|
'TaskHistoryRetentionLimit': task_history_retention_limit
|
||||||
|
}
|
||||||
|
if any([snapshot_interval, keep_old_snapshots,
|
||||||
|
log_entries_for_slow_followers, heartbeat_tick, election_tick]):
|
||||||
|
self['Raft'] = {
|
||||||
|
'SnapshotInterval': snapshot_interval,
|
||||||
|
'KeepOldSnapshots': keep_old_snapshots,
|
||||||
|
'LogEntriesForSlowFollowers': log_entries_for_slow_followers,
|
||||||
|
'HeartbeatTick': heartbeat_tick,
|
||||||
|
'ElectionTick': election_tick
|
||||||
|
}
|
||||||
|
|
||||||
|
if dispatcher_heartbeat_period:
|
||||||
|
self['Dispatcher'] = {
|
||||||
|
'HeartbeatPeriod': dispatcher_heartbeat_period
|
||||||
|
}
|
||||||
|
|
||||||
|
if node_cert_expiry or external_ca:
|
||||||
|
self['CAConfig'] = {
|
||||||
|
'NodeCertExpiry': node_cert_expiry,
|
||||||
|
'ExternalCA': external_ca
|
||||||
|
}
|
||||||
|
|
||||||
|
if name is not None:
|
||||||
|
self['Name'] = name
|
||||||
|
|
||||||
|
|
||||||
|
class SwarmExternalCA(dict):
|
||||||
|
def __init__(self, url, protocol=None, options=None):
|
||||||
|
self['URL'] = url
|
||||||
|
self['Protocol'] = protocol
|
||||||
|
self['Options'] = options
|
||||||
|
|
@ -8,8 +8,6 @@ from .utils import (
|
||||||
create_ipam_config, create_ipam_pool, parse_devices, normalize_links,
|
create_ipam_config, create_ipam_pool, parse_devices, normalize_links,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .types import LogConfig, Ulimit
|
from ..types import LogConfig, Ulimit
|
||||||
from .types import (
|
from ..types import SwarmExternalCA, SwarmSpec
|
||||||
SwarmExternalCA, SwarmSpec,
|
|
||||||
)
|
|
||||||
from .decorators import check_resource, minimum_version, update_headers
|
from .decorators import check_resource, minimum_version, update_headers
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ import six
|
||||||
from .. import constants
|
from .. import constants
|
||||||
from .. import errors
|
from .. import errors
|
||||||
from .. import tls
|
from .. import tls
|
||||||
from .types import Ulimit, LogConfig
|
from ..types import Ulimit, LogConfig
|
||||||
|
|
||||||
if six.PY2:
|
if six.PY2:
|
||||||
from urllib import splitnport
|
from urllib import splitnport
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ Initialize a new Swarm using the current connected engine as the first node.
|
||||||
|
|
||||||
#### Client.create_swarm_spec
|
#### Client.create_swarm_spec
|
||||||
|
|
||||||
Create a `docker.utils.SwarmSpec` instance that can be used as the `swarm_spec`
|
Create a `docker.types.SwarmSpec` instance that can be used as the `swarm_spec`
|
||||||
argument in `Client.init_swarm`.
|
argument in `Client.init_swarm`.
|
||||||
|
|
||||||
**Params:**
|
**Params:**
|
||||||
|
|
@ -113,12 +113,12 @@ argument in `Client.init_swarm`.
|
||||||
heartbeat to the dispatcher.
|
heartbeat to the dispatcher.
|
||||||
* node_cert_expiry (int): Automatic expiry for nodes certificates.
|
* node_cert_expiry (int): Automatic expiry for nodes certificates.
|
||||||
* external_ca (dict): Configuration for forwarding signing requests to an
|
* external_ca (dict): Configuration for forwarding signing requests to an
|
||||||
external certificate authority. Use `docker.utils.SwarmExternalCA`.
|
external certificate authority. Use `docker.types.SwarmExternalCA`.
|
||||||
* name (string): Swarm's name
|
* name (string): Swarm's name
|
||||||
|
|
||||||
**Returns:** `docker.utils.SwarmSpec` instance.
|
**Returns:** `docker.types.SwarmSpec` instance.
|
||||||
|
|
||||||
#### docker.utils.SwarmExternalCA
|
#### docker.types.SwarmExternalCA
|
||||||
|
|
||||||
Create a configuration dictionary for the `external_ca` argument in a
|
Create a configuration dictionary for the `external_ca` argument in a
|
||||||
`SwarmSpec`.
|
`SwarmSpec`.
|
||||||
|
|
|
||||||
3
setup.py
3
setup.py
|
|
@ -36,7 +36,8 @@ setup(
|
||||||
url='https://github.com/docker/docker-py/',
|
url='https://github.com/docker/docker-py/',
|
||||||
packages=[
|
packages=[
|
||||||
'docker', 'docker.api', 'docker.auth', 'docker.transport',
|
'docker', 'docker.api', 'docker.auth', 'docker.transport',
|
||||||
'docker.utils', 'docker.utils.ports', 'docker.ssladapter'
|
'docker.utils', 'docker.utils.ports', 'docker.ssladapter',
|
||||||
|
'docker.types',
|
||||||
],
|
],
|
||||||
install_requires=requirements,
|
install_requires=requirements,
|
||||||
tests_require=test_requirements,
|
tests_require=test_requirements,
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,10 @@ class ServiceTest(helpers.BaseTestCase):
|
||||||
else:
|
else:
|
||||||
name = self.get_service_name()
|
name = self.get_service_name()
|
||||||
|
|
||||||
container_spec = docker.api.ContainerSpec('busybox', ['echo', 'hello'])
|
container_spec = docker.types.ContainerSpec(
|
||||||
task_tmpl = docker.api.TaskTemplate(container_spec)
|
'busybox', ['echo', 'hello']
|
||||||
|
)
|
||||||
|
task_tmpl = docker.types.TaskTemplate(container_spec)
|
||||||
return name, self.client.create_service(task_tmpl, name=name)
|
return name, self.client.create_service(task_tmpl, name=name)
|
||||||
|
|
||||||
@requires_api_version('1.24')
|
@requires_api_version('1.24')
|
||||||
|
|
@ -74,7 +76,7 @@ class ServiceTest(helpers.BaseTestCase):
|
||||||
test_services = self.client.services(filters={'name': 'dockerpytest_'})
|
test_services = self.client.services(filters={'name': 'dockerpytest_'})
|
||||||
assert len(test_services) == 0
|
assert len(test_services) == 0
|
||||||
|
|
||||||
def test_rempve_service_by_name(self):
|
def test_remove_service_by_name(self):
|
||||||
svc_name, svc_id = self.create_simple_service()
|
svc_name, svc_id = self.create_simple_service()
|
||||||
assert self.client.remove_service(svc_name)
|
assert self.client.remove_service(svc_name)
|
||||||
test_services = self.client.services(filters={'name': 'dockerpytest_'})
|
test_services = self.client.services(filters={'name': 'dockerpytest_'})
|
||||||
|
|
@ -87,6 +89,94 @@ class ServiceTest(helpers.BaseTestCase):
|
||||||
assert len(services) == 1
|
assert len(services) == 1
|
||||||
assert services[0]['ID'] == svc_id['ID']
|
assert services[0]['ID'] == svc_id['ID']
|
||||||
|
|
||||||
|
def test_create_service_custom_log_driver(self):
|
||||||
|
container_spec = docker.types.ContainerSpec(
|
||||||
|
'busybox', ['echo', 'hello']
|
||||||
|
)
|
||||||
|
log_cfg = docker.types.LogDriver('none')
|
||||||
|
task_tmpl = docker.types.TaskTemplate(
|
||||||
|
container_spec, log_driver=log_cfg
|
||||||
|
)
|
||||||
|
name = self.get_service_name()
|
||||||
|
svc_id = self.client.create_service(task_tmpl, name=name)
|
||||||
|
svc_info = self.client.inspect_service(svc_id)
|
||||||
|
assert 'TaskTemplate' in svc_info['Spec']
|
||||||
|
res_template = svc_info['Spec']['TaskTemplate']
|
||||||
|
assert 'LogDriver' in res_template
|
||||||
|
assert 'Name' in res_template['LogDriver']
|
||||||
|
assert res_template['LogDriver']['Name'] == 'none'
|
||||||
|
|
||||||
|
def test_create_service_with_volume_mount(self):
|
||||||
|
vol_name = self.get_service_name()
|
||||||
|
container_spec = docker.types.ContainerSpec(
|
||||||
|
'busybox', ['ls'],
|
||||||
|
mounts=[
|
||||||
|
docker.types.Mount(target='/test', source=vol_name)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.tmp_volumes.append(vol_name)
|
||||||
|
task_tmpl = docker.types.TaskTemplate(container_spec)
|
||||||
|
name = self.get_service_name()
|
||||||
|
svc_id = self.client.create_service(task_tmpl, name=name)
|
||||||
|
svc_info = self.client.inspect_service(svc_id)
|
||||||
|
assert 'ContainerSpec' in svc_info['Spec']['TaskTemplate']
|
||||||
|
cspec = svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
||||||
|
assert 'Mounts' in cspec
|
||||||
|
assert len(cspec['Mounts']) == 1
|
||||||
|
mount = cspec['Mounts'][0]
|
||||||
|
assert mount['Target'] == '/test'
|
||||||
|
assert mount['Source'] == vol_name
|
||||||
|
assert mount['Type'] == 'volume'
|
||||||
|
|
||||||
|
def test_create_service_with_resources_constraints(self):
|
||||||
|
container_spec = docker.types.ContainerSpec('busybox', ['true'])
|
||||||
|
resources = docker.types.Resources(
|
||||||
|
cpu_limit=4000000, mem_limit=3 * 1024 * 1024 * 1024,
|
||||||
|
cpu_reservation=3500000, mem_reservation=2 * 1024 * 1024 * 1024
|
||||||
|
)
|
||||||
|
task_tmpl = docker.types.TaskTemplate(
|
||||||
|
container_spec, resources=resources
|
||||||
|
)
|
||||||
|
name = self.get_service_name()
|
||||||
|
svc_id = self.client.create_service(task_tmpl, name=name)
|
||||||
|
svc_info = self.client.inspect_service(svc_id)
|
||||||
|
assert 'TaskTemplate' in svc_info['Spec']
|
||||||
|
res_template = svc_info['Spec']['TaskTemplate']
|
||||||
|
assert 'Resources' in res_template
|
||||||
|
assert res_template['Resources']['Limits'] == resources['Limits']
|
||||||
|
assert res_template['Resources']['Reservations'] == resources[
|
||||||
|
'Reservations'
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_create_service_with_update_config(self):
|
||||||
|
container_spec = docker.types.ContainerSpec('busybox', ['true'])
|
||||||
|
task_tmpl = docker.types.TaskTemplate(container_spec)
|
||||||
|
update_config = docker.types.UpdateConfig(
|
||||||
|
parallelism=10, delay=5, failure_action='pause'
|
||||||
|
)
|
||||||
|
name = self.get_service_name()
|
||||||
|
svc_id = self.client.create_service(
|
||||||
|
task_tmpl, update_config=update_config, name=name
|
||||||
|
)
|
||||||
|
svc_info = self.client.inspect_service(svc_id)
|
||||||
|
assert 'UpdateConfig' in svc_info['Spec']
|
||||||
|
assert update_config == svc_info['Spec']['UpdateConfig']
|
||||||
|
|
||||||
|
def test_create_service_with_restart_policy(self):
|
||||||
|
container_spec = docker.types.ContainerSpec('busybox', ['true'])
|
||||||
|
policy = docker.types.RestartPolicy(
|
||||||
|
docker.types.RestartPolicy.condition_types.ANY,
|
||||||
|
delay=5, max_attempts=5
|
||||||
|
)
|
||||||
|
task_tmpl = docker.types.TaskTemplate(
|
||||||
|
container_spec, restart_policy=policy
|
||||||
|
)
|
||||||
|
name = self.get_service_name()
|
||||||
|
svc_id = self.client.create_service(task_tmpl, name=name)
|
||||||
|
svc_info = self.client.inspect_service(svc_id)
|
||||||
|
assert 'RestartPolicy' in svc_info['Spec']['TaskTemplate']
|
||||||
|
assert policy == svc_info['Spec']['TaskTemplate']['RestartPolicy']
|
||||||
|
|
||||||
def test_update_service_name(self):
|
def test_update_service_name(self):
|
||||||
name, svc_id = self.create_simple_service()
|
name, svc_id = self.create_simple_service()
|
||||||
svc_info = self.client.inspect_service(svc_id)
|
svc_info = self.client.inspect_service(svc_id)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue