carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "encoding/json"
5 "os"
6 "slices"
7 "strings"
8)
9
10func replacementReconstruction(g HistoryReconstruction, prior *reconstructionIntent) (HistoryReconstruction, error) {
11 raw, err := json.Marshal(prior.Grant)
12 if err != nil || reconstructionDigest(raw) != prior.Digest || g.Replaces != prior.Digest || g.Identity != prior.Grant.Identity || g.Revision != prior.Grant.Revision || g.Account != prior.Grant.Account || g.App != prior.Grant.App || g.Operation == prior.Grant.Operation || len(prior.Images) != len(prior.Grant.Hosts)+2 {
13 return g, os.ErrPermission
14 }
15 // Copy signed input before adding restrictive evidence from the old intent.
16 g.Hosts = append([]HistoryReconstructionHost(nil), g.Hosts...)
17 holds := map[string]HistoryReconstructionHold{}
18 for channel, bound := range g.Holds {
19 holds[channel] = bound
20 }
21 g.Holds = holds
22 for i := range g.Hosts {
23 g.Hosts[i].Refusals = append([]Refusal(nil), g.Hosts[i].Refusals...)
24 }
25 expected := map[string]bool{rootBakeKey: true, historyAppKey(g.Account, g.App): true}
26 for _, old := range prior.Grant.Hosts {
27 if !slices.ContainsFunc(g.Hosts, func(h HistoryReconstructionHost) bool { return h.Host == old.Host }) {
28 return g, os.ErrPermission
29 }
30 expected[historyKey(HistoryBinding{Host: old.Host, Account: g.Account, App: g.App})] = true
31 }
32 for _, im := range prior.Images {
33 if !expected[im.Name] {
34 return g, os.ErrPermission
35 }
36 delete(expected, im.Name)
37 switch im.Name {
38 case historyAppKey(g.Account, g.App):
39 var app historyApp
40 if strictCanonicalJSON(im.After, &app) != nil || !validHistoryApp(app) || app.Account != g.Account || app.App != g.App {
41 return g, os.ErrPermission
42 }
43 case rootBakeKey:
44 var bake rootBakeRecord
45 if strictCanonicalJSON(im.After, &bake) != nil || !validRootBake(bake) {
46 return g, os.ErrPermission
47 }
48 if p := bake.Clock.Production[prodKey(g.Account, g.App)]; p != nil {
49 g.Production = g.Production || p.WasProduction
50 if p.NewestSetAt.After(g.ProductionSetAt) {
51 g.ProductionSetAt = p.NewestSetAt
52 }
53 }
54 for key, mem := range bake.Clock.Holds {
55 if !strings.HasPrefix(key, holdKeyPrefix(g.Account, g.App)) {
56 continue
57 }
58 channel := strings.TrimPrefix(key, holdKeyPrefix(g.Account, g.App))
59 b := g.Holds[channel]
60 if mem.Window > b.Window {
61 b.Window = mem.Window
62 }
63 if mem.SetAt.After(b.SetAt) {
64 b.SetAt = mem.SetAt
65 }
66 g.Holds[channel] = b
67 }
68 default:
69 var r historyRecord
70 if strictCanonicalJSON(im.After, &r) != nil || !validHistory(r, r.Binding) || r.Binding.Account != g.Account || r.Binding.App != g.App || historyKey(r.Binding) != im.Name || r.Attempt != nil {
71 return g, os.ErrPermission
72 }
73 for i := range g.Hosts {
74 if g.Hosts[i].Host != r.Binding.Host {
75 continue
76 }
77 for _, ref := range r.Refusals.Values {
78 ref.Recovered = false
79 if !slices.Contains(g.Hosts[i].Refusals, ref) {
80 g.Hosts[i].Refusals = append(g.Hosts[i].Refusals, ref)
81 }
82 }
83 }
84 }
85 }
86 return g, nil
87}
88