Skip to content

feat(ansible): prepare the launch fleet (private overlay, catalogue sizing, runbook) #141

feat(ansible): prepare the launch fleet (private overlay, catalogue sizing, runbook)

feat(ansible): prepare the launch fleet (private overlay, catalogue sizing, runbook) #141

Workflow file for this run

---
name: Molecule
# Containerised converge + idempotence + verify for the decdn_node role.
# Heavy (privileged systemd Docker container) — scoped to ansible/ changes and
# blocking. Mark it a required status check in branch protection once proven.
# This file is in `paths` alongside ansible/ so a change to the job itself (its cache
# wiring, JOBS, the timeout) is exercised by the PR that makes it.
on:
pull_request:
paths: ['ansible/**', '.github/workflows/molecule.yml']
push:
branches: [main]
paths: ['ansible/**', '.github/workflows/molecule.yml']
permissions:
contents: read
concurrency:
group: molecule-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
molecule:
runs-on: ubuntu-latest # Docker is preinstalled
# ~4m41s at JOBS=3. A bound, not a target: without one a wedged privileged
# systemd container burns the 360-minute default, and with cancel-in-progress
# above, some branch-protection setups read the resulting cancelled check as
# "not failed" rather than as a failure.
timeout-minutes: 45
defaults:
run:
working-directory: ansible
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# Keyed on this workflow file: no pip manifest exists, and the workflow is
# where the package list lives. ci.yml's ansible-lint job has the full note.
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.12'
cache: pip
cache-dependency-path: .github/workflows/molecule.yml
- name: Install molecule + Ansible
run: |
python -m pip install --upgrade \
molecule "molecule-plugins[docker]" ansible ansible-lint docker
# Same key and same rationale as ci.yml's jobs (see ansible-lint there): a warm
# tree makes the install a no-op that never contacts galaxy.ansible.com, and the
# save is gated so a part-way Galaxy failure cannot poison the cache. The key is
# derived wholly from ansible/requirements.yml, so it cannot drift from ci.yml's
# copy the way a duplicated env var would. It wraps
# `make molecule` because the deps install is owned by the Make target here, not
# by a step of its own.
- uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
id: galaxy-cache
with:
path: ansible/collections
key: galaxy-${{ runner.os }}-${{ hashFiles('ansible/requirements.yml') }}
- name: molecule test
# `make molecule` runs every scenario under ansible/molecule/ and fans them out
# in parallel; ansible/Makefile documents the set and the fail-loud guards.
#
# There is deliberately no separate `make deps` step: the molecule targets take
# `deps` as a prerequisite, so a standalone one would resolve and install the
# Galaxy requirements a second time every run — a second chance to trip over a
# flaky galaxy.ansible.com, for no added coverage. The cache above wraps that
# Make-owned invocation instead.
#
# JOBS is capped at 3 rather than the default (one job per scenario, currently
# 7): every scenario is a privileged systemd container, and they share this
# runner's cores and cgroup hierarchy. If this job turns flaky, drop to JOBS=1
# or swap in `make molecule-serial` — the latter also serialises the output.
run: make molecule JOBS=3
# A step `if:` without a status-check function implies success(), so this is
# skipped when anything above failed — including a genuinely failing scenario,
# which leaves the cache cold for that run. Conservative on purpose: it is the
# same guard that keeps a part-way Galaxy install out of the cache.
- uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
if: steps.galaxy-cache.outputs.cache-hit != 'true'
with:
path: ansible/collections
key: galaxy-${{ runner.os }}-${{ hashFiles('ansible/requirements.yml') }}