package host import ( "context" "encoding/json" "errors" "testing" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostfiles" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/hostrelocate" "github.com/carlosframework/platform/internal/hostwrite" ) // Finding 10, ruling 46: the install policy admits only the install // actions of this box's relocating op at installed or probing before P, // at the install stamp, with no other op's hold, and refuses a stale // capture. func TestRelocationInstallPolicyIsTyped(t *testing.T) { hold := "" held := func(context.Context, string) (string, error) { return hold, nil } policy := relocationInstallPolicy(held, relocationPolicy("dst")) c := &hostclaim.Claim{Host: "shop.example", Holder: &hostclaim.Holder{Incarnation: "inc", Stamp: 9}, Op: hostclaim.Op{Kind: hostclaim.KindRelocating, ID: "op1", Phase: hostclaim.PhaseInstalled, Exec: "dst", Participants: []string{"src", "dst"}, Journal: map[string]string{"install_stamp": "9", "prepared": "P1"}}} sel := hostbox.Selection{Host: "shop.example", Action: relocationActionInstall, Box: "dst", Op: "op1", Captured: hostguard.Captured{Incarnation: "inc", Stamp: 9}} for _, action := range []string{relocationActionInstall, relocationActionHeldConfig, relocationActionHeldRelease} { s := sel s.Action = action if err := policy(context.Background(), c, s, hostwrite.Identity{}); err != nil { t.Fatalf("%s: %v", action, err) } } hold = "op1" if err := policy(context.Background(), c, sel, hostwrite.Identity{}); err != nil { t.Fatalf("its own hold: %v", err) } for name, tweak := range map[string]func(*hostclaim.Claim, *hostbox.Selection){ "a probe action": func(_ *hostclaim.Claim, s *hostbox.Selection) { s.Action = "probe" }, "installing": func(c *hostclaim.Claim, _ *hostbox.Selection) { c.Op.Phase = hostclaim.PhaseInstalling }, "P opened": func(c *hostclaim.Claim, _ *hostbox.Selection) { c.Op.Journal["prepared_opened"] = "P1" }, "another executor": func(c *hostclaim.Claim, _ *hostbox.Selection) { c.Op.Exec = "src" }, "a stale capture": func(_ *hostclaim.Claim, s *hostbox.Selection) { s.Captured.Stamp = 8 }, "lost": func(c *hostclaim.Claim, _ *hostbox.Selection) { c.Lost = []string{"dst"} }, "not a participant": func(c *hostclaim.Claim, _ *hostbox.Selection) { c.Op.Participants = []string{"src"} }, "stuck": func(c *hostclaim.Claim, _ *hostbox.Selection) { c.Op.Journal["stuck"] = "x" }, } { raw, _ := json.Marshal(c) var copy hostclaim.Claim _ = json.Unmarshal(raw, ©) s := sel tweak(©, &s) if err := policy(context.Background(), ©, s, hostwrite.Identity{}); err == nil { t.Fatalf("%s: admitted", name) } } hold = "another-op" if err := policy(context.Background(), c, sel, hostwrite.Identity{}); err == nil { t.Fatal("another op's hold admitted") } } // Ruling 68, amended in Task 8c (absence is never evidence): a live // platform item is installed; a missing one is "none" only under a pin of // a host that never published files (no digest, no retained copy); a // missing one under a pin of published files, and a tombstone (which no // publisher writes) under either pin, refuse. func TestRelocationPlatformItemNeedsEvidenceForNone(t *testing.T) { published := hostrelocate.PinObject{Files: hostrelocate.FilesPin{Prefix: "loc/files/g-g1/", SHA256: "ab", Retained: "retained/ab"}} never := hostrelocate.PinObject{Files: hostrelocate.FilesPin{Prefix: "loc/files/g-g1/"}} live := hostfiles.Manifest{Platform: map[string]hostfiles.Entry{hostfiles.EnvItem: {Object: "o", SHA256: "cd", Size: 1, Seq: 1}}} missing := hostfiles.Manifest{Platform: map[string]hostfiles.Entry{}} tomb := hostfiles.Manifest{Platform: map[string]hostfiles.Entry{hostfiles.EnvItem: {Deleted: true, Seq: 2}}} for _, tc := range []struct { name string pin hostrelocate.PinObject m hostfiles.Manifest want bool err error }{ {"a live item", published, live, true, nil}, {"a live item, evidence or not", never, live, true, nil}, {"missing under published files", published, missing, false, hostrelocate.ErrRefused}, {"missing under a host that never published", never, missing, false, nil}, {"a tombstone under published files", published, tomb, false, hostrelocate.ErrRefused}, {"a tombstone under a host that never published", never, tomb, false, hostrelocate.ErrRefused}, } { got, err := relocationPlatformItem(tc.pin, tc.m, hostfiles.EnvItem) if got != tc.want || !errors.Is(err, tc.err) || (tc.err == nil) != (err == nil) { t.Errorf("%s: %v %v, want %v %v", tc.name, got, err, tc.want, tc.err) } } }