Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: containerd/nerdctl
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: df70bc7586d94432665f00ade218d6e0d5500d85
Choose a base ref
..
head repository: containerd/nerdctl
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 72193e6c5400b73c02fd8111f189d53dfa7a34d4
Choose a head ref
Showing with 1,450 additions and 1,546 deletions.
  1. +1 −1 .github/workflows/ghcr-image-build-and-publish.yml
  2. +1 −1 .github/workflows/test.yml
  3. +164 −106 cmd/nerdctl/container/container_attach_linux_test.go
  4. +52 −13 cmd/nerdctl/container/container_exec_linux_test.go
  5. +78 −53 cmd/nerdctl/container/container_logs_test.go
  6. +12 −0 cmd/nerdctl/container/container_restart.go
  7. +29 −0 cmd/nerdctl/container/container_restart_linux_test.go
  8. +127 −75 cmd/nerdctl/container/container_run_linux_test.go
  9. +48 −29 cmd/nerdctl/container/container_start_linux_test.go
  10. +10 −0 cmd/nerdctl/container/container_stop.go
  11. +48 −4 cmd/nerdctl/container/container_stop_linux_test.go
  12. +10 −5 cmd/nerdctl/system/system_events_linux_test.go
  13. +4 −1 docs/command-reference.md
  14. +8 −10 go.mod
  15. +8 −8 go.sum
  16. +5 −0 pkg/api/types/container_types.go
  17. +2 −2 pkg/buildkitutil/buildkitutil_freebsd.go
  18. +7 −6 pkg/buildkitutil/buildkitutil_linux.go
  19. +4 −1 pkg/buildkitutil/buildkitutil_unix.go
  20. +5 −12 pkg/bypass4netnsutil/bypass4netnsutil.go
  21. +1 −1 pkg/cmd/container/restart.go
  22. +1 −1 pkg/cmd/container/stop.go
  23. +9 −3 pkg/composer/serviceparser/serviceparser.go
  24. +36 −0 pkg/composer/serviceparser/serviceparser_test.go
  25. +15 −8 pkg/containerutil/containerutil.go
  26. +2 −2 pkg/defaults/defaults_freebsd.go
  27. +7 −6 pkg/defaults/defaults_linux.go
  28. +2 −2 pkg/defaults/defaults_windows.go
  29. +14 −9 pkg/dnsutil/hostsstore/hostsstore.go
  30. +2 −1 pkg/infoutil/infoutil_freebsd.go
  31. +9 −4 pkg/infoutil/infoutil_linux.go
  32. +8 −4 pkg/infoutil/infoutil_windows.go
  33. +5 −1 pkg/netutil/netutil_unix.go
  34. +7 −2 pkg/rootlessutil/xdg_linux.go
  35. +0 −169 pkg/sysinfo/cgroup2_linux.go
  36. +0 −38 pkg/sysinfo/numcpu.go
  37. +0 −40 pkg/sysinfo/numcpu_linux.go
  38. +0 −59 pkg/sysinfo/numcpu_windows.go
  39. +0 −193 pkg/sysinfo/sysinfo.go
  40. +0 −330 pkg/sysinfo/sysinfo_linux.go
  41. +0 −147 pkg/sysinfo/sysinfo_linux_test.go
  42. +0 −31 pkg/sysinfo/sysinfo_other.go
  43. +0 −49 pkg/sysinfo/sysinfo_test.go
  44. +202 −0 pkg/testutil/test/LICENSE
  45. +212 −0 pkg/testutil/test/Makefile
  46. +77 −0 pkg/testutil/test/README.md
  47. +53 −22 pkg/testutil/test/command.go
  48. +14 −0 pkg/testutil/test/go.mod
  49. +10 −0 pkg/testutil/test/go.sum
  50. +19 −0 pkg/testutil/test/hack/dev-setup-linux.sh
  51. +19 −0 pkg/testutil/test/hack/dev-setup-macos.sh
  52. +13 −0 pkg/testutil/test/hack/headers/bash.txt
  53. +13 −0 pkg/testutil/test/hack/headers/dockerfile.txt
  54. +1 −17 pkg/{sysinfo/numcpu_other.go → testutil/test/hack/headers/go.txt}
  55. +16 −0 pkg/testutil/test/hack/headers/makefile.txt
  56. +30 −0 pkg/testutil/test/hack/make-lint-licenses.sh
  57. +5 −3 pkg/testutil/test/{ → internal/pty}/pty.go
  58. +9 −5 pkg/{sysinfo/doc.go → testutil/test/internal/pty/pty_darwin.go}
  59. +1 −1 pkg/testutil/test/{ → internal/pty}/pty_freebsd.go
  60. +12 −15 pkg/testutil/test/{ → internal/pty}/pty_linux.go
  61. +1 −1 pkg/testutil/test/{ → internal/pty}/pty_windows.go
  62. +2 −1 pkg/testutil/test/test.go
  63. +0 −54 pkg/testutil/testutil_linux.go
