Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,32 @@ jobs:
AUTH_SECRET: ci-placeholder
DATABASE_URL: ":memory:"

ansible-quality:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ansible
env:
ANSIBLE_INVENTORY: localhost,
ANSIBLE_COLLECTIONS_PATH: ./collections
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pip
cache-dependency-path: ansible/requirements.txt
- name: Install Ansible and lint tools
run: pip install -r requirements.txt
- name: Install Ansible collections
run: ansible-galaxy collection install -r requirements.yml -p ./collections --force
- name: Ansible lint – PaaS
run: ansible-lint --offline playbooks/paas/main.yml
- name: Ansible lint – SaaS deploy
run: ansible-lint --offline playbooks/saas/main.yml playbooks/saas/operate.yml
- name: Ansible lint – SaaS image build
run: ansible-lint --offline playbooks/saas/image-forkable.yml

build-ui:
needs: [setup, quality]
uses: ./.github/workflows/docker-build.yml
Expand All @@ -75,7 +101,7 @@ jobs:
registry_token: ${{ secrets.GITHUB_TOKEN }}

build-ansible:
needs: [setup, quality]
needs: [setup, quality, ansible-quality]
uses: ./.github/workflows/docker-build.yml
with:
image_name: ${{ github.repository }}-ansible
Expand Down
27 changes: 27 additions & 0 deletions ansible/.ansible-lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# ansible-lint configuration
# https://ansible.readthedocs.io/projects/lint/configuring/

profile: basic

exclude_paths:
- collections/

# Downgrade to warnings: style issues acceptable in the current codebase
warn_list:
- yaml[truthy]
- yaml[line-length]
- name[casing]
- name[template]
- no-changed-when
- risky-shell-pipe
- var-naming
- jinja[spacing]
- command-instead-of-module

# Skip rules that don't apply to internal roles (no galaxy metadata required)
skip_list:
- galaxy
- var-naming
- jinja[spacing]
- yaml[line-length]
- yaml[truthy]
10 changes: 8 additions & 2 deletions ansible/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
FROM ubuntu:25.04

ARG JAVA_VERSION=21
ARG TERRAFORM_VERSION=1.12.1
ARG TARGETARCH

