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 "fmt"
9 "github.com/carlosframework/platform/internal/hostbox"
10 "github.com/carlosframework/platform/internal/hostclaim"
11)
12
13type claimRecoveryJournal struct {
14 Prior hostclaim.Observed
15 Generation string
16 Sessions []uint64
17}
18
19func claimRecoveryJournalID(o hostclaim.Observed) string {
20 h := o.Claim.Holder
21 sum := sha256.Sum256([]byte(fmt.Sprintf("%s\x00%s\x00%d\x00%d\x00%s", o.Host, h.Incarnation, h.Stamp, h.Session, o.Claim.Op.ID)))
22 return hex.EncodeToString(sum[:])
23}
24
25func (j claimRecoveryJournal) successor(o hostclaim.Observed, box string) bool {
26 if j.Prior.Claim == nil || j.Prior.Claim.Holder == nil || o.Claim == nil || o.Claim.Holder == nil {
27 return false
28 }
29 p, c := j.Prior.Claim, o.Claim
30 a, b := p.Holder, c.Holder
31 if j.Prior.Host != o.Host || a.Incarnation != b.Incarnation || p.Op.ID != c.Op.ID || p.Op.Kind != c.Op.Kind || p.Op.Phase != c.Op.Phase || p.Op.Exec != c.Op.Exec || c.Op.Exec != box || b.WriterBox != box || b.Session <= a.Session || b.Stamp != b.Session || b.Generation != j.Generation || len(b.Admissions) == 0 {
32 return false
33 }
34 allowed := false
35 for _, session := range j.Sessions {
36 if session == b.Session {
37 allowed = true
38 break
39 }
40 }
41 if !allowed {
42 return false
43 }
44 last := b.Admissions[len(b.Admissions)-1]
45 return last.Session == b.Session && last.Box == box && last.Generation == j.Generation && last.Mode == "cold" && last.Predecessor == recoveryPredecessor(j.Prior).Generation
46}
47
48func (h claimCrossBoxHandover) recoveryJournal(ctx context.Context, o hostclaim.Observed) (string, string, *claimRecoveryJournal, error) {
49 if h.Registry == nil {
50 return "", "", nil, hostbox.ErrTypedInventory
51 }
52 bodies, err := h.Registry.PendingRecoveryHandovers(ctx)
53 if err != nil {
54 return "", "", nil, err
55 }
56 var found *claimRecoveryJournal
57 var id, body string
58 for key, raw := range bodies {
59 var j claimRecoveryJournal
60 if json.Unmarshal([]byte(raw), &j) != nil || j.Prior.Claim == nil || j.Prior.Claim.Holder == nil || j.Generation == "" {
61 return "", "", nil, hostbox.ErrChanged
62 }
63 if j.Prior.Host != o.Host {
64 continue
65 }
66 if found != nil || key != claimRecoveryJournalID(j.Prior) || (!hostclaim.SameWriterIdentity(j.Prior, o) && !j.successor(o, h.Box)) {
67 return "", "", nil, hostbox.ErrChanged
68 }
69 found, id, body = &j, key, raw
70 }
71 return id, body, found, nil
72}
73
74func (h claimCrossBoxHandover) prepareRecovery(ctx context.Context, o hostclaim.Observed, in hostclaim.Admission) (hostclaim.Admission, error) {
75 if !claimInitialSourceRecovery(o, h.Box) && !(claimRecoveryObservation(o) && claimRemotePredecessor(o, h.Box)) {
76 return in, nil
77 }
78 if err := h.recoveryEvidence(ctx, o); err != nil {
79 return in, err
80 }
81 id, body, j, err := h.recoveryJournal(ctx, o)
82 if err != nil {
83 return in, err
84 }
85 if j != nil {
86 if !hostclaim.SameWriterIdentity(j.Prior, o) {
87 return in, hostbox.ErrChanged
88 }
89 in.NewGeneration = j.Generation
90 session := o.Claim.Gen + 1
91 for _, existing := range j.Sessions {
92 if existing == session {
93 return in, nil
94 }
95 }
96 j.Sessions = append(j.Sessions, session)
97 raw, err := json.Marshal(j)
98 if err != nil {
99 return in, err
100 }
101 return in, h.Registry.ReviseRecoveryHandover(ctx, id, body, string(raw))
102 }
103 if in.Mode != "cold" || in.KeepStamp || in.NewGeneration == "" {
104 return in, hostbox.ErrChanged
105 }
106 raw, err := json.Marshal(claimRecoveryJournal{Prior: o, Generation: in.NewGeneration, Sessions: []uint64{o.Claim.Gen + 1}})
107 if err != nil {
108 return in, err
109 }
110 return in, h.Registry.BeginRecoveryHandover(ctx, claimRecoveryJournalID(o), string(raw))
111}
112
113func (h claimCrossBoxHandover) pendingRecovery(ctx context.Context, o hostclaim.Observed) (hostclaim.Observed, bool, error) {
114 if !claimRecoveryObservation(o) || o.Claim.Holder == nil || h.Registry == nil {
115 return o, false, nil
116 }
117 id, body, j, err := h.recoveryJournal(ctx, o)
118 if err != nil || j == nil {
119 return o, false, err
120 }
121 if hostclaim.SameWriterIdentity(j.Prior, o) {
122 return o, false, nil
123 }
124 if h.Launches == nil {
125 return o, false, hostbox.ErrTypedInventory
126 }
127 actor, err := h.Identity.Verify(ctx)
128 if err != nil {
129 return o, false, err
130 }
131 next := o.Claim.Holder
132 launches, err := h.Launches.SessionLaunches(ctx, o.Host, next.Incarnation, next.Stamp, next.Session, h.Box, actor.Enrollment)
133 if err != nil {
134 return o, false, err
135 }
136 if len(launches) > 0 {
137 return o, false, h.Registry.CompleteRecoveryHandover(ctx, id, body)
138 }
139 return j.Prior, true, nil
140}
141func (h claimCrossBoxHandover) completeRecovery(ctx context.Context, o hostclaim.Observed) error {
142 if !claimRecoveryObservation(o) || o.Claim.Holder == nil || h.Registry == nil {
143 return nil
144 }
145 id, body, j, err := h.recoveryJournal(ctx, o)
146 if err != nil || j == nil {
147 return err
148 }
149 if !j.successor(o, h.Box) {
150 return hostbox.ErrChanged
151 }
152 return h.Registry.CompleteRecoveryHandover(ctx, id, body)
153}
154