From fe2d52ff29ef734d7f40c539d418ba59e815c9d5 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sun, 13 Dec 2020 09:06:16 +0100 Subject: [PATCH] Fix AZP tests, add current_container_facts module (#48) * Add new facts module for determining whether the module is running inside a container or not. * Add containers to main network. * Fix running tests locally with newer docker. * Simplify setup_openssl to only install cryptography. * Add alias in network. * Make sure to upgrade cryptography to something more sensible on Ubuntu 16.04. * Don't jump through hoops for bridge. * Try to use other IPv4 nets. * Improve module docs. --- plugins/modules/current_container_facts.py | 100 ++++++++++++++++++ .../docker_container/tasks/tests/network.yml | 4 +- .../docker_swarm/tasks/tests/options-ca.yml | 2 +- .../targets/setup_docker/tasks/RedHat-7.yml | 2 +- .../targets/setup_docker/tasks/RedHat-8.yml | 2 +- .../targets/setup_docker/tasks/main.yml | 13 +++ .../tasks/setup-frontend.yml | 22 +++- .../setup_docker_registry/tasks/setup.yml | 9 ++ .../targets/setup_openssl/tasks/main.yml | 39 +++---- .../targets/setup_openssl/vars/Debian.yml | 6 +- .../targets/setup_openssl/vars/FreeBSD.yml | 6 +- .../targets/setup_openssl/vars/RedHat.yml | 6 +- .../targets/setup_openssl/vars/Suse.yml | 6 +- 13 files changed, 168 insertions(+), 49 deletions(-) create mode 100644 plugins/modules/current_container_facts.py diff --git a/plugins/modules/current_container_facts.py b/plugins/modules/current_container_facts.py new file mode 100644 index 00000000..926c6ee4 --- /dev/null +++ b/plugins/modules/current_container_facts.py @@ -0,0 +1,100 @@ +#!/usr/bin/python +# +# (c) 2020 Matt Clay +# (c) 2020 Felix Fontein +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +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. +author: + - Felix Fontein (@felixfontein) +''' + +EXAMPLES = ''' +- name: Get facts on current container + community.docker.current_container_facts: + +- name: Print information on current container when running in a container + ansible.builtin.debug: + msg: "Container ID is {{ ansible_module_container_id }}" + when: ansible_module_running_in_container +''' + +RETURN = ''' +ansible_facts: + description: Ansible facts returned by the module + type: dict + returned: always + contains: + ansible_module_running_in_container: + description: + - Whether the module was able to detect that it runs in a container or not. + returned: always + type: bool + ansible_module_container_id: + description: + - The detected container ID. + - Contains an empty string if no container was detected. + returned: always + type: str + 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) or C(azure_pipelines). + returned: always + type: str + # choices: + # - docker + # - azure_pipelines +''' + +import os + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule(dict(), supports_check_mode=True) + + path = '/proc/self/cpuset' + container_id = '' + container_type = '' + + if os.path.exists(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: + contents = f.read().decode('utf-8') + + cgroup_path, cgroup_name = os.path.split(contents.strip()) + + if cgroup_path == '/docker': + container_id = cgroup_name + container_type = 'docker' + + if cgroup_path == '/azpl_job': + container_id = cgroup_name + container_type = 'azure_pipelines' + + module.exit_json(ansible_facts=dict( + ansible_module_running_in_container=container_id != '', + ansible_module_container_id=container_id, + ansible_module_container_type=container_type, + )) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/docker_container/tasks/tests/network.yml b/tests/integration/targets/docker_container/tasks/tests/network.yml index 0ca341d9..9ef33643 100644 --- a/tests/integration/targets/docker_container/tasks/tests/network.yml +++ b/tests/integration/targets/docker_container/tasks/tests/network.yml @@ -23,10 +23,10 @@ when: docker_py_version is version('1.10.0', '>=') - set_fact: - subnet_ipv4_base: 192.168.{{ 64 + (192 | random) }} + subnet_ipv4_base: 10.{{ 16 + (240 | random) }}.{{ 16 + (240 | random) }} subnet_ipv6_base: fdb6:feea:{{ '%0.4x:%0.4x' | format(65536 | random, 65536 | random) }} # If netaddr would be installed on the controller, one could do: - # subnet_ipv4: "192.168.{{ 64 + (192 | random) }}.0/24" + # subnet_ipv4: "10.{{ 16 + (240 | random) }}.{{ 16 + (240 | random) }}.0/24" # subnet_ipv6: "fdb6:feea:{{ '%0.4x:%0.4x' | format(65536 | random, 65536 | random) }}::/64" - set_fact: diff --git a/tests/integration/targets/docker_swarm/tasks/tests/options-ca.yml b/tests/integration/targets/docker_swarm/tasks/tests/options-ca.yml index 6f9f1090..6221e3e7 100644 --- a/tests/integration/targets/docker_swarm/tasks/tests/options-ca.yml +++ b/tests/integration/targets/docker_swarm/tasks/tests/options-ca.yml @@ -1,7 +1,7 @@ - debug: msg: Running tests/options-ca.yml - name: options-ca - when: pyopenssl_version.stdout is version('0.15', '>=') or cryptography_version.stdout is version('1.6', '>=') + when: cryptography_version.stdout is version('1.6', '>=') block: - name: Generate privatekey loop: diff --git a/tests/integration/targets/setup_docker/tasks/RedHat-7.yml b/tests/integration/targets/setup_docker/tasks/RedHat-7.yml index 4fa28b74..6e798ce2 100644 --- a/tests/integration/targets/setup_docker/tasks/RedHat-7.yml +++ b/tests/integration/targets/setup_docker/tasks/RedHat-7.yml @@ -42,4 +42,4 @@ service: name: docker state: started - ignore_errors: "{{ ansible_virtualization_type == 'docker' }}" + ignore_errors: "{{ ansible_virtualization_type in ['docker', 'container', 'containerd'] }}" diff --git a/tests/integration/targets/setup_docker/tasks/RedHat-8.yml b/tests/integration/targets/setup_docker/tasks/RedHat-8.yml index 5e833483..c207f4ab 100644 --- a/tests/integration/targets/setup_docker/tasks/RedHat-8.yml +++ b/tests/integration/targets/setup_docker/tasks/RedHat-8.yml @@ -31,4 +31,4 @@ service: name: docker state: started - ignore_errors: "{{ ansible_virtualization_type == 'docker' }}" + ignore_errors: "{{ ansible_virtualization_type in ['docker', 'container', 'containerd'] }}" diff --git a/tests/integration/targets/setup_docker/tasks/main.yml b/tests/integration/targets/setup_docker/tasks/main.yml index a9c8a7c5..e1b25dc8 100644 --- a/tests/integration/targets/setup_docker/tasks/main.yml +++ b/tests/integration/targets/setup_docker/tasks/main.yml @@ -135,3 +135,16 @@ images: "{{ docker_images.stdout_lines | default([]) }}" when: docker_cli_version is version('0.0', '>') + + - name: Detect whether we are running inside a container + current_container_facts: + + - name: Inspect current container + docker_container_info: + name: "{{ ansible_module_container_id }}" + register: current_container_info + when: ansible_module_running_in_container + + - name: Determine network name + set_fact: + current_container_network_ip: "{{ (current_container_info.container.NetworkSettings.Networks | dictsort)[0].0 | default('') if ansible_module_running_in_container else '' }}" diff --git a/tests/integration/targets/setup_docker_registry/tasks/setup-frontend.yml b/tests/integration/targets/setup_docker_registry/tasks/setup-frontend.yml index b52a13f8..f1055ea3 100644 --- a/tests/integration/targets/setup_docker_registry/tasks/setup-frontend.yml +++ b/tests/integration/targets/setup_docker_registry/tasks/setup-frontend.yml @@ -11,10 +11,18 @@ name: '{{ docker_registry_container_name_frontend }}' image: "{{ docker_test_image_registry_nginx }}" ports: 5000 + # `links` does not work when using a network. That's why the docker_container task + # in setup.yml specifies `aliases` so we get the same effect. links: - '{{ docker_registry_container_name_registry }}:real-registry' volumes: - '{{ docker_registry_container_name_frontend }}:/etc/nginx/' + network_mode: '{{ current_container_network_ip | default(omit, true) }}' + networks: >- + {{ + [dict([['name', current_container_network_ip]])] + if current_container_network_ip not in ['', 'bridge'] else omit + }} register: nginx_container - name: Copy static files into volume @@ -65,9 +73,17 @@ debug: var: nginx_container.container.NetworkSettings + - name: Get registry URL + set_fact: + # Note that this host/port combination is used by the Docker daemon, that's why `localhost` is appropriate! + # This host/port combination cannot be used if the tests are running inside a docker container. + docker_registry_frontend_address: localhost:{{ nginx_container.container.NetworkSettings.Ports['5000/tcp'].0.HostPort }} + # The following host/port combination can be used from inside the docker container. + docker_registry_frontend_address_internal: "{{ nginx_container.container.NetworkSettings.Networks[current_container_network_ip].IPAddress if current_container_network_ip else nginx_container.container.NetworkSettings.IPAddress }}:5000" + - name: Wait for registry frontend uri: - url: https://{{ nginx_container.container.NetworkSettings.IPAddress }}:5000/v2/ + url: https://{{ docker_registry_frontend_address_internal }}/v2/ url_username: testuser url_password: hunter2 validate_certs: false @@ -76,10 +92,6 @@ retries: 5 delay: 1 - - name: Get registry URL - set_fact: - docker_registry_frontend_address: localhost:{{ nginx_container.container.NetworkSettings.Ports['5000/tcp'].0.HostPort }} - - set_fact: docker_registry_frontend_address: 'n/a' when: can_copy_files is failed diff --git a/tests/integration/targets/setup_docker_registry/tasks/setup.yml b/tests/integration/targets/setup_docker_registry/tasks/setup.yml index 9d2b52b9..6782da9d 100644 --- a/tests/integration/targets/setup_docker_registry/tasks/setup.yml +++ b/tests/integration/targets/setup_docker_registry/tasks/setup.yml @@ -44,6 +44,15 @@ name: '{{ docker_registry_container_name_registry }}' image: "{{ docker_test_image_registry }}" ports: 5000 + network_mode: '{{ current_container_network_ip | default(omit, true) }}' + # We need to define the alias `real-registry` here because the global `links` + # option for the NGINX containers (see setup-frontend.yml) does not work when + # using networks. + networks: >- + {{ + [dict([['name', current_container_network_ip], ['aliases', ['real-registry']]])] + if current_container_network_ip not in ['', 'bridge'] else omit + }} register: registry_container - name: Get registry URL diff --git a/tests/integration/targets/setup_openssl/tasks/main.yml b/tests/integration/targets/setup_openssl/tasks/main.yml index 62df7dd5..33030ab7 100644 --- a/tests/integration/targets/setup_openssl/tasks/main.yml +++ b/tests/integration/targets/setup_openssl/tasks/main.yml @@ -8,39 +8,24 @@ include_vars: '{{ ansible_os_family }}.yml' when: not ansible_os_family == "Darwin" -- name: Install OpenSSL - become: True +- name: Install cryptography (Python 3) + become: true package: - name: '{{ openssl_package_name }}' - when: not ansible_os_family == 'Darwin' + name: '{{ cryptography_package_name_python3 }}' + when: ansible_os_family != 'Darwin' and ansible_python_version is version('3.0', '>=') -- name: Install pyOpenSSL (Python 3) - become: True +- name: Install cryptography (Python 2) + become: true package: - name: '{{ pyopenssl_package_name_python3 }}' - when: not ansible_os_family == 'Darwin' and ansible_python_version is version('3.0', '>=') + name: '{{ cryptography_package_name }}' + when: ansible_os_family != 'Darwin' and ansible_python_version is version('3.0', '<') -- name: Install pyOpenSSL (Python 2) - become: True - package: - name: '{{ pyopenssl_package_name }}' - when: not ansible_os_family == 'Darwin' and ansible_python_version is version('3.0', '<') - -- name: Install pyOpenSSL (Darwin) - become: True +- name: Install cryptography (Darwin, and potentially upgrade for other OSes) + become: true pip: - name: pyOpenSSL + name: cryptography>=1.3.0 extra_args: "-c {{ remote_constraints }}" - when: ansible_os_family == 'Darwin' -- name: register pyOpenSSL version - command: "{{ ansible_python.executable }} -c 'import OpenSSL; print(OpenSSL.__version__)'" - register: pyopenssl_version - -- name: register openssl version - shell: "openssl version | cut -d' ' -f2" - register: openssl_version - -- name: register cryptography version +- name: Register cryptography version command: "{{ ansible_python.executable }} -c 'import cryptography; print(cryptography.__version__)'" register: cryptography_version diff --git a/tests/integration/targets/setup_openssl/vars/Debian.yml b/tests/integration/targets/setup_openssl/vars/Debian.yml index 755c7a08..5ddc85e4 100644 --- a/tests/integration/targets/setup_openssl/vars/Debian.yml +++ b/tests/integration/targets/setup_openssl/vars/Debian.yml @@ -1,3 +1,3 @@ -pyopenssl_package_name: python-openssl -pyopenssl_package_name_python3: python3-openssl -openssl_package_name: openssl +--- +cryptography_package_name: python-cryptography +cryptography_package_name_python3: python3-cryptography diff --git a/tests/integration/targets/setup_openssl/vars/FreeBSD.yml b/tests/integration/targets/setup_openssl/vars/FreeBSD.yml index 60868915..cd7f36a7 100644 --- a/tests/integration/targets/setup_openssl/vars/FreeBSD.yml +++ b/tests/integration/targets/setup_openssl/vars/FreeBSD.yml @@ -1,3 +1,3 @@ -pyopenssl_package_name: py27-openssl -pyopenssl_package_name_python3: py36-openssl -openssl_package_name: openssl +--- +cryptography_package_name: py27-cryptography +cryptography_package_name_python3: py36-cryptography diff --git a/tests/integration/targets/setup_openssl/vars/RedHat.yml b/tests/integration/targets/setup_openssl/vars/RedHat.yml index 2959932c..5ddc85e4 100644 --- a/tests/integration/targets/setup_openssl/vars/RedHat.yml +++ b/tests/integration/targets/setup_openssl/vars/RedHat.yml @@ -1,3 +1,3 @@ -pyopenssl_package_name: pyOpenSSL -pyopenssl_package_name_python3: python3-pyOpenSSL -openssl_package_name: openssl +--- +cryptography_package_name: python-cryptography +cryptography_package_name_python3: python3-cryptography diff --git a/tests/integration/targets/setup_openssl/vars/Suse.yml b/tests/integration/targets/setup_openssl/vars/Suse.yml index 2d5200f3..5ddc85e4 100644 --- a/tests/integration/targets/setup_openssl/vars/Suse.yml +++ b/tests/integration/targets/setup_openssl/vars/Suse.yml @@ -1,3 +1,3 @@ -pyopenssl_package_name: python-pyOpenSSL -pyopenssl_package_name_python3: python3-pyOpenSSL -openssl_package_name: openssl +--- +cryptography_package_name: python-cryptography +cryptography_package_name_python3: python3-cryptography