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 "encoding/json"
5 "os"
6 "strings"
7
8 "github.com/carlosframework/platform/internal/release"
9 "github.com/carlosframework/platform/internal/rootstate"
10)
11
12func recoveryResultKey(a rollbackAttempt) string {
13 return rollbackHostKey(a.Host) + "." + a.ID + ".recovery"
14}
15
16func recoveryHistoryOutcome(a rollbackAttempt) (release.HistoryRecovery, error) {
17 if !a.Independent || a.History == nil || a.HistoryBinding.Host != a.Host || a.History.ID != a.ID || len(a.ID) != 32 {
18 return release.HistoryRecovery{}, os.ErrPermission
19 }
20 return release.HistoryRecovery{Binding: a.HistoryBinding, Attempt: *a.History, Settled: a.Settled, Failed: a.Failed, Recovering: a.Recovering, Verified: a.Verified}, nil
21}
22
23func (c rootRestartChecks) copyRecoveryHistory(h *release.RootHistory, binding release.HistoryBinding) ([]release.HistoryRecovery, error) {
24 if c.attempts == nil || h == nil {
25 return nil, os.ErrPermission
26 }
27 s, err := c.attempts()
28 if err != nil {
29 return nil, err
30 }
31 defer s.Close()
32 names, err := s.Names()
33 if err != nil {
34 return nil, err
35 }
36 var unresolved []release.HistoryRecovery
37 for _, name := range names {
38 if !strings.HasSuffix(name, ".attempt") && !strings.HasSuffix(name, ".recovery") {
39 continue
40 }
41 raw, err := s.Read(name)
42 if err != nil {
43 return nil, err
44 }
45 var a rollbackAttempt
46 if len(raw) > rootstate.MaxRecord || json.Unmarshal(raw, &a) != nil || (name != rollbackJournalKey(a.Host) && name != recoveryResultKey(a)) {
47 return nil, os.ErrPermission
48 }
49 if !a.Independent {
50 continue
51 }
52 r, err := recoveryHistoryOutcome(a)
53 if err != nil {
54 return nil, err
55 }
56 if r.Binding.Account != binding.Account || r.Binding.App != binding.App {
57 continue
58 }
59 if err := s.Write(name, raw); err != nil {
60 return nil, err
61 }
62 if err := h.RetainRecovery(r); err != nil {
63 return nil, err
64 }
65 if !r.Settled || (!r.Failed && !r.Recovering && !r.Verified) {
66 unresolved = append(unresolved, r)
67 }
68 }
69 return unresolved, nil
70}
71
72func (c rootRestartChecks) retainRecoveryHistory(h *release.RootHistory, binding release.HistoryBinding) error {
73 unresolved, err := c.copyRecoveryHistory(h, binding)
74 if err != nil {
75 return err
76 }
77 return checkUnresolvedRecoveries(h, unresolved)
78}
79
80func checkUnresolvedRecoveries(h *release.RootHistory, unresolved []release.HistoryRecovery) error {
81 for _, r := range unresolved {
82 if !r.Settled {
83 return os.ErrPermission
84 }
85 retired, err := h.RecoveryRetired(r)
86 if err != nil {
87 return err
88 }
89 if !retired {
90 return os.ErrPermission
91 }
92 }
93 return nil
94}
95
96func (c rootRestartChecks) mintRefusals(h *release.RootHistory, binding release.HistoryBinding) ([]release.Refusal, error) {
97 if err := c.retainRecoveryHistory(h, binding); err != nil {
98 return nil, err
99 }
100 refs, err := h.MintRefusals(binding)
101 if err != nil {
102 return nil, err
103 }
104 if len(refs) > 256 {
105 return nil, os.ErrPermission
106 }
107 return refs, nil
108}
109