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 "encoding/json"
6 "net/http"
7 "net/http/httptest"
8 "os"
9 "path/filepath"
10 "strings"
11 "testing"
12 "time"
13
14 "github.com/carlosframework/platform/internal/activator"
15 "github.com/carlosframework/platform/internal/hostfiles"
16 "github.com/carlosframework/platform/internal/instancekey"
17 "github.com/carlosframework/platform/internal/registry"
18 "github.com/carlosframework/platform/internal/release"
19)
20
21// Ruling 22 (R-C): an adoption is published by the running session's
22// publisher, with no admission or release after it. The box is then lost:
23// every process and every local file is gone. The data bucket's manifest
24// for the session's generation still holds the record, bound to the
25// session's writer, which is what O2' pins.
26func TestAdoptionSurvivesBoxLossWithoutAnotherAdmissionOrRelease(t *testing.T) {
27 t.Parallel()
28 ctx, cancel := context.WithCancel(context.Background())
29 defer cancel()
30 data := release.NewDirStore(t.TempDir())
31 writer := instancekey.Binding{Host: "shop.example.com", Incarnation: "inc-1", Session: 3, Generation: "g3"}
32 prefix := "instances/" + writer.Host + "/" + writer.Incarnation + "/files/" + writer.Generation + "/"
33 pub, err := hostfiles.Open(ctx, data, prefix)
34 if err != nil {
35 t.Fatal(err)
36 }
37 box := t.TempDir()
38 filesDir, envDir := filepath.Join(box, "files"), filepath.Join(box, "env")
39 for _, d := range []string{filesDir, envDir} {
40 if err := os.MkdirAll(d, 0o700); err != nil {
41 t.Fatal(err)
42 }
43 }
44 // The session's continuous publisher and the adoption relay.
45 running := make(chan error, 2)
46 go func() {
47 running <- hostfiles.Run(ctx, pub, filesDir, hostfiles.Options{Deadline: 40 * time.Millisecond, Scan: time.Minute, Poll: 40 * time.Millisecond})
48 }()
49 relay := &hostfiles.PlatformRelay{Name: instancekey.AdoptionItem, Stage: pub, Retry: 10 * time.Millisecond}
50 go func() { running <- relay.Run(ctx) }()
51
52 key := strings.Repeat("K", 43) + "="
53 tok := strings.Repeat("f", 64)
54 launch := activator.AdoptionLaunch{Writer: writer, KeySHA256: instancekey.Digest(key), Source: instancekey.SourcePlatform, Dir: envDir, Publish: relay.Publish}
55 mux := http.NewServeMux()
56 (&activator.ExecBackend{}).MountInstanceKeyAdoption(mux, registry.Route{Host: writer.Host, Kind: "instance"},
57 func() (string, error) { return tok, nil }, func() (activator.AdoptionLaunch, bool) { return launch, true })
58 req := httptest.NewRequest(http.MethodPut, "/instance-key/adoption", strings.NewReader(`{"sha256":"`+instancekey.Digest(key)+`","source":"platform"}`))
59 req.Header.Set("Authorization", "Bearer "+tok)
60 w := httptest.NewRecorder()
61 mux.ServeHTTP(w, req)
62 if w.Code != http.StatusNoContent {
63 t.Fatalf("adoption = %d, want 204", w.Code)
64 }
65
66 // Within the publisher's continuous bound the record is committed.
67 readRecord := func() ([]byte, error) {
68 raw, _, err := data.Get(context.Background(), hostfiles.ManifestKey(prefix))
69 if err != nil {
70 return nil, err
71 }
72 var m hostfiles.Manifest
73 if err := json.Unmarshal(raw, &m); err != nil {
74 return nil, err
75 }
76 return hostfiles.ReadPlatform(context.Background(), data, prefix, m, instancekey.AdoptionItem, nil)
77 }
78 deadline := time.Now().Add(2 * time.Second)
79 for {
80 if _, err := readRecord(); err == nil {
81 break
82 }
83 if time.Now().After(deadline) {
84 t.Fatal("the adoption was not published within the continuous bound")
85 }
86 time.Sleep(10 * time.Millisecond)
87 }
88
89 // Box loss: the processes stop and the disk is gone.
90 cancel()
91 <-running
92 <-running
93 if err := os.RemoveAll(box); err != nil {
94 t.Fatal(err)
95 }
96 rec, err := readRecord()
97 if err != nil {
98 t.Fatalf("after box loss: %v", err)
99 }
100 a, err := instancekey.ParseAdoption(rec)
101 if err != nil || !a.Binds(writer) || a.SHA256 != instancekey.Digest(key) || a.Source != instancekey.SourcePlatform {
102 t.Fatalf("after box loss the bucket holds %+v (%v), want the session's record", a, err)
103 }
104}
105