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):
'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
......
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