474 lines
17 KiB
Go
474 lines
17 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/containers/podman/v5/pkg/specgen"
|
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
|
"go.podman.io/common/libnetwork/types"
|
|
)
|
|
|
|
func writeList(t *testing.T, lines ...string) string {
|
|
t.Helper()
|
|
f := filepath.Join(t.TempDir(), "hosts")
|
|
if err := os.WriteFile(f, []byte(strings.Join(lines, "\n")+"\n"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return f
|
|
}
|
|
|
|
func lines(s string, re string) []string {
|
|
var out []string
|
|
for _, l := range strings.Split(s, "\n") {
|
|
if regexp.MustCompile(re).MatchString(l) {
|
|
out = append(out, l)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func check[T any](t *testing.T, name string, want, got T) {
|
|
t.Helper()
|
|
if !reflect.DeepEqual(want, got) {
|
|
t.Errorf("%s:\nwant %#v\ngot %#v", name, want, got)
|
|
}
|
|
}
|
|
|
|
func TestRouterConfig(t *testing.T) {
|
|
f := writeList(t, "# comment", "", " API.Anthropic.com # trailing", ".example.org", "git.example.net:22",
|
|
"db:5432", "db", "web:443", "web:80", "git.example.net:5432")
|
|
entries, err := parseAllowlist(f)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
check(t, "entries", []entry{{".example.org", ""}, {"api.anthropic.com", ""}, {"db", ""}, {"db", "5432"},
|
|
{"git.example.net", "22"}, {"git.example.net", "5432"}, {"web", ""}}, entries)
|
|
|
|
dnsmasq, nginx, forwards := routerConfig(entries, "10.89.7.2")
|
|
check(t, "forwards", "0.0.0.0:443:10.89.7.2:443 0.0.0.0:80:10.89.7.2:80 127.1.0.2:5432:10.89.7.2:10000 "+
|
|
"127.1.0.3:22:10.89.7.2:10001 127.1.0.3:5432:10.89.7.2:10002", forwards)
|
|
check(t, "dnsmasq", "no-resolv\nno-hosts\nbind-interfaces\nlisten-address=10.89.7.2\naddress=/#/\n"+
|
|
"address=/example.org/127.0.0.2\nhost-record=api.anthropic.com,127.1.0.1\nhost-record=db,127.1.0.2\n"+
|
|
"host-record=git.example.net,127.1.0.3\nhost-record=web,127.1.0.4\n", dnsmasq)
|
|
check(t, "dnsmasq-never-forwards", []string(nil), lines(dnsmasq, `^(server|resolv-file)=`))
|
|
|
|
tlsMap := nginx[strings.Index(nginx, "map $ssl_preread_server_name"):]
|
|
httpMap := nginx[strings.Index(nginx, "map $host"):]
|
|
want := []string{" .example.org 1;", " api.anthropic.com 1;", " db 1;", " git.example.net 1;", " web 1;"}
|
|
check(t, "tls-map", want, lines(tlsMap[:strings.Index(tlsMap, "}")], ` 1;$`))
|
|
check(t, "http-map", want, lines(httpMap[:strings.Index(httpMap, "}")], ` 1;$`))
|
|
check(t, "tls-default-deny", []string{" default 127.0.0.1:9;"}, lines(nginx, `default 127`))
|
|
check(t, "http-default-deny", []string{" if ($http_allowed = 0) { return 403; }"}, lines(nginx, `return 403`))
|
|
check(t, "tcp-servers", []string{
|
|
" server { listen 10.89.7.2:10000; set $up db:5432; proxy_pass $up; }",
|
|
" server { listen 10.89.7.2:10001; set $up git.example.net:22; proxy_pass $up; }",
|
|
" server { listen 10.89.7.2:10002; set $up git.example.net:5432; proxy_pass $up; }",
|
|
}, lines(nginx, `set \$up`))
|
|
check(t, "listens-on-router-only", len(lines(nginx, `listen `)), len(lines(nginx, `listen 10\.89\.7\.2:`)))
|
|
check(t, "resolver-placeholder", 2, strings.Count(nginx, "resolver @RESOLVER@ ipv6=off valid=30s;"))
|
|
}
|
|
|
|
func TestRouterConfigOverlap(t *testing.T) {
|
|
entries, err := parseAllowlist(writeList(t, ".example.org", "example.org", "example.org:22", "a.example.org", "example.org"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
dnsmasq, nginx, _ := routerConfig(entries, "10.89.7.2")
|
|
check(t, "overlap-dnsmasq", []string{"address=/example.org/127.0.0.2", "host-record=a.example.org,127.1.0.1",
|
|
"host-record=example.org,127.1.0.2"}, lines(dnsmasq, `^(address=/e|host-record)`))
|
|
check(t, "overlap-map", []string{" .example.org 1;", " a.example.org 1;", " .example.org 1;", " a.example.org 1;"},
|
|
lines(nginx, `^ .* 1;$`))
|
|
}
|
|
|
|
func TestParseAllowlistRejects(t *testing.T) {
|
|
for _, bad := range []string{"exa mple.com", "example.com:0", "example.com:70000", "example.com:ssh", "*.example.com",
|
|
"-bad.com", "http://example.com", "a..b", ".example.com:22", "example.com:+22", "example.com:-1"} {
|
|
if _, err := parseAllowlist(writeList(t, bad)); err == nil {
|
|
t.Errorf("accepted %q", bad)
|
|
}
|
|
}
|
|
_, err := parseAllowlist(writeList(t, "ok.example.com", "bad host", "z.example.com"))
|
|
check(t, "bad-line-error", true, err != nil && strings.HasSuffix(err.Error(), ":2: one entry per line"))
|
|
}
|
|
|
|
func TestDefaultAllowlistParses(t *testing.T) {
|
|
if _, err := parseAllowlist("allowed-hosts"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestRealpath(t *testing.T) {
|
|
d, _ := filepath.EvalSymlinks(t.TempDir())
|
|
_ = os.MkdirAll(d+"/a/b", 0o755)
|
|
_ = os.Symlink(d+"/a/b", d+"/abs")
|
|
_ = os.Symlink("a/b", d+"/rel")
|
|
_ = os.Symlink(d+"/missing/x", d+"/dangling")
|
|
_ = os.Symlink(d+"/loop", d+"/loop")
|
|
for in, want := range map[string]string{
|
|
d + "/abs": d + "/a/b",
|
|
d + "/rel/c/d": d + "/a/b/c/d",
|
|
d + "/rel/..": d + "/a",
|
|
d + "/dangling/y": d + "/missing/x/y",
|
|
d + "/a/./b/": d + "/a/b",
|
|
"/": "/",
|
|
d + "/loop/z": d + "/loop/z",
|
|
d + "/nope/../a/b/..": d + "/a",
|
|
} {
|
|
check(t, in, want, realpath(in))
|
|
}
|
|
}
|
|
|
|
type fakeEngine struct {
|
|
calls []string
|
|
specs map[string]*specgen.SpecGenerator
|
|
pod *specgen.PodSpecGenerator
|
|
nets map[string]types.Network
|
|
apiDir string
|
|
execCode int
|
|
run func(ctx context.Context) (int, error)
|
|
}
|
|
|
|
func (f *fakeEngine) call(c string) { f.calls = append(f.calls, c) }
|
|
|
|
func (f *fakeEngine) NetworkCreate(_ context.Context, n *types.Network, ignore bool) (types.Network, error) {
|
|
f.call("network create " + n.Name)
|
|
f.nets[n.Name] = *n
|
|
sub, _ := types.ParseCIDR("10.89.7.0/24")
|
|
return types.Network{Name: n.Name, Subnets: []types.Subnet{{Subnet: sub}}}, nil
|
|
}
|
|
func (f *fakeEngine) NetworkRemove(_ context.Context, name string) error {
|
|
f.call("network rm " + name)
|
|
return nil
|
|
}
|
|
func (f *fakeEngine) VolumeCreate(_ context.Context, name string) error {
|
|
f.call("volume create " + name)
|
|
return nil
|
|
}
|
|
func (f *fakeEngine) PodCreate(_ context.Context, s *specgen.PodSpecGenerator) error {
|
|
f.call("pod create " + s.Name)
|
|
f.pod = s
|
|
return nil
|
|
}
|
|
func (f *fakeEngine) PodRemove(_ context.Context, name string) error {
|
|
f.call("pod rm " + name)
|
|
return nil
|
|
}
|
|
func (f *fakeEngine) ContainerCreate(_ context.Context, s *specgen.SpecGenerator) error {
|
|
f.call("create " + s.Name)
|
|
f.specs[s.Name] = s
|
|
return nil
|
|
}
|
|
func (f *fakeEngine) ContainerStart(_ context.Context, name string) error {
|
|
f.call("start " + name)
|
|
return nil
|
|
}
|
|
func (f *fakeEngine) ContainerRun(ctx context.Context, name string) (int, error) {
|
|
f.call("run " + name)
|
|
if f.run != nil {
|
|
return f.run(ctx)
|
|
}
|
|
return 0, nil
|
|
}
|
|
func (f *fakeEngine) ContainerExec(_ context.Context, name string, cmd []string) (int, error) {
|
|
f.call("exec " + name + " " + strings.Join(cmd, " "))
|
|
return f.execCode, nil
|
|
}
|
|
func (f *fakeEngine) ContainerLogs(_ context.Context, name string, tail int) string {
|
|
f.call("logs " + name)
|
|
return "boom"
|
|
}
|
|
func (f *fakeEngine) ContainerRemove(_ context.Context, name string) error {
|
|
f.call("rm " + name)
|
|
return nil
|
|
}
|
|
|
|
type env struct {
|
|
work string
|
|
good string
|
|
fake *fakeEngine
|
|
}
|
|
|
|
func setup(t *testing.T) *env {
|
|
work, _ := filepath.EvalSymlinks(t.TempDir())
|
|
for _, d := range []string{"home/proj", "other"} {
|
|
_ = os.MkdirAll(filepath.Join(work, d), 0o755)
|
|
}
|
|
e := &env{work: work, good: writeList(t, "api.anthropic.com")}
|
|
t.Setenv("HOME", work+"/home")
|
|
t.Setenv("XDG_RUNTIME_DIR", work)
|
|
t.Setenv("XDG_CONFIG_HOME", "")
|
|
t.Setenv("AGENT_SANDBOX_ALLOWLIST", e.good)
|
|
t.Setenv("AGENT_SANDBOX_IMAGE", "")
|
|
t.Setenv("AGENT_SANDBOX_ROUTER_IMAGE", "")
|
|
t.Setenv("AGENT_SANDBOX_HOME_VOLUME", "")
|
|
orig := startService
|
|
startService = func(dir string) (context.Context, engine, func(), error) {
|
|
e.fake.call("service start")
|
|
e.fake.apiDir = dir
|
|
return context.Background(), e.fake, func() { e.fake.call("service stop") }, nil
|
|
}
|
|
t.Cleanup(func() { startService = orig })
|
|
e.reset()
|
|
return e
|
|
}
|
|
|
|
func (e *env) reset() {
|
|
e.fake = &fakeEngine{specs: map[string]*specgen.SpecGenerator{}, nets: map[string]types.Network{}}
|
|
}
|
|
|
|
func (e *env) launch(t *testing.T, dir string, args ...string) int {
|
|
return e.launchCtx(t, context.Background(), dir, args...)
|
|
}
|
|
|
|
func (e *env) launchCtx(t *testing.T, ctx context.Context, dir string, args ...string) int {
|
|
t.Chdir(dir)
|
|
t.Setenv("PWD", dir)
|
|
return launch(ctx, args)
|
|
}
|
|
|
|
func TestLaunchRefusals(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
dir string
|
|
prep func(t *testing.T, e *env)
|
|
}{
|
|
{"bad-line", "home/proj", func(t *testing.T, e *env) {
|
|
t.Setenv("AGENT_SANDBOX_ALLOWLIST", writeList(t, "ok.example.com", "bad host"))
|
|
}},
|
|
{"home", "home", nil},
|
|
{"home-ancestor", ".", nil},
|
|
{"root", "/", nil},
|
|
{"home-symlink", "homelink", func(t *testing.T, e *env) { _ = os.Symlink(e.work+"/home", e.work+"/homelink") }},
|
|
{"config-parent", "other", func(t *testing.T, e *env) { t.Setenv("XDG_CONFIG_HOME", e.work+"/other/cfg") }},
|
|
{"allowlist-dir", "home/proj", func(t *testing.T, e *env) {
|
|
t.Setenv("AGENT_SANDBOX_ALLOWLIST", e.work+"/home/proj/hosts")
|
|
}},
|
|
{"colon", "home/a:b", func(t *testing.T, e *env) { _ = os.Mkdir(e.work+"/home/a:b", 0o755) }},
|
|
{"symlinked-allowlist-target", "home/dotfiles", func(t *testing.T, e *env) {
|
|
_ = os.MkdirAll(e.work+"/home/dotfiles", 0o755)
|
|
_ = os.MkdirAll(e.work+"/home/.config/agent-sandbox", 0o755)
|
|
b, _ := os.ReadFile(e.good)
|
|
_ = os.WriteFile(e.work+"/home/dotfiles/hosts", b, 0o644)
|
|
_ = os.Symlink(e.work+"/home/dotfiles/hosts", e.work+"/home/.config/agent-sandbox/allowed-hosts")
|
|
t.Setenv("XDG_CONFIG_HOME", e.work+"/home/.config")
|
|
t.Setenv("AGENT_SANDBOX_ALLOWLIST", "")
|
|
}},
|
|
{"symlinked-config-dir", "home/dotfiles", func(t *testing.T, e *env) {
|
|
_ = os.MkdirAll(e.work+"/home/dotfiles", 0o755)
|
|
_ = os.Symlink(e.work+"/home/dotfiles", e.work+"/home/cfglink")
|
|
t.Setenv("XDG_CONFIG_HOME", e.work+"/home/cfglink")
|
|
t.Setenv("AGENT_SANDBOX_ALLOWLIST", "")
|
|
}},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
e := setup(t)
|
|
if c.prep != nil {
|
|
c.prep(t, e)
|
|
}
|
|
dir := c.dir
|
|
if !filepath.IsAbs(dir) {
|
|
dir = filepath.Join(e.work, dir)
|
|
}
|
|
check(t, "exit", 1, e.launch(t, dir, "--exec", "true"))
|
|
check(t, "podman calls", []string(nil), e.fake.calls)
|
|
})
|
|
}
|
|
}
|
|
|
|
func leftovers(t *testing.T, dir string) {
|
|
t.Helper()
|
|
entries, _ := os.ReadDir(dir)
|
|
for _, d := range entries {
|
|
if strings.HasPrefix(d.Name(), "agent-sandbox-") {
|
|
t.Errorf("leftover %s", d.Name())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLaunch(t *testing.T) {
|
|
e := setup(t)
|
|
dir := e.work + "/home/proj"
|
|
var apiMode os.FileMode
|
|
e.fake.run = func(context.Context) (int, error) {
|
|
fi, err := os.Stat(e.fake.apiDir)
|
|
if err == nil {
|
|
apiMode = fi.Mode().Perm()
|
|
}
|
|
return 0, nil
|
|
}
|
|
check(t, "exit", 0, e.launch(t, dir, "--exec", "true"))
|
|
|
|
f := e.fake
|
|
session := strings.TrimPrefix(f.calls[3], "network create ")
|
|
check(t, "session", true, regexp.MustCompile(`^agent-sandbox-\d+-\d+$`).MatchString(session))
|
|
router, fwd, claude := session+"-router", session+"-forwarder", session+"-claude"
|
|
check(t, "calls", []string{
|
|
"service start",
|
|
"network create agent-sandbox-egress",
|
|
"volume create agent-sandbox-home",
|
|
"network create " + session,
|
|
"create " + router,
|
|
"start " + router,
|
|
"exec " + router + " nc -z 10.89.7.2 443",
|
|
"pod create " + session,
|
|
"create " + fwd,
|
|
"start " + fwd,
|
|
"exec " + fwd + " nc -z 127.0.0.1 443",
|
|
"create " + claude,
|
|
"run " + claude,
|
|
"pod rm " + session,
|
|
"rm " + router,
|
|
"network rm " + session,
|
|
"service stop",
|
|
}, f.calls)
|
|
|
|
check(t, "egress net", types.Network{Name: "agent-sandbox-egress", Driver: "bridge", DNSEnabled: true}, f.nets["agent-sandbox-egress"])
|
|
check(t, "session net", types.Network{Name: session, Driver: "bridge", Internal: true}, f.nets[session])
|
|
|
|
yes := true
|
|
r := f.specs[router]
|
|
tmp := r.Mounts[0].Source
|
|
check(t, "tmp under runtime dir", true, strings.HasPrefix(tmp, e.work+"/"+session+"."))
|
|
check(t, "api dir private", os.FileMode(0o700), apiMode)
|
|
check(t, "api dir not mounted", false, strings.HasPrefix(f.apiDir, tmp))
|
|
for _, d := range []string{tmp, f.apiDir} {
|
|
if _, err := os.Stat(d); !os.IsNotExist(err) {
|
|
t.Errorf("%s not removed", d)
|
|
}
|
|
}
|
|
leftovers(t, e.work)
|
|
|
|
check(t, "router image", "localhost/agent-sandbox-router:dev", r.Image)
|
|
check(t, "router rm", &yes, r.Remove)
|
|
check(t, "router netns", specgen.Bridge, r.NetNS.NSMode)
|
|
check(t, "router nets", map[string]types.PerNetworkOptions{
|
|
session: {StaticIPs: []net.IP{net.ParseIP("10.89.7.2")}},
|
|
"agent-sandbox-egress": {},
|
|
}, r.Networks)
|
|
check(t, "router mounts", []spec.Mount{{Type: "bind", Source: tmp, Destination: "/etc/router", Options: []string{"ro", "Z"}}}, r.Mounts)
|
|
|
|
p := f.pod
|
|
check(t, "pod hostname", "agent-sandbox", p.Hostname)
|
|
check(t, "pod netns", specgen.Bridge, p.NetNS.NSMode)
|
|
check(t, "pod nets", map[string]types.PerNetworkOptions{session: {}}, p.Networks)
|
|
check(t, "pod dns", []net.IP{net.ParseIP("10.89.7.2")}, p.DNSServer)
|
|
check(t, "pod no-hosts", true, p.NoManageHosts)
|
|
check(t, "pod userns", specgen.Namespace{NSMode: specgen.KeepID, Value: "uid=1000,gid=1000"}, p.Userns)
|
|
|
|
fw := f.specs[fwd]
|
|
check(t, "fwd pod", session, fw.Pod)
|
|
check(t, "fwd user", "0:0", fw.User)
|
|
check(t, "fwd ro", &yes, fw.ReadOnlyFilesystem)
|
|
check(t, "fwd caps", [][]string{{"all"}, {"NET_BIND_SERVICE"}}, [][]string{fw.CapDrop, fw.CapAdd})
|
|
check(t, "fwd entrypoint", []string{"/usr/local/bin/forwarder"}, fw.Entrypoint)
|
|
check(t, "fwd env", map[string]string{"FORWARDS": "0.0.0.0:443:10.89.7.2:443 0.0.0.0:80:10.89.7.2:80"}, fw.Env)
|
|
check(t, "fwd rm", (*bool)(nil), fw.Remove)
|
|
|
|
c := f.specs[claude]
|
|
check(t, "claude image", "localhost/agent-sandbox:dev", c.Image)
|
|
check(t, "claude flags", []*bool{&yes, &yes, &yes}, []*bool{c.Remove, c.Stdin, c.Init})
|
|
check(t, "claude tty", false, *c.Terminal)
|
|
check(t, "claude pod", session, c.Pod)
|
|
check(t, "claude cap-drop", []string{"NET_ADMIN", "NET_RAW", "SYS_ADMIN"}, c.CapDrop)
|
|
check(t, "claude cap-add", []string(nil), c.CapAdd)
|
|
check(t, "claude label", []string{"disable"}, c.SelinuxOpts)
|
|
check(t, "claude devices", []spec.LinuxDevice{{Path: "/dev/fuse"}, {Path: "/dev/net/tun"}}, c.Devices)
|
|
check(t, "claude volumes", []*specgen.NamedVolume{{Name: "agent-sandbox-home", Dest: "/home/claude"}}, c.Volumes)
|
|
check(t, "claude mounts", []spec.Mount{{Type: "bind", Source: dir, Destination: dir}}, c.Mounts)
|
|
check(t, "claude workdir", dir, c.WorkDir)
|
|
check(t, "claude env", map[string]string{"DISABLE_AUTOUPDATER": "1", "CLAUDE_CODE_TMPDIR": "/var/tmp"}, c.Env)
|
|
check(t, "claude cmd", []string{"true"}, c.Command)
|
|
check(t, "claude privileged", (*bool)(nil), c.Privileged)
|
|
}
|
|
|
|
func TestLaunchClaudeArgsAndOverrides(t *testing.T) {
|
|
e := setup(t)
|
|
t.Setenv("AGENT_SANDBOX_IMAGE", "img:1")
|
|
t.Setenv("AGENT_SANDBOX_ROUTER_IMAGE", "rimg:1")
|
|
t.Setenv("AGENT_SANDBOX_HOME_VOLUME", "vol")
|
|
check(t, "exit", 0, e.launch(t, e.work+"/home/proj", "--continue", "-p", "hi"))
|
|
for name, s := range e.fake.specs {
|
|
switch {
|
|
case strings.HasSuffix(name, "-claude"):
|
|
check(t, "claude cmd", []string{"claude", "--continue", "-p", "hi"}, s.Command)
|
|
check(t, "claude image", "img:1", s.Image)
|
|
check(t, "volume", "vol", s.Volumes[0].Name)
|
|
default:
|
|
check(t, name+" image", "rimg:1", s.Image)
|
|
}
|
|
}
|
|
check(t, "volume create", "volume create vol", e.fake.calls[2])
|
|
}
|
|
|
|
func TestImageNamesPinVersion(t *testing.T) {
|
|
t.Setenv("AGENT_SANDBOX_IMAGE", "")
|
|
t.Setenv("AGENT_SANDBOX_ROUTER_IMAGE", "")
|
|
defer func(v string) { version = v }(version)
|
|
version = "v1.2.3"
|
|
img, router := imageNames()
|
|
check(t, "pinned", [2]string{registry + "/agent-sandbox:v1.2.3", registry + "/agent-sandbox-router:v1.2.3"}, [2]string{img, router})
|
|
}
|
|
|
|
func TestLaunchExitCode(t *testing.T) {
|
|
e := setup(t)
|
|
e.fake.run = func(context.Context) (int, error) { return 7, nil }
|
|
check(t, "exit", 7, e.launch(t, e.work+"/home/proj", "--exec", "false"))
|
|
check(t, "cleanup last", "service stop", e.fake.calls[len(e.fake.calls)-1])
|
|
}
|
|
|
|
func TestLaunchSignalCleansUp(t *testing.T) {
|
|
e := setup(t)
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
e.fake.run = func(opCtx context.Context) (int, error) {
|
|
cancel()
|
|
<-opCtx.Done()
|
|
return 125, opCtx.Err()
|
|
}
|
|
check(t, "exit", 130, e.launchCtx(t, ctx, e.work+"/home/proj", "--exec", "sleep", "60"))
|
|
n := len(e.fake.calls)
|
|
check(t, "cleanup", []string{"pod rm", "rm", "network rm", "service stop"},
|
|
[]string{e.fake.calls[n-4][:6], e.fake.calls[n-3][:2], e.fake.calls[n-2][:10], e.fake.calls[n-1]})
|
|
leftovers(t, e.work)
|
|
}
|
|
|
|
func TestLaunchServiceFails(t *testing.T) {
|
|
e := setup(t)
|
|
startService = func(string) (context.Context, engine, func(), error) { return nil, nil, nil, errors.New("no podman") }
|
|
check(t, "exit", 125, e.launch(t, e.work+"/home/proj", "--exec", "true"))
|
|
leftovers(t, e.work)
|
|
}
|
|
|
|
func TestLaunchSeedsAllowlist(t *testing.T) {
|
|
e := setup(t)
|
|
t.Setenv("AGENT_SANDBOX_ALLOWLIST", "")
|
|
t.Setenv("XDG_CONFIG_HOME", e.work+"/cfg")
|
|
check(t, "exit", 0, e.launch(t, e.work+"/home/proj", "--exec", "true"))
|
|
got, err := os.ReadFile(e.work + "/cfg/agent-sandbox/allowed-hosts")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
check(t, "seeded", string(defaultAllowlist), string(got))
|
|
}
|
|
|
|
func TestLaunchRouterNeverStarts(t *testing.T) {
|
|
e := setup(t)
|
|
e.fake.execCode = 1
|
|
check(t, "exit", 1, e.launch(t, e.work+"/home/proj", "--exec", "true"))
|
|
for _, c := range e.fake.calls {
|
|
if strings.HasPrefix(c, "pod create") {
|
|
t.Error("pod created after router failed")
|
|
}
|
|
}
|
|
check(t, "logs fetched", true, strings.Contains(strings.Join(e.fake.calls, "\n"), "logs "))
|
|
check(t, "service stopped", "service stop", e.fake.calls[len(e.fake.calls)-1])
|
|
}
|