From 112a5367c98253c0d4c7388d227388d1a4ba4803 Mon Sep 17 00:00:00 2001 From: Jonathan Rosser Date: Mon, 28 Feb 2022 17:38:18 +0000 Subject: [PATCH] Add molecule testing Depends-On: https://review.opendev.org/c/openstack/openstack-ansible/+/938571 Change-Id: I1310915186dc286e592aa0db8127b172800fc96c --- examples/playbook.yml | 10 +- meta/main.yml | 2 + molecule/default/group_vars/all.yml | 188 ++++++++++++++++++++++++++++ molecule/default/molecule.yml | 35 ++++++ molecule/default/prepare.yml | 14 +++ molecule/default/verify.yml | 84 +++++++++++++ requirements.yml | 4 + tox.ini | 22 +++- zuul.d/project.yaml | 1 + 9 files changed, 353 insertions(+), 7 deletions(-) create mode 100644 molecule/default/group_vars/all.yml create mode 100644 molecule/default/molecule.yml create mode 100644 molecule/default/prepare.yml create mode 100644 molecule/default/verify.yml create mode 100644 requirements.yml diff --git a/examples/playbook.yml b/examples/playbook.yml index b953a4f..fee15f9 100644 --- a/examples/playbook.yml +++ b/examples/playbook.yml @@ -1,6 +1,8 @@ --- + - name: Install PKI - hosts: localhost - user: root - roles: - - role: "pki" + hosts: all + tasks: + - name: "Include pki role" + ansible.builtin.include_role: + name: "{{ playbook_dir | dirname | basename }}" diff --git a/meta/main.yml b/meta/main.yml index 309bb88..9d50349 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -4,6 +4,8 @@ galaxy_info: company: City Networks, BBC license: Apache2 min_ansible_version: 2.10 + namespace: openstack + role_name: pki platforms: - name: Ubuntu versions: diff --git a/molecule/default/group_vars/all.yml b/molecule/default/group_vars/all.yml new file mode 100644 index 0000000..12827cb --- /dev/null +++ b/molecule/default/group_vars/all.yml @@ -0,0 +1,188 @@ +--- + +molecule_packages: + debian: + - ca-certificates + - python3-cryptography + - gnutls-bin + - iproute2 + redhat: + - ca-certificates + - python3-cryptography + - gnutls-utils + - iproute + +pki_setup_host: "{{ inventory_hostname }}" + +functional_ca_name_1: "ExampleCorpRoot" + +# Example self-signed certificate authority +# Using the default variable +pki_authorities: + - name: "{{ functional_ca_name_1 }}" + provider: selfsigned + basic_constraints: "CA:TRUE" + cn: "Example Corp Root CA" + email_address: "pki@example.com" + country_name: "GB" + state_or_province_name: "England" + organization_name: "Example Corporation" + organizational_unit_name: "IT Security" + key_usage: + - digitalSignature + - cRLSign + - keyCertSign + not_after: "+3650d" + - name: "ExampleCorpIntermediate" + provider: ownca + basic_constraints: "CA:TRUE,pathlen:0" + cn: "Example Corp Openstack Infrastructure Intermediate CA" + email_address: "pki@example.com" + country_name: "GB" + state_or_province_name: "England" + organization_name: "Example Corporation" + organizational_unit_name: "IT Security" + key_usage: + - digitalSignature + - cRLSign + - keyCertSign + not_after: "+3650d" + signed_by: "ExampleCorpRoot" + +# Custom CA generation search pattern +pki_search_authorities_pattern: "foo_authorities_" + +# Certificate authority to cerate from a custom variable +functional_ca_name_2: "FooAuthorityNotInstalled" +functional_ca_name_3: "FooAuthorityInstalled" + +foo_authorities_variable: + - name: "{{ functional_ca_name_2 }}" + country: "GB" + state_or_province_name: "England" + organization_name: "Example Corporation" + organizational_unit_name: "IT Security" + cn: "FooAutorityNotInstalled" + provider: selfsigned + basic_constraints: "CA:TRUE" + key_usage: + - digitalSignature + - keyCertSign + not_after: "+3650d" + condition: false + - name: "{{ functional_ca_name_3 }}" + country: "GB" + state_or_province_name: "England" + organization_name: "Example Corporation" + organizational_unit_name: "IT Security" + cn: "FooAutorityInstalled" + provider: selfsigned + basic_constraints: "CA:TRUE" + key_usage: + - digitalSignature + - keyCertSign + not_after: "+3650d" + condition: true + +# install the root CA certificate +pki_install_ca: + - name: "ExampleCorpRoot" + +# Custom CA install search pattern +pki_search_install_ca_pattern: "foo_install_ca_" + +# CA to install from a custom variable +foo_install_ca_variable: + - name: "FooAuthorityInstalled" + +# Certificates to create from the default variable +pki_certificates: + - name: "{{ ansible_facts['hostname'] }}_1" + provider: ownca + cn: "{{ ansible_facts['hostname'] }}" + san: "{{ 'DNS:' ~ ansible_facts['hostname'] }}" + signed_by: "ExampleCorpIntermediate" + +# Custom certificate generation search pattern +pki_search_certificates_pattern: "foo_certificates_" + +# Certificates to create from a custom variable, with conditionals +foo_certificates_variable: + - name: "{{ ansible_facts['hostname'] }}_2" + provider: ownca + cn: "{{ ansible_facts['hostname'] }}" + san: "{{ 'DNS:' ~ ansible_facts['hostname'] }}" + signed_by: "ExampleCorpIntermediate" + condition: true + - name: "{{ ansible_facts['hostname'] }}_3" + provider: ownca + cn: "{{ ansible_facts['hostname'] }}" + san: "{{ 'DNS:' ~ ansible_facts['hostname'] }}" + signed_by: "ExampleCorpIntermediate" + condition: false + +# Certificates to install from the default variable +functional_install_cert_1_dest: "{{ '/root/' ~ ansible_facts['hostname'] ~ '_1.crt' }}" +functional_install_chain_1_dest: "{{ '/root/' ~ ansible_facts['hostname'] ~ '_1-chain.crt' }}" +functional_install_key_1_dest: "{{ '/root/' ~ ansible_facts['hostname'] ~ '_1.key.pem' }}" + +pki_install_certificates: + - src: "{{ pki_dir ~ '/certs/certs/' ~ ansible_facts['hostname'] ~ '_1.crt' }}" + dest: "{{ functional_install_cert_1_dest }}" + owner: "root" + group: "root" + mode: "0644" + - src: "{{ pki_dir ~ '/certs/certs/' ~ ansible_facts['hostname'] ~ '_1-chain.crt' }}" + dest: "{{ functional_install_chain_1_dest }}" + owner: "root" + group: "root" + mode: "0644" + - src: "{{ pki_dir ~ '/certs/private/' ~ ansible_facts['hostname'] ~ '_1.key.pem' }}" + dest: "{{ functional_install_key_1_dest }}" + owner: "root" + group: "root" + mode: "0640" + +# Custom certificate installation search pattern +pki_search_install_certificates_pattern: "foo_install_certificates_" + +# Certificates to isntall from a custom variable, with conditionals +functional_install_cert_2_dest: "{{ '/root/' ~ ansible_facts['hostname'] ~ '_2.crt' }}" +functional_install_chain_2_dest: "{{ '/root/' ~ ansible_facts['hostname'] ~ '_2-chain.crt' }}" +functional_install_key_2_dest: "{{ '/root/' ~ ansible_facts['hostname'] ~ '_2.key.pem' }}" + +functional_install_cert_3_dest: "{{ '/root/' ~ ansible_facts['hostname'] ~ '_3.crt' }}" +functional_install_chain_3_dest: "{{ '/root/' ~ ansible_facts['hostname'] ~ '_3-chain.crt' }}" +functional_install_key_3_dest: "{{ '/root/' ~ ansible_facts['hostname'] ~ '_3.key.pem' }}" + +foo_install_certificates_variable: + - src: "{{ pki_dir ~ '/certs/certs/' ~ ansible_facts['hostname'] ~ '_2.crt' }}" + dest: "{{ functional_install_cert_2_dest }}" + owner: "root" + group: "root" + mode: "0644" + condition: true + - src: "{{ pki_dir ~ '/certs/certs/' ~ ansible_facts['hostname'] ~ '_2-chain.crt' }}" + dest: "{{ functional_install_chain_2_dest }}" + owner: "root" + group: "root" + mode: "0644" + condition: true + - src: "{{ pki_dir ~ '/certs/private/' ~ ansible_facts['hostname'] ~ '_2.key.pem' }}" + dest: "{{ functional_install_key_2_dest }}" + owner: "root" + group: "root" + mode: "0640" + condition: true + - src: "{{ pki_dir ~ '/certs/certs/' ~ ansible_facts['hostname'] ~ '_3.crt' }}" + dest: "{{ functional_install_cert_3_dest }}" + owner: "root" + group: "root" + mode: "0644" + condition: false + - src: "{{ pki_dir ~ '/certs/private/' ~ ansible_facts['hostname'] ~ '_3.key.pem' }}" + dest: "{{ functional_install_key_3_dest }}" + owner: "root" + group: "root" + mode: "0640" + condition: false diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml new file mode 100644 index 0000000..db67cd5 --- /dev/null +++ b/molecule/default/molecule.yml @@ -0,0 +1,35 @@ +--- +dependency: + name: galaxy + options: + requirements-file: requirements.yml + force: ${GALAXY_FORCE:-false} + +driver: + name: docker + +platforms: + - name: "pki-${MOLECULE_SCENARIO_NAME}" + image: "${DOCKER_REGISTRY:-quay.io/gotmax23}/${DOCKER_IMAGE_TAG:-debian-systemd:bookworm}" + command: ${DOCKER_COMMAND:-""} + pre_build_image: true + privileged: true + systemd: true + +provisioner: + name: ansible + lint: + name: ansible-lint + playbooks: + prepare: prepare.yml + converge: ../../examples/playbook.yml + verify: verify.yml + inventory: + links: + group_vars: ./group_vars/ + config_options: + defaults: + inject_facts_as_vars: false + +scenario: + name: default diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml new file mode 100644 index 0000000..e21e817 --- /dev/null +++ b/molecule/default/prepare.yml @@ -0,0 +1,14 @@ +--- +- name: Prepare + hosts: all + tasks: + - name: Update apt cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 + when: + - ansible_facts['os_family'] | lower == 'debian' + + - name: Install packages + ansible.builtin.package: + name: "{{ molecule_packages[ansible_facts['os_family'] | lower] }}" diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml new file mode 100644 index 0000000..e1ebdd0 --- /dev/null +++ b/molecule/default/verify.yml @@ -0,0 +1,84 @@ +--- +- name: Verify + hosts: all + vars: + pki_trust_store_location: + apt: /usr/local/share/ca-certificates/ + dnf: /etc/pki/ca-trust/source/anchors/ + tasks: + + # Check that certificate authorities are installed (or absent) at the correct path + - stat: + path: "{{ pki_trust_store_location[ansible_facts['pkg_mgr']] }}/{{ functional_ca_name_1 }}.crt" + register: ca_1_stat + + - stat: + path: "{{ pki_trust_store_location[ansible_facts['pkg_mgr']] }}/{{ functional_ca_name_2 }}.crt" + register: ca_2_stat + + - stat: + path: "{{ pki_trust_store_location[ansible_facts['pkg_mgr']] }}/{{ functional_ca_name_3 }}.crt" + register: ca_3_stat + + - assert: + that: + - ca_1_stat.stat.exists + - not ca_2_stat.stat.exists + - ca_3_stat.stat.exists + + # Check that certificates are installed (or absent) at the correct path + - stat: + path: "{{ functional_install_cert_1_dest }}" + register: cert_1_stat + + - stat: + path: "{{ functional_install_chain_1_dest }}" + register: chain_1_stat + + - stat: + path: "{{ functional_install_key_1_dest }}" + register: key_1_stat + + - stat: + path: "{{ functional_install_cert_2_dest }}" + register: cert_2_stat + + - stat: + path: "{{ functional_install_chain_2_dest }}" + register: chain_2_stat + + - stat: + path: "{{ functional_install_key_2_dest }}" + register: key_2_stat + + - stat: + path: "{{ functional_install_cert_3_dest }}" + register: cert_3_stat + + - stat: + path: "{{ functional_install_chain_3_dest }}" + register: chain_3_stat + + - stat: + path: "{{ functional_install_key_3_dest }}" + register: key_3_stat + + - assert: + that: + - cert_1_stat.stat.exists + - chain_1_stat.stat.exists + - key_1_stat.stat.exists + - cert_2_stat.stat.exists + - key_2_stat.stat.exists + - not cert_3_stat.stat.exists + - not chain_3_stat.stat.exists + - not key_3_stat.stat.exists + + # Check that certificates can validate against the installed CA + - name: Validate server certificate against system trust store + command: certtool --verify --infile "{{ functional_install_chain_1_dest }}" + changed_when: false + + - name: Validate server certificate against system trust store + command: certtool --verify --infile "{{ functional_install_chain_2_dest }}" + changed_when: false \ No newline at end of file diff --git a/requirements.yml b/requirements.yml new file mode 100644 index 0000000..4b76f86 --- /dev/null +++ b/requirements.yml @@ -0,0 +1,4 @@ +collections: + - name: community.crypto + version: 2.0.2 + source: https://galaxy.ansible.com diff --git a/tox.ini b/tox.ini index b88906f..b426a19 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] -minversion = 3.1 +minversion = 4.0 skipsdist = True -envlist = docs,releasenotes +envlist = docs,releasenotes,molecule ignore_basepython_conflict = True @@ -27,7 +27,6 @@ allowlist_externals = setenv = PYTHONUNBUFFERED=1 ROLE_NAME=pki - TEST_IDEMPOTENCE=false VIRTUAL_ENV={envdir} WORKING_DIR={toxinidir} @@ -56,3 +55,20 @@ commands = [testenv:venv] commands = {posargs} + +[testenv:molecule] +# You can use DOCKER_REGISTRY and DOCKER_IMAGE_TAG to switch between +# tested distros. I.e: +# DOCKER_IMAGE_TAG=ubuntu-systemd:jammy tox -e molecule +deps = + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -rhttps://opendev.org/openstack/openstack-ansible/raw/branch/{env:TEST_BRANCH:master}/test-requirements.txt + +commands = + molecule test + +passenv = + {[testenv]passenv} + DOCKER_REGISTRY + DOCKER_IMAGE_TAG + DOCKER_COMMAND diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml index 0d049d0..ceaa987 100644 --- a/zuul.d/project.yaml +++ b/zuul.d/project.yaml @@ -18,5 +18,6 @@ - check-requirements - openstack-ansible-deploy-infra_lxc-jobs - openstack-ansible-linters-jobs + - openstack-ansible-molecule - publish-openstack-docs-pti - build-release-notes-jobs-python3