cmd/gitbay-runner/env_test.go

387b381242a77e37af3a58b7301f6365b176603b
gitbay/cmd/gitbay-runner/env_test.go history · blame · raw

131 lines · 4287 bytes

  1package main
  2
  3import (
  4	"os"
  5	"strings"
  6	"testing"
  7)
  8
  9// A step's environment is constructed, not inherited: repository content
 10// must not see what the operator set on the runner service (#144).
 11func TestStepEnvDoesNotInherit(t *testing.T) {
 12	t.Setenv("GITBAY_RUNNER_TOKEN", "a-secret-the-service-was-given")
 13	t.Setenv("AWS_SECRET_ACCESS_KEY", "also-not-for-builds")
 14
 15	env := stepEnv(job{Repo: "alice/app", SHA: "abc", Ref: "main", Job: "test"}, "/tmp/buildhome")
 16
 17	for _, e := range env {
 18		if strings.HasPrefix(e, "GITBAY_RUNNER_TOKEN=") || strings.HasPrefix(e, "AWS_SECRET_ACCESS_KEY=") {
 19			t.Errorf("the runner's own environment reached a build step: %q", e)
 20		}
 21	}
 22	want := map[string]string{
 23		"CI": "true", "GITBAY_REPO": "alice/app", "GITBAY_SHA": "abc",
 24		"GITBAY_REF": "main", "GITBAY_JOB": "test",
 25		// HOME is the shared build home, not the runner's own, so a
 26		// build cannot read the dotfiles where tools keep credentials —
 27		// and not the workspace, which is deleted after every build,
 28		// taking every tool cache with it.
 29		"HOME": "/tmp/buildhome",
 30	}
 31	got := map[string]string{}
 32	for _, e := range env {
 33		k, v, _ := strings.Cut(e, "=")
 34		got[k] = v
 35	}
 36	for k, v := range want {
 37		if got[k] != v {
 38			t.Errorf("%s = %q, want %q", k, got[k], v)
 39		}
 40	}
 41	if got["PATH"] == "" {
 42		t.Error("PATH is empty; a step could not find any tool")
 43	}
 44}
 45
 46// Secrets are passed through when the server sent them, which it does
 47// only for a trusted build.
 48func TestStepEnvCarriesSecrets(t *testing.T) {
 49	env := stepEnv(job{Secrets: map[string]string{"TOKEN": "s3cret"}}, "/tmp/buildhome")
 50	if !containsEnv(env, "TOKEN=s3cret") {
 51		t.Error("a trusted build's secret did not reach the step")
 52	}
 53	env = stepEnv(job{}, "/tmp/buildhome")
 54	for _, e := range env {
 55		if strings.HasPrefix(e, "TOKEN=") {
 56			t.Errorf("a secret appeared with none sent: %q", e)
 57		}
 58	}
 59}
 60
 61// PATH falls back rather than leaving a step unable to find anything.
 62func TestStepEnvPathFallback(t *testing.T) {
 63	old := os.Getenv("PATH")
 64	os.Unsetenv("PATH")
 65	defer os.Setenv("PATH", old)
 66	if env := stepEnv(job{}, "/tmp/buildhome"); !containsEnv(env, "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") {
 67		t.Errorf("no PATH fallback: %v", env)
 68	}
 69}
 70
 71func containsEnv(env []string, want string) bool {
 72	for _, e := range env {
 73		if e == want {
 74			return true
 75		}
 76	}
 77	return false
 78}
 79
 80// The build home must outlive a build. It was briefly the workspace,
 81// which run() removes when the build ends, so every build re-downloaded
 82// the Go module cache and the ~50MB sonar scanner.
 83func TestStepEnvHomeIsNotTheWorkspace(t *testing.T) {
 84	env := stepEnv(job{ID: 7}, "/var/lib/gitbay-runner/work/home")
 85	for _, e := range env {
 86		if strings.HasPrefix(e, "HOME=") && strings.Contains(e, "build-7") {
 87			t.Errorf("HOME is the per-build workspace, which is deleted after the build: %q", e)
 88		}
 89	}
 90}
 91
 92// podman runs from a system service, where the systemd cgroup manager
 93// has no user slice to work in. Every invocation must say so, or crun
 94// fails creating the container's scope (#144).
 95func TestPodmanUsesCgroupfs(t *testing.T) {
 96	r := &runner{}
 97	got := r.podmanGlobal()
 98	found := false
 99	for _, f := range got {
100		if f == "--cgroup-manager=cgroupfs" {
101			found = true
102		}
103	}
104	if !found {
105		t.Errorf("podmanGlobal() = %v, missing the cgroupfs manager", got)
106	}
107}
108
109// The build home is where caches live, so the container must see it at
110// the path HOME names; otherwise every containerised build starts cold.
111func TestEnvHomeFindsHome(t *testing.T) {
112	if got := envHome([]string{"PATH=/bin", "HOME=/var/lib/gitbay-runner/work/home", "CI=true"}); got != "/var/lib/gitbay-runner/work/home" {
113		t.Errorf("envHome = %q", got)
114	}
115	if got := envHome([]string{"PATH=/bin"}); got != "" {
116		t.Errorf("envHome with no HOME = %q, want empty", got)
117	}
118}
119
120// A limit is passed to podman only when set; unset means uncapped, not a
121// default that could kill the suite.
122func TestLimitArgs(t *testing.T) {
123	if got := (&runner{}).limitArgs(); len(got) != 0 {
124		t.Errorf("no limits set, got %v", got)
125	}
126	got := (&runner{memory: "4g", cpus: "2"}).limitArgs()
127	want := []string{"--memory", "4g", "--cpus", "2"}
128	if strings.Join(got, " ") != strings.Join(want, " ") {
129		t.Errorf("limitArgs = %v, want %v", got, want)
130	}
131}