carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "context"
5 "errors"
6 "testing"
7 "time"
8
9 "github.com/carlosframework/platform/internal/hostbox"
10 "github.com/carlosframework/platform/internal/hostclaim"
11 "github.com/carlosframework/platform/internal/hostwrite"
12 "github.com/carlosframework/platform/internal/lease/leasetest"
13 "github.com/carlosframework/platform/internal/registry"
14)
15
16// The claims-off fallback keeps the legacy refusal even when serving
17// sources exist. Claims-on dispatch is covered by the assembly flag matrix.
18func TestClaimOffKeepsLegacyRefusalWithRegisteredSource(t *testing.T) {
19 t.Setenv("CARLOS_HOST_CLAIM", "")
20 served := 0
21 d5 := plan9D5Fixture(t)
22 sources := d5.owners.Sources
23 launch := sources.Launch
24 sources.Launch = func(ctx context.Context, rt registry.Route) (ClaimServingLaunch, error) {
25 served++
26 return launch(ctx, rt)
27 }
28 b := leasetest.NewVersionedBucket()
29 b.AddBucket("data")
30 out := ClaimOwnerBindings{Data: b, Serving: sources}
31 bindClaimLegacyOwners(&out)
32 if out.Inputs == nil || out.AdmissionEvidence == nil || out.PublicationEvidence == nil {
33 t.Fatal("legacy owners unbound: the moves-off constructor would refuse at startup")
34 }
35 if out.Serving.Files == nil || out.Serving.Launch == nil || out.Serving.Markers == nil {
36 t.Fatal("serving sources lost for the moves-on constructor")
37 }
38 var refusal *ClaimDependencyError
39 f := newRecoveryFixture(t)
40 c := f.controller("boot-1", "controller-1", 4101, "start-1")
41 route := registry.Route{Host: recoveryHost, DBPath: "/data/" + recoveryHost + "/data.db"}
42 // Rebuild the runtime with the constructor's moves-off bindings: the legacy
43 // Inputs, and the retry digest selected through them.
44 c.wake.Inputs = out.Inputs
45 c.wake.Retries.Inputs = func(ctx context.Context, rt registry.Route) (string, error) {
46 o, err := hostclaim.Read(ctx, f.claims, rt.Host)
47 if err != nil {
48 return "", err
49 }
50 _, err = out.Inputs.Select(ctx, rt, o, recoveryPolicy)
51 return "inputs", err
52 }
53 c.wake.pending = nil
54 source := hostbox.WorkSource{Guard: f.guard, Box: "box-1", Region: "ie", Now: func() time.Time { return f.now }, Policy: func(context.Context, *hostclaim.Claim, hostbox.Selection, hostwrite.Identity) error { return nil }, Local: func(context.Context, *hostclaim.Claim, hostbox.Selection) error { return nil }}
55 writer := hostbox.Writer{Claims: f.claims, Box: "box-1", Region: "ie", AdmissionEvidence: out.AdmissionEvidence}
56 runtime, err := c.wake.Build(t.Context(), source, writer)
57 if err != nil {
58 t.Fatal(err)
59 }
60 c.runtime = runtime
61 before := f.read()
62 if _, err := out.Inputs.Select(t.Context(), route, before, recoveryPolicy); !errors.As(err, &refusal) {
63 t.Fatalf("moves-off select = %v, want main's typed refusal", err)
64 }
65 for name, call := range map[string]func() error{
66 "materialize": func() error { return out.Inputs.Materialize(t.Context(), before, ClaimLaunchInputs{}, nil) },
67 "empty": func() error { return out.Inputs.EmptyAuthority(t.Context(), before, ClaimLaunchInputs{}) },
68 "publish": func() error { return out.Inputs.Publish(t.Context(), before, ClaimLaunchInputs{}) },
69 "admission": func() error { return out.AdmissionEvidence(t.Context(), before, hostclaim.Admission{}, nil) },
70 "publication": func() error { return out.PublicationEvidence(t.Context(), before) },
71 } {
72 if err := call(); !errors.As(err, &refusal) {
73 t.Fatalf("moves-off %s = %v, want main's typed refusal", name, err)
74 }
75 }
76 // Admit runs with the lease the fixture stands in for; the refusal must
77 // come before any session, stop or retry reservation.
78 live, cancel := context.WithCancel(context.Background())
79 t.Cleanup(cancel)
80 done := make(chan struct{})
81 go func() { <-live.Done(); close(done) }()
82 c.wake.mu.Lock()
83 c.wake.pending[recoveryHost] = &claimWakeAttempt{observed: before, held: recoveryHeld{}, ctx: live, cancel: cancel, done: done}
84 c.wake.mu.Unlock()
85 if _, err := c.runtime.Admit(t.Context(), route, before, recoveryPolicy); !errors.As(err, &refusal) {
86 t.Fatalf("moves-off admit = %v, want main's typed refusal", err)
87 }
88 after := f.read()
89 if after.ETag != before.ETag || after.Claim.Holder.Session != before.Claim.Holder.Session || after.Claim.Holder.Generation != before.Claim.Holder.Generation || after.Claim.Holder.WakeRetry != nil {
90 t.Fatalf("moves-off wake consumed a session: %+v", after.Claim.Holder)
91 }
92 if len(f.requests) != 0 {
93 t.Fatalf("moves-off wake stopped something: %+v", f.requests)
94 }
95 if served != 0 || len(d5.files) != 0 {
96 t.Fatalf("moves-off wake reached the serving sources: launch=%d files=%v", served, d5.files)
97 }
98}
99