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 "encoding/json"
6 "reflect"
7 "slices"
8 "strings"
9
10 lss3 "github.com/benbjohnson/litestream/s3"
11 "github.com/carlosframework/platform/internal/hostbox"
12 "github.com/carlosframework/platform/internal/hostclaim"
13 "github.com/carlosframework/platform/internal/hostfiles"
14 "github.com/carlosframework/platform/internal/hostguard"
15 "github.com/carlosframework/platform/internal/instancekey"
16)
17
18type claimEmptyRecovery func(context.Context, hostclaim.Observed, []hostclaim.AdmissionRecord) (bool, error)
19
20func rootEmptyRecovery(g hostguard.Guard) claimEmptyRecovery {
21 return func(ctx context.Context, o hostclaim.Observed, admissions []hostclaim.AdmissionRecord) (bool, error) {
22 if o.Claim == nil || o.Claim.Holder == nil || len(admissions) == 0 {
23 return false, nil
24 }
25 h := o.Claim.Holder
26 if len(admissions) > len(h.Admissions) || len(admissions) < len(h.Admissions)-1 || !reflect.DeepEqual(admissions, h.Admissions[:len(admissions)]) {
27 return false, hostbox.ErrChanged
28 }
29 captured := hostguard.Captured{Incarnation: h.Incarnation, Stamp: h.Stamp}
30 var view hostguard.FenceView
31 var err error
32 if g.Root != nil {
33 view, err = g.Root.View(ctx, o.Host, captured)
34 } else if g.FenceView != nil {
35 view, err = g.FenceView(ctx, o.Host, captured)
36 } else {
37 return false, nil
38 }
39 if err != nil {
40 return false, err
41 }
42 if view.Installation.Host != o.Host || view.Installation.Incarnation != h.Incarnation || view.Installation.Stamp != h.Stamp {
43 return false, hostbox.ErrChanged
44 }
45 predecessor := ""
46 for _, a := range admissions {
47 if a.Mode != "cold" || a.Covered || a.Box != view.Installation.Box || a.Predecessor != predecessor || a.Generation == "" {
48 return false, nil
49 }
50 found := false
51 for _, r := range view.Receipts {
52 if r.Target.Host != o.Host || r.Target.Incarnation != h.Incarnation || r.Target.Session != a.Session || r.Target.Box != a.Box || r.Target.Enrollment != view.Installation.Enrollment || r.Target.Op != "" {
53 continue
54 }
55 capturedReceipt := hostguard.Captured{Incarnation: r.Target.Incarnation, Stamp: r.Target.Stamp}
56 if r.Target.Stamp != h.Stamp && !slices.Contains(view.Retired, capturedReceipt) {
57 continue
58 }
59 var receipt StopReceipt
60 if json.Unmarshal(r.Bytes, &receipt) != nil {
61 return false, hostbox.ErrChanged
62 }
63 if receipt.Kind == StopReceiptStopped && receipt.Host == r.Target.Host && receipt.Incarnation == r.Target.Incarnation && receipt.InstallationStamp == r.Target.Stamp && receipt.WriterSession == r.Target.Session && receipt.FenceID == r.Target.FenceID && slices.Contains(receipt.CgroupIdentities, "unlaunched:"+digestStop(r.Target)) {
64 found = true
65 break
66 }
67 }
68 if !found {
69 return false, nil
70 }
71 predecessor = a.Generation
72 }
73 return true, nil
74 }
75}
76
77func (s *claimServingOwners) emptyBootstrap(ctx context.Context, o hostclaim.Observed) (bool, error) {
78 if claimEmptyBootstrap(o.Claim.Holder) {
79 return true, nil
80 }
81 if s.emptyRecovery == nil {
82 return false, nil
83 }
84 yes, err := s.emptyRecovery(ctx, o, o.Claim.Holder.Admissions)
85 if err != nil || !yes {
86 return false, err
87 }
88 return true, s.emptyLocation(ctx, o.Claim.Holder.Location, o.Claim.Holder.Admissions...)
89}
90
91func (s *claimServingOwners) emptyLocation(ctx context.Context, location string, admissions ...hostclaim.AdmissionRecord) error {
92 versions, err := s.Data.ListVersions(ctx, s.Bucket, strings.TrimSuffix(location, "/")+"/")
93 if err != nil {
94 return err
95 }
96 leaseKey := strings.TrimSuffix(location, "/") + "/" + lss3.DefaultLeasePath
97 allowed := map[string]bool{leaseKey: true}
98 for _, a := range admissions {
99 prefix := movesFilesPrefix(location, a.Generation)
100 key := hostfiles.ManifestKey(prefix)
101 for _, v := range versions {
102 if v.Key != key {
103 continue
104 }
105 if v.DeleteMarker || v.Size > 1<<20 {
106 return hostbox.ErrChanged
107 }
108 raw, err := s.Data.GetVersion(ctx, s.Bucket, key, v.VersionID)
109 if err != nil {
110 return err
111 }
112 var m hostfiles.Manifest
113 if json.Unmarshal(raw, &m) != nil || m.V != hostfiles.ManifestVersion || len(m.Entries) != 0 {
114 return hostbox.ErrChanged
115 }
116 for name, e := range m.Platform {
117 if name == instancekey.AdoptionItem && e.Deleted {
118 continue
119 }
120 if name != hostfiles.EnvItem || e.Deleted || e.Torn || e.Skipped != "" || !strings.HasPrefix(e.Object, prefix+"objects/") || !claimHexDigest(e.SHA256) {
121 return hostbox.ErrChanged
122 }
123 allowed[e.Object] = true
124 }
125 allowed[key] = true
126 }
127 }
128 for _, v := range versions {
129 if !allowed[v.Key] || v.Key != leaseKey && v.DeleteMarker {
130 return hostbox.ErrChanged
131 }
132 }
133 return nil
134}
135