commit
e0e0977b1e
@ -0,0 +1,5 @@ |
||||
--- |
||||
skip_list: |
||||
- fqcn-builtins |
||||
- no-jinja-nesting |
||||
- experimental |
@ -0,0 +1,16 @@ |
||||
---- |
||||
kind: pipeline |
||||
name: default |
||||
steps: |
||||
- name: integration test |
||||
image: python:3.9-buster |
||||
environment: |
||||
REMOTE_USER: molecule |
||||
HCLOUD_TOKEN: |
||||
from_secret: HCLOUD_TOKEN |
||||
commands: |
||||
- apt update && apt install -y pwgen |
||||
- mkdir -p /root/.ansible/roles && ln -sr . /root/.ansible/roles/autonomic.new-hetzner |
||||
- export INSTANCE_UUID=$(pwgen 8 1) |
||||
- pip install -r requirements.txt |
||||
- molecule test |
@ -0,0 +1,18 @@ |
||||
# Your username that you use for accounts on our machines. |
||||
export REMOTE_USER= |
||||
export ANSIBLE_USER=$REMOTE_USER |
||||
|
||||
# The path to our pass credentials store |
||||
export PASSWORD_STORE_DIR= |
||||
|
||||
# The Hetzner Cloud API token for managing our instances |
||||
# Uncomment the prod/test line below depending on what you're doing |
||||
# export HCLOUD_TOKEN=$(pass show logins/hetzner/prod/api_key) |
||||
# export HCLOUD_TOKEN=$(pass show logins/hetzner/test/api_key) |
||||
export HCLOUD_TOKEN=$(pass show logins/hetzner/cicd/api_key) |
||||
|
||||
# For molecule role testing |
||||
export INSTANCE_UUID=$RANDOM |
||||
|
||||
# So molecule will show credentials in the logs |
||||
export MOLECULE_NO_LOG=False |
@ -0,0 +1,16 @@ |
||||
--- |
||||
extends: default |
||||
|
||||
yaml-files: |
||||
- "*.yaml" |
||||
- "*.yml" |
||||
|
||||
ignore: | |
||||
.venv |
||||
.drone.yml |
||||
|
||||
rules: |
||||
line-length: disable |
||||
braces: |
||||
max-spaces-inside: 1 |
||||
level: error |
@ -0,0 +1,15 @@ |
||||
autonomic.new-hetzner: Creates and runs "bootstrapping" roles against a new Hetzner Cloud server |
||||
Copyright (C) 2022 Autonomic Co-operative <helo@autonomic.zone> |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. |
@ -0,0 +1,3 @@ |
||||
# autonomic.new-hetzner |
||||
|
||||
[](https://drone.autonomic.zone/autonomic-cooperative/autonomic.new-hetzner) |
@ -0,0 +1,4 @@ |
||||
--- |
||||
new_hetzner_backups_enabled: true |
||||
new_hetzner_delete_protection: true |
||||
new_hetzner_rebuild_protection: true |
@ -0,0 +1,20 @@ |
||||
--- |
||||
dependencies: [] |
||||
galaxy_info: |
||||
role_name: new_hetzner |
||||
namespace: autonomic |
||||
author: autonomic |
||||
description: | |
||||
Creates and runs "bootstrapping" roles against a new Hetzner Cloud server. |
||||
The roles that are run are: autonomic.add-users, autonomic.sshd, |
||||
autonomic.ufw, autonomic.packages, autonomic.name and autonomic.motd. The |
||||
new user passwords are generated and stored in the password store. If |
||||
generating a new server, don't forget to add the details to the inventory |
||||
listing. |
||||
company: Autonomic |
||||
license: GPLv3 |
||||
min_ansible_version: 2.9 |
||||
platforms: |
||||
- name: Debian |
||||
versions: |
||||
- buster |
@ -0,0 +1,17 @@ |
||||
# Molecule managed |
||||
|
||||
{% if item.registry is defined %} |
||||
FROM {{ item.registry.url }}/{{ item.image }} |
||||
{% else %} |
||||
FROM {{ item.image }} |
||||
{% endif %} |
||||
|
||||
{% if item.env is defined %} |
||||
{% for var, value in item.env.items() %} |
||||
{% if value %} |
||||
ENV {{ var }} {{ value }} |
||||
{% endif %} |
||||
{% endfor %} |
||||
{% endif %} |
||||
|
||||
RUN apt-get update && apt-get install -y python sudo bash ca-certificates iproute2 && apt-get clean; |
@ -0,0 +1,25 @@ |
||||
--- |
||||
- name: Converge |
||||
hosts: all |
||||
vars: |
||||
- new_hetzner_server_name: autonomic.new-hetzner-molecule |
||||
- add_users_inventory_hostname: autonomic.new-hetzner-molecule |
||||
- new_hetzner_server_type: cx11 |
||||
- new_hetzner_server_image: debian-10 |
||||
- new_hetzner_delete_protection: false |
||||
- new_hetzner_rebuild_protection: false |
||||
tasks: |
||||
- name: Run the role under test |
||||
block: |
||||
- import_role: |
||||
name: autonomic.new-hetzner |
||||
always: |
||||
- name: Flush all handlers |
||||
meta: flush_handlers |
||||
ignore_errors: true |
||||
|
||||
- name: Ensure the server is deleted |
||||
hcloud_server: |
||||
name: "{{ new_hetzner_server_name }}" |
||||
api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" |
||||
state: absent |
@ -0,0 +1,37 @@ |
||||
--- |
||||
dependency: |
||||
name: galaxy |
||||
|
||||
driver: |
||||
name: docker |
||||
|
||||
platforms: |
||||
- name: "autonomic.new-hetzner-${INSTANCE_UUID}" |
||||
image: debian:buster |
||||
|
||||
provisioner: |
||||
name: ansible |
||||
|
||||
lint: | |
||||
set -e |
||||
yamllint -c .yamllint.yml . |
||||
ansible-lint --exclude .drone.yml -c .ansible-lint.yml . |
||||
|
||||
scenario: |
||||
test_sequence: |
||||
- lint |
||||
- dependency |
||||
- cleanup |
||||
- destroy |
||||
- syntax |
||||
- create |
||||
- prepare |
||||
- converge |
||||
# TODO(decentral1se): Disabled for now since there are so many tasks which |
||||
# simply always report changed and I'd rather not patch this issue which is |
||||
# really something to do with Ansible and not Molecule. |
||||
# - idempotence |
||||
- side_effect |
||||
- verify |
||||
- cleanup |
||||
- destroy |
@ -0,0 +1,16 @@ |
||||
--- |
||||
- name: Converge |
||||
hosts: all |
||||
tasks: |
||||
- name: Install python-pip |
||||
package: |
||||
name: |
||||
- python-apt |
||||
- python-pip |
||||
- openssh-client |
||||
- pass |
||||
state: present |
||||
- name: Install module dependencies |
||||
pip: |
||||
name: hcloud |
||||
state: present |
@ -0,0 +1,36 @@ |
||||
--- |
||||
|
||||
roles: |
||||
- name: autonomic.add-users |
||||
src: https://git.autonomic.zone/autonomic-cooperative/autonomic.add-users |
||||
version: 0.1.0 |
||||
scm: git |
||||
|
||||
- name: autonomic.sshd |
||||
src: https://git.autonomic.zone/autonomic-cooperative/autonomic.sshd |
||||
version: 0.1.0 |
||||
scm: git |
||||
|
||||
- name: autonomic.ufw |
||||
src: https://git.autonomic.zone/autonomic-cooperative/autonomic.ufw |
||||
version: 0.1.0 |
||||
scm: git |
||||
|
||||
- name: autonomic.packages |
||||
src: https://git.autonomic.zone/autonomic-cooperative/autonomic.packages |
||||
version: 0.1.0 |
||||
scm: git |
||||
|
||||
- name: autonomic.name |
||||
src: https://git.autonomic.zone/autonomic-cooperative/autonomic.name |
||||
version: 0.1.0 |
||||
scm: git |
||||
|
||||
- name: autonomic.motd |
||||
src: https://git.autonomic.zone/autonomic-cooperative/autonomic.motd |
||||
version: 0.1.0 |
||||
scm: git |
||||
|
||||
collections: |
||||
- name: hetzner.hcloud |
||||
version: 1.6.0 |
@ -0,0 +1,5 @@ |
||||
ansible-lint==6.0.0 |
||||
ansible==5.4.0 |
||||
molecule-docker=1.1.0 |
||||
molecule-hetznercloud==1.3.0 |
||||
molecule==3.6.1 |
@ -0,0 +1,143 @@ |
||||
--- |
||||
- name: Ensure mandatory variables are configured |
||||
assert: |
||||
that: "{{ item }} is defined" |
||||
fail_msg: "You must define the '{{ item }}' variable" |
||||
with_items: |
||||
- new_hetzner_server_name |
||||
- new_hetzner_server_type |
||||
- new_hetzner_server_image |
||||
|
||||
- name: Include resource variables |
||||
include_vars: "{{ role_path }}/../../resources/{{ lookup('env', 'MEMBERS_FILE') | default('members.yml', True) }}" |
||||
|
||||
# Note(decentral1se): gives root SSH access for all autonomic members |
||||
- name: Ensure all Autonomic member SSH keys are registered |
||||
hcloud_ssh_key: |
||||
name: "{{ item.email }}" |
||||
api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" |
||||
public_key: "{{ item.ssh_key }}" |
||||
state: present |
||||
with_items: "{{ members }}" |
||||
|
||||
- name: Create new hetzner cloud instance |
||||
hcloud_server: |
||||
api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" |
||||
image: "{{ new_hetzner_server_image }}" |
||||
name: "{{ new_hetzner_server_name }}" |
||||
server_type: "{{ new_hetzner_server_type }}" |
||||
ssh_keys: "{{ members | map(attribute='email') | list }}" |
||||
labels: |
||||
managed: ansible |
||||
backups: "{{ new_hetzner_backups_enabled }}" |
||||
delete_protection: "{{ new_hetzner_delete_protection }}" |
||||
rebuild_protection: "{{ new_hetzner_rebuild_protection }}" |
||||
state: present |
||||
register: new_instance |
||||
async: 7200 |
||||
poll: 0 |
||||
|
||||
- name: Wait for instance creation to complete |
||||
async_status: |
||||
jid: "{{ new_instance.ansible_job_id }}" |
||||
register: hetzner_job |
||||
until: hetzner_job.finished |
||||
retries: 300 |
||||
|
||||
# Note(decentral1se): before user accounts are created we connect as root. This |
||||
# is possible because we registered the SSH keys with Hetzner Cloud and when |
||||
# the new instance is created, those keys are in /root/authorized_keys |
||||
- name: Dynamically create root connection details for the new instance |
||||
add_host: |
||||
hostname: root-new-instance |
||||
ansible_host: "{{ hetzner_job.hcloud_server.ipv4_address }}" |
||||
ansible_ssh_extra_args: "-o StrictHostKeyChecking=no" |
||||
ansible_user: root |
||||
|
||||
- name: "Wait for SSH on {{ new_hetzner_server_name }} to come up on port 22" |
||||
wait_for: |
||||
port: 22 |
||||
host: "{{ hetzner_job.hcloud_server.ipv4_address }}" |
||||
search_regex: SSH |
||||
delay: 10 |
||||
|
||||
- name: Run the add-users role on the new instance |
||||
vars: |
||||
members: "../../../resources/members.yml" |
||||
delegate_to: root-new-instance |
||||
import_role: |
||||
name: autonomic.add-users |
||||
tags: |
||||
- molecule-notest |
||||
|
||||
- name: Run the sshd role on the new instance |
||||
delegate_to: root-new-instance |
||||
import_role: |
||||
name: autonomic.sshd |
||||
tags: |
||||
- molecule-notest |
||||
|
||||
- name: Run all service restart handlers |
||||
delegate_to: root-new-instance |
||||
meta: flush_handlers |
||||
tags: |
||||
- molecule-notest |
||||
|
||||
- name: "Wait for SSH to come up again on {{ sshd_port }}" |
||||
wait_for: |
||||
port: "{{ sshd_port }}" |
||||
host: "{{ hetzner_job.hcloud_server.ipv4_address }}" |
||||
search_regex: SSH |
||||
delay: 10 |
||||
tags: |
||||
- molecule-notest |
||||
|
||||
# Note(decentral1se): At this point we're connecting with our own new user |
||||
# account and using sudo based privilege escalation with the password generated |
||||
# by pass from the passwords repository. Dog fooding our own connection setup ensures it |
||||
# works |
||||
- name: "Dynamically create {{ lookup('env', 'REMOTE_USER') }} connection details for the new instance" |
||||
add_host: |
||||
hostname: user-new-instance |
||||
ansible_host: "{{ hetzner_job.hcloud_server.ipv4_address }}" |
||||
ansible_ssh_extra_args: "-o StrictHostKeyChecking=no" |
||||
ansible_user: "{{ ansible_user }}" |
||||
ansible_port: "{{ sshd_port }}" |
||||
|
||||
# Note(decentral1se): Ansible refuses to use the correct password without specifying both of these |
||||
ansible_sudo_pass: "{{ lookup('passwordstore', 'users/{{ ansible_user }}/sudo/{{ new_hetzner_server_name }}') }}" |
||||
ansible_become_password: "{{ lookup('passwordstore', 'users/{{ ansible_user }}/sudo/{{ new_hetzner_server_name }}') }}" |
||||
tags: |
||||
- molecule-notest |
||||
|
||||
- name: Run the ufw role on the new instance |
||||
delegate_to: user-new-instance |
||||
become: true |
||||
import_role: |
||||
name: autonomic.ufw |
||||
tags: |
||||
- molecule-notest |
||||
|
||||
- name: Run the packages role on the new instance |
||||
delegate_to: user-new-instance |
||||
become: true |
||||
import_role: |
||||
name: autonomic.packages |
||||
tags: |
||||
- molecule-notest |
||||
|
||||
- name: Run the name role on the new instance |
||||
delegate_to: user-new-instance |
||||
become: true |
||||
import_role: |
||||
name: autonomic.name |
||||
tags: |
||||
- molecule-notest |
||||
|
||||
- name: Run the motd role on the new instance |
||||
delegate_to: user-new-instance |
||||
become: true |
||||
import_role: |
||||
name: autonomic.motd |
||||
tags: |
||||
- molecule-notest |
Reference in new issue