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 "net/http"
6 "os"
7 "os/exec"
8 "path/filepath"
9 "strings"
10
11 "github.com/carlosframework/platform/internal/controlsock"
12 "github.com/carlosframework/platform/internal/hostbox"
13 "github.com/carlosframework/platform/internal/hostclaim"
14 execlease "github.com/carlosframework/platform/internal/hostexec"
15 "github.com/carlosframework/platform/internal/instancectl"
16)
17
18// ClaimRootWithLaunch is the claim-mode root handler with the isolated
19// launch route beside the existing claim verbs, and the moves held-release
20// route (Task 7b), which is nil, and refuses, unless CARLOS_MOVES_PHASE1 is
21// on (WithProductionMovesHeldRelease).
22type ClaimRootWithLaunch struct {
23 Root ClaimRootHTTP
24 Launch *ClaimLaunchRoot
25 HeldRelease http.Handler
26}
27
28func (h ClaimRootWithLaunch) ServeHTTP(w http.ResponseWriter, r *http.Request) {
29 if r.URL.Path == heldReleasePath {
30 if h.HeldRelease == nil {
31 http.Error(w, "refused", http.StatusServiceUnavailable)
32 return
33 }
34 h.HeldRelease.ServeHTTP(w, r)
35 return
36 }
37 if r.URL.Path != "/claim/launch" {
38 h.Root.ServeHTTP(w, r)
39 return
40 }
41 if h.Launch == nil {
42 http.Error(w, "refused", http.StatusServiceUnavailable)
43 return
44 }
45 h.Launch.ServeHTTP(w, r)
46}
47
48func (h ClaimRootWithLaunch) Start(ctx context.Context) { h.Root.Start(ctx) }
49
50func (h ClaimRootWithLaunch) RecoverPending(ctx context.Context) error {
51 return h.Root.RecoverPending(ctx)
52}
53
54// WithProductionClaimLaunch adds the reservation-bearing isolated launch to
55// the production claim root handler (NewProductionRootClaimStop's). It
56// reuses that handler's peer authentication, guard and root launch journal,
57// and the assembly's roster identity and release root; it also puts the
58// isolated units in the root stop's evidence.
59func WithProductionClaimLaunch(ctx context.Context, handler http.Handler) (http.Handler, error) {
60 root, ok := handler.(ClaimRootHTTP)
61 if !ok {
62 return nil, ErrClaimStopUnavailable
63 }
64 entry, _ := ctx.Value(claimEntryKey{}).(claimEntry)
65 if entry.Assembly == nil {
66 return nil, &ClaimDependencyError{Contracts: []string{"claim Plan 11 isolated root launch: production claim assembly"}}
67 }
68 a := entry.Assembly
69 root.history = productionCreationHistory
70 wrapped, err := claimLaunchFor(root, a.Identity.Verify, a.ManagedRoots)
71 if err != nil {
72 return nil, err
73 }
74 if a.Local == nil {
75 return nil, ErrClaimLaunchUnavailable
76 }
77 wrapped.Launch.Local = a.Local
78 wrapped.Launch.history = root.history
79 wrapped.Launch.generations = productionRootGenerations(a.Claims, a.ManagedRoots["opt"])
80 wrapped.Launch.installedProgram = wrapped.Launch.rootInstalledProgram
81 if err := bindProductionClaimUnits(ctx, &wrapped, a); err != nil {
82 return nil, err
83 }
84 return wrapped, nil
85}
86
87func claimLaunchFor(root ClaimRootHTTP, actor func(context.Context) (hostbox.Actor, error), roots map[string]string) (ClaimRootWithLaunch, error) {
88 coordinator := root.Stop.Coordinator
89 if root.Launches == nil || root.Stop.Authenticate == nil || coordinator.Kernel == nil || coordinator.Guard.DataDir == "" || coordinator.Guard.Claims == nil || actor == nil ||
90 !claimLaunchAbs(roots["opt"]) || roots["data"] != "/data" {
91 return ClaimRootWithLaunch{}, ErrClaimStopUnavailable
92 }
93 units := instancectl.ClaimUnits{
94 Systemctl: func(ctx context.Context, args ...string) (string, error) {
95 out, err := exec.CommandContext(ctx, "systemctl", args...).CombinedOutput()
96 return string(out), err
97 },
98 CgroupRoot: "/sys/fs/cgroup",
99 UnitRoot: "/run/systemd/system", DataRoot: roots["data"],
100 ExeRoot: filepath.Join(controlsock.Dir, "exe"),
101 Room: instancectl.DiskRoom(filepath.Join(roots["data"], "instances")),
102 }
103 launch := &ClaimLaunchRoot{SeedController: verifyFilesSeedDelegation, ValidateExecutable: claimStagedExecutable(&instancectl.Server{OptDir: roots["opt"], HostApp: rootInstallationHostApp(coordinator.Guard, coordinator.Evidence)}), DataRoot: roots["data"], Clock: execlease.SystemClock{}, Authenticate: root.Stop.Authenticate, Guard: coordinator.Guard, Actor: actor, Journal: root.Launches, Units: units,
104 Kernel: linuxClaimLaunchKernel{OptRoot: roots["opt"], CgroupRoot: "/sys/fs/cgroup", ProcRoot: "/proc"}}
105 if regionHAEnabled(claimLaunchEnv) {
106 // Claim Plan 11 "Region HA integration and rollout": the root-owned
107 // authoritative attempt reader for kind relocating (region HA ruling
108 // 48). Off, Attempts stays empty and the kind is refused as before.
109 launch.Attempts = map[string]claimLaunchAttemptReader{hostclaim.KindRelocating: relocationAttemptReader{DataDir: roots["data"]}}
110 }
111 root.Stop.Coordinator.Kernel = claimUnitStopKernel{Inner: coordinator.Kernel, Journal: root.Launches, Units: units}
112 if root.executionRuntime != nil {
113 root.executionRuntime.root = &root
114 }
115 return ClaimRootWithLaunch{Root: root, Launch: launch}, nil
116}
117
118// claimLaunchEnv is the process environment; tests replace it.
119var claimLaunchEnv = os.Getenv
120
121func claimLaunchAbs(p string) bool { return filepath.IsAbs(p) && filepath.Clean(p) == p }
122
123func claimStagedExecutable(server *instancectl.Server) func(string, string, string) error {
124 return func(host, path, digest string) error {
125 rel, err := filepath.Rel(filepath.Join(server.OptDir, "versions"), path)
126 if err != nil || !filepath.IsLocal(rel) {
127 return instancectl.ErrStagedExe
128 }
129 version, _, ok := strings.Cut(rel, string(filepath.Separator))
130 if !ok {
131 return instancectl.ErrStagedExe
132 }
133 return server.ValidateStagedExe(host, instancectl.StagedExe{Path: path, Version: version, Digest: digest})
134 }
135}
136