summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/docker/plugins
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:18:34 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:18:34 +0000
commit3667197efb7b18ec842efd504785965911f8ac4b (patch)
tree0b986a4bc6879d080b100666a97cdabbc9ca1f28 /ansible_collections/community/docker/plugins
parentAdding upstream version 9.5.1+dfsg. (diff)
downloadansible-upstream/10.0.0+dfsg.tar.xz
ansible-upstream/10.0.0+dfsg.zip
Adding upstream version 10.0.0+dfsg.upstream/10.0.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/community/docker/plugins')
-rw-r--r--ansible_collections/community/docker/plugins/connection/docker.py2
-rw-r--r--ansible_collections/community/docker/plugins/connection/docker_api.py1
-rw-r--r--ansible_collections/community/docker/plugins/connection/nsenter.py4
-rw-r--r--ansible_collections/community/docker/plugins/doc_fragments/docker.py9
-rw-r--r--ansible_collections/community/docker/plugins/inventory/docker_containers.py1
-rw-r--r--ansible_collections/community/docker/plugins/inventory/docker_swarm.py3
-rw-r--r--ansible_collections/community/docker/plugins/module_utils/_api/transport/basehttpadapter.py12
-rw-r--r--ansible_collections/community/docker/plugins/module_utils/module_container/base.py2
-rw-r--r--ansible_collections/community/docker/plugins/module_utils/module_container/docker_api.py18
-rw-r--r--ansible_collections/community/docker/plugins/module_utils/selectors.py21
-rw-r--r--ansible_collections/community/docker/plugins/module_utils/util.py15
-rw-r--r--ansible_collections/community/docker/plugins/modules/docker_compose.py5
-rw-r--r--ansible_collections/community/docker/plugins/modules/docker_container.py19
-rw-r--r--ansible_collections/community/docker/plugins/modules/docker_container_exec.py3
-rw-r--r--ansible_collections/community/docker/plugins/modules/docker_image_build.py247
-rw-r--r--ansible_collections/community/docker/plugins/modules/docker_network.py35
-rw-r--r--ansible_collections/community/docker/plugins/modules/docker_prune.py60
-rw-r--r--ansible_collections/community/docker/plugins/modules/docker_swarm_service.py16
-rw-r--r--ansible_collections/community/docker/plugins/plugin_utils/socket_handler.py2
19 files changed, 455 insertions, 20 deletions
diff --git a/ansible_collections/community/docker/plugins/connection/docker.py b/ansible_collections/community/docker/plugins/connection/docker.py
index 68247dae2..133fe6a75 100644
--- a/ansible_collections/community/docker/plugins/connection/docker.py
+++ b/ansible_collections/community/docker/plugins/connection/docker.py
@@ -83,7 +83,6 @@ import os.path
import subprocess
import re
-from ansible.compat import selectors
from ansible.errors import AnsibleError, AnsibleFileNotFound
from ansible.module_utils.six.moves import shlex_quote
from ansible.module_utils.common.process import get_bin_path
@@ -91,6 +90,7 @@ from ansible.module_utils.common.text.converters import to_bytes, to_native, to_
from ansible.plugins.connection import ConnectionBase, BUFSIZE
from ansible.utils.display import Display
+from ansible_collections.community.docker.plugins.module_utils.selectors import selectors
from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion
display = Display()
diff --git a/ansible_collections/community/docker/plugins/connection/docker_api.py b/ansible_collections/community/docker/plugins/connection/docker_api.py
index 3b99281c3..a6dec85aa 100644
--- a/ansible_collections/community/docker/plugins/connection/docker_api.py
+++ b/ansible_collections/community/docker/plugins/connection/docker_api.py
@@ -21,6 +21,7 @@ notes:
with Python's C(SSLSocket)s. See U(https://github.com/ansible-collections/community.docker/issues/605) for more information.
extends_documentation_fragment:
- community.docker.docker.api_documentation
+ - community.docker.docker.ssl_version_deprecation
- community.docker.docker.var_names
options:
remote_user:
diff --git a/ansible_collections/community/docker/plugins/connection/nsenter.py b/ansible_collections/community/docker/plugins/connection/nsenter.py
index f429f8cef..ccc660b99 100644
--- a/ansible_collections/community/docker/plugins/connection/nsenter.py
+++ b/ansible_collections/community/docker/plugins/connection/nsenter.py
@@ -50,13 +50,15 @@ import fcntl
import ansible.constants as C
from ansible.errors import AnsibleError
-from ansible.module_utils.compat import selectors
from ansible.module_utils.six import binary_type, text_type
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
from ansible.plugins.connection import ConnectionBase
from ansible.utils.display import Display
from ansible.utils.path import unfrackpath
+from ansible_collections.community.docker.plugins.module_utils.selectors import selectors
+
+
display = Display()
diff --git a/ansible_collections/community/docker/plugins/doc_fragments/docker.py b/ansible_collections/community/docker/plugins/doc_fragments/docker.py
index 92989a97b..2c78c5fae 100644
--- a/ansible_collections/community/docker/plugins/doc_fragments/docker.py
+++ b/ansible_collections/community/docker/plugins/doc_fragments/docker.py
@@ -392,3 +392,12 @@ notes:
- This module does B(not) use the L(Docker SDK for Python,https://docker-py.readthedocs.io/en/stable/) to
communicate with the Docker daemon. It directly calls the Docker CLI program.
'''
+
+ # DEPRECATED: this will be removed from community.docker 4.0.0! Use with care!
+ SSL_VERSION_DEPRECATION = '''
+options:
+ ssl_version:
+ deprecated:
+ why: This was necessary a long time ago to handle problems with older TLS/SSL versions. It is no longer necessary nowadays.
+ version: 4.0.0
+'''
diff --git a/ansible_collections/community/docker/plugins/inventory/docker_containers.py b/ansible_collections/community/docker/plugins/inventory/docker_containers.py
index 0cae05472..f353b03bd 100644
--- a/ansible_collections/community/docker/plugins/inventory/docker_containers.py
+++ b/ansible_collections/community/docker/plugins/inventory/docker_containers.py
@@ -21,6 +21,7 @@ author:
extends_documentation_fragment:
- ansible.builtin.constructed
- community.docker.docker.api_documentation
+ - community.docker.docker.ssl_version_deprecation
- community.library_inventory_filtering_v1.inventory_filter
description:
- Reads inventories from the Docker API.
diff --git a/ansible_collections/community/docker/plugins/inventory/docker_swarm.py b/ansible_collections/community/docker/plugins/inventory/docker_swarm.py
index acceac86c..6d1556ff5 100644
--- a/ansible_collections/community/docker/plugins/inventory/docker_swarm.py
+++ b/ansible_collections/community/docker/plugins/inventory/docker_swarm.py
@@ -78,6 +78,9 @@ DOCUMENTATION = '''
- Provide a valid SSL version number. Default value determined
by L(SSL Python module, https://docs.python.org/3/library/ssl.html).
type: str
+ deprecated:
+ why: This was necessary a long time ago to handle problems with SSL versions. It is no longer necessary nowadays.
+ version: 4.0.0
api_version:
description:
- The version of the Docker API running on the Docker Host.
diff --git a/ansible_collections/community/docker/plugins/module_utils/_api/transport/basehttpadapter.py b/ansible_collections/community/docker/plugins/module_utils/_api/transport/basehttpadapter.py
index 2afa60aea..14062a0ba 100644
--- a/ansible_collections/community/docker/plugins/module_utils/_api/transport/basehttpadapter.py
+++ b/ansible_collections/community/docker/plugins/module_utils/_api/transport/basehttpadapter.py
@@ -18,3 +18,15 @@ class BaseHTTPAdapter(_HTTPAdapter):
super(BaseHTTPAdapter, self).close()
if hasattr(self, 'pools'):
self.pools.clear()
+
+ # Hotfix for requests 2.32.0 and 2.32.1: its commit
+ # https://github.com/psf/requests/commit/c0813a2d910ea6b4f8438b91d315b8d181302356
+ # changes requests.adapters.HTTPAdapter to no longer call get_connection() from
+ # send(), but instead call _get_connection().
+ def _get_connection(self, request, *args, **kwargs):
+ return self.get_connection(request.url, kwargs.get('proxies'))
+
+ # Fix for requests 2.32.2+:
+ # https://github.com/psf/requests/commit/c98e4d133ef29c46a9b68cd783087218a8075e05
+ def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None):
+ return self.get_connection(request.url, proxies)
diff --git a/ansible_collections/community/docker/plugins/module_utils/module_container/base.py b/ansible_collections/community/docker/plugins/module_utils/module_container/base.py
index 0f776aa5c..410ffb153 100644
--- a/ansible_collections/community/docker/plugins/module_utils/module_container/base.py
+++ b/ansible_collections/community/docker/plugins/module_utils/module_container/base.py
@@ -935,9 +935,11 @@ OPTION_HEALTHCHECK = (
OptionGroup(preprocess=_preprocess_healthcheck)
.add_option('healthcheck', type='dict', ansible_suboptions=dict(
test=dict(type='raw'),
+ test_cli_compatible=dict(type='bool', default=False),
interval=dict(type='str'),
timeout=dict(type='str'),
start_period=dict(type='str'),
+ start_interval=dict(type='str'),
retries=dict(type='int'),
))
)
diff --git a/ansible_collections/community/docker/plugins/module_utils/module_container/docker_api.py b/ansible_collections/community/docker/plugins/module_utils/module_container/docker_api.py
index 61a5500c9..d3da84fe5 100644
--- a/ansible_collections/community/docker/plugins/module_utils/module_container/docker_api.py
+++ b/ansible_collections/community/docker/plugins/module_utils/module_container/docker_api.py
@@ -436,6 +436,7 @@ class DockerAPIEngine(Engine):
min_api_version=None,
preprocess_value=None,
update_parameter=None,
+ extra_option_minimal_versions=None,
):
def preprocess_value_(module, client, api_version, options, values):
if len(options) != 1:
@@ -499,6 +500,7 @@ class DockerAPIEngine(Engine):
set_value=set_value,
min_api_version=min_api_version,
update_value=update_value,
+ extra_option_minimal_versions=extra_option_minimal_versions,
)
@classmethod
@@ -512,6 +514,7 @@ class DockerAPIEngine(Engine):
min_api_version=None,
preprocess_value=None,
update_parameter=None,
+ extra_option_minimal_versions=None,
):
def preprocess_value_(module, client, api_version, options, values):
if len(options) != 1:
@@ -577,6 +580,7 @@ class DockerAPIEngine(Engine):
set_value=set_value,
min_api_version=min_api_version,
update_value=update_value,
+ extra_option_minimal_versions=extra_option_minimal_versions,
)
@@ -742,7 +746,7 @@ def _preprocess_etc_hosts(module, client, api_version, value):
def _preprocess_healthcheck(module, client, api_version, value):
if value is None:
return value
- if not value or not value.get('test'):
+ if not value or not (value.get('test') or (value.get('test_cli_compatible') and value.get('test') is None)):
value = {'test': ['NONE']}
elif 'test' in value:
value['test'] = normalize_healthcheck_test(value['test'])
@@ -751,6 +755,7 @@ def _preprocess_healthcheck(module, client, api_version, value):
'Interval': value.get('interval'),
'Timeout': value.get('timeout'),
'StartPeriod': value.get('start_period'),
+ 'StartInterval': value.get('start_interval'),
'Retries': value.get('retries'),
})
@@ -1300,7 +1305,16 @@ OPTION_ETC_HOSTS.add_engine('docker_api', DockerAPIEngine.host_config_value('Ext
OPTION_GROUPS.add_engine('docker_api', DockerAPIEngine.host_config_value('GroupAdd'))
OPTION_HEALTHCHECK.add_engine('docker_api', DockerAPIEngine.config_value(
- 'Healthcheck', preprocess_value=_preprocess_healthcheck, postprocess_for_get=_postprocess_healthcheck_get_value))
+ 'Healthcheck',
+ preprocess_value=_preprocess_healthcheck,
+ postprocess_for_get=_postprocess_healthcheck_get_value,
+ extra_option_minimal_versions={
+ 'healthcheck.start_interval': {
+ 'docker_api_version': '1.44',
+ 'detect_usage': lambda c: c.module.params['healthcheck'] and c.module.params['healthcheck']['start_interval'] is not None,
+ },
+ },
+))
OPTION_HOSTNAME.add_engine('docker_api', DockerAPIEngine.config_value('Hostname'))
diff --git a/ansible_collections/community/docker/plugins/module_utils/selectors.py b/ansible_collections/community/docker/plugins/module_utils/selectors.py
new file mode 100644
index 000000000..ca52cc877
--- /dev/null
+++ b/ansible_collections/community/docker/plugins/module_utils/selectors.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2024, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+"""Provide selectors import."""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+# Once we drop support for ansible-core 2.16, we can remove the try/except.
+
+from sys import version_info as _python_version_info
+
+
+if _python_version_info < (3, 4):
+ from ansible.module_utils.compat import selectors # noqa: F401, pylint: disable=unused-import
+else:
+ import selectors # noqa: F401, pylint: disable=unused-import
diff --git a/ansible_collections/community/docker/plugins/module_utils/util.py b/ansible_collections/community/docker/plugins/module_utils/util.py
index efd3301f1..9235e34d7 100644
--- a/ansible_collections/community/docker/plugins/module_utils/util.py
+++ b/ansible_collections/community/docker/plugins/module_utils/util.py
@@ -29,7 +29,12 @@ DOCKER_COMMON_ARGS = dict(
ca_path=dict(type='path', aliases=['ca_cert', 'tls_ca_cert', 'cacert_path']),
client_cert=dict(type='path', aliases=['tls_client_cert', 'cert_path']),
client_key=dict(type='path', aliases=['tls_client_key', 'key_path']),
- ssl_version=dict(type='str', fallback=(env_fallback, ['DOCKER_SSL_VERSION'])),
+ ssl_version=dict(
+ type='str',
+ fallback=(env_fallback, ['DOCKER_SSL_VERSION']),
+ removed_in_version='4.0.0',
+ removed_from_collection='community.docker',
+ ),
tls=dict(type='bool', default=DEFAULT_TLS, fallback=(env_fallback, ['DOCKER_TLS'])),
use_ssh_client=dict(type='bool', default=False),
validate_certs=dict(type='bool', default=DEFAULT_TLS_VERIFY, fallback=(env_fallback, ['DOCKER_TLS_VERIFY']), aliases=['tls_verify']),
@@ -348,9 +353,9 @@ def normalize_healthcheck(healthcheck, normalize_test=False):
result = dict()
# All supported healthcheck parameters
- options = ('test', 'interval', 'timeout', 'start_period', 'retries')
+ options = ('test', 'test_cli_compatible', 'interval', 'timeout', 'start_period', 'start_interval', 'retries')
- duration_options = ('interval', 'timeout', 'start_period')
+ duration_options = ('interval', 'timeout', 'start_period', 'start_interval')
for key in options:
if key in healthcheck:
@@ -361,7 +366,7 @@ def normalize_healthcheck(healthcheck, normalize_test=False):
continue
if key in duration_options:
value = convert_duration_to_nanosecond(value)
- if not value:
+ if not value and not (healthcheck.get('test_cli_compatible') and key == 'test'):
continue
if key == 'retries':
try:
@@ -371,7 +376,7 @@ def normalize_healthcheck(healthcheck, normalize_test=False):
'Cannot parse number of retries for healthcheck. '
'Expected an integer, got "{0}".'.format(value)
)
- if key == 'test' and normalize_test:
+ if key == 'test' and value and normalize_test:
value = normalize_healthcheck_test(value)
result[key] = value
diff --git a/ansible_collections/community/docker/plugins/modules/docker_compose.py b/ansible_collections/community/docker/plugins/modules/docker_compose.py
index f8edbee4b..3af3bebb1 100644
--- a/ansible_collections/community/docker/plugins/modules/docker_compose.py
+++ b/ansible_collections/community/docker/plugins/modules/docker_compose.py
@@ -14,6 +14,11 @@ module: docker_compose
short_description: Manage multi-container Docker applications with Docker Compose V1
+deprecated:
+ removed_in: 4.0.0
+ why: This module uses docker-compose v1, which is End of Life since July 2022.
+ alternative: Migrate to M(community.docker.docker_compose_v2)
+
author: "Chris Houseknecht (@chouseknecht)"
description:
diff --git a/ansible_collections/community/docker/plugins/modules/docker_container.py b/ansible_collections/community/docker/plugins/modules/docker_container.py
index d7dbc3780..60768637f 100644
--- a/ansible_collections/community/docker/plugins/modules/docker_container.py
+++ b/ansible_collections/community/docker/plugins/modules/docker_container.py
@@ -369,7 +369,7 @@ options:
- Configure a check that is run to determine whether or not containers for this service are "healthy".
- "See the docs for the L(HEALTHCHECK Dockerfile instruction,https://docs.docker.com/engine/reference/builder/#healthcheck)
for details on how healthchecks work."
- - "O(healthcheck.interval), O(healthcheck.timeout) and O(healthcheck.start_period) are specified as durations.
+ - "O(healthcheck.interval), O(healthcheck.timeout), O(healthcheck.start_period), and O(healthcheck.start_interval) are specified as durations.
They accept duration as a string in a format that look like: V(5h34m56s), V(1m30s), and so on.
The supported units are V(us), V(ms), V(s), V(m) and V(h)."
type: dict
@@ -379,6 +379,16 @@ options:
- Command to run to check health.
- Must be either a string or a list. If it is a list, the first item must be one of V(NONE), V(CMD) or V(CMD-SHELL).
type: raw
+ test_cli_compatible:
+ description:
+ - If set to V(true), omitting O(healthcheck.test) while providing O(healthcheck) does not disable healthchecks,
+ but simply overwrites the image's values by the ones specified in O(healthcheck). This is
+ the behavior used by the Docker CLI.
+ - If set to V(false), omitting O(healthcheck.test) will disable the container's health check.
+ This is the classical behavior of the module and currently the default behavior.
+ default: false
+ type: bool
+ version_added: 3.10.0
interval:
description:
- Time between running the check.
@@ -399,6 +409,12 @@ options:
- Start period for the container to initialize before starting health-retries countdown.
- The default used by the Docker daemon is V(0s).
type: str
+ start_interval:
+ description:
+ - Time between health checks during the start period. This option requires Docker Engine version 25.0 or later.
+ - The default used by the Docker daemon is V(5s).
+ type: str
+ version_added: 3.10.0
hostname:
description:
- The container's hostname.
@@ -1196,6 +1212,7 @@ EXAMPLES = '''
timeout: 10s
retries: 3
start_period: 30s
+ start_interval: 10s
- name: Remove healthcheck from container
community.docker.docker_container:
diff --git a/ansible_collections/community/docker/plugins/modules/docker_container_exec.py b/ansible_collections/community/docker/plugins/modules/docker_container_exec.py
index 0d92dad96..251af5bcd 100644
--- a/ansible_collections/community/docker/plugins/modules/docker_container_exec.py
+++ b/ansible_collections/community/docker/plugins/modules/docker_container_exec.py
@@ -162,7 +162,6 @@ import shlex
import traceback
from ansible.module_utils.common.text.converters import to_text, to_bytes, to_native
-from ansible.module_utils.compat import selectors
from ansible.module_utils.six import string_types
from ansible_collections.community.docker.plugins.module_utils.common_api import (
@@ -170,6 +169,8 @@ from ansible_collections.community.docker.plugins.module_utils.common_api import
RequestException,
)
+from ansible_collections.community.docker.plugins.module_utils.selectors import selectors
+
from ansible_collections.community.docker.plugins.module_utils.socket_handler import (
DockerSocketHandlerModule,
)
diff --git a/ansible_collections/community/docker/plugins/modules/docker_image_build.py b/ansible_collections/community/docker/plugins/modules/docker_image_build.py
index 7f9502098..48478b550 100644
--- a/ansible_collections/community/docker/plugins/modules/docker_image_build.py
+++ b/ansible_collections/community/docker/plugins/modules/docker_image_build.py
@@ -18,6 +18,9 @@ version_added: 3.6.0
description:
- This module allows you to build Docker images using Docker's buildx plugin (BuildKit).
+ - Note that the module is B(not idempotent) in the sense of classical Ansible modules.
+ The only idempotence check is whether the built image already exists. This check can
+ be disabled with the O(rebuild) option.
extends_documentation_fragment:
- community.docker.docker.cli_documentation
@@ -89,8 +92,10 @@ options:
type: str
platform:
description:
- - Platform in the format C(os[/arch[/variant]]).
- type: str
+ - Platforms in the format C(os[/arch[/variant]]).
+ - Since community.docker 3.10.0 this can be a list of platforms, instead of just a single platform.
+ type: list
+ elements: str
shm_size:
description:
- "Size of C(/dev/shm) in format C(<number>[<unit>]). Number is positive integer.
@@ -110,7 +115,121 @@ options:
- never
- always
default: never
-
+ secrets:
+ description:
+ - Secrets to expose to the build.
+ type: list
+ elements: dict
+ version_added: 3.10.0
+ suboptions:
+ id:
+ description:
+ - The secret identifier.
+ - The secret will be made available as a file in the container under C(/run/secrets/<id>).
+ type: str
+ required: true
+ type:
+ description:
+ - Type of the secret.
+ type: str
+ choices:
+ file:
+ - Reads the secret from a file on the target.
+ - The file must be specified in O(secrets[].src).
+ env:
+ - Reads the secret from an environment variable on the target.
+ - The environment variable must be named in O(secrets[].env).
+ - Note that this requires the Buildkit plugin to have version 0.6.0 or newer.
+ value:
+ - Provides the secret from a given value O(secrets[].value).
+ - B(Note) that the secret will be passed as an environment variable to C(docker compose).
+ Use another mean of transport if you consider this not safe enough.
+ - Note that this requires the Buildkit plugin to have version 0.6.0 or newer.
+ required: true
+ src:
+ description:
+ - Source path of the secret.
+ - Only supported and required for O(secrets[].type=file).
+ type: path
+ env:
+ description:
+ - Environment value of the secret.
+ - Only supported and required for O(secrets[].type=env).
+ type: str
+ value:
+ description:
+ - Value of the secret.
+ - B(Note) that the secret will be passed as an environment variable to C(docker compose).
+ Use another mean of transport if you consider this not safe enough.
+ - Only supported and required for O(secrets[].type=value).
+ type: str
+ outputs:
+ description:
+ - Output destinations.
+ - You can provide a list of exporters to export the built image in various places.
+ Note that not all exporters might be supported by the build driver used.
+ - Note that depending on how this option is used, no image with name O(name) and tag O(tag) might
+ be created, which can cause the basic idempotency this module offers to not work.
+ - Providing an empty list to this option is equivalent to not specifying it at all.
+ The default behavior is a single entry with O(outputs[].type=image).
+ type: list
+ elements: dict
+ version_added: 3.10.0
+ suboptions:
+ type:
+ description:
+ - The type of exporter to use.
+ type: str
+ choices:
+ local:
+ - This export type writes all result files to a directory on the client.
+ The new files will be owned by the current user.
+ On multi-platform builds, all results will be put in subdirectories by their platform.
+ - The destination has to be provided in O(outputs[].dest).
+ tar:
+ - This export type export type writes all result files as a single tarball on the client.
+ On multi-platform builds, all results will be put in subdirectories by their platform.
+ - The destination has to be provided in O(outputs[].dest).
+ oci:
+ - This export type writes the result image or manifest list as an
+ L(OCI image layout, https://github.com/opencontainers/image-spec/blob/v1.0.1/image-layout.md)
+ tarball on the client.
+ - The destination has to be provided in O(outputs[].dest).
+ docker:
+ - This export type writes the single-platform result image as a Docker image specification tarball on the client.
+ Tarballs created by this exporter are also OCI compatible.
+ - The destination can be provided in O(outputs[].dest).
+ If not specified, the tar will be loaded automatically to the local image store.
+ - The Docker context where to import the result can be provided in O(outputs[].context).
+ image:
+ - This exporter writes the build result as an image or a manifest list.
+ When using this driver, the image will appear in C(docker images).
+ - The image name can be provided in O(outputs[].name). If it is not provided, the
+ - Optionally, image can be automatically pushed to a registry by setting O(outputs[].push=true).
+ required: true
+ dest:
+ description:
+ - The destination path.
+ - Required for O(outputs[].type=local), O(outputs[].type=tar), O(outputs[].type=oci).
+ - Optional for O(outputs[].type=docker).
+ type: path
+ context:
+ description:
+ - Name for the Docker context where to import the result.
+ - Optional for O(outputs[].type=docker).
+ type: str
+ name:
+ description:
+ - Name under which the image is stored under.
+ - If not provided, O(name) and O(tag) will be used.
+ - Optional for O(outputs[].type=image).
+ type: str
+ push:
+ description:
+ - Whether to push the built image to a registry.
+ - Only used for O(outputs[].type=image).
+ type: bool
+ default: false
requirements:
- "Docker CLI with Docker buildx plugin"
@@ -128,6 +247,15 @@ EXAMPLES = '''
name: localhost/python/3.12:latest
path: /home/user/images/python
dockerfile: Dockerfile-3.12
+
+- name: Build multi-platform image
+ community.docker.docker_image_build:
+ name: multi-platform-image
+ tag: "1.5.2"
+ path: /home/user/images/multi-platform
+ platform:
+ - linux/amd64
+ - linux/arm64/v8
'''
RETURN = '''
@@ -138,6 +266,7 @@ image:
sample: {}
'''
+import base64
import os
import traceback
@@ -156,6 +285,8 @@ from ansible_collections.community.docker.plugins.module_utils.util import (
is_valid_tag,
)
+from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion
+
from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import (
parse_repository_tag,
)
@@ -194,10 +325,26 @@ class ImageBuilder(DockerBaseClass):
self.shm_size = convert_to_bytes(parameters['shm_size'], self.client.module, 'shm_size')
self.labels = clean_dict_booleans_for_docker_api(parameters['labels'])
self.rebuild = parameters['rebuild']
+ self.secrets = parameters['secrets']
+ self.outputs = parameters['outputs']
buildx = self.client.get_client_plugin_info('buildx')
if buildx is None:
self.fail('Docker CLI {0} does not have the buildx plugin installed'.format(self.client.get_cli()))
+ buildx_version = buildx['Version'].lstrip('v')
+
+ if self.secrets:
+ for secret in self.secrets:
+ if secret['type'] in ('env', 'value'):
+ if LooseVersion(buildx_version) < LooseVersion('0.6.0'):
+ self.fail('The Docker buildx plugin has version {version}, but 0.6.0 is needed for secrets of type=env and type=value'.format(
+ version=buildx_version,
+ ))
+ if self.outputs and len(self.outputs) > 1:
+ if LooseVersion(buildx_version) < LooseVersion('0.13.0'):
+ self.fail('The Docker buildx plugin has version {version}, but 0.13.0 is needed to specify more than one output'.format(
+ version=buildx_version,
+ ))
self.path = parameters['path']
if not os.path.isdir(self.path):
@@ -230,6 +377,7 @@ class ImageBuilder(DockerBaseClass):
args.extend([option, value])
def add_args(self, args):
+ environ_update = {}
args.extend(['--tag', '%s:%s' % (self.name, self.tag)])
if self.dockerfile:
args.extend(['--file', os.path.join(self.path, self.dockerfile)])
@@ -248,11 +396,54 @@ class ImageBuilder(DockerBaseClass):
if self.target:
args.extend(['--target', self.target])
if self.platform:
- args.extend(['--platform', self.platform])
+ for platform in self.platform:
+ args.extend(['--platform', platform])
if self.shm_size:
args.extend(['--shm-size', str(self.shm_size)])
if self.labels:
self.add_list_arg(args, '--label', dict_to_list(self.labels))
+ if self.secrets:
+ random_prefix = None
+ for index, secret in enumerate(self.secrets):
+ if secret['type'] == 'file':
+ args.extend(['--secret', 'id={id},type=file,src={src}'.format(id=secret['id'], src=secret['src'])])
+ if secret['type'] == 'env':
+ args.extend(['--secret', 'id={id},type=env,env={env}'.format(id=secret['id'], env=secret['src'])])
+ if secret['type'] == 'value':
+ # We pass values on using environment variables. The user has been warned in the documentation
+ # that they should only use this mechanism when being comfortable with it.
+ if random_prefix is None:
+ # Use /dev/urandom to generate some entropy to make the environment variable's name unguessable
+ random_prefix = base64.b64encode(os.urandom(16)).decode('utf-8').replace('=', '')
+ env_name = 'ANSIBLE_DOCKER_COMPOSE_ENV_SECRET_{random}_{id}'.format(
+ random=random_prefix,
+ id=index,
+ )
+ environ_update[env_name] = secret['value']
+ args.extend(['--secret', 'id={id},type=env,env={env}'.format(id=secret['id'], env=env_name)])
+ if self.outputs:
+ for output in self.outputs:
+ if output['type'] == 'local':
+ args.extend(['--output', 'type=local,dest={dest}'.format(dest=output['dest'])])
+ if output['type'] == 'tar':
+ args.extend(['--output', 'type=tar,dest={dest}'.format(dest=output['dest'])])
+ if output['type'] == 'oci':
+ args.extend(['--output', 'type=oci,dest={dest}'.format(dest=output['dest'])])
+ if output['type'] == 'docker':
+ more = []
+ if output['dest'] is not None:
+ more.append('dest={dest}'.format(dest=output['dest']))
+ if output['dest'] is not None:
+ more.append('context={context}'.format(context=output['context']))
+ args.extend(['--output', 'type=docker,{more}'.format(more=','.join(more))])
+ if output['type'] == 'image':
+ more = []
+ if output['name'] is not None:
+ more.append('name={name}'.format(name=output['name']))
+ if output['push']:
+ more.append('push=true')
+ args.extend(['--output', 'type=image,{more}'.format(more=','.join(more))])
+ return environ_update
def build_image(self):
image = self.client.find_image(self.name, self.tag)
@@ -269,9 +460,9 @@ class ImageBuilder(DockerBaseClass):
results['changed'] = True
if not self.check_mode:
args = ['buildx', 'build', '--progress', 'plain']
- self.add_args(args)
+ environ_update = self.add_args(args)
args.extend(['--', self.path])
- rc, stdout, stderr = self.client.call_cli(*args)
+ rc, stdout, stderr = self.client.call_cli(*args, environ_update=environ_update)
if rc != 0:
self.fail('Building %s:%s failed' % (self.name, self.tag), stdout=to_native(stdout), stderr=to_native(stderr))
results['stdout'] = to_native(stdout)
@@ -294,10 +485,52 @@ def main():
etc_hosts=dict(type='dict'),
args=dict(type='dict'),
target=dict(type='str'),
- platform=dict(type='str'),
+ platform=dict(type='list', elements='str'),
shm_size=dict(type='str'),
labels=dict(type='dict'),
rebuild=dict(type='str', choices=['never', 'always'], default='never'),
+ secrets=dict(
+ type='list',
+ elements='dict',
+ options=dict(
+ id=dict(type='str', required=True),
+ type=dict(type='str', choices=['file', 'env', 'value'], required=True),
+ src=dict(type='path'),
+ env=dict(type='str'),
+ value=dict(type='str', no_log=True),
+ ),
+ required_if=[
+ ('type', 'file', ['src']),
+ ('type', 'env', ['env']),
+ ('type', 'value', ['value']),
+ ],
+ mutually_exclusive=[
+ ('src', 'env', 'value'),
+ ],
+ no_log=False,
+ ),
+ outputs=dict(
+ type='list',
+ elements='dict',
+ options=dict(
+ type=dict(type='str', choices=['local', 'tar', 'oci', 'docker', 'image'], required=True),
+ dest=dict(type='path'),
+ context=dict(type='str'),
+ name=dict(type='str'),
+ push=dict(type='bool', default=False),
+ ),
+ required_if=[
+ ('type', 'local', ['dest']),
+ ('type', 'tar', ['dest']),
+ ('type', 'oci', ['dest']),
+ ],
+ mutually_exclusive=[
+ ('dest', 'name'),
+ ('dest', 'push'),
+ ('context', 'name'),
+ ('context', 'push'),
+ ],
+ ),
)
client = AnsibleModuleDockerClient(
diff --git a/ansible_collections/community/docker/plugins/modules/docker_network.py b/ansible_collections/community/docker/plugins/modules/docker_network.py
index 5670ceea0..c5dd3b229 100644
--- a/ansible_collections/community/docker/plugins/modules/docker_network.py
+++ b/ansible_collections/community/docker/plugins/modules/docker_network.py
@@ -35,6 +35,18 @@ options:
aliases:
- network_name
+ config_from:
+ description:
+ - Specifies the config only network to use the config from.
+ type: str
+ version_added: 3.10.0
+
+ config_only:
+ description:
+ - Sets that this is a config only network.
+ type: bool
+ version_added: 3.10.0
+
connected:
description:
- List of container names or container IDs to connect to a network.
@@ -283,6 +295,8 @@ class TaskParameters(DockerBaseClass):
self.name = None
self.connected = None
+ self.config_from = None
+ self.config_only = None
self.driver = None
self.driver_options = None
self.ipam_driver = None
@@ -300,6 +314,11 @@ class TaskParameters(DockerBaseClass):
for key, value in client.module.params.items():
setattr(self, key, value)
+ # config_only sets driver to 'null' (and scope to 'local') so force that here. Otherwise we get
+ # diffs of 'null' --> 'bridge' given that the driver option defaults to 'bridge'.
+ if self.config_only:
+ self.driver = 'null'
+
def container_names_in_network(network):
return [c['Name'] for c in network['Containers'].values()] if network['Containers'] else []
@@ -401,6 +420,14 @@ class DockerNetworkManager(object):
:return: (bool, list)
'''
differences = DifferenceTracker()
+ if self.parameters.config_only is not None and self.parameters.config_only != net.get('ConfigOnly', False):
+ differences.add('config_only',
+ parameter=self.parameters.config_only,
+ active=net.get('ConfigOnly', False))
+ if self.parameters.config_from is not None and self.parameters.config_from != net.get('ConfigFrom', {}).get('Network', ''):
+ differences.add('config_from',
+ parameter=self.parameters.config_from,
+ active=net.get('ConfigFrom', {}).get('Network', ''))
if self.parameters.driver and self.parameters.driver != net['Driver']:
differences.add('driver',
parameter=self.parameters.driver,
@@ -503,6 +530,10 @@ class DockerNetworkManager(object):
'CheckDuplicate': None,
}
+ if self.parameters.config_only is not None:
+ data['ConfigOnly'] = self.parameters.config_only
+ if self.parameters.config_from:
+ data['ConfigFrom'] = {'Network': self.parameters.config_from}
if self.parameters.enable_ipv6:
data['EnableIPv6'] = True
if self.parameters.internal:
@@ -630,6 +661,8 @@ class DockerNetworkManager(object):
def main():
argument_spec = dict(
name=dict(type='str', required=True, aliases=['network_name']),
+ config_from=dict(type='str'),
+ config_only=dict(type='bool'),
connected=dict(type='list', default=[], elements='str', aliases=['containers']),
state=dict(type='str', default='present', choices=['present', 'absent']),
driver=dict(type='str', default='bridge'),
@@ -653,6 +686,8 @@ def main():
)
option_minimal_versions = dict(
+ config_from=dict(docker_api_version='1.30'),
+ config_only=dict(docker_api_version='1.30'),
scope=dict(docker_api_version='1.30'),
attachable=dict(docker_api_version='1.26'),
)
diff --git a/ansible_collections/community/docker/plugins/modules/docker_prune.py b/ansible_collections/community/docker/plugins/modules/docker_prune.py
index 1dfbf290e..a333c52fe 100644
--- a/ansible_collections/community/docker/plugins/modules/docker_prune.py
+++ b/ansible_collections/community/docker/plugins/modules/docker_prune.py
@@ -81,6 +81,28 @@ options:
- Whether to prune the builder cache.
type: bool
default: false
+ builder_cache_all:
+ description:
+ - Whether to remove all types of build cache.
+ type: bool
+ default: false
+ version_added: 3.10.0
+ builder_cache_filters:
+ description:
+ - A dictionary of filter values used for selecting images to delete.
+ - "For example, C(until: 10m)."
+ - See L(the API documentation,https://docs.docker.com/engine/api/v1.44/#tag/Image/operation/BuildPrune)
+ for more information on possible filters.
+ type: dict
+ version_added: 3.10.0
+ builder_cache_keep_storage:
+ description:
+ - Amount of disk space to keep for cache in format C(<number>[<unit>])."
+ - "Number is a positive integer. Unit can be one of V(B) (byte), V(K) (kibibyte, 1024B), V(M) (mebibyte), V(G) (gibibyte),
+ V(T) (tebibyte), or V(P) (pebibyte)."
+ - "Omitting the unit defaults to bytes."
+ type: str
+ version_added: 3.10.0
author:
- "Felix Fontein (@felixfontein)"
@@ -181,11 +203,20 @@ builder_cache_space_reclaimed:
returned: O(builder_cache=true)
type: int
sample: 0
+builder_cache_caches_deleted:
+ description:
+ - The build caches that were deleted.
+ returned: O(builder_cache=true) and API version is 1.39 or later
+ type: list
+ elements: str
+ sample: []
+ version_added: 3.10.0
'''
import traceback
from ansible.module_utils.common.text.converters import to_native
+from ansible.module_utils.common.text.formatters import human_to_bytes
from ansible_collections.community.docker.plugins.module_utils.common_api import (
AnsibleDockerClient,
@@ -209,13 +240,29 @@ def main():
volumes=dict(type='bool', default=False),
volumes_filters=dict(type='dict'),
builder_cache=dict(type='bool', default=False),
+ builder_cache_all=dict(type='bool', default=False),
+ builder_cache_filters=dict(type='dict'),
+ builder_cache_keep_storage=dict(type='str'), # convert to bytes
)
client = AnsibleDockerClient(
argument_spec=argument_spec,
+ option_minimal_versions=dict(
+ builder_cache=dict(docker_py_version='1.31'),
+ builder_cache_all=dict(docker_py_version='1.39'),
+ builder_cache_filters=dict(docker_py_version='1.31'),
+ builder_cache_keep_storage=dict(docker_py_version='1.39'),
+ ),
# supports_check_mode=True,
)
+ builder_cache_keep_storage = None
+ if client.module.params.get('builder_cache_keep_storage') is not None:
+ try:
+ builder_cache_keep_storage = human_to_bytes(client.module.params.get('builder_cache_keep_storage'))
+ except ValueError as exc:
+ client.module.fail_json(msg='Error while parsing value of builder_cache_keep_storage: {0}'.format(exc))
+
try:
result = dict()
changed = False
@@ -256,10 +303,21 @@ def main():
changed = True
if client.module.params['builder_cache']:
- res = client.post_to_json('/build/prune')
+ filters = clean_dict_booleans_for_docker_api(client.module.params.get('builder_cache_filters'))
+ params = {'filters': convert_filters(filters)}
+ if client.module.params.get('builder_cache_all'):
+ params['all'] = 'true'
+ if builder_cache_keep_storage is not None:
+ params['keep-storage'] = builder_cache_keep_storage
+ res = client.post_to_json('/build/prune', params=params)
result['builder_cache_space_reclaimed'] = res['SpaceReclaimed']
if result['builder_cache_space_reclaimed']:
changed = True
+ if 'CachesDeleted' in res:
+ # API version 1.39+: return value CachesDeleted (list of str)
+ result['builder_cache_caches_deleted'] = res['CachesDeleted']
+ if result['builder_cache_caches_deleted']:
+ changed = True
result['changed'] = changed
client.module.exit_json(**result)
diff --git a/ansible_collections/community/docker/plugins/modules/docker_swarm_service.py b/ansible_collections/community/docker/plugins/modules/docker_swarm_service.py
index 95cc10366..4660d1138 100644
--- a/ansible_collections/community/docker/plugins/modules/docker_swarm_service.py
+++ b/ansible_collections/community/docker/plugins/modules/docker_swarm_service.py
@@ -83,6 +83,11 @@ options:
- Dictionary of key value pairs.
- Corresponds to the C(--container-label) option of C(docker service create).
type: dict
+ sysctls:
+ description:
+ - Dictionary of key, value pairs.
+ version_added: 3.10.0
+ type: dict
dns:
description:
- List of custom DNS servers.
@@ -681,6 +686,7 @@ swarm_service:
"engine.labels.operatingsystem == ubuntu 14.04"
],
"container_labels": null,
+ "sysctls": null,
"dns": null,
"dns_options": null,
"dns_search": null,
@@ -1226,6 +1232,7 @@ class DockerService(DockerBaseClass):
self.log_driver_options = None
self.labels = None
self.container_labels = None
+ self.sysctls = None
self.limit_cpu = None
self.limit_memory = None
self.reserve_cpu = None
@@ -1292,6 +1299,7 @@ class DockerService(DockerBaseClass):
'placement_preferences': self.placement_preferences,
'labels': self.labels,
'container_labels': self.container_labels,
+ 'sysctls': self.sysctls,
'mode': self.mode,
'replicas': self.replicas,
'endpoint_mode': self.endpoint_mode,
@@ -1539,6 +1547,7 @@ class DockerService(DockerBaseClass):
s.tty = ap['tty']
s.labels = ap['labels']
s.container_labels = ap['container_labels']
+ s.sysctls = ap['sysctls']
s.mode = ap['mode']
s.stop_signal = ap['stop_signal']
s.user = ap['user']
@@ -1740,6 +1749,8 @@ class DockerService(DockerBaseClass):
differences.add('reserve_memory', parameter=self.reserve_memory, active=os.reserve_memory)
if self.container_labels is not None and self.container_labels != (os.container_labels or {}):
differences.add('container_labels', parameter=self.container_labels, active=os.container_labels)
+ if self.sysctls is not None and self.sysctls != (os.sysctls or {}):
+ differences.add('sysctls', parameter=self.sysctls, active=os.sysctls)
if self.stop_signal is not None and self.stop_signal != os.stop_signal:
differences.add('stop_signal', parameter=self.stop_signal, active=os.stop_signal)
if self.stop_grace_period is not None and self.stop_grace_period != os.stop_grace_period:
@@ -1934,6 +1945,8 @@ class DockerService(DockerBaseClass):
container_spec_args['user'] = self.user
if self.container_labels is not None:
container_spec_args['labels'] = self.container_labels
+ if self.sysctls is not None:
+ container_spec_args['sysctls'] = self.sysctls
if self.healthcheck is not None:
container_spec_args['healthcheck'] = types.Healthcheck(**self.healthcheck)
elif self.healthcheck_disabled:
@@ -2163,6 +2176,7 @@ class DockerServiceManager(object):
ds.read_only = task_template_data['ContainerSpec'].get('ReadOnly')
ds.cap_add = task_template_data['ContainerSpec'].get('CapabilityAdd')
ds.cap_drop = task_template_data['ContainerSpec'].get('CapabilityDrop')
+ ds.sysctls = task_template_data['ContainerSpec'].get('Sysctls')
healthcheck_data = task_template_data['ContainerSpec'].get('Healthcheck')
if healthcheck_data:
@@ -2676,6 +2690,7 @@ def main():
hosts=dict(type='dict'),
labels=dict(type='dict'),
container_labels=dict(type='dict'),
+ sysctls=dict(type='dict'),
mode=dict(
type='str',
default='replicated',
@@ -2751,6 +2766,7 @@ def main():
init=dict(docker_py_version='4.0.0', docker_api_version='1.37'),
cap_add=dict(docker_py_version='5.0.3', docker_api_version='1.41'),
cap_drop=dict(docker_py_version='5.0.3', docker_api_version='1.41'),
+ sysctls=dict(docker_py_version='6.0.0', docker_api_version='1.40'),
# specials
publish_mode=dict(
docker_py_version='3.0.0',
diff --git a/ansible_collections/community/docker/plugins/plugin_utils/socket_handler.py b/ansible_collections/community/docker/plugins/plugin_utils/socket_handler.py
index 204996f24..e8fd266c8 100644
--- a/ansible_collections/community/docker/plugins/plugin_utils/socket_handler.py
+++ b/ansible_collections/community/docker/plugins/plugin_utils/socket_handler.py
@@ -6,7 +6,7 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-from ansible.compat import selectors
+from ansible_collections.community.docker.plugins.module_utils.selectors import selectors
from ansible_collections.community.docker.plugins.module_utils.socket_handler import (
DockerSocketHandlerBase,