|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- 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.
- maxreplicas (int): :py:class:`~docker.types.Placement` maxreplicas
- or (int) representing maximum number of replicas per node.
- 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.
- cap_add (:py:class:`list`): A list of kernel capabilities to add to
- the default set for the container.
- cap_drop (:py:class:`list`): A list of kernel capabilities to drop
- from the default set for the container.
- sysctls (:py:class:`dict`): A dict of sysctl values to add to the
- container
-
- 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``.
- status (bool): Include the service task count of running and
- desired tasks. 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',
- 'cap_add',
- 'cap_drop',
- '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',
- 'sysctls',
- ]
-
- # 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',
- 'rollback_config',
- 'endpoint_spec',
- ]
-
- PLACEMENT_KWARGS = [
- 'constraints',
- 'preferences',
- 'platforms',
- 'maxreplicas',
- ]
-
-
- 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
|