diff --git a/.drone.jsonnet b/.drone.jsonnet index 18fab09..439fb8e 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -71,7 +71,7 @@ local PipelineDeployment(scenario='centos7') = { 'linting', ], trigger: { - ref: ['refs/heads/master', 'refs/tags/**'], + ref: ['refs/heads/master', 'refs/tags/**', 'refs/pull/**'], }, }; @@ -116,6 +116,7 @@ local PipelineDocumentation = { }, depends_on: [ 'testing-centos7', + 'testing-rocky8', ], }; @@ -154,6 +155,7 @@ local PipelineNotification = { [ PipelineLinting, PipelineDeployment(scenario='centos7'), + PipelineDeployment(scenario='rocky8'), PipelineDocumentation, PipelineNotification, ] diff --git a/.drone.yml b/.drone.yml index 4bfbc42..76d0ce9 100644 --- a/.drone.yml +++ b/.drone.yml @@ -62,6 +62,40 @@ trigger: ref: - refs/heads/master - refs/tags/** + - refs/pull/** + +depends_on: + - linting + +--- +kind: pipeline +name: testing-rocky8 + +platform: + os: linux + arch: amd64 + +concurrency: + limit: 1 + +workspace: + base: /drone/src + path: ${DRONE_REPO_NAME} + +steps: + - name: ansible-molecule + image: thegeeklab/molecule:3 + commands: + - molecule test -s rocky8 + environment: + HCLOUD_TOKEN: + from_secret: hcloud_token + +trigger: + ref: + - refs/heads/master + - refs/tags/** + - refs/pull/** depends_on: - linting @@ -108,6 +142,7 @@ trigger: depends_on: - testing-centos7 + - testing-rocky8 --- kind: pipeline @@ -147,6 +182,6 @@ depends_on: --- kind: signature -hmac: e48d58848c0a99a4ef582484ad9fdf20adcc004e1716c9e759a04be556573e9a +hmac: 9498be8bb2cd2775954b8a17e2acbb30ad8f98a7f6f0defd7e6d28c03aa0b4b6 ... diff --git a/molecule/default b/molecule/default index 2fdf3e8..62ea184 120000 --- a/molecule/default +++ b/molecule/default @@ -1 +1 @@ -centos7 \ No newline at end of file +rocky8 \ No newline at end of file diff --git a/molecule/rocky8/converge.yml b/molecule/rocky8/converge.yml new file mode 100644 index 0000000..5608f84 --- /dev/null +++ b/molecule/rocky8/converge.yml @@ -0,0 +1,10 @@ +--- +- name: Converge + hosts: all + vars: + mosquitto_packages_extra: + - epel-release + mosquitto_bind_address: "127.0.0.1" + + roles: + - role: xoxys.mosquitto diff --git a/molecule/rocky8/create.yml b/molecule/rocky8/create.yml new file mode 100644 index 0000000..8b945cd --- /dev/null +++ b/molecule/rocky8/create.yml @@ -0,0 +1,120 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + tasks: + - name: Create SSH key + user: + name: "{{ lookup('env', 'USER') }}" + generate_ssh_key: true + ssh_key_file: "{{ ssh_path }}" + force: true + register: generated_ssh_key + + - name: Register the SSH key name + set_fact: + ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" + + - name: Register SSH key for test instance(s) + hcloud_ssh_key: + name: "{{ ssh_key_name }}" + public_key: "{{ generated_ssh_key.ssh_public_key }}" + state: present + + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + image: "{{ item.image }}" + location: "{{ item.location | default(omit) }}" + datacenter: "{{ item.datacenter | default(omit) }}" + user_data: "{{ item.user_data | default(omit) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: present + register: server + loop: "{{ molecule_yml.platforms }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + loop: "{{ server.results }}" + + - name: Create volume(s) + hcloud_volume: + name: "{{ item.name }}" + server: "{{ item.name }}" + location: "{{ item.location | default(omit) }}" + size: "{{ item.volume_size | default(10) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "present" + loop: "{{ molecule_yml.platforms }}" + when: item.volume | default(False) | bool + register: volumes + async: 7200 + poll: 0 + + - name: Wait for volume(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_volumes + until: hetzner_volumes.finished + retries: 300 + when: volumes.changed + loop: "{{ volumes.results }}" + + # Mandatory configuration for Molecule to function. + + - name: Populate instance config dict + set_fact: + instance_conf_dict: + { + "instance": "{{ item.hcloud_server.name }}", + "ssh_key_name": "{{ ssh_key_name }}", + "address": "{{ item.hcloud_server.ipv4_address }}", + "user": "{{ ssh_user }}", + "port": "{{ ssh_port }}", + "identity_file": "{{ ssh_path }}", + "volume": "{{ item.item.item.volume | default(False) | bool }}", + } + loop: "{{ hetzner_jobs.results }}" + register: instance_config_dict + when: server.changed | bool + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + when: server.changed | bool + + - name: Dump instance config + copy: + content: | + # Molecule managed + + {{ instance_conf | to_nice_yaml(indent=2) }} + dest: "{{ molecule_instance_config }}" + when: server.changed | bool + + - name: Wait for SSH + wait_for: + port: "{{ ssh_port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + loop: "{{ lookup('file', molecule_instance_config) | from_yaml }}" + + - name: Wait for VM to settle down + pause: + seconds: 30 diff --git a/molecule/rocky8/destroy.yml b/molecule/rocky8/destroy.yml new file mode 100644 index 0000000..6454c71 --- /dev/null +++ b/molecule/rocky8/destroy.yml @@ -0,0 +1,78 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Check existing instance config file + stat: + path: "{{ molecule_instance_config }}" + register: cfg + + - name: Populate the instance config + set_fact: + instance_conf: "{{ (lookup('file', molecule_instance_config) | from_yaml) if cfg.stat.exists else [] }}" + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + loop: "{{ instance_conf }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + loop: "{{ server.results }}" + + - pause: + seconds: 5 + + - name: Destroy volume(s) + hcloud_volume: + name: "{{ item.instance }}" + server: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "absent" + register: volumes + loop: "{{ instance_conf }}" + when: item.volume | default(False) | bool + async: 7200 + poll: 0 + + - name: Wait for volume(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_volumes + until: hetzner_volumes.finished + retries: 300 + when: volumes.changed + loop: "{{ volumes.results }}" + + - name: Remove registered SSH key + hcloud_ssh_key: + name: "{{ instance_conf[0].ssh_key_name }}" + state: absent + when: (instance_conf | default([])) | length > 0 + + # Mandatory configuration for Molecule to function. + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: | + # Molecule managed + + {{ instance_conf | to_nice_yaml(indent=2) }} + dest: "{{ molecule_instance_config }}" + when: server.changed | bool diff --git a/molecule/rocky8/molecule.yml b/molecule/rocky8/molecule.yml new file mode 100644 index 0000000..b06454d --- /dev/null +++ b/molecule/rocky8/molecule.yml @@ -0,0 +1,24 @@ +--- +dependency: + name: galaxy + options: + role-file: molecule/requirements.yml + requirements-file: molecule/requirements.yml + env: + ANSIBLE_GALAXY_DISPLAY_PROGRESS: "false" +driver: + name: delegated +platforms: + - name: rocky8-mosquitto + image: rocky-8 + server_type: cx11 +lint: | + /usr/local/bin/flake8 +provisioner: + name: ansible + env: + ANSIBLE_FILTER_PLUGINS: ${ANSIBLE_FILTER_PLUGINS:-./plugins/filter} + ANSIBLE_LIBRARY: ${ANSIBLE_LIBRARY:-./library} + log: False +verifier: + name: testinfra diff --git a/molecule/rocky8/prepare.yml b/molecule/rocky8/prepare.yml new file mode 100644 index 0000000..183f4d3 --- /dev/null +++ b/molecule/rocky8/prepare.yml @@ -0,0 +1,15 @@ +--- +- name: Prepare + hosts: all + gather_facts: false + tasks: + - name: Bootstrap python for Ansible + raw: | + command -v python3 python || ( + (test -e /usr/bin/dnf && sudo dnf install -y python3) || + (test -e /usr/bin/apt && (apt -y update && apt install -y python-minimal)) || + (test -e /usr/bin/yum && sudo yum -y -qq install python3) || + echo "Warning: Python not boostrapped due to unknown platform." + ) + become: true + changed_when: false diff --git a/molecule/rocky8/tests/test_default.py b/molecule/rocky8/tests/test_default.py new file mode 100644 index 0000000..ba373fb --- /dev/null +++ b/molecule/rocky8/tests/test_default.py @@ -0,0 +1,23 @@ +import os + +import testinfra.utils.ansible_runner + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + os.environ["MOLECULE_INVENTORY_FILE"] +).get_hosts("all") + + +def test_mosquitto_is_installed(host): + mosquitto = host.package("mosquitto") + assert mosquitto.is_installed + + +def test_mosquitto_running_and_enabled(host): + mosquitto = host.service("mosquitto") + assert mosquitto.is_running + assert mosquitto.is_enabled + + +def test_mosquitto_socket(host): + # Verify the socket is listening for HTTP traffic + assert host.socket("tcp://127.0.0.1:1883").is_listening