mirror of
https://github.com/ansible-collections/community.docker.git
synced 2026-07-29 11:55:04 +00:00
Reformat code with black and isort.
This commit is contained in:
@@ -285,23 +285,21 @@ 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._api.utils.utils import (
|
||||
parse_repository_tag,
|
||||
)
|
||||
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.version import LooseVersion
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import (
|
||||
parse_repository_tag,
|
||||
from ansible_collections.community.docker.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
|
||||
@@ -309,15 +307,15 @@ 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)):
|
||||
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=f'Failed to convert {name} to bytes: {exc}')
|
||||
module.fail_json(msg=f"Failed to convert {name} to bytes: {exc}")
|
||||
|
||||
|
||||
def dict_to_list(dictionary, concat='='):
|
||||
return [f'{k}{concat}{v}' for k, v in sorted(dictionary.items())]
|
||||
def dict_to_list(dictionary, concat="="):
|
||||
return [f"{k}{concat}{v}" for k, v in sorted(dictionary.items())]
|
||||
|
||||
|
||||
def _quote_csv(input):
|
||||
@@ -334,47 +332,59 @@ class ImageBuilder(DockerBaseClass):
|
||||
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']
|
||||
self.secrets = parameters['secrets']
|
||||
self.outputs = parameters['outputs']
|
||||
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"]
|
||||
self.secrets = parameters["secrets"]
|
||||
self.outputs = parameters["outputs"]
|
||||
|
||||
buildx = self.client.get_client_plugin_info('buildx')
|
||||
buildx = self.client.get_client_plugin_info("buildx")
|
||||
if buildx is None:
|
||||
self.fail(f'Docker CLI {self.client.get_cli()} does not have the buildx plugin installed')
|
||||
buildx_version = buildx['Version'].lstrip('v')
|
||||
self.fail(
|
||||
f"Docker CLI {self.client.get_cli()} does not have the buildx plugin installed"
|
||||
)
|
||||
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(f'The Docker buildx plugin has version {buildx_version}, but 0.6.0 is needed for secrets of type=env and type=value')
|
||||
if secret["type"] in ("env", "value"):
|
||||
if LooseVersion(buildx_version) < LooseVersion("0.6.0"):
|
||||
self.fail(
|
||||
f"The Docker buildx plugin has version {buildx_version}, but 0.6.0 is needed for secrets of type=env and type=value"
|
||||
)
|
||||
if self.outputs and len(self.outputs) > 1:
|
||||
if LooseVersion(buildx_version) < LooseVersion('0.13.0'):
|
||||
self.fail(f'The Docker buildx plugin has version {buildx_version}, but 0.13.0 is needed to specify more than one output')
|
||||
if LooseVersion(buildx_version) < LooseVersion("0.13.0"):
|
||||
self.fail(
|
||||
f"The Docker buildx plugin has version {buildx_version}, but 0.13.0 is needed to specify more than one output"
|
||||
)
|
||||
|
||||
self.path = parameters['path']
|
||||
self.path = parameters["path"]
|
||||
if not os.path.isdir(self.path):
|
||||
self.fail(f'"{self.path}" is not an existing directory')
|
||||
self.dockerfile = parameters['dockerfile']
|
||||
if self.dockerfile and not os.path.isfile(os.path.join(self.path, self.dockerfile)):
|
||||
self.fail(f'"{os.path.join(self.path, self.dockerfile)}" is not an existing file')
|
||||
self.dockerfile = parameters["dockerfile"]
|
||||
if self.dockerfile and not os.path.isfile(
|
||||
os.path.join(self.path, self.dockerfile)
|
||||
):
|
||||
self.fail(
|
||||
f'"{os.path.join(self.path, self.dockerfile)}" is not an existing file'
|
||||
)
|
||||
|
||||
self.name = parameters['name']
|
||||
self.tag = parameters['tag']
|
||||
self.name = parameters["name"]
|
||||
self.tag = parameters["tag"]
|
||||
if not is_valid_tag(self.tag, allow_empty=True):
|
||||
self.fail(f'"{self.tag}" is not a valid docker tag')
|
||||
if is_image_name_id(self.name):
|
||||
self.fail('Image name must not be a digest')
|
||||
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)
|
||||
@@ -383,25 +393,27 @@ class ImageBuilder(DockerBaseClass):
|
||||
self.tag = repo_tag
|
||||
|
||||
if is_image_name_id(self.tag):
|
||||
self.fail('Image name must not contain a digest, but have a tag')
|
||||
self.fail("Image name must not contain a digest, but have a tag")
|
||||
|
||||
if self.outputs:
|
||||
found = False
|
||||
name_tag = f'{self.name}:{self.tag}'
|
||||
name_tag = f"{self.name}:{self.tag}"
|
||||
for output in self.outputs:
|
||||
if output['type'] == 'image':
|
||||
if not output['name']:
|
||||
if output["type"] == "image":
|
||||
if not output["name"]:
|
||||
# Since we no longer pass --tag if --output is provided, we need to set this manually
|
||||
output['name'] = [name_tag]
|
||||
if output['name'] and name_tag in output['name']:
|
||||
output["name"] = [name_tag]
|
||||
if output["name"] and name_tag in output["name"]:
|
||||
found = True
|
||||
if not found:
|
||||
self.outputs.append({
|
||||
'type': 'image',
|
||||
'name': [name_tag],
|
||||
'push': False,
|
||||
})
|
||||
if LooseVersion(buildx_version) < LooseVersion('0.13.0'):
|
||||
self.outputs.append(
|
||||
{
|
||||
"type": "image",
|
||||
"name": [name_tag],
|
||||
"push": False,
|
||||
}
|
||||
)
|
||||
if LooseVersion(buildx_version) < LooseVersion("0.13.0"):
|
||||
self.fail(
|
||||
f"The output does not include an image with name {name_tag}, and the Docker"
|
||||
f" buildx plugin has version {buildx_version} which only supports one output."
|
||||
@@ -417,78 +429,86 @@ class ImageBuilder(DockerBaseClass):
|
||||
def add_args(self, args):
|
||||
environ_update = {}
|
||||
if not self.outputs:
|
||||
args.extend(['--tag', f'{self.name}:{self.tag}'])
|
||||
args.extend(["--tag", f"{self.name}:{self.tag}"])
|
||||
if self.dockerfile:
|
||||
args.extend(['--file', os.path.join(self.path, 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)
|
||||
self.add_list_arg(args, "--cache-from", self.cache_from)
|
||||
if self.pull:
|
||||
args.append('--pull')
|
||||
args.append("--pull")
|
||||
if self.network:
|
||||
args.extend(['--network', self.network])
|
||||
args.extend(["--network", self.network])
|
||||
if self.nocache:
|
||||
args.append('--no-cache')
|
||||
args.append("--no-cache")
|
||||
if self.etc_hosts:
|
||||
self.add_list_arg(args, '--add-host', dict_to_list(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))
|
||||
self.add_list_arg(args, "--build-arg", dict_to_list(self.args))
|
||||
if self.target:
|
||||
args.extend(['--target', self.target])
|
||||
args.extend(["--target", self.target])
|
||||
if self.platform:
|
||||
for platform in self.platform:
|
||||
args.extend(['--platform', platform])
|
||||
args.extend(["--platform", platform])
|
||||
if self.shm_size:
|
||||
args.extend(['--shm-size', str(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))
|
||||
self.add_list_arg(args, "--label", dict_to_list(self.labels))
|
||||
if self.secrets:
|
||||
random_prefix = None
|
||||
for index, secret in enumerate(self.secrets):
|
||||
sid = secret['id']
|
||||
if secret['type'] == 'file':
|
||||
src = secret['src']
|
||||
args.extend(['--secret', f'id={sid},type=file,src={src}'])
|
||||
if secret['type'] == 'env':
|
||||
env = secret['src']
|
||||
args.extend(['--secret', f'id={sid},type=env,env={env}'])
|
||||
if secret['type'] == 'value':
|
||||
sid = secret["id"]
|
||||
if secret["type"] == "file":
|
||||
src = secret["src"]
|
||||
args.extend(["--secret", f"id={sid},type=file,src={src}"])
|
||||
if secret["type"] == "env":
|
||||
env = secret["src"]
|
||||
args.extend(["--secret", f"id={sid},type=env,env={env}"])
|
||||
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 = f'ANSIBLE_DOCKER_COMPOSE_ENV_SECRET_{random_prefix}_{index}'
|
||||
environ_update[env_name] = secret['value']
|
||||
args.extend(['--secret', f'id={sid},type=env,env={env_name}'])
|
||||
random_prefix = (
|
||||
base64.b64encode(os.urandom(16))
|
||||
.decode("utf-8")
|
||||
.replace("=", "")
|
||||
)
|
||||
env_name = (
|
||||
f"ANSIBLE_DOCKER_COMPOSE_ENV_SECRET_{random_prefix}_{index}"
|
||||
)
|
||||
environ_update[env_name] = secret["value"]
|
||||
args.extend(["--secret", f"id={sid},type=env,env={env_name}"])
|
||||
if self.outputs:
|
||||
for output in self.outputs:
|
||||
subargs = []
|
||||
if output['type'] == 'local':
|
||||
dest = output['dest']
|
||||
subargs.extend(['type=local', f'dest={dest}'])
|
||||
if output['type'] == 'tar':
|
||||
dest = output['dest']
|
||||
subargs.extend(['type=tar', f'dest={dest}'])
|
||||
if output['type'] == 'oci':
|
||||
dest = output['dest']
|
||||
subargs.extend(['type=oci', f'dest={dest}'])
|
||||
if output['type'] == 'docker':
|
||||
subargs.append('type=docker')
|
||||
dest = output['dest']
|
||||
if output['dest'] is not None:
|
||||
subargs.append(f'dest={dest}')
|
||||
if output['context'] is not None:
|
||||
context = output['context']
|
||||
subargs.append(f'context={context}')
|
||||
if output['type'] == 'image':
|
||||
subargs.append('type=image')
|
||||
if output['name'] is not None:
|
||||
name = ','.join(output['name'])
|
||||
subargs.append(f'name={name}')
|
||||
if output['push']:
|
||||
subargs.append('push=true')
|
||||
if output["type"] == "local":
|
||||
dest = output["dest"]
|
||||
subargs.extend(["type=local", f"dest={dest}"])
|
||||
if output["type"] == "tar":
|
||||
dest = output["dest"]
|
||||
subargs.extend(["type=tar", f"dest={dest}"])
|
||||
if output["type"] == "oci":
|
||||
dest = output["dest"]
|
||||
subargs.extend(["type=oci", f"dest={dest}"])
|
||||
if output["type"] == "docker":
|
||||
subargs.append("type=docker")
|
||||
dest = output["dest"]
|
||||
if output["dest"] is not None:
|
||||
subargs.append(f"dest={dest}")
|
||||
if output["context"] is not None:
|
||||
context = output["context"]
|
||||
subargs.append(f"context={context}")
|
||||
if output["type"] == "image":
|
||||
subargs.append("type=image")
|
||||
if output["name"] is not None:
|
||||
name = ",".join(output["name"])
|
||||
subargs.append(f"name={name}")
|
||||
if output["push"]:
|
||||
subargs.append("push=true")
|
||||
if subargs:
|
||||
args.extend(['--output', ','.join(_quote_csv(subarg) for subarg in subargs)])
|
||||
args.extend(
|
||||
["--output", ",".join(_quote_csv(subarg) for subarg in subargs)]
|
||||
)
|
||||
return environ_update
|
||||
|
||||
def build_image(self):
|
||||
@@ -500,82 +520,93 @@ class ImageBuilder(DockerBaseClass):
|
||||
)
|
||||
|
||||
if image:
|
||||
if self.rebuild == 'never':
|
||||
if self.rebuild == "never":
|
||||
return results
|
||||
|
||||
results['changed'] = True
|
||||
results["changed"] = True
|
||||
if not self.check_mode:
|
||||
args = ['buildx', 'build', '--progress', 'plain']
|
||||
args = ["buildx", "build", "--progress", "plain"]
|
||||
environ_update = self.add_args(args)
|
||||
args.extend(['--', self.path])
|
||||
rc, stdout, stderr = self.client.call_cli(*args, environ_update=environ_update)
|
||||
args.extend(["--", self.path])
|
||||
rc, stdout, stderr = self.client.call_cli(
|
||||
*args, environ_update=environ_update
|
||||
)
|
||||
if rc != 0:
|
||||
self.fail(f'Building {self.name}:{self.tag} failed', stdout=to_native(stdout), stderr=to_native(stderr), command=args)
|
||||
results['stdout'] = to_native(stdout)
|
||||
results['stderr'] = to_native(stderr)
|
||||
results['image'] = self.client.find_image(self.name, self.tag) or {}
|
||||
results['command'] = args
|
||||
self.fail(
|
||||
f"Building {self.name}:{self.tag} failed",
|
||||
stdout=to_native(stdout),
|
||||
stderr=to_native(stderr),
|
||||
command=args,
|
||||
)
|
||||
results["stdout"] = to_native(stdout)
|
||||
results["stderr"] = to_native(stderr)
|
||||
results["image"] = self.client.find_image(self.name, self.tag) or {}
|
||||
results["command"] = args
|
||||
|
||||
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='list', elements='str'),
|
||||
shm_size=dict(type='str'),
|
||||
labels=dict(type='dict'),
|
||||
rebuild=dict(type='str', choices=['never', 'always'], default='never'),
|
||||
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="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',
|
||||
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),
|
||||
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']),
|
||||
("type", "file", ["src"]),
|
||||
("type", "env", ["env"]),
|
||||
("type", "value", ["value"]),
|
||||
],
|
||||
mutually_exclusive=[
|
||||
('src', 'env', 'value'),
|
||||
("src", "env", "value"),
|
||||
],
|
||||
no_log=False,
|
||||
),
|
||||
outputs=dict(
|
||||
type='list',
|
||||
elements='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='list', elements='str'),
|
||||
push=dict(type='bool', default=False),
|
||||
type=dict(
|
||||
type="str",
|
||||
choices=["local", "tar", "oci", "docker", "image"],
|
||||
required=True,
|
||||
),
|
||||
dest=dict(type="path"),
|
||||
context=dict(type="str"),
|
||||
name=dict(type="list", elements="str"),
|
||||
push=dict(type="bool", default=False),
|
||||
),
|
||||
required_if=[
|
||||
('type', 'local', ['dest']),
|
||||
('type', 'tar', ['dest']),
|
||||
('type', 'oci', ['dest']),
|
||||
("type", "local", ["dest"]),
|
||||
("type", "tar", ["dest"]),
|
||||
("type", "oci", ["dest"]),
|
||||
],
|
||||
mutually_exclusive=[
|
||||
('dest', 'name'),
|
||||
('dest', 'push'),
|
||||
('context', 'name'),
|
||||
('context', 'push'),
|
||||
("dest", "name"),
|
||||
("dest", "push"),
|
||||
("context", "name"),
|
||||
("context", "push"),
|
||||
],
|
||||
),
|
||||
)
|
||||
@@ -590,8 +621,11 @@ def main():
|
||||
results = ImageBuilder(client).build_image()
|
||||
client.module.exit_json(**results)
|
||||
except DockerException as e:
|
||||
client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc())
|
||||
client.fail(
|
||||
f"An unexpected Docker error occurred: {e}",
|
||||
exception=traceback.format_exc(),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user