package host import ( "context" "encoding/base64" "net" "os" "path/filepath" "testing" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/registry" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/rootstate" ) func generationHeldProductionSource(t *testing.T, f *hrFixture) generationPinSource { t.Helper() reg, err := registry.Open(filepath.Join(t.TempDir(), "registry.db")) if err != nil { t.Fatal(err) } t.Cleanup(func() { reg.Close() }) a := &ClaimAssembly{Guard: hostguard.Guard{DataDir: f.data, Root: hostguard.NewRootAuthority(&rollbackDisk{dir: t.TempDir()})}, ManagedRoots: map[string]string{"opt": f.opt}} ctx := context.WithValue(t.Context(), claimEntryKey{}, claimEntry{Assembly: a, Store: f.s}) root := ClaimRootWithLaunch{Root: ClaimRootHTTP{Stop: ClaimStopHTTP{Authenticate: func(context.Context, net.Conn) (StopPeer, error) { return hrPeer, nil }}}} h, err := WithProductionMovesHeldRelease(ctx, root, reg, func(key string) string { switch key { case "CARLOS_MOVES_PHASE1": return "1" case "CARLOS_RELEASE_PUBKEY": return base64.StdEncoding.EncodeToString(hsPub) } return "" }) if err != nil { t.Fatal(err) } held, ok := h.(ClaimRootWithLaunch).HeldRelease.(*movesHeldReleaseRoot) if !ok { t.Fatal("held root was not assembled") } source, ok := held.generationPins.(generationPinSource) if !ok { t.Fatal("held root has no production generation source") } return source } func TestPlan14GenerationHeldStage(t *testing.T) { for _, action := range []string{heldReleaseStage, heldReleasePublish} { for _, mode := range []string{"pinned", "unknown", "legacy", "legacy-flag", "fresh"} { t.Run(action+"/"+mode, func(t *testing.T) { f := newHRFixture(t) source := generationHeldProductionSource(t, f) lock := &plan14ReleaseStore{} f.root.OpenRelease = func() (rootstate.Store, error) { return lock, nil } if action == heldReleaseStage { f.set(func(f *hrFixture) { f.claim.Op = hostclaim.Op{Kind: hostclaim.KindMoving, ID: p1ID, Exec: "ie-box", Participants: []string{"ie-box", "uk-box"}, Intent: map[string]string{"source": "ie-box", "target": "uk-box", "to": "uk"}} f.rec.Move.State, f.rec.Move.LandingPhase, f.rec.Move.Inputs = provision.MoveReady, "", nil f.row = nil }) } path := release.VersionPath(f.opt, "bab", "v7", "app") if mode != "fresh" { if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { t.Fatal(err) } } reads, opens := 0, 0 f.root.generationPins = release.PinSourceFunc(func(context.Context, string) (release.StagedPaths, error) { if !lock.held { t.Fatal("pin read outside release exclusion") } if _, err := os.Lstat(filepath.Join(f.data, ".moves", heldStageLockFile(p1Host))); !os.IsNotExist(err) { t.Fatalf("pin reader runs inside held-stage leaf lock: %v", err) } reads++ if mode == "pinned" { return release.StagedPaths{filepath.Dir(path): true}, nil } return nil, os.ErrPermission }) if mode == "legacy" || mode == "legacy-flag" { if mode == "legacy-flag" { t.Setenv("CARLOS_HOST_CLAIM", "1") } else { t.Setenv("CARLOS_HOST_CLAIM", "") } source.legacy = func() (bool, error) { return true, nil } source.open = func() (rootstate.Store, error) { opens++; return nil, os.ErrPermission } f.root.generationPins = source } _, err := f.root.Serve(t.Context(), hrPeer, f.request(action)) if mode == "pinned" || mode == "unknown" { if err == nil || reads == 0 { t.Fatalf("held write bypassed reservations: %v reads=%d", err, reads) } if _, err := os.Stat(path); !os.IsNotExist(err) { t.Fatalf("protected path written: %v", err) } if _, err := os.Lstat(release.LiveLink(f.opt, p1Host)); !os.IsNotExist(err) { t.Fatalf("held release published: %v", err) } if entries, err := os.ReadDir(f.receipts); err != nil || len(entries) != 0 { t.Fatalf("refused release receipted: %v %v", entries, err) } } else { if err != nil || opens != 0 || reads != 0 { t.Fatalf("permitted held stage refused: %v opens=%d reads=%d", err, opens, reads) } if _, err := os.Stat(path); err != nil { t.Fatal(err) } live, liveErr := release.LiveVersion(f.opt, p1Host) if liveErr != nil { t.Fatal(liveErr) } if action == heldReleasePublish && live != "v7" || action == heldReleaseStage && live != "" { t.Fatalf("wrong live effect: %q", live) } entries, err := os.ReadDir(f.receipts) if err != nil || action == heldReleasePublish && len(entries) != 1 || action == heldReleaseStage && len(entries) != 0 { t.Fatalf("wrong receipt effect: %v %v", entries, err) } if _, err := os.Stat(release.StagedManifestPath(f.opt, "bab", "v7", "app")); err != nil { t.Fatal(err) } } }) } } }