carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package edge
2
3import (
4 "net/http"
5 "path/filepath"
6 "sync/atomic"
7 "testing"
8
9 "github.com/carlosframework/platform/internal/registry"
10)
11
12// genActivator is an Activator that is also a GenSource: an instant wake
13// that proxies straight through, over a hand-driven fakeGens. served counts
14// the requests that reached it, so a test can tell a cache hit (none) from
15// a proxied request.
16type genActivator struct {
17 *fakeGens
18 served atomic.Int64
19}
20
21func (a *genActivator) ServeHTTP(w http.ResponseWriter, r *http.Request, _ registry.Route, proxy http.Handler) bool {
22 a.served.Add(1)
23 proxy.ServeHTTP(w, r)
24 return true
25}
26
27func (a *genActivator) RetireExcept(map[string]bool) {}
28
29// genFixture is a registry with one activator-managed instance whose
30// CANONICAL socket is served by an app answering "canonical", and a
31// generation-1 app answering "A". Which one a request reaches says which
32// path the edge took.
33func genFixture(t *testing.T, on bool, hibernate bool) (*Edge, *genActivator, *genApp) {
34 t.Helper()
35 dir := genSockDir(t)
36 startGenApp(t, filepath.Join(dir, "canon.sock"), body("canonical", ""))
37 a := startGenApp(t, filepath.Join(dir, "a.sock"), body("A", ""))
38 reg := openReg(t)
39 if err := reg.Upsert(registry.Route{Host: "inst.example.com", Kind: "instance", Hibernate: hibernate,
40 Socket: filepath.Join(dir, "canon.sock"), Status: "ready", Version: "v0"}); err != nil {
41 t.Fatal(err)
42 }
43 act := &genActivator{fakeGens: newFakeGens(on)}
44 act.switchTo("inst.example.com", GenInfo{Socket: a.sock, N: 1, Build: "v1"})
45 e, err := New(reg, act)
46 if err != nil {
47 t.Fatalf("New: %v", err)
48 }
49 return e, act, a
50}
51
52func TestManagedRouteDialsTheCurrentGeneration(t *testing.T) {
53 e, act, _ := genFixture(t, true, true)
54 rec := serve(t, e, "http://inst.example.com/")
55 if rec.Body.String() != "A" {
56 t.Fatalf("body %q, want A: the edge dialled the canonical socket, not the current generation", rec.Body.String())
57 }
58 if got := rec.Header().Get("X-Carlos-Version"); got != "v1" {
59 t.Fatalf("X-Carlos-Version = %q, want v1, the serving generation's build, not the row's v0", got)
60 }
61 if !e.current()["inst.example.com"].genManaged {
62 t.Fatal("the managed route's upstream is not marked genManaged")
63 }
64 if len(act.retired) != 1 {
65 t.Fatalf("New registered %d retire hooks, want 1 (the edge's pools)", len(act.retired))
66 }
67}
68
69// Flag off: byte for byte the old edge. unixProxy on the canonical socket,
70// the row's version, no retire hook, no pools.
71func TestGenerationsOffKeepsTheCanonicalProxyAndTheRowVersion(t *testing.T) {
72 e, act, _ := genFixture(t, false, true)
73 rec := serve(t, e, "http://inst.example.com/")
74 if rec.Body.String() != "canonical" || rec.Header().Get("X-Carlos-Version") != "v0" {
75 t.Fatalf("got %q version %q, want canonical v0", rec.Body.String(), rec.Header().Get("X-Carlos-Version"))
76 }
77 if e.gens != nil || e.genPools != nil || len(act.retired) != 0 {
78 t.Fatal("generations were wired with the switch off")
79 }
80 up := e.current()["inst.example.com"]
81 if up.genManaged {
82 t.Fatal("upstream marked genManaged with the switch off")
83 }
84}
85
86// An unflagged socket route is an externally supervised process: canonical,
87// whatever the switch says.
88func TestUnmanagedSocketRouteStaysCanonicalWithGenerationsOn(t *testing.T) {
89 e, _, _ := genFixture(t, true, false)
90 rec := serve(t, e, "http://inst.example.com/")
91 if rec.Body.String() != "canonical" || rec.Header().Get("X-Carlos-Version") != "v0" {
92 t.Fatalf("got %q version %q, want canonical v0", rec.Body.String(), rec.Header().Get("X-Carlos-Version"))
93 }
94 up := e.current()["inst.example.com"]
95 if up.genManaged {
96 t.Fatal("an unflagged route was marked genManaged")
97 }
98}
99