From 346b492be4c1a660565be6f61fff91caec926b02 Mon Sep 17 00:00:00 2001 From: apostasie Date: Wed, 25 Sep 2024 13:20:21 -0700 Subject: [PATCH 1/4] Test tooling fixes As we make progress rewriting tests, the new tooling needs to adapt. In a shell, this is: - introducing (more) `Requirements`, with a better API - update documentation - fix some t.Helper calls - fix broken stdin implementation - do cleanup custom namespaces properly - change hashing function - disable "private" implying custom data root which is more trouble than is worth - minor cleanups Signed-off-by: apostasie --- docs/testing/tools.md | 56 ++++++-- pkg/testutil/nerdtest/helpers.go | 119 +++++++++++++++++ pkg/testutil/nerdtest/requirements.go | 143 +++++++++++++++++++++ pkg/testutil/nerdtest/test.go | 177 ++++++-------------------- pkg/testutil/test/case.go | 2 +- pkg/testutil/test/command.go | 11 +- pkg/testutil/test/data.go | 30 +++-- pkg/testutil/test/requirement.go | 71 +++++------ pkg/testutil/test/test.go | 2 +- 9 files changed, 411 insertions(+), 200 deletions(-) create mode 100644 pkg/testutil/nerdtest/helpers.go create mode 100644 pkg/testutil/nerdtest/requirements.go diff --git a/docs/testing/tools.md b/docs/testing/tools.md index 655306dc48b..36cee3c0510 100644 --- a/docs/testing/tools.md +++ b/docs/testing/tools.md @@ -5,7 +5,7 @@ The integration test suite in nerdctl is meant to apply to both nerdctl and docker, and further support additional test properties to target specific contexts (ipv6, kube). -Basic _usage_ is covered in the [testing docs](testing.md). +Basic _usage_ is covered in the [testing docs](README.md). This here covers how to write tests, leveraging nerdctl `pkg/testutil/test` which has been specifically developed to take care of repetitive tasks, @@ -166,9 +166,6 @@ Note that `Data` additionally exposes the following functions: Secondly, `Data` allows defining and manipulating "configuration" data. In the case of nerdctl here, the following configuration options are defined: -- `WithConfig(Docker, NotCompatible)` to flag a test as not compatible -- `WithConfig(Mode, Private)` will entirely isolate the test using a different -namespace, data root, nerdctl config, etc - `WithConfig(NerdctlToml, "foo")` which allows specifying a custom config - `WithConfig(DataRoot, "foo")` allowing to point to a custom data-root - `WithConfig(HostsDir, "foo")` to point to a specific hosts directory @@ -358,7 +355,50 @@ All tests (and subtests) are assumed to be parallelizable. You can force a specific `test.Case` to not be run in parallel though, by setting its `NoParallel` property to `true`. -Note that if you want better isolation, it is usually better to use -`WithConfig(nerdtest.Mode, nerdtest.Private)` instead. -This will keep the test parallel (for nerdctl), but isolate it in a different context. -For Docker (which does not support namespaces), it is equivalent to passing `NoParallel: true`. +Note that if you want better isolation, it is usually better to use the requirement +`nerdtest.Private` instead of `NoParallel` (see below). + +## Requirements + +`test.Case` has a `Require` property that allow enforcing specific, per-test requirements. + +Here are a few: +```go +test.Windows // a test runs only on Windows (or Not(Windows)) +test.Linux // a test runs only on Linux +test.Darwin // a test runs only on Darwin +test.OS(name string) // a test runs only on the OS `name` +test.Binary(name string) // a test requires the bin `name` to be in the PATH +test.Not(req Requirement) // a test runs only if the opposite of the requirement `req` is fulfilled +test.Require(req ...Requirement) // a test runs only if all requirements are fulfilled + +nerdtest.Docker // a test only run on Docker - normally used with test.Not(nerdtest.Docker) +nerdtest.Soci // a test requires the soci snapshotter +nerdtest.Rootless // a test requires Rootless (or Not(Rootless), indicating it requires Rootful) +nerdtest.Build // a test requires buildkit +nerdtest.CGroup // a test requires cgroup +nerdtest.OnlyIPv6 // a test is meant to run solely in the ipv6 environment +nerdtest.NerdctlNeedsFixing // indicates that a test cannot be run on nerdctl yet as a fix is required +nerdtest.Private // see below +``` + +### About `nerdtest.Private` + +While all requirements above are self-descriptive or obvious, and are going to skip +tests for environments that do not match the requirements, `nerdtest.Private` is a +special case. + +What it does when required is: create a private namespace, data-root, hosts-dir, nerdctl.toml and +DOCKER_CONFIG that is private to the test. + +Note that subtests are going to inherit that environment as well. + +If the target is Docker - which does not support namespaces for eg - asking for `private` +will merely disable parallelization. + +The purpose of private is to provide a truly clean-room environment for tests +that are guaranteed to have side effects on others, or that do require an exclusive, pristine +environment. + +Using private is generally preferable to disabling parallelization, as doing the latter +would slow down the run and won't have the same guarantees about the environment. diff --git a/pkg/testutil/nerdtest/helpers.go b/pkg/testutil/nerdtest/helpers.go new file mode 100644 index 00000000000..346a6c9122a --- /dev/null +++ b/pkg/testutil/nerdtest/helpers.go @@ -0,0 +1,119 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package nerdtest + +import ( + "encoding/json" + "testing" + "time" + + "gotest.tools/v3/assert" + + "github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat" + "github.com/containerd/nerdctl/v2/pkg/inspecttypes/native" + "github.com/containerd/nerdctl/v2/pkg/testutil/test" +) + +// InspectContainer is a helper that can be used inside custom commands or Setup +func InspectContainer(helpers test.Helpers, name string) dockercompat.Container { + var dc []dockercompat.Container + cmd := helpers.Command("container", "inspect", name) + cmd.Run(&test.Expected{ + ExitCode: 0, + Output: func(stdout string, info string, t *testing.T) { + err := json.Unmarshal([]byte(stdout), &dc) + assert.NilError(t, err, "Unable to unmarshal output\n"+info) + assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info) + }, + }) + return dc[0] +} + +func InspectVolume(helpers test.Helpers, name string, args ...string) native.Volume { + var dc []native.Volume + cmdArgs := append([]string{"volume", "inspect"}, args...) + cmdArgs = append(cmdArgs, name) + + cmd := helpers.Command(cmdArgs...) + cmd.Run(&test.Expected{ + ExitCode: 0, + Output: func(stdout string, info string, t *testing.T) { + err := json.Unmarshal([]byte(stdout), &dc) + assert.NilError(t, err, "Unable to unmarshal output\n"+info) + assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info) + }, + }) + return dc[0] +} + +func InspectNetwork(helpers test.Helpers, name string, args ...string) dockercompat.Network { + var dc []dockercompat.Network + cmdArgs := append([]string{"network", "inspect"}, args...) + cmdArgs = append(cmdArgs, name) + + cmd := helpers.Command(cmdArgs...) + cmd.Run(&test.Expected{ + ExitCode: 0, + Output: func(stdout string, info string, t *testing.T) { + err := json.Unmarshal([]byte(stdout), &dc) + assert.NilError(t, err, "Unable to unmarshal output\n"+info) + assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info) + }, + }) + return dc[0] +} + +func InspectImage(helpers test.Helpers, name string) dockercompat.Image { + var dc []dockercompat.Image + cmd := helpers.Command("image", "inspect", name) + cmd.Run(&test.Expected{ + ExitCode: 0, + Output: func(stdout string, info string, t *testing.T) { + err := json.Unmarshal([]byte(stdout), &dc) + assert.NilError(t, err, "Unable to unmarshal output\n"+info) + assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info) + }, + }) + return dc[0] +} + +func EnsureContainerStarted(helpers test.Helpers, con string) { + const ( + maxRetry = 5 + sleep = time.Second + ) + for i := 0; i < maxRetry; i++ { + count := i + cmd := helpers.Command("container", "inspect", con) + cmd.Run(&test.Expected{ + ExitCode: 0, + Output: func(stdout string, info string, t *testing.T) { + var dc []dockercompat.Container + err := json.Unmarshal([]byte(stdout), &dc) + assert.NilError(t, err, "Unable to unmarshal output\n"+info) + assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info) + if dc[0].State.Running { + return + } + if count == maxRetry-1 { + t.Fatalf("conainer %s not running", con) + } + time.Sleep(sleep) + }, + }) + } +} diff --git a/pkg/testutil/nerdtest/requirements.go b/pkg/testutil/nerdtest/requirements.go new file mode 100644 index 00000000000..f75f3e8b7f7 --- /dev/null +++ b/pkg/testutil/nerdtest/requirements.go @@ -0,0 +1,143 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package nerdtest + +import ( + "encoding/json" + "fmt" + "testing" + + "gotest.tools/v3/assert" + + "github.com/containerd/nerdctl/v2/pkg/buildkitutil" + "github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat" + "github.com/containerd/nerdctl/v2/pkg/rootlessutil" + "github.com/containerd/nerdctl/v2/pkg/testutil" + "github.com/containerd/nerdctl/v2/pkg/testutil/test" +) + +var ipv6 test.ConfigKey = "IPv6Test" +var only test.ConfigValue = "Only" +var mode test.ConfigKey = "Mode" +var modePrivate test.ConfigValue = "Private" + +var OnlyIPv6 = test.MakeRequirement(func(data test.Data, t *testing.T) (ret bool, mess string) { + ret = testutil.GetEnableIPv6() + if !ret { + mess = "runner skips IPv6 compatible tests in the non-IPv6 environment" + } + data.WithConfig(ipv6, only) + return ret, mess +}) + +var Private = test.MakeRequirement(func(data test.Data, t *testing.T) (ret bool, mess string) { + data.WithConfig(mode, modePrivate) + return true, "private mode" +}) + +var Soci = test.MakeRequirement(func(data test.Data, t *testing.T) (ret bool, mess string) { + ret = false + mess = "soci is not enabled" + (&test.GenericCommand{}). + WithT(t). + WithBinary(testutil.GetTarget()). + WithArgs("info", "--format", "{{ json . }}"). + Run(&test.Expected{ + Output: func(stdout string, info string, t *testing.T) { + var dinf dockercompat.Info + err := json.Unmarshal([]byte(stdout), &dinf) + assert.NilError(t, err, "failed to parse docker info") + for _, p := range dinf.Plugins.Storage { + if p == "soci" { + ret = true + mess = "soci is enabled" + } + } + }, + }) + + return ret, mess +}) + +var Docker = test.MakeRequirement(func(data test.Data, t *testing.T) (ret bool, mess string) { + ret = testutil.GetTarget() == testutil.Docker + if ret { + mess = "current target is docker" + } else { + mess = "current target is not docker" + } + return ret, mess +}) + +var NerdctlNeedsFixing = test.MakeRequirement(func(data test.Data, t *testing.T) (ret bool, mess string) { + ret = testutil.GetTarget() == testutil.Docker + if ret { + mess = "current target is docker" + } else { + mess = "current target is nerdctl, but it is currently broken and not working for this" + } + return ret, mess +}) + +var Rootless = test.MakeRequirement(func(data test.Data, t *testing.T) (ret bool, mess string) { + // Make sure we DO not return "IsRootless true" for docker + ret = testutil.GetTarget() != testutil.Docker && rootlessutil.IsRootless() + if ret { + mess = "environment is rootless" + } else { + mess = "environment is rootful" + } + return ret, mess +}) + +var Build = test.MakeRequirement(func(data test.Data, t *testing.T) (ret bool, mess string) { + // FIXME: shouldn't we run buildkitd in a container? At least for testing, that would be so much easier than + // against the host install + ret = true + mess = "buildkitd is enabled" + if testutil.GetTarget() == testutil.Nerdctl { + _, err := buildkitutil.GetBuildkitHost(testutil.Namespace) + if err != nil { + ret = false + mess = fmt.Sprintf("buildkitd is not enabled: %+v", err) + } + } + return ret, mess +}) + +var CGroup = test.MakeRequirement(func(data test.Data, t *testing.T) (ret bool, mess string) { + ret = true + mess = "cgroup is enabled" + (&test.GenericCommand{}). + WithT(t). + WithBinary(testutil.GetTarget()). + WithArgs("info", "--format", "{{ json . }}"). + Run(&test.Expected{ + Output: func(stdout string, info string, t *testing.T) { + var dinf dockercompat.Info + err := json.Unmarshal([]byte(stdout), &dinf) + assert.NilError(t, err, "failed to parse docker info") + switch dinf.CgroupDriver { + case "none", "": + ret = false + mess = "cgroup is none" + } + }, + }) + + return ret, mess +}) diff --git a/pkg/testutil/nerdtest/test.go b/pkg/testutil/nerdtest/test.go index 812ff08591d..78046cb9cca 100644 --- a/pkg/testutil/nerdtest/test.go +++ b/pkg/testutil/nerdtest/test.go @@ -17,18 +17,12 @@ package nerdtest import ( - "encoding/json" - "fmt" "os" "path/filepath" "testing" "gotest.tools/v3/assert" - "github.com/containerd/nerdctl/v2/pkg/buildkitutil" - "github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat" - "github.com/containerd/nerdctl/v2/pkg/inspecttypes/native" - "github.com/containerd/nerdctl/v2/pkg/rootlessutil" "github.com/containerd/nerdctl/v2/pkg/testutil" "github.com/containerd/nerdctl/v2/pkg/testutil/test" ) @@ -39,64 +33,11 @@ func Setup() { // Nerdctl specific config key and values var NerdctlToml test.ConfigKey = "NerdctlToml" +var DockerConfig test.ConfigKey = "DockerConfig" var HostsDir test.ConfigKey = "HostsDir" var DataRoot test.ConfigKey = "DataRoot" var Namespace test.ConfigKey = "Namespace" -var Mode test.ConfigKey = "Mode" -var ModePrivate test.ConfigValue = "Private" -var IPv6 test.ConfigKey = "IPv6Test" -var Only test.ConfigValue = "Only" - -var OnlyIPv6 = test.MakeRequirement(func(data test.Data) (ret bool, mess string) { - ret = testutil.GetEnableIPv6() - if !ret { - mess = "runner skips IPv6 compatible tests in the non-IPv6 environment" - } - data.WithConfig(IPv6, Only) - return ret, mess -}) - -var Private = test.MakeRequirement(func(data test.Data) (ret bool, mess string) { - data.WithConfig(Mode, ModePrivate) - return true, "" -}) - -var Docker = test.MakeRequirement(func(data test.Data) (ret bool, mess string) { - ret = testutil.GetTarget() == testutil.Docker - if ret { - mess = "current target is docker" - } else { - mess = "current target is not docker" - } - return ret, mess -}) - -var Rootless = test.MakeRequirement(func(data test.Data) (ret bool, mess string) { - ret = rootlessutil.IsRootless() - if ret { - mess = "environment is rootless" - } else { - mess = "environment is rootful" - } - return ret, mess -}) - -var Build = test.MakeRequirement(func(data test.Data) (ret bool, mess string) { - // FIXME: shouldn't we run buildkitd in a container? At least for testing, that would be so much easier than - // against the host install - ret = true - mess = "" - if testutil.GetTarget() == testutil.Nerdctl { - _, err := buildkitutil.GetBuildkitHost(testutil.Namespace) - if err != nil { - ret = false - mess = fmt.Sprintf("test requires buildkitd: %+v", err) - } - } - return ret, mess -}) - type NerdCommand struct { test.GenericCommand // FIXME: annoying - forces custom Clone, etc @@ -121,69 +62,6 @@ func (nc *NerdCommand) Clone() test.Command { } } -// InspectContainer is a helper that can be used inside custom commands or Setup -func InspectContainer(helpers test.Helpers, name string) dockercompat.Container { - var dc []dockercompat.Container - cmd := helpers.Command("container", "inspect", name) - cmd.Run(&test.Expected{ - ExitCode: 0, - Output: func(stdout string, info string, t *testing.T) { - err := json.Unmarshal([]byte(stdout), &dc) - assert.NilError(t, err, "Unable to unmarshal output\n"+info) - assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info) - }, - }) - return dc[0] -} - -func InspectVolume(helpers test.Helpers, name string, args ...string) native.Volume { - var dc []native.Volume - cmdArgs := append([]string{"volume", "inspect"}, args...) - cmdArgs = append(cmdArgs, name) - - cmd := helpers.Command(cmdArgs...) - cmd.Run(&test.Expected{ - ExitCode: 0, - Output: func(stdout string, info string, t *testing.T) { - err := json.Unmarshal([]byte(stdout), &dc) - assert.NilError(t, err, "Unable to unmarshal output\n"+info) - assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info) - }, - }) - return dc[0] -} - -func InspectNetwork(helpers test.Helpers, name string, args ...string) dockercompat.Network { - var dc []dockercompat.Network - cmdArgs := append([]string{"network", "inspect"}, args...) - cmdArgs = append(cmdArgs, name) - - cmd := helpers.Command(cmdArgs...) - cmd.Run(&test.Expected{ - ExitCode: 0, - Output: func(stdout string, info string, t *testing.T) { - err := json.Unmarshal([]byte(stdout), &dc) - assert.NilError(t, err, "Unable to unmarshal output\n"+info) - assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info) - }, - }) - return dc[0] -} - -func InspectImage(helpers test.Helpers, name string) dockercompat.Image { - var dc []dockercompat.Image - cmd := helpers.Command("image", "inspect", name) - cmd.Run(&test.Expected{ - ExitCode: 0, - Output: func(stdout string, info string, t *testing.T) { - err := json.Unmarshal([]byte(stdout), &dc) - assert.NilError(t, err, "Unable to unmarshal output\n"+info) - assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info) - }, - }) - return dc[0] -} - func nerdctlSetup(testCase *test.Case, t *testing.T) test.Command { t.Helper() @@ -192,11 +70,11 @@ func nerdctlSetup(testCase *test.Case, t *testing.T) test.Command { var pvNamespace string inherited := false - if dt.ReadConfig(IPv6) != Only && testutil.GetEnableIPv6() { + if dt.ReadConfig(ipv6) != only && testutil.GetEnableIPv6() { t.Skip("runner skips non-IPv6 compatible tests in the IPv6 environment") } - if dt.ReadConfig(Mode) == ModePrivate { + if dt.ReadConfig(mode) == modePrivate { // If private was inherited, we already got a configured namespace if dt.ReadConfig(Namespace) != "" { pvNamespace = string(dt.ReadConfig(Namespace)) @@ -208,7 +86,8 @@ func nerdctlSetup(testCase *test.Case, t *testing.T) test.Command { testCase.Env["DOCKER_CONFIG"] = testCase.Data.TempDir() testCase.Env["NERDCTL_TOML"] = filepath.Join(testCase.Data.TempDir(), "nerdctl.toml") dt.WithConfig(HostsDir, test.ConfigValue(testCase.Data.TempDir())) - dt.WithConfig(DataRoot, test.ConfigValue(testCase.Data.TempDir())) + // Setting data root is more trouble than anything and does not significantly increase isolation + // dt.WithConfig(DataRoot, test.ConfigValue(testCase.Data.TempDir())) } testUtilBase = testutil.NewBaseWithNamespace(t, pvNamespace) if testUtilBase.Target == testutil.Docker { @@ -224,11 +103,19 @@ func nerdctlSetup(testCase *test.Case, t *testing.T) test.Command { // If we were passed custom content for NerdctlToml, save it // Not happening if this is not nerdctl of course - if testUtilBase.Target == testutil.Nerdctl && dt.ReadConfig(NerdctlToml) != "" { - dest := filepath.Join(testCase.Data.TempDir(), "nerdctl.toml") - testCase.Env["NERDCTL_TOML"] = dest - err := os.WriteFile(dest, []byte(dt.ReadConfig(NerdctlToml)), 0400) - assert.NilError(t, err, "failed to write custom nerdctl toml file for test") + if testUtilBase.Target == testutil.Nerdctl { + if dt.ReadConfig(NerdctlToml) != "" { + dest := filepath.Join(testCase.Data.TempDir(), "nerdctl.toml") + testCase.Env["NERDCTL_TOML"] = dest + err := os.WriteFile(dest, []byte(dt.ReadConfig(NerdctlToml)), 0400) + assert.NilError(t, err, "failed to write custom nerdctl toml file for test") + } + if dt.ReadConfig(DockerConfig) != "" { + dest := filepath.Join(testCase.Data.TempDir(), "config.json") + testCase.Env["DOCKER_CONFIG"] = filepath.Dir(dest) + err := os.WriteFile(dest, []byte(dt.ReadConfig(DockerConfig)), 0400) + assert.NilError(t, err, "failed to write custom docker config json file for test") + } } // Build the base @@ -242,21 +129,37 @@ func nerdctlSetup(testCase *test.Case, t *testing.T) test.Command { if testUtilBase.Target == testutil.Nerdctl { if dt.ReadConfig(HostsDir) != "" { - baseCommand.GenericCommand.WithArgs("--hosts-dir=" + string(dt.ReadConfig(HostsDir))) + baseCommand.WithArgs("--hosts-dir=" + string(dt.ReadConfig(HostsDir))) } if dt.ReadConfig(DataRoot) != "" { - baseCommand.GenericCommand.WithArgs("--data-root=" + string(dt.ReadConfig(DataRoot))) + baseCommand.WithArgs("--data-root=" + string(dt.ReadConfig(DataRoot))) } } // If we were in a custom namespace, not inherited - make sure we clean up the namespace - // FIXME: this is broken, and custom namespaces are not cleaned properly if testUtilBase.Target == testutil.Nerdctl && pvNamespace != "" && !inherited { cleanup := func() { - cl := baseCommand.Clone() - cl.WithArgs("namespace", "remove", pvNamespace) - cl.Run(nil) + // Stop all containers, then prune everything + containerList := baseCommand.Clone() + containerList.WithArgs("ps", "-q") + containerList.Run(&test.Expected{ + Output: func(stdout string, info string, t *testing.T) { + if stdout != "" { + containerRm := baseCommand.Clone() + containerRm.WithArgs("rm", "-f", stdout) + containerRm.Run(&test.Expected{}) + } + }, + }) + + systemPrune := baseCommand.Clone() + systemPrune.WithArgs("system", "prune", "-f", "--all", "--volumes") + systemPrune.Run(&test.Expected{}) + + cleanNamespace := baseCommand.Clone() + cleanNamespace.WithArgs("namespace", "remove", pvNamespace) + cleanNamespace.Run(nil) } cleanup() t.Cleanup(cleanup) diff --git a/pkg/testutil/test/case.go b/pkg/testutil/test/case.go index eed35a929f6..36384c6962b 100644 --- a/pkg/testutil/test/case.go +++ b/pkg/testutil/test/case.go @@ -133,7 +133,7 @@ func (test *Case) seal(t *testing.T) { // Check the requirements if test.Require != nil { - test.Require(test.Data, t) + test.Require(test.Data, true, t) } } diff --git a/pkg/testutil/test/command.go b/pkg/testutil/test/command.go index 6fbb1779d52..3e1b840db6e 100644 --- a/pkg/testutil/test/command.go +++ b/pkg/testutil/test/command.go @@ -128,7 +128,9 @@ func (gc *GenericCommand) Run(expect *Expected) { func (gc *GenericCommand) boot() icmd.Cmd { // This is a helper function, not to appear in the debugging output - gc.t.Helper() + if gc.t != nil { + gc.t.Helper() + } binary := gc.mainBinary args := gc.mainArgs @@ -155,6 +157,10 @@ func (gc *GenericCommand) boot() icmd.Cmd { icmdCmd.Dir = gc.tempDir } + if gc.stdin != nil { + icmdCmd.Stdin = gc.stdin + } + // Attach any extra env we have for k, v := range gc.Env { icmdCmd.Env = append(icmdCmd.Env, fmt.Sprintf("%s=%s", k, v)) @@ -182,8 +188,9 @@ func (gc *GenericCommand) Clear() Command { return gc } -func (gc *GenericCommand) WithT(t *testing.T) { +func (gc *GenericCommand) WithT(t *testing.T) Command { gc.t = t + return gc } func (gc *GenericCommand) WithTempDir(tempDir string) { diff --git a/pkg/testutil/test/data.go b/pkg/testutil/test/data.go index 99f2aa041ad..a2cc2d58b87 100644 --- a/pkg/testutil/test/data.go +++ b/pkg/testutil/test/data.go @@ -19,6 +19,7 @@ package test import ( "crypto/sha256" "fmt" + "regexp" "strings" "testing" ) @@ -116,17 +117,26 @@ func (dt *data) getConfig() map[ConfigKey]ConfigValue { return dt.config } -func defaultIdentifierHashing(name string) string { - s := strings.ReplaceAll(name, " ", "_") - s = strings.ReplaceAll(s, "/", "_") - s = strings.ReplaceAll(s, "-", "_") - s = strings.ReplaceAll(s, ",", "_") - s = strings.ToLower(s) - if len(s) > 76 { - s = fmt.Sprintf("%x", sha256.Sum256([]byte(s))) +func defaultIdentifierHashing(names ...string) string { + // Notes: identifier MAY be used for namespaces, image names, etc. + // So, the rules are stringent on what it can contain. + replaceWith := []byte("-") + name := strings.ToLower(strings.Join(names, string(replaceWith))) + // Ensure we have a unique identifier despite characters replacements (well, as unique as the names collection being passed) + signature := fmt.Sprintf("%x", sha256.Sum256([]byte(name)))[0:8] + // Make sure we do not use any unsafe characters + safeName := regexp.MustCompile(`[^a-z0-9-]+`) + // And we avoid repeats of the separator + noRepeat := regexp.MustCompile(fmt.Sprintf(`[%s]{2,}`, replaceWith)) + sn := safeName.ReplaceAll([]byte(name), replaceWith) + sn = noRepeat.ReplaceAll(sn, replaceWith) + // Do not allow trailing or leading dash (as that may stutter) + name = strings.Trim(string(sn), string(replaceWith)) + // Ensure we will never go above 76 characters in length (with signature) + if len(name) > 67 { + name = name[0:67] } - - return s + return name + "-" + signature } // TODO: allow to pass custom hashing methods? diff --git a/pkg/testutil/test/requirement.go b/pkg/testutil/test/requirement.go index 1acad13af28..c148fef1914 100644 --- a/pkg/testutil/test/requirement.go +++ b/pkg/testutil/test/requirement.go @@ -23,11 +23,11 @@ import ( "testing" ) -func MakeRequirement(fn func(data Data) (bool, string)) Requirement { - return func(data Data, t *testing.T) (bool, string) { - ret, mess := fn(data) +func MakeRequirement(fn func(data Data, t *testing.T) (bool, string)) Requirement { + return func(data Data, skip bool, t *testing.T) (bool, string) { + ret, mess := fn(data, t) - if t != nil && !ret { + if skip && !ret { t.Helper() t.Skipf("Test skipped as %s", mess) } @@ -37,7 +37,7 @@ func MakeRequirement(fn func(data Data) (bool, string)) Requirement { } func Binary(name string) Requirement { - return MakeRequirement(func(data Data) (ret bool, mess string) { + return MakeRequirement(func(data Data, t *testing.T) (ret bool, mess string) { mess = fmt.Sprintf("executable %q has been found in PATH", name) ret = true if _, err := exec.LookPath(name); err != nil { @@ -50,8 +50,8 @@ func Binary(name string) Requirement { } func OS(os string) Requirement { - return MakeRequirement(func(data Data) (ret bool, mess string) { - mess = fmt.Sprintf("current operating is %q", runtime.GOOS) + return MakeRequirement(func(data Data, t *testing.T) (ret bool, mess string) { + mess = fmt.Sprintf("current operating system is %q", runtime.GOOS) ret = true if runtime.GOOS != os { ret = false @@ -61,49 +61,38 @@ func OS(os string) Requirement { }) } -var Windows = MakeRequirement(func(data Data) (ret bool, mess string) { - ret = runtime.GOOS == "windows" - if ret { - mess = "operating system is Windows" - } else { - mess = "operating system is not Windows" - } - return ret, mess -}) - -var Linux = MakeRequirement(func(data Data) (ret bool, mess string) { - ret = runtime.GOOS == "linux" - if ret { - mess = "operating system is Linux" - } else { - mess = "operating system is not Linux" - } - return ret, mess -}) - -var Darwin = MakeRequirement(func(data Data) (ret bool, mess string) { - ret = runtime.GOOS == "darwin" - if ret { - mess = "operating system is Darwin" - } else { - mess = "operating system is not Darwin" - } - return ret, mess -}) +func Arch(arch string) Requirement { + return MakeRequirement(func(data Data, t *testing.T) (ret bool, mess string) { + mess = fmt.Sprintf("current architecture is %q", runtime.GOARCH) + ret = true + if runtime.GOARCH != arch { + ret = false + } + + return ret, mess + }) +} + +var Amd64 = Arch("amd64") +var Arm64 = Arch("arm64") +var Windows = OS("windows") +var Linux = OS("linux") +var Darwin = OS("darwin") func Not(requirement Requirement) Requirement { - return MakeRequirement(func(data Data) (ret bool, mess string) { - b, mess := requirement(data, nil) + return MakeRequirement(func(data Data, t *testing.T) (ret bool, mess string) { + b, mess := requirement(data, false, t) + return !b, mess }) } func Require(thing ...Requirement) Requirement { - return func(data Data, t *testing.T) (ret bool, mess string) { + return func(data Data, skip bool, t *testing.T) (ret bool, mess string) { for _, th := range thing { - b, m := th(data, nil) + b, m := th(data, false, t) if !b { - if t != nil { + if skip { t.Helper() t.Skipf("Test skipped as %s", m) } diff --git a/pkg/testutil/test/test.go b/pkg/testutil/test/test.go index 2e6743be8e6..858563be789 100644 --- a/pkg/testutil/test/test.go +++ b/pkg/testutil/test/test.go @@ -24,7 +24,7 @@ import ( // A Requirement is a function that can evaluate random requirement and possibly skip a test // See test.MakeRequirement to make your own -type Requirement func(data Data, t *testing.T) (bool, string) +type Requirement func(data Data, skip bool, t *testing.T) (bool, string) // A Butler is the function signature meant to be attached to a Setup or Cleanup routine for a test.Case type Butler func(data Data, helpers Helpers) From 19fa3d934c0bee7f4cca3d2c5ed627c23a2298ee Mon Sep 17 00:00:00 2001 From: apostasie Date: Wed, 25 Sep 2024 14:03:17 -0700 Subject: [PATCH 2/4] Increase delay of delayOnceReader to reduce flakyness of TestAttach Signed-off-by: apostasie --- pkg/testutil/testutil_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/testutil/testutil_linux.go b/pkg/testutil/testutil_linux.go index 0629abbdac2..dce9431e021 100644 --- a/pkg/testutil/testutil_linux.go +++ b/pkg/testutil/testutil_linux.go @@ -126,7 +126,7 @@ func NewDelayOnceReader(wrapped io.Reader) io.Reader { func (r *delayOnceReader) Read(p []byte) (int, error) { // FIXME: this is obviously not exact science. At 1 second, it will fail regularly on the CI under load. - r.once.Do(func() { time.Sleep(2 * time.Second) }) + r.once.Do(func() { time.Sleep(5 * time.Second) }) n, err := r.wrapped.Read(p) if errors.Is(err, io.EOF) { time.Sleep(time.Second) From 5ce1e4b69f9d24b85acd0ac51102d0f35834973c Mon Sep 17 00:00:00 2001 From: apostasie Date: Wed, 25 Sep 2024 20:35:26 -0700 Subject: [PATCH 3/4] Fix parallelism for docker Signed-off-by: apostasie --- .../network/network_prune_linux_test.go | 77 ++++++----- cmd/nerdctl/system/system_prune_linux_test.go | 128 +++++++++--------- cmd/nerdctl/volume/volume_prune_linux_test.go | 94 +++++++------ 3 files changed, 156 insertions(+), 143 deletions(-) diff --git a/cmd/nerdctl/network/network_prune_linux_test.go b/cmd/nerdctl/network/network_prune_linux_test.go index 1692ac55053..333b3aefd95 100644 --- a/cmd/nerdctl/network/network_prune_linux_test.go +++ b/cmd/nerdctl/network/network_prune_linux_test.go @@ -27,45 +27,50 @@ import ( func TestNetworkPrune(t *testing.T) { nerdtest.Setup() - testGroup := &test.Group{ - { - Description: "Prune does not collect started container network", - Require: nerdtest.Private, - Setup: func(data test.Data, helpers test.Helpers) { - helpers.Ensure("network", "create", data.Identifier()) - helpers.Ensure("run", "-d", "--net", data.Identifier(), "--name", data.Identifier(), testutil.NginxAlpineImage) - }, - Cleanup: func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", data.Identifier()) - helpers.Anyhow("network", "rm", data.Identifier()) - }, - Command: test.RunCommand("network", "prune", "-f"), - Expected: func(data test.Data, helpers test.Helpers) *test.Expected { - return &test.Expected{ - Output: test.DoesNotContain(data.Identifier()), - } - }, - }, - { - Description: "Prune does collect stopped container network", - Require: nerdtest.Private, - Setup: func(data test.Data, helpers test.Helpers) { - helpers.Ensure("network", "create", data.Identifier()) - helpers.Ensure("run", "-d", "--net", data.Identifier(), "--name", data.Identifier(), testutil.NginxAlpineImage) - helpers.Ensure("stop", data.Identifier()) - }, - Cleanup: func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", data.Identifier()) - helpers.Anyhow("network", "rm", data.Identifier()) + testCase := &test.Case{ + Description: "TestNetworkPrune", + Require: nerdtest.Private, + + SubTests: []*test.Case{ + { + Description: "Prune does not collect started container network", + NoParallel: true, + Setup: func(data test.Data, helpers test.Helpers) { + helpers.Ensure("network", "create", data.Identifier()) + helpers.Ensure("run", "-d", "--net", data.Identifier(), "--name", data.Identifier(), testutil.NginxAlpineImage) + }, + Cleanup: func(data test.Data, helpers test.Helpers) { + helpers.Anyhow("rm", "-f", data.Identifier()) + helpers.Anyhow("network", "rm", data.Identifier()) + }, + Command: test.RunCommand("network", "prune", "-f"), + Expected: func(data test.Data, helpers test.Helpers) *test.Expected { + return &test.Expected{ + Output: test.DoesNotContain(data.Identifier()), + } + }, }, - Command: test.RunCommand("network", "prune", "-f"), - Expected: func(data test.Data, helpers test.Helpers) *test.Expected { - return &test.Expected{ - Output: test.Contains(data.Identifier()), - } + { + Description: "Prune does collect stopped container network", + NoParallel: true, + Setup: func(data test.Data, helpers test.Helpers) { + helpers.Ensure("network", "create", data.Identifier()) + helpers.Ensure("run", "-d", "--net", data.Identifier(), "--name", data.Identifier(), testutil.NginxAlpineImage) + helpers.Ensure("stop", data.Identifier()) + }, + Cleanup: func(data test.Data, helpers test.Helpers) { + helpers.Anyhow("rm", "-f", data.Identifier()) + helpers.Anyhow("network", "rm", data.Identifier()) + }, + Command: test.RunCommand("network", "prune", "-f"), + Expected: func(data test.Data, helpers test.Helpers) *test.Expected { + return &test.Expected{ + Output: test.Contains(data.Identifier()), + } + }, }, }, } - testGroup.Run(t) + testCase.Run(t) } diff --git a/cmd/nerdctl/system/system_prune_linux_test.go b/cmd/nerdctl/system/system_prune_linux_test.go index 40d08f1beea..dbf78726fcd 100644 --- a/cmd/nerdctl/system/system_prune_linux_test.go +++ b/cmd/nerdctl/system/system_prune_linux_test.go @@ -32,75 +32,79 @@ import ( func TestSystemPrune(t *testing.T) { nerdtest.Setup() - testGroup := &test.Group{ - { - Description: "volume prune all success", - // Private because of prune evidently - Require: nerdtest.Private, - Setup: func(data test.Data, helpers test.Helpers) { - helpers.Ensure("network", "create", data.Identifier()) - helpers.Ensure("volume", "create", data.Identifier()) - anonIdentifier := helpers.Capture("volume", "create") - helpers.Ensure("run", "-v", fmt.Sprintf("%s:/volume", data.Identifier()), - "--net", data.Identifier(), "--name", data.Identifier(), testutil.CommonImage) + testCase := &test.Case{ + Description: "TestSystemPrune", + NoParallel: true, + SubTests: []*test.Case{ + { + Description: "volume prune all success", + // Private because of prune evidently + Require: nerdtest.Private, + Setup: func(data test.Data, helpers test.Helpers) { + helpers.Ensure("network", "create", data.Identifier()) + helpers.Ensure("volume", "create", data.Identifier()) + anonIdentifier := helpers.Capture("volume", "create") + helpers.Ensure("run", "-v", fmt.Sprintf("%s:/volume", data.Identifier()), + "--net", data.Identifier(), "--name", data.Identifier(), testutil.CommonImage) - data.Set("anonIdentifier", anonIdentifier) + data.Set("anonIdentifier", anonIdentifier) + }, + Cleanup: func(data test.Data, helpers test.Helpers) { + helpers.Anyhow("network", "rm", data.Identifier()) + helpers.Anyhow("volume", "rm", data.Identifier()) + helpers.Anyhow("volume", "rm", data.Get("anonIdentifier")) + helpers.Anyhow("rm", "-f", data.Identifier()) + }, + Command: test.RunCommand("system", "prune", "-f", "--volumes", "--all"), + Expected: func(data test.Data, helpers test.Helpers) *test.Expected { + return &test.Expected{ + ExitCode: 0, + Output: func(stdout string, info string, t *testing.T) { + volumes := helpers.Capture("volume", "ls") + networks := helpers.Capture("network", "ls") + images := helpers.Capture("images") + containers := helpers.Capture("ps", "-a") + assert.Assert(t, strings.Contains(volumes, data.Identifier()), volumes) + assert.Assert(t, !strings.Contains(volumes, data.Get("anonIdentifier")), volumes) + assert.Assert(t, !strings.Contains(containers, data.Identifier()), containers) + assert.Assert(t, !strings.Contains(networks, data.Identifier()), networks) + assert.Assert(t, !strings.Contains(images, testutil.CommonImage), images) + }, + } + }, }, - Cleanup: func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("network", "rm", data.Identifier()) - helpers.Anyhow("volume", "rm", data.Identifier()) - helpers.Anyhow("volume", "rm", data.Get("anonIdentifier")) - helpers.Anyhow("rm", "-f", data.Identifier()) - }, - Command: test.RunCommand("system", "prune", "-f", "--volumes", "--all"), - Expected: func(data test.Data, helpers test.Helpers) *test.Expected { - return &test.Expected{ - ExitCode: 0, - Output: func(stdout string, info string, t *testing.T) { - volumes := helpers.Capture("volume", "ls") - networks := helpers.Capture("network", "ls") - images := helpers.Capture("images") - containers := helpers.Capture("ps", "-a") - assert.Assert(t, strings.Contains(volumes, data.Identifier()), volumes) - assert.Assert(t, !strings.Contains(volumes, data.Get("anonIdentifier")), volumes) - assert.Assert(t, !strings.Contains(containers, data.Identifier()), containers) - assert.Assert(t, !strings.Contains(networks, data.Identifier()), networks) - assert.Assert(t, !strings.Contains(images, testutil.CommonImage), images) - }, - } - }, - }, - { - Description: "buildkit", - // FIXME: using a dedicated namespace does not work with rootful (because of buildkitd) - NoParallel: true, - // buildkitd is not available with docker - Require: test.Require(nerdtest.Build, test.Not(nerdtest.Docker)), - // FIXME: this test will happily say "green" even if the command actually fails to do its duty - // if there is nothing in the build cache. - // Ensure with setup here that we DO build something first - Setup: func(data test.Data, helpers test.Helpers) { - helpers.Ensure("system", "prune", "-f", "--volumes", "--all") - }, - Command: func(data test.Data, helpers test.Helpers) test.Command { - buildctlBinary, err := buildkitutil.BuildctlBinary() - if err != nil { - t.Fatal(err) - } + { + Description: "buildkit", + // FIXME: using a dedicated namespace does not work with rootful (because of buildkitd) + NoParallel: true, + // buildkitd is not available with docker + Require: test.Require(nerdtest.Build, test.Not(nerdtest.Docker)), + // FIXME: this test will happily say "green" even if the command actually fails to do its duty + // if there is nothing in the build cache. + // Ensure with setup here that we DO build something first + Setup: func(data test.Data, helpers test.Helpers) { + helpers.Ensure("system", "prune", "-f", "--volumes", "--all") + }, + Command: func(data test.Data, helpers test.Helpers) test.Command { + buildctlBinary, err := buildkitutil.BuildctlBinary() + if err != nil { + t.Fatal(err) + } - host, err := buildkitutil.GetBuildkitHost(testutil.Namespace) - if err != nil { - t.Fatal(err) - } + host, err := buildkitutil.GetBuildkitHost(testutil.Namespace) + if err != nil { + t.Fatal(err) + } - buildctlArgs := buildkitutil.BuildctlBaseArgs(host) - buildctlArgs = append(buildctlArgs, "du") + buildctlArgs := buildkitutil.BuildctlBaseArgs(host) + buildctlArgs = append(buildctlArgs, "du") - return helpers.CustomCommand(buildctlBinary, buildctlArgs...) + return helpers.CustomCommand(buildctlBinary, buildctlArgs...) + }, + Expected: test.Expects(0, nil, test.Contains("Total:\t\t0B")), }, - Expected: test.Expects(0, nil, test.Contains("Total:\t\t0B")), }, } - testGroup.Run(t) + testCase.Run(t) } diff --git a/cmd/nerdctl/volume/volume_prune_linux_test.go b/cmd/nerdctl/volume/volume_prune_linux_test.go index 8898ad30ab4..da859b1af43 100644 --- a/cmd/nerdctl/volume/volume_prune_linux_test.go +++ b/cmd/nerdctl/volume/volume_prune_linux_test.go @@ -56,54 +56,58 @@ func TestVolumePrune(t *testing.T) { } // This set must be marked as private, since we cannot prune without interacting with other tests. - testGroup := &test.Group{ - { - Description: "prune anonymous only", - Require: nerdtest.Private, - Command: test.RunCommand("volume", "prune", "-f"), - Setup: setup, - Cleanup: cleanup, - Expected: func(data test.Data, helpers test.Helpers) *test.Expected { - return &test.Expected{ - Output: test.All( - test.DoesNotContain(data.Get("anonIDBusy")), - test.Contains(data.Get("anonIDDangling")), - test.DoesNotContain(data.Get("namedBusy")), - test.DoesNotContain(data.Get("namedDangling")), - func(stdout string, info string, t *testing.T) { - helpers.Ensure("volume", "inspect", data.Get("anonIDBusy")) - helpers.Fail("volume", "inspect", data.Get("anonIDDangling")) - helpers.Ensure("volume", "inspect", data.Get("namedBusy")) - helpers.Ensure("volume", "inspect", data.Get("namedDangling")) - }, - ), - } + testCase := &test.Case{ + Description: "Prune", + Require: nerdtest.Private, + SubTests: []*test.Case{ + { + Description: "prune anonymous only", + NoParallel: true, + Command: test.RunCommand("volume", "prune", "-f"), + Setup: setup, + Cleanup: cleanup, + Expected: func(data test.Data, helpers test.Helpers) *test.Expected { + return &test.Expected{ + Output: test.All( + test.DoesNotContain(data.Get("anonIDBusy")), + test.Contains(data.Get("anonIDDangling")), + test.DoesNotContain(data.Get("namedBusy")), + test.DoesNotContain(data.Get("namedDangling")), + func(stdout string, info string, t *testing.T) { + helpers.Ensure("volume", "inspect", data.Get("anonIDBusy")) + helpers.Fail("volume", "inspect", data.Get("anonIDDangling")) + helpers.Ensure("volume", "inspect", data.Get("namedBusy")) + helpers.Ensure("volume", "inspect", data.Get("namedDangling")) + }, + ), + } + }, }, - }, - { - Description: "prune all", - Require: nerdtest.Private, - Command: test.RunCommand("volume", "prune", "-f", "--all"), - Setup: setup, - Cleanup: cleanup, - Expected: func(data test.Data, helpers test.Helpers) *test.Expected { - return &test.Expected{ - Output: test.All( - test.DoesNotContain(data.Get("anonIDBusy")), - test.Contains(data.Get("anonIDDangling")), - test.DoesNotContain(data.Get("namedBusy")), - test.Contains(data.Get("namedDangling")), - func(stdout string, info string, t *testing.T) { - helpers.Ensure("volume", "inspect", data.Get("anonIDBusy")) - helpers.Fail("volume", "inspect", data.Get("anonIDDangling")) - helpers.Ensure("volume", "inspect", data.Get("namedBusy")) - helpers.Fail("volume", "inspect", data.Get("namedDangling")) - }, - ), - } + { + Description: "prune all", + NoParallel: true, + Command: test.RunCommand("volume", "prune", "-f", "--all"), + Setup: setup, + Cleanup: cleanup, + Expected: func(data test.Data, helpers test.Helpers) *test.Expected { + return &test.Expected{ + Output: test.All( + test.DoesNotContain(data.Get("anonIDBusy")), + test.Contains(data.Get("anonIDDangling")), + test.DoesNotContain(data.Get("namedBusy")), + test.Contains(data.Get("namedDangling")), + func(stdout string, info string, t *testing.T) { + helpers.Ensure("volume", "inspect", data.Get("anonIDBusy")) + helpers.Fail("volume", "inspect", data.Get("anonIDDangling")) + helpers.Ensure("volume", "inspect", data.Get("namedBusy")) + helpers.Fail("volume", "inspect", data.Get("namedDangling")) + }, + ), + } + }, }, }, } - testGroup.Run(t) + testCase.Run(t) } From 2c1a5b8721f901933d9d752f301e3f70a04f6c05 Mon Sep 17 00:00:00 2001 From: apostasie Date: Wed, 2 Oct 2024 01:00:10 -0700 Subject: [PATCH 4/4] Fix tests broken by tooling update Signed-off-by: apostasie --- .../container/container_create_linux_test.go | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/cmd/nerdctl/container/container_create_linux_test.go b/cmd/nerdctl/container/container_create_linux_test.go index 234b2170db1..ff5e076d241 100644 --- a/cmd/nerdctl/container/container_create_linux_test.go +++ b/cmd/nerdctl/container/container_create_linux_test.go @@ -206,14 +206,16 @@ func TestIssue2993(t *testing.T) { testCase := &test.Group{ { Description: "Issue #2993 - nerdctl no longer leaks containers and etchosts directories and files when container creation fails.", - Require: nerdtest.Private, Setup: func(data test.Data, helpers test.Helpers) { - helpers.Ensure("run", "--name", data.Identifier(), "-d", testutil.AlpineImage, "sleep", "infinity") + dataRoot := data.TempDir() + + helpers.Ensure("run", "--data-root", dataRoot, "--name", data.Identifier(), "-d", testutil.AlpineImage, "sleep", "infinity") - dataRoot := string(data.ReadConfig(nerdtest.DataRoot)) h := getAddrHash(defaults.DefaultAddress) dataStore := filepath.Join(dataRoot, h) - namespace := data.Identifier() + + // FIXME: update with next tooling iteration to retrieve from the command + namespace := "nerdctl-test" containersPath := filepath.Join(dataStore, "containers", namespace) containersDirs, err := os.ReadDir(containersPath) @@ -229,10 +231,10 @@ func TestIssue2993(t *testing.T) { data.Set(etchostsPathKey, etchostsPath) }, Cleanup: func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", data.Identifier()) + helpers.Anyhow("rm", "--data-root", data.TempDir(), "-f", data.Identifier()) }, Command: func(data test.Data, helpers test.Helpers) test.Command { - return helpers.Command("run", "--name", data.Identifier(), "-d", testutil.AlpineImage, "sleep", "infinity") + return helpers.Command("run", "--data-root", data.TempDir(), "--name", data.Identifier(), "-d", testutil.AlpineImage, "sleep", "infinity") }, Expected: func(data test.Data, helpers test.Helpers) *test.Expected { return &test.Expected{ @@ -252,14 +254,16 @@ func TestIssue2993(t *testing.T) { }, { Description: "Issue #2993 - nerdctl no longer leaks containers and etchosts directories and files when containers are removed.", - Require: nerdtest.Private, Setup: func(data test.Data, helpers test.Helpers) { - helpers.Ensure("run", "--name", data.Identifier(), "-d", testutil.AlpineImage, "sleep", "infinity") + dataRoot := data.TempDir() + + helpers.Ensure("run", "--data-root", dataRoot, "--name", data.Identifier(), "-d", testutil.AlpineImage, "sleep", "infinity") - dataRoot := string(data.ReadConfig(nerdtest.DataRoot)) h := getAddrHash(defaults.DefaultAddress) dataStore := filepath.Join(dataRoot, h) - namespace := data.Identifier() + + // FIXME: update with next tooling iteration to retrieve from the command + namespace := "nerdctl-test" containersPath := filepath.Join(dataStore, "containers", namespace) containersDirs, err := os.ReadDir(containersPath) @@ -275,10 +279,10 @@ func TestIssue2993(t *testing.T) { data.Set(etchostsPathKey, etchostsPath) }, Cleanup: func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", data.Identifier()) + helpers.Anyhow("--data-root", data.TempDir(), "rm", "-f", data.Identifier()) }, Command: func(data test.Data, helpers test.Helpers) test.Command { - return helpers.Command("rm", "-f", data.Identifier()) + return helpers.Command("--data-root", data.TempDir(), "rm", "-f", data.Identifier()) }, Expected: func(data test.Data, helpers test.Helpers) *test.Expected { return &test.Expected{