package activator import ( "context" "errors" "sync/atomic" "testing" "time" "github.com/carlosframework/platform/internal/registry" ) // Final review round 1, finding 6 (Astra): a failed wake's cleanup decides // whether to prove the host cgroup from the layout its launch used, not // from the layout the row reads at cleanup. A flat launch whose layout reads // fenced or conflicting by the time the cleanup runs is still proven, and so // is a nested launch whose layout no longer reads nested (unknown, fenced, // conflicting or flat): only a launch that was nested, and still reads // nested, keeps its own proof (ruling c). Here the host cgroup stays // populated, so a proven wake is retained and its lease kept. func TestAFailedWakeIsProvenByTheLayoutItsLaunchUsed(t *testing.T) { t.Parallel() for _, row := range []struct { name string launch, cleanup Layout retained, killed bool }{ {"flat, fenced at cleanup", LayoutFlat, LayoutFenced, true, true}, {"flat, conflicting at cleanup", LayoutFlat, LayoutConflict, true, true}, {"nested, fenced at cleanup", LayoutNested, LayoutFenced, true, true}, {"nested, flat at cleanup", LayoutNested, LayoutFlat, true, true}, {"nested, still nested", LayoutNested, LayoutNested, false, false}, } { t.Run(row.name, func(t *testing.T) { a, b, _, _ := testActivator(t, time.Hour) a.gens = false var launched atomic.Bool b.layoutFn = func(registry.Route) (Layout, string) { if launched.Load() { return row.cleanup, "changed after the launch" } return row.launch, "" } leader := &sigtermIgnorer{} leader.exited.Store(true) kb := &killGenBackend{fakeBackend: b, err: errNoGenContainment} ub := &unprovenStartBackend{killGenBackend: kb, fail: func(uint64) (Proc, error) { launched.Store(true) // the layout changes once the launch has run return leader, errors.New("health: no answer within 30s") }} hb := &uncontainedStartBackend{unprovenStartBackend: ub, populated: true} a.backend = hb rt := testRoute("f6-layout.example.com") if _, err := a.beginRequest(context.Background(), rt); err == nil { t.Fatal("the wake should have failed") } a.mu.Lock() _, retained := a.unprovenWakes[rt.Host] a.mu.Unlock() if retained != row.retained { t.Fatalf("retained = %v, want %v", retained, row.retained) } if asked := hb.hostKillCount() > 0; asked != row.killed { t.Fatalf("KillHost asked = %v, want %v", asked, row.killed) } }) } }