diff --git a/bosh-stemcell/spec/stemcells/warden_spec.rb b/bosh-stemcell/spec/stemcells/warden_spec.rb index df2d074f2a..897d5644ad 100644 --- a/bosh-stemcell/spec/stemcells/warden_spec.rb +++ b/bosh-stemcell/spec/stemcells/warden_spec.rb @@ -58,4 +58,34 @@ its(:content) { should include('"UseMonitIptablesFirewall": true') } end end + + context "systemd unit cleanup for warden containers" do + # The Docker CPI runs warden stemcells with `exec /sbin/init`. base_warden + # strips non-essential stock systemd units from the boot sequence so they + # don't contend with the monit-managed bpm jobs in the BOSH director + # container (symptom: postgres role never created, bosh/0 never converges). + # Keep-list mirrors the historical Docker CPI allow-list. See + # base_warden/apply.sh and cloudfoundry/bosh-docker-cpi-release#60. + keep_patterns = %w[ + *bosh-agent* *dbus* *journald* *logrotate* *runit* *ssh* + *systemd-user-sessions* *systemd-tmpfiles* + ] + not_name = keep_patterns.map { |g| "-not -name '#{g}'" }.join(" ") + wants = "find /etc/systemd/system /lib/systemd/system -type l -path '*.wants/*'" + + describe "non-essential units are removed from the boot sequence" do + describe command("#{wants} #{not_name}") do + its(:exit_status) { should eq 0 } + its(:stdout) { should eq "" } + end + end + + describe "essential units are preserved (guards against an over-broad prune)" do + describe command(wants) do + its(:exit_status) { should eq 0 } + its(:stdout) { should include("runit.service") } + its(:stdout) { should include("ssh.service") } + end + end + end end diff --git a/stemcell_builder/stages/base_warden/apply.sh b/stemcell_builder/stages/base_warden/apply.sh index 7d1844495a..b19949d5fd 100755 --- a/stemcell_builder/stages/base_warden/apply.sh +++ b/stemcell_builder/stages/base_warden/apply.sh @@ -85,3 +85,33 @@ JSON # Mask systemd-binfmt.service which fails under Rosetta emulation run_in_chroot "$chroot" "systemctl mask systemd-binfmt.service" + +# Trim non-essential systemd units from the boot sequence. +# +# When the Docker CPI runs a warden stemcell with `exec /sbin/init`, full stock +# systemd comes up and its units contend with the monit-managed bpm jobs in the +# BOSH director container (symptom: the postgres role is never created and +# bosh/0 never converges). The Docker CPI used to strip these units at +# container-create time, but that allow-list prune was removed in +# cloudfoundry/bosh-docker-cpi-release#60 on the assumption the stemcell would +# take it over. The jammy stemcell only ever picked up the systemd-binfmt mask +# above, so we reproduce the CPI's full allow-list here. +# +# This removes the `.wants` symlinks (equivalent to `systemctl disable`): units +# are dropped from the boot sequence but can still start as dependencies of a +# kept unit. The keep patterns mirror the historical CPI list exactly. Deriving +# the set at build time keeps it correct as the stemcell's package set changes. +run_in_chroot "$chroot" " +find /etc/systemd/system /lib/systemd/system \ + -type l \ + -path '*.wants/*' \ + -not -name '*bosh-agent*' \ + -not -name '*dbus*' \ + -not -name '*journald*' \ + -not -name '*logrotate*' \ + -not -name '*runit*' \ + -not -name '*ssh*' \ + -not -name '*systemd-user-sessions*' \ + -not -name '*systemd-tmpfiles*' \ + -delete +"