Add workspace generation playbook
Sem-Ver: feature
Change-Id: Ic08f9b5b208799261aca140f212fb18e7acb5ee0
diff --git a/.gitignore b/.gitignore
index 79f3920..e02caf7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,9 @@
.vscode
doc/build/*
doc/source/roles/*/defaults
+molecule/default/group_vars/*
+!molecule/default/group_vars/.gitkeep
+molecule/default/host_vars/*
+!molecule/default/host_vars/.gitkeep
galaxy.yml
*.tar.gz
\ No newline at end of file
diff --git a/molecule/default/create.yml b/molecule/default/create.yml
index 1261a25..54c5267 100644
--- a/molecule/default/create.yml
+++ b/molecule/default/create.yml
@@ -12,9 +12,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-- import_playbook: vexxhost.atmosphere.generate_secrets
+- import_playbook: vexxhost.atmosphere.generate_workspace
vars:
- secrets_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/secrets.yml"
+ workspace_path: "{{ lookup('env', 'MOLECULE_SCENARIO_DIRECTORY') }}"
+ domain_name: "{{ '{{' }} hostvars['ctl1']['ansible_host'].replace('.', '-') {{ '}}' }}.nip.io"
- hosts: localhost
connection: local
diff --git a/molecule/default/destroy.yml b/molecule/default/destroy.yml
index 9b99c6d..599fe49 100644
--- a/molecule/default/destroy.yml
+++ b/molecule/default/destroy.yml
@@ -17,7 +17,7 @@
gather_facts: false
no_log: "{{ molecule_no_log }}"
vars:
- secrets_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/secrets.yml"
+ workspace_path: "{{ lookup('env', 'MOLECULE_SCENARIO_DIRECTORY') }}"
stack_name: "{{ lookup('env', 'ATMOSPHERE_STACK_NAME') | default('atmosphere', True) }}"
tasks:
@@ -29,6 +29,7 @@
path: "{{ molecule_instance_config }}"
state: absent
- - copy:
- dest: "{{ secrets_path }}"
- content: "{}"
+ - shell: rm -rf {{ workspace_path }}/{{ item }}/*
+ loop:
+ - group_vars
+ - host_vars
diff --git a/molecule/default/group_vars/.gitkeep b/molecule/default/group_vars/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/molecule/default/group_vars/.gitkeep
diff --git a/molecule/default/host_vars/.gitkeep b/molecule/default/host_vars/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/molecule/default/host_vars/.gitkeep
diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml
index ac0b158..9d4dfe7 100644
--- a/molecule/default/molecule.yml
+++ b/molecule/default/molecule.yml
@@ -39,57 +39,13 @@
provisioner:
name: ansible
options:
- extra-vars: "@${MOLECULE_EPHEMERAL_DIRECTORY}/secrets.yml"
+ inventory: "${MOLECULE_EPHEMERAL_DIRECTORY}/workspace"
config_options:
ssh_connection:
pipelining: true
inventory:
- group_vars:
- all:
- atmosphere_image_repository: us-docker.pkg.dev/vexxhost-infra/openstack
- ceph_mon_fsid: 441193d8-fed9-485b-87f4-09245ddc1fe7
- ceph_mon_public_network: 10.96.240.0/24
- kubernetes_hostname: 10.96.240.10
- # OpenStack
- openstack_helm_endpoints_region_name: RegionOne
- # Keystone
- openstack_helm_endpoints_keystone_api_host: "identity.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- # Glance
- openstack_helm_endpoints_glance_api_host: "image.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- # Cinder
- openstack_helm_endpoints_cinder_api_host: "volume.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- # Placement
- openstack_helm_endpoints_placement_api_host: "placement.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- # Neutron
- openstack_helm_endpoints_neutron_api_host: "network.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- openstack_helm_neutron_values:
- conf:
- auto_bridge_add:
- br-ex: ens4
- # Nova
- openstack_helm_endpoints_nova_api_host: "compute.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- openstack_helm_endpoints_nova_novnc_host: "vnc.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- # Ironic
- openstack_helm_endpoints_ironic_api_host: "baremetal.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- # Designate
- openstack_helm_endpoints_designate_api_host: "dns.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- # Octavia
- openstack_helm_endpoints_octavia_api_host: "load-balancer.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- # Senlin
- openstack_helm_endpoints_senlin_api_host: "clustering.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- # Heat
- openstack_helm_endpoints_heat_api_host: "orchestration.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- openstack_helm_endpoints_heat_cfn_api_host: "cloudformation.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- # Horizon
- openstack_helm_endpoints_horizon_api_host: "dashboard.{{ hostvars['ctl1']['ansible_host'].replace('.', '-') }}.nip.io"
- controllers:
- kubernetes_keepalived_vrid: 42
- kubernetes_keepalived_interface: ens3
- kubernetes_keepalived_vip: 10.96.240.10
- cephs:
- ceph_osd_devices:
- - /dev/vdb
- - /dev/vdc
- - /dev/vdd
+ links:
+ host_vars: "${MOLECULE_SCENARIO_DIRECTORY}/host_vars"
+ group_vars: "${MOLECULE_SCENARIO_DIRECTORY}/group_vars"
verifier:
name: testinfra
diff --git a/playbooks/generate_secrets.yml b/playbooks/generate_secrets.yml
deleted file mode 100644
index 6ed535c..0000000
--- a/playbooks/generate_secrets.yml
+++ /dev/null
@@ -1,38 +0,0 @@
----
-- hosts: localhost
- gather_facts: false
- tasks:
- - name: Ensure the secrets file exists
- ansible.builtin.file:
- path: "{{ secrets_path }}"
- state: touch
-
- - name: Load the current secrets into a variable
- ansible.builtin.include_vars:
- file: "{{ secrets_path }}"
- name: secrets
-
- - name: Generate secrets for missing variables
- ansible.builtin.set_fact:
- secrets: "{{ secrets| default({}) | combine({item: lookup('password', '/dev/null chars=ascii_lowercase,ascii_uppercase,digits length=32')}) }}"
- # NOTE(mnaser): We don't want to override existing secrets, so we generate
- # a new one if and only if it doesn't exist
- when: item not in secrets
- # NOTE(mnaser): This is absolutely hideous but there's no clean way of
- # doing this using `with_fileglob` or `with_filetree`
- with_lines: >
- ls {{ playbook_dir }}/../roles/*/defaults/main.yml |
- xargs grep undef |
- egrep -v '(_host|region_name)' |
- cut -d':' -f2
-
- - name: Write new secrets file to disk
- ansible.builtin.copy:
- content: "{{ secrets | to_nice_yaml }}"
- dest: "{{ secrets_path }}"
-
- - name: Encrypt secrets file with Vault password
- ansible.builtin.shell:
- ansible-vault encrypt --vault-password-file {{ secrets_vault_password_file }} {{ secrets_path }}
- when:
- - secrets_vault_password_file is defined
\ No newline at end of file
diff --git a/playbooks/generate_workspace.yml b/playbooks/generate_workspace.yml
new file mode 100644
index 0000000..4211fa0
--- /dev/null
+++ b/playbooks/generate_workspace.yml
@@ -0,0 +1,279 @@
+# Copyright (c) 2022 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+- name: Generate workspace for Atmosphere
+ hosts: localhost
+ gather_facts: false
+ tasks:
+ - name: Create folders for workspace
+ ansible.builtin.file:
+ path: "{{ workspace_path }}/{{ item }}"
+ state: directory
+ loop:
+ - group_vars
+ - group_vars/all
+ - group_vars/controllers
+ - group_vars/cephs
+ - group_vars/computes
+ - host_vars
+
+- name: Generate Ceph control plane configuration for workspace
+ hosts: localhost
+ gather_facts: false
+ vars:
+ _ceph_path: "{{ workspace_path }}/group_vars/all/ceph.yml"
+ # Input variables
+ ceph_fsid: "{{ lookup('password', '/dev/null chars=ascii_letters,digits') | to_uuid }}"
+ ceph_public_network: 10.96.240.0/24
+ tasks:
+ - name: Ensure the Ceph control plane configuration file exists
+ ansible.builtin.file:
+ path: "{{ _ceph_path }}"
+ state: touch
+
+ - name: Load the current Ceph control plane configuration into a variable
+ ansible.builtin.include_vars:
+ file: "{{ _ceph_path }}"
+ name: ceph
+
+ - name: Generate Ceph control plane values for missing variables
+ ansible.builtin.set_fact:
+ ceph: "{{ ceph | default({}) | combine({item.key: item.value}) }}"
+ # NOTE(mnaser): We don't want to override existing Ceph configurations,
+ # so we generate a stub one if and only if it doesn't exist
+ when: item.key not in ceph
+ # NOTE(mnaser): This is absolutely hideous but there's no clean way of
+ # doing this using `with_fileglob` or `with_filetree`
+ with_dict:
+ ceph_mon_fsid: "{{ ceph_fsid }}"
+ ceph_mon_public_network: "{{ ceph_public_network }}"
+
+ - name: Write new Ceph control plane configuration file to disk
+ ansible.builtin.copy:
+ content: "{{ ceph | to_nice_yaml(indent=2, width=180) }}"
+ dest: "{{ _ceph_path }}"
+
+- name: Generate Ceph OSD configuration for workspace
+ hosts: localhost
+ gather_facts: false
+ vars:
+ _ceph_osd_path: "{{ workspace_path }}/group_vars/cephs/osds.yml"
+ tasks:
+ - name: Ensure the Ceph OSDs configuration file exists
+ ansible.builtin.file:
+ path: "{{ _ceph_osd_path }}"
+ state: touch
+
+ - name: Load the current Ceph OSDs configuration into a variable
+ ansible.builtin.include_vars:
+ file: "{{ _ceph_osd_path }}"
+ name: ceph_osd
+
+ - name: Generate Ceph OSDs values for missing variables
+ ansible.builtin.set_fact:
+ ceph_osd: "{{ ceph_osd | default({}) | combine({item.key: item.value}) }}"
+ # NOTE(mnaser): We don't want to override existing Ceph configurations,
+ # so we generate a stub one if and only if it doesn't exist
+ when: item.key not in ceph_osd
+ # NOTE(mnaser): This is absolutely hideous but there's no clean way of
+ # doing this using `with_fileglob` or `with_filetree`
+ with_dict:
+ ceph_osd_devices:
+ - /dev/vdb
+ - /dev/vdc
+ - /dev/vdd
+
+ - name: Write new Ceph OSDs configuration file to disk
+ ansible.builtin.copy:
+ content: "{{ ceph_osd | to_nice_yaml(indent=2, width=180) }}"
+ dest: "{{ _ceph_osd_path }}"
+
+- name: Generate Kubernetes configuration for workspace
+ hosts: localhost
+ gather_facts: false
+ vars:
+ _kubernetes_path: "{{ workspace_path }}/group_vars/all/kubernetes.yml"
+ tasks:
+ - name: Ensure the Kubernetes configuration file exists
+ ansible.builtin.file:
+ path: "{{ _kubernetes_path }}"
+ state: touch
+
+ - name: Load the current Kubernetes configuration into a variable
+ ansible.builtin.include_vars:
+ file: "{{ _kubernetes_path }}"
+ name: kubernetes
+
+ - name: Generate Kubernetes values for missing variables
+ ansible.builtin.set_fact:
+ kubernetes: "{{ kubernetes | default({}) | combine({item.key: item.value}) }}"
+ # NOTE(mnaser): We don't want to override existing Ceph configurations,
+ # so we generate a stub one if and only if it doesn't exist
+ when: item.key not in kubernetes
+ # NOTE(mnaser): This is absolutely hideous but there's no clean way of
+ # doing this using `with_fileglob` or `with_filetree`
+ with_dict:
+ kubernetes_hostname: 10.96.240.10
+ kubernetes_keepalived_vrid: 42
+ kubernetes_keepalived_interface: ens3
+ kubernetes_keepalived_vip: 10.96.240.10
+
+ - name: Write new Kubernetes configuration file to disk
+ ansible.builtin.copy:
+ content: "{{ kubernetes | to_nice_yaml(indent=2, width=180) }}"
+ dest: "{{ _kubernetes_path }}"
+
+- name: Generate endpoints for workspace
+ hosts: localhost
+ gather_facts: false
+ vars:
+ _endpoints_path: "{{ workspace_path }}/group_vars/all/endpoints.yml"
+ # Input variables
+ region_name: RegionOne
+ domain_name: vexxhost.cloud
+ tasks:
+ - name: Ensure the endpoints file exists
+ ansible.builtin.file:
+ path: "{{ _endpoints_path }}"
+ state: touch
+
+ - name: Load the current endpoints into a variable
+ ansible.builtin.include_vars:
+ file: "{{ _endpoints_path }}"
+ name: endpoints
+
+ - name: Generate endpoint skeleton for missing variables
+ ansible.builtin.set_fact:
+ endpoints: |
+ {{
+ endpoints |
+ default({}) |
+ combine({item: default_map[item]})
+ }}
+ # NOTE(mnaser): We don't want to override existing endpoints, so we generate
+ # a stub one if and only if it doesn't exist
+ when: item not in endpoints
+ # NOTE(mnaser): This is absolutely hideous but there's no clean way of
+ # doing this using `with_fileglob` or `with_filetree`
+ with_lines: >
+ ls {{ playbook_dir }}/../roles/*/defaults/main.yml |
+ xargs grep undef |
+ egrep '(_host|region_name)' |
+ cut -d':' -f2
+ # NOTE(mnaser): We use these variables to generate map of service name to
+ # service type in order to generate the URLs
+ vars:
+ default_map:
+ openstack_helm_endpoints_region_name: "{{ region_name }}"
+ openstack_helm_endpoints_cinder_api_host: "volume.{{ domain_name }}"
+ openstack_helm_endpoints_designate_api_host: "dns.{{ domain_name }}"
+ openstack_helm_endpoints_glance_api_host: "image.{{ domain_name }}"
+ openstack_helm_endpoints_heat_api_host: "orchestration.{{ domain_name }}"
+ openstack_helm_endpoints_heat_cfn_api_host: "cloudformation.{{ domain_name }}"
+ openstack_helm_endpoints_horizon_api_host: "dashboard.{{ domain_name }}"
+ openstack_helm_endpoints_ironic_api_host: "baremetal.{{ domain_name }}"
+ openstack_helm_endpoints_keystone_api_host: "identity.{{ domain_name }}"
+ openstack_helm_endpoints_neutron_api_host: "network.{{ domain_name }}"
+ openstack_helm_endpoints_nova_api_host: "compute.{{ domain_name }}"
+ openstack_helm_endpoints_nova_novnc_host: "vnc.{{ domain_name }}"
+ openstack_helm_endpoints_octavia_api_host: "load-balancer.{{ domain_name }}"
+ openstack_helm_endpoints_placement_api_host: "placement.{{ domain_name }}"
+ openstack_helm_endpoints_senlin_api_host: "clustering.{{ domain_name }}"
+
+ - name: Write new endpoints file to disk
+ ansible.builtin.copy:
+ content: "{{ endpoints | to_nice_yaml(indent=2, width=180) }}"
+ dest: "{{ _endpoints_path }}"
+
+ - name: Ensure the endpoints file exists
+ ansible.builtin.file:
+ path: "{{ _endpoints_path }}"
+ state: touch
+
+- name: Generate Neutron configuration for workspace
+ hosts: localhost
+ gather_facts: false
+ vars:
+ _neutron_path: "{{ workspace_path }}/group_vars/all/neutron.yml"
+ # Input variables
+ tasks:
+ - name: Ensure the Neutron configuration file exists
+ ansible.builtin.file:
+ path: "{{ _neutron_path }}"
+ state: touch
+
+ - name: Load the current Neutron configuration into a variable
+ ansible.builtin.include_vars:
+ file: "{{ _neutron_path }}"
+ name: neutron
+
+ - name: Generate Neutron values for missing variables
+ ansible.builtin.set_fact:
+ neutron: "{{ neutron | default({}) | combine({item.key: item.value}) }}"
+ # NOTE(mnaser): We don't want to override existing Ceph configurations,
+ # so we generate a stub one if and only if it doesn't exist
+ when: item.key not in neutron
+ # NOTE(mnaser): This is absolutely hideous but there's no clean way of
+ # doing this using `with_fileglob` or `with_filetree`
+ with_dict:
+ openstack_helm_neutron_values:
+ conf:
+ auto_bridge_add:
+ br-ex: ens4
+
+ - name: Write new Neutron configuration file to disk
+ ansible.builtin.copy:
+ content: "{{ neutron | to_nice_yaml(indent=2, width=180) }}"
+ dest: "{{ _neutron_path }}"
+
+- name: Generate secrets for workspace
+ hosts: localhost
+ gather_facts: false
+ vars:
+ secrets_path: "{{ workspace_path }}/group_vars/all/secrets.yml"
+ tasks:
+ - name: Ensure the secrets file exists
+ ansible.builtin.file:
+ path: "{{ secrets_path }}"
+ state: touch
+
+ - name: Load the current secrets into a variable
+ ansible.builtin.include_vars:
+ file: "{{ secrets_path }}"
+ name: secrets
+
+ - name: Generate secrets for missing variables
+ ansible.builtin.set_fact:
+ secrets: "{{ secrets | default({}) | combine({item: lookup('password', '/dev/null chars=ascii_lowercase,ascii_uppercase,digits length=32')}) }}"
+ # NOTE(mnaser): We don't want to override existing secrets, so we generate
+ # a new one if and only if it doesn't exist
+ when: item not in secrets
+ # NOTE(mnaser): This is absolutely hideous but there's no clean way of
+ # doing this using `with_fileglob` or `with_filetree`
+ with_lines: >
+ ls {{ playbook_dir }}/../roles/*/defaults/main.yml |
+ xargs grep undef |
+ egrep -v '(_host|region_name)' |
+ cut -d':' -f2
+
+ - name: Write new secrets file to disk
+ ansible.builtin.copy:
+ content: "{{ secrets | to_nice_yaml }}"
+ dest: "{{ secrets_path }}"
+
+ - name: Encrypt secrets file with Vault password
+ ansible.builtin.shell:
+ ansible-vault encrypt --vault-password-file {{ secrets_vault_password_file }} {{ secrets_path }}
+ when:
+ - secrets_vault_password_file is defined
\ No newline at end of file
diff --git a/releasenotes/notes/add-workspace-generation-8ff28781216beccd.yaml b/releasenotes/notes/add-workspace-generation-8ff28781216beccd.yaml
new file mode 100644
index 0000000..a4780e5
--- /dev/null
+++ b/releasenotes/notes/add-workspace-generation-8ff28781216beccd.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - Added playbook to allow for generating workspace for deployment and
+ integrate it into Molecule in order to make sure we always test it.
\ No newline at end of file