2 changes: 1 addition & 1 deletion .github/workflows/ghcr-image-build-and-publish.yml
Original file line number Diff line number Diff line change
@@ -60,7 +60,7 @@ jobs:
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
- name: Build and push Docker image
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 # v6.14.0
with:
context: .
platforms: linux/amd64,linux/arm64
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -397,7 +397,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 1
- uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
with:
path: /root/.vagrant.d
key: vagrant-${{ matrix.box }}
270 changes: 164 additions & 106 deletions cmd/nerdctl/container/container_attach_linux_test.go
Original file line number Diff line number Diff line change
@@ -17,9 +17,11 @@
package container

import (
"bytes"
"errors"
"os"
"strings"
"testing"
"time"

"gotest.tools/v3/assert"

@@ -28,133 +30,189 @@ import (
"github.com/containerd/nerdctl/v2/pkg/testutil/test"
)

// skipAttachForDocker should be called by attach-related tests that assert 'read detach keys' in stdout.
func skipAttachForDocker(t *testing.T) {
t.Helper()
if testutil.GetTarget() == testutil.Docker {
t.Skip("When detaching from a container, for a session started with 'docker attach'" +
", it prints 'read escape sequence', but for one started with 'docker (run|start)', it prints nothing." +
" However, the flag is called '--detach-keys' in all cases" +
", so nerdctl prints 'read detach keys' for all cases" +
", and that's why this test is skipped for Docker.")
}
}

// prepareContainerToAttach spins up a container (entrypoint = shell) with `-it` and detaches from it
// so that it can be re-attached to later.
func prepareContainerToAttach(base *testutil.Base, containerName string) {
opts := []func(*testutil.Cmd){
testutil.WithStdin(testutil.NewDelayOnceReader(bytes.NewReader(
[]byte{16, 17}, // ctrl+p,ctrl+q, see https://www.physics.udel.edu/~watson/scen103/ascii.html
))),
}
// unbuffer(1) emulates tty, which is required by `nerdctl run -t`.
// unbuffer(1) can be installed with `apt-get install expect`.
//
// "-p" is needed because we need unbuffer to read from stdin, and from [1]:
// "Normally, unbuffer does not read from stdin. This simplifies use of unbuffer in some situations.
// To use unbuffer in a pipeline, use the -p flag."
//
// [1] https://linux.die.net/man/1/unbuffer
base.CmdWithHelper([]string{"unbuffer", "-p"}, "run", "-it", "--name", containerName, testutil.CommonImage).
CmdOption(opts...).AssertOutContains("read detach keys")
container := base.InspectContainer(containerName)
assert.Equal(base.T, container.State.Running, true)
}
/*
Important notes:
- for both docker and nerdctl, you can run+detach of a container and exit 0, while the container would actually fail starting
- nerdctl (not docker): on run, detach will race anything on stdin before the detach sequence from reaching the container
- nerdctl AND docker: on attach ^
- exit code variants: https://github.com/containerd/nerdctl/issues/3571
*/

func TestAttach(t *testing.T) {
t.Parallel()
// In nerdctl the detach return code from the container after attach is 0, but in docker the return code is 1.
// This behaviour is reported in https://github.com/containerd/nerdctl/issues/3571
ex := 0
if nerdtest.IsDocker() {
ex = 1
}

t.Skip("This test is very unstable and currently skipped. See https://github.com/containerd/nerdctl/issues/3558")
testCase := nerdtest.Setup()

skipAttachForDocker(t)
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
helpers.Anyhow("rm", "-f", data.Identifier())
}

base := testutil.NewBase(t)
containerName := testutil.Identifier(t)
testCase.Setup = func(data test.Data, helpers test.Helpers) {
cmd := helpers.Command("run", "--rm", "-it", "--name", data.Identifier(), testutil.CommonImage)
cmd.WithPseudoTTY(func(f *os.File) error {
// ctrl+p and ctrl+q (see https://en.wikipedia.org/wiki/C0_and_C1_control_codes)
_, err := f.Write([]byte{16, 17})
return err
})

cmd.Run(&test.Expected{
ExitCode: 0,
Errors: []error{errors.New("read detach keys")},
Output: func(stdout string, info string, t *testing.T) {
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"))
},
})
}

defer base.Cmd("container", "rm", "-f", containerName).AssertOK()
prepareContainerToAttach(base, containerName)
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
// Run interactively and detach
cmd := helpers.Command("attach", data.Identifier())
cmd.WithPseudoTTY(func(f *os.File) error {
_, _ = f.WriteString("echo mark${NON}mark\n")
// Interestingly, and unlike with run, on attach, docker (like nerdctl) ALSO needs a pause so that the
// container can read stdin before we detach
time.Sleep(time.Second)
// ctrl+p and ctrl+q (see https://en.wikipedia.org/wiki/C0_and_C1_control_codes)
_, err := f.Write([]byte{16, 17})

return err
})

return cmd
}

