Own DPkg::Lock::Timeout in Puppet and bound the boot security upgrade - #290
Merged
Conversation
Behaviour changes are in environments/development only; sandbox and modules/ follow after testing there. profile::apt_lock_timeout (new, declared by profile::repos with stage => init) writes /etc/apt/apt.conf.d/99-lock-timeout with an *unscoped* DPkg::Lock::Timeout. Ubuntu's shipped default is binary::apt::-scoped, so it covers the `apt` command only -- apt-get, which Puppet's package provider, cloud-init and the AWS agents all use, inherits nothing and fails instantly on a held lock. Measured on noble / apt 2.8.3 against a held frontend lock: apt-get install exits 100 in 0s without the unscoped key, 0 in 45s with it. The value comes from the apt_lock_timeout custom fact, defaulting to the 300 current AMIs ship. It manages the same path infrahouse-ubuntu-pro writes, because two drop-ins setting the key resolve by lexical filename order. The init stage is load-bearing: a drop-in applied halfway through the catalog does not help the Package resources Puppet already evaluated. Exec[gha-boot-security-upgrade] moves its retry logic into gha-boot-security-upgrade.sh with a hard cumulative deadline (480s budget, exec timeout 540) rather than tries/try_sleep. exec's timeout is per-attempt, so tries multiplies the worst case with no cumulative cap -- and an overrun is not merely a slow run: an ih-puppet resource failure exits 4/6 under --detailed-exitcodes, tripping ih-bootstrap's ERR trap into ABANDON. Retries are still needed because the apt lists lock is not covered by DPkg::Lock::Timeout and unattended-upgrade refuses to run concurrently with itself. Also corrects profile::unattended_upgrades' docstring in all three copies (comments only, no behaviour change): those units are masked by terraform-aws-cloud-init's bootcmd, not by the AMI. That misattribution caused a wrong root-cause analysis, so the correction is applied everywhere rather than left to the promotion cycle. The unmask execs are load-bearing until infrahouse/terraform-aws-cloud-init#91 lands. Refs #289, infrahouse/infrahouse-ubuntu-pro#20, infrahouse/terraform-aws-cloud-init#91 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
infrahouse8
previously approved these changes
Jul 31, 2026
Member
Author
|
test: |
Integer(pick_default(...)) parses fine on Puppet 8 -- verified with `puppet parser validate` on 8.4.0 -- but the Puppet 4/5-era grammars still bundled in editor language servers reject it: after a bare capitalized name, which is a Type reference, that grammar accepts `[`, an operator, `<| |>`, CONSUMES or PRODUCES, but not `(`. It surfaced as a permanent false error in the IDE. Dropped the conversion and the type rather than swapping in a stricter type. $timeout is only interpolated into the drop-in's content, where an Integer and a String are indistinguishable, so there was never anything to convert or validate. This also matches the existing idiom for fact-sourced parameters -- see $deregistration_hookname in profile::github_runner::service. Refs #289 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
infrahouse8
approved these changes
Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Puppet-side hardening for dpkg lock contention during provisioning. Root cause of #289 was in the AMI and is already fixed upstream (infrahouse/infrahouse-ubuntu-pro#20); this is the part that belongs here.
Behaviour changes are in
environments/developmentonly. Sandbox andmodules/follow after testing there.profile::apt_lock_timeout(new)Declared by
profile::reposwithstage => init, writes/etc/apt/apt.conf.d/99-lock-timeoutwith an unscopedDPkg::Lock::Timeout.Ubuntu's shipped default is
binary::apt::DPkg::Lock::Timeout "120"— scoped to theaptcommand only. Puppet's package provider, cloud-init and the AWS agents all shell out toapt-get, which inherits nothing and fails instantly on a held lock. Measured on noble / apt 2.8.3 against a held/var/lib/dpkg/lock-frontend:apt-get install, no optionsexit=100, elapsed 0s-oflagexit=0, elapsed 45s — waitedValue comes from the
apt_lock_timeoutcustom fact, defaulting to the 300 current AMIs ship.Two details worth reviewing:
infrahouse-ubuntu-pro'sprovision.shwrites the same file. Two drop-ins both setting the key would resolve by lexical filename order, so Puppet converges the AMI's file instead of racing a second one.Packageresources Puppet already evaluated. This is why it is a separate class rather than afileresource insideprofile::repos— only theaptclass declaration there carriesstage => init, not the profile itself.Exec[gha-boot-security-upgrade]is now boundedRetry logic moved into
gha-boot-security-upgrade.shwith a hard cumulative deadline (480s budget, exectimeout540) rather thantries/try_sleep.exec'stimeoutis per-attempt, sotriesmultiplies the worst case with no cumulative cap — the previoustimeout => 1200could consume the entire bootstrap lifecycle-hook budget in a single attempt. And an overrun is not merely a slow run:ih-puppetapplies with--detailed-exitcodesand exits 4/6 on a resource failure, which tripsih-bootstrap.sh'strap _ih_signal_abandon ERRinto an immediate ABANDON.Retries are still needed because
DPkg::Lock::Timeoutdoes not cover/var/lib/apt/lists/lock(verified:apt-get updatefails in ~1s with the option set) andunattended-upgraderefuses to run concurrently with itself.Comment correction (all three copies, no behaviour change)
profile::unattended_upgradesclaimed the base AMI ships those units masked. It does not — the masking comes fromterraform-aws-cloud-init'sbootcmd, on every boot, beforeruncmdstarts Puppet. That misattribution caused a wrong root-cause analysis, so the correction is applied everywhere rather than left to the promotion cycle. The unmask execs are load-bearing until infrahouse/terraform-aws-cloud-init#91 lands.Testing
puppet-lint --fail-on-warningsclean on all four module dirs;shellcheckclean.The script's bounding logic is verified with stubbed
apt-get/unattended-upgrade, 10/10 checks: gives up at its budget with exit 1 and no marker, retries through transient failure and writes the marker on success, no needless delay on the fast path, and — the important one — a hung command cannot overshoot the deadline (exits at 25s against a 600s sleep).The apt lock measurements above were taken on live EC2 instances from both the old and rebuilt AMIs.
Not yet tested: the real thing end to end on a provisioned
gha_runner. That is what development is for.Follow-ups
modules/after testingRefs #289
🤖 Generated with Claude Code