package osupdates import ( "strings" "testing" "time" ) // R-812 option A (`09` ยง3 decision 163): the Proxmox package set โ€” approved only by the operator's button, after every // ring-0 box ran it in healthy night steps (the host health rule, judged on the box), never a kernel, never pushed to // ring 1 by the desired state (a signed os_pve_step only). var pveSet = []Package{{Name: "pve-manager", Version: "9.2.21", Origin: "Proxmox"}, {Name: "qemu-server", Version: "9.0.9", Origin: "Proxmox"}} func (f *fix) pveNight(t *testing.T, host string, healthy bool) { t.Helper() out := "nothing" if !healthy { out = "health_failed" } f.ingest(t, host, Report{Layer: LayerPVE, Trigger: "night", Mode: "apply", Outcome: out, Healthy: healthy, Installed: append([]Package{pk("libc6", "x"), {Name: "proxmox-kernel-helper", Version: "9.0.6", Origin: "Proxmox"}}, pveSet...)}) } // COMPANION RED-PROOF (observed): add LayerPVE to Layers (the auto-approved list) โ†’ this fails. func TestPVE_NeverAutoApproved(t *testing.T) { f := newFix(t) for i := 0; i < 3; i++ { f.pveNight(t, "hp", true) f.pveNight(t, "n100", true) f.now = f.now.Add(25 * time.Hour) f.s.Evaluate() } if rel, _ := f.s.Store.LatestOSRelease(LayerPVE); rel != nil { t.Fatalf("a Proxmox set was auto-approved: %+v", rel) } } // The button works only after two healthy nights on every ring-0 box; the release holds Proxmox userspace only (no // kernel name, no Debian package) and nudges no box. // // COMPANION RED-PROOF (observed): drop the hostSlowRE check for the pve layer in candidate โ†’ "proxmox-kernel-helper". func TestPVE_ApproveNeedsTwoHealthyNightsOnEveryRing0Box(t *testing.T) { f := newFix(t) f.pveNight(t, "hp", true) f.pveNight(t, "n100", true) if _, err := f.s.ApprovePVE(); err == nil || !strings.Contains(err.Error(), "1 of 2") { t.Fatalf("approved after one night: %v", err) } f.now = f.now.Add(24 * time.Hour) f.pveNight(t, "hp", true) f.pveNight(t, "n100", true) id, err := f.s.ApprovePVE() if err != nil || !strings.HasPrefix(id, "os-pve-") { t.Fatalf("%q %v", id, err) } rel, _ := f.s.Store.LatestOSRelease(LayerPVE) if rel.ApprovedBy != "operator" || !strings.Contains(rel.PackagesJSON, "pve-manager") || strings.Contains(rel.PackagesJSON, "libc6") || strings.Contains(rel.PackagesJSON, "proxmox-kernel") { t.Fatalf("release %+v", rel) } if len(f.bumps) != 0 { t.Fatalf("a Proxmox approval must nudge no box (ring 1 takes it by a signed job): %v", f.bumps) } if b := f.s.DesiredBlock("cust1"); b.Release != nil || b.HostRelease != nil { t.Fatalf("the Proxmox set leaked into the desired block: %+v", b) } } func TestPVE_UnhealthyStepBlocksTheButton(t *testing.T) { f := newFix(t) f.pveNight(t, "hp", true) f.pveNight(t, "n100", true) f.now = f.now.Add(24 * time.Hour) f.pveNight(t, "hp", false) f.pveNight(t, "n100", true) if _, err := f.s.ApprovePVE(); err == nil || !strings.Contains(err.Error(), "health_failed") { t.Fatalf("an unhealthy Proxmox step did not block: %v", err) } } // The System page lists the Proxmox candidate beside the Docker one. func TestPVE_InTheCandidates(t *testing.T) { f := newFix(t) f.pveNight(t, "hp", true) f.pveNight(t, "n100", true) found := false for _, c := range f.s.Candidates() { if c.Layer == LayerPVE && c.Packages == 2 { found = true } } if !found { t.Fatalf("no pve candidate with 2 packages: %+v", f.s.Candidates()) } }