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 "os"
7 "path/filepath"
8 "reflect"
9 "testing"
10
11 "github.com/carlosframework/platform/internal/activator"
12 "github.com/carlosframework/platform/internal/hostbox"
13 "github.com/carlosframework/platform/internal/hostclaim"
14 "github.com/carlosframework/platform/internal/provision"
15 "github.com/carlosframework/platform/internal/registry"
16)
17
18type isolationInputs struct {
19 recoveryInputs
20 roles []string
21}
22
23func (in isolationInputs) Select(ctx context.Context, rt registry.Route, o hostclaim.Observed, policy activator.StartPolicy) (ClaimLaunchInputs, error) {
24 selected, err := in.recoveryInputs.Select(ctx, rt, o, policy)
25 selected.Roles = in.roles
26 return selected, err
27}
28
29func TestClaimWakeRefusesEveryUnisolatedShapeBeforeAdmission(t *testing.T) {
30 for _, shape := range []string{"nested", "flat", "foreign", "systemd", "seed", "standalone-sidecar", "co-spawn", "no-roles"} {
31 t.Run(shape, func(t *testing.T) {
32 f := newRecoveryFixture(t)
33 c := f.controller("boot-1", "controller-1", 4101, "41")
34 // Install the concrete production preflight, not a fixture override.
35 if shape != "no-roles" {
36 c.wake.Roles = &ClaimRoleAssembly{Manager: c.wake.Manager, Journal: f.journal}
37 }
38 roles := []string{"main"}
39 route := registry.Route{Host: recoveryHost, Kind: "instance", Hibernate: true}
40 switch shape {
41 case "nested":
42 route.DBPath = provision.NestedDBPath(f.guard.DataDir, route.Host)
43 route.Socket = filepath.Join(provision.SocketDir, "instances", provision.InstanceID(route.Host), "app.sock")
44 case "flat":
45 route.DBPath = provision.DefaultDBPath(f.guard.DataDir, route.Host)
46 route.Socket = filepath.Join(provision.SocketDir, route.Host+".sock")
47 case "foreign":
48 route.DBPath, route.Socket = "/foreign/data.db", "/foreign/app.sock"
49 case "systemd":
50 route.Unit = "tenant.service"
51 case "seed":
52 roles = []string{"seed"}
53 case "standalone-sidecar":
54 route.Kind = "sidecar"
55 case "co-spawn":
56 roles = []string{"main", "sidecar"}
57 }
58 c.wake.Inputs = isolationInputs{recoveryInputs{f}, roles}
59 before := f.read()
60 guardBefore, err := os.ReadDir(filepath.Join(f.guard.DataDir, "hostguard"))
61 if err != nil {
62 t.Fatal(err)
63 }
64 err = c.runtime.Prerequisites(t.Context(), route, before, recoveryPolicy)
65 if !errors.Is(err, hostbox.ErrUnsupported) {
66 t.Fatalf("shape %s: %v", shape, err)
67 }
68 if after := f.read(); after.ETag != before.ETag || after.Claim.Holder.WakeRetry != nil {
69 t.Fatalf("refusal changed claim or spent retry: %+v", after)
70 }
71 guardAfter, err := os.ReadDir(filepath.Join(f.guard.DataDir, "hostguard"))
72 if err != nil || !reflect.DeepEqual(guardBefore, guardAfter) || len(c.wake.pending) != 0 || len(f.requests) != 0 {
73 t.Fatalf("refusal reached guard/stop/lease: err=%v pending=%d requests=%d", err, len(c.wake.pending), len(f.requests))
74 }
75 if _, err := f.journal.Read(t.Context(), recoveryTuple(before, c.local)); err == nil {
76 t.Fatal("refusal journaled a launch")
77 }
78 })
79 }
80}
81