package host import ( "errors" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/lease" "github.com/carlosframework/platform/internal/park" "strings" "testing" "time" ) func plan12Retired(t *testing.T) hostclaim.Observed { t.Helper() t.Setenv("CARLOS_HOST_CLAIM_RETIRED_LOCATIONS", "1") f := newRecoveryFixture(t) o := f.read() var err error o, err = hostclaim.Update(t.Context(), f.claims, o, func(c *hostclaim.Claim) error { c.Holder.Location = "instances/" + o.Host c.Touched = hostclaim.Touched{Complete: true} return hostclaim.BeginDelete("delete", "console", "record", f.now, nil, nil, "")(c) }) if err != nil { t.Fatal(err) } o, err = hostclaim.Update(t.Context(), f.claims, o, hostclaim.ToParking("delete", nil, nil, f.newID)) if err != nil { t.Fatal(err) } o, err = hostclaim.Update(t.Context(), f.claims, o, hostclaim.FreeAfterPark("delete")) if err != nil { t.Fatal(err) } return o } func TestPlan12ArchivedIncarnationCensus(t *testing.T) { for _, mode := range []string{"legacy", "old-incarnation", "missing-history", "qualified-beyond-history", "ambiguous-legacy"} { t.Run(mode, func(t *testing.T) { o := plan12Retired(t) inc := o.Claim.RetiredIncarnations[0] loc := "instances/" + o.Host if mode == "old-incarnation" { loc += "/" + inc } if mode == "missing-history" { o.Claim.RetiredLocations = nil } if mode == "qualified-beyond-history" { loc += "/" + strings.Repeat("b", 64) } if mode == "ambiguous-legacy" { other := strings.Repeat("b", 64) o.Claim.RetiredIncarnations = append(o.Claim.RetiredIncarnations, other) o.Claim.RetiredLocations[other] = loc } versions := []lease.ObjectVersion{{Key: loc + "/0000/db", VersionID: "v1"}} labels, err := ClaimRetirementCensus(o, versions) if mode == "qualified-beyond-history" { inc = strings.Repeat("b", 64) } if mode == "legacy" || mode == "old-incarnation" || mode == "qualified-beyond-history" { if err != nil || labels[loc] != inc { t.Fatal(labels, err) } } else { var dependency *ClaimDependencyError if !errors.As(err, &dependency) { t.Fatal("not a typed dependency", err) } } }) } } func TestPlan12LegacyAndMovesHaveIndependentLabels(t *testing.T) { o := plan12Retired(t) legacy := o.Claim.RetiredIncarnations[0] move := strings.Repeat("c", 64) versions := []lease.ObjectVersion{{Key: "instances/" + o.Host + "/0000/db", VersionID: "v1"}, {Key: "moves/" + o.Host + "/op/replica/db", VersionID: "v2"}} view := park.View{ClaimETag: o.ETag, Region: "ie", ArchivedIncarnations: claimArchivedLocations(o.Claim)} d, err := park.Decide(o, view, versions, move) if err != nil { t.Fatal(err) } if len(d.Candidates) != 2 { t.Fatal(d) } for _, c := range d.Candidates { want := move if strings.HasPrefix(c.Location, "instances/") { want = legacy } if c.Incarnation != want { t.Fatal(c) } } p := productionClaimRegional{Now: func() time.Time { return time.Unix(100, 0) }} label, at, err := p.retirement(t.Context(), o, versions[:1]) if err == nil || label != "" || !at.IsZero() { t.Fatal(label, at, err) } } func TestPlan12HolderResidueInOtherRegion(t *testing.T) { t.Parallel() f := newRecoveryFixture(t) o := f.read() c := o.Claim c.Holder.Location = "instances/" + o.Host c.Holder.Generation = "live" loc := c.Holder.Location versions := []lease.ObjectVersion{{Key: loc + "/live/db", VersionID: "v1"}} labels, err := ClaimRetirementCensus(o, versions) if err != nil || labels[loc] != c.Holder.Incarnation { t.Fatal(labels, err) } view := park.View{ClaimETag: o.ETag, Region: "uk", AuthoritativeRegion: "ie", ArchivedIncarnations: labels} d, err := park.Decide(o, view, versions, "") if err != nil || len(d.Candidates) != 1 || d.Candidates[0].Incarnation != c.Holder.Incarnation { t.Fatal(d, err) } view.Region = "ie" view.MarkerKnown = true view.MarkerGeneration = "live" view.GenerationOf = func(park.Ref) (string, bool) { return "live", true } d, err = park.Decide(o, view, versions, "") if err != nil || len(d.Candidates) != 0 { t.Fatal("live generation swept", d, err) } } func TestPlan12CensusNamespacesAndFreeLanes(t *testing.T) { o := plan12Retired(t) o.Claim.QuietAfter = map[string]time.Time{"ie": time.Unix(10, 0), "uk": time.Unix(20, 0)} proof, err := claimFreeMoveProof(o) if err != nil || proof.Check(o, o.Claim.RetiredIncarnations[0], time.Unix(21, 0)) != nil { t.Fatal(proof, err) } if proof.Check(o, proof.Incarnation, time.Unix(20, 0)) == nil { t.Fatal("accepted listing before retirement") } delete(o.Claim.RetiredLocations, o.Claim.RetiredIncarnations[0]) v := []lease.ObjectVersion{{Key: "instances/" + o.Host + "/0000/db", VersionID: "one"}, {Key: "moves/" + o.Host + "/move/db", VersionID: "two"}} p := productionClaimRegional{Now: func() time.Time { return time.Unix(30, 0) }} label, _, err := p.retirement(t.Context(), o, v) if err != nil || label != proof.Incarnation { t.Fatal("legacy refusal blocked lanes", err) } view := park.View{ClaimETag: o.ETag, Region: "ie", RefusedLocations: map[string]error{"instances/" + o.Host: claimRetirementDependency()}} d, err := park.Decide(o, view, v, label) if err != nil || len(d.Candidates) != 1 || d.Candidates[0].Location != "moves/"+o.Host { t.Fatal(d, err) } o.Claim.QuietAfter = nil if _, err := claimFreeMoveProof(o); err == nil { t.Fatal("invented retirement bound") } } func TestPlan12QualifiedIdentityBeyondHistoryCap(t *testing.T) { o := plan12Retired(t) loc := "instances/" + o.Host + "/" + strings.Repeat("e", 64) labels, err := ClaimRetirementCensus(o, []lease.ObjectVersion{{Key: loc + "/g/db", VersionID: "v"}}) if err != nil || labels[loc] != strings.Repeat("e", 64) { t.Fatal(labels, err) } }