Branch protection is the part of this lesson everybody already agrees
with, so it goes first and briefly. The second half is the part that gets
skipped, and it is the one that causes 02:00 incidents.
The uncontroversial half
main is protected. No direct pushes, no force pushes, no branch
deletion. Changes arrive through pull requests.
Branches are short-lived. A branch open for three weeks accumulates
merge conflicts and stops being reviewable, and the review is the control.
Days, not weeks.
Review is required, and CODEOWNERS decides who. Lesson 7 of the
previous part covers scoping it to the paths that can cause outages rather
than to everything.
Status checks are required. The pipeline must pass before merge, and
“required” has to mean it — an override anybody can click is a suggestion.
None of that is Ansible-specific. What is Ansible-specific is that the
paths worth protecting are not the ones an application repository would
choose. inventories/production/ matters more than most roles, because a
change there re-scopes runs rather than changing behaviour, and it will be
a two-line diff.
What does production run? Whatever main contained at the moment the job
started. That is not a version — it is a timestamp, and it has three
consequences that all show up at once during an incident.
Nobody can say what production is running. “The latest main” is
true and useless. The commit that ran at 02:00 is not the commit at the
top of main now.
A merge changes production without anyone deciding to. Somebody merges
a reviewed, tested, entirely correct change on Friday afternoon. It
applies itself on Sunday at 02:00, unattended, because that is when the
job runs. Nothing was wrong with the change; the problem is that nobody
chose the moment.
Two runs of “the same thing” are not the same thing. A run that
started before a merge and a run that started after it are different runs
of a job with one name.
Now merging is not deploying. Tagging is deploying, and tagging is a
deliberate act with a name attached to it.
Which commit ran, recorded where the answer is findable
Pinning the run is half of it. Being able to answer which pin ran three
weeks later is the other half.
The provenance play from the previous part carries release_identifier
into the run output for exactly this reason:
Read-only / Safethe release identifier in the run output— Real output from ansible-core 2.21.3, paths shortened. Only set_fact and debug execute, so this play changes nothing.
The failure this prevents is specific. Without it, the run log records
that a job called deploy-production succeeded, and reconstructing what
that job actually ran means correlating a timestamp with the repository
history — which works until somebody force-pushes, rewrites history to
remove a secret, or deletes the branch.
UNTAGGED in that field is itself a useful signal. A production run
without a release identifier is a run somebody started by hand, and
knowing that is often the answer to the question you are asking.
Knowledge check
Knowledge check · 4 questions
Q1. A scheduled job runs git checkout main && git pull && ansible-playbook against production every night. What is the core problem?
Q2. Which of these keep a tag-based production deploy trustworthy? Select all that apply.
Q3. ansible-pull with no --checkout argument runs the repository default branch, so hosts polling at different times can be running different code.
Q4. Emergency bypasses of branch protection have risen to roughly one a week. What does that most likely indicate?
Passing score: 75%. Answers are checked in this browser.