carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3import (
4 "context"
5 "reflect"
6 "testing"
7 "time"
8
9 "github.com/carlosframework/platform/internal/edgesfile"
10 "github.com/carlosframework/platform/internal/release"
11 "github.com/carlosframework/platform/internal/status"
12)
13
14// The guidance a member is shown must name EVERY armed edge, not just the
15// ones this console's own PLATFORM_EDGE_IPS happens to list — otherwise a
16// multi-region deployment tells people to point at one region and then grades
17// them against all of them. Union, configured addresses first, no duplicates.
18func TestDNSTargetsUnionsTheConfiguredListWithTheFold(t *testing.T) {
19 s := &Server{
20 cfg: Config{EdgeIPs: []string{"203.0.113.7"}},
21 foldEdgeIPs: func(context.Context) []string { return []string{"198.51.100.9", "203.0.113.7"} },
22 }
23 got := s.dnsTargets()
24 if !reflect.DeepEqual(got.EdgeIPs, []string{"203.0.113.7", "198.51.100.9"}) {
25 t.Fatalf("EdgeIPs = %v, want the configured list first, then the fold's extras, deduped", got.EdgeIPs)
26 }
27}
28
29// dnsTargets is called once PER CLAIM inside the domains-list loop. An
30// uncached fold would put a box-snapshot sweep plus one Get per edges file
31// behind every row of every listing — the cost discipline
32// domainListCNAMELookupCap already established for this door.
33func TestEdgeFoldIsCachedForTheTTL(t *testing.T) {
34 folds := 0
35 s := &Server{foldEdgeIPs: func(context.Context) []string {
36 folds++
37 return []string{"198.51.100.9"}
38 }}
39 for i := 0; i < 5; i++ {
40 s.dnsTargets()
41 }
42 if folds != 1 {
43 t.Fatalf("folded %d times inside the TTL, want 1", folds)
44 }
45
46 // Past the TTL the next reader refolds — an edge that armed a minute ago
47 // has to become visible without a console restart.
48 s.edgeFold.mu.Lock()
49 s.edgeFold.at = time.Now().Add(-edgeFoldTTL - time.Second)
50 s.edgeFold.mu.Unlock()
51 if got := s.dnsTargets(); folds != 2 || len(got.EdgeIPs) != 1 {
52 t.Fatalf("after the TTL: folds = %d, EdgeIPs = %v, want a refold", folds, got.EdgeIPs)
53 }
54}
55
56// A fold that comes back empty is a bucket that could not be read this
57// minute far more often than it is a deployment that lost every edge, so the
58// previous answer stands rather than being emptied — narrowing the guidance
59// every member is shown on the strength of one failed read is the worse
60// error. The timestamp still moves, so a persistent outage costs one bounded
61// attempt per TTL, not one per request.
62func TestEdgeFoldKeepsTheLastAnswerWhenAFoldFails(t *testing.T) {
63 folds := 0
64 s := &Server{foldEdgeIPs: func(context.Context) []string {
65 folds++
66 if folds == 1 {
67 return []string{"198.51.100.9"}
68 }
69 return nil // the store is down
70 }}
71 if got := s.dnsTargets(); len(got.EdgeIPs) != 1 {
72 t.Fatalf("first fold: EdgeIPs = %v", got.EdgeIPs)
73 }
74 s.edgeFold.mu.Lock()
75 s.edgeFold.at = time.Now().Add(-edgeFoldTTL - time.Second)
76 s.edgeFold.mu.Unlock()
77 got := s.dnsTargets()
78 if folds != 2 {
79 t.Fatalf("folds = %d, want the stale entry to have been retried", folds)
80 }
81 if !reflect.DeepEqual(got.EdgeIPs, []string{"198.51.100.9"}) {
82 t.Fatalf("EdgeIPs = %v, want the previous answer left standing", got.EdgeIPs)
83 }
84 // And the failed attempt still moved the clock: a console whose bucket is
85 // down must not refold on every request of every listing.
86 before := folds
87 s.dnsTargets()
88 if folds != before {
89 t.Fatalf("folds = %d, want the failed attempt to still hold off for the TTL", folds)
90 }
91}
92
93// The real fold, end to end over a bucket: a live box's published EIP reaches
94// the guidance even though nothing ever set it in this console's env.
95func TestRealEdgeFoldReadsTheDeploymentsEdgesFiles(t *testing.T) {
96 ctx := context.Background()
97 store := release.NewDirStore(t.TempDir())
98 if _, err := store.Put(ctx, edgesfile.Key("annie"),
99 []byte(`{"v":1,"box":"annie","eip":"198.51.100.9","hosts":{}}`), ""); err != nil {
100 t.Fatal(err)
101 }
102 if err := status.WriteBox(ctx, store, status.Box{Label: "annie", GeneratedAt: time.Now()}); err != nil {
103 t.Fatal(err)
104 }
105 s := &Server{cfg: Config{EdgeIPs: []string{"203.0.113.7"}, Releases: store}}
106 if got := s.dnsTargets(); !reflect.DeepEqual(got.EdgeIPs, []string{"203.0.113.7", "198.51.100.9"}) {
107 t.Fatalf("EdgeIPs = %v, want the configured address plus the folded one", got.EdgeIPs)
108 }
109}
110
111// A console with no deployment bucket at all (Releases nil) is a legal
112// deployment: it degrades to the configured list rather than panicking or
113// emptying the guidance.
114func TestRealEdgeFoldWithNoStoreDegradesToTheConfiguredList(t *testing.T) {
115 s := &Server{cfg: Config{EdgeIPs: []string{"203.0.113.7"}}}
116 if got := s.dnsTargets(); !reflect.DeepEqual(got.EdgeIPs, []string{"203.0.113.7"}) {
117 t.Fatalf("EdgeIPs = %v, want the configured list", got.EdgeIPs)
118 }
119}
120