mirror of https://github.com/docker/docker-py.git
Add support for new ContainerSpec parameters
Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
parent
10ea65f5ab
commit
601d6be526
|
@ -237,10 +237,9 @@ class BuildApiMixin(object):
|
|||
'extra_hosts was only introduced in API version 1.27'
|
||||
)
|
||||
|
||||
encoded_extra_hosts = [
|
||||
'{}:{}'.format(k, v) for k, v in extra_hosts.items()
|
||||
]
|
||||
params.update({'extrahosts': encoded_extra_hosts})
|
||||
if isinstance(extra_hosts, dict):
|
||||
extra_hosts = utils.format_extra_hosts(extra_hosts)
|
||||
params.update({'extrahosts': extra_hosts})
|
||||
|
||||
if context is not None:
|
||||
headers = {'Content-Type': 'application/tar'}
|
||||
|
|
|
@ -4,45 +4,62 @@ from ..types import ServiceMode
|
|||
|
||||
|
||||
def _check_api_features(version, task_template, update_config):
|
||||
|
||||
def raise_version_error(param, min_version):
|
||||
raise errors.InvalidVersion(
|
||||
'{} is not supported in API version < {}'.format(
|
||||
param, min_version
|
||||
)
|
||||
)
|
||||
|
||||
if update_config is not None:
|
||||
if utils.version_lt(version, '1.25'):
|
||||
if 'MaxFailureRatio' in update_config:
|
||||
raise errors.InvalidVersion(
|
||||
'UpdateConfig.max_failure_ratio is not supported in'
|
||||
' API version < 1.25'
|
||||
)
|
||||
raise_version_error('UpdateConfig.max_failure_ratio', '1.25')
|
||||
if 'Monitor' in update_config:
|
||||
raise errors.InvalidVersion(
|
||||
'UpdateConfig.monitor is not supported in'
|
||||
' API version < 1.25'
|
||||
)
|
||||
raise_version_error('UpdateConfig.monitor', '1.25')
|
||||
|
||||
if task_template is not None:
|
||||
if 'ForceUpdate' in task_template and utils.version_lt(
|
||||
version, '1.25'):
|
||||
raise errors.InvalidVersion(
|
||||
'force_update is not supported in API version < 1.25'
|
||||
)
|
||||
raise_version_error('force_update', '1.25')
|
||||
|
||||
if task_template.get('Placement'):
|
||||
if utils.version_lt(version, '1.30'):
|
||||
if task_template['Placement'].get('Platforms'):
|
||||
raise errors.InvalidVersion(
|
||||
'Placement.platforms is not supported in'
|
||||
' API version < 1.30'
|
||||
)
|
||||
|
||||
raise_version_error('Placement.platforms', '1.30')
|
||||
if utils.version_lt(version, '1.27'):
|
||||
if task_template['Placement'].get('Preferences'):
|
||||
raise errors.InvalidVersion(
|
||||
'Placement.preferences is not supported in'
|
||||
' API version < 1.27'
|
||||
)
|
||||
if task_template.get('ContainerSpec', {}).get('TTY'):
|
||||
raise_version_error('Placement.preferences', '1.27')
|
||||
|
||||
if task_template.get('ContainerSpec'):
|
||||
container_spec = task_template.get('ContainerSpec')
|
||||
|
||||
if utils.version_lt(version, '1.25'):
|
||||
raise errors.InvalidVersion(
|
||||
'ContainerSpec.TTY is not supported in API version < 1.25'
|
||||
)
|
||||
if container_spec.get('TTY'):
|
||||
raise_version_error('ContainerSpec.tty', '1.25')
|
||||
if container_spec.get('Hostname') is not None:
|
||||
raise_version_error('ContainerSpec.hostname', '1.25')
|
||||
if container_spec.get('Hosts') is not None:
|
||||
raise_version_error('ContainerSpec.hosts', '1.25')
|
||||
if container_spec.get('Groups') is not None:
|
||||
raise_version_error('ContainerSpec.groups', '1.25')
|
||||
if container_spec.get('DNSConfig') is not None:
|
||||
raise_version_error('ContainerSpec.dns_config', '1.25')
|
||||
if container_spec.get('Healthcheck') is not None:
|
||||
raise_version_error('ContainerSpec.healthcheck', '1.25')
|
||||
|
||||
if utils.version_lt(version, '1.28'):
|
||||
if container_spec.get('ReadOnly') is not None:
|
||||
raise_version_error('ContainerSpec.dns_config', '1.28')
|
||||
if container_spec.get('StopSignal') is not None:
|
||||
raise_version_error('ContainerSpec.stop_signal', '1.28')
|
||||
|
||||
if utils.version_lt(version, '1.30'):
|
||||
if container_spec.get('Configs') is not None:
|
||||
raise_version_error('ContainerSpec.configs', '1.30')
|
||||
if container_spec.get('Privileges') is not None:
|
||||
raise_version_error('ContainerSpec.privileges', '1.30')
|
||||
|
||||
|
||||
class ServiceApiMixin(object):
|
||||
|
|
|
@ -147,6 +147,22 @@ class ServiceCollection(Collection):
|
|||
user (str): User to run commands as.
|
||||
workdir (str): Working directory for commands to run.
|
||||
tty (boolean): Whether a pseudo-TTY should be allocated.
|
||||
groups (:py:class:`list`): A list of additional groups that the
|
||||
container process will run as.
|
||||
open_stdin (boolean): Open ``stdin``
|
||||
read_only (boolean): Mount the container's root filesystem as read
|
||||
only.
|
||||
stop_signal (string): Set signal to stop the service's containers
|
||||
healthcheck (Healthcheck): Healthcheck
|
||||
configuration for this service.
|
||||
hosts (:py:class:`dict`): A set of host to IP mappings to add to
|
||||
the container's `hosts` file.
|
||||
dns_config (DNSConfig): Specification for DNS
|
||||
related configurations in resolver configuration file.
|
||||
configs (:py:class:`list`): List of :py:class:`ConfigReference`
|
||||
that will be exposed to the service.
|
||||
privileges (Privileges): Security options for the service's
|
||||
containers.
|
||||
|
||||
Returns:
|
||||
(:py:class:`Service`) The created service.
|
||||
|
@ -202,18 +218,27 @@ class ServiceCollection(Collection):
|
|||
|
||||
# kwargs to copy straight over to ContainerSpec
|
||||
CONTAINER_SPEC_KWARGS = [
|
||||
'image',
|
||||
'command',
|
||||
'args',
|
||||
'command',
|
||||
'configs',
|
||||
'dns_config',
|
||||
'env',
|
||||
'groups',
|
||||
'healthcheck',
|
||||
'hostname',
|
||||
'workdir',
|
||||
'user',
|
||||
'hosts',
|
||||
'image',
|
||||
'labels',
|
||||
'mounts',
|
||||
'stop_grace_period',
|
||||
'open_stdin',
|
||||
'privileges'
|
||||
'read_only',
|
||||
'secrets',
|
||||
'tty'
|
||||
'stop_grace_period',
|
||||
'stop_signal',
|
||||
'tty',
|
||||
'user',
|
||||
'workdir',
|
||||
]
|
||||
|
||||
# kwargs to copy straight over to TaskTemplate
|
||||
|
|
|
@ -3,7 +3,8 @@ from .containers import ContainerConfig, HostConfig, LogConfig, Ulimit
|
|||
from .healthcheck import Healthcheck
|
||||
from .networks import EndpointConfig, IPAMConfig, IPAMPool, NetworkingConfig
|
||||
from .services import (
|
||||
ContainerSpec, DriverConfig, EndpointSpec, Mount, Placement, Resources,
|
||||
RestartPolicy, SecretReference, ServiceMode, TaskTemplate, UpdateConfig
|
||||
ConfigReference, ContainerSpec, DNSConfig, DriverConfig, EndpointSpec,
|
||||
Mount, Placement, Privileges, Resources, RestartPolicy, SecretReference,
|
||||
ServiceMode, TaskTemplate, UpdateConfig
|
||||
)
|
||||
from .swarm import SwarmSpec, SwarmExternalCA
|
||||
|
|
|
@ -4,8 +4,8 @@ import warnings
|
|||
from .. import errors
|
||||
from ..utils.utils import (
|
||||
convert_port_bindings, convert_tmpfs_mounts, convert_volume_binds,
|
||||
format_environment, normalize_links, parse_bytes, parse_devices,
|
||||
split_command, version_gte, version_lt,
|
||||
format_environment, format_extra_hosts, normalize_links, parse_bytes,
|
||||
parse_devices, split_command, version_gte, version_lt,
|
||||
)
|
||||
from .base import DictType
|
||||
from .healthcheck import Healthcheck
|
||||
|
@ -257,10 +257,7 @@ class HostConfig(dict):
|
|||
|
||||
if extra_hosts is not None:
|
||||
if isinstance(extra_hosts, dict):
|
||||
extra_hosts = [
|
||||
'{0}:{1}'.format(k, v)
|
||||
for k, v in sorted(six.iteritems(extra_hosts))
|
||||
]
|
||||
extra_hosts = format_extra_hosts(extra_hosts)
|
||||
|
||||
self['ExtraHosts'] = extra_hosts
|
||||
|
||||
|
|
|
@ -4,6 +4,30 @@ import six
|
|||
|
||||
|
||||
class Healthcheck(DictType):
|
||||
"""
|
||||
Defines a healthcheck configuration for a container or service.
|
||||
|
||||
Args:
|
||||
|
||||
test (:py:class:`list` or str): Test to perform to determine
|
||||
container health. Possible values:
|
||||
- Empty list: Inherit healthcheck from parent image
|
||||
- ``["NONE"]``: Disable healthcheck
|
||||
- ``["CMD", args...]``: exec arguments directly.
|
||||
- ``["CMD-SHELL", command]``: RUn command in the system's
|
||||
default shell.
|
||||
If a string is provided, it will be used as a ``CMD-SHELL``
|
||||
command.
|
||||
interval (int): The time to wait between checks in nanoseconds. It
|
||||
should be 0 or at least 1000000 (1 ms).
|
||||
timeout (int): The time to wait before considering the check to
|
||||
have hung. It should be 0 or at least 1000000 (1 ms).
|
||||
retries (integer): The number of consecutive failures needed to
|
||||
consider a container as unhealthy.
|
||||
start_period (integer): Start period for the container to
|
||||
initialize before starting health-retries countdown in
|
||||
nanoseconds. It should be 0 or at least 1000000 (1 ms).
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
test = kwargs.get('test', kwargs.get('Test'))
|
||||
if isinstance(test, six.string_types):
|
||||
|
|
|
@ -3,7 +3,8 @@ import six
|
|||
from .. import errors
|
||||
from ..constants import IS_WINDOWS_PLATFORM
|
||||
from ..utils import (
|
||||
check_resource, format_environment, parse_bytes, split_command
|
||||
check_resource, format_environment, format_extra_hosts, parse_bytes,
|
||||
split_command,
|
||||
)
|
||||
|
||||
|
||||
|
@ -84,13 +85,31 @@ class ContainerSpec(dict):
|
|||
:py:class:`~docker.types.Mount` class for details.
|
||||
stop_grace_period (int): Amount of time to wait for the container to
|
||||
terminate before forcefully killing it.
|
||||
secrets (list of py:class:`SecretReference`): List of secrets to be
|
||||
secrets (:py:class:`list`): List of :py:class:`SecretReference` to be
|
||||
made available inside the containers.
|
||||
tty (boolean): Whether a pseudo-TTY should be allocated.
|
||||
groups (:py:class:`list`): A list of additional groups that the
|
||||
container process will run as.
|
||||
open_stdin (boolean): Open ``stdin``
|
||||
read_only (boolean): Mount the container's root filesystem as read
|
||||
only.
|
||||
stop_signal (string): Set signal to stop the service's containers
|
||||
healthcheck (Healthcheck): Healthcheck
|
||||
configuration for this service.
|
||||
hosts (:py:class:`dict`): A set of host to IP mappings to add to
|
||||
the container's `hosts` file.
|
||||
dns_config (DNSConfig): Specification for DNS
|
||||
related configurations in resolver configuration file.
|
||||
configs (:py:class:`list`): List of :py:class:`ConfigReference` that
|
||||
will be exposed to the service.
|
||||
privileges (Privileges): Security options for the service's containers.
|
||||
"""
|
||||
def __init__(self, image, command=None, args=None, hostname=None, env=None,
|
||||
workdir=None, user=None, labels=None, mounts=None,
|
||||
stop_grace_period=None, secrets=None, tty=None):
|
||||
stop_grace_period=None, secrets=None, tty=None, groups=None,
|
||||
open_stdin=None, read_only=None, stop_signal=None,
|
||||
healthcheck=None, hosts=None, dns_config=None, configs=None,
|
||||
privileges=None):
|
||||
self['Image'] = image
|
||||
|
||||
if isinstance(command, six.string_types):
|
||||
|
@ -109,8 +128,17 @@ class ContainerSpec(dict):
|
|||
self['Dir'] = workdir
|
||||
if user is not None:
|
||||
self['User'] = user
|
||||
if groups is not None:
|
||||
self['Groups'] = groups
|
||||
if stop_signal is not None:
|
||||
self['StopSignal'] = stop_signal
|
||||
if stop_grace_period is not None:
|
||||
self['StopGracePeriod'] = stop_grace_period
|
||||
if labels is not None:
|
||||
self['Labels'] = labels
|
||||
if hosts is not None:
|
||||
self['Hosts'] = format_extra_hosts(hosts)
|
||||
|
||||
if mounts is not None:
|
||||
parsed_mounts = []
|
||||
for mount in mounts:
|
||||
|
@ -120,16 +148,30 @@ class ContainerSpec(dict):
|
|||
# If mount already parsed
|
||||
parsed_mounts.append(mount)
|
||||
self['Mounts'] = parsed_mounts
|
||||
if stop_grace_period is not None:
|
||||
self['StopGracePeriod'] = stop_grace_period
|
||||
|
||||
if secrets is not None:
|
||||
if not isinstance(secrets, list):
|
||||
raise TypeError('secrets must be a list')
|
||||
self['Secrets'] = secrets
|
||||
|
||||
if configs is not None:
|
||||
if not isinstance(configs, list):
|
||||
raise TypeError('configs must be a list')
|
||||
self['Configs'] = configs
|
||||
|
||||
if dns_config is not None:
|
||||
self['DNSConfig'] = dns_config
|
||||
if privileges is not None:
|
||||
self['Privileges'] = privileges
|
||||
if healthcheck is not None:
|
||||
self['Healthcheck'] = healthcheck
|
||||
|
||||
if tty is not None:
|
||||
self['TTY'] = tty
|
||||
if open_stdin is not None:
|
||||
self['OpenStdin'] = open_stdin
|
||||
if read_only is not None:
|
||||
self['ReadOnly'] = read_only
|
||||
|
||||
|
||||
class Mount(dict):
|
||||
|
@ -487,6 +529,34 @@ class SecretReference(dict):
|
|||
}
|
||||
|
||||
|
||||
class ConfigReference(dict):
|
||||
"""
|
||||
Config reference to be used as part of a :py:class:`ContainerSpec`.
|
||||
Describes how a config is made accessible inside the service's
|
||||
containers.
|
||||
|
||||
Args:
|
||||
config_id (string): Config's ID
|
||||
config_name (string): Config's name as defined at its creation.
|
||||
filename (string): Name of the file containing the config. Defaults
|
||||
to the config's name if not specified.
|
||||
uid (string): UID of the config file's owner. Default: 0
|
||||
gid (string): GID of the config file's group. Default: 0
|
||||
mode (int): File access mode inside the container. Default: 0o444
|
||||
"""
|
||||
@check_resource('config_id')
|
||||
def __init__(self, config_id, config_name, filename=None, uid=None,
|
||||
gid=None, mode=0o444):
|
||||
self['ConfigName'] = config_name
|
||||
self['ConfigID'] = config_id
|
||||
self['File'] = {
|
||||
'Name': filename or config_name,
|
||||
'UID': uid or '0',
|
||||
'GID': gid or '0',
|
||||
'Mode': mode
|
||||
}
|
||||
|
||||
|
||||
class Placement(dict):
|
||||
"""
|
||||
Placement constraints to be used as part of a :py:class:`TaskTemplate`
|
||||
|
@ -510,3 +580,75 @@ class Placement(dict):
|
|||
self['Platforms'].append({
|
||||
'Architecture': plat[0], 'OS': plat[1]
|
||||
})
|
||||
|
||||
|
||||
class DNSConfig(dict):
|
||||
"""
|
||||
Specification for DNS related configurations in resolver configuration
|
||||
file (``resolv.conf``). Part of a :py:class:`ContainerSpec` definition.
|
||||
|
||||
Args:
|
||||
nameservers (:py:class:`list`): The IP addresses of the name
|
||||
servers.
|
||||
search (:py:class:`list`): A search list for host-name lookup.
|
||||
options (:py:class:`list`): A list of internal resolver variables
|
||||
to be modified (e.g., ``debug``, ``ndots:3``, etc.).
|
||||
"""
|
||||
def __init__(self, nameservers=None, search=None, options=None):
|
||||
self['Nameservers'] = nameservers
|
||||
self['Search'] = search
|
||||
self['Options'] = options
|
||||
|
||||
|
||||
class Privileges(dict):
|
||||
"""
|
||||
Security options for a service's containers.
|
||||
Part of a :py:class:`ContainerSpec` definition.
|
||||
|
||||
Args:
|
||||
credentialspec_file (str): Load credential spec from this file.
|
||||
The file is read by the daemon, and must be present in the
|
||||
CredentialSpecs subdirectory in the docker data directory,
|
||||
which defaults to ``C:\ProgramData\Docker\`` on Windows.
|
||||
Can not be combined with credentialspec_registry.
|
||||
|
||||
credentialspec_registry (str): Load credential spec from this value
|
||||
in the Windows registry. The specified registry value must be
|
||||
located in: ``HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
|
||||
\Virtualization\Containers\CredentialSpecs``.
|
||||
Can not be combined with credentialspec_file.
|
||||
|
||||
selinux_disable (boolean): Disable SELinux
|
||||
selinux_user (string): SELinux user label
|
||||
selinux_role (string): SELinux role label
|
||||
selinux_type (string): SELinux type label
|
||||
selinux_level (string): SELinux level label
|
||||
"""
|
||||
def __init__(self, credentialspec_file=None, credentialspec_registry=None,
|
||||
selinux_disable=None, selinux_user=None, selinux_role=None,
|
||||
selinux_type=None, selinux_level=None):
|
||||
credential_spec = {}
|
||||
if credentialspec_registry is not None:
|
||||
credential_spec['Registry'] = credentialspec_registry
|
||||
if credentialspec_file is not None:
|
||||
credential_spec['File'] = credentialspec_file
|
||||
|
||||
if len(credential_spec) > 1:
|
||||
raise errors.InvalidArgument(
|
||||
'credentialspec_file and credentialspec_registry are mutually'
|
||||
' exclusive'
|
||||
)
|
||||
|
||||
selinux_context = {
|
||||
'Disable': selinux_disable,
|
||||
'User': selinux_user,
|
||||
'Role': selinux_role,
|
||||
'Type': selinux_type,
|
||||
'Level': selinux_level,
|
||||
}
|
||||
|
||||
if len(credential_spec) > 0:
|
||||
self['CredentialSpec'] = credential_spec
|
||||
|
||||
if len(selinux_context) > 0:
|
||||
self['SELinuxContext'] = selinux_context
|
||||
|
|
|
@ -8,6 +8,6 @@ from .utils import (
|
|||
create_host_config, parse_bytes, ping_registry, parse_env_file, version_lt,
|
||||
version_gte, decode_json_header, split_command, create_ipam_config,
|
||||
create_ipam_pool, parse_devices, normalize_links, convert_service_networks,
|
||||
format_environment, create_archive
|
||||
format_environment, create_archive, format_extra_hosts
|
||||
)
|
||||
|
||||
|
|
|
@ -564,6 +564,12 @@ def format_environment(environment):
|
|||
return [format_env(*var) for var in six.iteritems(environment)]
|
||||
|
||||
|
||||
def format_extra_hosts(extra_hosts):
|
||||
return [
|
||||
'{}:{}'.format(k, v) for k, v in sorted(six.iteritems(extra_hosts))
|
||||
]
|
||||
|
||||
|
||||
def create_host_config(self, *args, **kwargs):
|
||||
raise errors.DeprecatedMethod(
|
||||
'utils.create_host_config has been removed. Please use a '
|
||||
|
|
|
@ -122,13 +122,17 @@ Configuration types
|
|||
|
||||
.. py:module:: docker.types
|
||||
|
||||
.. autoclass:: IPAMConfig
|
||||
.. autoclass:: IPAMPool
|
||||
.. autoclass:: ConfigReference
|
||||
.. autoclass:: ContainerSpec
|
||||
.. autoclass:: DNSConfig
|
||||
.. autoclass:: DriverConfig
|
||||
.. autoclass:: EndpointSpec
|
||||
.. autoclass:: Healthcheck
|
||||
.. autoclass:: IPAMConfig
|
||||
.. autoclass:: IPAMPool
|
||||
.. autoclass:: Mount
|
||||
.. autoclass:: Placement
|
||||
.. autoclass:: Privileges
|
||||
.. autoclass:: Resources
|
||||
.. autoclass:: RestartPolicy
|
||||
.. autoclass:: SecretReference
|
||||
|
|
Loading…
Reference in New Issue