carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostclaim
2
3import (
4 "context"
5 "encoding/json"
6 "errors"
7 "io"
8 "reflect"
9 "strings"
10 "testing"
11
12 "github.com/carlosframework/platform/internal/hostwrite"
13 "github.com/carlosframework/platform/internal/release"
14)
15
16func TestPlan15ActivationDoesNotTrustRetainedAdmissions(t *testing.T) {
17 t.Setenv("CARLOS_HOST_CLAIM", "")
18 t.Setenv("CARLOS_HOST_CLAIM_ORIGINS", "")
19 s := release.NewDirStore(t.TempDir())
20 o := servingClaim(t, t.Context(), s)
21 o.Claim.Holder.Admissions = append(o.Claim.Holder.Admissions, AdmissionRecord{Region: "ie", Generation: "injected", Mode: "cold"})
22 key, _ := Key(o.Host)
23 body, _ := json.Marshal(o.Claim)
24 var err error
25 o.ETag, err = s.Put(t.Context(), key, body, o.ETag)
26 if err != nil {
27 t.Fatal(err)
28 }
29 plan15Enable(t)
30 o = updateGenerationTest(t, t.Context(), s, o, plan15Admit(o, "after-boundary", "ie"))
31 if o.Claim.Origins.Count != 1 {
32 t.Fatalf("migration seeded %d origins", o.Claim.Origins.Count)
33 }
34 plan15Proof(t, s, o, "injected", "ie", false)
35 plan15Proof(t, s, o, "after-boundary", "ie", true)
36}
37
38func TestPlan15UpdateAuthenticatesObservedHead(t *testing.T) {
39 for _, erase := range []bool{false, true} {
40 t.Run(map[bool]string{false: "forged", true: "erased"}[erase], func(t *testing.T) {
41 plan15Enable(t)
42 s := release.NewDirStore(t.TempDir())
43 o := servingClaim(t, t.Context(), s)
44 o = updateGenerationTest(t, t.Context(), s, o, plan15Admit(o, "real", "ie"))
45 stored := *o.Claim.Origins
46 if erase {
47 o.Claim.Origins = nil
48 } else {
49 o.Claim.Origins = &OriginHead{strings.Repeat("f", 64), 1}
50 t.Setenv("CARLOS_HOST_CLAIM_ORIGINS", "")
51 }
52 if _, err := Update(t.Context(), s, o, func(*Claim) error { return nil }); !errors.Is(err, ErrConflict) {
53 t.Fatalf("edited head: %v", err)
54 }
55 fresh, err := Read(t.Context(), s, o.Host)
56 if err != nil || !reflect.DeepEqual(fresh.Claim.Origins, &stored) {
57 t.Fatalf("stored head changed: %+v %v", fresh, err)
58 }
59 })
60 }
61}
62
63func TestPlan15SwitchParsingMatchesClaim(t *testing.T) {
64 for _, claim := range []string{"", " ", "1", "true", " 1", "false", "0"} {
65 for _, origins := range []string{"", " ", "1", "true", " 1", "false", "0"} {
66 t.Setenv("CARLOS_HOST_CLAIM", claim)
67 t.Setenv("CARLOS_HOST_CLAIM_ORIGINS", origins)
68 enabled := func(v string) bool { return hostwrite.Enabled(func(string) string { return v }) }
69 if got, want := originEnabled(), enabled(claim) && enabled(origins); got != want {
70 t.Errorf("%q/%q = %v want %v", claim, origins, got, want)
71 }
72 }
73 }
74}
75
76func TestPlan15ProofEarlyExit(t *testing.T) {
77 plan15Enable(t)
78 s := &plan15BoundedStore{DirStore: release.NewDirStore(t.TempDir())}
79 o := servingClaim(t, t.Context(), s)
80 for _, g := range []string{"first", "middle", "last"} {
81 o = updateGenerationTest(t, t.Context(), s, o, plan15Admit(o, g, "ie"))
82 }
83 h := o.Claim.Holder
84 last := OriginTuple{h.Incarnation, h.Location, "ie", "last"}
85 s.opens = 0
86 if got, err := AdmittedOrigins(t.Context(), s, o, []OriginTuple{last, last}); err != nil || !got[last] || s.opens != 1 {
87 t.Fatalf("head proof=%v err=%v opens=%d", got, err, s.opens)
88 }
89 s.opens = 0
90 if _, err := AdmittedOrigins(t.Context(), s, o, nil); err != nil || s.opens != 0 {
91 t.Fatalf("empty proof err=%v opens=%d", err, s.opens)
92 }
93 missing := last
94 missing.Generation = "never"
95 s.opens = 0
96 if got, err := AdmittedOrigins(t.Context(), s, o, []OriginTuple{last, missing}); err != nil || !got[last] || got[missing] || s.opens != 3 {
97 t.Fatalf("negative proof=%v err=%v opens=%d", got, err, s.opens)
98 }
99}
100
101type plan15GetOnly struct{ release.Store }
102
103func (s plan15GetOnly) Get(ctx context.Context, key string) ([]byte, string, error) {
104 if strings.Contains(key, "control/host-origins/") {
105 panic("unbounded node Get through wrapper")
106 }
107 return s.Store.Get(ctx, key)
108}
109func TestPlan15PrefixCannotBufferOrigin(t *testing.T) {
110 plan15Enable(t)
111 raw := release.NewDirStore(t.TempDir())
112 prefixed := release.NewPrefixStore(raw, "deployment")
113 o := servingClaim(t, t.Context(), prefixed)
114 o = updateGenerationTest(t, t.Context(), prefixed, o, plan15Admit(o, "real", "ie"))
115 s := release.NewPrefixStore(plan15GetOnly{raw}, "deployment")
116 h := o.Claim.Holder
117 if ok, err := AdmittedOrigin(t.Context(), s, o, h.Incarnation, h.Location, "ie", "real"); ok || err == nil {
118 t.Fatalf("buffered origin %v %v", ok, err)
119 }
120}
121
122// A comparable outer type can still hold a noncomparable dynamic value.
123type plan15Noncomparable struct {
124 *plan15BoundedStore
125 slice []byte
126}
127type plan15InterfaceWrapper struct{ release.Store }
128
129func (s plan15InterfaceWrapper) Open(ctx context.Context, key string) (io.ReadCloser, string, error) {
130 return release.OpenObject(ctx, s.Store, key)
131}
132
133func TestPlan15CacheBoundAtConstruction(t *testing.T) {
134 plan15Enable(t)
135 raw := &plan15BoundedStore{DirStore: release.NewDirStore(t.TempDir())}
136 s := plan15InterfaceWrapper{plan15Noncomparable{raw, []byte{1}}}
137 o := servingClaim(t, t.Context(), s)
138 o = updateGenerationTest(t, t.Context(), s, o, plan15Admit(o, "real", "ie"))
139 h := o.Claim.Holder
140 tuple := OriginTuple{h.Incarnation, h.Location, "ie", "real"}
141 cache := NewOriginProofCache(s)
142 raw.opens, raw.gets = 0, 0
143 for i := 0; i < 2; i++ {
144 got, err := cache.Proven(t.Context(), o, []OriginTuple{tuple})
145 if err != nil || !got[tuple] {
146 t.Fatalf("proof %v %v", got, err)
147 }
148 got[tuple] = false
149 }
150 if raw.opens != 1 || raw.gets != 2 {
151 t.Fatalf("opens=%d gets=%d", raw.opens, raw.gets)
152 }
153 // The method has no store argument: changing the constructor's variable
154 // cannot redirect this cache, and a second cache cannot inherit its result.
155 other := release.NewDirStore(t.TempDir())
156 key, _ := Key(o.Host)
157 body, _ := json.Marshal(o.Claim)
158 if _, err := other.Put(t.Context(), key, body, ""); err != nil {
159 t.Fatal(err)
160 }
161 s = plan15InterfaceWrapper{other}
162 if got, err := NewOriginProofCache(s).Proven(t.Context(), o, []OriginTuple{tuple}); got != nil || err == nil {
163 t.Fatalf("foreign store reused proof %v %v", got, err)
164 }
165 if got, err := cache.Proven(t.Context(), o, []OriginTuple{tuple}); err != nil || !got[tuple] {
166 t.Fatalf("bound store changed %v %v", got, err)
167 }
168 if got, err := new(OriginProofCache).Proven(t.Context(), o, []OriginTuple{tuple}); got != nil || !errors.Is(err, ErrMalformed) {
169 t.Fatalf("unbound cache %v %v", got, err)
170 }
171}
172