package pkg_test //go:generate env CGO_ENABLED=0 go build -tags testtool -o testdata/testtool ./testdata import ( _ "embed" "errors" "log" "os" "os/exec" "testing" "hakurei.app/container/check" "hakurei.app/container/fhs" "hakurei.app/container/stub" "hakurei.app/hst" "hakurei.app/internal/pkg" "hakurei.app/message" ) // testtoolBin is the container test tool binary made available to the // execArtifact for testing its curing environment. // //go:embed testdata/testtool var testtoolBin []byte func TestExec(t *testing.T) { t.Parallel() wantChecksumOffline := pkg.MustDecode( "GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9", ) checkWithCache(t, []cacheTestCase{ {"offline", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) { c.SetStrict(true) testtool, testtoolDestroy := newTesttool() msg := message.New(log.New(os.Stderr, "container: ", 0)) msg.SwapVerbose(testing.Verbose()) cureMany(t, c, []cureStep{ {"container", pkg.NewExec( t.Context(), msg, 0, nil, check.MustAbs("/work"), []string{"HAKUREI_TEST=1"}, check.MustAbs("/opt/bin/testtool"), []string{"testtool"}, pkg.MustPath("/file", newStubFile( pkg.KindHTTPGet, pkg.ID{0xfe, 0}, nil, nil, nil, )), pkg.MustPath("/.hakurei", stubArtifact{ kind: pkg.KindTar, params: []byte("empty directory"), cure: func(c *pkg.CureContext) error { return os.MkdirAll(c.GetWorkDir().String(), 0700) }, }), pkg.MustPath("/opt", testtool), ), ignorePathname, wantChecksumOffline, nil}, {"error passthrough", pkg.NewExec( t.Context(), msg, 0, nil, check.MustAbs("/work"), []string{"HAKUREI_TEST=1"}, check.MustAbs("/opt/bin/testtool"), []string{"testtool"}, pkg.MustPath("/proc/nonexistent", stubArtifact{ kind: pkg.KindTar, params: []byte("doomed artifact"), cure: func(c *pkg.CureContext) error { return stub.UniqueError(0xcafe) }, }), ), nil, pkg.Checksum{}, errors.Join(stub.UniqueError(0xcafe))}, {"invalid paths", pkg.NewExec( t.Context(), msg, 0, nil, check.MustAbs("/work"), []string{"HAKUREI_TEST=1"}, check.MustAbs("/opt/bin/testtool"), []string{"testtool"}, pkg.ExecContainerPath{}, ), nil, pkg.Checksum{}, os.ErrInvalid}, }) // check init failure passthrough var exitError *exec.ExitError if _, _, err := c.Cure(pkg.NewExec( t.Context(), msg, 0, nil, check.MustAbs("/work"), nil, check.MustAbs("/opt/bin/testtool"), []string{"testtool"}, )); !errors.As(err, &exitError) || exitError.ExitCode() != hst.ExitFailure { t.Fatalf("Cure: error = %v, want init exit status 1", err) } testtoolDestroy(t, base, c) }, pkg.MustDecode("7PoPpWLjFPXIymbuIYLZAzOpCYr-2PN4CZ11jFdO-mDlnZNgFO3JyOtK8HW8Jxvm")}, {"net", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) { c.SetStrict(true) testtool, testtoolDestroy := newTesttool() msg := message.New(log.New(os.Stderr, "container: ", 0)) msg.SwapVerbose(testing.Verbose()) wantChecksum := pkg.MustDecode( "a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W", ) cureMany(t, c, []cureStep{ {"container", pkg.NewExec( t.Context(), msg, 0, &wantChecksum, check.MustAbs("/work"), []string{"HAKUREI_TEST=1"}, check.MustAbs("/opt/bin/testtool"), []string{"testtool", "net"}, pkg.MustPath("/file", newStubFile( pkg.KindHTTPGet, pkg.ID{0xfe, 0}, nil, nil, nil, )), pkg.MustPath("/.hakurei", stubArtifact{ kind: pkg.KindTar, params: []byte("empty directory"), cure: func(c *pkg.CureContext) error { return os.MkdirAll(c.GetWorkDir().String(), 0700) }, }), pkg.MustPath("/opt", testtool), ), ignorePathname, wantChecksum, nil}, }) testtoolDestroy(t, base, c) }, pkg.MustDecode("bBQVFIt0FnOulljgpLnGtuzHSFgwiCMjc4pmc4rHRqXKQ60Q5aBVYp5f6aH9VdZi")}, {"overlay root", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) { c.SetStrict(true) testtool, testtoolDestroy := newTesttool() msg := message.New(log.New(os.Stderr, "container: ", 0)) msg.SwapVerbose(testing.Verbose()) cureMany(t, c, []cureStep{ {"container", pkg.NewExec( t.Context(), msg, 0, nil, check.MustAbs("/work"), []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"}, check.MustAbs("/opt/bin/testtool"), []string{"testtool"}, pkg.MustPath("/", stubArtifact{ kind: pkg.KindTar, params: []byte("empty directory"), cure: func(c *pkg.CureContext) error { return os.MkdirAll(c.GetWorkDir().String(), 0700) }, }), pkg.MustPath("/opt", testtool), ), ignorePathname, wantChecksumOffline, nil}, }) testtoolDestroy(t, base, c) }, pkg.MustDecode("gFT9kprYBqEJKifJIl2sHn_3TgULWVLTU4DrYAHiGcRmcdFRZ0YtjiROW820cAEc")}, {"overlay temp", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) { c.SetStrict(true) testtool, testtoolDestroy := newTesttool() msg := message.New(log.New(os.Stderr, "container: ", 0)) msg.SwapVerbose(testing.Verbose()) cureMany(t, c, []cureStep{ {"container", pkg.NewExec( t.Context(), msg, 0, nil, check.MustAbs("/work"), []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"}, check.MustAbs("/tmp/bin/testtool"), []string{"testtool"}, pkg.MustPath("/", stubArtifact{ kind: pkg.KindTar, params: []byte("empty directory"), cure: func(c *pkg.CureContext) error { return os.MkdirAll(c.GetWorkDir().String(), 0700) }, }), pkg.MustPath("/tmp/", stubArtifact{ kind: pkg.KindTar, params: []byte("empty directory"), cure: func(c *pkg.CureContext) error { return os.MkdirAll(c.GetWorkDir().String(), 0700) }, }), pkg.ExecContainerPath{ P: fhs.AbsTmp, A: testtool, }, ), ignorePathname, wantChecksumOffline, nil}, }) testtoolDestroy(t, base, c) }, pkg.MustDecode("_r1IBeMWCkLwQ9Im9w0tV9_CWIOfQlXkkP2CogPHLmZp_AB6W3_8HVZqDV00dNAm")}, }) } // newTesttool returns an [Artifact] that cures into testtoolBin. The returned // function must be called at the end of the test but not deferred. func newTesttool() ( testtool pkg.Artifact, testtoolDestroy func(t *testing.T, base *check.Absolute, c *pkg.Cache), ) { // testtoolBin is built during go:generate and is not deterministic testtool = overrideIdent{pkg.ID{0xfe, 0xff}, stubArtifact{ kind: pkg.KindTar, cure: func(c *pkg.CureContext) error { work := c.GetWorkDir() if err := os.MkdirAll( work.Append("bin").String(), 0700, ); err != nil { return err } return os.WriteFile(c.GetWorkDir().Append( "bin", "testtool", ).String(), testtoolBin, 0500) }, }} testtoolDestroy = func(t *testing.T, base *check.Absolute, c *pkg.Cache) { if pathname, checksum, err := c.Cure(testtool); err != nil { t.Fatalf("Cure: error = %v", err) } else if err = os.Remove(pathname.String()); err != nil { t.Fatal(err) } else { p := base.Append( "checksum", pkg.Encode(checksum), ) if err = os.Chmod(p.Append("bin").String(), 0700); err != nil { t.Fatal(err) } if err = os.Chmod(p.String(), 0700); err != nil { t.Fatal(err) } if err = os.RemoveAll(p.String()); err != nil { t.Fatal(err) } } } return }