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 "crypto/sha256"
6 "encoding/hex"
7 "encoding/json"
8 "errors"
9 "path/filepath"
10
11 "github.com/carlosframework/platform/internal/activator"
12 "github.com/carlosframework/platform/internal/cgroup"
13 "github.com/carlosframework/platform/internal/hostbox"
14 "github.com/carlosframework/platform/internal/hostclaim"
15 "github.com/carlosframework/platform/internal/hostguard"
16 "github.com/carlosframework/platform/internal/hostwrite"
17 "github.com/carlosframework/platform/internal/provision"
18 "github.com/carlosframework/platform/internal/release"
19)
20
21type ClaimRoleAssembly struct {
22 Guard hostguard.Guard
23 Claims release.Store
24 // Root is the reservation-bearing isolated root launch protocol (claim
25 // Plan 11, R10). Nil keeps every claimed launch refused at Preflight.
26 prepareGroup func(string, cgroup.Limit) (*cgroup.Prepared, hostbox.LaunchGroup, error)
27 Root activator.RootRoleLauncher
28 Manager *cgroup.Manager
29 Journal *hostbox.LaunchJournal
30 Inventory *hostbox.StopInventoryDB
31 ManagedRoots map[string]string
32 Identity hostbox.IdentityProvider
33 BootID, ControllerID string
34}
35
36func claimContainmentName(target hostguard.FenceTarget) string {
37 identity := struct {
38 Host, Incarnation string
39 Stamp, Session uint64
40 Box, Enrollment, Boot, Controller string
41 }{target.Host, target.Incarnation, target.Stamp, target.Session, target.Box, target.Enrollment, target.BootID, target.ControllerID}
42 raw, _ := json.Marshal(identity)
43 sum := sha256.Sum256(raw)
44 return cgroup.ClaimContainmentName(hex.EncodeToString(sum[:24])) // the one format (merge item 8)
45}
46
47// launchTarget binds the observed session to this controller's verified tuple.
48func (a *ClaimRoleAssembly) launchTarget(ctx context.Context, o hostclaim.Observed) (hostguard.FenceTarget, error) {
49 if a == nil || a.BootID == "" || a.ControllerID == "" || o.Claim == nil || o.Claim.Holder == nil {
50 return hostguard.FenceTarget{}, hostwrite.ErrUnavailable
51 }
52 actor, err := a.Identity.Verify(ctx)
53 if err != nil {
54 return hostguard.FenceTarget{}, err
55 }
56 h := o.Claim.Holder
57 return hostguard.FenceTarget{Region: actor.Region, Host: o.Host, Incarnation: h.Incarnation, Stamp: h.Stamp, Session: h.Session, Op: o.Claim.Op.ID, Phase: o.Claim.Op.Phase, Box: actor.Box, Enrollment: actor.Enrollment, BootID: a.BootID, ControllerID: a.ControllerID, ClaimETag: o.ETag, DirectoryRevision: actor.Binding, RosterRevision: actor.Revision}, nil
58}
59
60// Preflight admits a launch before retry reservation, admission or state
61// preparation only while root speaks the complete isolated launch protocol.
62// Every other answer, including no answer, is the isolation refusal.
63func (a *ClaimRoleAssembly) Preflight(ctx context.Context, _ ClaimLaunchInputs) error {
64 if a == nil || a.Root == nil {
65 return activator.ErrClaimIsolationUnavailable
66 }
67 protocol, err := a.Root.Capability(ctx)
68 if err != nil {
69 return errors.Join(activator.ErrClaimIsolationUnavailable, err)
70 }
71 if protocol != hostbox.RoleLaunchProtocol {
72 return activator.ErrClaimIsolationUnavailable
73 }
74 return nil
75}
76
77// PreflightLaunch refuses, before admission, selected inputs the isolated
78// unit cannot carry. The runtime's prerequisites call it after Select.
79func (a *ClaimRoleAssembly) PreflightLaunch(host string, in ClaimLaunchInputs) error {
80 if a == nil || a.Root == nil {
81 return activator.ErrClaimIsolationUnavailable
82 }
83 if a.ManagedRoots["data"] != "/data" {
84 return hostbox.ErrRoleLaunchShape
85 }
86 if len(in.Roles) != 1 || in.Roles[0] != "main" {
87 return hostbox.ErrUnsupported
88 }
89 if !filepath.IsAbs(in.Executable) || filepath.Clean(in.Executable) != in.Executable || !claimHexDigest(in.ExecutableSHA256) || in.Identity.Config == "" || in.Identity.Release == "" {
90 return hostbox.ErrRoleLaunchShape
91 }
92 paths, err := claimIsolatedPaths(a.ManagedRoots, host, in)
93 if err != nil {
94 return err
95 }
96 if in.Unit != nil {
97 return claimBoundInputMatch(host, in)
98 }
99 // The same validator startIsolated runs on the built command.
100 _, socket, db, err := activator.IsolatedArgv(host, paths.LaunchDir, paths.RunDir, in.Args, in.Env)
101 if err != nil {
102 return err
103 }
104 if db != in.DBPath || socket != provision.NestedSocket(host) {
105 return hostbox.ErrRoleLaunchShape
106 }
107 return nil
108}
109
110// claimIsolatedLaunch is where main's isolated run unit finds a host: its
111// instance directory (the only data the unit binds), its run directory, its
112// stdout socket and its slice tier.
113type claimIsolatedLaunch struct {
114 LaunchDir, RunDir, LogSocket string
115 MemoryMax int64
116}
117
118// claimIsolatedPaths admits only the nested layout's database, directly in
119// the host's instance directory, and a platform tier.
120func claimIsolatedPaths(roots map[string]string, host string, in ClaimLaunchInputs) (claimIsolatedLaunch, error) {
121 data := roots["data"]
122 if !filepath.IsAbs(data) || !provision.ValidHostShape(host) || !filepath.IsAbs(in.DBPath) || filepath.Clean(in.DBPath) != in.DBPath ||
123 (in.Limit.MemoryMax != cgroup.TierDefault.MemoryMax && in.Limit.MemoryMax != cgroup.TierChildren.MemoryMax) {
124 return claimIsolatedLaunch{}, hostbox.ErrRoleLaunchShape
125 }
126 dir := provision.InstanceDir(data, host)
127 if filepath.Dir(in.DBPath) != dir {
128 return claimIsolatedLaunch{}, hostbox.ErrRoleLaunchShape
129 }
130 return claimIsolatedLaunch{LaunchDir: dir, RunDir: provision.InstanceRunDir(host), LogSocket: provision.LogSocket(host, "run"), MemoryMax: in.Limit.MemoryMax}, nil
131}
132
133// PreflightKind checks root's installed verifier before any attempt/admission.
134// The existing RootRoleLauncher signatures remain unchanged.
135func (a *ClaimRoleAssembly) PreflightKind(ctx context.Context, kind string) error {
136 if kind == hostclaim.KindServing {
137 return nil
138 }
139 client, ok := a.Root.(interface {
140 KindCapability(context.Context, string) error
141 })
142 if !ok {
143 return activator.ErrClaimIsolationUnavailable
144 }
145 return client.KindCapability(ctx, kind)
146}
147