| 1 | package host |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "encoding/json" |
| 6 | "testing" |
| 7 | "time" |
| 8 | |
| 9 | "github.com/carlosframework/platform/internal/hostbox" |
| 10 | "github.com/carlosframework/platform/internal/hostclaim" |
| 11 | execlease "github.com/carlosframework/platform/internal/hostexec" |
| 12 | "github.com/carlosframework/platform/internal/hostguard" |
| 13 | ) |
| 14 | |
| 15 | func TestPlan14OrdinaryDeleteParticipation(t *testing.T) { |
| 16 | t.Parallel() |
| 17 | for _, mode := range []string{"live", "stopped", "parking", "retired", "serving stopped", "pending", "record present", "delete absent", "etag", "key", "enrollment", "foreign home", "session", "stamp", "op", "box", "inventory"} { |
| 18 | t.Run(mode, func(t *testing.T) { |
| 19 | f, root, iq := plan14InstallRegistration(t, hostclaim.KindServing, "") |
| 20 | if _, err := root.commitRootInventory(t.Context(), iq); err != nil { |
| 21 | t.Fatal(err) |
| 22 | } |
| 23 | actor, _ := root.InventoryActor(t.Context()) |
| 24 | actor.FleetHomeRegion, actor.HomeRegion = "ie", "ie" |
| 25 | root.InventoryActor = func(context.Context) (hostbox.Actor, error) { return actor, nil } |
| 26 | o, err := hostclaim.Update(t.Context(), f.claims, f.read(), hostclaim.AdmitWriter(hostclaim.Admission{ObservedSession: f.read().Claim.Holder.Session, ObservedGeneration: f.read().Claim.Holder.Generation, Box: actor.Box, Region: "ie", Mode: "cold", NewGeneration: "delete-generation", KeepStamp: true})) |
| 27 | if err != nil { |
| 28 | t.Fatal(err) |
| 29 | } |
| 30 | target := iq.Target |
| 31 | target.Session, target.ClaimETag, target.ClaimGen = o.Claim.Holder.Session, o.ETag, o.Claim.Gen |
| 32 | target.FenceID, target.Region, target.BootID, target.ControllerID, target.DirectoryRevision, target.RosterRevision = "delete-stop", "ie", "boot", "controller", "1", 1 |
| 33 | if mode != "live" { |
| 34 | if target, err = root.Stop.Coordinator.Guard.BeginFence(t.Context(), target); err != nil { |
| 35 | t.Fatal(err) |
| 36 | } |
| 37 | if mode != "pending" { |
| 38 | at := time.Now().UTC() |
| 39 | receipt := StopReceipt{V: 1, Kind: "stopped", ID: "delete-stop-receipt", FenceID: target.FenceID, Host: target.Host, Incarnation: target.Incarnation, InstallationStamp: target.Stamp, WriterSession: target.Session, Region: target.Region, ClaimETag: target.ClaimETag, ClaimGen: target.ClaimGen, Box: target.Box, Enrollment: target.Enrollment, BootID: target.BootID, ControllerID: target.ControllerID, DirectoryRevision: testReceiptNumeral(target), Binding: target.DirectoryRevision, EvidenceAt: at} |
| 40 | raw, _ := json.Marshal(receipt) |
| 41 | if err = root.Stop.Coordinator.Guard.CompleteFence(t.Context(), target, raw, at, func(ctx context.Context, t hostguard.FenceTarget, b []byte, at time.Time) error { |
| 42 | return verifyRetirementReceipt(ctx, hostguard.FenceReceipt{Target: t, Bytes: b, EvidenceAt: at}) |
| 43 | }); err != nil { |
| 44 | t.Fatal(err) |
| 45 | } |
| 46 | } |
| 47 | } |
| 48 | if mode != "serving stopped" { |
| 49 | o, err = hostclaim.Update(t.Context(), f.claims, f.read(), hostclaim.BeginDelete("delete", "console", iq.RecordETag, time.Now(), []string{"ie"}, []string{actor.Box}, "")) |
| 50 | if err != nil { |
| 51 | t.Fatal(err) |
| 52 | } |
| 53 | } |
| 54 | if mode == "parking" || mode == "retired" || mode == "record present" { |
| 55 | o, err = hostclaim.Update(t.Context(), f.claims, o, hostclaim.ToScopedParking("delete", []string{"ie"}, []string{actor.Box}, f.newID)) |
| 56 | if err != nil { |
| 57 | t.Fatal(err) |
| 58 | } |
| 59 | } |
| 60 | if mode == "parking" || mode == "retired" || mode == "delete absent" { |
| 61 | if err = f.claims.Delete(t.Context(), iq.RecordKey); err != nil { |
| 62 | t.Fatal(err) |
| 63 | } |
| 64 | } |
| 65 | if mode == "retired" { |
| 66 | if err = root.Stop.Coordinator.Guard.RetireInstallationWith(t.Context(), o.Host, hostguard.Captured{Incarnation: target.Incarnation, Stamp: target.Stamp}, func(*hostclaim.Claim) error { return nil }, func() error { return nil }); err != nil { |
| 67 | t.Fatal(err) |
| 68 | } |
| 69 | } |
| 70 | if mode == "etag" || mode == "key" { |
| 71 | o, err = hostclaim.Update(t.Context(), f.claims, o, func(c *hostclaim.Claim) error { c.Op.Intent[mode] = "forged"; return nil }) |
| 72 | if err != nil { |
| 73 | t.Fatal(err) |
| 74 | } |
| 75 | } |
| 76 | q := execlease.ExecutionRequest{Schema: 1, ID: "delete-participation", Operation: "executionBegin", Host: o.Host, Incarnation: target.Incarnation, Stamp: target.Stamp, Session: target.Session, Op: o.Claim.Op.ID} |
| 77 | switch mode { |
| 78 | case "enrollment": |
| 79 | actor.Enrollment = "foreign" |
| 80 | case "foreign home": |
| 81 | actor.FleetHomeRegion = "uk" |
| 82 | case "session": |
| 83 | q.Session++ |
| 84 | case "stamp": |
| 85 | q.Stamp++ |
| 86 | case "op": |
| 87 | q.Op = "foreign" |
| 88 | case "box": |
| 89 | actor.Box = "foreign" |
| 90 | case "inventory": |
| 91 | if err = root.Journals.Inventory.WriteStopInventory(t.Context(), iq.Inventory); err != nil { |
| 92 | t.Fatal(err) |
| 93 | } |
| 94 | } |
| 95 | a, err := root.executionAuthority(t.Context(), q, execlease.ControllerProcess{}) |
| 96 | want := mode == "live" || mode == "stopped" || mode == "parking" || mode == "retired" || mode == "delete absent" |
| 97 | if want { |
| 98 | if err != nil || !a.ParticipationOnly || a.Installation == "" || a.RecordRevision != iq.RecordETag { |
| 99 | t.Fatalf("participation: %+v %v", a, err) |
| 100 | } |
| 101 | } else if err == nil { |
| 102 | t.Fatalf("unsafe participation: %+v", a) |
| 103 | } |
| 104 | if mode == "retired" { |
| 105 | root.Stop.Coordinator.Kernel = f.kernel |
| 106 | f.kernel.noProcess = true |
| 107 | if err := root.cleanupExecution(t.Context(), execlease.ExecutionRecord{Request: q, Authority: a}); err != nil || f.kernel.noProcessCalls != 1 { |
| 108 | t.Fatalf("retired participation cleanup: %v, kernel calls=%d", err, f.kernel.noProcessCalls) |
| 109 | } |
| 110 | a.Enrollment = "foreign" |
| 111 | if err := root.cleanupExecution(t.Context(), execlease.ExecutionRecord{Request: q, Authority: a}); err == nil || f.kernel.noProcessCalls != 1 { |
| 112 | t.Fatalf("foreign retired cleanup: %v, kernel calls=%d", err, f.kernel.noProcessCalls) |
| 113 | } |
| 114 | } |
| 115 | }) |
| 116 | } |
| 117 | } |
| 118 | |