diff --git a/.drone.jsonnet b/.drone.jsonnet index b82d8be..6c48934 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -112,6 +112,7 @@ local PipelineRelease = { 'testing-centos7', 'testing-rocky8', 'testing-opensuse15', + 'testing-debian11', ], trigger: { ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'], @@ -207,6 +208,7 @@ local PipelineNotification = { PipelineTesting(scenario='centos7'), PipelineTesting(scenario='rocky8'), PipelineTesting(scenario='opensuse15'), + PipelineTesting(scenario='debian11'), PipelineRelease, PipelineDocumentation, PipelineNotification, diff --git a/.drone.yml b/.drone.yml index 599d6d8..0d3a753 100644 --- a/.drone.yml +++ b/.drone.yml @@ -170,6 +170,40 @@ trigger: depends_on: - linting +--- +kind: pipeline +name: testing-debian11 + +platform: + os: linux + arch: amd64 + +concurrency: + limit: 1 + +workspace: + base: /drone/src + path: redis + +steps: + - name: molecule + image: thegeeklab/molecule:3 + commands: + - molecule test -s debian11 + environment: + HCLOUD_TOKEN: + from_secret: hcloud_token + PY_COLORS: 1 + +trigger: + ref: + - refs/heads/main + - refs/tags/** + - refs/pull/** + +depends_on: + - linting + --- kind: pipeline name: release @@ -209,6 +243,7 @@ depends_on: - testing-centos7 - testing-rocky8 - testing-opensuse15 + - testing-debian11 --- kind: pipeline @@ -293,6 +328,6 @@ depends_on: --- kind: signature -hmac: 8ece82098addbb6ded7dcf88cb75e03dd0d028840224bb4b5168b3be35f8e9dc +hmac: 34bdef932a311ec6826159aa17bd1e21fb0327281d7e5a17c8b5093e6a84df56 ... diff --git a/molecule/debian11/converge.yml b/molecule/debian11/converge.yml new file mode 100644 index 0000000..c450aa2 --- /dev/null +++ b/molecule/debian11/converge.yml @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: all + vars: + redis_apt_cache_update: True + + roles: + - role: redis diff --git a/molecule/debian11/create.yml b/molecule/debian11/create.yml new file mode 100644 index 0000000..6331f47 --- /dev/null +++ b/molecule/debian11/create.yml @@ -0,0 +1,121 @@ +--- +- 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 | string }}" + 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 | string }}" + 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 }}", + "from_snapshot": "{{ item.item.item.from_snapshot | 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/debian11/destroy.yml b/molecule/debian11/destroy.yml new file mode 100644 index 0000000..c7da305 --- /dev/null +++ b/molecule/debian11/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 | string }}" + 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 | string }}" + 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/debian11/from_snapshot.yml b/molecule/debian11/from_snapshot.yml new file mode 100644 index 0000000..b1f7805 --- /dev/null +++ b/molecule/debian11/from_snapshot.yml @@ -0,0 +1,47 @@ +--- +- block: + - name: Restart in rescue mode + hcloud_server: + name: "{{ item.instance }}" + ssh_keys: + - "{{ item.ssh_key_name }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + rescue_mode: linux64 + state: restarted + async: 7200 + poll: 0 + + - name: Wait for SSH + wait_for: + port: "{{ item.port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + delegate_to: localhost + +- name: Mount root user volume + mount: + path: /mnt/root + src: /dev/sda2 + fstype: btrfs + opts: subvol=@/root + fstab: /tmp/fstab + state: mounted + +- name: Add ssh public key + authorized_key: + user: root + path: /mnt/root/.ssh/authorized_keys + key: "{{ lookup('file', item.identity_file + '.pub') }}" + state: present + +- name: Restart server + reboot: + +- name: Wait for SSH + wait_for: + port: "{{ item.port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + delegate_to: localhost diff --git a/molecule/debian11/molecule.yml b/molecule/debian11/molecule.yml new file mode 100644 index 0000000..13e509c --- /dev/null +++ b/molecule/debian11/molecule.yml @@ -0,0 +1,23 @@ +--- +dependency: + name: galaxy + options: + role-file: molecule/requirements.yml + env: + ANSIBLE_GALAXY_DISPLAY_PROGRESS: "false" +driver: + name: delegated +platforms: + - name: debian-11-redis + server_type: cx11 + image: debian-11 +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/debian11/prepare.yml b/molecule/debian11/prepare.yml new file mode 100644 index 0000000..bef0d85 --- /dev/null +++ b/molecule/debian11/prepare.yml @@ -0,0 +1,37 @@ +--- +- name: Prepare + hosts: all + gather_facts: false + tasks: + - name: Check existing instance config file + stat: + path: "{{ molecule_instance_config }}" + register: cfg + delegate_to: localhost + run_once: true + + - name: Populate the instance config + no_log: "{{ molecule_no_log }}" + set_fact: + instance_conf: "{{ (lookup('file', molecule_instance_config) | from_yaml) if cfg.stat.exists else [] }}" + delegate_to: localhost + run_once: true + + - name: Prepare instance(s) from snapshot + no_log: "{{ molecule_no_log }}" + include_tasks: from_snapshot.yml + loop: "{{ instance_conf }}" + when: + - inventory_hostname == item.instance + - item.from_snapshot | bool + + - 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/debian11/tests/test_default.py b/molecule/debian11/tests/test_default.py new file mode 100644 index 0000000..b9e76dc --- /dev/null +++ b/molecule/debian11/tests/test_default.py @@ -0,0 +1,21 @@ +import os + +import testinfra.utils.ansible_runner + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + os.environ["MOLECULE_INVENTORY_FILE"] +).get_hosts("all") + + +def test_redis_running_and_enabled(host): + redis = host.service("redis-server") + + assert redis.is_running + assert redis.is_enabled + + +def test_redis_config(host): + assert "127.0.0.1" in host.run("redis-cli config get bind").stdout + assert "6379" in host.run("redis-cli config get port").stdout + + assert "300" in host.run("redis-cli config get timeout").stdout