RUN apt-get update && apt-get install --no-install-recommends -y \
bash \
Expand All @@ -16,9 +18,13 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
&& rm -rf /var/lib/apt/lists/*

RUN mkdir /tmp/terraform /root/.ssh && \
case "${TARGETARCH}" in \
amd64|arm64) TF_ARCH="${TARGETARCH}" ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}" && exit 1 ;; \
esac && \
cd /tmp/terraform && \
wget https://releases.hashicorp.com/terraform/1.12.1/terraform_1.12.1_linux_arm64.zip && \
unzip terraform_1.12.1_linux_arm64.zip && \
wget "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_${TF_ARCH}.zip" && \
unzip "terraform_${TERRAFORM_VERSION}_linux_${TF_ARCH}.zip" && \
mv terraform /usr/local/bin/

COPY . /ansible
Expand Down
4 changes: 2 additions & 2 deletions ansible/ansible.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
callbacks_enabled = minimal, webhook_notifier
collections_path = ./collections
deprecation_warnings = False
host_key_checking = False
host_key_checking = True
interpreter_python = auto_silent
inventory = inventory.py
library = ./library
Expand All @@ -19,7 +19,7 @@ fact_caching_connection = tmp/facts
[ssh_connection]
retries = 5
scp_if_ssh = True
ssh_args = -F ~/.ssh/config
ssh_args = -F ~/.ssh/config -o StrictHostKeyChecking=accept-new
pipelining = True

[url_lookup]
Expand Down
2 changes: 1 addition & 1 deletion ansible/demo-inventory.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ plugin: cloud.terraform.terraform_provider
project_path:
- ../terraform/demo-openstack-instances
- ../terraform/demo-openstack-loadbalancer
- ../terraform/demo-openstack-instances
- ../terraform/demo-standalone-instances
binary_path: terraform
70 changes: 55 additions & 15 deletions ansible/inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,74 @@
import requests
import os
import logging
import sys
import time

# Configure logging
logging.basicConfig(level=logging.INFO)

REQUIRED_ENV_VARS = [
"SIMPLE_STACK_UI_URL",
"SIMPLE_STACK_UI_USER",
"SIMPLE_STACK_UI_PASSWORD",
]


def get_required_env():
missing = [var for var in REQUIRED_ENV_VARS if not os.environ.get(var)]
if missing:
logging.error("Missing required environment variables: %s", ", ".join(missing))
return None

return {
"url": os.environ["SIMPLE_STACK_UI_URL"],
"user": os.environ["SIMPLE_STACK_UI_USER"],
"password": os.environ["SIMPLE_STACK_UI_PASSWORD"],
}

def fetch_inventory():
"""
Fetches inventory data from the SIMPLE STACK UI API.

Returns:
dict or None: The inventory data as a dictionary if successful, None otherwise.
"""
env = get_required_env()
if env is None:
return None

session = requests.Session()
session.auth = (os.environ["SIMPLE_STACK_UI_USER"], os.environ["SIMPLE_STACK_UI_PASSWORD"])
session.auth = (env["user"], env["password"])
retries = int(os.environ.get("SIMPLE_STACK_UI_RETRIES", "3"))
timeout = float(os.environ.get("SIMPLE_STACK_UI_TIMEOUT", "10"))

try:
url = f"{os.environ['SIMPLE_STACK_UI_URL']}/api/inventory"
logging.debug(f"Fetching inventory from {url}")
r = session.get(url)
r.raise_for_status() # Raise an error for bad responses
result = r.json() # Directly parse JSON response
return result
except requests.exceptions.HTTPError as e:
logging.error(f"HTTP error occurred: {e}")
except requests.exceptions.ConnectionError as e:
logging.error(f"Connection error occurred: {e}")
except requests.exceptions.Timeout as e:
logging.error(f"Timeout error occurred: {e}")
except requests.exceptions.RequestException as e:
logging.error(f"An error occurred: {e}")
url = f"{env['url']}/api/inventory"
logging.debug("Fetching inventory from %s", url)

for attempt in range(1, retries + 1):
try:
response = session.get(url, timeout=timeout)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
status = getattr(e.response, "status_code", 0)
is_retryable = 500 <= status < 600
if is_retryable and attempt < retries:
logging.warning("HTTP %s while fetching inventory (attempt %s/%s)", status, attempt, retries)
time.sleep(attempt)
continue
logging.error("HTTP error occurred: %s", e)
break
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as e:
if attempt < retries:
logging.warning("Connection error while fetching inventory (attempt %s/%s): %s", attempt, retries, e)
time.sleep(attempt)
continue
logging.error("Connection error occurred: %s", e)
except requests.exceptions.RequestException as e:
logging.error("An error occurred: %s", e)
break
finally:
session.close() # Ensure the session is closed

Expand All @@ -44,3 +83,4 @@ def fetch_inventory():
print(json.dumps(inventory, indent=2))
else:
logging.error("Failed to retrieve inventory data.")
sys.exit(1)
2 changes: 0 additions & 2 deletions ansible/playbooks/paas/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@
- unattended-upgrades
- ansible-ufw

- name: Configure sshd
ansible.builtin.import_playbook: sshd.yml
- name: Configure timesyncd
ansible.builtin.import_playbook: timesyncd.yml
- name: Configure systemd resolved
Expand Down
14 changes: 10 additions & 4 deletions ansible/playbooks/paas/nomad-clean-errors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@
hosts: "{{ hosts_limit | default('infrastructure') }}"
become: true
gather_facts: false
vars:
nomad_tls_skip_verify: true
tasks:
- name: Nomad system reconcile summaries
ansible.builtin.shell: nomad system reconcile summaries -address=https://127.0.0.1:4646 -tls-skip-verify
ansible.builtin.command: >
nomad system reconcile summaries -address=https://127.0.0.1:4646
{% if nomad_tls_skip_verify | bool %}-tls-skip-verify{% endif %}
environment:
NOMAD_TOKEN: "{{ lookup('simple-stack-ui', type='secret', key=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
NOMAD_TOKEN: "{{ lookup('simple-stack-ui', type='secret', key2=inventory_hostname, subkey='nomad_management_token', missing='error') }}"

- name: Nomad system gc
ansible.builtin.shell: nomad system gc -address=https://127.0.0.1:4646 -tls-skip-verify
ansible.builtin.command: >
nomad system gc -address=https://127.0.0.1:4646
{% if nomad_tls_skip_verify | bool %}-tls-skip-verify{% endif %}
environment:
NOMAD_TOKEN: "{{ lookup('simple-stack-ui', type='secret', key=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
NOMAD_TOKEN: "{{ lookup('simple-stack-ui', type='secret', key2=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
22 changes: 15 additions & 7 deletions ansible/playbooks/paas/nvidia.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,18 @@
cmd: "gpg --dearmor -o {{ nvidia_keyring_path }} /tmp/nvidia-container-toolkit.gpg"
creates: "{{ nvidia_keyring_path }}"

- name: Télécharger le fichier de dépôt NVIDIA et ajouter le signed-by
ansible.builtin.shell: |
curl -s -L {{ nvidia_repo_list_url }} | \
sed 's#deb https://#deb [signed-by={{ nvidia_keyring_path }}] https://#g' > {{ nvidia_list_path }}
args:
creates: "{{ nvidia_list_path }}"
- name: Télécharger le fichier de dépôt NVIDIA
ansible.builtin.get_url:
url: "{{ nvidia_repo_list_url }}"
dest: "{{ nvidia_list_path }}"
mode: "0644"
force: false

- name: Ajouter signed-by au dépôt NVIDIA
ansible.builtin.replace:
path: "{{ nvidia_list_path }}"
regexp: '^deb\s+https://'
replace: 'deb [signed-by={{ nvidia_keyring_path }}] https://'

- name: Activer la section experimental (décommenter)
ansible.builtin.replace:
Expand Down Expand Up @@ -134,7 +140,9 @@
ansible.builtin.command: nvidia-ctk runtime configure --runtime=docker

- name: Nomad-nvidia-plugin | Restart docker
ansible.builtin.command: systemctl restart docker
ansible.builtin.systemd:
name: docker
state: restarted

# https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/sample-workload.html
- name: Nomad-nvidia-plugin | Test nvidia support
Expand Down
2 changes: 1 addition & 1 deletion ansible/playbooks/paas/roles/ansible-ufw/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
delay: 6
retries: 10

- name: reset firewall
- name: Reset firewall
ufw:
state: reset
when: ufw_reset
Expand Down
2 changes: 1 addition & 1 deletion ansible/playbooks/paas/roles/nomad/handlers/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
chdir: "{{ nomad_job_files_dir }}"
environment:
NOMAD_ADDR: "https://{{ nomad_http_ip }}:4646"
NOMAD_TOKEN: "{{ lookup('simple-stack-ui', type='secret', key=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
NOMAD_TOKEN: "{{ lookup('simple-stack-ui', type='secret', key2=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
NOMAD_CLIENT_CERT: "{{ nomad_tls_host_certificate_dir }}/{{ nomad_tls_cert_server }}"
NOMAD_CLIENT_KEY: "{{ nomad_tls_host_certificate_dir }}/{{ nomad_tls_privatekey_server }}"
NOMAD_CACERT: "{{ nomad_tls_host_certificate_dir }}/{{ nomad_tls_ca_pubkey }}"
Expand Down
4 changes: 2 additions & 2 deletions ansible/playbooks/paas/roles/nomad/tasks/06_configuration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
block:
- name: "Nomad Install | Read Gossip Encryption Key"
ansible.builtin.set_fact:
nomad_encrypt_key: "{{ lookup('simple-stack-ui', type='secret', key=inventory_hostname, subkey='nomad_encrypt_key', missing='error') }}"
nomad_encrypt_key: "{{ lookup('simple-stack-ui', type='secret', key2=inventory_hostname, subkey='nomad_encrypt_key', missing='error') }}"
rescue:
- name: "Nomad Install | Generate Gossip Encryption Key"
ansible.builtin.command: "openssl rand -base64 32"
Expand All @@ -13,7 +13,7 @@

- name: "Nomad Install | Save Gossip Encryption Key"
ansible.builtin.set_fact:
nomad_encrypt_key: "{{ lookup('simple-stack-ui', type='secret', key=inventory_hostname, subkey='nomad_encrypt_key', missing='create', userpass=nomad_encrypt_key_out.stdout) }}"
nomad_encrypt_key: "{{ lookup('simple-stack-ui', type='secret', key2=inventory_hostname, subkey='nomad_encrypt_key', missing='create', userpass=nomad_encrypt_key_out.stdout) }}"

- name: "Nomad Configuration | Add user nomad to docker group"
ansible.builtin.user:
Expand Down
12 changes: 6 additions & 6 deletions ansible/playbooks/paas/roles/nomad/tasks/07_autoeligibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
client_key: "{{ nomad_tls_host_certificate_dir }}/{{ nomad_tls_privatekey_server }}"
method: GET
headers:
X-Nomad-Token: "{{ lookup('simple-stack-ui', type='secret', key=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
X-Nomad-Token: "{{ lookup('simple-stack-ui', type='secret', key2=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
status_code:
- 200
- 404
Expand All @@ -29,7 +29,7 @@
client_key: "{{ nomad_tls_host_certificate_dir }}/{{ nomad_tls_privatekey_server }}"
method: POST
headers:
X-Nomad-Token: "{{ lookup('simple-stack-ui', type='secret', key=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
X-Nomad-Token: "{{ lookup('simple-stack-ui', type='secret', key2=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
body: |
{
"Name": "autoeligibility",
Expand All @@ -56,7 +56,7 @@
client_key: "{{ nomad_tls_host_certificate_dir }}/{{ nomad_tls_privatekey_server }}"
method: GET
headers:
X-Nomad-Token: "{{ lookup('simple-stack-ui', type='secret', key=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
X-Nomad-Token: "{{ lookup('simple-stack-ui', type='secret', key2=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
status_code:
- 200
- 404
Expand All @@ -76,7 +76,7 @@
block:
- name: "Nomad Install | Read Nomad nomad autoeligibility token"
ansible.builtin.set_fact:
nomad_autoeligibility_token: "{{ lookup('simple-stack-ui', type='secret', key=inventory_hostname, subkey='nomad_autoeligibility_token', missing='error') }}"
nomad_autoeligibility_token: "{{ lookup('simple-stack-ui', type='secret', key2=inventory_hostname, subkey='nomad_autoeligibility_token', missing='error') }}"

rescue:
- name: "Nomad Token | Create token for Nomad access autoeligibility"
Expand All @@ -87,7 +87,7 @@
client_key: "{{ nomad_tls_host_certificate_dir }}/{{ nomad_tls_privatekey_server }}"
method: PUT
headers:
X-Nomad-Token: "{{ lookup('simple-stack-ui', type='secret', key=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
X-Nomad-Token: "{{ lookup('simple-stack-ui', type='secret', key2=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
body: |
{
"Name": "autoeligibility",
Expand All @@ -102,4 +102,4 @@

- name: "Nomad Install | Save Nomad Autoeligibility token"
ansible.builtin.set_fact:
nomad_autoeligibility_token: "{{ lookup('simple-stack-ui', type='secret', key=inventory_hostname, subkey='nomad_autoeligibility_token', missing='create', userpass=nomad_new_token_name.json.SecretID) }}"
nomad_autoeligibility_token: "{{ lookup('simple-stack-ui', type='secret', key2=inventory_hostname, subkey='nomad_autoeligibility_token', missing='create', userpass=nomad_new_token_name.json.SecretID) }}"
4 changes: 2 additions & 2 deletions ansible/playbooks/paas/roles/nomad/tasks/10_juicefs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
chdir: "{{ nomad_job_files_dir }}"
environment:
NOMAD_ADDR: "https://{{ nomad_http_ip }}:4646"
NOMAD_TOKEN: "{{ lookup('simple-stack-ui', type='secret', key=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
NOMAD_TOKEN: "{{ lookup('simple-stack-ui', type='secret', key2=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
NOMAD_CLIENT_CERT: "{{ nomad_tls_host_certificate_dir }}/{{ nomad_tls_cert_server }}"
NOMAD_CLIENT_KEY: "{{ nomad_tls_host_certificate_dir }}/{{ nomad_tls_privatekey_server }}"
NOMAD_CACERT: "{{ nomad_tls_host_certificate_dir }}/{{ nomad_tls_ca_pubkey }}"
Expand All @@ -44,7 +44,7 @@
chdir: "{{ nomad_job_files_dir }}"
environment:
NOMAD_ADDR: "https://{{ nomad_http_ip }}:4646"
NOMAD_TOKEN: "{{ lookup('simple-stack-ui', type='secret', key=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
NOMAD_TOKEN: "{{ lookup('simple-stack-ui', type='secret', key2=inventory_hostname, subkey='nomad_management_token', missing='error') }}"
NOMAD_CLIENT_CERT: "{{ nomad_tls_host_certificate_dir }}/{{ nomad_tls_cert_server }}"
NOMAD_CLIENT_KEY: "{{ nomad_tls_host_certificate_dir }}/{{ nomad_tls_privatekey_server }}"
NOMAD_CACERT: "{{ nomad_tls_host_certificate_dir }}/{{ nomad_tls_ca_pubkey }}"
Expand Down
Loading
Loading