Kaydet (Commit) cd47a1f9 authored tarafından Joffrey F's avatar Joffrey F Kaydeden (comit) Joffrey F

Add support for new ContainerSpec parameters

Signed-off-by: 's avatarJoffrey F <joffrey@docker.com>
üst bb437e92
...@@ -237,10 +237,9 @@ class BuildApiMixin(object): ...@@ -237,10 +237,9 @@ class BuildApiMixin(object):
'extra_hosts was only introduced in API version 1.27' 'extra_hosts was only introduced in API version 1.27'
) )
encoded_extra_hosts = [ if isinstance(extra_hosts, dict):
'{}:{}'.format(k, v) for k, v in extra_hosts.items() extra_hosts = utils.format_extra_hosts(extra_hosts)
] params.update({'extrahosts': extra_hosts})
params.update({'extrahosts': encoded_extra_hosts})
if context is not None: if context is not None:
headers = {'Content-Type': 'application/tar'} headers = {'Content-Type': 'application/tar'}
......
...@@ -4,45 +4,62 @@ from ..types import ServiceMode ...@@ -4,45 +4,62 @@ from ..types import ServiceMode
def _check_api_features(version, task_template, update_config): 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 update_config is not None:
if utils.version_lt(version, '1.25'): if utils.version_lt(version, '1.25'):
if 'MaxFailureRatio' in update_config: if 'MaxFailureRatio' in update_config:
raise errors.InvalidVersion( raise_version_error('UpdateConfig.max_failure_ratio', '1.25')
'UpdateConfig.max_failure_ratio is not supported in'
' API version < 1.25'
)
if 'Monitor' in update_config: if 'Monitor' in update_config:
raise errors.InvalidVersion( raise_version_error('UpdateConfig.monitor', '1.25')
'UpdateConfig.monitor is not supported in'
' API version < 1.25'
)
if task_template is not None: if task_template is not None:
if 'ForceUpdate' in task_template and utils.version_lt( if 'ForceUpdate' in task_template and utils.version_lt(
version, '1.25'): version, '1.25'):
raise errors.InvalidVersion( raise_version_error('force_update', '1.25')
'force_update is not supported in API version < 1.25'
)
if task_template.get('Placement'): if task_template.get('Placement'):
if utils.version_lt(version, '1.30'): if utils.version_lt(version, '1.30'):
if task_template['Placement'].get('Platforms'): if task_template['Placement'].get('Platforms'):
raise errors.InvalidVersion( raise_version_error('Placement.platforms', '1.30')
'Placement.platforms is not supported in'
' API version < 1.30'
)
if utils.version_lt(version, '1.27'): if utils.version_lt(version, '1.27'):
if task_template['Placement'].get('Preferences'): if task_template['Placement'].get('Preferences'):
raise errors.InvalidVersion( raise_version_error('Placement.preferences', '1.27')
'Placement.preferences is not supported in'
' API version < 1.27' if task_template.get('ContainerSpec'):
) container_spec = task_template.get('ContainerSpec')
if task_template.get('ContainerSpec', {}).get('TTY'):
if utils.version_lt(version, '1.25'): if utils.version_lt(version, '1.25'):
raise errors.InvalidVersion( if container_spec.get('TTY'):
'ContainerSpec.TTY is not supported in API version < 1.25' 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): class ServiceApiMixin(object):
......
...@@ -147,6 +147,22 @@ class ServiceCollection(Collection): ...@@ -147,6 +147,22 @@ class ServiceCollection(Collection):
user (str): User to run commands as. user (str): User to run commands as.
workdir (str): Working directory for commands to run. workdir (str): Working directory for commands to run.
tty (boolean): Whether a pseudo-TTY should be allocated. 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: Returns:
(:py:class:`Service`) The created service. (:py:class:`Service`) The created service.
...@@ -202,18 +218,27 @@ class ServiceCollection(Collection): ...@@ -202,18 +218,27 @@ class ServiceCollection(Collection):
# kwargs to copy straight over to ContainerSpec # kwargs to copy straight over to ContainerSpec
CONTAINER_SPEC_KWARGS = [ CONTAINER_SPEC_KWARGS = [
'image',
'command',
'args', 'args',
'command',
'configs',
'dns_config',
'env', 'env',
'groups',
'healthcheck',
'hostname', 'hostname',
'workdir', 'hosts',
'user', 'image',
'labels', 'labels',
'mounts', 'mounts',
'stop_grace_period', 'open_stdin',
'privileges'
'read_only',
'secrets', 'secrets',
'tty' 'stop_grace_period',
'stop_signal',
'tty',
'user',
'workdir',
] ]
# kwargs to copy straight over to TaskTemplate # kwargs to copy straight over to TaskTemplate
......
...@@ -3,7 +3,8 @@ from .containers import ContainerConfig, HostConfig, LogConfig, Ulimit ...@@ -3,7 +3,8 @@ from .containers import ContainerConfig, HostConfig, LogConfig, Ulimit
from .healthcheck import Healthcheck from .healthcheck import Healthcheck
from .networks import EndpointConfig, IPAMConfig, IPAMPool, NetworkingConfig from .networks import EndpointConfig, IPAMConfig, IPAMPool, NetworkingConfig
from .services import ( from .services import (
ContainerSpec, DriverConfig, EndpointSpec, Mount, Placement, Resources, ConfigReference, ContainerSpec, DNSConfig, DriverConfig, EndpointSpec,
RestartPolicy, SecretReference, ServiceMode, TaskTemplate, UpdateConfig Mount, Placement, Privileges, Resources, RestartPolicy, SecretReference,
ServiceMode, TaskTemplate, UpdateConfig
) )
from .swarm import SwarmSpec, SwarmExternalCA from .swarm import SwarmSpec, SwarmExternalCA
...@@ -4,8 +4,8 @@ import warnings ...@@ -4,8 +4,8 @@ import warnings
from .. import errors from .. import errors
from ..utils.utils import ( from ..utils.utils import (
convert_port_bindings, convert_tmpfs_mounts, convert_volume_binds, convert_port_bindings, convert_tmpfs_mounts, convert_volume_binds,
format_environment, normalize_links, parse_bytes, parse_devices, format_environment, format_extra_hosts, normalize_links, parse_bytes,
split_command, version_gte, version_lt, parse_devices, split_command, version_gte, version_lt,
) )
from .base import DictType from .base import DictType
from .healthcheck import Healthcheck from .healthcheck import Healthcheck
...@@ -257,10 +257,7 @@ class HostConfig(dict): ...@@ -257,10 +257,7 @@ class HostConfig(dict):
if extra_hosts is not None: if extra_hosts is not None:
if isinstance(extra_hosts, dict): if isinstance(extra_hosts, dict):
extra_hosts = [ extra_hosts = format_extra_hosts(extra_hosts)
'{0}:{1}'.format(k, v)
for k, v in sorted(six.iteritems(extra_hosts))
]
self['ExtraHosts'] = extra_hosts self['ExtraHosts'] = extra_hosts
......
...@@ -4,6 +4,30 @@ import six ...@@ -4,6 +4,30 @@ import six
class Healthcheck(DictType): 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): def __init__(self, **kwargs):
test = kwargs.get('test', kwargs.get('Test')) test = kwargs.get('test', kwargs.get('Test'))
if isinstance(test, six.string_types): if isinstance(test, six.string_types):
......
...@@ -3,7 +3,8 @@ import six ...@@ -3,7 +3,8 @@ import six
from .. import errors from .. import errors
from ..constants import IS_WINDOWS_PLATFORM from ..constants import IS_WINDOWS_PLATFORM
from ..utils import ( 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): ...@@ -84,13 +85,31 @@ class ContainerSpec(dict):
:py:class:`~docker.types.Mount` class for details. :py:class:`~docker.types.Mount` class for details.
stop_grace_period (int): Amount of time to wait for the container to stop_grace_period (int): Amount of time to wait for the container to
terminate before forcefully killing it. 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. made available inside the containers.
tty (boolean): Whether a pseudo-TTY should be allocated. 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, def __init__(self, image, command=None, args=None, hostname=None, env=None,
workdir=None, user=None, labels=None, mounts=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 self['Image'] = image
if isinstance(command, six.string_types): if isinstance(command, six.string_types):
...@@ -109,8 +128,17 @@ class ContainerSpec(dict): ...@@ -109,8 +128,17 @@ class ContainerSpec(dict):
self['Dir'] = workdir self['Dir'] = workdir
if user is not None: if user is not None:
self['User'] = user 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: if labels is not None:
self['Labels'] = labels self['Labels'] = labels
if hosts is not None:
self['Hosts'] = format_extra_hosts(hosts)
if mounts is not None: if mounts is not None:
parsed_mounts = [] parsed_mounts = []
for mount in mounts: for mount in mounts:
...@@ -120,16 +148,30 @@ class ContainerSpec(dict): ...@@ -120,16 +148,30 @@ class ContainerSpec(dict):
# If mount already parsed # If mount already parsed
parsed_mounts.append(mount) parsed_mounts.append(mount)
self['Mounts'] = parsed_mounts self['Mounts'] = parsed_mounts
if stop_grace_period is not None:
self['StopGracePeriod'] = stop_grace_period
if secrets is not None: if secrets is not None:
if not isinstance(secrets, list): if not isinstance(secrets, list):
raise TypeError('secrets must be a list') raise TypeError('secrets must be a list')
self['Secrets'] = secrets 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: if tty is not None:
self['TTY'] = tty 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): class Mount(dict):
...@@ -487,6 +529,34 @@ class SecretReference(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): class Placement(dict):
""" """
Placement constraints to be used as part of a :py:class:`TaskTemplate` Placement constraints to be used as part of a :py:class:`TaskTemplate`
...@@ -510,3 +580,75 @@ class Placement(dict): ...@@ -510,3 +580,75 @@ class Placement(dict):
self['Platforms'].append({ self['Platforms'].append({
'Architecture': plat[0], 'OS': plat[1] '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 ( ...@@ -8,6 +8,6 @@ from .utils import (
create_host_config, parse_bytes, ping_registry, parse_env_file, version_lt, create_host_config, parse_bytes, ping_registry, parse_env_file, version_lt,
version_gte, decode_json_header, split_command, create_ipam_config, version_gte, decode_json_header, split_command, create_ipam_config,
create_ipam_pool, parse_devices, normalize_links, convert_service_networks, 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): ...@@ -564,6 +564,12 @@ def format_environment(environment):
return [format_env(*var) for var in six.iteritems(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): def create_host_config(self, *args, **kwargs):
raise errors.DeprecatedMethod( raise errors.DeprecatedMethod(
'utils.create_host_config has been removed. Please use a ' 'utils.create_host_config has been removed. Please use a '
......
...@@ -122,13 +122,17 @@ Configuration types ...@@ -122,13 +122,17 @@ Configuration types
.. py:module:: docker.types .. py:module:: docker.types
.. autoclass:: IPAMConfig .. autoclass:: ConfigReference
.. autoclass:: IPAMPool
.. autoclass:: ContainerSpec .. autoclass:: ContainerSpec
.. autoclass:: DNSConfig
.. autoclass:: DriverConfig .. autoclass:: DriverConfig
.. autoclass:: EndpointSpec .. autoclass:: EndpointSpec
.. autoclass:: Healthcheck
.. autoclass:: IPAMConfig
.. autoclass:: IPAMPool
.. autoclass:: Mount .. autoclass:: Mount
.. autoclass:: Placement .. autoclass:: Placement
.. autoclass:: Privileges
.. autoclass:: Resources .. autoclass:: Resources
.. autoclass:: RestartPolicy .. autoclass:: RestartPolicy
.. autoclass:: SecretReference .. autoclass:: SecretReference
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment