mirror of https://github.com/docker/docker-py.git
374 lines
13 KiB
Python
374 lines
13 KiB
Python
import copy
|
|
from docker.errors import create_unexpected_kwargs_error, InvalidArgument
|
|
from docker.types import TaskTemplate, ContainerSpec, Placement, ServiceMode
|
|
from .resource import Model, Collection
|
|
|
|
|
|
class Service(Model):
|
|
"""A service."""
|
|
id_attribute = 'ID'
|
|
|
|
@property
|
|
def name(self):
|
|
"""The service's name."""
|
|
return self.attrs['Spec']['Name']
|
|
|
|
@property
|
|
def version(self):
|
|
"""
|
|
The version number of the service. If this is not the same as the
|
|
server, the :py:meth:`update` function will not work and you will
|
|
need to call :py:meth:`reload` before calling it again.
|
|
"""
|
|
return self.attrs.get('Version').get('Index')
|
|
|
|
def remove(self):
|
|
"""
|
|
Stop and remove the service.
|
|
|
|
Raises:
|
|
:py:class:`docker.errors.APIError`
|
|
If the server returns an error.
|
|
"""
|
|
return self.client.api.remove_service(self.id)
|
|
|
|
def tasks(self, filters=None):
|
|
"""
|
|
List the tasks in this service.
|
|
|
|
Args:
|
|
filters (dict): A map of filters to process on the tasks list.
|
|
Valid filters: ``id``, ``name``, ``node``,
|
|
``label``, and ``desired-state``.
|
|
|
|
Returns:
|
|
:py:class:`list`: List of task dictionaries.
|
|
|
|
Raises:
|
|
:py:class:`docker.errors.APIError`
|
|
If the server returns an error.
|
|
"""
|
|
if filters is None:
|
|
filters = {}
|
|
filters['service'] = self.id
|
|
return self.client.api.tasks(filters=filters)
|
|
|
|
def update(self, **kwargs):
|
|
"""
|
|
Update a service's configuration. Similar to the ``docker service
|
|
update`` command.
|
|
|
|
Takes the same parameters as :py:meth:`~ServiceCollection.create`.
|
|
|
|
Raises:
|
|
:py:class:`docker.errors.APIError`
|
|
If the server returns an error.
|
|
"""
|
|
# Image is required, so if it hasn't been set, use current image
|
|
if 'image' not in kwargs:
|
|
spec = self.attrs['Spec']['TaskTemplate']['ContainerSpec']
|
|
kwargs['image'] = spec['Image']
|
|
|
|
if kwargs.get('force_update') is True:
|
|
task_template = self.attrs['Spec']['TaskTemplate']
|
|
current_value = int(task_template.get('ForceUpdate', 0))
|
|
kwargs['force_update'] = current_value + 1
|
|
|
|
create_kwargs = _get_create_service_kwargs('update', kwargs)
|
|
|
|
return self.client.api.update_service(
|
|
self.id,
|
|
self.version,
|
|
**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)
|
|
|
|
def scale(self, replicas):
|
|
"""
|
|
Scale service container.
|
|
|
|
Args:
|
|
replicas (int): The number of containers that should be running.
|
|
|
|
Returns:
|
|
bool: ``True`` if successful.
|
|
"""
|
|
|
|
if 'Global' in self.attrs['Spec']['Mode'].keys():
|
|
raise InvalidArgument('Cannot scale a global container')
|
|
|
|
service_mode = ServiceMode('replicated', replicas)
|
|
return self.client.api.update_service(self.id, self.version,
|
|
mode=service_mode,
|
|
fetch_current_spec=True)
|
|
|
|
def force_update(self):
|
|
"""
|
|
Force update the service even if no changes require it.
|
|
|
|
Returns:
|
|
bool: ``True`` if successful.
|
|
"""
|
|
|
|
return self.update(force_update=True, fetch_current_spec=True)
|
|
|
|
|
|
class ServiceCollection(Collection):
|
|
"""Services on the Docker server."""
|
|
model = Service
|
|
|
|
def create(self, image, command=None, **kwargs):
|
|
"""
|
|
Create a service. Similar to the ``docker service create`` command.
|
|
|
|
Args:
|
|
image (str): The image name to use for the containers.
|
|
command (list of str or str): Command to run.
|
|
args (list of str): Arguments to the command.
|
|
constraints (list of str): :py:class:`~docker.types.Placement`
|
|
constraints.
|
|
preferences (list of tuple): :py:class:`~docker.types.Placement`
|
|
preferences.
|
|
platforms (list of tuple): A list of platform constraints
|
|
expressed as ``(arch, os)`` tuples.
|
|
container_labels (dict): Labels to apply to the container.
|
|
endpoint_spec (EndpointSpec): Properties that can be configured to
|
|
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.
|
|
init (boolean): Run an init inside the container that forwards
|
|
signals and reaps processes
|
|
isolation (string): Isolation technology used by the service's
|
|
containers. Only used for Windows containers.
|
|
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 (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``.
|
|
name (str): Name to give to the service.
|
|
networks (:py:class:`list`): List of network names or IDs or
|
|
:py:class:`~docker.types.NetworkAttachmentConfig` to attach the
|
|
service to. Default: ``None``.
|
|
resources (Resources): Resource limits and reservations.
|
|
restart_policy (RestartPolicy): Restart policy for containers.
|
|
secrets (list of :py:class:`docker.types.SecretReference`): List
|
|
of secrets accessible to containers for this service.
|
|
stop_grace_period (int): Amount of time to wait for
|
|
containers to terminate before forcefully killing them.
|
|
update_config (UpdateConfig): Specification for the update strategy
|
|
of the service. Default: ``None``
|
|
rollback_config (RollbackConfig): Specification for the rollback
|
|
strategy of the service. Default: ``None``
|
|
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:`~docker.types.ConfigReference` that will be exposed
|
|
to the service.
|
|
privileges (Privileges): Security options for the service's
|
|
containers.
|
|
|
|
Returns:
|
|
:py:class:`Service`: The created service.
|
|
|
|
Raises:
|
|
:py:class:`docker.errors.APIError`
|
|
If the server returns an error.
|
|
"""
|
|
kwargs['image'] = image
|
|
kwargs['command'] = command
|
|
create_kwargs = _get_create_service_kwargs('create', kwargs)
|
|
service_id = self.client.api.create_service(**create_kwargs)
|
|
return self.get(service_id)
|
|
|
|
def get(self, service_id, insert_defaults=None):
|
|
"""
|
|
Get a service.
|
|
|
|
Args:
|
|
service_id (str): The ID of the service.
|
|
insert_defaults (boolean): If true, default values will be merged
|
|
into the output.
|
|
|
|
Returns:
|
|
:py:class:`Service`: The service.
|
|
|
|
Raises:
|
|
:py:class:`docker.errors.NotFound`
|
|
If the service does not exist.
|
|
:py:class:`docker.errors.APIError`
|
|
If the server returns an error.
|
|
:py:class:`docker.errors.InvalidVersion`
|
|
If one of the arguments is not supported with the current
|
|
API version.
|
|
"""
|
|
return self.prepare_model(
|
|
self.client.api.inspect_service(service_id, insert_defaults)
|
|
)
|
|
|
|
def list(self, **kwargs):
|
|
"""
|
|
List services.
|
|
|
|
Args:
|
|
filters (dict): Filters to process on the nodes list. Valid
|
|
filters: ``id``, ``name`` , ``label`` and ``mode``.
|
|
Default: ``None``.
|
|
|
|
Returns:
|
|
list of :py:class:`Service`: The services.
|
|
|
|
Raises:
|
|
:py:class:`docker.errors.APIError`
|
|
If the server returns an error.
|
|
"""
|
|
return [
|
|
self.prepare_model(s)
|
|
for s in self.client.api.services(**kwargs)
|
|
]
|
|
|
|
|
|
# kwargs to copy straight over to ContainerSpec
|
|
CONTAINER_SPEC_KWARGS = [
|
|
'args',
|
|
'command',
|
|
'configs',
|
|
'dns_config',
|
|
'env',
|
|
'groups',
|
|
'healthcheck',
|
|
'hostname',
|
|
'hosts',
|
|
'image',
|
|
'init',
|
|
'isolation',
|
|
'labels',
|
|
'mounts',
|
|
'open_stdin',
|
|
'privileges',
|
|
'read_only',
|
|
'secrets',
|
|
'stop_grace_period',
|
|
'stop_signal',
|
|
'tty',
|
|
'user',
|
|
'workdir',
|
|
]
|
|
|
|
# kwargs to copy straight over to TaskTemplate
|
|
TASK_TEMPLATE_KWARGS = [
|
|
'networks',
|
|
'resources',
|
|
'restart_policy',
|
|
]
|
|
|
|
# kwargs to copy straight over to create_service
|
|
CREATE_SERVICE_KWARGS = [
|
|
'name',
|
|
'labels',
|
|
'mode',
|
|
'update_config',
|
|
'endpoint_spec',
|
|
]
|
|
|
|
PLACEMENT_KWARGS = [
|
|
'constraints',
|
|
'preferences',
|
|
'platforms',
|
|
]
|
|
|
|
|
|
def _get_create_service_kwargs(func_name, kwargs):
|
|
# Copy over things which can be copied directly
|
|
create_kwargs = {}
|
|
for key in copy.copy(kwargs):
|
|
if key in CREATE_SERVICE_KWARGS:
|
|
create_kwargs[key] = kwargs.pop(key)
|
|
container_spec_kwargs = {}
|
|
for key in copy.copy(kwargs):
|
|
if key in CONTAINER_SPEC_KWARGS:
|
|
container_spec_kwargs[key] = kwargs.pop(key)
|
|
task_template_kwargs = {}
|
|
for key in copy.copy(kwargs):
|
|
if key in TASK_TEMPLATE_KWARGS:
|
|
task_template_kwargs[key] = kwargs.pop(key)
|
|
|
|
if 'container_labels' in kwargs:
|
|
container_spec_kwargs['labels'] = kwargs.pop('container_labels')
|
|
|
|
placement = {}
|
|
for key in copy.copy(kwargs):
|
|
if key in PLACEMENT_KWARGS:
|
|
placement[key] = kwargs.pop(key)
|
|
placement = Placement(**placement)
|
|
task_template_kwargs['placement'] = placement
|
|
|
|
if 'log_driver' in kwargs:
|
|
task_template_kwargs['log_driver'] = {
|
|
'Name': kwargs.pop('log_driver'),
|
|
'Options': kwargs.pop('log_driver_options', {})
|
|
}
|
|
|
|
if func_name == 'update':
|
|
if 'force_update' in kwargs:
|
|
task_template_kwargs['force_update'] = kwargs.pop('force_update')
|
|
|
|
# fetch the current spec by default if updating the service
|
|
# through the model
|
|
fetch_current_spec = kwargs.pop('fetch_current_spec', True)
|
|
create_kwargs['fetch_current_spec'] = fetch_current_spec
|
|
|
|
# All kwargs should have been consumed by this point, so raise
|
|
# error if any are left
|
|
if kwargs:
|
|
raise create_unexpected_kwargs_error(func_name, kwargs)
|
|
|
|
container_spec = ContainerSpec(**container_spec_kwargs)
|
|
task_template_kwargs['container_spec'] = container_spec
|
|
create_kwargs['task_template'] = TaskTemplate(**task_template_kwargs)
|
|
return create_kwargs
|