opts := []func(*testutil.Cmd){
testutil.WithStdin(testutil.NewDelayOnceReader(strings.NewReader("expr 1 + 1\nexit\n"))),
testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected {
return &test.Expected{
ExitCode: ex,
Errors: []error{errors.New("read detach keys")},
Output: test.All(
test.Contains("markmark"),
func(stdout string, info string, t *testing.T) {
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"))
},
),
}
}
// `unbuffer -p` returns 0 even if the underlying nerdctl process returns a non-zero exit code,
// so the exit code cannot be easily tested here.
base.CmdWithHelper([]string{"unbuffer", "-p"}, "attach", containerName).CmdOption(opts...).AssertOutContains("2")
container := base.InspectContainer(containerName)
assert.Equal(base.T, container.State.Running, false)

testCase.Run(t)
}

func TestAttachDetachKeys(t *testing.T) {
t.Parallel()
// In nerdctl the detach return code from the container after attach is 0, but in docker the return code is 1.
// This behaviour is reported in https://github.com/containerd/nerdctl/issues/3571
ex := 0
if nerdtest.IsDocker() {
ex = 1
}

skipAttachForDocker(t)
testCase := nerdtest.Setup()

base := testutil.NewBase(t)
containerName := testutil.Identifier(t)
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
helpers.Anyhow("rm", "-f", data.Identifier())
}

defer base.Cmd("container", "rm", "-f", containerName).AssertOK()
prepareContainerToAttach(base, containerName)
testCase.Setup = func(data test.Data, helpers test.Helpers) {
cmd := helpers.Command("run", "--rm", "-it", "--detach-keys=ctrl-q", "--name", data.Identifier(), testutil.CommonImage)
cmd.WithPseudoTTY(func(f *os.File) error {
_, err := f.Write([]byte{17})
return err
})

cmd.Run(&test.Expected{
ExitCode: 0,
Errors: []error{errors.New("read detach keys")},
Output: func(stdout string, info string, t *testing.T) {
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"))
},
})
}

opts := []func(*testutil.Cmd){
testutil.WithStdin(testutil.NewDelayOnceReader(bytes.NewReader(
[]byte{1, 2}, // https://www.physics.udel.edu/~watson/scen103/ascii.html
))),
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
// Run interactively and detach
cmd := helpers.Command("attach", "--detach-keys=ctrl-a,ctrl-b", data.Identifier())
cmd.WithPseudoTTY(func(f *os.File) error {
_, _ = f.WriteString("echo mark${NON}mark\n")
// Interestingly, and unlike with run, on attach, docker (like nerdctl) ALSO needs a pause so that the
// container can read stdin before we detach
time.Sleep(time.Second)
// ctrl+a and ctrl+b (see https://en.wikipedia.org/wiki/C0_and_C1_control_codes)
_, err := f.Write([]byte{1, 2})

return err
})

return cmd
}
base.CmdWithHelper([]string{"unbuffer", "-p"}, "attach", "--detach-keys=ctrl-a,ctrl-b", containerName).
CmdOption(opts...).AssertOutContains("read detach keys")
container := base.InspectContainer(containerName)
assert.Equal(base.T, container.State.Running, true)

testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected {
return &test.Expected{
ExitCode: ex,
Errors: []error{errors.New("read detach keys")},
Output: test.All(
test.Contains("markmark"),
func(stdout string, info string, t *testing.T) {
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"))
},
),
}
}

testCase.Run(t)
}

// TestIssue3568 tests https://github.com/containerd/nerdctl/issues/3568
func TestDetachAttachKeysForAutoRemovedContainer(t *testing.T) {
func TestAttachForAutoRemovedContainer(t *testing.T) {
testCase := nerdtest.Setup()

testCase.SubTests = []*test.Case{
{
Description: "Issue #3568 - A container should be deleted when detaching and attaching a container started with the --rm option.",
// In nerdctl the detach return code from the container is 0, but in docker the return code is 1.
// This behaviour is reported in https://github.com/containerd/nerdctl/issues/3571 so this test is skipped for Docker.
Require: test.Require(
test.Not(nerdtest.Docker),
),
Setup: func(data test.Data, helpers test.Helpers) {
cmd := helpers.Command("run", "--rm", "-it", "--detach-keys=ctrl-a,ctrl-b", "--name", data.Identifier(), testutil.CommonImage)
// unbuffer(1) can be installed with `apt-get install expect`.
//
// "-p" is needed because we need unbuffer to read from stdin, and from [1]:
// "Normally, unbuffer does not read from stdin. This simplifies use of unbuffer in some situations.
// To use unbuffer in a pipeline, use the -p flag."
//
// [1] https://linux.die.net/man/1/unbuffer
cmd.WithWrapper("unbuffer", "-p")
cmd.WithStdin(testutil.NewDelayOnceReader(bytes.NewReader([]byte{1, 2}))) // https://www.physics.udel.edu/~watson/scen103/ascii.html
cmd.Run(&test.Expected{
ExitCode: 0,
})
},
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
cmd := helpers.Command("attach", data.Identifier())
cmd.WithWrapper("unbuffer", "-p")
cmd.WithStdin(testutil.NewDelayOnceReader(strings.NewReader("exit\n")))
return cmd
},
Cleanup: func(data test.Data, helpers test.Helpers) {
helpers.Anyhow("rm", "-f", data.Identifier())
},
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
return &test.Expected{
ExitCode: 0,
Errors: []error{},
Output: test.All(
func(stdout string, info string, t *testing.T) {
assert.Assert(t, !strings.Contains(helpers.Capture("ps", "-a"), data.Identifier()))
},
),
}
testCase.Description = "Issue #3568 - A container should be deleted when detaching and attaching a container started with the --rm option."

testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
helpers.Anyhow("rm", "-f", data.Identifier())
}

testCase.Setup = func(data test.Data, helpers test.Helpers) {
cmd := helpers.Command("run", "--rm", "-it", "--detach-keys=ctrl-a,ctrl-b", "--name", data.Identifier(), testutil.CommonImage)
cmd.WithPseudoTTY(func(f *os.File) error {
// ctrl+a and ctrl+b (see https://en.wikipedia.org/wiki/C0_and_C1_control_codes)
_, err := f.Write([]byte{1, 2})
return err
})

cmd.Run(&test.Expected{
ExitCode: 0,
Errors: []error{errors.New("read detach keys")},
Output: func(stdout string, info string, t *testing.T) {
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"), info)
},
},
})
}

testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
// Run interactively and detach
cmd := helpers.Command("attach", data.Identifier())
cmd.WithPseudoTTY(func(f *os.File) error {
_, err := f.WriteString("echo mark${NON}mark\nexit 42\n")
return err
})

return cmd
}

testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected {
return &test.Expected{
ExitCode: 42,
Output: test.All(
test.Contains("markmark"),
func(stdout string, info string, t *testing.T) {
assert.Assert(t, !strings.Contains(helpers.Capture("ps", "-a"), data.Identifier()))
},
),
}
}

testCase.Run(t)
Loading