carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package activator
2
3import (
4 "errors"
5 "fmt"
6 "io/fs"
7 "os"
8 "path/filepath"
9 "strings"
10
11 "github.com/carlosframework/platform/internal/hostbox"
12 "github.com/carlosframework/platform/internal/instancectl"
13 "github.com/carlosframework/platform/internal/registry"
14)
15
16// ClaimLaunchRoles are the processes the real start of rt spawns: main
17// always, the route's first-boot seed when it has one, and the legacy
18// co-spawned sidecar when its env file is staged. The isolated claim launch
19// carries main alone.
20func (b *ExecBackend) ClaimLaunchRoles(rt registry.Route) ([]string, error) {
21 return b.claimLaunchRoles(rt, os.Stat)
22}
23
24func (b *ExecBackend) claimLaunchRoles(rt registry.Route, stat func(string) (os.FileInfo, error)) ([]string, error) {
25 roles := []string{"main"}
26 if rt.Seed != "" {
27 roles = append(roles, "seed")
28 }
29 _, err := stat(b.pathsFor(rt).sidecarEnv)
30 switch {
31 case err == nil:
32 roles = append(roles, "sidecar")
33 case errors.Is(err, fs.ErrNotExist):
34 // A proven absence is the only safe negative answer. Other inspection
35 // failures cannot admit a launch whose complete role set is unknown.
36 default:
37 return nil, fmt.Errorf("inspect staged sidecar for %s: %w", rt.Host, err)
38 }
39 return roles, nil
40}
41
42// claimLaunchShape refuses, read-only, a route whose real start is more than
43// the isolated main launch.
44func (b *ExecBackend) claimLaunchShape(rt registry.Route) error {
45 roles, err := b.ClaimLaunchRoles(rt)
46 if err != nil {
47 return err
48 }
49 if len(roles) != 1 {
50 return hostbox.ErrUnsupported
51 }
52 return nil
53}
54
55func isolatedCleanAbs(p string) bool {
56 return filepath.IsAbs(p) && filepath.Clean(p) == p && !strings.ContainsRune(p, 0)
57}
58
59// IsolatedArgv validates the complete isolated launch shape and returns the
60// trampoline's argv: the unit's view of the host's live build, then exactly
61// --socket in the run directory and --db in the instance directory, each
62// once. args excludes argv[0]. env must be well-formed (KEY=value, no NUL,
63// no duplicate key) and carry the instance's admin token. It is the one
64// validator for startIsolated and the claim preflight.
65func IsolatedArgv(host, launchDir, runDir string, args, env []string) (argv []string, socket, db string, err error) {
66 if len(args)%2 != 0 || !isolatedCleanAbs(launchDir) || !isolatedCleanAbs(runDir) {
67 return nil, "", "", hostbox.ErrRoleLaunchShape
68 }
69 for i := 0; i < len(args); i += 2 {
70 value := args[i+1]
71 switch {
72 case args[i] == "--socket" && socket == "" && isolatedCleanAbs(value) && filepath.Dir(value) == runDir:
73 socket = value
74 case args[i] == "--db" && db == "" && isolatedCleanAbs(value) && filepath.Dir(value) == launchDir:
75 db = value
76 default:
77 return nil, "", "", hostbox.ErrRoleLaunchShape
78 }
79 }
80 if socket == "" || db == "" {
81 return nil, "", "", hostbox.ErrRoleLaunchShape
82 }
83 seen := make(map[string]bool, len(env))
84 for _, kv := range env {
85 k, _, ok := strings.Cut(kv, "=")
86 if !ok || k == "" || strings.ContainsRune(kv, 0) || seen[k] {
87 return nil, "", "", hostbox.ErrRoleLaunchShape
88 }
89 seen[k] = true
90 }
91 if !seen["CARLOS_ADMIN_TOKEN"] || !envHas(env, "CARLOS_ADMIN_TOKEN=") {
92 return nil, "", "", hostbox.ErrRoleLaunchShape
93 }
94 return append([]string{filepath.Join(instancectl.LiveDir, host)}, args...), socket, db, nil
95}
96
97// envHas reports a non-empty value for the given "KEY=" prefix.
98func envHas(env []string, prefix string) bool {
99 for _, kv := range env {
100 if v, ok := strings.CutPrefix(kv, prefix); ok && v != "" {
101 return true
102 }
103 }
104 return false
105}
106