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 "crypto/sha256"
6 "encoding/hex"
7 "encoding/json"
8 "errors"
9 "github.com/carlosframework/platform/internal/activator"
10 "github.com/carlosframework/platform/internal/provision"
11 "github.com/carlosframework/platform/internal/registry"
12 "os"
13 "strings"
14)
15
16func rootRecordRoute(rec *provision.Record) (registry.Route, error) {
17 if rec.Unit == "" {
18 return registry.Route{}, os.ErrPermission
19 }
20 rt := registry.Route{Host: rec.Host, Account: rec.Account, App: rec.App, Kind: rec.KindOrDefault(), Unit: rec.Unit,
21 Addr: rec.Addr, Addrs: strings.Join(rec.Addrs, ","), Channel: rec.Channel, Region: rec.Region, Pool: rec.Pool}
22 if rec.DerivesPaths() {
23 unit := activator.UnitName(rec.Host)
24 if rec.Unit != "" && rec.Unit != unit {
25 return registry.Route{}, os.ErrPermission
26 }
27 rt.Unit, rt.Socket, rt.Backing = unit, provision.DefaultSocket(rec.Host), registry.BackingUnit
28 }
29 if (rt.Unit != "" && !registry.ValidUnitName(rt.Unit)) || (rt.Kind != "instance" && rt.Kind != "service") {
30 return registry.Route{}, os.ErrPermission
31 }
32 if h := rec.HealthOrDefault(); h != nil {
33 body, err := json.Marshal(h.WithDefaults())
34 if err != nil {
35 return registry.Route{}, err
36 }
37 rt.Health = string(body)
38 }
39 if rt.Socket == "" && len(rec.AddrsOrDefault()) == 0 {
40 return registry.Route{}, os.ErrPermission
41 }
42
43 return rt, nil
44}
45
46func sameProofCandidate(candidate, r registry.Route, declaredUnit string) bool {
47 return candidate.Account == r.Account && candidate.App == r.App && candidate.Host == r.Host && (candidate.Unit == declaredUnit || candidate.Unit == r.Unit) && candidate.Socket == r.Socket && candidate.Addr == r.Addr && candidate.Addrs == r.Addrs && candidate.Health == r.Health && candidate.Kind == r.Kind && candidate.Backing == r.Backing
48}
49func (s *proofBindingSource) needsRestart(ctx context.Context, candidate registry.Route) (bool, error) {
50 evidence, err := s.record(ctx, candidate.Host)
51 if err != nil {
52 if errors.Is(err, os.ErrNotExist) && candidate.Unit == "" {
53 return false, nil
54 }
55 return false, err
56 }
57 rec := evidence.record
58 if evidence.local {
59 _, err := s.forHost(ctx, candidate.Host)
60 return err == nil, err
61 }
62 if rec.Unit == "" {
63 return false, nil
64 }
65 if _, err := s.claimIdentity(ctx, rec); err != nil {
66 return false, err
67 }
68 rt, err := rootRecordRoute(rec)
69 if err != nil {
70 return false, err
71 }
72 if !sameProofCandidate(candidate, rt, rec.Unit) {
73 return false, os.ErrPermission
74 }
75 return rt.Unit != "", nil
76}
77
78func (s *proofBindingSource) claimIdentity(ctx context.Context, rec *provision.Record) (string, error) {
79 if s.claim == nil && s.box == nil {
80 return "", nil
81 }
82 if s.claim == nil || s.box == nil {
83 return "", os.ErrPermission
84 }
85 box, err := s.box(ctx)
86 if err != nil {
87 return "", err
88 }
89 o, err := s.claim(ctx, rec.Host)
90 if err != nil {
91 return "", err
92 }
93 if o.Absent || o.ETag == "" || o.Host != rec.Host || o.Claim == nil || o.Claim.Host != rec.Host || o.Claim.Holder == nil {
94 return "", os.ErrPermission
95 }
96 h := o.Claim.Holder
97 if h.Key != provision.RecordKey(rec.Account, rec.App, rec.Host) || h.Account != rec.Account || h.App != rec.App || h.Incarnation == "" || h.Stamp == 0 || h.Session == 0 || box == "" || h.WriterBox != box || o.Claim.Op.Exec != box {
98 return "", os.ErrPermission
99 }
100 for _, lost := range o.Claim.Lost {
101 if lost == box {
102 return "", os.ErrPermission
103 }
104 }
105 b, err := json.Marshal(o.Claim)
106 if err != nil {
107 return "", err
108 }
109 sum := sha256.Sum256(b)
110 return o.ETag + "@" + hex.EncodeToString(sum[:]) + "@", nil
111}
112