carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostresolve
2
3import (
4 "context"
5 "encoding/json"
6
7 "github.com/carlosframework/platform/internal/hostclaim"
8 "github.com/carlosframework/platform/internal/hostfence"
9)
10
11// OperationOwner supplies operation policy. The service owns challenge signing,
12// acceptance, quarantine and the durable prepared/committed loss sequence.
13// InspectLoss and LossWriteSources are read-only. PrepareLoss journals the
14// operation's decision before claim revocation; FinishLoss releases only what
15// the committed declaration permits. Both hooks must be idempotent. Retry
16// dispatch uses the accepted loss's kind, even after the current kind changes.
17// Callbacks receive detached values and must guard every external write.
18// PrepareLoss can arrive after a concurrent FinishLoss or after the loss is
19// conflicted. Its conditional record write must never regress a later state;
20// it must not write the claim or treat its journal as execution authority.
21// A prepared record is inert until both the claim loss reference and committed
22// loss record exist. FinishLoss can observe a successor holder or operation:
23// it derives effects from the signed loss, refuses mismatched identities, and
24// treats its own durable completion as success without touching the successor.
25type OperationOwner interface {
26 InspectLoss(context.Context, hostclaim.Observed, hostfence.RecordObservation, string) (hostfence.Consequences, hostclaim.LossConsequences, error)
27 LossWriteSources(context.Context, hostclaim.Observed, hostclaim.LossConsequences) ([]hostclaim.WriteSource, error)
28 hostclaim.LossOperationOwner
29 PrepareLoss(context.Context, hostclaim.Observed, hostclaim.LossRecord) error
30 FinishLoss(context.Context, hostclaim.Observed, hostclaim.LossRecord) error
31}
32
33func (s Service) operationOwner(kind string) (OperationOwner, error) {
34 switch kind {
35 case hostclaim.KindServing, hostclaim.KindParking, hostclaim.KindDeleting:
36 return builtinOwner{}, nil
37 case hostclaim.KindMoving, hostclaim.KindLanding, hostclaim.KindReverting, hostclaim.KindRestoring:
38 if owner := s.Owners[kind]; owner != nil {
39 return owner, nil
40 }
41 }
42 return nil, ErrResolve
43}
44
45type builtinOwner struct{ owner }
46
47func (builtinOwner) InspectLoss(_ context.Context, o hostclaim.Observed, _ hostfence.RecordObservation, box string) (hostfence.Consequences, hostclaim.LossConsequences, error) {
48 return effects(o.Claim, box)
49}
50func (builtinOwner) LossWriteSources(context.Context, hostclaim.Observed, hostclaim.LossConsequences) ([]hostclaim.WriteSource, error) {
51 return nil, nil
52}
53func (builtinOwner) PrepareLoss(context.Context, hostclaim.Observed, hostclaim.LossRecord) error {
54 return nil
55}
56func (builtinOwner) FinishLoss(context.Context, hostclaim.Observed, hostclaim.LossRecord) error {
57 return nil
58}
59
60func detached[T any](v T) (T, error) {
61 var out T
62 wire, err := json.Marshal(v)
63 if err == nil {
64 err = json.Unmarshal(wire, &out)
65 }
66 return out, err
67}
68
69func inspectOwner(ctx context.Context, owner OperationOwner, o hostclaim.Observed, record hostfence.RecordObservation, box string) (hostfence.Consequences, hostclaim.LossConsequences, error) {
70 copy, err := detached(o)
71 if err != nil {
72 return hostfence.Consequences{}, hostclaim.LossConsequences{}, err
73 }
74 shown, actual, err := owner.InspectLoss(ctx, copy, record, box)
75 if err != nil {
76 return shown, actual, err
77 }
78 shown, err = detached(shown)
79 if err != nil {
80 return shown, actual, err
81 }
82 actual, err = detached(actual)
83 return shown, actual, err
84}
85
86func ownerSources(ctx context.Context, owner OperationOwner, o hostclaim.Observed, actual hostclaim.LossConsequences) ([]hostclaim.WriteSource, error) {
87 copy, err := detached(o)
88 if err != nil {
89 return nil, err
90 }
91 effects, err := detached(actual)
92 if err != nil {
93 return nil, err
94 }
95 sources, err := owner.LossWriteSources(ctx, copy, effects)
96 if err != nil {
97 return nil, err
98 }
99 return detached(sources)
100}
101
102func ownerHook(ctx context.Context, owner OperationOwner, o hostclaim.Observed, loss hostclaim.LossRecord, finish bool) error {
103 copy, err := detached(o)
104 if err != nil {
105 return err
106 }
107 record, err := detached(loss)
108 if err != nil {
109 return err
110 }
111 if finish {
112 return owner.FinishLoss(ctx, copy, record)
113 }
114 return owner.PrepareLoss(ctx, copy, record)
115}
116