first commit
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
from ..api import APIClient
|
||||
from .resource import Collection, Model
|
||||
|
||||
|
||||
class Config(Model):
|
||||
"""A config."""
|
||||
id_attribute = 'ID'
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__}: '{self.name}'>"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.attrs['Spec']['Name']
|
||||
|
||||
def remove(self):
|
||||
"""
|
||||
Remove this config.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If config failed to remove.
|
||||
"""
|
||||
return self.client.api.remove_config(self.id)
|
||||
|
||||
|
||||
class ConfigCollection(Collection):
|
||||
"""Configs on the Docker server."""
|
||||
model = Config
|
||||
|
||||
def create(self, **kwargs):
|
||||
obj = self.client.api.create_config(**kwargs)
|
||||
obj.setdefault("Spec", {})["Name"] = kwargs.get("name")
|
||||
return self.prepare_model(obj)
|
||||
create.__doc__ = APIClient.create_config.__doc__
|
||||
|
||||
def get(self, config_id):
|
||||
"""
|
||||
Get a config.
|
||||
|
||||
Args:
|
||||
config_id (str): Config ID.
|
||||
|
||||
Returns:
|
||||
(:py:class:`Config`): The config.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.NotFound`
|
||||
If the config does not exist.
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
return self.prepare_model(self.client.api.inspect_config(config_id))
|
||||
|
||||
def list(self, **kwargs):
|
||||
"""
|
||||
List configs. Similar to the ``docker config ls`` command.
|
||||
|
||||
Args:
|
||||
filters (dict): Server-side list filtering options.
|
||||
|
||||
Returns:
|
||||
(list of :py:class:`Config`): The configs.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
resp = self.client.api.configs(**kwargs)
|
||||
return [self.prepare_model(obj) for obj in resp]
|
||||
1197
backend/venv/lib/python3.9/site-packages/docker/models/containers.py
Normal file
1197
backend/venv/lib/python3.9/site-packages/docker/models/containers.py
Normal file
File diff suppressed because it is too large
Load Diff
505
backend/venv/lib/python3.9/site-packages/docker/models/images.py
Normal file
505
backend/venv/lib/python3.9/site-packages/docker/models/images.py
Normal file
@@ -0,0 +1,505 @@
|
||||
import itertools
|
||||
import re
|
||||
import warnings
|
||||
|
||||
from ..api import APIClient
|
||||
from ..constants import DEFAULT_DATA_CHUNK_SIZE
|
||||
from ..errors import BuildError, ImageLoadError, InvalidArgument
|
||||
from ..utils import parse_repository_tag
|
||||
from ..utils.json_stream import json_stream
|
||||
from .resource import Collection, Model
|
||||
|
||||
|
||||
class Image(Model):
|
||||
"""
|
||||
An image on the server.
|
||||
"""
|
||||
def __repr__(self):
|
||||
tag_str = "', '".join(self.tags)
|
||||
return f"<{self.__class__.__name__}: '{tag_str}'>"
|
||||
|
||||
@property
|
||||
def labels(self):
|
||||
"""
|
||||
The labels of an image as dictionary.
|
||||
"""
|
||||
result = self.attrs['Config'].get('Labels')
|
||||
return result or {}
|
||||
|
||||
@property
|
||||
def short_id(self):
|
||||
"""
|
||||
The ID of the image truncated to 12 characters, plus the ``sha256:``
|
||||
prefix.
|
||||
"""
|
||||
if self.id.startswith('sha256:'):
|
||||
return self.id[:19]
|
||||
return self.id[:12]
|
||||
|
||||
@property
|
||||
def tags(self):
|
||||
"""
|
||||
The image's tags.
|
||||
"""
|
||||
tags = self.attrs.get('RepoTags')
|
||||
if tags is None:
|
||||
tags = []
|
||||
return [tag for tag in tags if tag != '<none>:<none>']
|
||||
|
||||
def history(self):
|
||||
"""
|
||||
Show the history of an image.
|
||||
|
||||
Returns:
|
||||
(list): The history of the image.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
return self.client.api.history(self.id)
|
||||
|
||||
def remove(self, force=False, noprune=False):
|
||||
"""
|
||||
Remove this image.
|
||||
|
||||
Args:
|
||||
force (bool): Force removal of the image
|
||||
noprune (bool): Do not delete untagged parents
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
return self.client.api.remove_image(
|
||||
self.id,
|
||||
force=force,
|
||||
noprune=noprune,
|
||||
)
|
||||
|
||||
def save(self, chunk_size=DEFAULT_DATA_CHUNK_SIZE, named=False):
|
||||
"""
|
||||
Get a tarball of an image. Similar to the ``docker save`` command.
|
||||
|
||||
Args:
|
||||
chunk_size (int): The generator will return up to that much data
|
||||
per iteration, but may return less. If ``None``, data will be
|
||||
streamed as it is received. Default: 2 MB
|
||||
named (str or bool): If ``False`` (default), the tarball will not
|
||||
retain repository and tag information for this image. If set
|
||||
to ``True``, the first tag in the :py:attr:`~tags` list will
|
||||
be used to identify the image. Alternatively, any element of
|
||||
the :py:attr:`~tags` list can be used as an argument to use
|
||||
that specific tag as the saved identifier.
|
||||
|
||||
Returns:
|
||||
(generator): A stream of raw archive data.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
|
||||
Example:
|
||||
|
||||
>>> image = cli.images.get("busybox:latest")
|
||||
>>> f = open('/tmp/busybox-latest.tar', 'wb')
|
||||
>>> for chunk in image.save():
|
||||
>>> f.write(chunk)
|
||||
>>> f.close()
|
||||
"""
|
||||
img = self.id
|
||||
if named:
|
||||
img = self.tags[0] if self.tags else img
|
||||
if isinstance(named, str):
|
||||
if named not in self.tags:
|
||||
raise InvalidArgument(
|
||||
f"{named} is not a valid tag for this image"
|
||||
)
|
||||
img = named
|
||||
|
||||
return self.client.api.get_image(img, chunk_size)
|
||||
|
||||
def tag(self, repository, tag=None, **kwargs):
|
||||
"""
|
||||
Tag this image into a repository. Similar to the ``docker tag``
|
||||
command.
|
||||
|
||||
Args:
|
||||
repository (str): The repository to set for the tag
|
||||
tag (str): The tag name
|
||||
force (bool): Force
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
|
||||
Returns:
|
||||
(bool): ``True`` if successful
|
||||
"""
|
||||
return self.client.api.tag(self.id, repository, tag=tag, **kwargs)
|
||||
|
||||
|
||||
class RegistryData(Model):
|
||||
"""
|
||||
Image metadata stored on the registry, including available platforms.
|
||||
"""
|
||||
def __init__(self, image_name, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.image_name = image_name
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
"""
|
||||
The ID of the object.
|
||||
"""
|
||||
return self.attrs['Descriptor']['digest']
|
||||
|
||||
@property
|
||||
def short_id(self):
|
||||
"""
|
||||
The ID of the image truncated to 12 characters, plus the ``sha256:``
|
||||
prefix.
|
||||
"""
|
||||
return self.id[:19]
|
||||
|
||||
def pull(self, platform=None):
|
||||
"""
|
||||
Pull the image digest.
|
||||
|
||||
Args:
|
||||
platform (str): The platform to pull the image for.
|
||||
Default: ``None``
|
||||
|
||||
Returns:
|
||||
(:py:class:`Image`): A reference to the pulled image.
|
||||
"""
|
||||
repository, _ = parse_repository_tag(self.image_name)
|
||||
return self.collection.pull(repository, tag=self.id, platform=platform)
|
||||
|
||||
def has_platform(self, platform):
|
||||
"""
|
||||
Check whether the given platform identifier is available for this
|
||||
digest.
|
||||
|
||||
Args:
|
||||
platform (str or dict): A string using the ``os[/arch[/variant]]``
|
||||
format, or a platform dictionary.
|
||||
|
||||
Returns:
|
||||
(bool): ``True`` if the platform is recognized as available,
|
||||
``False`` otherwise.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.InvalidArgument`
|
||||
If the platform argument is not a valid descriptor.
|
||||
"""
|
||||
if platform and not isinstance(platform, dict):
|
||||
parts = platform.split('/')
|
||||
if len(parts) > 3 or len(parts) < 1:
|
||||
raise InvalidArgument(
|
||||
f'"{platform}" is not a valid platform descriptor'
|
||||
)
|
||||
platform = {'os': parts[0]}
|
||||
if len(parts) > 2:
|
||||
platform['variant'] = parts[2]
|
||||
if len(parts) > 1:
|
||||
platform['architecture'] = parts[1]
|
||||
return normalize_platform(
|
||||
platform, self.client.version()
|
||||
) in self.attrs['Platforms']
|
||||
|
||||
def reload(self):
|
||||
self.attrs = self.client.api.inspect_distribution(self.image_name)
|
||||
|
||||
reload.__doc__ = Model.reload.__doc__
|
||||
|
||||
|
||||
class ImageCollection(Collection):
|
||||
model = Image
|
||||
|
||||
def build(self, **kwargs):
|
||||
"""
|
||||
Build an image and return it. Similar to the ``docker build``
|
||||
command. Either ``path`` or ``fileobj`` must be set.
|
||||
|
||||
If you already have a tar file for the Docker build context (including
|
||||
a Dockerfile), pass a readable file-like object to ``fileobj``
|
||||
and also pass ``custom_context=True``. If the stream is also
|
||||
compressed, set ``encoding`` to the correct value (e.g ``gzip``).
|
||||
|
||||
If you want to get the raw output of the build, use the
|
||||
:py:meth:`~docker.api.build.BuildApiMixin.build` method in the
|
||||
low-level API.
|
||||
|
||||
Args:
|
||||
path (str): Path to the directory containing the Dockerfile
|
||||
fileobj: A file object to use as the Dockerfile. (Or a file-like
|
||||
object)
|
||||
tag (str): A tag to add to the final image
|
||||
quiet (bool): Whether to return the status
|
||||
nocache (bool): Don't use the cache when set to ``True``
|
||||
rm (bool): Remove intermediate containers. The ``docker build``
|
||||
command now defaults to ``--rm=true``, but we have kept the old
|
||||
default of `False` to preserve backward compatibility
|
||||
timeout (int): HTTP timeout
|
||||
custom_context (bool): Optional if using ``fileobj``
|
||||
encoding (str): The encoding for a stream. Set to ``gzip`` for
|
||||
compressing
|
||||
pull (bool): Downloads any updates to the FROM image in Dockerfiles
|
||||
forcerm (bool): Always remove intermediate containers, even after
|
||||
unsuccessful builds
|
||||
dockerfile (str): path within the build context to the Dockerfile
|
||||
buildargs (dict): A dictionary of build arguments
|
||||
container_limits (dict): A dictionary of limits applied to each
|
||||
container created by the build process. Valid keys:
|
||||
|
||||
- memory (int): set memory limit for build
|
||||
- memswap (int): Total memory (memory + swap), -1 to disable
|
||||
swap
|
||||
- cpushares (int): CPU shares (relative weight)
|
||||
- cpusetcpus (str): CPUs in which to allow execution, e.g.,
|
||||
``"0-3"``, ``"0,1"``
|
||||
shmsize (int): Size of `/dev/shm` in bytes. The size must be
|
||||
greater than 0. If omitted the system uses 64MB
|
||||
labels (dict): A dictionary of labels to set on the image
|
||||
cache_from (list): A list of images used for build cache
|
||||
resolution
|
||||
target (str): Name of the build-stage to build in a multi-stage
|
||||
Dockerfile
|
||||
network_mode (str): networking mode for the run commands during
|
||||
build
|
||||
squash (bool): Squash the resulting images layers into a
|
||||
single layer.
|
||||
extra_hosts (dict): Extra hosts to add to /etc/hosts in building
|
||||
containers, as a mapping of hostname to IP address.
|
||||
platform (str): Platform in the format ``os[/arch[/variant]]``.
|
||||
isolation (str): Isolation technology used during build.
|
||||
Default: `None`.
|
||||
use_config_proxy (bool): If ``True``, and if the docker client
|
||||
configuration file (``~/.docker/config.json`` by default)
|
||||
contains a proxy configuration, the corresponding environment
|
||||
variables will be set in the container being built.
|
||||
|
||||
Returns:
|
||||
(tuple): The first item is the :py:class:`Image` object for the
|
||||
image that was built. The second item is a generator of the
|
||||
build logs as JSON-decoded objects.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.BuildError`
|
||||
If there is an error during the build.
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns any other error.
|
||||
``TypeError``
|
||||
If neither ``path`` nor ``fileobj`` is specified.
|
||||
"""
|
||||
resp = self.client.api.build(**kwargs)
|
||||
if isinstance(resp, str):
|
||||
return self.get(resp)
|
||||
last_event = None
|
||||
image_id = None
|
||||
result_stream, internal_stream = itertools.tee(json_stream(resp))
|
||||
for chunk in internal_stream:
|
||||
if 'error' in chunk:
|
||||
raise BuildError(chunk['error'], result_stream)
|
||||
if 'stream' in chunk:
|
||||
match = re.search(
|
||||
r'(^Successfully built |sha256:)([0-9a-f]+)$',
|
||||
chunk['stream']
|
||||
)
|
||||
if match:
|
||||
image_id = match.group(2)
|
||||
last_event = chunk
|
||||
if image_id:
|
||||
return (self.get(image_id), result_stream)
|
||||
raise BuildError(last_event or 'Unknown', result_stream)
|
||||
|
||||
def get(self, name):
|
||||
"""
|
||||
Gets an image.
|
||||
|
||||
Args:
|
||||
name (str): The name of the image.
|
||||
|
||||
Returns:
|
||||
(:py:class:`Image`): The image.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.ImageNotFound`
|
||||
If the image does not exist.
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
return self.prepare_model(self.client.api.inspect_image(name))
|
||||
|
||||
def get_registry_data(self, name, auth_config=None):
|
||||
"""
|
||||
Gets the registry data for an image.
|
||||
|
||||
Args:
|
||||
name (str): The name of the image.
|
||||
auth_config (dict): Override the credentials that are found in the
|
||||
config for this request. ``auth_config`` should contain the
|
||||
``username`` and ``password`` keys to be valid.
|
||||
|
||||
Returns:
|
||||
(:py:class:`RegistryData`): The data object.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
return RegistryData(
|
||||
image_name=name,
|
||||
attrs=self.client.api.inspect_distribution(name, auth_config),
|
||||
client=self.client,
|
||||
collection=self,
|
||||
)
|
||||
|
||||
def list(self, name=None, all=False, filters=None):
|
||||
"""
|
||||
List images on the server.
|
||||
|
||||
Args:
|
||||
name (str): Only show images belonging to the repository ``name``
|
||||
all (bool): Show intermediate image layers. By default, these are
|
||||
filtered out.
|
||||
filters (dict): Filters to be processed on the image list.
|
||||
Available filters:
|
||||
- ``dangling`` (bool)
|
||||
- `label` (str|list): format either ``"key"``, ``"key=value"``
|
||||
or a list of such.
|
||||
|
||||
Returns:
|
||||
(list of :py:class:`Image`): The images.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
resp = self.client.api.images(name=name, all=all, filters=filters)
|
||||
return [self.get(r["Id"]) for r in resp]
|
||||
|
||||
def load(self, data):
|
||||
"""
|
||||
Load an image that was previously saved using
|
||||
:py:meth:`~docker.models.images.Image.save` (or ``docker save``).
|
||||
Similar to ``docker load``.
|
||||
|
||||
Args:
|
||||
data (binary): Image data to be loaded.
|
||||
|
||||
Returns:
|
||||
(list of :py:class:`Image`): The images.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
resp = self.client.api.load_image(data)
|
||||
images = []
|
||||
for chunk in resp:
|
||||
if 'stream' in chunk:
|
||||
match = re.search(
|
||||
r'(^Loaded image ID: |^Loaded image: )(.+)$',
|
||||
chunk['stream']
|
||||
)
|
||||
if match:
|
||||
image_id = match.group(2)
|
||||
images.append(image_id)
|
||||
if 'error' in chunk:
|
||||
raise ImageLoadError(chunk['error'])
|
||||
|
||||
return [self.get(i) for i in images]
|
||||
|
||||
def pull(self, repository, tag=None, all_tags=False, **kwargs):
|
||||
"""
|
||||
Pull an image of the given name and return it. Similar to the
|
||||
``docker pull`` command.
|
||||
If ``tag`` is ``None`` or empty, it is set to ``latest``.
|
||||
If ``all_tags`` is set, the ``tag`` parameter is ignored and all image
|
||||
tags will be pulled.
|
||||
|
||||
If you want to get the raw pull output, use the
|
||||
:py:meth:`~docker.api.image.ImageApiMixin.pull` method in the
|
||||
low-level API.
|
||||
|
||||
Args:
|
||||
repository (str): The repository to pull
|
||||
tag (str): The tag to pull
|
||||
auth_config (dict): Override the credentials that are found in the
|
||||
config for this request. ``auth_config`` should contain the
|
||||
``username`` and ``password`` keys to be valid.
|
||||
platform (str): Platform in the format ``os[/arch[/variant]]``
|
||||
all_tags (bool): Pull all image tags
|
||||
|
||||
Returns:
|
||||
(:py:class:`Image` or list): The image that has been pulled.
|
||||
If ``all_tags`` is True, the method will return a list
|
||||
of :py:class:`Image` objects belonging to this repository.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
|
||||
Example:
|
||||
|
||||
>>> # Pull the image tagged `latest` in the busybox repo
|
||||
>>> image = client.images.pull('busybox')
|
||||
|
||||
>>> # Pull all tags in the busybox repo
|
||||
>>> images = client.images.pull('busybox', all_tags=True)
|
||||
"""
|
||||
repository, image_tag = parse_repository_tag(repository)
|
||||
tag = tag or image_tag or 'latest'
|
||||
|
||||
if 'stream' in kwargs:
|
||||
warnings.warn(
|
||||
'`stream` is not a valid parameter for this method'
|
||||
' and will be overridden',
|
||||
stacklevel=1,
|
||||
)
|
||||
del kwargs['stream']
|
||||
|
||||
pull_log = self.client.api.pull(
|
||||
repository, tag=tag, stream=True, all_tags=all_tags, **kwargs
|
||||
)
|
||||
for _ in pull_log:
|
||||
# We don't do anything with the logs, but we need
|
||||
# to keep the connection alive and wait for the image
|
||||
# to be pulled.
|
||||
pass
|
||||
if not all_tags:
|
||||
sep = '@' if tag.startswith('sha256:') else ':'
|
||||
return self.get(f'{repository}{sep}{tag}')
|
||||
return self.list(repository)
|
||||
|
||||
def push(self, repository, tag=None, **kwargs):
|
||||
return self.client.api.push(repository, tag=tag, **kwargs)
|
||||
push.__doc__ = APIClient.push.__doc__
|
||||
|
||||
def remove(self, *args, **kwargs):
|
||||
self.client.api.remove_image(*args, **kwargs)
|
||||
remove.__doc__ = APIClient.remove_image.__doc__
|
||||
|
||||
def search(self, *args, **kwargs):
|
||||
return self.client.api.search(*args, **kwargs)
|
||||
search.__doc__ = APIClient.search.__doc__
|
||||
|
||||
def prune(self, filters=None):
|
||||
return self.client.api.prune_images(filters=filters)
|
||||
prune.__doc__ = APIClient.prune_images.__doc__
|
||||
|
||||
def prune_builds(self, *args, **kwargs):
|
||||
return self.client.api.prune_builds(*args, **kwargs)
|
||||
prune_builds.__doc__ = APIClient.prune_builds.__doc__
|
||||
|
||||
|
||||
def normalize_platform(platform, engine_info):
|
||||
if platform is None:
|
||||
platform = {}
|
||||
if 'os' not in platform:
|
||||
platform['os'] = engine_info['Os']
|
||||
if 'architecture' not in platform:
|
||||
platform['architecture'] = engine_info['Arch']
|
||||
return platform
|
||||
@@ -0,0 +1,218 @@
|
||||
from ..api import APIClient
|
||||
from ..utils import version_gte
|
||||
from .containers import Container
|
||||
from .resource import Collection, Model
|
||||
|
||||
|
||||
class Network(Model):
|
||||
"""
|
||||
A Docker network.
|
||||
"""
|
||||
@property
|
||||
def name(self):
|
||||
"""
|
||||
The name of the network.
|
||||
"""
|
||||
return self.attrs.get('Name')
|
||||
|
||||
@property
|
||||
def containers(self):
|
||||
"""
|
||||
The containers that are connected to the network, as a list of
|
||||
:py:class:`~docker.models.containers.Container` objects.
|
||||
"""
|
||||
return [
|
||||
self.client.containers.get(cid) for cid in
|
||||
(self.attrs.get('Containers') or {}).keys()
|
||||
]
|
||||
|
||||
def connect(self, container, *args, **kwargs):
|
||||
"""
|
||||
Connect a container to this network.
|
||||
|
||||
Args:
|
||||
container (str): Container to connect to this network, as either
|
||||
an ID, name, or :py:class:`~docker.models.containers.Container`
|
||||
object.
|
||||
aliases (:py:class:`list`): A list of aliases for this endpoint.
|
||||
Names in that list can be used within the network to reach the
|
||||
container. Defaults to ``None``.
|
||||
links (:py:class:`list`): A list of links for this endpoint.
|
||||
Containers declared in this list will be linkedto this
|
||||
container. Defaults to ``None``.
|
||||
ipv4_address (str): The IP address of this container on the
|
||||
network, using the IPv4 protocol. Defaults to ``None``.
|
||||
ipv6_address (str): The IP address of this container on the
|
||||
network, using the IPv6 protocol. Defaults to ``None``.
|
||||
link_local_ips (:py:class:`list`): A list of link-local (IPv4/IPv6)
|
||||
addresses.
|
||||
driver_opt (dict): A dictionary of options to provide to the
|
||||
network driver. Defaults to ``None``.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
if isinstance(container, Container):
|
||||
container = container.id
|
||||
return self.client.api.connect_container_to_network(
|
||||
container, self.id, *args, **kwargs
|
||||
)
|
||||
|
||||
def disconnect(self, container, *args, **kwargs):
|
||||
"""
|
||||
Disconnect a container from this network.
|
||||
|
||||
Args:
|
||||
container (str): Container to disconnect from this network, as
|
||||
either an ID, name, or
|
||||
:py:class:`~docker.models.containers.Container` object.
|
||||
force (bool): Force the container to disconnect from a network.
|
||||
Default: ``False``
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
if isinstance(container, Container):
|
||||
container = container.id
|
||||
return self.client.api.disconnect_container_from_network(
|
||||
container, self.id, *args, **kwargs
|
||||
)
|
||||
|
||||
def remove(self):
|
||||
"""
|
||||
Remove this network.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
return self.client.api.remove_network(self.id)
|
||||
|
||||
|
||||
class NetworkCollection(Collection):
|
||||
"""
|
||||
Networks on the Docker server.
|
||||
"""
|
||||
model = Network
|
||||
|
||||
def create(self, name, *args, **kwargs):
|
||||
"""
|
||||
Create a network. Similar to the ``docker network create``.
|
||||
|
||||
Args:
|
||||
name (str): Name of the network
|
||||
driver (str): Name of the driver used to create the network
|
||||
options (dict): Driver options as a key-value dictionary
|
||||
ipam (IPAMConfig): Optional custom IP scheme for the network.
|
||||
check_duplicate (bool): Request daemon to check for networks with
|
||||
same name. Default: ``None``.
|
||||
internal (bool): Restrict external access to the network. Default
|
||||
``False``.
|
||||
labels (dict): Map of labels to set on the network. Default
|
||||
``None``.
|
||||
enable_ipv6 (bool): Enable IPv6 on the network. Default ``False``.
|
||||
attachable (bool): If enabled, and the network is in the global
|
||||
scope, non-service containers on worker nodes will be able to
|
||||
connect to the network.
|
||||
scope (str): Specify the network's scope (``local``, ``global`` or
|
||||
``swarm``)
|
||||
ingress (bool): If set, create an ingress network which provides
|
||||
the routing-mesh in swarm mode.
|
||||
|
||||
Returns:
|
||||
(:py:class:`Network`): The network that was created.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
|
||||
Example:
|
||||
A network using the bridge driver:
|
||||
|
||||
>>> client.networks.create("network1", driver="bridge")
|
||||
|
||||
You can also create more advanced networks with custom IPAM
|
||||
configurations. For example, setting the subnet to
|
||||
``192.168.52.0/24`` and gateway address to ``192.168.52.254``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> ipam_pool = docker.types.IPAMPool(
|
||||
subnet='192.168.52.0/24',
|
||||
gateway='192.168.52.254'
|
||||
)
|
||||
>>> ipam_config = docker.types.IPAMConfig(
|
||||
pool_configs=[ipam_pool]
|
||||
)
|
||||
>>> client.networks.create(
|
||||
"network1",
|
||||
driver="bridge",
|
||||
ipam=ipam_config
|
||||
)
|
||||
|
||||
"""
|
||||
resp = self.client.api.create_network(name, *args, **kwargs)
|
||||
return self.get(resp['Id'])
|
||||
|
||||
def get(self, network_id, *args, **kwargs):
|
||||
"""
|
||||
Get a network by its ID.
|
||||
|
||||
Args:
|
||||
network_id (str): The ID of the network.
|
||||
verbose (bool): Retrieve the service details across the cluster in
|
||||
swarm mode.
|
||||
scope (str): Filter the network by scope (``swarm``, ``global``
|
||||
or ``local``).
|
||||
|
||||
Returns:
|
||||
(:py:class:`Network`) The network.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.NotFound`
|
||||
If the network does not exist.
|
||||
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
|
||||
"""
|
||||
return self.prepare_model(
|
||||
self.client.api.inspect_network(network_id, *args, **kwargs)
|
||||
)
|
||||
|
||||
def list(self, *args, **kwargs):
|
||||
"""
|
||||
List networks. Similar to the ``docker network ls`` command.
|
||||
|
||||
Args:
|
||||
names (:py:class:`list`): List of names to filter by.
|
||||
ids (:py:class:`list`): List of ids to filter by.
|
||||
filters (dict): Filters to be processed on the network list.
|
||||
Available filters:
|
||||
- ``driver=[<driver-name>]`` Matches a network's driver.
|
||||
- `label` (str|list): format either ``"key"``, ``"key=value"``
|
||||
or a list of such.
|
||||
- ``type=["custom"|"builtin"]`` Filters networks by type.
|
||||
greedy (bool): Fetch more details for each network individually.
|
||||
You might want this to get the containers attached to them.
|
||||
|
||||
Returns:
|
||||
(list of :py:class:`Network`) The networks on the server.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
greedy = kwargs.pop('greedy', False)
|
||||
resp = self.client.api.networks(*args, **kwargs)
|
||||
networks = [self.prepare_model(item) for item in resp]
|
||||
if greedy and version_gte(self.client.api._version, '1.28'):
|
||||
for net in networks:
|
||||
net.reload()
|
||||
return networks
|
||||
|
||||
def prune(self, filters=None):
|
||||
return self.client.api.prune_networks(filters=filters)
|
||||
prune.__doc__ = APIClient.prune_networks.__doc__
|
||||
107
backend/venv/lib/python3.9/site-packages/docker/models/nodes.py
Normal file
107
backend/venv/lib/python3.9/site-packages/docker/models/nodes.py
Normal file
@@ -0,0 +1,107 @@
|
||||
from .resource import Collection, Model
|
||||
|
||||
|
||||
class Node(Model):
|
||||
"""A node in a swarm."""
|
||||
id_attribute = 'ID'
|
||||
|
||||
@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 update(self, node_spec):
|
||||
"""
|
||||
Update the node's configuration.
|
||||
|
||||
Args:
|
||||
node_spec (dict): Configuration settings to update. Any values
|
||||
not provided will be removed. Default: ``None``
|
||||
|
||||
Returns:
|
||||
`True` if the request went through.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
|
||||
Example:
|
||||
|
||||
>>> node_spec = {'Availability': 'active',
|
||||
'Name': 'node-name',
|
||||
'Role': 'manager',
|
||||
'Labels': {'foo': 'bar'}
|
||||
}
|
||||
>>> node.update(node_spec)
|
||||
|
||||
"""
|
||||
return self.client.api.update_node(self.id, self.version, node_spec)
|
||||
|
||||
def remove(self, force=False):
|
||||
"""
|
||||
Remove this node from the swarm.
|
||||
|
||||
Args:
|
||||
force (bool): Force remove an active node. Default: `False`
|
||||
|
||||
Returns:
|
||||
`True` if the request was successful.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.NotFound`
|
||||
If the node doesn't exist in the swarm.
|
||||
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
return self.client.api.remove_node(self.id, force=force)
|
||||
|
||||
|
||||
class NodeCollection(Collection):
|
||||
"""Nodes on the Docker server."""
|
||||
model = Node
|
||||
|
||||
def get(self, node_id):
|
||||
"""
|
||||
Get a node.
|
||||
|
||||
Args:
|
||||
node_id (string): ID of the node to be inspected.
|
||||
|
||||
Returns:
|
||||
A :py:class:`Node` object.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
return self.prepare_model(self.client.api.inspect_node(node_id))
|
||||
|
||||
def list(self, *args, **kwargs):
|
||||
"""
|
||||
List swarm nodes.
|
||||
|
||||
Args:
|
||||
filters (dict): Filters to process on the nodes list. Valid
|
||||
filters: ``id``, ``name``, ``membership`` and ``role``.
|
||||
Default: ``None``
|
||||
|
||||
Returns:
|
||||
A list of :py:class:`Node` objects.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
|
||||
Example:
|
||||
|
||||
>>> client.nodes.list(filters={'role': 'manager'})
|
||||
"""
|
||||
return [
|
||||
self.prepare_model(n)
|
||||
for n in self.client.api.nodes(*args, **kwargs)
|
||||
]
|
||||
@@ -0,0 +1,206 @@
|
||||
from .. import errors
|
||||
from .resource import Collection, Model
|
||||
|
||||
|
||||
class Plugin(Model):
|
||||
"""
|
||||
A plugin on the server.
|
||||
"""
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__}: '{self.name}'>"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""
|
||||
The plugin's name.
|
||||
"""
|
||||
return self.attrs.get('Name')
|
||||
|
||||
@property
|
||||
def enabled(self):
|
||||
"""
|
||||
Whether the plugin is enabled.
|
||||
"""
|
||||
return self.attrs.get('Enabled')
|
||||
|
||||
@property
|
||||
def settings(self):
|
||||
"""
|
||||
A dictionary representing the plugin's configuration.
|
||||
"""
|
||||
return self.attrs.get('Settings')
|
||||
|
||||
def configure(self, options):
|
||||
"""
|
||||
Update the plugin's settings.
|
||||
|
||||
Args:
|
||||
options (dict): A key-value mapping of options.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
self.client.api.configure_plugin(self.name, options)
|
||||
self.reload()
|
||||
|
||||
def disable(self, force=False):
|
||||
"""
|
||||
Disable the plugin.
|
||||
|
||||
Args:
|
||||
force (bool): Force disable. Default: False
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
|
||||
self.client.api.disable_plugin(self.name, force)
|
||||
self.reload()
|
||||
|
||||
def enable(self, timeout=0):
|
||||
"""
|
||||
Enable the plugin.
|
||||
|
||||
Args:
|
||||
timeout (int): Timeout in seconds. Default: 0
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
self.client.api.enable_plugin(self.name, timeout)
|
||||
self.reload()
|
||||
|
||||
def push(self):
|
||||
"""
|
||||
Push the plugin to a remote registry.
|
||||
|
||||
Returns:
|
||||
A dict iterator streaming the status of the upload.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
return self.client.api.push_plugin(self.name)
|
||||
|
||||
def remove(self, force=False):
|
||||
"""
|
||||
Remove the plugin from the server.
|
||||
|
||||
Args:
|
||||
force (bool): Remove even if the plugin is enabled.
|
||||
Default: False
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
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)
|
||||
yield from self.client.api.upgrade_plugin(
|
||||
self.name,
|
||||
remote,
|
||||
privileges,
|
||||
)
|
||||
self.reload()
|
||||
|
||||
|
||||
class PluginCollection(Collection):
|
||||
model = Plugin
|
||||
|
||||
def create(self, name, plugin_data_dir, gzip=False):
|
||||
"""
|
||||
Create a new plugin.
|
||||
|
||||
Args:
|
||||
name (string): The name of the plugin. The ``:latest`` tag is
|
||||
optional, and is the default if omitted.
|
||||
plugin_data_dir (string): Path to the plugin data directory.
|
||||
Plugin data directory must contain the ``config.json``
|
||||
manifest file and the ``rootfs`` directory.
|
||||
gzip (bool): Compress the context using gzip. Default: False
|
||||
|
||||
Returns:
|
||||
(:py:class:`Plugin`): The newly created plugin.
|
||||
"""
|
||||
self.client.api.create_plugin(name, plugin_data_dir, gzip)
|
||||
return self.get(name)
|
||||
|
||||
def get(self, name):
|
||||
"""
|
||||
Gets a plugin.
|
||||
|
||||
Args:
|
||||
name (str): The name of the plugin.
|
||||
|
||||
Returns:
|
||||
(:py:class:`Plugin`): The plugin.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.NotFound` If the plugin does not
|
||||
exist.
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
return self.prepare_model(self.client.api.inspect_plugin(name))
|
||||
|
||||
def install(self, remote_name, local_name=None):
|
||||
"""
|
||||
Pull and install a plugin.
|
||||
|
||||
Args:
|
||||
remote_name (string): Remote reference for the plugin to
|
||||
install. The ``:latest`` tag is optional, and is the
|
||||
default if omitted.
|
||||
local_name (string): Local name for the pulled plugin.
|
||||
The ``:latest`` tag is optional, and is the default if
|
||||
omitted. Optional.
|
||||
|
||||
Returns:
|
||||
(:py:class:`Plugin`): The installed plugin
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
privileges = self.client.api.plugin_privileges(remote_name)
|
||||
it = self.client.api.pull_plugin(remote_name, privileges, local_name)
|
||||
for _data in it:
|
||||
pass
|
||||
return self.get(local_name or remote_name)
|
||||
|
||||
def list(self):
|
||||
"""
|
||||
List plugins installed on the server.
|
||||
|
||||
Returns:
|
||||
(list of :py:class:`Plugin`): The plugins.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
resp = self.client.api.plugins()
|
||||
return [self.prepare_model(r) for r in resp]
|
||||
@@ -0,0 +1,92 @@
|
||||
class Model:
|
||||
"""
|
||||
A base class for representing a single object on the server.
|
||||
"""
|
||||
id_attribute = 'Id'
|
||||
|
||||
def __init__(self, attrs=None, client=None, collection=None):
|
||||
#: A client pointing at the server that this object is on.
|
||||
self.client = client
|
||||
|
||||
#: The collection that this model is part of.
|
||||
self.collection = collection
|
||||
|
||||
#: The raw representation of this object from the API
|
||||
self.attrs = attrs
|
||||
if self.attrs is None:
|
||||
self.attrs = {}
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__}: {self.short_id}>"
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, self.__class__) and self.id == other.id
|
||||
|
||||
def __hash__(self):
|
||||
return hash(f"{self.__class__.__name__}:{self.id}")
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
"""
|
||||
The ID of the object.
|
||||
"""
|
||||
return self.attrs.get(self.id_attribute)
|
||||
|
||||
@property
|
||||
def short_id(self):
|
||||
"""
|
||||
The ID of the object, truncated to 12 characters.
|
||||
"""
|
||||
return self.id[:12]
|
||||
|
||||
def reload(self):
|
||||
"""
|
||||
Load this object from the server again and update ``attrs`` with the
|
||||
new data.
|
||||
"""
|
||||
new_model = self.collection.get(self.id)
|
||||
self.attrs = new_model.attrs
|
||||
|
||||
|
||||
class Collection:
|
||||
"""
|
||||
A base class for representing all objects of a particular type on the
|
||||
server.
|
||||
"""
|
||||
|
||||
#: The type of object this collection represents, set by subclasses
|
||||
model = None
|
||||
|
||||
def __init__(self, client=None):
|
||||
#: The client pointing at the server that this collection of objects
|
||||
#: is on.
|
||||
self.client = client
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
raise TypeError(
|
||||
f"'{self.__class__.__name__}' object is not callable. "
|
||||
"You might be trying to use the old (pre-2.0) API - "
|
||||
"use docker.APIClient if so."
|
||||
)
|
||||
|
||||
def list(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get(self, key):
|
||||
raise NotImplementedError
|
||||
|
||||
def create(self, attrs=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def prepare_model(self, attrs):
|
||||
"""
|
||||
Create a model from a set of attributes.
|
||||
"""
|
||||
if isinstance(attrs, Model):
|
||||
attrs.client = self.client
|
||||
attrs.collection = self
|
||||
return attrs
|
||||
elif isinstance(attrs, dict):
|
||||
return self.model(attrs=attrs, client=self.client, collection=self)
|
||||
else:
|
||||
raise Exception(f"Can't create {self.model.__name__} from {attrs}")
|
||||
@@ -0,0 +1,70 @@
|
||||
from ..api import APIClient
|
||||
from .resource import Collection, Model
|
||||
|
||||
|
||||
class Secret(Model):
|
||||
"""A secret."""
|
||||
id_attribute = 'ID'
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__}: '{self.name}'>"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.attrs['Spec']['Name']
|
||||
|
||||
def remove(self):
|
||||
"""
|
||||
Remove this secret.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If secret failed to remove.
|
||||
"""
|
||||
return self.client.api.remove_secret(self.id)
|
||||
|
||||
|
||||
class SecretCollection(Collection):
|
||||
"""Secrets on the Docker server."""
|
||||
model = Secret
|
||||
|
||||
def create(self, **kwargs):
|
||||
obj = self.client.api.create_secret(**kwargs)
|
||||
obj.setdefault("Spec", {})["Name"] = kwargs.get("name")
|
||||
return self.prepare_model(obj)
|
||||
create.__doc__ = APIClient.create_secret.__doc__
|
||||
|
||||
def get(self, secret_id):
|
||||
"""
|
||||
Get a secret.
|
||||
|
||||
Args:
|
||||
secret_id (str): Secret ID.
|
||||
|
||||
Returns:
|
||||
(:py:class:`Secret`): The secret.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.NotFound`
|
||||
If the secret does not exist.
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
return self.prepare_model(self.client.api.inspect_secret(secret_id))
|
||||
|
||||
def list(self, **kwargs):
|
||||
"""
|
||||
List secrets. Similar to the ``docker secret ls`` command.
|
||||
|
||||
Args:
|
||||
filters (dict): Server-side list filtering options.
|
||||
|
||||
Returns:
|
||||
(list of :py:class:`Secret`): The secrets.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
resp = self.client.api.secrets(**kwargs)
|
||||
return [self.prepare_model(obj) for obj in resp]
|
||||
@@ -0,0 +1,390 @@
|
||||
import copy
|
||||
|
||||
from docker.errors import InvalidArgument, create_unexpected_kwargs_error
|
||||
from docker.types import ContainerSpec, Placement, ServiceMode, TaskTemplate
|
||||
|
||||
from .resource import Collection, Model
|
||||
|
||||
|
||||
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
|
||||
190
backend/venv/lib/python3.9/site-packages/docker/models/swarm.py
Normal file
190
backend/venv/lib/python3.9/site-packages/docker/models/swarm.py
Normal file
@@ -0,0 +1,190 @@
|
||||
from docker.api import APIClient
|
||||
from docker.errors import APIError
|
||||
|
||||
from .resource import Model
|
||||
|
||||
|
||||
class Swarm(Model):
|
||||
"""
|
||||
The server's Swarm state. This a singleton that must be reloaded to get
|
||||
the current state of the Swarm.
|
||||
"""
|
||||
id_attribute = 'ID'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.client:
|
||||
try:
|
||||
self.reload()
|
||||
except APIError as e:
|
||||
# FIXME: https://github.com/docker/docker/issues/29192
|
||||
if e.response.status_code not in (406, 503):
|
||||
raise
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
"""
|
||||
The version number of the swarm. 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 get_unlock_key(self):
|
||||
return self.client.api.get_unlock_key()
|
||||
get_unlock_key.__doc__ = APIClient.get_unlock_key.__doc__
|
||||
|
||||
def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377',
|
||||
force_new_cluster=False, default_addr_pool=None,
|
||||
subnet_size=None, data_path_addr=None, data_path_port=None,
|
||||
**kwargs):
|
||||
"""
|
||||
Initialize a new swarm on this Engine.
|
||||
|
||||
Args:
|
||||
advertise_addr (str): Externally reachable address advertised to
|
||||
other nodes. This can either be an address/port combination in
|
||||
the form ``192.168.1.1:4567``, or an interface followed by a
|
||||
port number, like ``eth0:4567``. If the port number is omitted,
|
||||
the port number from the listen address is used.
|
||||
|
||||
If not specified, it will be automatically detected when
|
||||
possible.
|
||||
listen_addr (str): Listen address used for inter-manager
|
||||
communication, as well as determining the networking interface
|
||||
used for the VXLAN Tunnel Endpoint (VTEP). This can either be
|
||||
an address/port combination in the form ``192.168.1.1:4567``,
|
||||
or an interface followed by a port number, like ``eth0:4567``.
|
||||
If the port number is omitted, the default swarm listening port
|
||||
is used. Default: ``0.0.0.0:2377``
|
||||
force_new_cluster (bool): Force creating a new Swarm, even if
|
||||
already part of one. Default: False
|
||||
default_addr_pool (list of str): Default Address Pool specifies
|
||||
default subnet pools for global scope networks. Each pool
|
||||
should be specified as a CIDR block, like '10.0.0.0/8'.
|
||||
Default: None
|
||||
subnet_size (int): SubnetSize specifies the subnet size of the
|
||||
networks created from the default subnet pool. Default: None
|
||||
data_path_addr (string): Address or interface to use for data path
|
||||
traffic. For example, 192.168.1.1, or an interface, like eth0.
|
||||
data_path_port (int): Port number to use for data path traffic.
|
||||
Acceptable port range is 1024 to 49151. If set to ``None`` or
|
||||
0, the default port 4789 will be used. Default: None
|
||||
task_history_retention_limit (int): Maximum number of tasks
|
||||
history stored.
|
||||
snapshot_interval (int): Number of logs entries between snapshot.
|
||||
keep_old_snapshots (int): Number of snapshots to keep beyond the
|
||||
current snapshot.
|
||||
log_entries_for_slow_followers (int): Number of log entries to
|
||||
keep around to sync up slow followers after a snapshot is
|
||||
created.
|
||||
heartbeat_tick (int): Amount of ticks (in seconds) between each
|
||||
heartbeat.
|
||||
election_tick (int): Amount of ticks (in seconds) needed without a
|
||||
leader to trigger a new election.
|
||||
dispatcher_heartbeat_period (int): The delay for an agent to send
|
||||
a heartbeat to the dispatcher.
|
||||
node_cert_expiry (int): Automatic expiry for nodes certificates.
|
||||
external_ca (dict): Configuration for forwarding signing requests
|
||||
to an external certificate authority. Use
|
||||
``docker.types.SwarmExternalCA``.
|
||||
name (string): Swarm's name
|
||||
labels (dict): User-defined key/value metadata.
|
||||
signing_ca_cert (str): The desired signing CA certificate for all
|
||||
swarm node TLS leaf certificates, in PEM format.
|
||||
signing_ca_key (str): The desired signing CA key for all swarm
|
||||
node TLS leaf certificates, in PEM format.
|
||||
ca_force_rotate (int): An integer whose purpose is to force swarm
|
||||
to generate a new signing CA certificate and key, if none have
|
||||
been specified.
|
||||
autolock_managers (boolean): If set, generate a key and use it to
|
||||
lock data stored on the managers.
|
||||
log_driver (DriverConfig): The default log driver to use for tasks
|
||||
created in the orchestrator.
|
||||
|
||||
Returns:
|
||||
(str): The ID of the created node.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
|
||||
Example:
|
||||
|
||||
>>> client.swarm.init(
|
||||
advertise_addr='eth0', listen_addr='0.0.0.0:5000',
|
||||
force_new_cluster=False, default_addr_pool=['10.20.0.0/16],
|
||||
subnet_size=24, snapshot_interval=5000,
|
||||
log_entries_for_slow_followers=1200
|
||||
)
|
||||
|
||||
"""
|
||||
init_kwargs = {
|
||||
'advertise_addr': advertise_addr,
|
||||
'listen_addr': listen_addr,
|
||||
'force_new_cluster': force_new_cluster,
|
||||
'default_addr_pool': default_addr_pool,
|
||||
'subnet_size': subnet_size,
|
||||
'data_path_addr': data_path_addr,
|
||||
'data_path_port': data_path_port,
|
||||
}
|
||||
init_kwargs['swarm_spec'] = self.client.api.create_swarm_spec(**kwargs)
|
||||
node_id = self.client.api.init_swarm(**init_kwargs)
|
||||
self.reload()
|
||||
return node_id
|
||||
|
||||
def join(self, *args, **kwargs):
|
||||
return self.client.api.join_swarm(*args, **kwargs)
|
||||
join.__doc__ = APIClient.join_swarm.__doc__
|
||||
|
||||
def leave(self, *args, **kwargs):
|
||||
return self.client.api.leave_swarm(*args, **kwargs)
|
||||
leave.__doc__ = APIClient.leave_swarm.__doc__
|
||||
|
||||
def reload(self):
|
||||
"""
|
||||
Inspect the swarm on the server and store the response in
|
||||
:py:attr:`attrs`.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
self.attrs = self.client.api.inspect_swarm()
|
||||
|
||||
def unlock(self, key):
|
||||
return self.client.api.unlock_swarm(key)
|
||||
unlock.__doc__ = APIClient.unlock_swarm.__doc__
|
||||
|
||||
def update(self, rotate_worker_token=False, rotate_manager_token=False,
|
||||
rotate_manager_unlock_key=False, **kwargs):
|
||||
"""
|
||||
Update the swarm's configuration.
|
||||
|
||||
It takes the same arguments as :py:meth:`init`, except
|
||||
``advertise_addr``, ``listen_addr``, and ``force_new_cluster``. In
|
||||
addition, it takes these arguments:
|
||||
|
||||
Args:
|
||||
rotate_worker_token (bool): Rotate the worker join token. Default:
|
||||
``False``.
|
||||
rotate_manager_token (bool): Rotate the manager join token.
|
||||
Default: ``False``.
|
||||
rotate_manager_unlock_key (bool): Rotate the manager unlock key.
|
||||
Default: ``False``.
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
|
||||
"""
|
||||
# this seems to have to be set
|
||||
if kwargs.get('node_cert_expiry') is None:
|
||||
kwargs['node_cert_expiry'] = 7776000000000000
|
||||
|
||||
return self.client.api.update_swarm(
|
||||
version=self.version,
|
||||
swarm_spec=self.client.api.create_swarm_spec(**kwargs),
|
||||
rotate_worker_token=rotate_worker_token,
|
||||
rotate_manager_token=rotate_manager_token,
|
||||
rotate_manager_unlock_key=rotate_manager_unlock_key
|
||||
)
|
||||
@@ -0,0 +1,99 @@
|
||||
from ..api import APIClient
|
||||
from .resource import Collection, Model
|
||||
|
||||
|
||||
class Volume(Model):
|
||||
"""A volume."""
|
||||
id_attribute = 'Name'
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""The name of the volume."""
|
||||
return self.attrs['Name']
|
||||
|
||||
def remove(self, force=False):
|
||||
"""
|
||||
Remove this volume.
|
||||
|
||||
Args:
|
||||
force (bool): Force removal of volumes that were already removed
|
||||
out of band by the volume driver plugin.
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If volume failed to remove.
|
||||
"""
|
||||
return self.client.api.remove_volume(self.id, force=force)
|
||||
|
||||
|
||||
class VolumeCollection(Collection):
|
||||
"""Volumes on the Docker server."""
|
||||
model = Volume
|
||||
|
||||
def create(self, name=None, **kwargs):
|
||||
"""
|
||||
Create a volume.
|
||||
|
||||
Args:
|
||||
name (str): Name of the volume. If not specified, the engine
|
||||
generates a name.
|
||||
driver (str): Name of the driver used to create the volume
|
||||
driver_opts (dict): Driver options as a key-value dictionary
|
||||
labels (dict): Labels to set on the volume
|
||||
|
||||
Returns:
|
||||
(:py:class:`Volume`): The volume created.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
|
||||
Example:
|
||||
|
||||
>>> volume = client.volumes.create(name='foobar', driver='local',
|
||||
driver_opts={'foo': 'bar', 'baz': 'false'},
|
||||
labels={"key": "value"})
|
||||
|
||||
"""
|
||||
obj = self.client.api.create_volume(name, **kwargs)
|
||||
return self.prepare_model(obj)
|
||||
|
||||
def get(self, volume_id):
|
||||
"""
|
||||
Get a volume.
|
||||
|
||||
Args:
|
||||
volume_id (str): Volume name.
|
||||
|
||||
Returns:
|
||||
(:py:class:`Volume`): The volume.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.NotFound`
|
||||
If the volume does not exist.
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
return self.prepare_model(self.client.api.inspect_volume(volume_id))
|
||||
|
||||
def list(self, **kwargs):
|
||||
"""
|
||||
List volumes. Similar to the ``docker volume ls`` command.
|
||||
|
||||
Args:
|
||||
filters (dict): Server-side list filtering options.
|
||||
|
||||
Returns:
|
||||
(list of :py:class:`Volume`): The volumes.
|
||||
|
||||
Raises:
|
||||
:py:class:`docker.errors.APIError`
|
||||
If the server returns an error.
|
||||
"""
|
||||
resp = self.client.api.volumes(**kwargs)
|
||||
if not resp.get('Volumes'):
|
||||
return []
|
||||
return [self.prepare_model(obj) for obj in resp['Volumes']]
|
||||
|
||||
def prune(self, filters=None):
|
||||
return self.client.api.prune_volumes(filters=filters)
|
||||
prune.__doc__ = APIClient.prune_volumes.__doc__
|
||||
Reference in New Issue
Block a user