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 "os"
6 "path/filepath"
7
8 "github.com/carlosframework/platform/internal/hostclaim"
9 "github.com/carlosframework/platform/internal/hostguard"
10 "github.com/carlosframework/platform/internal/provision"
11 "github.com/carlosframework/platform/internal/release"
12 "github.com/carlosframework/platform/internal/rootstate"
13)
14
15type generationRecord struct {
16 Key, ETag string
17 Record provision.Record
18}
19
20func generationRequiredInventory(ctx context.Context, store release.Store, snapshot generationInventory, fresh func(hostguard.RootInstallation) error) (generationInventory, error) {
21 var zero generationInventory
22 if store == nil {
23 return zero, os.ErrPermission
24 }
25 retirements, err := generationRetirements(ctx, snapshot)
26 if err != nil {
27 return zero, err
28 }
29 snapshot.Records = nil
30 for _, view := range snapshot.Installations {
31 if err := ctx.Err(); err != nil {
32 return zero, err
33 }
34 in := view.Installation
35 if _, err := inventoryTimerNames(in.Artifacts); err != nil {
36 return zero, err
37 }
38 if in.Record != provision.RecordKey(in.Account, in.App, in.Host) {
39 return zero, os.ErrPermission
40 }
41 retired := false
42 for _, old := range view.Retired {
43 if old.Incarnation == in.Incarnation && old.Stamp == in.Stamp {
44 retired = true
45 }
46 }
47 if retired {
48 for i, payload := range snapshot.Payloads {
49 if payload.Inventory.Host == in.Host && len(payload.Inventory.Entries) != 0 && retirements[i] == "" {
50 return zero, os.ErrPermission
51 }
52 }
53 continue
54 }
55 rec, etag, err := provision.GetRecordETag(ctx, store, in.Account, in.App, in.Host)
56 if err != nil {
57 return zero, err
58 }
59 if rec.Schema != 1 || !release.ValidAccount(rec.Account) || !release.ValidApp(rec.App) || provision.ValidateShape(*rec) != nil {
60 return zero, os.ErrPermission
61 }
62 inc, err := hostclaim.Incarnation(rec.Account, rec.App, rec.Host, rec.CreatedAt)
63 if err != nil || etag == "" || inc != in.Incarnation {
64 return zero, os.ErrPermission
65 }
66 snapshot.Records = append(snapshot.Records, generationRecord{Key: in.Record, ETag: etag, Record: *rec})
67
68 }
69 return generationLocalObligations(ctx, snapshot, fresh)
70}
71
72func generationLocalObligations(ctx context.Context, snapshot generationInventory, fresh func(hostguard.RootInstallation) error) (generationInventory, error) {
73 var zero generationInventory
74 snapshot.Unlaunched = map[string]string{}
75 installs := map[string]hostguard.FenceView{}
76 for _, view := range snapshot.Installations {
77 installs[view.Host] = view
78 }
79 if err := generationPayloadBindings(snapshot.Payloads, installs); err != nil {
80 return zero, err
81 }
82 if err := generationRetainedTimers(snapshot); err != nil {
83 return zero, err
84 }
85 for _, view := range snapshot.Installations {
86 if err := ctx.Err(); err != nil {
87 return zero, err
88 }
89 in := view.Installation
90 if !release.ValidAccount(in.Account) || !release.ValidApp(in.App) || in.Record != provision.RecordKey(in.Account, in.App, in.Host) {
91 return zero, os.ErrPermission
92 }
93 if _, err := hostclaim.Key(in.Host); err != nil {
94 return zero, err
95 }
96 unlaunched, err := generationUnlaunched(view, snapshot, fresh)
97 if err != nil {
98 return zero, err
99 }
100 if unlaunched {
101 snapshot.Unlaunched[in.Host] = rootHistoryInstallation(in)
102 }
103 for _, record := range snapshot.Records {
104 if record.Key == in.Record && record.Record.DerivesPaths() {
105 in.Artifacts.StateDirectory = true
106 }
107 }
108 for _, p := range snapshot.Payloads {
109 payload := p.Inventory
110 if payload.Host != in.Host {
111 continue
112 }
113 required := len(payload.Entries) > 0 || !in.Empty || payload.InstallationStamp != in.Stamp
114 for _, launch := range snapshot.Launches {
115 t := launch.Target
116 if t.Host == payload.Host && t.Incarnation == payload.Incarnation && t.Stamp == payload.InstallationStamp {
117 required = true
118 }
119 }
120 if !required {
121 // Retained predecessors are checked below as nonempty obligations.
122 // Without one, an empty payload needs root's creation evidence.
123 if len(view.Retired) == 0 {
124 if fresh == nil {
125 return zero, os.ErrPermission
126 }
127 if err := fresh(in); err != nil {
128 return zero, err
129 }
130 }
131 continue
132 }
133 state, err := filepath.Rel(dataDir(), provision.InstanceDir(dataDir(), in.Host))
134 if err != nil || !filepath.IsLocal(state) || state == "." {
135 return zero, os.ErrPermission
136 }
137 routes, directories := 0, 0
138 for _, entry := range payload.Entries {
139 if entry.Kind == "route" {
140 if entry.ManagedRoot != "registry" || entry.RelativePath != in.Host || entry.RowIdentity == "" {
141 return zero, os.ErrPermission
142 }
143 routes++
144 }
145 if entry.Kind == "path" && entry.ManagedRoot == "data" && entry.RelativePath == state {
146 if entry.Type != 0040000 || entry.Device == 0 || entry.Inode == 0 {
147 return zero, os.ErrPermission
148 }
149 directories++
150 }
151 }
152 if routes != 1 {
153 return zero, os.ErrPermission
154 }
155 if in.Artifacts.StateDirectory && directories != 1 {
156 if directories != 0 || payload.InstallationStamp != in.Stamp || len(view.Retired) != 0 || !unlaunched {
157 return zero, os.ErrPermission
158 }
159 if _, err := os.Lstat(filepath.Join(dataDir(), state)); !os.IsNotExist(err) {
160 return zero, os.ErrPermission
161 }
162 }
163 }
164 }
165 return snapshot, ctx.Err()
166}
167
168func generationUnlaunched(view hostguard.FenceView, snapshot generationInventory, fresh func(hostguard.RootInstallation) error) (bool, error) {
169 if fresh == nil || view.Installation.Empty {
170 return false, nil
171 }
172 for _, launch := range snapshot.Launches {
173 if launch.Target.Host == view.Host {
174 return false, nil
175 }
176 }
177 in := view.Installation
178 for _, old := range view.Retired {
179 if old.Incarnation == in.Incarnation && old.Stamp < in.Stamp {
180 in.Stamp = old.Stamp
181 }
182 }
183 if err := fresh(in); err != nil {
184 return false, err
185 }
186 return true, nil
187}
188
189func productionGenerationCreation(in hostguard.RootInstallation) error {
190 state, err := rootstate.Open(release.RootHistoryDir, false, false)
191 if err != nil {
192 return err
193 }
194 defer state.Close()
195 fresh, err := (&release.RootHistory{Store: state}).FreshInstallation(release.HistoryBinding{Host: in.Host, Account: in.Account, App: in.App}, rootHistoryInstallation(in))
196 if err != nil {
197 return err
198 }
199 if !fresh {
200 return os.ErrPermission
201 }
202 return nil
203}
204