1// SPDX-License-Identifier: MIT
2
3package guest
4
5import (
6 "context"
7 "fmt"
8 "net"
9 "strings"
10 "testing"
11 "time"
12
13 "bnema.dev/ermite-runner/internal/execstream"
14 "bnema.dev/ermite-runner/internal/execstream/execpb"
15)
16
17// 100 secrets of 48 KiB (4.8 MB) do not fit one frame (MaxFrame is 2 MiB) but
18// do as NamedValue frames: this crosses the real transport, DriveJob on the
19// host side and waitForRun on the executor side.
20func TestManyLargeSecretsCrossTheStream(t *testing.T) {
21 secrets := map[string]string{}
22 for i := 0; i < 100; i++ {
23 secrets[fmt.Sprintf("S%03d", i)] = strings.Repeat(string(rune('a'+i%26)), 48<<10)
24 }
25 vars := map[string]string{"A": "1", "B": "2"}
26
27 // the old shape, every secret in the Run frame, would not have worked
28 err := execstream.NewConn(discard{}).Send(&execpb.Frame{Body: &execpb.Frame_Run{Run: &execpb.Run{Job: &execpb.JobSpec{Contexts: strings.Join(valuesOf(secrets), "")}}}})
29 if err == nil {
30 t.Fatal("test premise: all the secrets must exceed one frame")
31 }
32
33 hostSide, guestSide := net.Pipe()
34 defer func() { _ = hostSide.Close(); _ = guestSide.Close() }()
35 got := make(chan error, 1)
36 go func() {
37 c := execstream.NewConn(guestSide)
38 _ = c.Send(&execpb.Frame{Body: &execpb.Frame_Hello{Hello: &execpb.Hello{}}})
39 run, vals, err := waitForRun(context.Background(), c, 10*time.Second)
40 switch {
41 case err != nil:
42 got <- err
43 case run.GetJob().GetJobKey() != "k" || len(vals.secrets) != 100 || len(vals.vars) != 2 || vals.vars["B"] != "2":
44 got <- fmt.Errorf("run %v, %d secrets, vars %v", run.GetJob().GetJobKey(), len(vals.secrets), vals.vars)
45 default:
46 for k, v := range secrets {
47 if vals.secrets[k] != v {
48 got <- fmt.Errorf("secret %s differs", k)
49 return
50 }
51 }
52 got <- nil
53 _ = c.Send(&execpb.Frame{Body: &execpb.Frame_Exit{Exit: &execpb.Exit{}}})
54 }
55 }()
56 _, exit, err := execstream.DriveJob(hostSide, &execpb.Run{Job: &execpb.JobSpec{JobKey: "k"}}, nil, execstream.JobHandler{Secrets: secrets, Vars: vars})
57 if err != nil || exit == nil {
58 t.Fatalf("DriveJob: %v", err)
59 }
60 if err := <-got; err != nil {
61 t.Fatal(err)
62 }
63}
64
65type discard struct{}
66
67func (discard) Write(p []byte) (int, error) { return len(p), nil }
68func (discard) Read([]byte) (int, error) { select {} }
69
70func valuesOf(m map[string]string) []string {
71 var out []string
72 for _, v := range m {
73 out = append(out, v)
74 }
75 return out
76}