Skip to content

test(molecule): #75 review follow-ups, and a host-wide lock on the su… #154

test(molecule): #75 review follow-ups, and a host-wide lock on the su…

test(molecule): #75 review follow-ups, and a host-wide lock on the su… #154

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. cloud-init/ is in
# `paths` because the `cloud-init` scenario boots cloud-init/user-data.yaml and runs
# cloud-init/bootstrap.sh.
on:
pull_request:
paths: ['ansible/**', 'cloud-init/**', '.github/workflows/molecule.yml']
push:
branches: [main]
paths: ['ansible/**', 'cloud-init/**', '.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 with 8 scenarios. os-matrix (3 containers) and lifecycle took the
# suite to ~10m at JOBS=3 on a local 16-thread box; re-measure from CI's first run.
# 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): 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') }}