mirror of
https://github.com/ansible-collections/community.docker.git
synced 2025-12-15 19:42:06 +00:00
Add docker_image_build module. (#735)
This commit is contained in:
parent
199d9e50d3
commit
ce7402dc9f
@ -62,6 +62,7 @@ If you use the Ansible package and do not update collections independently, use
|
||||
- community.docker.docker_container_info: retrieve information on Docker containers
|
||||
- community.docker.docker_host_info: retrieve information on the Docker daemon
|
||||
- community.docker.docker_image: manage Docker images
|
||||
- community.docker.docker_image_build: build Docker images using Docker buildx
|
||||
- community.docker.docker_image_info: retrieve information on Docker images
|
||||
- community.docker.docker_image_load: load Docker images from archives
|
||||
- community.docker.docker_image_pull: pull Docker images from registries
|
||||
|
||||
@ -14,6 +14,7 @@ action_groups:
|
||||
- docker_container_info
|
||||
- docker_host_info
|
||||
- docker_image
|
||||
- docker_image_build
|
||||
- docker_image_info
|
||||
- docker_image_load
|
||||
- docker_image_pull
|
||||
|
||||
@ -297,3 +297,100 @@ requirements:
|
||||
- pyOpenSSL (when using TLS)
|
||||
- backports.ssl_match_hostname (when using TLS on Python 2)
|
||||
'''
|
||||
|
||||
# Docker doc fragment when using the Docker CLI
|
||||
CLI_DOCUMENTATION = r'''
|
||||
options:
|
||||
docker_cli:
|
||||
description:
|
||||
- Path to the Docker CLI. If not provided, will search for Docker CLI on the E(PATH).
|
||||
type: path
|
||||
docker_host:
|
||||
description:
|
||||
- The URL or Unix socket path used to connect to the Docker API. To connect to a remote host, provide the
|
||||
TCP connection string. For example, V(tcp://192.0.2.23:2376). If TLS is used to encrypt the connection,
|
||||
the module will automatically replace C(tcp) in the connection URL with C(https).
|
||||
- If the value is not specified in the task, the value of environment variable E(DOCKER_HOST) will be used
|
||||
instead. If the environment variable is not set, the default value will be used.
|
||||
type: str
|
||||
default: unix:///var/run/docker.sock
|
||||
aliases: [ docker_url ]
|
||||
tls_hostname:
|
||||
description:
|
||||
- When verifying the authenticity of the Docker Host server, provide the expected name of the server.
|
||||
- If the value is not specified in the task, the value of environment variable E(DOCKER_TLS_HOSTNAME) will
|
||||
be used instead. If the environment variable is not set, the default value will be used.
|
||||
- Note that this option had a default value V(localhost) in older versions. It was removed in community.docker 3.0.0.
|
||||
type: str
|
||||
api_version:
|
||||
description:
|
||||
- The version of the Docker API running on the Docker Host.
|
||||
- Defaults to the latest version of the API supported by this collection and the docker daemon.
|
||||
- If the value is not specified in the task, the value of environment variable E(DOCKER_API_VERSION) will be
|
||||
used instead. If the environment variable is not set, the default value will be used.
|
||||
type: str
|
||||
default: auto
|
||||
aliases: [ docker_api_version ]
|
||||
timeout:
|
||||
description:
|
||||
- The maximum amount of time in seconds to wait on a response from the API.
|
||||
- If the value is not specified in the task, the value of environment variable E(DOCKER_TIMEOUT) will be used
|
||||
instead. If the environment variable is not set, the default value will be used.
|
||||
type: int
|
||||
default: 60
|
||||
ca_cert:
|
||||
description:
|
||||
- Use a CA certificate when performing server verification by providing the path to a CA certificate file.
|
||||
- If the value is not specified in the task and the environment variable E(DOCKER_CERT_PATH) is set,
|
||||
the file C(ca.pem) from the directory specified in the environment variable E(DOCKER_CERT_PATH) will be used.
|
||||
type: path
|
||||
aliases: [ tls_ca_cert, cacert_path ]
|
||||
client_cert:
|
||||
description:
|
||||
- Path to the client's TLS certificate file.
|
||||
- If the value is not specified in the task and the environment variable E(DOCKER_CERT_PATH) is set,
|
||||
the file C(cert.pem) from the directory specified in the environment variable E(DOCKER_CERT_PATH) will be used.
|
||||
type: path
|
||||
aliases: [ tls_client_cert, cert_path ]
|
||||
client_key:
|
||||
description:
|
||||
- Path to the client's TLS key file.
|
||||
- If the value is not specified in the task and the environment variable E(DOCKER_CERT_PATH) is set,
|
||||
the file C(key.pem) from the directory specified in the environment variable E(DOCKER_CERT_PATH) will be used.
|
||||
type: path
|
||||
aliases: [ tls_client_key, key_path ]
|
||||
tls:
|
||||
description:
|
||||
- Secure the connection to the API by using TLS without verifying the authenticity of the Docker host
|
||||
server. Note that if O(validate_certs) is set to V(true) as well, it will take precedence.
|
||||
- If the value is not specified in the task, the value of environment variable E(DOCKER_TLS) will be used
|
||||
instead. If the environment variable is not set, the default value will be used.
|
||||
type: bool
|
||||
default: false
|
||||
validate_certs:
|
||||
description:
|
||||
- Secure the connection to the API by using TLS and verifying the authenticity of the Docker host server.
|
||||
- If the value is not specified in the task, the value of environment variable E(DOCKER_TLS_VERIFY) will be
|
||||
used instead. If the environment variable is not set, the default value will be used.
|
||||
type: bool
|
||||
default: false
|
||||
aliases: [ tls_verify ]
|
||||
debug:
|
||||
description:
|
||||
- Debug mode
|
||||
type: bool
|
||||
default: false
|
||||
cli_context:
|
||||
description:
|
||||
- The Docker CLI context to use.
|
||||
type: str
|
||||
|
||||
notes:
|
||||
- Connect to the Docker daemon by providing parameters with each task or by defining environment variables.
|
||||
You can define E(DOCKER_HOST), E(DOCKER_TLS_HOSTNAME), E(DOCKER_API_VERSION), E(DOCKER_CERT_PATH),
|
||||
E(DOCKER_TLS), E(DOCKER_TLS_VERIFY) and E(DOCKER_TIMEOUT). If you are using docker machine, run the script shipped
|
||||
with the product that sets up the environment. It will set these variables for you. See
|
||||
U(https://docs.docker.com/machine/reference/env/) for more details.
|
||||
- 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.
|
||||
'''
|
||||
|
||||
@ -482,7 +482,7 @@ class AnsibleDockerClientBase(Client):
|
||||
images = self._image_lookup(lookup, tag)
|
||||
|
||||
if len(images) > 1:
|
||||
self.fail("Registry returned more than one result for %s:%s" % (name, tag))
|
||||
self.fail("Daemon returned more than one result for %s:%s" % (name, tag))
|
||||
|
||||
if len(images) == 1:
|
||||
try:
|
||||
|
||||
@ -396,7 +396,7 @@ class AnsibleDockerClientBase(Client):
|
||||
images = self._image_lookup(lookup, tag)
|
||||
|
||||
if len(images) > 1:
|
||||
self.fail("Registry returned more than one result for %s:%s" % (name, tag))
|
||||
self.fail("Daemon returned more than one result for %s:%s" % (name, tag))
|
||||
|
||||
if len(images) == 1:
|
||||
try:
|
||||
|
||||
345
plugins/module_utils/common_cli.py
Normal file
345
plugins/module_utils/common_cli.py
Normal file
@ -0,0 +1,345 @@
|
||||
# Copyright (c) 2023, 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
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
import abc
|
||||
import json
|
||||
import shlex
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||
from ansible.module_utils.common.process import get_bin_path
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils._api.auth import resolve_repository_name
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils.util import ( # noqa: F401, pylint: disable=unused-import
|
||||
DEFAULT_DOCKER_HOST,
|
||||
DEFAULT_TLS,
|
||||
DEFAULT_TLS_VERIFY,
|
||||
DEFAULT_TIMEOUT_SECONDS,
|
||||
DOCKER_MUTUALLY_EXCLUSIVE,
|
||||
DOCKER_REQUIRED_TOGETHER,
|
||||
sanitize_result,
|
||||
)
|
||||
|
||||
|
||||
DOCKER_COMMON_ARGS = dict(
|
||||
docker_cli=dict(type='path'),
|
||||
docker_host=dict(type='str', default=DEFAULT_DOCKER_HOST, fallback=(env_fallback, ['DOCKER_HOST']), aliases=['docker_url']),
|
||||
tls_hostname=dict(type='str', fallback=(env_fallback, ['DOCKER_TLS_HOSTNAME'])),
|
||||
api_version=dict(type='str', default='auto', fallback=(env_fallback, ['DOCKER_API_VERSION']), aliases=['docker_api_version']),
|
||||
timeout=dict(type='int', default=DEFAULT_TIMEOUT_SECONDS, fallback=(env_fallback, ['DOCKER_TIMEOUT'])),
|
||||
ca_cert=dict(type='path', aliases=['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']),
|
||||
tls=dict(type='bool', default=DEFAULT_TLS, fallback=(env_fallback, ['DOCKER_TLS'])),
|
||||
validate_certs=dict(type='bool', default=DEFAULT_TLS_VERIFY, fallback=(env_fallback, ['DOCKER_TLS_VERIFY']), aliases=['tls_verify']),
|
||||
debug=dict(type='bool', default=False),
|
||||
cli_context=dict(type='str'),
|
||||
)
|
||||
|
||||
|
||||
class DockerException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AnsibleDockerClientBase(object):
|
||||
def __init__(self, common_args, min_docker_api_version=None):
|
||||
self._environment = {}
|
||||
if common_args['tls_hostname']:
|
||||
self._environment['DOCKER_TLS_HOSTNAME'] = common_args['tls_hostname']
|
||||
if common_args['api_version'] and common_args['api_version'] != 'auto':
|
||||
self._environment['DOCKER_API_VERSION'] = common_args['api_version']
|
||||
self._cli = common_args.get('docker_cli')
|
||||
if self._cli is None:
|
||||
try:
|
||||
self._cli = get_bin_path('docker')
|
||||
except ValueError:
|
||||
self.fail('Cannot find docker CLI in path. Please provide it explicitly with the docker_cli parameter')
|
||||
|
||||
self._cli_base = [self._cli]
|
||||
self._cli_base.extend(['--host', common_args['docker_host']])
|
||||
if common_args['validate_certs']:
|
||||
self._cli_base.append('--tlsverify')
|
||||
elif common_args['tls']:
|
||||
self._cli_base.append('--tls')
|
||||
if common_args['ca_cert']:
|
||||
self._cli_base.extend(['--tlscacert', common_args['ca_cert']])
|
||||
if common_args['client_cert']:
|
||||
self._cli_base.extend(['--tlscert', common_args['client_cert']])
|
||||
if common_args['client_key']:
|
||||
self._cli_base.extend(['--tlskey', common_args['client_key']])
|
||||
if common_args['cli_context']:
|
||||
self._cli_base.extend(['--context', common_args['cli_context']])
|
||||
|
||||
# `--format json` was only added as a shorthand for `--format {{ json . }}` in Docker 23.0
|
||||
dummy, self._version, dummy = self.call_cli_json('version', '--format', '{{ json . }}', check_rc=True)
|
||||
self._info = None
|
||||
|
||||
self.docker_api_version_str = self._version['Server']['ApiVersion']
|
||||
self.docker_api_version = LooseVersion(self.docker_api_version_str)
|
||||
min_docker_api_version = min_docker_api_version or '1.25'
|
||||
if self.docker_api_version < LooseVersion(min_docker_api_version):
|
||||
self.fail('Docker API version is %s. Minimum version required is %s.' % (self.docker_api_version_str, min_docker_api_version))
|
||||
|
||||
def log(self, msg, pretty_print=False):
|
||||
pass
|
||||
# if self.debug:
|
||||
# log_file = open('docker.log', 'a')
|
||||
# if pretty_print:
|
||||
# log_file.write(json.dumps(msg, sort_keys=True, indent=4, separators=(',', ': ')))
|
||||
# log_file.write(u'\n')
|
||||
# else:
|
||||
# log_file.write(msg + u'\n')
|
||||
|
||||
def get_cli(self):
|
||||
return self._cli
|
||||
|
||||
def get_version_info(self):
|
||||
return self._version
|
||||
|
||||
def _compose_cmd(self, args):
|
||||
return self._cli_base + list(args)
|
||||
|
||||
def _compose_cmd_str(self, args):
|
||||
return ' '.join(shlex.quote(a) for a in self._compose_cmd(args))
|
||||
|
||||
@abc.abstractmethod
|
||||
# def call_cli(self, *args, check_rc=False, data=None, cwd=None, environ_update=None):
|
||||
def call_cli(self, *args, **kwargs):
|
||||
# Python 2.7 doesn't like anything than '**kwargs' after '*args', so we have to do this manually...
|
||||
pass
|
||||
|
||||
# def call_cli_json(self, *args, check_rc=False, data=None, cwd=None, environ_update=None, warn_on_stderr=False):
|
||||
def call_cli_json(self, *args, **kwargs):
|
||||
warn_on_stderr = kwargs.pop('warn_on_stderr', False)
|
||||
rc, stdout, stderr = self.call_cli(*args, **kwargs)
|
||||
if warn_on_stderr and stderr:
|
||||
self.warn(to_native(stderr))
|
||||
try:
|
||||
data = json.loads(stdout)
|
||||
except Exception as exc:
|
||||
self.fail('Error while parsing JSON output of {cmd}: {exc}\nJSON output: {stdout}'.format(
|
||||
cmd=self._compose_cmd_str(args),
|
||||
exc=to_native(exc),
|
||||
stdout=to_native(stdout),
|
||||
))
|
||||
return rc, data, stderr
|
||||
|
||||
# def call_cli_json_stream(self, *args, check_rc=False, data=None, cwd=None, environ_update=None, warn_on_stderr=False):
|
||||
def call_cli_json_stream(self, *args, **kwargs):
|
||||
warn_on_stderr = kwargs.pop('warn_on_stderr', False)
|
||||
rc, stdout, stderr = self.call_cli(*args, **kwargs)
|
||||
if warn_on_stderr and stderr:
|
||||
self.warn(to_native(stderr))
|
||||
result = []
|
||||
try:
|
||||
for line in stdout.splitlines():
|
||||
line = line.strip()
|
||||
if line.startswith(b'{'):
|
||||
result.append(json.loads(line))
|
||||
except Exception as exc:
|
||||
self.fail('Error while parsing JSON output of {cmd}: {exc}\nJSON output: {stdout}'.format(
|
||||
cmd=self._compose_cmd_str(args),
|
||||
exc=to_native(exc),
|
||||
stdout=to_native(stdout),
|
||||
))
|
||||
return rc, result, stderr
|
||||
|
||||
@abc.abstractmethod
|
||||
def fail(self, msg, **kwargs):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def warn(self, msg):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def deprecate(self, msg, version=None, date=None, collection_name=None):
|
||||
pass
|
||||
|
||||
def get_cli_info(self):
|
||||
if self._info is None:
|
||||
dummy, self._info, dummy = self.call_cli_json('info', '--format', '{{ json . }}', check_rc=True)
|
||||
return self._info
|
||||
|
||||
def get_client_plugin_info(self, component):
|
||||
for plugin in self.get_cli_info()['ClientInfo'].get('Plugins') or []:
|
||||
if plugin.get('Name') == component:
|
||||
return plugin
|
||||
return None
|
||||
|
||||
def _image_lookup(self, name, tag):
|
||||
'''
|
||||
Including a tag in the name parameter sent to the Docker SDK for Python images method
|
||||
does not work consistently. Instead, get the result set for name and manually check
|
||||
if the tag exists.
|
||||
'''
|
||||
dummy, images, dummy = self.call_cli_json_stream(
|
||||
'image', 'ls', '--format', '{{ json . }}', '--no-trunc', '--filter', 'reference={0}'.format(name),
|
||||
check_rc=True,
|
||||
)
|
||||
if tag:
|
||||
lookup = "%s:%s" % (name, tag)
|
||||
lookup_digest = "%s@%s" % (name, tag)
|
||||
response = images
|
||||
images = []
|
||||
for image in response:
|
||||
if image.get('Tag') == tag or image.get('Digest') == tag:
|
||||
images = [image]
|
||||
break
|
||||
return images
|
||||
|
||||
def find_image(self, name, tag):
|
||||
'''
|
||||
Lookup an image (by name and tag) and return the inspection results.
|
||||
'''
|
||||
if not name:
|
||||
return None
|
||||
|
||||
self.log("Find image %s:%s" % (name, tag))
|
||||
images = self._image_lookup(name, tag)
|
||||
if not images:
|
||||
# In API <= 1.20 seeing 'docker.io/<name>' as the name of images pulled from docker hub
|
||||
registry, repo_name = resolve_repository_name(name)
|
||||
if registry == 'docker.io':
|
||||
# If docker.io is explicitly there in name, the image
|
||||
# isn't found in some cases (#41509)
|
||||
self.log("Check for docker.io image: %s" % repo_name)
|
||||
images = self._image_lookup(repo_name, tag)
|
||||
if not images and repo_name.startswith('library/'):
|
||||
# Sometimes library/xxx images are not found
|
||||
lookup = repo_name[len('library/'):]
|
||||
self.log("Check for docker.io image: %s" % lookup)
|
||||
images = self._image_lookup(lookup, tag)
|
||||
if not images:
|
||||
# Last case for some Docker versions: if docker.io wasn't there,
|
||||
# it can be that the image wasn't found either
|
||||
# (https://github.com/ansible/ansible/pull/15586)
|
||||
lookup = "%s/%s" % (registry, repo_name)
|
||||
self.log("Check for docker.io image: %s" % lookup)
|
||||
images = self._image_lookup(lookup, tag)
|
||||
if not images and '/' not in repo_name:
|
||||
# This seems to be happening with podman-docker
|
||||
# (https://github.com/ansible-collections/community.docker/issues/291)
|
||||
lookup = "%s/library/%s" % (registry, repo_name)
|
||||
self.log("Check for docker.io image: %s" % lookup)
|
||||
images = self._image_lookup(lookup, tag)
|
||||
|
||||
if len(images) > 1:
|
||||
self.fail("Daemon returned more than one result for %s:%s" % (name, tag))
|
||||
|
||||
if len(images) == 1:
|
||||
rc, image, stderr = self.call_cli_json('image', 'inspect', images[0]['ID'])
|
||||
if not image:
|
||||
self.log("Image %s:%s not found." % (name, tag))
|
||||
return None
|
||||
if rc != 0:
|
||||
self.fail("Error inspecting image %s:%s - %s" % (name, tag, to_native(stderr)))
|
||||
return image[0]
|
||||
|
||||
self.log("Image %s:%s not found." % (name, tag))
|
||||
return None
|
||||
|
||||
def find_image_by_id(self, image_id, accept_missing_image=False):
|
||||
'''
|
||||
Lookup an image (by ID) and return the inspection results.
|
||||
'''
|
||||
if not image_id:
|
||||
return None
|
||||
|
||||
self.log("Find image %s (by ID)" % image_id)
|
||||
rc, image, stderr = self.call_cli_json('image', 'inspect', image_id)
|
||||
if not image:
|
||||
if not accept_missing_image:
|
||||
self.fail("Error inspecting image ID %s - %s" % (image_id, to_native(stderr)))
|
||||
self.log("Image %s not found." % image_id)
|
||||
return None
|
||||
if rc != 0:
|
||||
self.fail("Error inspecting image ID %s - %s" % (image_id, to_native(stderr)))
|
||||
return image[0]
|
||||
|
||||
|
||||
class AnsibleModuleDockerClient(AnsibleDockerClientBase):
|
||||
def __init__(self, argument_spec=None, supports_check_mode=False, mutually_exclusive=None,
|
||||
required_together=None, required_if=None, required_one_of=None, required_by=None,
|
||||
min_docker_api_version=None, fail_results=None):
|
||||
|
||||
# Modules can put information in here which will always be returned
|
||||
# in case client.fail() is called.
|
||||
self.fail_results = fail_results or {}
|
||||
|
||||
merged_arg_spec = dict()
|
||||
merged_arg_spec.update(DOCKER_COMMON_ARGS)
|
||||
if argument_spec:
|
||||
merged_arg_spec.update(argument_spec)
|
||||
self.arg_spec = merged_arg_spec
|
||||
|
||||
mutually_exclusive_params = []
|
||||
mutually_exclusive_params += DOCKER_MUTUALLY_EXCLUSIVE
|
||||
if mutually_exclusive:
|
||||
mutually_exclusive_params += mutually_exclusive
|
||||
|
||||
required_together_params = []
|
||||
required_together_params += DOCKER_REQUIRED_TOGETHER
|
||||
if required_together:
|
||||
required_together_params += required_together
|
||||
|
||||
self.module = AnsibleModule(
|
||||
argument_spec=merged_arg_spec,
|
||||
supports_check_mode=supports_check_mode,
|
||||
mutually_exclusive=mutually_exclusive_params,
|
||||
required_together=required_together_params,
|
||||
required_if=required_if,
|
||||
required_one_of=required_one_of,
|
||||
required_by=required_by or {},
|
||||
)
|
||||
|
||||
self.debug = self.module.params['debug']
|
||||
self.check_mode = self.module.check_mode
|
||||
self.diff = self.module._diff
|
||||
|
||||
common_args = dict((k, self.module.params[k]) for k in DOCKER_COMMON_ARGS)
|
||||
super(AnsibleModuleDockerClient, self).__init__(common_args, min_docker_api_version=min_docker_api_version)
|
||||
|
||||
# def call_cli(self, *args, check_rc=False, data=None, cwd=None, environ_update=None):
|
||||
def call_cli(self, *args, **kwargs):
|
||||
# Python 2.7 doesn't like anything than '**kwargs' after '*args', so we have to do this manually...
|
||||
check_rc = kwargs.pop('check_rc', False)
|
||||
data = kwargs.pop('data', None)
|
||||
cwd = kwargs.pop('cwd', None)
|
||||
environ_update = kwargs.pop('environ_update', None)
|
||||
if kwargs:
|
||||
raise TypeError("call_cli() got an unexpected keyword argument '%s'" % list(kwargs)[0])
|
||||
|
||||
environment = self._environment.copy()
|
||||
if environ_update:
|
||||
environment.update(environ_update)
|
||||
rc, stdout, stderr = self.module.run_command(
|
||||
self._compose_cmd(args),
|
||||
binary_data=True,
|
||||
check_rc=check_rc,
|
||||
cwd=cwd,
|
||||
data=data,
|
||||
encoding=None,
|
||||
environ_update=environment,
|
||||
expand_user_and_vars=False,
|
||||
ignore_invalid_cwd=False,
|
||||
)
|
||||
return rc, stdout, stderr
|
||||
|
||||
def fail(self, msg, **kwargs):
|
||||
self.fail_results.update(kwargs)
|
||||
self.module.fail_json(msg=msg, **sanitize_result(self.fail_results))
|
||||
|
||||
def warn(self, msg):
|
||||
self.module.warn(msg)
|
||||
|
||||
def deprecate(self, msg, version=None, date=None, collection_name=None):
|
||||
self.module.deprecate(msg, version=version, date=date, collection_name=collection_name)
|
||||
@ -20,6 +20,7 @@ description:
|
||||
|
||||
notes:
|
||||
- Building images is done using Docker daemon's API. It is not possible to use BuildKit / buildx this way.
|
||||
Use M(community.docker.docker_image_build) to build images with BuildKit.
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.docker.docker.api_documentation
|
||||
@ -251,6 +252,7 @@ author:
|
||||
- Sorin Sbarnea (@ssbarnea)
|
||||
|
||||
seealso:
|
||||
- module: community.docker.docker_image_build
|
||||
- module: community.docker.docker_image_info
|
||||
- module: community.docker.docker_image_load
|
||||
- module: community.docker.docker_image_pull
|
||||
|
||||
316
plugins/modules/docker_image_build.py
Normal file
316
plugins/modules/docker_image_build.py
Normal file
@ -0,0 +1,316 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2023, 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
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: docker_image_build
|
||||
|
||||
short_description: Build Docker images using Docker buildx
|
||||
|
||||
version_added: 3.6.0
|
||||
|
||||
description:
|
||||
- This module allos to build Docker images using Docker's buildx plugin (BuildKit).
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.docker.docker.cli_documentation
|
||||
- community.docker.attributes
|
||||
- community.docker.attributes.actiongroup_docker
|
||||
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
diff_mode:
|
||||
support: none
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- "Image name. Name format will be one of: C(name), C(repository/name), C(registry_server:port/name).
|
||||
When pushing or pulling an image the name can optionally include the tag by appending C(:tag_name)."
|
||||
- Note that image IDs (hashes) and names with digest cannot be used.
|
||||
type: str
|
||||
required: true
|
||||
tag:
|
||||
description:
|
||||
- Tag for the image name O(name) that is to be tagged.
|
||||
- If O(name)'s format is C(name:tag), then the tag value from O(name) will take precedence.
|
||||
type: str
|
||||
default: latest
|
||||
path:
|
||||
description:
|
||||
- The path for the build environment.
|
||||
type: path
|
||||
required: true
|
||||
dockerfile:
|
||||
description:
|
||||
- Provide an alternate name for the Dockerfile to use when building an image.
|
||||
- This can also include a relative path (relative to O(path)).
|
||||
type: str
|
||||
cache_from:
|
||||
description:
|
||||
- List of image names to consider as cache source.
|
||||
type: list
|
||||
elements: str
|
||||
pull:
|
||||
description:
|
||||
- When building an image downloads any updates to the FROM image in Dockerfile.
|
||||
type: bool
|
||||
default: false
|
||||
network:
|
||||
description:
|
||||
- The network to use for C(RUN) build instructions.
|
||||
type: str
|
||||
nocache:
|
||||
description:
|
||||
- Do not use cache when building an image.
|
||||
type: bool
|
||||
default: false
|
||||
etc_hosts:
|
||||
description:
|
||||
- Extra hosts to add to C(/etc/hosts) in building containers, as a mapping of hostname to IP address.
|
||||
type: dict
|
||||
args:
|
||||
description:
|
||||
- Provide a dictionary of C(key:value) build arguments that map to Dockerfile ARG directive.
|
||||
- Docker expects the value to be a string. For convenience any non-string values will be converted to strings.
|
||||
type: dict
|
||||
target:
|
||||
description:
|
||||
- When building an image specifies an intermediate build stage by
|
||||
name as a final stage for the resulting image.
|
||||
type: str
|
||||
platform:
|
||||
description:
|
||||
- Platform in the format C(os[/arch[/variant]]).
|
||||
type: str
|
||||
shm_size:
|
||||
description:
|
||||
- "Size of C(/dev/shm) in format C(<number>[<unit>]). Number is positive integer.
|
||||
Unit can be 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. If you omit the size entirely, Docker daemon uses V(64M).
|
||||
type: str
|
||||
labels:
|
||||
description:
|
||||
- Dictionary of key value pairs.
|
||||
type: dict
|
||||
rebuild:
|
||||
description:
|
||||
- Defines the behavior of the module if the image to build (as specified in O(name) and O(tag)) already exists.
|
||||
type: str
|
||||
choices:
|
||||
- never
|
||||
- always
|
||||
default: never
|
||||
|
||||
requirements:
|
||||
- "Docker CLI with Docker buildx plugin"
|
||||
|
||||
author:
|
||||
- Felix Fontein (@felixfontein)
|
||||
|
||||
seealso:
|
||||
- module: community.docker.docker_image_push
|
||||
- module: community.docker.docker_image_tag
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Build Python 3.12 image
|
||||
community.docker.docker_image_build:
|
||||
name: localhost/python/3.12:latest
|
||||
path: /home/user/images/python
|
||||
dockerfile: Dockerfile-3.12
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
image:
|
||||
description: Image inspection results for the affected image.
|
||||
returned: success
|
||||
type: dict
|
||||
sample: {}
|
||||
'''
|
||||
|
||||
import os
|
||||
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_cli import (
|
||||
AnsibleModuleDockerClient,
|
||||
DockerException,
|
||||
)
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||
DockerBaseClass,
|
||||
clean_dict_booleans_for_docker_api,
|
||||
is_image_name_id,
|
||||
is_valid_tag,
|
||||
)
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import (
|
||||
parse_repository_tag,
|
||||
)
|
||||
|
||||
|
||||
def convert_to_bytes(value, module, name, unlimited_value=None):
|
||||
if value is None:
|
||||
return value
|
||||
try:
|
||||
if unlimited_value is not None and value in ('unlimited', str(unlimited_value)):
|
||||
return unlimited_value
|
||||
return human_to_bytes(value)
|
||||
except ValueError as exc:
|
||||
module.fail_json(msg='Failed to convert %s to bytes: %s' % (name, to_native(exc)))
|
||||
|
||||
|
||||
def dict_to_list(dictionary, concat='='):
|
||||
return ['%s%s%s' % (k, concat, v) for k, v in sorted(dictionary.items())]
|
||||
|
||||
|
||||
class ImageBuilder(DockerBaseClass):
|
||||
def __init__(self, client):
|
||||
super(ImageBuilder, self).__init__()
|
||||
self.client = client
|
||||
self.check_mode = self.client.check_mode
|
||||
parameters = self.client.module.params
|
||||
|
||||
self.cache_from = parameters['cache_from']
|
||||
self.pull = parameters['pull']
|
||||
self.network = parameters['network']
|
||||
self.nocache = parameters['nocache']
|
||||
self.etc_hosts = clean_dict_booleans_for_docker_api(parameters['etc_hosts'])
|
||||
self.args = clean_dict_booleans_for_docker_api(parameters['args'])
|
||||
self.target = parameters['target']
|
||||
self.platform = parameters['platform']
|
||||
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']
|
||||
|
||||
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()))
|
||||
|
||||
self.path = parameters['path']
|
||||
if not os.path.isdir(self.path):
|
||||
self.fail('"{0}" is not an existing directory'.format(self.path))
|
||||
self.dockerfile = parameters['dockerfile']
|
||||
if self.dockerfile and not os.path.isfile(os.path.join(self.path, self.dockerfile)):
|
||||
self.fail('"{0}" is not an existing file'.format(os.path.join(self.path, self.dockerfile)))
|
||||
|
||||
self.name = parameters['name']
|
||||
self.tag = parameters['tag']
|
||||
if not is_valid_tag(self.tag, allow_empty=True):
|
||||
self.fail('"{0}" is not a valid docker tag'.format(self.tag))
|
||||
if is_image_name_id(self.name):
|
||||
self.fail('Image name must not be a digest')
|
||||
|
||||
# If name contains a tag, it takes precedence over tag parameter.
|
||||
repo, repo_tag = parse_repository_tag(self.name)
|
||||
if repo_tag:
|
||||
self.name = repo
|
||||
self.tag = repo_tag
|
||||
|
||||
if is_image_name_id(self.tag):
|
||||
self.fail('Image name must not contain a digest, but have a tag')
|
||||
|
||||
def fail(self, msg, **kwargs):
|
||||
self.client.fail(msg, **kwargs)
|
||||
|
||||
def add_list_arg(self, args, option, values):
|
||||
for value in values:
|
||||
args.extend([option, value])
|
||||
|
||||
def add_args(self, args):
|
||||
args.extend(['--tag', '%s:%s' % (self.name, self.tag)])
|
||||
if self.dockerfile:
|
||||
args.extend(['--file', os.path.join(self.path, self.dockerfile)])
|
||||
if self.cache_from:
|
||||
self.add_list_arg(args, '--cache-from', self.cache_from)
|
||||
if self.pull:
|
||||
args.append('--pull')
|
||||
if self.network:
|
||||
args.extend(['--network', self.network])
|
||||
if self.nocache:
|
||||
args.append('--no-cache')
|
||||
if self.etc_hosts:
|
||||
self.add_list_arg(args, '--add-host', dict_to_list(self.etc_hosts, ':'))
|
||||
if self.args:
|
||||
self.add_list_arg(args, '--build-arg', dict_to_list(self.args))
|
||||
if self.target:
|
||||
args.extend(['--target', self.target])
|
||||
if self.platform:
|
||||
args.extend(['--platform', self.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))
|
||||
|
||||
def build_image(self):
|
||||
image = self.client.find_image(self.name, self.tag)
|
||||
results = dict(
|
||||
changed=False,
|
||||
actions=[],
|
||||
image=image or {},
|
||||
)
|
||||
|
||||
if image:
|
||||
if self.rebuild == 'never':
|
||||
return results
|
||||
|
||||
results['changed'] = True
|
||||
if not self.check_mode:
|
||||
args = ['buildx', 'build', '--progress', 'plain']
|
||||
self.add_args(args)
|
||||
args.extend(['--', self.path])
|
||||
rc, stdout, stderr = self.client.call_cli(*args)
|
||||
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)
|
||||
results['stderr'] = to_native(stderr)
|
||||
results['image'] = self.client.find_image(self.name, self.tag) or {}
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
name=dict(type='str', required=True),
|
||||
tag=dict(type='str', default='latest'),
|
||||
path=dict(type='path', required=True),
|
||||
dockerfile=dict(type='str'),
|
||||
cache_from=dict(type='list', elements='str'),
|
||||
pull=dict(type='bool', default=False),
|
||||
network=dict(type='str'),
|
||||
nocache=dict(type='bool', default=False),
|
||||
etc_hosts=dict(type='dict'),
|
||||
args=dict(type='dict'),
|
||||
target=dict(type='str'),
|
||||
platform=dict(type='str'),
|
||||
shm_size=dict(type='str'),
|
||||
labels=dict(type='dict'),
|
||||
rebuild=dict(type='str', choices=['never', 'always'], default='never'),
|
||||
)
|
||||
|
||||
client = AnsibleModuleDockerClient(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
try:
|
||||
results = ImageBuilder(client).build_image()
|
||||
client.module.exit_json(**results)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected Docker error occurred: {0}'.format(to_native(e)), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
6
tests/integration/targets/docker_image_build/aliases
Normal file
6
tests/integration/targets/docker_image_build/aliases
Normal file
@ -0,0 +1,6 @@
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
azp/4
|
||||
destructive
|
||||
10
tests/integration/targets/docker_image_build/meta/main.yml
Normal file
10
tests/integration/targets/docker_image_build/meta/main.yml
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
dependencies:
|
||||
- setup_docker_cli_buildx
|
||||
# The Python dependencies are needed for the other modules
|
||||
- setup_docker_python_deps
|
||||
- setup_remote_tmp_dir
|
||||
13
tests/integration/targets/docker_image_build/tasks/main.yml
Normal file
13
tests/integration/targets/docker_image_build/tasks/main.yml
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
####################################################################
|
||||
# WARNING: These are designed specifically for Ansible tests #
|
||||
# and should not be used as examples of how to write Ansible roles #
|
||||
####################################################################
|
||||
|
||||
- when: ansible_facts.distribution ~ ansible_facts.distribution_major_version not in ['CentOS6', 'RedHat6']
|
||||
include_tasks:
|
||||
file: test.yml
|
||||
@ -0,0 +1,7 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
- name: "Loading tasks from {{ item }}"
|
||||
include_tasks: "{{ item }}"
|
||||
57
tests/integration/targets/docker_image_build/tasks/test.yml
Normal file
57
tests/integration/targets/docker_image_build/tasks/test.yml
Normal file
@ -0,0 +1,57 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
- name: Create random name prefix
|
||||
set_fact:
|
||||
name_prefix: "{{ 'ansible-docker-test-%0x' % ((2**32) | random) }}"
|
||||
- name: Create image and container list
|
||||
set_fact:
|
||||
inames: []
|
||||
cnames: []
|
||||
|
||||
- debug:
|
||||
msg: "Using name prefix {{ name_prefix }}"
|
||||
|
||||
- name: Create files directory
|
||||
file:
|
||||
path: '{{ remote_tmp_dir }}/files'
|
||||
state: directory
|
||||
|
||||
- name: Template files
|
||||
template:
|
||||
src: '{{ item }}'
|
||||
dest: '{{ remote_tmp_dir }}/files/{{ item }}'
|
||||
loop:
|
||||
- ArgsDockerfile
|
||||
- Dockerfile
|
||||
- EtcHostsDockerfile
|
||||
- MyDockerfile
|
||||
- StagedDockerfile
|
||||
|
||||
- debug:
|
||||
msg: "Has buildx plugin: {{ docker_has_buildx }}"
|
||||
|
||||
- block:
|
||||
- include_tasks: run-test.yml
|
||||
with_fileglob:
|
||||
- "tests/*.yml"
|
||||
|
||||
always:
|
||||
- name: "Make sure all images are removed"
|
||||
docker_image:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
with_items: "{{ inames }}"
|
||||
- name: "Make sure all containers are removed"
|
||||
docker_container:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
force_kill: true
|
||||
with_items: "{{ cnames }}"
|
||||
|
||||
when: docker_api_version is version('1.25', '>=') and docker_cli_version is version('19.03', '>=') and docker_has_buildx
|
||||
|
||||
- fail: msg="Too old docker / docker-py version to run docker_image tests!"
|
||||
when: not(docker_api_version is version('1.25', '>=') and docker_cli_version is version('19.03', '>=')) and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6) and docker_has_buildx
|
||||
@ -0,0 +1,204 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
- name: Registering image name
|
||||
set_fact:
|
||||
iname: "{{ name_prefix ~ '-options' }}"
|
||||
|
||||
- name: Registering image name
|
||||
set_fact:
|
||||
inames: "{{ inames + [iname] }}"
|
||||
|
||||
####################################################################
|
||||
## args ############################################################
|
||||
####################################################################
|
||||
|
||||
- name: cleanup
|
||||
docker_image_remove:
|
||||
name: "{{ iname }}"
|
||||
|
||||
- name: buildargs
|
||||
docker_image_build:
|
||||
name: "{{ iname }}"
|
||||
path: "{{ remote_tmp_dir }}/files"
|
||||
dockerfile: "ArgsDockerfile"
|
||||
args:
|
||||
IMAGE: "{{ docker_test_image_busybox }}"
|
||||
TEST1: val1
|
||||
TEST2: val2
|
||||
TEST3: "True"
|
||||
pull: false
|
||||
register: buildargs_1
|
||||
|
||||
- name: buildargs (idempotency)
|
||||
docker_image_build:
|
||||
name: "{{ iname }}"
|
||||
path: "{{ remote_tmp_dir }}/files"
|
||||
dockerfile: "ArgsDockerfile"
|
||||
args:
|
||||
IMAGE: "{{ docker_test_image_busybox }}"
|
||||
TEST1: val1
|
||||
TEST2: val2
|
||||
TEST3: "True"
|
||||
pull: false
|
||||
register: buildargs_2
|
||||
|
||||
- name: cleanup
|
||||
docker_image_remove:
|
||||
name: "{{ iname }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- buildargs_1 is changed
|
||||
- buildargs_2 is not changed
|
||||
|
||||
####################################################################
|
||||
## dockerfile ######################################################
|
||||
####################################################################
|
||||
|
||||
- name: dockerfile
|
||||
docker_image_build:
|
||||
name: "{{ iname }}"
|
||||
path: "{{ remote_tmp_dir }}/files"
|
||||
dockerfile: "MyDockerfile"
|
||||
pull: false
|
||||
register: dockerfile_1
|
||||
|
||||
- name: cleanup
|
||||
docker_image_remove:
|
||||
name: "{{ iname }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- dockerfile_1 is changed
|
||||
- "('FROM ' ~ docker_test_image_alpine) in dockerfile_1.stderr"
|
||||
- dockerfile_1['image']['Config']['WorkingDir'] == '/newdata'
|
||||
|
||||
####################################################################
|
||||
## platform ########################################################
|
||||
####################################################################
|
||||
|
||||
- name: cleanup
|
||||
docker_image_remove:
|
||||
name: "{{ iname }}"
|
||||
|
||||
- name: platform
|
||||
docker_image_build:
|
||||
name: "{{ iname }}"
|
||||
path: "{{ remote_tmp_dir }}/files"
|
||||
platform: linux
|
||||
pull: false
|
||||
register: platform_1
|
||||
|
||||
- name: platform (idempotency)
|
||||
docker_image_build:
|
||||
name: "{{ iname }}"
|
||||
path: "{{ remote_tmp_dir }}/files"
|
||||
platform: linux
|
||||
pull: false
|
||||
register: platform_2
|
||||
|
||||
- name: cleanup
|
||||
docker_image_remove:
|
||||
name: "{{ iname }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- platform_1 is changed
|
||||
- platform_2 is not changed
|
||||
|
||||
####################################################################
|
||||
## target ##########################################################
|
||||
####################################################################
|
||||
|
||||
- name: Build multi-stage image
|
||||
docker_image_build:
|
||||
name: "{{ iname }}"
|
||||
path: "{{ remote_tmp_dir }}/files"
|
||||
dockerfile: "StagedDockerfile"
|
||||
target: first
|
||||
pull: false
|
||||
register: dockerfile_2
|
||||
|
||||
- name: cleanup
|
||||
docker_image_remove:
|
||||
name: "{{ iname }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- dockerfile_2 is changed
|
||||
- dockerfile_2.image.Config.WorkingDir == '/first'
|
||||
|
||||
####################################################################
|
||||
## etc_hosts #######################################################
|
||||
####################################################################
|
||||
|
||||
- name: Build image with custom etc_hosts
|
||||
docker_image_build:
|
||||
name: "{{ iname }}"
|
||||
path: "{{ remote_tmp_dir }}/files"
|
||||
dockerfile: "EtcHostsDockerfile"
|
||||
pull: false
|
||||
etc_hosts:
|
||||
some-custom-host: "127.0.0.1"
|
||||
register: path_1
|
||||
|
||||
- name: cleanup
|
||||
docker_image_remove:
|
||||
name: "{{ iname }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- path_1 is changed
|
||||
|
||||
####################################################################
|
||||
## shm_size ########################################################
|
||||
####################################################################
|
||||
|
||||
- name: Build image with custom shm_size
|
||||
docker_image_build:
|
||||
name: "{{ iname }}"
|
||||
path: "{{ remote_tmp_dir }}/files"
|
||||
dockerfile: "MyDockerfile"
|
||||
pull: false
|
||||
shm_size: 128MB
|
||||
register: path_1
|
||||
|
||||
- name: cleanup
|
||||
docker_image_remove:
|
||||
name: "{{ iname }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- path_1 is changed
|
||||
|
||||
####################################################################
|
||||
## labels ##########################################################
|
||||
####################################################################
|
||||
|
||||
- name: Build image with labels
|
||||
docker_image_build:
|
||||
name: "{{ iname }}"
|
||||
path: "{{ remote_tmp_dir }}/files"
|
||||
dockerfile: "MyDockerfile"
|
||||
pull: false
|
||||
labels:
|
||||
FOO: BAR
|
||||
this is a label: this is the label's value
|
||||
register: labels_1
|
||||
|
||||
- name: cleanup
|
||||
docker_image_remove:
|
||||
name: "{{ iname }}"
|
||||
|
||||
- name: Show image information
|
||||
debug:
|
||||
var: labels_1.image
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- labels_1 is changed
|
||||
- labels_1.image.Config.Labels.FOO == 'BAR'
|
||||
- labels_1.image.Config.Labels["this is a label"] == "this is the label's value"
|
||||
@ -0,0 +1,13 @@
|
||||
# Copyright (c) 2022, Felix Fontein
|
||||
# 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
|
||||
|
||||
ARG IMAGE
|
||||
ARG TEST1
|
||||
ARG TEST2
|
||||
ARG TEST3
|
||||
|
||||
FROM ${IMAGE}
|
||||
ENV foo /bar
|
||||
WORKDIR ${foo}
|
||||
RUN echo "${TEST1} - ${TEST2} - ${TEST3}"
|
||||
@ -0,0 +1,7 @@
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
FROM {{ docker_test_image_busybox }}
|
||||
ENV foo /bar
|
||||
WORKDIR ${foo}
|
||||
@ -0,0 +1,7 @@
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
FROM {{ docker_test_image_busybox }}
|
||||
# This should fail building if docker cannot resolve some-custom-host
|
||||
RUN ping -c1 some-custom-host
|
||||
@ -0,0 +1,9 @@
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
FROM {{ docker_test_image_alpine }}
|
||||
ENV INSTALL_PATH /newdata
|
||||
RUN mkdir -p $INSTALL_PATH
|
||||
|
||||
WORKDIR $INSTALL_PATH
|
||||
@ -0,0 +1,11 @@
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
FROM {{ docker_test_image_busybox }} AS first
|
||||
ENV dir /first
|
||||
WORKDIR ${dir}
|
||||
|
||||
FROM {{ docker_test_image_busybox }} AS second
|
||||
ENV dir /second
|
||||
WORKDIR ${dir}
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
- name: Install docker
|
||||
apk:
|
||||
name: docker
|
||||
name:
|
||||
- docker
|
||||
update_cache: true
|
||||
notify: cleanup docker
|
||||
|
||||
@ -25,6 +25,14 @@
|
||||
set_fact:
|
||||
needs_docker_daemon: '{{ not ansible_module_running_in_container }}'
|
||||
|
||||
- name:
|
||||
debug:
|
||||
msg: |-
|
||||
OS family: {{ ansible_facts.os_family }}
|
||||
Distribution: {{ ansible_facts.distribution }}
|
||||
Distribution major version: {{ ansible_facts.distribution_major_version }}
|
||||
Distribution full version: {{ ansible_facts.distribution_version }}
|
||||
|
||||
- name: Include distribution specific variables
|
||||
include_vars: "{{ lookup('first_found', params) }}"
|
||||
vars:
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
dependencies:
|
||||
- setup_docker
|
||||
@ -0,0 +1,13 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
- name: Buildx is available from Alpine 3.14 on
|
||||
set_fact:
|
||||
docker_has_buildx: "{{ ansible_facts.distribution_version is version('3.14', '>=') }}"
|
||||
|
||||
- name: Install Docker buildx CLI plugin
|
||||
apk:
|
||||
name: docker-cli-buildx
|
||||
when: docker_has_buildx
|
||||
@ -0,0 +1,8 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
- name: Install Docker buildx CLI plugin
|
||||
community.general.pacman:
|
||||
name: docker-buildx
|
||||
@ -0,0 +1 @@
|
||||
nothing.yml
|
||||
@ -0,0 +1 @@
|
||||
nothing.yml
|
||||
@ -0,0 +1 @@
|
||||
nothing.yml
|
||||
@ -0,0 +1,8 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
- name: For some reason we don't have the buildx plugin
|
||||
set_fact:
|
||||
docker_has_buildx: false
|
||||
@ -0,0 +1 @@
|
||||
nothing.yml
|
||||
@ -0,0 +1,14 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
- name: Buildx is available in OpenSuSE 15.5, not sure which versions before
|
||||
set_fact:
|
||||
docker_has_buildx: "{{ ansible_facts.distribution_version is version('15.5', '>=') }}"
|
||||
|
||||
- name: Install Docker buildx CLI plugin
|
||||
community.general.zypper:
|
||||
name: docker-buildx
|
||||
disable_gpg_check: true
|
||||
when: docker_has_buildx
|
||||
@ -0,0 +1,49 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
####################################################################
|
||||
# WARNING: These are designed specifically for Ansible tests #
|
||||
# and should not be used as examples of how to write Ansible roles #
|
||||
####################################################################
|
||||
|
||||
- name: Setup Docker
|
||||
when: ansible_facts.distribution ~ ansible_facts.distribution_major_version not in ['CentOS6', 'RedHat6']
|
||||
block:
|
||||
- name:
|
||||
debug:
|
||||
msg: |-
|
||||
OS family: {{ ansible_facts.os_family }}
|
||||
Distribution: {{ ansible_facts.distribution }}
|
||||
Distribution major version: {{ ansible_facts.distribution_major_version }}
|
||||
Distribution full version: {{ ansible_facts.distribution_version }}
|
||||
|
||||
- name: Set facts for Docker features to defaults
|
||||
set_fact:
|
||||
docker_has_buildx: true
|
||||
|
||||
- name: Include distribution specific variables
|
||||
include_vars: "{{ lookup('first_found', params) }}"
|
||||
vars:
|
||||
params:
|
||||
files:
|
||||
- "{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml"
|
||||
- "{{ ansible_facts.os_family }}-{{ ansible_facts.distribution_major_version }}.yml"
|
||||
- "{{ ansible_facts.distribution }}.yml"
|
||||
- "{{ ansible_facts.os_family }}.yml"
|
||||
- default.yml
|
||||
paths:
|
||||
- "{{ role_path }}/vars"
|
||||
|
||||
- name: Include distribution specific tasks
|
||||
include_tasks: "{{ lookup('first_found', params) }}"
|
||||
vars:
|
||||
params:
|
||||
files:
|
||||
- "{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml"
|
||||
- "{{ ansible_facts.os_family }}-{{ ansible_facts.distribution_major_version }}.yml"
|
||||
- "{{ ansible_facts.distribution }}.yml"
|
||||
- "{{ ansible_facts.os_family }}.yml"
|
||||
paths:
|
||||
- "{{ role_path }}/tasks"
|
||||
@ -0,0 +1,7 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
|
||||
# nothing to do
|
||||
[]
|
||||
@ -0,0 +1,4 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# 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
|
||||
Loading…
Reference in New Issue
Block a user