diff options
Diffstat (limited to 'docker')
| -rw-r--r-- | docker/api/container.py | 3 | ||||
| -rw-r--r-- | docker/api/daemon.py | 18 | ||||
| -rw-r--r-- | docker/api/plugin.py | 37 | ||||
| -rw-r--r-- | docker/api/service.py | 50 | ||||
| -rw-r--r-- | docker/client.py | 4 | ||||
| -rw-r--r-- | docker/constants.py | 2 | ||||
| -rw-r--r-- | docker/models/containers.py | 7 | ||||
| -rw-r--r-- | docker/models/plugins.py | 25 | ||||
| -rw-r--r-- | docker/models/services.py | 34 | ||||
| -rw-r--r-- | docker/types/containers.py | 13 | ||||
| -rw-r--r-- | docker/types/services.py | 9 | ||||
| -rw-r--r-- | docker/utils/ports.py | 20 | ||||
| -rw-r--r-- | docker/version.py | 2 |
13 files changed, 210 insertions, 14 deletions
diff --git a/docker/api/container.py b/docker/api/container.py index 453e378..4e7364b 100644 --- a/docker/api/container.py +++ b/docker/api/container.py @@ -498,6 +498,9 @@ class ContainerApiMixin(object): container, as a mapping of hostname to IP address. group_add (:py:class:`list`): List of additional group names and/or IDs that the container process will run as. + init (bool): Run an init inside the container that forwards + signals and reaps processes + init_path (str): Path to the docker-init binary ipc_mode (str): Set the IPC mode for the container. isolation (str): Isolation technology to use. Default: `None`. links (dict or list of tuples): Either a dictionary mapping name diff --git a/docker/api/daemon.py b/docker/api/daemon.py index d40631f..00367bc 100644 --- a/docker/api/daemon.py +++ b/docker/api/daemon.py @@ -7,6 +7,22 @@ from ..constants import INSECURE_REGISTRY_DEPRECATION_WARNING class DaemonApiMixin(object): + @utils.minimum_version('1.25') + def df(self): + """ + Get data usage information. + + Returns: + (dict): A dictionary representing different resource categories + and their respective data usage. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ + url = self._url('/system/df') + return self._result(self._get(url), True) + def events(self, since=None, until=None, filters=None, decode=None): """ Get real-time events from the server. Similar to the ``docker events`` @@ -54,7 +70,7 @@ class DaemonApiMixin(object): } return self._stream_helper( - self.get(self._url('/events'), params=params, stream=True), + self._get(self._url('/events'), params=params, stream=True), decode=decode ) diff --git a/docker/api/plugin.py b/docker/api/plugin.py index 772d263..ba40c88 100644 --- a/docker/api/plugin.py +++ b/docker/api/plugin.py @@ -195,6 +195,7 @@ class PluginApiMixin(object): return self._stream_helper(res, decode=True) @utils.minimum_version('1.25') + @utils.check_resource def remove_plugin(self, name, force=False): """ Remove an installed plugin. @@ -212,3 +213,39 @@ class PluginApiMixin(object): res = self._delete(url, params={'force': force}) self._raise_for_status(res) return True + + @utils.minimum_version('1.26') + @utils.check_resource + def upgrade_plugin(self, name, remote, privileges): + """ + Upgrade an installed plugin. + + Args: + name (string): Name of the plugin to upgrade. The ``:latest`` + tag is optional and is the default if omitted. + remote (string): Remote reference to upgrade to. The + ``:latest`` tag is optional and is the default if omitted. + privileges (list): A list of privileges the user consents to + grant to the plugin. Can be retrieved using + :py:meth:`~plugin_privileges`. + + Returns: + An iterable object streaming the decoded API logs + """ + + url = self._url('/plugins/{0}/upgrade', name) + params = { + 'remote': remote, + } + + headers = {} + registry, repo_name = auth.resolve_repository_name(remote) + header = auth.get_config_header(self, registry) + if header: + headers['X-Registry-Auth'] = header + response = self._post_json( + url, params=params, headers=headers, data=privileges, + stream=True + ) + self._raise_for_status(response) + return self._stream_helper(response, decode=True) diff --git a/docker/api/service.py b/docker/api/service.py index 0b2abdc..4972c16 100644 --- a/docker/api/service.py +++ b/docker/api/service.py @@ -166,6 +166,56 @@ class ServiceApiMixin(object): url = self._url('/services') return self._result(self._get(url, params=params), True) + @utils.minimum_version('1.25') + @utils.check_resource + def service_logs(self, service, details=False, follow=False, stdout=False, + stderr=False, since=0, timestamps=False, tail='all', + is_tty=None): + """ + Get log stream for a service. + Note: This endpoint works only for services with the ``json-file`` + or ``journald`` logging drivers. + + Args: + service (str): ID or name of the service + details (bool): Show extra details provided to logs. + Default: ``False`` + follow (bool): Keep connection open to read logs as they are + sent by the Engine. Default: ``False`` + stdout (bool): Return logs from ``stdout``. Default: ``False`` + stderr (bool): Return logs from ``stderr``. Default: ``False`` + since (int): UNIX timestamp for the logs staring point. + Default: 0 + timestamps (bool): Add timestamps to every log line. + tail (string or int): Number of log lines to be returned, + counting from the current end of the logs. Specify an + integer or ``'all'`` to output all log lines. + Default: ``all`` + is_tty (bool): Whether the service's :py:class:`ContainerSpec` + enables the TTY option. If omitted, the method will query + the Engine for the information, causing an additional + roundtrip. + + Returns (generator): Logs for the service. + """ + params = { + 'details': details, + 'follow': follow, + 'stdout': stdout, + 'stderr': stderr, + 'since': since, + 'timestamps': timestamps, + 'tail': tail + } + + url = self._url('/services/{0}/logs', service) + res = self._get(url, params=params, stream=True) + if is_tty is None: + is_tty = self.inspect_service( + service + )['Spec']['TaskTemplate']['ContainerSpec'].get('TTY', False) + return self._get_result_tty(True, res, is_tty) + @utils.minimum_version('1.24') def tasks(self, filters=None): """ diff --git a/docker/client.py b/docker/client.py index 09bda67..151e194 100644 --- a/docker/client.py +++ b/docker/client.py @@ -155,6 +155,10 @@ class DockerClient(object): return self.api.events(*args, **kwargs) events.__doc__ = APIClient.events.__doc__ + def df(self): + return self.api.df() + df.__doc__ = APIClient.df.__doc__ + def info(self, *args, **kwargs): return self.api.info(*args, **kwargs) info.__doc__ = APIClient.info.__doc__ diff --git a/docker/constants.py b/docker/constants.py index 4337fed..91a6528 100644 --- a/docker/constants.py +++ b/docker/constants.py @@ -1,7 +1,7 @@ import sys from .version import version -DEFAULT_DOCKER_API_VERSION = '1.24' +DEFAULT_DOCKER_API_VERSION = '1.26' MINIMUM_DOCKER_API_VERSION = '1.21' DEFAULT_TIMEOUT_SECONDS = 60 STREAM_HEADER_SIZE_BYTES = 8 diff --git a/docker/models/containers.py b/docker/models/containers.py index b7a7787..0d328e7 100644 --- a/docker/models/containers.py +++ b/docker/models/containers.py @@ -491,6 +491,9 @@ class ContainerCollection(Collection): group_add (:py:class:`list`): List of additional group names and/or IDs that the container process will run as. hostname (str): Optional hostname for the container. + init (bool): Run an init inside the container that forwards + signals and reaps processes + init_path (str): Path to the docker-init binary ipc_mode (str): Set the IPC mode for the container. isolation (str): Isolation technology to use. Default: `None`. labels (dict or list): A dictionary of name-value labels (e.g. @@ -726,7 +729,7 @@ class ContainerCollection(Collection): Args: all (bool): Show all containers. Only running containers are shown - by default trunc (bool): Truncate output + by default since (str): Show only containers created since Id or Name, include non-running ones before (str): Show only container created before Id or Name, @@ -814,6 +817,8 @@ RUN_HOST_CONFIG_KWARGS = [ 'dns', 'extra_hosts', 'group_add', + 'init', + 'init_path', 'ipc_mode', 'isolation', 'kernel_memory', diff --git a/docker/models/plugins.py b/docker/models/plugins.py index 8b6ede9..6cdf01c 100644 --- a/docker/models/plugins.py +++ b/docker/models/plugins.py @@ -1,3 +1,4 @@ +from .. import errors from .resource import Collection, Model @@ -96,6 +97,30 @@ class Plugin(Model): """ return self.client.api.remove_plugin(self.name, force=force) + def upgrade(self, remote=None): + """ + Upgrade the plugin. + + Args: + remote (string): Remote reference to upgrade to. The + ``:latest`` tag is optional and is the default if omitted. + Default: this plugin's name. + + Returns: + A generator streaming the decoded API logs + """ + if self.enabled: + raise errors.DockerError( + 'Plugin must be disabled before upgrading.' + ) + + if remote is None: + remote = self.name + privileges = self.client.api.plugin_privileges(remote) + for d in self.client.api.upgrade_plugin(self.name, remote, privileges): + yield d + self._reload() + class PluginCollection(Collection): model = Plugin diff --git a/docker/models/services.py b/docker/models/services.py index bd95b5f..c10804d 100644 --- a/docker/models/services.py +++ b/docker/models/services.py @@ -77,6 +77,34 @@ class Service(Model): **create_kwargs ) + def logs(self, **kwargs): + """ + Get log stream for the service. + Note: This method works only for services with the ``json-file`` + or ``journald`` logging drivers. + + Args: + details (bool): Show extra details provided to logs. + Default: ``False`` + follow (bool): Keep connection open to read logs as they are + sent by the Engine. Default: ``False`` + stdout (bool): Return logs from ``stdout``. Default: ``False`` + stderr (bool): Return logs from ``stderr``. Default: ``False`` + since (int): UNIX timestamp for the logs staring point. + Default: 0 + timestamps (bool): Add timestamps to every log line. + tail (string or int): Number of log lines to be returned, + counting from the current end of the logs. Specify an + integer or ``'all'`` to output all log lines. + Default: ``all`` + + Returns (generator): Logs for the service. + """ + is_tty = self.attrs['Spec']['TaskTemplate']['ContainerSpec'].get( + 'TTY', False + ) + return self.client.api.service_logs(self.id, is_tty=is_tty, **kwargs) + class ServiceCollection(Collection): """Services on the Docker server.""" @@ -96,11 +124,12 @@ class ServiceCollection(Collection): access and load balance a service. Default: ``None``. env (list of str): Environment variables, in the form ``KEY=val``. + hostname (string): Hostname to set on the container. labels (dict): Labels to apply to the service. log_driver (str): Log driver to use for containers. log_driver_options (dict): Log driver options. - mode (str): Scheduling mode for the service (``replicated`` or - ``global``). Defaults to ``replicated``. + mode (ServiceMode): Scheduling mode for the service. + Default:``None`` mounts (list of str): Mounts for the containers, in the form ``source:target:options``, where options is either ``ro`` or ``rw``. @@ -176,6 +205,7 @@ CONTAINER_SPEC_KWARGS = [ 'command', 'args', 'env', + 'hostname', 'workdir', 'user', 'labels', diff --git a/docker/types/containers.py b/docker/types/containers.py index 9a8d157..5a5079a 100644 --- a/docker/types/containers.py +++ b/docker/types/containers.py @@ -117,7 +117,8 @@ class HostConfig(dict): oom_kill_disable=False, shm_size=None, sysctls=None, tmpfs=None, oom_score_adj=None, dns_opt=None, cpu_shares=None, cpuset_cpus=None, userns_mode=None, pids_limit=None, - isolation=None, auto_remove=False, storage_opt=None): + isolation=None, auto_remove=False, storage_opt=None, + init=None, init_path=None): if mem_limit is not None: self['Memory'] = parse_bytes(mem_limit) @@ -417,6 +418,16 @@ class HostConfig(dict): raise host_config_version_error('storage_opt', '1.24') self['StorageOpt'] = storage_opt + if init is not None: + if version_lt(version, '1.25'): + raise host_config_version_error('init', '1.25') + self['Init'] = init + + if init_path is not None: + if version_lt(version, '1.25'): + raise host_config_version_error('init_path', '1.25') + self['InitPath'] = init_path + def host_config_type_error(param, param_value, expected): error_msg = 'Invalid type for {0} param: expected {1} but found {2}' diff --git a/docker/types/services.py b/docker/types/services.py index b903fa4..9291c9b 100644 --- a/docker/types/services.py +++ b/docker/types/services.py @@ -70,6 +70,7 @@ class ContainerSpec(dict): image (string): The image name to use for the container. command (string or list): The command to be run in the image. args (:py:class:`list`): Arguments to the command. + hostname (string): The hostname to set on the container. env (dict): Environment variables. dir (string): The working directory for commands to run in. user (string): The user inside the container. @@ -82,9 +83,9 @@ class ContainerSpec(dict): secrets (list of py:class:`SecretReference`): List of secrets to be made available inside the containers. """ - def __init__(self, image, command=None, args=None, env=None, workdir=None, - user=None, labels=None, mounts=None, stop_grace_period=None, - secrets=None): + 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): self['Image'] = image if isinstance(command, six.string_types): @@ -92,6 +93,8 @@ class ContainerSpec(dict): self['Command'] = command self['Args'] = args + if hostname is not None: + self['Hostname'] = hostname if env is not None: if isinstance(env, dict): self['Env'] = format_environment(env) diff --git a/docker/utils/ports.py b/docker/utils/ports.py index 326ef94..3708958 100644 --- a/docker/utils/ports.py +++ b/docker/utils/ports.py @@ -24,7 +24,7 @@ def build_port_bindings(ports): return port_bindings -def to_port_range(port): +def to_port_range(port, randomly_available_port=False): if not port: return None @@ -37,6 +37,9 @@ def to_port_range(port): port, protocol = parts protocol = "/" + protocol + if randomly_available_port: + return ["%s%s" % (port, protocol)] + parts = str(port).split('-') if len(parts) == 1: @@ -64,14 +67,18 @@ def split_port(port): if len(parts) == 1: internal_port, = parts + if not internal_port: + _raise_invalid_port(port) return to_port_range(internal_port), None if len(parts) == 2: external_port, internal_port = parts internal_range = to_port_range(internal_port) - external_range = to_port_range(external_port) + if internal_range is None: + _raise_invalid_port(port) - if internal_range is None or external_range is None: + external_range = to_port_range(external_port, len(internal_range) == 1) + if external_range is None: _raise_invalid_port(port) if len(internal_range) != len(external_range): @@ -80,8 +87,13 @@ def split_port(port): return internal_range, external_range external_ip, external_port, internal_port = parts + + if not internal_port: + _raise_invalid_port(port) + internal_range = to_port_range(internal_port) - external_range = to_port_range(external_port) + external_range = to_port_range(external_port, len(internal_range) == 1) + if not external_range: external_range = [None] * len(internal_range) diff --git a/docker/version.py b/docker/version.py index 491566c..b2474bd 100644 --- a/docker/version.py +++ b/docker/version.py @@ -1,2 +1,2 @@ -version = "2.1.0" +version = "2.2.0" version_info = tuple([int(d) for d in version.split("-")[0].split(".")]) |
