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 "net/http"
6 "net/http/httptest"
7 "os"
8 "path/filepath"
9 "testing"
10 "time"
11
12 "github.com/carlosframework/platform/internal/activator"
13 "github.com/carlosframework/platform/internal/registry"
14 "github.com/carlosframework/platform/internal/release"
15 "github.com/carlosframework/platform/internal/rollout"
16)
17
18// liveIdentityBackend is liveLaunchBackend with the exec backend's identity
19// capability exposed, as production's ExecBackend and SplitBackend expose
20// it: the activator's cold wake then captures live/<host> once at step 2b
21// (LiveIdentity) and runs that file by path (StartGenExe, SeedExe), never
22// the backend's live-link Start. before runs inside LiveIdentity, before
23// the capture: root's repoint after the plan.
24type liveIdentityBackend struct {
25 liveLaunchBackend
26}
27
28func (b *liveIdentityBackend) LiveIdentity(ctx context.Context, rt registry.Route) (release.StagedIdentity, error) {
29 if b.before != nil {
30 b.before()
31 }
32 return b.eb.LiveIdentity(ctx, rt)
33}
34
35func (b *liveIdentityBackend) StartGenExe(ctx context.Context, rt registry.Route, n uint64, perGen bool, id release.StagedIdentity) (activator.Proc, activator.Gen, error) {
36 return b.eb.StartGenExe(ctx, rt, n, perGen, id)
37}
38
39func (b *liveIdentityBackend) SeedExe(ctx context.Context, rt registry.Route, id release.StagedIdentity) error {
40 return b.eb.SeedExe(ctx, rt, id)
41}
42
43// TestDeclinedBoxRefusesAHeldBuildAtLaunch on the path production takes on
44// this branch (merge of zdd-flag-readiness): the wake plans with the link on
45// v1, root repoints it at held v2 before the capture, and the capture is
46// asked of the declined box's gate before it is recorded or run. Nothing of
47// v2 is exec'd and no build state names it.
48func TestDeclinedBoxRefusesAHeldCapturedIdentityAtLaunch(t *testing.T) {
49 prev := activator.GenerationsEnabled()
50 activator.SetGenerations(false)
51 t.Cleanup(func() { activator.SetGenerations(prev) })
52 resetRollout(t)
53 env := newFastFixture(t)
54 t.Setenv("CARLOS_OPT_DIR", env.optDir)
55 host := "warm.test"
56 run := t.TempDir()
57 rt := registry.Route{Host: host, Kind: "instance", App: "fastapp", Account: "bab", Channel: "edge",
58 Status: "ready", Hibernate: true, Version: "v1", GoodVersion: "v1", GoodDigest: "sha256:v1",
59 Socket: filepath.Join(run, host+".sock"), DBPath: filepath.Join(env.dataDir, host+".db")}
60 if err := env.reg.Upsert(rt); err != nil {
61 t.Fatal(err)
62 }
63 env.stageLive(t, host, "v1")
64 mark := filepath.Join(t.TempDir(), "v2.ran")
65 if err := release.StageArtifact(env.optDir, "bab", "v2", "fastapp", []byte("#!/bin/sh\necho ran > '"+mark+"'\nexec sleep 30\n")); err != nil {
66 t.Fatal(err)
67 }
68 writeFailureBook(t, env.dataDir, []rollout.FailureRecord{{Account: release.RouteAccount(rt), App: "fastapp", Build: "v2", Host: "sibling.test"}}, nil)
69 eb := &activator.ExecBackend{Cfg: activator.Config{Bucket: "test-bucket", Idle: time.Hour},
70 OptDir: env.optDir, DataDir: t.TempDir(), RunDirOverride: run}
71 backend := &liveIdentityBackend{liveLaunchBackend{eb: eb}}
72 svc := activator.New(activator.Config{Bucket: "test-bucket", Idle: time.Hour}, backend, env.reg)
73 svc.Routes = env.reg
74 var recorded []string
75 svc.BeforeExec = func(_ context.Context, _ string, build string) error {
76 recorded = append(recorded, build)
77 return nil
78 }
79 t.Cleanup(func() {
80 sctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
81 defer cancel()
82 svc.Shutdown(sctx)
83 })
84 declineWithBook(t, svc, env.reg, env.dataDir)
85 if eb.LiveExecGate == nil {
86 t.Fatal("no LiveExecGate: the box did not decline with its book")
87 }
88 if p := svc.Planner(context.Background(), rt); !isTodaysWake(p) {
89 t.Fatalf("plan with the link on v1 = %+v, want today's wake", p)
90 }
91 backend.before = func() {
92 if err := release.PointLive(env.optDir, host, "bab", "v2", "fastapp"); err != nil {
93 t.Error(err)
94 }
95 }
96 rec := httptest.NewRecorder()
97 proxy := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNoContent) })
98 if !svc.ServeHTTP(rec, httptest.NewRequest("GET", "http://"+host+"/", nil), rt, proxy) {
99 t.Fatal("the activator did not take the request")
100 }
101 if rec.Code == http.StatusNoContent {
102 t.Fatal("the wake onto held v2 was served")
103 }
104 time.Sleep(200 * time.Millisecond)
105 if _, err := os.Stat(mark); err == nil {
106 t.Fatal("held v2 was exec'd by path: the declined gate never saw the captured identity")
107 }
108 if len(recorded) != 0 {
109 t.Fatalf("build states recorded %v: a build the box will not start was named as about to run", recorded)
110 }
111}
112