package host import ( "context" "encoding/json" "errors" "slices" "testing" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/park" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/release" ) // Moves into the home region, part 2, Task 2: the fixtures for a home // target box. intoHome mirrors fromHome for the target: the source is // ca-box in ca, the target home-box with placement "", whose roster name is // ie and whose fleet designates ie the home. The witnesses of Tasks 3 to 5 // stand on these fixtures, so this test pins what each one is. var _ release.Store = p1FilesStore{} // TestIntoHomeFixtureIsAHomeTarget: the record's move names the home's // code, the target's edges file publishes no placement, the claim's op // names the placement-derived @home with home_region ie, the target's held // environment sets no CARLOS_REGION, and the close harness's production // probe mode admits on home-box in the claim engine's region ie. func TestIntoHomeFixtureIsAHomeTarget(t *testing.T) { ctx := context.Background() t.Run("the move, the edges file, the claim and the held env", func(t *testing.T) { h := newPhase1Harness(t, intoHome) rec := h.start(t, "manual") if rec.Move == nil || rec.Move.To != "" || rec.Move.From != "ca" || rec.Region != "ca" { t.Fatalf("the record: region %q move %+v", rec.Region, rec.Move) } body, _, err := h.s.Get(ctx, "control/edges/home-box.json") if err != nil { t.Fatal(err) } var edges map[string]any if err := json.Unmarshal(body, &edges); err != nil { t.Fatal(err) } if _, ok := edges["placement"]; ok || edges["data_bucket"] != "data-home" || edges["box"] != "home-box" { t.Fatalf("the home box's edges file: %s", body) } if _, _, err := h.s.Get(ctx, "control/edges/uk-box.json"); err == nil { t.Fatal("a uk-box edges file is still published beside the home box") } c := h.claim(t) if c.Op.Kind != hostclaim.KindMoving || c.Op.Intent["to"] != park.HomeRegion || c.Op.Intent["home_region"] != "ie" || c.Op.Intent["source"] != "ca-box" || c.Op.Intent["target"] != "home-box" || c.Op.Exec != "ca-box" { t.Fatalf("the claim's op: %+v", c.Op) } if c.Op.Intent["source_enrollment"] != p1SrcEnrollment || c.Op.Intent["target_enrollment"] != p1DstEnrollment || c.Op.Intent["admission_id"] != p1ID { t.Fatalf("the op was not begun as the claim-mode door begins it: %+v", c.Op.Intent) } a := h.roster[h.dstBox] if a.Region != "ie" || a.FleetHomeRegion != "ie" || a.HomeRegion != "ie" || h.roster[h.srcBox].Region != "ca" || h.roster[h.srcBox].HomeRegion != "" { t.Fatalf("the roster: %+v", h.roster) } tc := h.targetCfg() if tc.Region != "" || tc.Box != "home-box" || tc.OwnBucket != "data-home" || tc.OwnBucketRegion != "eu-west-1" || tc.ParkHomeRegion != "ie" { t.Fatalf("the target's configuration: region %q box %q bucket %q/%q park home %q", tc.Region, tc.Box, tc.OwnBucket, tc.OwnBucketRegion, tc.ParkHomeRegion) } env, _, _, _, err := tc.ComposeHeldEnv(ctx, h.s, rec, nil) if err != nil { t.Fatal(err) } if _, ok := env["CARLOS_REGION"]; ok { t.Fatal("the home target's held env sets CARLOS_REGION") } ch := newCutHarness(t, intoHome) env, _, _, _, err = ch.target().ComposeHeldEnv(ctx, ch.s, rec, nil) if err != nil { t.Fatal(err) } if _, ok := env["CARLOS_REGION"]; ok { t.Fatal("the cut harness's home target sets CARLOS_REGION") } // The default fixture is unchanged: uk sets its own region. named := newPhase1Harness(t) env, _, _, _, err = named.targetCfg().ComposeHeldEnv(ctx, named.s, rec, nil) if err != nil || env["CARLOS_REGION"] != "uk" { t.Fatalf("the named target's held env: %v %q", err, env["CARLOS_REGION"]) } }) t.Run("retained state in either spelling", func(t *testing.T) { for _, spelling := range []string{"ie", park.HomeRegion} { h := newPhase1Harness(t, intoHome, retainTouched(spelling), retainQuiet(spelling), retainChore("ie")) h.start(t, "manual") c := h.claim(t) if !containsString(c.Touched.Regions, spelling) || !containsString(c.Touched.Regions, "ca") { t.Fatalf("%s: touched %+v", spelling, c.Touched) } if _, ok := c.QuietAfter[spelling]; !ok || len(c.QuietAfter) != 1 { t.Fatalf("%s: quiet_after %v", spelling, c.QuietAfter) } var chore *hostclaim.Obligation for _, o := range c.Obligations { if o.Op == retainedChoreOp { chore = &o } } if chore == nil || chore.Region != "ie" || chore.Owner != "region:ie" || chore.Kind != "park-generation" || chore.Generation != retainedChoreGeneration || chore.Park == nil || chore.Park.Location != c.Holder.Location || chore.Park.Incarnation != h.inc { t.Fatalf("%s: the earlier residue chore: %+v", spelling, c.Obligations) } if c.Op.Intent["to"] != park.HomeRegion { t.Fatalf("%s: the op's to: %+v", spelling, c.Op.Intent) } } }) t.Run("the production probe admits on home-box in ie", func(t *testing.T) { h := newCloseHarness(t, intoHome, realLaunchEnv) if m := h.read(t, p1Host).Move; m.To != "" || m.State != provision.MoveLanding || m.LandingPhase != provision.LandRestored { t.Fatalf("the close harness: %+v", m) } p := h.productionProbe(t) h.land(t) if len(p.admissions) != 1 { t.Fatalf("admissions through the claim runtime: %+v", p.admissions) } a := p.admissions[0] if a.Box != "home-box" || a.Region != "ie" || a.PredecessorRegion != "ca" || a.Mode != "cold" || a.Generation != p1ID || a.Coverage != "sealed" { t.Fatalf("the probe's admission: %+v", a) } if p.owners.Box != "home-box" || p.owners.Region != "ie" || p.owners.HomeRegion != "ie" || p.owners.FleetHomeRegion != "ie" { t.Fatalf("the owners' identity: box %q region %q home %q fleet %q", p.owners.Box, p.owners.Region, p.owners.HomeRegion, p.owners.FleetHomeRegion) } c := h.claim(t) if c.Holder.WriterBox != "home-box" || c.Op.Intent["to"] != park.HomeRegion { t.Fatalf("the claim after the probe: writer %q to %q", c.Holder.WriterBox, c.Op.Intent["to"]) } // The writer's evidence was the owners': asked once, for the one // admission. if p.evidence != 1 { t.Fatalf("the writer's admission evidence ran %d times, want 1", p.evidence) } // Materialize ran the production owners' held steps, in order. if want := []string{"render", "select:held-config", "select:held-release", "release:v7"}; len(p.order) < len(want) || !slices.Equal(p.order[:len(want)], want) { t.Fatalf("the owners' held steps: %v", p.order) } h.committedAndOpen(t) }) t.Run("rejected admission evidence admits nothing", func(t *testing.T) { h := newCloseHarness(t, intoHome, realLaunchEnv) p := h.productionProbe(t) before := h.claim(t).Holder p.rejectEvidence = hostbox.ErrChanged h.land(t) if p.evidence == 0 { t.Fatal("the writer never asked for admission evidence") } after := h.claim(t).Holder if len(p.admissions) != 0 || len(after.Admissions) != len(before.Admissions) || after.Session != before.Session || after.WriterBox == "home-box" { t.Fatalf("an admission went through rejected evidence: %+v (before %+v)", after, before) } if m := h.read(t, p1Host).Move; m.State != provision.MoveLanding || m.LandingPhase != provision.LandProbing { t.Fatalf("the landing moved past probing: %+v", m) } // The owners' own evidence refuses an admission Select never made: // the dispatch the writer calls is the owners'. p.rejectEvidence = nil o, err := hostclaim.Read(t.Context(), h.s, p1Host) if err != nil { t.Fatal(err) } in, err := p.owners.Select(t.Context(), h.route, o, poProbe) if err != nil { t.Fatal(err) } if err := p.writerEvidence(t.Context(), o, in.Admission, in.Baseline); err != nil { t.Fatalf("the writer's evidence refused the owners' own admission: %v", err) } bad := in.Admission bad.Box = "ca-box" if err := p.writerEvidence(t.Context(), o, bad, in.Baseline); !errors.Is(err, hostbox.ErrChanged) { t.Fatalf("the writer's evidence accepted an admission on %s: %v", bad.Box, err) } }) t.Run("the probe and recovery owners' fixtures", func(t *testing.T) { f := newPOFixture(t, poIntoHome) in, err := f.owners.Select(t.Context(), f.route, f.read(), poProbe) if err != nil { t.Fatal(err) } if a := in.Admission; a.Box != "home-box" || a.Region != "ie" || a.PredecessorRegion != "ca" || a.Mode != "cold" { t.Fatalf("the probe owners' admission: %+v", a) } if err := f.owners.AdmissionEvidence(t.Context(), f.read(), in.Admission, in.Baseline); err != nil { t.Fatal(err) } if o := f.read(); o.Claim.Op.Intent["to"] != park.HomeRegion || o.Claim.Op.Intent["home_region"] != "ie" || f.rec.Move.To != "" { t.Fatalf("the probe owners' fixture: %+v %+v", o.Claim.Op.Intent, f.rec.Move) } for _, kind := range []string{rcClean, rcDirty, rcRevert} { f := newRCFixture(t, kind, poIntoHome) o := f.read() in := f.selectAdmission(o) if a := in.Admission; a.Box != "ca-box" || a.Region != "ca" { t.Fatalf("%s: the recovery owners' admission: %+v", kind, a) } if err := f.owners.AdmissionEvidence(t.Context(), o, in.Admission, in.Baseline); err != nil { t.Fatalf("%s: %v", kind, err) } if o.Claim.Op.Intent["to"] != park.HomeRegion || o.Claim.Op.Intent["target"] != "home-box" { t.Fatalf("%s: the recovery claim: %+v", kind, o.Claim.Op) } } }) t.Run("the revert fixture", func(t *testing.T) { for _, from := range []string{"stopping", "stopped"} { h := newRevertHarness(t, intoHome) if from == "stopping" { h.fromStopping(t) h.ev.stopped = true } else { h.fromStopped(t) } h.arm(t) h.drive(t) h.closed(t) if rec := h.read(t, p1Host); rec.Region != "ca" || rec.LastMove == nil || rec.LastMove.Outcome != "rolled_back" { t.Fatalf("%s: the record: %+v", from, rec) } if keys := h.laneKeys(); len(keys) != 0 { t.Fatalf("%s: the home bucket's lane still holds %v", from, keys) } } }) }