diff --git a/cmd/nerdctl/container/container_run_network_windows_test.go b/cmd/nerdctl/container/container_run_network_windows_test.go index a727978eed5..848acd848fe 100644 --- a/cmd/nerdctl/container/container_run_network_windows_test.go +++ b/cmd/nerdctl/container/container_run_network_windows_test.go @@ -25,39 +25,24 @@ import ( "github.com/Microsoft/hcsshim" "gotest.tools/v3/assert" + "github.com/containerd/nerdctl/mod/tigron/expect" + "github.com/containerd/nerdctl/mod/tigron/test" + "github.com/containerd/nerdctl/mod/tigron/tig" + "github.com/containerd/nerdctl/v2/pkg/defaults" "github.com/containerd/nerdctl/v2/pkg/netutil" "github.com/containerd/nerdctl/v2/pkg/testutil" + "github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest" ) // TestRunInternetConnectivity tests Internet connectivity by pinging github.com. func TestRunInternetConnectivity(t *testing.T) { - base := testutil.NewBase(t) - - type testCase struct { - args []string - } - testCases := []testCase{ - { - args: []string{"--net", "nat"}, - }, - } - for _, tc := range testCases { - tc := tc // IMPORTANT - name := "default" - if len(tc.args) > 0 { - name = strings.Join(tc.args, "_") - } - t.Run(name, func(t *testing.T) { - args := []string{"run", "--rm"} - args = append(args, tc.args...) - // TODO(aznashwan): smarter way to ensure internet connectivity is working. - // ping doesn't seem to work on GitHub Actions ("Request timed out.") - args = append(args, testutil.CommonImage, "curl.exe -sSL https://github.com") - cmd := base.Cmd(args...) - cmd.AssertOutContains("") - }) - } + testCase := nerdtest.Setup() + // TODO(aznashwan): smarter way to ensure internet connectivity is working. + // ping doesn't seem to work on GitHub Actions ("Request timed out.") + testCase.Command = test.Command("run", "--rm", "--net", "nat", testutil.CommonImage, "curl.exe -sSL https://github.com") + testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains("")) + testCase.Run(t) } func TestRunPort(t *testing.T) { @@ -88,60 +73,75 @@ func listHnsEndpointsRegex(hnsEndpointNameRegex string) ([]hcsshim.HNSEndpoint, // Asserts whether the container with the provided has any HNS endpoints with the expected // naming format (`${container_id}_${network_name}`) for all of the provided network names. // The container ID can be a regex. -func assertHnsEndpointsExistence(t *testing.T, shouldExist bool, containerIDRegex string, networkNames ...string) { +func assertHnsEndpointsExistence(helpers test.Helpers, shouldExist bool, containerIDRegex string, networkNames ...string) { + helpers.T().Helper() for _, netName := range networkNames { endpointName := fmt.Sprintf("%s_%s", containerIDRegex, netName) - - testName := fmt.Sprintf("hns_endpoint_%s_shouldExist_%t", endpointName, shouldExist) - t.Run(testName, func(t *testing.T) { - matchingEndpoints, err := listHnsEndpointsRegex(endpointName) - assert.NilError(t, err) - if shouldExist { - assert.Equal(t, len(matchingEndpoints), 1) - assert.Equal(t, matchingEndpoints[0].Name, endpointName) - } else { - assert.Equal(t, len(matchingEndpoints), 0) - } - }) + matchingEndpoints, err := listHnsEndpointsRegex(endpointName) + assert.NilError(helpers.T(), err) + if shouldExist { + assert.Equal(helpers.T(), len(matchingEndpoints), 1) + assert.Equal(helpers.T(), matchingEndpoints[0].Name, endpointName) + } else { + assert.Equal(helpers.T(), len(matchingEndpoints), 0) + } } } // Tests whether HNS endpoints are properly created and managed throughout the lifecycle of a container. func TestHnsEndpointsExistDuringContainerLifecycle(t *testing.T) { - base := testutil.NewBase(t) - - testNet, err := getTestingNetwork() - assert.NilError(t, err) - - tID := testutil.Identifier(t) - defer base.Cmd("rm", "-f", tID).Run() - cmd := base.Cmd( - "create", - "--name", tID, - "--net", testNet.Name, - testutil.CommonImage, - "bash", "-c", - // NOTE: the BusyBox image used in Windows testing's `sleep` binary - // does not support the `infinity` argument. - "tail", "-f", - ) - t.Logf("Creating HNS lifecycle test container with command: %q", strings.Join(cmd.Command, " ")) - containerID := strings.TrimSpace(cmd.Run().Stdout()) - t.Logf("HNS endpoint lifecycle test container ID: %q", containerID) - - // HNS endpoints should be allocated on container creation. - assertHnsEndpointsExistence(t, true, containerID, testNet.Name) + testCase := nerdtest.Setup() + // This test inspects host-wide HNS endpoint state on the shared default network, + // which is not safe to run in parallel with other tests touching the same network. + testCase.NoParallel = true - // Starting and stopping the container should NOT affect/change the endpoints. - base.Cmd("start", containerID).AssertOK() - assertHnsEndpointsExistence(t, true, containerID, testNet.Name) + var netName string + var containerID string - base.Cmd("stop", containerID).AssertOK() - assertHnsEndpointsExistence(t, true, containerID, testNet.Name) + testCase.Cleanup = func(data test.Data, helpers test.Helpers) { + helpers.Anyhow("rm", "-f", data.Identifier()) + } + testCase.Setup = func(data test.Data, helpers test.Helpers) { + testNet, err := getTestingNetwork() + assert.NilError(helpers.T(), err) + netName = testNet.Name - // Removing the container should remove the HNS endpoints. - base.Cmd("rm", containerID).AssertOK() - assertHnsEndpointsExistence(t, false, containerID, testNet.Name) + // NOTE: the BusyBox image used in Windows testing's `sleep` binary + // does not support the `infinity` argument. + createOut := helpers.Capture( + "create", + "--name", data.Identifier(), + "--net", testNet.Name, + testutil.CommonImage, + "bash", "-c", + "tail", "-f", + ) + containerID = strings.TrimSpace(createOut) + helpers.T().Log(fmt.Sprintf("HNS endpoint lifecycle test container ID: %q", containerID)) + + // HNS endpoints should be allocated on container creation. + assertHnsEndpointsExistence(helpers, true, containerID, netName) + + // Starting and stopping the container should NOT affect/change the endpoints. + helpers.Ensure("start", containerID) + assertHnsEndpointsExistence(helpers, true, containerID, netName) + + helpers.Ensure("stop", containerID) + assertHnsEndpointsExistence(helpers, true, containerID, netName) + } + testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand { + // Removing the container should remove the HNS endpoints. + return helpers.Command("rm", containerID) + } + testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected { + return &test.Expected{ + ExitCode: expect.ExitCodeSuccess, + Output: func(stdout string, t tig.T) { + assertHnsEndpointsExistence(helpers, false, containerID, netName) + }, + } + } + testCase.Run(t) } // Returns a network to be used for testing. @@ -159,30 +159,46 @@ func getTestingNetwork() (*netutil.NetworkConfig, error) { // Tests whether HNS endpoints are properly removed when running `run --rm`. func TestHnsEndpointsRemovedAfterAttachedRun(t *testing.T) { - base := testutil.NewBase(t) - - testNet, err := getTestingNetwork() - assert.NilError(t, err) - - // NOTE: because we cannot set/obtain the ID of the container to check for the exact HNS - // endpoint name, we record the number of HNS endpoints on the testing network and - // ensure it remains constant until after the test. - existingEndpoints, err := listHnsEndpointsRegex(fmt.Sprintf(".*_%s", testNet.Name)) - assert.NilError(t, err) - originalEndpointsCount := len(existingEndpoints) - - tID := testutil.Identifier(t) - base.Cmd( - "run", - "--name", - tID, - "--rm", - "--net", testNet.Name, - testutil.CommonImage, - "ipconfig", "/all", - ).AssertOK() - - existingEndpoints, err = listHnsEndpointsRegex(fmt.Sprintf(".*_%s", testNet.Name)) - assert.NilError(t, err) - assert.Equal(t, originalEndpointsCount, len(existingEndpoints), "the number of HNS endpoints should equal pre-test amount") + testCase := nerdtest.Setup() + // This test counts host-wide HNS endpoints on the shared default network before and after + // the run; concurrent tests creating/removing containers on the same network would corrupt + // the count. Container cleanup is handled by `--rm`, so no Cleanup callback is needed. + testCase.NoParallel = true + + var netName string + var originalEndpointsCount int + + testCase.Setup = func(data test.Data, helpers test.Helpers) { + testNet, err := getTestingNetwork() + assert.NilError(helpers.T(), err) + netName = testNet.Name + + // NOTE: because we cannot set/obtain the ID of the container to check for the exact HNS + // endpoint name, we record the number of HNS endpoints on the testing network and + // ensure it remains constant until after the test. + existingEndpoints, err := listHnsEndpointsRegex(fmt.Sprintf(".*_%s", testNet.Name)) + assert.NilError(helpers.T(), err) + originalEndpointsCount = len(existingEndpoints) + } + testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand { + return helpers.Command( + "run", + "--name", data.Identifier(), + "--rm", + "--net", netName, + testutil.CommonImage, + "ipconfig", "/all", + ) + } + testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected { + return &test.Expected{ + ExitCode: expect.ExitCodeSuccess, + Output: func(stdout string, t tig.T) { + existingEndpoints, err := listHnsEndpointsRegex(fmt.Sprintf(".*_%s", netName)) + assert.NilError(t, err) + assert.Equal(t, originalEndpointsCount, len(existingEndpoints), "the number of HNS endpoints should equal pre-test amount") + }, + } + } + testCase.Run(t) }