carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux
2
3package host
4
5import (
6 "context"
7 "errors"
8 "fmt"
9 "os"
10 "path/filepath"
11 "strings"
12 "testing"
13 "time"
14
15 "github.com/carlosframework/platform/internal/cgroup"
16 "github.com/carlosframework/platform/internal/hostbox"
17 "github.com/carlosframework/platform/internal/hostclaim"
18 execlease "github.com/carlosframework/platform/internal/hostexec"
19 "github.com/carlosframework/platform/internal/hostguard"
20 "github.com/carlosframework/platform/internal/hostwrite"
21 "github.com/carlosframework/platform/internal/instancectl"
22 "github.com/carlosframework/platform/internal/provision"
23 "github.com/carlosframework/platform/internal/registry"
24)
25
26// L2, launch authorize (claimlaunch_authority.go): root's launch authority
27// reads the production identity fresh. A reservation whose target carries the
28// box binding is authorized before and after six roster writes; a target
29// stamped with the roster numeral is refused, and after a quarantine and
30// readmission the same reservation is refused.
31func TestClaimBindingLaunchAuthorizeSurvivesRosterWrites(t *testing.T) {
32 ctx := t.Context()
33 roster := newBindingRosterFixture(t, "dst", "ie")
34 identity := roster.identity()
35 actor, err := identity.Verify(ctx)
36 if err != nil {
37 t.Fatal(err)
38 }
39 dataDir := t.TempDir()
40 f := newROFixture(t, roOptions{})
41 f.inputs.Launch = func(context.Context, registry.Route) (ClaimServingLaunch, error) {
42 return ClaimServingLaunch{Identity: hostwrite.Identity{Config: "sha256:config", Release: "sha256:release"}, Executable: "/opt/carlos/versions/v1/app", ExecutableSHA256: strings.Repeat("b", 64), Version: "v1",
43 Args: []string{"--socket", filepath.Join(provision.InstanceRunDir(roHost), "app.sock"), "--db", provision.NestedDBPath(dataDir, roHost)}, Env: []string{"CARLOS_ADMIN_TOKEN=test"}}, nil
44 }
45 in, err := f.inputs.Select(ctx, f.route, f.read(), roProbe)
46 if err != nil {
47 t.Fatal(err)
48 }
49 admitted := f.admit(f.read(), in)
50 h := admitted.Claim.Holder
51 boot, err := execlease.BootID()
52 if err != nil {
53 t.Fatal(err)
54 }
55 start, err := claimConstructionProcessStart()
56 if err != nil {
57 t.Fatal(err)
58 }
59 peer := StopPeer{UID: 1001, PID: os.Getpid(), ProcessStart: start, BootID: boot, ControllerID: "controller-1"}
60 target := hostguard.FenceTarget{Region: "ie", Host: roHost, Incarnation: h.Incarnation, Stamp: h.Stamp, Session: h.Session, Op: roOp, Phase: hostclaim.PhaseRelocatingProbing, Box: actor.Box, Enrollment: actor.Enrollment, BootID: peer.BootID, ControllerID: peer.ControllerID, ClaimETag: admitted.ETag, DirectoryRevision: actor.Binding}
61 attempts := &relocationProbeAttempts{Claims: f.claims, DataDir: dataDir, Box: actor.Box, NewID: f.inputs.NewID, Now: f.inputs.Now}
62 s := OperationStart{Admitted: admitted, Policy: roProbe, Inputs: in, Owner: target}
63 if err := attempts.Reserve(ctx, s, 0); err != nil {
64 t.Fatal(err)
65 }
66 a := attempts.CurrentAttempt(s)
67 exe := hostbox.LaunchFile{Device: 5, Inode: 6}
68 kernel := &launchKernel{path: in.Executable, want: exe, digest: in.ExecutableSHA256}
69 root := &ClaimLaunchRoot{ValidateExecutable: func(string, string, string) error { return nil }, Local: &rev2Local{}, Clock: &rev2Clock{wall: time.Now(), boot: 100}, DataRoot: dataDir, ExecLockDir: t.TempDir(),
70 Attempts: map[string]claimLaunchAttemptReader{hostclaim.KindRelocating: relocationAttemptReader{DataDir: dataDir}},
71 Guard: hostguard.Guard{DataDir: dataDir, LockDir: t.TempDir(), Claims: hostguard.StoreReader{Store: f.claims}},
72 Actor: identity.Verify, Kernel: kernel}
73 configureLaunchParticipation(t, &launchFixture{root: root, peer: peer, target: target, claims: &launchClaims{obs: admitted}})
74 spec := &hostbox.LaunchSpec{Identity: in.Identity, Executable: in.Executable, Digest: in.ExecutableSHA256, Args: in.Args, Env: in.Env}
75 reservation := hostbox.LaunchReservation{ID: a.ID + ":main", Role: "main", InvocationID: a.InvocationID, Inputs: a.LaunchInputs, Target: target, Group: hostbox.LaunchGroup{RelativePath: instancectl.ClaimUnitControlGroup(roHost)}, Executable: exe}
76 q := hostbox.RoleLaunchRequest{V: hostbox.RoleLaunchV, Op: hostbox.RoleLaunchStart, Spec: spec, Reservation: &reservation, Executable: in.Executable, MemoryMax: cgroup.TierDefault.MemoryMax}
77 if err := root.authorize(ctx, peer, q, admitted.Claim); err != nil {
78 t.Fatal("authorize at the install's binding:", err)
79 }
80 roster.sixWrites()
81 if err := root.authorize(ctx, peer, q, admitted.Claim); err != nil {
82 t.Fatal("authorize after six roster writes:", err)
83 }
84 numeral := reservation
85 numeral.Target.DirectoryRevision = fmt.Sprint(roster.read().Roster.Revision)
86 q.Reservation = &numeral
87 if err := root.authorize(ctx, peer, q, admitted.Claim); !errors.Is(err, hostbox.ErrChanged) {
88 t.Fatalf("numeral-stamped target: %v, want ErrChanged", err)
89 }
90 q.Reservation = &reservation
91 roster.quarantine("loss_1")
92 roster.readmit()
93 if err := root.authorize(ctx, peer, q, admitted.Claim); !errors.Is(err, hostbox.ErrChanged) {
94 t.Fatalf("after readmission: %v, want ErrChanged", err)
95 }
96}
97
98// L2, serving handover (claimservinghandover.go validateServingHandover): the
99// prior installation and the successor are both stamped with the binding;
100// after six roster writes the handover runs. A successor stamped with the
101// roster numeral is refused before any successor state.
102func TestClaimBindingServingHandoverSurvivesRosterWrites(t *testing.T) {
103 ctx := t.Context()
104 roster := newBindingRosterFixture(t, "box-1", "ie")
105 identity := roster.identity()
106 actor, err := identity.Verify(ctx)
107 if err != nil {
108 t.Fatal(err)
109 }
110 f := newServingRootFixtureFor(t, true, actor)
111 f.p.root.InventoryActor = identity.Verify
112 root := servingHandoverRoot(t, f)
113 roster.sixWrites()
114 before := f.f.read()
115 next := hostbox.ColdSuccessor{Host: f.current.Host, Incarnation: f.current.Incarnation, Box: actor.Box, Enrollment: actor.Enrollment, DirectoryRevision: fmt.Sprint(roster.read().Roster.Revision), ETag: before.ETag, Generation: f.current.Generation, Stamp: f.current.Stamp, Session: f.current.Session}
116 if err := root.servingHandover().Run(ctx, f.receipt.Target, next); !errors.Is(err, hostbox.ErrChanged) {
117 t.Fatalf("numeral successor: %v, want ErrChanged", err)
118 }
119 if _, err := f.f.guard.Capture(f.current.Host); err != nil {
120 t.Fatal(err)
121 }
122 next.DirectoryRevision = actor.Binding
123 if err := root.servingHandover().Run(ctx, f.receipt.Target, next); err != nil {
124 t.Fatal("serving handover after six roster writes:", err)
125 }
126 installed, err := f.f.guard.Capture(f.current.Host)
127 if err != nil || installed != (hostguard.Captured{Incarnation: f.current.Incarnation, Stamp: f.current.Stamp}) {
128 t.Fatalf("successor installation: %+v %v", installed, err)
129 }
130 in, _, err := root.Reconcile.Inventory.Snapshot(ctx, f.current.Host, installed)
131 if err != nil || in.DirectoryRevision != actor.Binding {
132 t.Fatalf("successor inventory stamp %q: %v", in.DirectoryRevision, err)
133 }
134}
135