Old kernels removed by the box (R-1368, wrapper mode kernel-cleanup); security profiles refreshed after a kernel change (R-1388, mode profile-refresh); Go 1.27.4 (R-1391)
gates / gates (push) Successful in 40s
gates / gates (push) Successful in 40s
This commit is contained in:
1 parent
91a2924ad0
commit
cc63dd5204
11 files changed
+1542
-10
No files matched your search
@@ -28,13 +28,23 @@ func tonight(ring int) *hub.WireOSUpdate {
|
||||
func kernelCalls(w *fakeWrapper) []string {
|
||||
var m []string
|
||||
for _, p := range w.plans {
|
||||
if p["layer"] == LayerKernel {
|
||||
if p["layer"] == LayerKernel && p["mode"] != "kernel-cleanup" { // the kernel STEP's calls (the clean-up: cleanupCalls)
|
||||
m = append(m, p["mode"].(string))
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func cleanupCalls(w *fakeWrapper) []map[string]any {
|
||||
var m []map[string]any
|
||||
for _, p := range w.plans {
|
||||
if p["layer"] == LayerKernel && p["mode"] == "kernel-cleanup" {
|
||||
m = append(m, p)
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Ring 0, a told night: after the healthy host step the leg stages the pending kernel (select pending-kernel, the
|
||||
// kernel the household was told about), tells the hub "staged", THEN reboots — the kernel step ends the night.
|
||||
func TestKernel_Ring0ToldNightStagesThenReboots(t *testing.T) {
|
||||
@@ -326,3 +336,177 @@ func TestKernelSet(t *testing.T) {
|
||||
t.Fatal("a non-kernel string must give no set")
|
||||
}
|
||||
}
|
||||
|
||||
// ---- the clean-up of old kernels (R-1368, `09` §3 decision 286, `11` §5.11.1) ----
|
||||
|
||||
func cleanupRep(js string) map[string][]WrapperReport {
|
||||
return map[string][]WrapperReport{"kernel-cleanup": {{KernelCleanup: json.RawMessage(js)}}}
|
||||
}
|
||||
|
||||
// The night: the clean-up is the LAST call, after the host step, exactly once, and its plan names NO package.
|
||||
// COMPANION RED-PROOF (observed): make kernelCleanupSkip return "" before its checks → the BYO / debug / unhealthy
|
||||
// sub-cases of TestKernelCleanup_WhenItMayNotRun fail; put the call before runFast → this test's order check fails.
|
||||
func TestKernelCleanup_NightOrderOnceAndNoNames(t *testing.T) {
|
||||
w := &fakeWrapper{t: t, kernelRep: cleanupRep(`{"outcome":"removed","removed":["proxmox-kernel-7.0.14-20-pve-signed"],` +
|
||||
`"kept":{"7.0.14-22-pve":["running"],"7.0.2-6-pve":["from"]},"bytes_gained":1073741824,"leftovers":[]}`)}
|
||||
l, h := newLeg(t, w, &hub.WireOSUpdate{Ring: 0, Enabled: true})
|
||||
p := l.Run(context.Background(), 9201, "night")
|
||||
cc := cleanupCalls(w)
|
||||
if len(cc) != 1 {
|
||||
t.Fatalf("clean-up calls = %d, want exactly one a night", len(cc))
|
||||
}
|
||||
if last := w.plans[len(w.plans)-1]; last["mode"] != "kernel-cleanup" {
|
||||
t.Fatalf("the clean-up must be the night's last call (after the host step), last = %v", last)
|
||||
}
|
||||
hostAt, cleanAt := -1, -1
|
||||
for i, x := range w.plans {
|
||||
if x["layer"] == LayerHost {
|
||||
hostAt = i
|
||||
}
|
||||
if x["mode"] == "kernel-cleanup" {
|
||||
cleanAt = i
|
||||
}
|
||||
}
|
||||
if hostAt < 0 || cleanAt < hostAt {
|
||||
t.Fatalf("the clean-up ran before the host step: host %d clean-up %d", hostAt, cleanAt)
|
||||
}
|
||||
plan := cc[0]
|
||||
if pk, _ := json.Marshal(plan["packages"]); string(pk) != "[]" {
|
||||
t.Fatalf("the clean-up plan names packages: %s", pk)
|
||||
}
|
||||
for _, k := range []string{"select", "signed", "allow_new", "expect_kver", "dry_run"} {
|
||||
if _, ok := plan[k]; ok {
|
||||
t.Fatalf("the clean-up plan carries %q: %v", k, plan)
|
||||
}
|
||||
}
|
||||
if plan["lane"] != "slow" || plan["layer"] != LayerKernel {
|
||||
t.Fatalf("plan = %v", plan)
|
||||
}
|
||||
// the hub hears "cleaned" with what was removed, kept and gained — and NO kernel view / version (the hub would read
|
||||
// it as a kernel step for that kernel)
|
||||
rep := p.KernelCleanup
|
||||
if rep.Outcome != "cleaned" || !rep.Healthy || rep.Mode != "kernel-cleanup" || rep.Layer != LayerKernel {
|
||||
t.Fatalf("report = %+v", rep)
|
||||
}
|
||||
for _, want := range []string{"removed 1 old kernel package(s): proxmox-kernel-7.0.14-20-pve-signed", "kept 7.0.14-22-pve, 7.0.2-6-pve", "1.0 GiB gained"} {
|
||||
if !strings.Contains(rep.HealthReason, want) {
|
||||
t.Fatalf("report reason %q lacks %q", rep.HealthReason, want)
|
||||
}
|
||||
}
|
||||
lastRep := h.reports[len(h.reports)-1]
|
||||
if lastRep.Mode != "kernel-cleanup" || lastRep.Outcome != "cleaned" {
|
||||
t.Fatalf("the hub did not hear the clean-up: %+v", lastRep)
|
||||
}
|
||||
if len(lastRep.Kernel) != 0 || kverRE.MatchString(lastRep.ReleaseID) {
|
||||
t.Fatalf("the clean-up's report names a kernel (the hub reads that as a kernel step): %+v", lastRep)
|
||||
}
|
||||
// the same night again: the pass is skipped as a whole — never a second clean-up
|
||||
l.Run(context.Background(), 9201, "night")
|
||||
if len(cleanupCalls(w)) != 1 {
|
||||
t.Fatalf("a second clean-up in the same night: %d calls", len(cleanupCalls(w)))
|
||||
}
|
||||
}
|
||||
|
||||
// "Nothing to remove" and "not run" are log lines, never a report (one a night would bury the kernel step's own line).
|
||||
func TestKernelCleanup_NothingAndNotRunSendNoReport(t *testing.T) {
|
||||
for name, js := range map[string]string{
|
||||
"nothing": `{"outcome":"nothing","removal":[],"kept":{"7.0.14-22-pve":["running"]}}`,
|
||||
"not_run": `{"outcome":"not_run","not_run":[{"code":"no-ring0-mark","reason":"ring 0 only in this release"}]}`,
|
||||
} {
|
||||
w := &fakeWrapper{t: t, kernelRep: cleanupRep(js)}
|
||||
l, h := newLeg(t, w, &hub.WireOSUpdate{Ring: 1, Enabled: true})
|
||||
p := l.Run(context.Background(), 9201, "night")
|
||||
if len(cleanupCalls(w)) != 1 {
|
||||
t.Fatalf("%s: the wrapper was not asked", name)
|
||||
}
|
||||
if p.KernelCleanup.Layer != "" {
|
||||
t.Fatalf("%s: a report was made: %+v", name, p.KernelCleanup)
|
||||
}
|
||||
for _, r := range h.reports {
|
||||
if r.Mode == "kernel-cleanup" {
|
||||
t.Fatalf("%s: the hub got a clean-up report: %+v", name, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A refusal and a failure reach the hub (the operator), once, and are not retried.
|
||||
func TestKernelCleanup_RefusedAndFailedReachTheHub(t *testing.T) {
|
||||
for name, wr := range map[string]WrapperReport{
|
||||
"refused": {Refused: json.RawMessage(`{"code":"P3","reason":"the pinned default and grub.cfg disagree"}`)},
|
||||
"failed": {Failed: json.RawMessage(`{"rc":3,"step":"verify","reason":"the default in grub.cfg moved"}`), KernelCleanup: json.RawMessage(`{"outcome":"failed"}`)},
|
||||
} {
|
||||
w := &fakeWrapper{t: t, kernelRep: map[string][]WrapperReport{"kernel-cleanup": {wr}}}
|
||||
l, h := newLeg(t, w, &hub.WireOSUpdate{Ring: 0, Enabled: true})
|
||||
p := l.Run(context.Background(), 9201, "night")
|
||||
if p.KernelCleanup.Outcome != name || p.KernelCleanup.Healthy || len(p.KernelCleanup.Refused) == 0 {
|
||||
t.Fatalf("%s: report = %+v", name, p.KernelCleanup)
|
||||
}
|
||||
if last := h.reports[len(h.reports)-1]; last.Mode != "kernel-cleanup" || last.Outcome != name {
|
||||
t.Fatalf("%s: the hub did not hear it: %+v", name, last)
|
||||
}
|
||||
if len(cleanupCalls(w)) != 1 {
|
||||
t.Fatalf("%s: retried the same night (%d calls)", name, len(cleanupCalls(w)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When it may NOT run: never with a kernel step in the night (due, staged, or reported), not in a debug pass, not on a
|
||||
// BYO host, not with OS updates off, not after an unhealthy host step.
|
||||
func TestKernelCleanup_WhenItMayNotRun(t *testing.T) {
|
||||
type tc struct {
|
||||
blk *hub.WireOSUpdate
|
||||
trigger string
|
||||
prep func(*fakeWrapper, *Leg)
|
||||
}
|
||||
bad := &Health{DockerOK: false}
|
||||
cases := map[string]tc{
|
||||
"a kernel step is due tonight (ring 0)": {tonight(0), "night", func(w *fakeWrapper, _ *Leg) {
|
||||
w.kernelRep = map[string][]WrapperReport{"kernel-status": {{Kernel: kview("none")}},
|
||||
"apply": {{Upgraded: []Package{{Name: "proxmox-kernel-7.0", Version: "7.0.14-22"}}, Authority: "ring0", Kernel: kview("staged")}},
|
||||
"kernel-reboot": {{Kernel: kview("oneshot")}}}
|
||||
}},
|
||||
"a kernel step is due tonight (ring 1, nothing staged)": {tonight(1), "night", func(w *fakeWrapper, _ *Leg) {
|
||||
w.kernelRep = map[string][]WrapperReport{"kernel-status": {{Kernel: kview("none")}}}
|
||||
}},
|
||||
"a debug pass": {&hub.WireOSUpdate{Ring: 0, Enabled: true}, "debug", nil},
|
||||
"a BYO host": {&hub.WireOSUpdate{Ring: 0, Enabled: true}, "night", func(_ *fakeWrapper, l *Leg) { l.Appliance = false }},
|
||||
"updates off": {&hub.WireOSUpdate{Ring: 0, Enabled: false}, "night", nil},
|
||||
"host unhealthy": {&hub.WireOSUpdate{Ring: 0, Enabled: true, HostRelease: &hub.WireOSRelease{ID: "os-host-1", Packages: []hub.WireOSPackage{{Name: "openssl", Version: "u3", Origin: "Debian"}}}}, "night", func(w *fakeWrapper, _ *Leg) {
|
||||
w.applyRep[LayerHost] = WrapperReport{Upgraded: []Package{{Name: "openssl", Version: "u3"}}, HealthAfter: &Health{HostServices: map[string]string{"pveproxy": "failed"}, GuestRunning: yes(), Guest: bad}}
|
||||
w.healthSeq[LayerHost] = []*Health{{HostServices: map[string]string{"pveproxy": "failed"}, GuestRunning: yes(), Guest: bad}}
|
||||
}},
|
||||
}
|
||||
for name, c := range cases {
|
||||
w := &fakeWrapper{t: t}
|
||||
l, _ := newLeg(t, w, c.blk)
|
||||
if c.prep != nil {
|
||||
c.prep(w, l)
|
||||
}
|
||||
l.HealthWait = time.Second
|
||||
l.Run(context.Background(), 9201, c.trigger)
|
||||
if n := len(cleanupCalls(w)); n != 0 {
|
||||
t.Fatalf("%s: the clean-up ran (%d call(s)); calls = %s", name, n, calls(w))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The dry run is the same mode with dry_run: true — and it sends nothing to the hub.
|
||||
func TestKernelCleanup_DryRun(t *testing.T) {
|
||||
w := &fakeWrapper{t: t, kernelRep: cleanupRep(`{"outcome":"dry_run","would":"remove","removal":["proxmox-kernel-7.0.14-20-pve-signed"]}`)}
|
||||
l, h := newLeg(t, w, &hub.WireOSUpdate{Ring: 0, Enabled: true})
|
||||
raw, err := l.KernelCleanupDryRun(context.Background(), 9201)
|
||||
if err != nil || !strings.Contains(string(raw), `"would":"remove"`) {
|
||||
t.Fatalf("dry run = %s, %v", raw, err)
|
||||
}
|
||||
cc := cleanupCalls(w)
|
||||
if len(cc) != 1 || cc[0]["dry_run"] != true {
|
||||
t.Fatalf("the dry run's plan = %v", cc)
|
||||
}
|
||||
if pk, _ := json.Marshal(cc[0]["packages"]); string(pk) != "[]" {
|
||||
t.Fatalf("the dry run's plan names packages: %s", pk)
|
||||
}
|
||||
if len(h.reports) != 0 {
|
||||
t.Fatalf("a dry run reported to the hub: %+v", h.reports)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user