diff --git a/cmd/nerdctl/container/container_list_windows_test.go b/cmd/nerdctl/container/container_list_windows_test.go index 08bd3c3c5c8..7614864f933 100644 --- a/cmd/nerdctl/container/container_list_windows_test.go +++ b/cmd/nerdctl/container/container_list_windows_test.go @@ -1,19 +1,17 @@ /* - 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. +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 container import ( @@ -21,226 +19,177 @@ import ( "strings" "testing" - "gotest.tools/v3/assert" - + "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" + "gotest.tools/v3/assert" ) -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) - - base.Cmd("pull", "--quiet", testutil.NginxAlpineImage).AssertOK() - - testContainerName := testutil.Identifier(t) + identity - t.Cleanup(func() { - base.Cmd("rm", "-f", testContainerName).AssertOK() - }) - - // 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 := []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" - } - - base.Cmd(args...).AssertOK() - if restart { - base.EnsureContainerStarted(testContainerName) +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) + } + args := []string{ + "run", + "-d", + } + if hyperv { + args = append(args, "--isolation", "hyperv") + } + args = append(args, + "--name", containerName, + "--label", formatter.FormatLabels(testLabels), + testutil.NginxAlpineImage, + ) + if !restart { + args = append(args, "--restart=no") + } + 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 " - - if !strings.Contains(size, expectedSize) { - return fmt.Errorf("expect container size %s, but got %s", expectedSize, size) + 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"))) + } + testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected { + return &test.Expected{ + ExitCode: 0, + 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"), + 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") + 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") } - - 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.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"))) } - 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) + testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected { + return &test.Expected{ + ExitCode: 0, + 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)) + }, } - - 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 := "72.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 TestListProcessContainerWideMode(t *testing.T) { - testutil.DockerIncompatible(t) - base, testContainer := preparePsTestContainer(t, "listWithMode", true, false) - - // 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)) - } - - 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) - } - - 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) + testCase := nerdtest.Setup() + testCase.Require = require.Not(nerdtest.Docker) + testCase.Setup = setupPsTestContainer("listWithMode", true, false) + testCase.Cleanup = cleanupPsTestContainer() + 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"))) + } + testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected { + return &test.Expected{ + ExitCode: 0, + 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)) + }, } - return nil - }) + } + testCase.Run(t) } - func TestListProcessContainerWithLabels(t *testing.T) { - base, testContainer := preparePsTestContainer(t, "listWithLabels", true, false) - - // 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) - } - - for i := range testContainer.labels { - if value, ok := labelsMap[i]; ok { - assert.Equal(t, value, testContainer.labels[i]) - } + testCase := nerdtest.Setup() + testCase.Setup = setupPsTestContainer("listWithLabels", true, false) + testCase.Cleanup = cleanupPsTestContainer() + 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"))) + } + testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected { + return &test.Expected{ + ExitCode: 0, + 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) }