Skip to content
Open
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
341 changes: 160 additions & 181 deletions cmd/nerdctl/container/container_list_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,224 +23,203 @@ import (

"gotest.tools/v3/assert"

"github.com/containerd/nerdctl/mod/tigron/expect"
"github.com/containerd/nerdctl/mod/tigron/require"
"github.com/containerd/nerdctl/mod/tigron/test"
"github.com/containerd/nerdctl/mod/tigron/tig"

"github.com/containerd/nerdctl/v2/pkg/formatter"
"github.com/containerd/nerdctl/v2/pkg/strutil"
"github.com/containerd/nerdctl/v2/pkg/tabutil"
"github.com/containerd/nerdctl/v2/pkg/testutil"
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
)

type psTestContainer struct {
name string
labels map[string]string
network string
}

func preparePsTestContainer(t *testing.T, identity string, restart bool, hyperv bool) (*testutil.Base, psTestContainer) {
base := testutil.NewBase(t)
func setupPsTestContainer(identity string, restart bool, hyperv bool) func(data test.Data, helpers test.Helpers) {
return func(data test.Data, helpers test.Helpers) {
helpers.Ensure("pull", "--quiet", testutil.NginxAlpineImage)
containerName := data.Identifier(identity)
data.Labels().Set("containerName", containerName)
// A container can have multiple labels.
testLabels := make(map[string]string)

for i := 0; i < 2; i++ {
k := fmt.Sprintf("%s-%d", data.Identifier(identity), i)
testLabels[k] = k
data.Labels().Set(fmt.Sprintf("label-key-%d", i), k)
data.Labels().Set(fmt.Sprintf("label-value-%d", i), k)
}

base.Cmd("pull", "--quiet", testutil.NginxAlpineImage).AssertOK()
args := []string{
"run",
"-d",
}

testContainerName := testutil.Identifier(t) + identity
t.Cleanup(func() {
base.Cmd("rm", "-f", testContainerName).AssertOK()
})
if hyperv {
args = append(args, "--isolation", "hyperv")
}

// A container can have multiple labels.
// Therefore, this test container has multiple labels to check it.
testLabels := make(map[string]string)
keys := []string{
testutil.Identifier(t) + identity,
testutil.Identifier(t) + identity,
}
// fill the value of testLabels
for _, k := range keys {
testLabels[k] = k
}
args = append(args,
"--name", containerName,
"--label", formatter.FormatLabels(testLabels),
testutil.NginxAlpineImage,
)

args := []string{
"run",
"-d",
"--name",
testContainerName,
"--label",
formatter.FormatLabels(testLabels),
testutil.NginxAlpineImage,
}
if !restart {
args = append(args, "--restart=no")
}
if hyperv {
args = append(args[:3], args[1:]...)
args[1], args[2] = "--isolation", "hyperv"
}
if !restart {
args = append(args, "--restart=no")
}
Comment on lines +68 to +70
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we move the --restart=no append above the --name / image block? Otherwise, when restart is false, the container exits immediately instead of running with --restart=no.


base.Cmd(args...).AssertOK()
if restart {
base.EnsureContainerStarted(testContainerName)
helpers.Ensure(args...)
if restart {
nerdtest.EnsureContainerStarted(helpers, containerName)
}
}
}

return base, psTestContainer{
name: testContainerName,
labels: testLabels,
network: testContainerName,
func cleanupPsTestContainer() func(data test.Data, helpers test.Helpers) {
return func(data test.Data, helpers test.Helpers) {
helpers.Anyhow("rm", "-f", data.Labels().Get("containerName"))
}
}

func TestListProcessContainer(t *testing.T) {
base, testContainer := preparePsTestContainer(t, "list", true, false)

// hope there are no tests running parallel
base.Cmd("ps", "-n", "1", "-s").AssertOutWithFunc(func(stdout string) error {
// An example of nerdctl/docker ps -n 1 -s
// CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
// be8d386c991e docker.io/library/busybox:latest "top" 1 second ago Up c1 16.0 KiB (virtual 1.3 MiB)

lines := strings.Split(strings.TrimSpace(stdout), "\n")
if len(lines) < 2 {
return fmt.Errorf("expected at least 2 lines, got %d", len(lines))
}

tab := tabutil.NewReader("CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES\tSIZE")
err := tab.ParseHeader(lines[0])
if err != nil {
return fmt.Errorf("failed to parse header: %v", err)
}

container, _ := tab.ReadRow(lines[1], "NAMES")
assert.Equal(t, container, testContainer.name)

image, _ := tab.ReadRow(lines[1], "IMAGE")
assert.Equal(t, image, testutil.NginxAlpineImage)

size, _ := tab.ReadRow(lines[1], "SIZE")

// there is some difference between nerdctl and docker in calculating the size of the container
expectedSize := "36.0 MiB (virtual "
testCase := nerdtest.Setup()
testCase.Setup = setupPsTestContainer("list", true, false)
testCase.Cleanup = cleanupPsTestContainer()
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
return helpers.Command("ps", "-s", "--filter", fmt.Sprintf("name=%s", data.Labels().Get("containerName")))
}

if !strings.Contains(size, expectedSize) {
return fmt.Errorf("expect container size %s, but got %s", expectedSize, size)
testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected {
return &test.Expected{
ExitCode: expect.ExitCodeSuccess,
Output: func(stdout string, t tig.T) {
lines := strings.Split(strings.TrimSpace(stdout), "\n")
assert.Assert(t, len(lines) >= 2, fmt.Sprintf("expected at least 2 lines, got %d", len(lines)))
tab := tabutil.NewReader("CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES\tSIZE")
err := tab.ParseHeader(lines[0])
assert.NilError(t, err, "failed to parse header")
container, _ := tab.ReadRow(lines[1], "NAMES")
assert.Equal(t, container, data.Labels().Get("containerName"))
image, _ := tab.ReadRow(lines[1], "IMAGE")
assert.Equal(t, image, testutil.NginxAlpineImage)
size, _ := tab.ReadRow(lines[1], "SIZE")
assert.Assert(
t,
strings.Contains(size, "(virtual"),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally, the string was checked to see if it contained "36.0 MiB (virtual ", but is there a reason for this?

fmt.Sprintf("expect container size to contain '(virtual', but got %s", size),
)
},
}
}

return nil
})
testCase.Run(t)
}

func TestListHyperVContainer(t *testing.T) {
if !testutil.HyperVSupported() {
t.Skip("HyperV is not enabled, skipping test")
}

base, testContainer := preparePsTestContainer(t, "list", true, true)
inspect := base.InspectContainer(testContainer.name)
//check with HCS if the container is ineed a VM
isHypervContainer, err := testutil.HyperVContainer(inspect)
if err != nil {
t.Fatalf("unable to list HCS containers: %s", err)
testCase := nerdtest.Setup()
testCase.Require = nerdtest.HyperV

testCase.Setup = func(data test.Data, helpers test.Helpers) {
setupPsTestContainer("list", true, true)(data, helpers)
containerName := data.Labels().Get("containerName")
inspect := nerdtest.InspectContainer(helpers, containerName)
isHypervContainer, err := testutil.HyperVContainer(inspect)
assert.NilError(helpers.T(), err, "unable to list HCS containers")
assert.Assert(helpers.T(), isHypervContainer, "expected HyperV container")
}
assert.Assert(t, isHypervContainer, true)

// hope there are no tests running parallel
base.Cmd("ps", "-n", "1", "-s").AssertOutWithFunc(func(stdout string) error {
// An example of nerdctl/docker ps -n 1 -s
// CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
// be8d386c991e docker.io/library/busybox:latest "top" 1 second ago Up c1 16.0 KiB (virtual 1.3 MiB)

lines := strings.Split(strings.TrimSpace(stdout), "\n")
if len(lines) < 2 {
return fmt.Errorf("expected at least 2 lines, got %d", len(lines))
}

tab := tabutil.NewReader("CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES\tSIZE")
err := tab.ParseHeader(lines[0])
if err != nil {
return fmt.Errorf("failed to parse header: %v", err)
}

container, _ := tab.ReadRow(lines[1], "NAMES")
assert.Equal(t, container, testContainer.name)

image, _ := tab.ReadRow(lines[1], "IMAGE")
assert.Equal(t, image, testutil.NginxAlpineImage)
testCase.Cleanup = cleanupPsTestContainer()

size, _ := tab.ReadRow(lines[1], "SIZE")

// there is some difference between nerdctl and docker in calculating the size of the container
expectedSize := "72.0 MiB (virtual "
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
return helpers.Command("ps", "-s", "--filter", fmt.Sprintf("name=%s", data.Labels().Get("containerName")))
}

if !strings.Contains(size, expectedSize) {
return fmt.Errorf("expect container size %s, but got %s", expectedSize, size)
testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected {
return &test.Expected{
ExitCode: expect.ExitCodeSuccess,
Output: func(stdout string, t tig.T) {
lines := strings.Split(strings.TrimSpace(stdout), "\n")
assert.Assert(t, len(lines) >= 2, fmt.Sprintf("expected at least 2 lines, got %d", len(lines)))
tab := tabutil.NewReader("CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES\tSIZE")
err := tab.ParseHeader(lines[0])
assert.NilError(t, err, "failed to parse header")
container, _ := tab.ReadRow(lines[1], "NAMES")
assert.Equal(t, container, data.Labels().Get("containerName"))
image, _ := tab.ReadRow(lines[1], "IMAGE")
assert.Equal(t, image, testutil.NginxAlpineImage)
size, _ := tab.ReadRow(lines[1], "SIZE")
expectedSize := "72.0 MiB (virtual "
assert.Assert(t, strings.Contains(size, expectedSize),
fmt.Sprintf("expect container size %s, but got %s", expectedSize, size))
},
}
}

return nil
})
testCase.Run(t)
}

func TestListProcessContainerWideMode(t *testing.T) {
testutil.DockerIncompatible(t)
base, testContainer := preparePsTestContainer(t, "listWithMode", true, false)
testCase := nerdtest.Setup()
testCase.Require = require.Not(nerdtest.Docker)
testCase.Setup = setupPsTestContainer("listWithMode", true, false)
testCase.Cleanup = cleanupPsTestContainer()

// hope there are no tests running parallel
base.Cmd("ps", "-n", "1", "--format", "wide").AssertOutWithFunc(func(stdout string) error {

// An example of nerdctl ps --format wide
// CONTAINER ID IMAGE PLATFORM COMMAND CREATED STATUS PORTS NAMES RUNTIME SIZE
// 17181f208b61 docker.io/library/busybox:latest linux/amd64 "top" About an hour ago Up busybox-17181 io.containerd.runc.v2 16.0 KiB (virtual 1.3 MiB)

lines := strings.Split(strings.TrimSpace(stdout), "\n")
if len(lines) < 2 {
return fmt.Errorf("expected at least 2 lines, got %d", len(lines))
}
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
return helpers.Command("ps", "--format", "wide", "--filter", fmt.Sprintf("name=%s", data.Labels().Get("containerName")))
}

tab := tabutil.NewReader("CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES\tRUNTIME\tPLATFORM\tSIZE")
err := tab.ParseHeader(lines[0])
if err != nil {
return fmt.Errorf("failed to parse header: %v", err)
testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected {
return &test.Expected{
ExitCode: expect.ExitCodeSuccess,
Output: func(stdout string, t tig.T) {
lines := strings.Split(strings.TrimSpace(stdout), "\n")
assert.Assert(t, len(lines) >= 2, fmt.Sprintf("expected at least 2 lines, got %d", len(lines)))
tab := tabutil.NewReader("CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES\tRUNTIME\tPLATFORM\tSIZE")
err := tab.ParseHeader(lines[0])
assert.NilError(t, err, "failed to parse header")
container, _ := tab.ReadRow(lines[1], "NAMES")
assert.Equal(t, container, data.Labels().Get("containerName"))
image, _ := tab.ReadRow(lines[1], "IMAGE")
assert.Equal(t, image, testutil.NginxAlpineImage)
runtime, _ := tab.ReadRow(lines[1], "RUNTIME")
assert.Equal(t, runtime, "io.containerd.runhcs.v1")
size, _ := tab.ReadRow(lines[1], "SIZE")
expectedSize := "36.0 MiB (virtual "
assert.Assert(t, strings.Contains(size, expectedSize),
fmt.Sprintf("expect container size %s, but got %s", expectedSize, size))
},
}
}

container, _ := tab.ReadRow(lines[1], "NAMES")
assert.Equal(t, container, testContainer.name)

image, _ := tab.ReadRow(lines[1], "IMAGE")
assert.Equal(t, image, testutil.NginxAlpineImage)

runtime, _ := tab.ReadRow(lines[1], "RUNTIME")
assert.Equal(t, runtime, "io.containerd.runhcs.v1")

size, _ := tab.ReadRow(lines[1], "SIZE")
expectedSize := "36.0 MiB (virtual "
if !strings.Contains(size, expectedSize) {
return fmt.Errorf("expect container size %s, but got %s", expectedSize, size)
}
return nil
})
testCase.Run(t)
}

func TestListProcessContainerWithLabels(t *testing.T) {
base, testContainer := preparePsTestContainer(t, "listWithLabels", true, false)
testCase := nerdtest.Setup()
testCase.Setup = setupPsTestContainer("listWithLabels", true, false)
testCase.Cleanup = cleanupPsTestContainer()

// hope there are no tests running parallel
base.Cmd("ps", "-n", "1", "--format", "{{.Labels}}").AssertOutWithFunc(func(stdout string) error {

// An example of nerdctl ps --format "{{.Labels}}"
// key1=value1,key2=value2,key3=value3
lines := strings.Split(strings.TrimSpace(stdout), "\n")
if len(lines) != 1 {
return fmt.Errorf("expected 1 line, got %d", len(lines))
}

// check labels using map
// 1. the results has no guarantee to show the same order.
// 2. the results has no guarantee to show only configured labels.
labelsMap, err := strutil.ParseCSVMap(lines[0])
if err != nil {
return fmt.Errorf("failed to parse labels: %v", err)
}
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
return helpers.Command("ps", "--format", "{{.Labels}}", "--filter", fmt.Sprintf("name=%s", data.Labels().Get("containerName")))
}

for i := range testContainer.labels {
if value, ok := labelsMap[i]; ok {
assert.Equal(t, value, testContainer.labels[i])
}
testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected {
return &test.Expected{
ExitCode: expect.ExitCodeSuccess,
Output: func(stdout string, t tig.T) {
lines := strings.Split(strings.TrimSpace(stdout), "\n")
assert.Assert(t, len(lines) == 1, fmt.Sprintf("expected 1 line, got %d", len(lines)))
labelsMap, err := strutil.ParseCSVMap(lines[0])
assert.NilError(t, err, "failed to parse labels")

for idx := 0; idx < 2; idx++ {
labelKey := data.Labels().Get(fmt.Sprintf("label-key-%d", idx))
labelValue := data.Labels().Get(fmt.Sprintf("label-value-%d", idx))

if value, ok := labelsMap[labelKey]; ok {
assert.Equal(t, value, labelValue)
}
}
},
}
return nil
})
}
testCase.Run(t)
}
Loading