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 "log"
7 "sync"
8
9 "github.com/carlosframework/platform/internal/activator"
10 "github.com/carlosframework/platform/internal/hostbox"
11 "github.com/carlosframework/platform/internal/hostclaim"
12 "github.com/carlosframework/platform/internal/hostwrite"
13 "github.com/carlosframework/platform/internal/lease"
14 "github.com/carlosframework/platform/internal/park"
15 "github.com/carlosframework/platform/internal/registry"
16 "github.com/carlosframework/platform/internal/release"
17)
18
19type claimMoveParkSources struct {
20 View func(context.Context, hostclaim.Observed) (park.View, error)
21 Evidence hostbox.RegionalEvidence
22}
23
24// RegisterProductionClaimMoveParks installs the regional contracts independently
25// of the wake owners and C8 files manifest. Registration is atomic and one-shot.
26func RegisterProductionClaimMoveParks(view func(context.Context, hostclaim.Observed) (park.View, error), retirementEvidence hostbox.RegionalEvidence) error {
27 var missing []string
28 if view == nil {
29 missing = append(missing, "moves Plan 5 exact-version generation and files-manifest view")
30 }
31 if retirementEvidence == nil {
32 missing = append(missing, "moves Plan 5 retired-move regional evidence")
33 }
34 if len(missing) != 0 {
35 return &ClaimDependencyError{Contracts: missing}
36 }
37 claimMoveSources.Lock()
38 defer claimMoveSources.Unlock()
39 if claimMoveSources.Parks != nil {
40 return hostwrite.ErrUnavailable
41 }
42 claimMoveSources.Parks = &claimMoveParkSources{View: view, Evidence: retirementEvidence}
43 return nil
44}
45
46// claimMoveWakeLogf and claimMoveWakeLogOnce log a moves source that did
47// not open, once per process (per agent start); variables so the tests can
48// read and reset them.
49var (
50 claimMoveWakeLogf = log.Printf
51 claimMoveWakeLogOnce = new(sync.Once)
52)
53
54// openProductionClaimMoveWake opens the registered moves source over the
55// claim's data bucket as the serving owners see it (moves Task 8 fix round
56// 1): through hostbox.RequestStore, so every bucket request the moves
57// owners make is bounded by the claim's request limit, runs under the
58// operation's own context (its cancellation still reaches the request), and
59// is preceded by check, the box's bound identity (claimBoundIdentity), so a
60// refused identity issues no later bucket call.
61func openProductionClaimMoveWake(ctx context.Context, store release.Store, actor hostbox.Actor, external ClaimProductionMoveSources, out *ClaimOwnerBindings, check func(context.Context) error) {
62 if external == nil {
63 out.wakeUnavailable = &ClaimDependencyError{Contracts: []string{"moves wake owners and C8 origin-qualified files manifest provider"}}
64 return
65 }
66 var data lease.VersionedStore
67 if out.Data != nil && check != nil {
68 data = hostbox.RequestStore{Store: out.Data, Check: check}
69 }
70 owners, files, err := external.Open(ctx, store, data, actor, out.moveRead)
71 if err != nil {
72 out.wakeUnavailable = errors.Join(&ClaimDependencyError{Contracts: []string{"moves wake owners and C8 origin-qualified files manifest provider"}}, err)
73 // Fail closed (platform-1e): every wake on this box is refused. Say
74 // why once, so an operator can see it (for example, an unusable
75 // CARLOS_SECRETS_KEY with CARLOS_MOVES_PHASE1 on).
76 claimMoveWakeLogOnce.Do(func() {
77 claimMoveWakeLogf("host claims: the moves wake source did not open, so every wake on this box is refused, serving included: %v", err)
78 })
79 return
80 }
81 out.Moves, out.Serving.Files = owners, files
82}
83
84func refusingClaimMoveWake(box string, store release.Store, reason error) *activator.ClaimRuntime {
85 w := refusingRootWake(box, store)
86 w.Prerequisites = func(context.Context, registry.Route, hostclaim.Observed, activator.StartPolicy) error { return reason }
87 w.Admit = func(context.Context, registry.Route, hostclaim.Observed, activator.StartPolicy) (hostclaim.Observed, error) {
88 return hostclaim.Observed{}, reason
89 }
90 w.Prepare = func(context.Context, registry.Route, hostclaim.Observed, activator.StartPolicy) (activator.ClaimStart, error) {
91 return activator.ClaimStart{}, reason
92 }
93 return w
94}
95
96// claimBoundIdentity pins the entire verified enrollment, including its cloud
97// instance, buckets, namespace and binding, for a built assembly. The roster
98// write counter is not pinned: holds and other boxes' rows move it.
99func claimBoundIdentity(verify func(context.Context) (hostbox.Actor, error), expected hostbox.Actor) func(context.Context) error {
100 return func(ctx context.Context) error {
101 if ctx == nil || verify == nil || expected.Enrollment == "" {
102 return hostwrite.ErrUnavailable
103 }
104 current, err := verify(ctx)
105 if err != nil {
106 return err
107 }
108 if !hostbox.SameBinding(current, expected) {
109 return hostbox.ErrChanged
110 }
111 return nil
112 }
113}
114
115// ClaimLocalParkAuthorizer is the owner's local check before park.Local effects.
116type ClaimLocalParkAuthorizer func(context.Context, *hostclaim.Claim, hostclaim.Obligation) error
117
118type claimMoveParks struct {
119 local ClaimLocalParkAuthorizer
120 engine park.Engine
121 leases hostbox.LeaseSet
122 check func(context.Context) error
123}
124
125// MoveParks returns the regional engine, lease inventory and local authorizer.
126// Only Build can populate this capability, after Identity.Verify succeeds.
127func (p ClaimProviders) MoveParks(ctx context.Context) (park.Engine, hostbox.LeaseSet, ClaimLocalParkAuthorizer, error) {
128 if p.moveParks == nil || p.moveParks.check == nil || p.moveParks.leases == nil {
129 return park.Engine{}, nil, nil, hostwrite.ErrUnavailable
130 }
131 if err := p.moveParks.check(ctx); err != nil {
132 return park.Engine{}, nil, nil, err
133 }
134 if p.moveParks.local == nil {
135 return park.Engine{}, nil, nil, hostwrite.ErrUnavailable
136 }
137 return p.moveParks.engine, p.moveParks.leases, p.moveParks.local, nil
138}
139
140func (p productionClaimRegional) VerifyMovesSweep(ctx context.Context, o hostclaim.Observed, versions []lease.ObjectVersion) (park.MoveSweepProof, error) {
141 if _, err := p.Identity.Verify(ctx); err != nil {
142 return park.MoveSweepProof{}, err
143 }
144 if p.Parks == nil {
145 return park.MoveSweepProof{}, park.ErrUnsafe
146 }
147 if o.Claim != nil && o.Claim.Free() {
148 return claimFreeMoveProof(o)
149 }
150 verifier, ok := p.Parks.Evidence.(park.MoveSweepVerifier)
151 if !ok {
152 return park.MoveSweepProof{}, &ClaimDependencyError{Contracts: []string{"moves Plan 5 newest retired move fence or release evidence"}}
153 }
154 if _, err := p.Identity.Verify(ctx); err != nil {
155 return park.MoveSweepProof{}, err
156 }
157 return verifier.VerifyMovesSweep(ctx, o, versions)
158}
159