Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 112 additions & 96 deletions cmd/nerdctl/container/container_run_network_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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("<!DOCTYPE html>")
})
}
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("<!DOCTYPE html>"))
testCase.Run(t)
}

func TestRunPort(t *testing.T) {
Expand Down Expand Up @@ -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.
Expand All @@ -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)
}
Loading