diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..8827676 --- /dev/null +++ b/.yamllint @@ -0,0 +1,33 @@ +--- +# Based on ansible-lint config +extends: default + +rules: + braces: + max-spaces-inside: 1 + level: error + brackets: + max-spaces-inside: 1 + level: error + colons: + max-spaces-after: -1 + level: error + commas: + max-spaces-after: -1 + level: error + comments: disable + comments-indentation: disable + document-start: disable + empty-lines: + max: 3 + level: error + hyphens: + level: error + indentation: disable + key-duplicates: enable + line-length: disable + new-line-at-end-of-file: disable + new-lines: + type: unix + trailing-spaces: disable + truthy: disable diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..58f9f65 --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +ANSIBLE_KEY:=~/.ssh/ansible +# FIXME: This image is currently private +IMAGE:=molecule-test +SCENARIO:=docker-version +ROLE_NAME:=ansible-role-docker +WORK_DIR:=/workspace/$(ROLE_NAME) + +DOCKER_VERSIONS:=18.09.1 18.09.4 18.09.5 18.09.6 19.03.1 19.03.2 19.03.3 + +RUN_CMD=docker run --rm \ + --env ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS=ignore \ + --env HCLOUD_TOKEN=${HCLOUD_TOKEN} \ + --env MOLECULE_NO_LOG=yes \ + --env DOCKER_VERSION_CI=${DOCKER_VERSION_CI} \ + -v /tmp:/tmp \ + -v $(CURDIR):$(WORK_DIR) \ + -w $(WORK_DIR) \ + -v $(ANSIBLE_KEY):/root/.ssh/ansible $(IMAGE) + +.PHONY: help +help: ## Show help + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: all +all: $(DOCKER_VERSIONS) ## Run tests against all versions + +$(DOCKER_VERSIONS): ## internal: version loop + @echo version is $@ + $(MAKE) test DOCKER_VERSION_CI=$@ + +.PHONY: test +test: guard-HCLOUD_TOKEN guard-DOCKER_VERSION_CI ## Run molecule test + $(RUN_CMD) molecule test --scenario-name $(SCENARIO) + +guard-%: ## Check required variables + @ if [ "${${*}}" = "" ]; then \ + echo "Environment variable $* not set"; \ + exit 1; \ + fi diff --git a/molecule/docker-version/INSTALL.rst b/molecule/docker-version/INSTALL.rst new file mode 100644 index 0000000..7e64cca --- /dev/null +++ b/molecule/docker-version/INSTALL.rst @@ -0,0 +1,23 @@ +*************************************** +Hetzner Cloud driver installation guide +*************************************** + +Requirements +============ + +* Ansible >= 2.8 +* ``HCLOUD_TOKEN`` exposed in your environment + +Install +======= + +Please refer to the `Virtual environment`_ documentation for installation best +practices. If not using a virtual environment, please consider passing the +widely recommended `'--user' flag`_ when invoking ``pip``. + +.. _Virtual environment: https://virtualenv.pypa.io/en/latest/ +.. _'--user' flag: https://packaging.python.org/tutorials/installing-packages/#installing-to-the-user-site + +.. code-block:: bash + + $ pip install 'molecule[hetznercloud]' diff --git a/molecule/docker-version/create.yml b/molecule/docker-version/create.yml new file mode 100644 index 0000000..d53d8c1 --- /dev/null +++ b/molecule/docker-version/create.yml @@ -0,0 +1,71 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "/root/.ssh/ansible" + ssh_key_name: "ansible@planetary-networks.de" + tasks: + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + volumes: "{{ item.volumes | default(omit) }}" + 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 + with_items: "{{ 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 + with_items: "{{ server.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 }}", } + with_items: "{{ 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: "{{ instance_conf | to_json | from_json | molecule_to_yaml | molecule_header }}" + 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 + with_items: "{{ lookup('file', molecule_instance_config) | molecule_from_yaml }}" diff --git a/molecule/docker-version/destroy.yml b/molecule/docker-version/destroy.yml new file mode 100644 index 0000000..d5e1479 --- /dev/null +++ b/molecule/docker-version/destroy.yml @@ -0,0 +1,49 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Populate the instance config + block: + - name: Populate instance config from file + set_fact: + instance_conf: "{{ lookup('file', molecule_instance_config) | molecule_from_yaml }}" + skip_instances: false + rescue: + - name: Populate instance config when file missing + set_fact: + instance_conf: {} + skip_instances: true + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + with_items: "{{ instance_conf }}" + when: not skip_instances + 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 + with_items: "{{ server.results }}" + + # Mandatory configuration for Molecule to function. + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: "{{ instance_conf | molecule_to_yaml | molecule_header }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool diff --git a/molecule/docker-version/molecule.yml b/molecule/docker-version/molecule.yml new file mode 100644 index 0000000..7b28a69 --- /dev/null +++ b/molecule/docker-version/molecule.yml @@ -0,0 +1,19 @@ +--- +dependency: + name: galaxy +driver: + name: hetznercloud +lint: + name: yamllint +platforms: + - name: ansible-role-docker-${DOCKER_VERSION_CI} + server_type: cx11 + image: centos-7 +provisioner: + name: ansible + lint: + name: ansible-lint +verifier: + name: testinfra + lint: + name: flake8 diff --git a/molecule/docker-version/playbook.yml b/molecule/docker-version/playbook.yml new file mode 100644 index 0000000..6c1cc8f --- /dev/null +++ b/molecule/docker-version/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + roles: + - role: ansible-role-docker + vars: + docker_package_version: "{{ lookup('env', 'DOCKER_VERSION_CI') or '18.09.2' }}" diff --git a/molecule/docker-version/prepare.yml b/molecule/docker-version/prepare.yml new file mode 100644 index 0000000..ddb01fb --- /dev/null +++ b/molecule/docker-version/prepare.yml @@ -0,0 +1,9 @@ +--- +- name: Prepare + hosts: all + gather_facts: false + tasks: + - name: Install python for Ansible + raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal python-zipstream) + become: true + changed_when: false diff --git a/molecule/docker-version/tests/test_default.py b/molecule/docker-version/tests/test_default.py new file mode 100644 index 0000000..dea0eab --- /dev/null +++ b/molecule/docker-version/tests/test_default.py @@ -0,0 +1,45 @@ +import os +import pytest + +import testinfra.utils.ansible_runner + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + os.environ['MOLECULE_INVENTORY_FILE'] +).get_hosts('all') + + +@pytest.mark.parametrize('pkg', [ + 'docker-ce', + 'docker-ce-cli', + 'containerd.io' +]) +def test_packages_are_installed(host, pkg): + package = host.package(pkg) + assert package.is_installed + + +def test_srv(host): + service = host.service("docker") + + assert service.is_running + assert service.is_enabled + + +def test_docker_version(host): + docker_version = os.environ.get("DOCKER_VERSION_CI") + if not docker_version: + docker_version = "18.09.2" + + # docker_cli = host.check_output('docker -v') + # assert docker_cli.find(docker_version) != -1 + + docker_daemon = host.check_output('docker info|grep Version') + assert docker_daemon.find(docker_version) != -1 + + +def test_hosts_file(host): + f = host.file('/etc/hosts') + + assert f.exists + assert f.user == 'root' + assert f.group == 'root'