diff --git a/changelogs/fragments/510-current_container_facts.yml b/changelogs/fragments/510-current_container_facts.yml new file mode 100644 index 00000000..628e2bb1 --- /dev/null +++ b/changelogs/fragments/510-current_container_facts.yml @@ -0,0 +1,2 @@ +minor_changes: + - "current_container_facts - make work with current Docker version (https://github.com/ansible-collections/community.docker/pull/510, https://github.com/ansible-collections/community.docker/pull/512)." diff --git a/plugins/modules/current_container_facts.py b/plugins/modules/current_container_facts.py index fd9cf754..f3c403d3 100644 --- a/plugins/modules/current_container_facts.py +++ b/plugins/modules/current_container_facts.py @@ -8,13 +8,16 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = r''' --- module: current_container_facts short_description: Return facts about whether the module runs in a Docker container version_added: 1.1.0 description: - Return facts about whether the module runs in a Docker container. + - This module attempts a best-effort detection. There might be special cases where + it does not work; if you encounter one, L(please file an issue, + https://github.com/ansible-collections/community.docker/issues/new?assignees=&labels=&template=bug_report.md). author: - Felix Fontein (@felixfontein) ''' @@ -29,7 +32,7 @@ EXAMPLES = ''' when: ansible_module_running_in_container ''' -RETURN = ''' +RETURN = r''' ansible_facts: description: Ansible facts returned by the module type: dict @@ -49,8 +52,7 @@ ansible_facts: ansible_module_container_type: description: - The detected container environment. - - Contains an empty string if no container was detected. - - Otherwise, will be one of C(docker), C(azure_pipelines), or C(github_actions). + - Contains an empty string if no container was detected, or a non-empty string identifying the container environment. - C(github_actions) is supported since community.docker 2.4.0. returned: always type: str @@ -62,6 +64,7 @@ ansible_facts: ''' import os +import re from ansible.module_utils.basic import AnsibleModule @@ -69,17 +72,23 @@ from ansible.module_utils.basic import AnsibleModule def main(): module = AnsibleModule(dict(), supports_check_mode=True) - path = '/proc/self/cpuset' + cpuset_path = '/proc/self/cpuset' + mountinfo_path = '/proc/self/mountinfo' + container_id = '' container_type = '' - if os.path.exists(path): + contents = None + if os.path.exists(cpuset_path): # File content varies based on the environment: # No Container: / # Docker: /docker/c86f3732b5ba3d28bb83b6e14af767ab96abbc52de31313dcb1176a62d91a507 # Azure Pipelines (Docker): /azpl_job/0f2edfed602dd6ec9f2e42c867f4d5ee640ebf4c058e6d3196d4393bb8fd0891 # Podman: /../../../../../.. - with open(path, 'rb') as f: + # While this was true and worked well for a long time, this seems to be no longer accurate + # with newer Docker / Podman versions and/or with cgroupv2. That's why the /proc/self/mountinfo + # detection further down is done when this test is inconclusive. + with open(cpuset_path, 'rb') as f: contents = f.read().decode('utf-8') cgroup_path, cgroup_name = os.path.split(contents.strip()) @@ -96,6 +105,18 @@ def main(): container_id = cgroup_name container_type = 'github_actions' + if not container_id and os.path.exists(mountinfo_path): + with open(mountinfo_path, 'rb') as f: + contents = f.read().decode('utf-8') + + for line in contents.splitlines(): + parts = line.split() + if len(parts) >= 5 and parts[4] == '/etc/hostname': + m = re.match('.*/docker/containers/([a-f0-9]+)/hostname', parts[3]) + if m: + container_id = m.group(1) + container_type = 'docker' + module.exit_json(ansible_facts=dict( ansible_module_running_in_container=container_id != '', ansible_module_container_id=container_id, diff --git a/tests/integration/targets/current_container_facts/aliases b/tests/integration/targets/current_container_facts/aliases new file mode 100644 index 00000000..8577a295 --- /dev/null +++ b/tests/integration/targets/current_container_facts/aliases @@ -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 +skip/rhel diff --git a/tests/integration/targets/current_container_facts/tasks/main.yml b/tests/integration/targets/current_container_facts/tasks/main.yml new file mode 100644 index 00000000..a0d1ae79 --- /dev/null +++ b/tests/integration/targets/current_container_facts/tasks/main.yml @@ -0,0 +1,41 @@ +--- +# 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: Get facts + current_container_facts: + register: result + + # WARNING: This is not a proper test as it won't fail when the module does not work! + # To make this a proper test, we need to know the environment in which this + # test runs, which we do not know in general... + +- name: Print facts + ansible.builtin.debug: + var: result.ansible_facts + +- name: Read files + ansible.builtin.slurp: + src: '{{ item }}' + loop: + - /proc/self/cgroup + - /proc/self/cpuset + - /proc/self/mountinfo + register: slurp + ignore_errors: true + +- name: Print files + ansible.builtin.debug: + msg: |- + {{ item.content | ansible.builtin.b64decode | split(' + ') }} + loop: '{{ slurp.results }}' + loop_control: + label: '{{ item.source | default(item.item) }}' + when: item is not failed