From a3b0cd67c19993bbcff5490df547d10b0e27e15d Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 5 Nov 2024 20:54:08 +0100 Subject: [PATCH] Sanitize labels. --- changelogs/fragments/985-label-sanitize.yml | 9 +++++++ plugins/module_utils/module_container/base.py | 14 ++++++++++- plugins/module_utils/util.py | 24 +++++++++++++++++++ plugins/modules/docker_compose_v2_run.py | 3 +++ plugins/modules/docker_config.py | 2 ++ plugins/modules/docker_network.py | 2 ++ plugins/modules/docker_node.py | 3 +++ plugins/modules/docker_secret.py | 2 ++ plugins/modules/docker_swarm.py | 2 ++ plugins/modules/docker_swarm_service.py | 11 +++++---- plugins/modules/docker_volume.py | 2 ++ 11 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 changelogs/fragments/985-label-sanitize.yml diff --git a/changelogs/fragments/985-label-sanitize.yml b/changelogs/fragments/985-label-sanitize.yml new file mode 100644 index 000000000..e9b8601e3 --- /dev/null +++ b/changelogs/fragments/985-label-sanitize.yml @@ -0,0 +1,9 @@ +bugfixes: + - "docker_compose_v2_run - make sure to sanitize ``labels`` before sending them to the Docker Daemon (https://github.com/ansible-collections/community.docker/pull/985)." + - "docker_config - make sure to sanitize ``labels`` before sending them to the Docker Daemon (https://github.com/ansible-collections/community.docker/pull/985)." + - "docker_network - make sure to sanitize ``labels`` before sending them to the Docker Daemon (https://github.com/ansible-collections/community.docker/pull/985)." + - "docker_node - make sure to sanitize ``labels`` before sending them to the Docker Daemon (https://github.com/ansible-collections/community.docker/pull/985)." + - "docker_secret - make sure to sanitize ``labels`` before sending them to the Docker Daemon (https://github.com/ansible-collections/community.docker/pull/985)." + - "docker_swarm - make sure to sanitize ``labels`` before sending them to the Docker Daemon (https://github.com/ansible-collections/community.docker/pull/985)." + - "docker_swarm_service - make sure to sanitize ``labels`` and ``container_labels`` before sending them to the Docker Daemon (https://github.com/ansible-collections/community.docker/pull/985)." + - "docker_volume - make sure to sanitize ``labels`` before sending them to the Docker Daemon (https://github.com/ansible-collections/community.docker/pull/985)." diff --git a/plugins/module_utils/module_container/base.py b/plugins/module_utils/module_container/base.py index 3953177f6..a09450cdc 100644 --- a/plugins/module_utils/module_container/base.py +++ b/plugins/module_utils/module_container/base.py @@ -22,6 +22,7 @@ compare_generic, normalize_healthcheck, omit_none_from_dict, + sanitize_labels, ) from ansible_collections.community.docker.plugins.module_utils._platform import ( @@ -651,6 +652,17 @@ def check_collision(t, name): return values +def _preprocess_labels(module, values): + result = {} + if 'labels' in values: + labels = values['labels'] + if labels is not None: + labels = dict(labels) + sanitize_labels(labels, 'labels', module=module) + result['labels'] = labels + return result + + def _preprocess_log(module, values): result = {} if 'log_driver' not in values: @@ -978,7 +990,7 @@ def _compare_platform(option, param_value, container_value): ) OPTION_LABELS = ( - OptionGroup() + OptionGroup(preprocess=_preprocess_labels) .add_option('labels', type='dict', needs_no_suboptions=True) ) diff --git a/plugins/module_utils/util.py b/plugins/module_utils/util.py index 1c472a78c..3b48b92ad 100644 --- a/plugins/module_utils/util.py +++ b/plugins/module_utils/util.py @@ -12,7 +12,9 @@ from ansible.module_utils.basic import env_fallback from ansible.module_utils.common.collections import is_sequence +from ansible.module_utils.six import string_types from ansible.module_utils.six.moves.urllib.parse import urlparse +from ansible.module_utils.common.text.converters import to_text DEFAULT_DOCKER_HOST = 'unix:///var/run/docker.sock' @@ -277,6 +279,28 @@ def get_legacy_docker_diffs(self): return result +def sanitize_labels(labels, labels_field, client=None, module=None): + def fail(msg): + if client is not None: + client.module.fail(msg) + if module is not None: + module.fail_json(msg=msg) + raise ValueError(msg) + + if labels is None: + return + for k, v in list(labels): + if not isinstance(k, string_types): + fail( + "The key {key!r} of {field} is not a string!".format( + field=labels_field, key=k)) + if isinstance(v, (bool, float)): + fail( + "The value {value!r} for {key!r} of {field} is not a string or something than can be safely converted to a string!".format( + field=labels_field, key=k, value=v)) + labels[k] = to_text(v) + + def clean_dict_booleans_for_docker_api(data, allow_sequences=False): ''' Go doesn't like Python booleans 'True' or 'False', while Ansible is just diff --git a/plugins/modules/docker_compose_v2_run.py b/plugins/modules/docker_compose_v2_run.py index 9dbaf789c..c27a85c8b 100644 --- a/plugins/modules/docker_compose_v2_run.py +++ b/plugins/modules/docker_compose_v2_run.py @@ -248,6 +248,8 @@ common_compose_argspec_ex, ) +from ansible_collections.community.docker.plugins.module_utils.util import sanitize_labels + class ExecManager(BaseComposeManager): def __init__(self, client): @@ -416,6 +418,7 @@ def main(): needs_api_version=False, **argspec_ex ) + sanitize_labels(client.module.params['labels'], 'labels', client) try: manager = ExecManager(client) diff --git a/plugins/modules/docker_config.py b/plugins/modules/docker_config.py index 86654e78b..50a1f92fe 100644 --- a/plugins/modules/docker_config.py +++ b/plugins/modules/docker_config.py @@ -212,6 +212,7 @@ from ansible_collections.community.docker.plugins.module_utils.util import ( DockerBaseClass, compare_generic, + sanitize_labels, ) from ansible.module_utils.common.text.converters import to_native, to_bytes @@ -414,6 +415,7 @@ def main(): min_docker_api_version='1.30', option_minimal_versions=option_minimal_versions, ) + sanitize_labels(client.module.params['labels'], 'labels', client) try: results = dict( diff --git a/plugins/modules/docker_network.py b/plugins/modules/docker_network.py index c5dd3b229..00b79cd2d 100644 --- a/plugins/modules/docker_network.py +++ b/plugins/modules/docker_network.py @@ -284,6 +284,7 @@ DockerBaseClass, DifferenceTracker, clean_dict_booleans_for_docker_api, + sanitize_labels, ) from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException @@ -698,6 +699,7 @@ def main(): # "The docker server >= 1.10.0" option_minimal_versions=option_minimal_versions, ) + sanitize_labels(client.module.params['labels'], 'labels', client) try: cm = DockerNetworkManager(client) diff --git a/plugins/modules/docker_node.py b/plugins/modules/docker_node.py index bfa369e98..6ba08ed62 100644 --- a/plugins/modules/docker_node.py +++ b/plugins/modules/docker_node.py @@ -153,6 +153,7 @@ from ansible.module_utils.common.text.converters import to_native from ansible_collections.community.docker.plugins.module_utils.swarm import AnsibleDockerSwarmClient +from ansible_collections.community.docker.plugins.module_utils.util import sanitize_labels class TaskParameters(DockerBaseClass): @@ -172,6 +173,8 @@ def __init__(self, client): for key, value in client.module.params.items(): setattr(self, key, value) + sanitize_labels(self.labels, "labels", client) + class SwarmNodeManager(DockerBaseClass): diff --git a/plugins/modules/docker_secret.py b/plugins/modules/docker_secret.py index cf4324541..cbc5a037a 100644 --- a/plugins/modules/docker_secret.py +++ b/plugins/modules/docker_secret.py @@ -204,6 +204,7 @@ from ansible_collections.community.docker.plugins.module_utils.util import ( DockerBaseClass, compare_generic, + sanitize_labels, ) from ansible.module_utils.common.text.converters import to_native, to_bytes @@ -384,6 +385,7 @@ def main(): mutually_exclusive=mutually_exclusive, min_docker_version='2.1.0', ) + sanitize_labels(client.module.params['labels'], 'labels', client) try: results = dict( diff --git a/plugins/modules/docker_swarm.py b/plugins/modules/docker_swarm.py index dc04c0a40..5e840f8cb 100644 --- a/plugins/modules/docker_swarm.py +++ b/plugins/modules/docker_swarm.py @@ -324,6 +324,7 @@ ) from ansible_collections.community.docker.plugins.module_utils.util import ( DifferenceTracker, + sanitize_labels, ) from ansible_collections.community.docker.plugins.module_utils.swarm import AnsibleDockerSwarmClient @@ -714,6 +715,7 @@ def main(): min_docker_version='1.10.0', option_minimal_versions=option_minimal_versions, ) + sanitize_labels(client.module.params['labels'], 'labels', client) try: results = dict( diff --git a/plugins/modules/docker_swarm_service.py b/plugins/modules/docker_swarm_service.py index 4660d1138..368f5f621 100644 --- a/plugins/modules/docker_swarm_service.py +++ b/plugins/modules/docker_swarm_service.py @@ -954,6 +954,7 @@ convert_duration_to_nanosecond, parse_healthcheck, clean_dict_booleans_for_docker_api, + sanitize_labels, ) from ansible.module_utils.basic import human_to_bytes @@ -1531,10 +1532,9 @@ def from_ansible_params( secret_ids, config_ids, network_ids, - docker_api_version, - docker_py_version, + client, ): - s = DockerService(docker_api_version, docker_py_version) + s = DockerService(client.docker_api_version, client.docker_py_version) s.image = image_digest s.args = ap['args'] s.endpoint_mode = ap['endpoint_mode'] @@ -1546,7 +1546,9 @@ def from_ansible_params( s.hosts = ap['hosts'] s.tty = ap['tty'] s.labels = ap['labels'] + sanitize_labels(s.labels, 'labels', client) s.container_labels = ap['container_labels'] + sanitize_labels(s.container_labels, 'container_labels', client) s.sysctls = ap['sysctls'] s.mode = ap['mode'] s.stop_signal = ap['stop_signal'] @@ -2488,8 +2490,7 @@ def run(self): secret_ids, config_ids, network_ids, - self.client.docker_api_version, - self.client.docker_py_version + self.client, ) except Exception as e: return self.client.fail( diff --git a/plugins/modules/docker_volume.py b/plugins/modules/docker_volume.py index f282cafd0..e0d6962e7 100644 --- a/plugins/modules/docker_volume.py +++ b/plugins/modules/docker_volume.py @@ -127,6 +127,7 @@ from ansible_collections.community.docker.plugins.module_utils.util import ( DockerBaseClass, DifferenceTracker, + sanitize_labels, ) from ansible_collections.community.docker.plugins.module_utils._api.errors import ( APIError, @@ -296,6 +297,7 @@ def main(): supports_check_mode=True, # "The docker server >= 1.9.0" ) + sanitize_labels(client.module.params['labels'], 'labels', client) try: cm = DockerVolumeManager(client)