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 "math"
6 "os"
7 "strings"
8 "time"
9
10 "github.com/carlosframework/platform/internal/rootstate"
11)
12
13const rootBakeKey = "bake.json"
14
15type rootBakeRecord struct {
16 Schema int `json:"schema"`
17 Revision uint64 `json:"revision"`
18 Complete map[string]uint64 `json:"complete"`
19 Observed time.Time `json:"observed"`
20 Clock FirstSeen `json:"clock"`
21}
22
23func validRootBake(r rootBakeRecord) bool {
24 if r.Schema != 1 || r.Revision == 0 || r.Observed.IsZero() || r.Complete == nil ||
25 r.Clock.Channels == nil || math.IsNaN(r.Clock.Jitter) || r.Clock.Jitter < 0 || r.Clock.Jitter >= .25 {
26 return false
27 }
28 for key, revision := range r.Complete {
29 parts := strings.Split(key, clockSep)
30 if len(parts) != 2 || !ValidAccount(parts[0]) || !ValidApp(parts[1]) || revision == 0 || revision > r.Revision {
31 return false
32 }
33 }
34 for _, versions := range r.Clock.Channels {
35 if versions == nil || len(versions) > firstSeenKeep {
36 return false
37 }
38 for version, at := range versions {
39 if !ValidVersion(version) || at.IsZero() || at.After(r.Observed) {
40 return false
41 }
42 }
43 }
44 for _, mem := range r.Clock.Production {
45 if mem == nil || mem.OffFirstSeen.After(r.Observed) || mem.OffSetAt.IsZero() != mem.OffFirstSeen.IsZero() {
46 return false
47 }
48 }
49 for _, mem := range r.Clock.Holds {
50 if mem == nil || mem.Window < 0 || mem.Pending < 0 || mem.PendingAt.After(r.Observed) || mem.DeletedAt.After(r.Observed) {
51 return false
52 }
53 }
54 return true
55}
56
57func (h *RootHistory) writeBake(r rootBakeRecord) error {
58 if h == nil || h.Store == nil || !validRootBake(r) {
59 return os.ErrPermission
60 }
61 raw, err := json.Marshal(r)
62 if err != nil {
63 return err
64 }
65 if len(raw) > rootstate.MaxRecord {
66 return os.ErrPermission
67 }
68 return h.Store.Write(rootBakeKey, raw)
69}
70
71func (h *RootHistory) LoadBake(bindings []HistoryBinding, now time.Time) (*FirstSeen, error) {
72 if err := h.reconstructionReady(); err != nil {
73 return nil, err
74 }
75 if h == nil || h.Store == nil || now.IsZero() {
76 return nil, os.ErrPermission
77 }
78 raw, err := h.Store.Read(rootBakeKey)
79 if err != nil {
80 return nil, err
81 }
82 var r rootBakeRecord
83 if len(raw) > rootstate.MaxRecord || strictCanonicalJSON(raw, &r) != nil || !validRootBake(r) || now.Before(r.Observed) {
84 return nil, os.ErrPermission
85 }
86 for _, b := range bindings {
87 if !b.valid() || r.Complete[prodKey(b.Account, b.App)] == 0 {
88 return nil, os.ErrPermission
89 }
90 }
91 if err := h.Store.Write(rootBakeKey, raw); err != nil {
92 return nil, err
93 }
94 clock := r.Clock
95 clock.migrated = true
96 clock.save = func() error {
97 if r.Revision == ^uint64(0) {
98 return os.ErrPermission
99 }
100 r.Revision++
101 r.Observed, r.Clock = now.UTC(), clock
102 for key := range r.Complete {
103 r.Complete[key] = r.Revision
104 }
105 return h.writeBake(r)
106 }
107 return &clock, nil
108}
109