Compare commits

..

No commits in common. "main" and "add-networking-feature" have entirely different histories.

9 changed files with 315 additions and 51 deletions

View File

@ -7,27 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [1.3.0] - 2021-09-02
- See [here](https://github.com/ansible-community/molecule-hetznercloud/releases/tag/1.3.0)
## [1.2.1] - 2021-06-02
### Fixed
- Remove async task handling for network deletion ([#30](https://github.com/ansible-community/molecule-hetznercloud/pull/30), credit @ggggut)
## [1.2.0] - 2021-06-02
### Added
- Allow to create networks during test runs ([#29](https://github.com/ansible-community/molecule-hetznercloud/pull/29), thanks @ggggut!)
## [1.1.0] - 2021-03-30
## Changed
- Relaxed bounds on Molecule to allow all versions less than `v4` ([#27](https://github.com/ansible-community/molecule-hetznercloud/pull/27))
- Relaxed bounds on Molecule to allow all versions < v4 ([#27](https://github.com/ansible-community/molecule-hetznercloud/pull/27))
## [1.0.0] - 2021-01-06
@ -45,7 +29,7 @@ tracker so that we can mention them here.
## [0.2.2] - 2020-06-15
### Fixed
## Fixed
- Point to an open issue tracker

View File

@ -27,8 +27,6 @@ any financial support from RedHat or Hetzner Cloud for this work.
$ pip install molecule-hetznercloud
```
If you're looking for a container approach, see [ansible-community/toolset](https://github.com/ansible-community/toolset).
## Upgrade
Please see the [CHANGELOG.md](./CHANGELOG.md) for migration guides.
@ -89,11 +87,13 @@ $ export MOLECULE_DEBUG=True # very verbose, last ditch effort
## Volume Handling
> **WARNING**: this feature appears to be broke. See [#24](https://github.com/ansible-community/molecule-hetznercloud/issues/24) for more
It is possible to have the driver manage volumes during the test run.
It is possible to have the driver manage volumes during the test run. You can
add the following stanza to your Molecule configuration to have Molecule create
this volume for the managed VPS. This volume will be cleaned up after use.
You can add the following stanza to your Molecule configuration to have
Molecule create this volume for the managed VPS. This volume will be cleaned up
after use (**Please note**: there is a bug raised against clean-up right now,
see [#24](https://github.com/ansible-community/molecule-hetznercloud/issues/24)
for more).
```yaml
platforms:
@ -183,7 +183,7 @@ See [CHANGELOG.md](./CHANGELOG.md).
## Contact
- Ping @decentral1se on the `#ansible-molecule` channel on [Libera](https://libera.chat/).
- Ping @decentral1se on the `#ansible-molecule` channel on [Freenode](https://webchat.freenode.net).
## License
@ -202,12 +202,12 @@ $ tox -v
### Integration
```
git clone https://github.com/ansible-community/molecule-hetznercloud.git
cd molecule-hetznercloud
python3 -m venv .venv && source .venv/bin/activate
pip install -e . "ansible<4" netaddr
export INSTANCE_UUID=$(openssl rand -hex 5)
export HCLOUD_TOKEN=YOURKEY
cd integration && molecule test
Only doable by [Autonomic Cooperative](https://autonomic.zone/) members.
```bash
$ sudo apt install -y direnv
$ cp .envrc.sample .envrc
$ direnv allow
$ pip install -e . ansible
$ cd integration && molecule test
```

View File

@ -7,10 +7,9 @@ platforms:
- name: "molecule-hetznercloud-${INSTANCE_UUID}"
server_type: cx11
image: debian-10
# https://github.com/ansible-community/molecule-hetznercloud/issues/24
# volumes:
# - name: "molecule-hetznercloud-volume-1-${INSTANCE_UUID}"
# - name: "molecule-hetznercloud-volume-2-${INSTANCE_UUID}"
volumes:
- name: "molecule-hetznercloud-volume-1-${INSTANCE_UUID}"
# - name: "molecule-hetznercloud-volume-2-${INSTANCE_UUID}"
networks:
molecule-hetznercloud-network-1:
ip_range: 10.10.0.0/16

View File

@ -0,0 +1,158 @@
---
{% raw -%}
- 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
openssh_keypair:
path: "{{ 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.public_key }}"
api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}"
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
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 }}"
- name: Create volume(s)
hcloud_volume:
name: "{{ item.volumes.name | default(item.name) }}"
server: "{{ item.name }}"
location: "{{ item.volumes.location | default(omit) }}"
size: "{{ item.volumes.size | default(10) }}"
api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}"
state: "present"
with_items: "{{ molecule_yml.platforms }}"
when:
- item.volumes is defined
- item.volumes.create | 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
with_items: "{{ volumes.results }}"
- name: Create private network(s)
hcloud_network:
name: "{{ item.name }}"
ip_range: "{{ item.ip_range | default(omit) }}"
api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}"
state: "present"
loop: "{{ molecule_yml.platforms|molecule_get_hetznercloud_networks('networks') }}"
register: networks
async: 7200
poll: 0
- name: Wait for network(s) creation to complete
async_status:
jid: "{{ item.ansible_job_id }}"
register: hetzner_networks
until: hetzner_networks.finished
retries: 300
when:
- networks is defined
- networks.changed
with_items: "{{ networks.results }}"
- name: Create private subnetwork(s)
hcloud_subnetwork:
network: "{{ item.network_name }}"
ip_range: "{{ item.ip|ipaddr('network/prefix') }}"
network_zone: "{{ item.network_zone | default('eu-central') }}"
type: "{{ item.type | default('cloud') }}"
api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}"
state: "present"
loop: "{{ molecule_yml.platforms|molecule_get_hetznercloud_networks('subnetworks') }}"
register: subnetworks
- name: Attach Server to Subnetwork(s)
hcloud_server_network:
network: "{{ item.network_name }}"
server: "{{ item.server_name }}"
ip: "{{ item.ip|ipaddr('address') }}"
api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}"
state: "present"
loop: "{{ molecule_yml.platforms|molecule_get_hetznercloud_networks('subnetworks') }}"
- 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 }}",
'volumes': "{{ item.item.item.volumes | default({}) }}",
'networks': "{{ item.item.item.networks | default({}) | dict2items(key_name='name', value_name='data') }}", }
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: |
# Molecule managed
{{ instance_conf | to_json | from_json | to_yaml }}
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) | from_yaml }}"
{%- endraw %}

View File

@ -0,0 +1,103 @@
---
{% raw -%}
- 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) | 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 }}"
- name: Destroy volume(s)
hcloud_volume:
name: "{{ item.volumes.name | default(item.instance) }}"
server: "{{ item.instance }}"
api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}"
state: "absent"
register: volumes
with_items: "{{ instance_conf }}"
when:
- item.volumes is defined
- item.volumes.create | 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
with_items: "{{ volumes.results }}"
- name: Destroy network(s)
hcloud_network:
name: "{{ item.1.name }}"
api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}"
state: absent
register: networks
loop: "{{ instance_conf|subelements('networks', skip_missing=True) }}"
async: 7200
poll: 0
- name: Wait for network(s) deletion to complete
async_status:
jid: "{{ item.ansible_job_id }}"
register: hetzner_networks
until: hetzner_networks.finished
retries: 300
when: networks.changed
with_items: "{{ networks.results }}"
- name: Remove registered SSH key
hcloud_ssh_key:
name: "{{ instance_conf[0].ssh_key_name }}"
api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}"
state: absent
when:
- not skip_instances
- instance_conf | length # must contain at least one instance
- name: Populate instance config
set_fact:
instance_conf: {}
- name: Dump instance config
copy:
content: |
# Molecule managed
{{ instance_conf | to_json | from_json | to_yaml }}
dest: "{{ molecule_instance_config }}"
when: server.changed | bool
{%- endraw %}

View File

@ -1,9 +1,8 @@
import os
from ansible_compat.ports import cache
from molecule import logger, util
from molecule.api import Driver
from molecule.util import sysexit_with_message
from molecule.util import lru_cache, sysexit_with_message
log = logger.get_logger(__name__)
@ -76,7 +75,7 @@ class HetznerCloud(Driver):
item for item in instance_config_dict if item["instance"] == instance_name
)
@cache
@lru_cache()
def sanity_checks(self):
"""Hetzner Cloud driver sanity checks."""

View File

@ -6,16 +6,16 @@
no_log: "{{ molecule_no_log }}"
tasks:
- name: Populate the instance config
set_fact:
instance_conf: "{{ lookup('file', molecule_instance_config, errors='warn') | from_yaml }}"
skip_instances: false
register: instance_config_lookup
- name: Populate instance config when file missing
set_fact:
instance_conf: {}
skip_instances: true
when: not instance_config_lookup.ansible_facts.instance_conf
block:
- name: Populate instance config from file
set_fact:
instance_conf: "{{ lookup('file', molecule_instance_config) | 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:
@ -63,6 +63,17 @@
state: absent
register: networks
loop: "{{ instance_conf|subelements('networks', skip_missing=True) }}"
async: 7200
poll: 0
- name: Wait for network(s) deletion to complete
async_status:
jid: "{{ item.ansible_job_id }}"
register: hetzner_networks
until: hetzner_networks.finished
retries: 300
when: networks.changed
with_items: "{{ networks.results }}"
- name: Remove registered SSH key
hcloud_ssh_key:

View File

@ -66,6 +66,17 @@
state: absent
register: networks
loop: "{{ instance_conf|subelements('networks', skip_missing=True) }}"
async: 7200
poll: 0
- name: Wait for network(s) deletion to complete
async_status:
jid: "{{ item.ansible_job_id }}"
register: hetzner_networks
until: hetzner_networks.finished
retries: 300
when: networks.changed
with_items: "{{ networks.results }}"
- name: Remove registered SSH key
hcloud_ssh_key:

View File

@ -55,7 +55,6 @@ setup_requires =
setuptools_scm
setuptools_scm_git_archive
install_requires =
ansible-compat >= 0.5.0
hcloud >= 1.10.0, < 2
molecule >= 3.2.1, < 4
pyyaml >= 5.3.1, < 6