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 "bytes"
5 "context"
6 "encoding/json"
7 "errors"
8 "os"
9 "path/filepath"
10 "reflect"
11 "sort"
12 "strings"
13
14 "github.com/carlosframework/platform/internal/hostbox"
15 "github.com/carlosframework/platform/internal/hostguard"
16 "github.com/carlosframework/platform/internal/rootstate"
17)
18
19func productionGenerationHandovers(ctx context.Context) ([]hostbox.GenerationHandover, error) {
20 path := filepath.Join(claimRetireJournalDir("", ""), "serving")
21 s, err := rootstate.Open(path, false, false)
22 if errors.Is(err, os.ErrNotExist) {
23 return nil, nil
24 }
25 if err != nil {
26 return nil, err
27 }
28 defer s.Close()
29 out, err := generationReadHandovers(ctx, s)
30 if err != nil {
31 return nil, err
32 }
33 directory, err := os.Open(path)
34 if err != nil {
35 return nil, err
36 }
37 defer directory.Close()
38 if err = directory.Sync(); err != nil {
39 return nil, err
40 }
41 return out, nil
42}
43
44func generationReadHandovers(ctx context.Context, s rootstate.Store) ([]hostbox.GenerationHandover, error) {
45 names, err := s.Names()
46 if err != nil {
47 return nil, err
48 }
49 if len(names) > 8192 {
50 return nil, os.ErrPermission
51 }
52 sort.Strings(names)
53 var out []hostbox.GenerationHandover
54 for _, name := range names {
55 if err := ctx.Err(); err != nil {
56 return nil, err
57 }
58 if strings.HasPrefix(name, ".handover-") {
59 continue
60 }
61 raw, err := s.Read(name)
62 if err != nil {
63 return nil, err
64 }
65 j, err := hostbox.DecodeGenerationHandover(raw)
66 if err != nil || name != j.ID+".json" {
67 return nil, os.ErrPermission
68 }
69 out = append(out, j)
70 }
71 return out, nil
72}
73
74func generationTransfers(ctx context.Context, snapshot generationInventory) (map[int]int, error) {
75 out := map[int]int{}
76 for _, j := range snapshot.Handovers {
77 if err := ctx.Err(); err != nil {
78 return nil, err
79 }
80 raw, err := json.Marshal(j)
81 if err != nil {
82 return nil, err
83 }
84 if _, err = hostbox.DecodeGenerationHandover(raw); err != nil {
85 return nil, err
86 }
87 p, n := j.Prior, j.Successor
88 oldIndex, nextIndex := -1, -1
89 for i, payload := range snapshot.Payloads {
90 in := payload.Inventory
91 if in.Host != p.Host || in.Incarnation != p.Incarnation {
92 continue
93 }
94 if in.InstallationStamp == p.Stamp {
95 if oldIndex != -1 || j.ServingInstallation == nil || payload.Revision != j.ServingInstallation.InventoryRevision || !reflect.DeepEqual(in, j.Inventory) {
96 return nil, os.ErrPermission
97 }
98 oldIndex = i
99 }
100 if in.InstallationStamp == n.Stamp {
101 if nextIndex != -1 || in.Box != n.Box || in.Enrollment != n.Enrollment || in.DirectoryRevision != n.DirectoryRevision {
102 return nil, os.ErrPermission
103 }
104 nextIndex = i
105 }
106 }
107 if oldIndex < 0 || nextIndex < 0 {
108 return nil, os.ErrPermission
109 }
110 if _, exists := out[oldIndex]; exists {
111 return nil, os.ErrPermission
112 }
113 bound := false
114 for _, view := range snapshot.Installations {
115 if view.Host != p.Host {
116 continue
117 }
118 in, err := generationInstallation(view, generationOwner(p))
119 if err != nil || rootHistoryInstallation(in) != rootHistoryInstallation(*j.ServingInstallation) {
120 return nil, os.ErrPermission
121 }
122 retired := false
123 for _, old := range view.Retired {
124 if old == (hostguard.Captured{Incarnation: p.Incarnation, Stamp: p.Stamp}) {
125 retired = true
126 }
127 }
128 next := p
129 next.Stamp, next.Session = n.Stamp, n.Session
130 if _, err := generationInstallation(view, generationOwner(next)); err != nil || !retired {
131 return nil, os.ErrPermission
132 }
133 receipt := p.Session == 0 && reflect.DeepEqual(j.Receipt, hostguard.FenceReceipt{})
134 for _, r := range view.Receipts {
135 if r.Target == p && j.Receipt.Target == p && bytes.Equal(r.Bytes, j.Receipt.Bytes) && verifyRetirementReceipt(ctx, r) == nil {
136 receipt = true
137 }
138 }
139 if !receipt {
140 return nil, os.ErrPermission
141 }
142 bound = true
143 }
144 if !bound {
145 return nil, os.ErrPermission
146 }
147 next := snapshot.Payloads[nextIndex].Inventory
148 for _, old := range j.Inventory.Entries {
149 matches := 0
150 for _, entry := range next.Entries {
151 if entry.Kind != old.Kind || entry.ManagedRoot != old.ManagedRoot || entry.RelativePath != old.RelativePath || entry.ArtifactID != old.ArtifactID {
152 continue
153 }
154 if old.Materialization != "materialized" || entry.Materialization != "materialized" || old.Ownership != j.Before.Route.ClaimOwner || entry.Ownership != j.After.Route.ClaimOwner {
155 return nil, os.ErrPermission
156 }
157 switch old.Kind {
158 case "route":
159 if old.RowIdentity != j.Before.Route.ClaimRowID || entry.RowIdentity != j.After.Route.ClaimRowID {
160 return nil, os.ErrPermission
161 }
162 case "path":
163 if old.Type != entry.Type || old.Inode != entry.Inode || (old.Identity.Boot != "" && !old.Identity.Same(entry.Identity)) || (old.Identity.Boot == "" && (old.Device != entry.Device || entry.Identity.Boot != p.BootID || entry.Identity.Device != entry.Device || entry.Identity.Inode != entry.Inode)) {
164 return nil, os.ErrPermission
165 }
166 case "timer":
167 found := false
168 for i, timer := range j.Before.Timers {
169 if old.RelativePath == p.Host+"/"+timer.Name && old.RowIdentity == timer.ClaimRowID && entry.RowIdentity == j.After.Timers[i].ClaimRowID {
170 found = true
171 }
172 }
173 if !found {
174 return nil, os.ErrPermission
175 }
176 default:
177 return nil, os.ErrPermission
178 }
179 matches++
180 }
181 if matches != 1 {
182 return nil, os.ErrPermission
183 }
184 }
185 out[oldIndex] = nextIndex
186 }
187 return out, nil
188}
189