diff --git a/Dockerfile.d/test-integration-rootless.sh b/Dockerfile.d/test-integration-rootless.sh index bc8ee25cb..cb656c1b5 100755 --- a/Dockerfile.d/test-integration-rootless.sh +++ b/Dockerfile.d/test-integration-rootless.sh @@ -17,7 +17,7 @@ set -eux -o pipefail if [[ "$(id -u)" = "0" ]]; then if [ -e /sys/kernel/security/apparmor/profiles ]; then - # Load the "prefix-default" profile for TestRunApparmor + # Load the default profile for TestRunApparmor nerdctl apparmor load fi diff --git a/cmd/nerdctl/builder/builder_build_test.go b/cmd/nerdctl/builder/builder_build_test.go index b19740513..302a5afe4 100644 --- a/cmd/nerdctl/builder/builder_build_test.go +++ b/cmd/nerdctl/builder/builder_build_test.go @@ -205,7 +205,6 @@ func TestBuildFromContainerd(t *testing.T) { Setup: func(data test.Data, helpers test.Helpers) { helpers.Ensure("pull", "--quiet", testutil.CommonImage) helpers.Ensure("tag", testutil.CommonImage, data.Identifier("first")) - dockerfile := fmt.Sprintf(`FROM %s RUN echo hello2 > /hello2 CMD ["cat", "/hello2"]`, data.Identifier("first")) diff --git a/cmd/nerdctl/compose/compose_config_test.go b/cmd/nerdctl/compose/compose_config_test.go index 99a433c23..6e8b3dd2d 100644 --- a/cmd/nerdctl/compose/compose_config_test.go +++ b/cmd/nerdctl/compose/compose_config_test.go @@ -70,7 +70,7 @@ services: defer comp.CleanUp() // `--hash=*` is broken in Docker Compose v2.23.0: https://github.com/docker/compose/issues/11145 - if base.Target == testutil.Nerdctl { + if base.Target == testutil.Nerdishctl { base.ComposeCmd("-f", comp.YAMLFullPath(), "config", "--hash=*").AssertOutContains("hello1") } diff --git a/cmd/nerdctl/image/image_remove_linux_test.go b/cmd/nerdctl/image/image_remove_linux_test.go new file mode 100644 index 000000000..709633393 --- /dev/null +++ b/cmd/nerdctl/image/image_remove_linux_test.go @@ -0,0 +1,107 @@ +/* + 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 image + +import ( + "testing" + + "github.com/containerd/nerdctl/v2/pkg/testutil" +) + +func TestRemoveImage(t *testing.T) { + base := testutil.NewBase(t) + tID := testutil.Identifier(t) + base.Cmd("image", "prune", "--force", "--all").AssertOK() + + // ignore error + base.Cmd("rmi", "-f", tID).AssertOK() + + base.Cmd("run", "--name", tID, testutil.CommonImage).AssertOK() + defer base.Cmd("rm", "-f", tID).AssertOK() + + base.Cmd("rmi", testutil.CommonImage).AssertFail() + defer base.Cmd("rmi", "-f", testutil.CommonImage).Run() + base.Cmd("rmi", "-f", testutil.CommonImage).AssertOK() + + base.Cmd("images").AssertOutNotContains(testutil.ImageRepo(testutil.CommonImage)) +} + +func TestRemoveRunningImage(t *testing.T) { + // If an image is associated with a running/paused containers, `docker rmi -f imageName` + // untags `imageName` (left a `` image) without deletion; `docker rmi -rf imageID` fails. + // In both cases, `rmi -f` will fail. + testutil.DockerIncompatible(t) + base := testutil.NewBase(t) + tID := testutil.Identifier(t) + + base.Cmd("run", "--name", tID, "-d", testutil.CommonImage, "sleep", "infinity").AssertOK() + defer base.Cmd("rm", "-f", tID).AssertOK() + + base.Cmd("rmi", testutil.CommonImage).AssertFail() + base.Cmd("rmi", "-f", testutil.CommonImage).AssertFail() + base.Cmd("images").AssertOutContains(testutil.ImageRepo(testutil.CommonImage)) + + base.Cmd("kill", tID).AssertOK() + base.Cmd("rmi", testutil.CommonImage).AssertFail() + base.Cmd("rmi", "-f", testutil.CommonImage).AssertOK() + base.Cmd("images").AssertOutNotContains(testutil.ImageRepo(testutil.CommonImage)) +} + +func TestRemovePausedImage(t *testing.T) { + // If an image is associated with a running/paused containers, `docker rmi -f imageName` + // untags `imageName` (left a `` image) without deletion; `docker rmi -rf imageID` fails. + // In both cases, `rmi -f` will fail. + testutil.DockerIncompatible(t) + base := testutil.NewBase(t) + switch base.Info().CgroupDriver { + case "none", "": + t.Skip("requires cgroup (for pausing)") + } + tID := testutil.Identifier(t) + + base.Cmd("run", "--name", tID, "-d", testutil.CommonImage, "sleep", "infinity").AssertOK() + base.Cmd("pause", tID).AssertOK() + defer base.Cmd("rm", "-f", tID).AssertOK() + + base.Cmd("rmi", testutil.CommonImage).AssertFail() + base.Cmd("rmi", "-f", testutil.CommonImage).AssertFail() + base.Cmd("images").AssertOutContains(testutil.ImageRepo(testutil.CommonImage)) + + base.Cmd("kill", tID).AssertOK() + base.Cmd("rmi", testutil.CommonImage).AssertFail() + base.Cmd("rmi", "-f", testutil.CommonImage).AssertOK() + base.Cmd("images").AssertOutNotContains(testutil.ImageRepo(testutil.CommonImage)) +} + +func TestRemoveImageWithCreatedContainer(t *testing.T) { + base := testutil.NewBase(t) + tID := testutil.Identifier(t) + + base.Cmd("pull", testutil.AlpineImage).AssertOK() + base.Cmd("pull", testutil.NginxAlpineImage).AssertOK() + + base.Cmd("create", "--name", tID, testutil.AlpineImage, "sleep", "infinity").AssertOK() + defer base.Cmd("rm", "-f", tID).AssertOK() + + base.Cmd("rmi", testutil.AlpineImage).AssertFail() + base.Cmd("rmi", "-f", testutil.AlpineImage).AssertOK() + base.Cmd("images").AssertOutNotContains(testutil.ImageRepo(testutil.AlpineImage)) + + // a created container with removed image doesn't impact other `rmi` command + base.Cmd("rmi", "-f", testutil.NginxAlpineImage).AssertOK() + base.Cmd("images").AssertOutNotContains(testutil.ImageRepo(testutil.NginxAlpineImage)) +} diff --git a/cmd/nerdctl/main_linux_test.go b/cmd/nerdctl/main_linux_test.go new file mode 100644 index 000000000..bcfccd894 --- /dev/null +++ b/cmd/nerdctl/main_linux_test.go @@ -0,0 +1,36 @@ +/* + 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 main + +import ( + "testing" + + "github.com/containerd/nerdctl/v2/pkg/testutil" +) + +// TestIssue108 tests https://github.com/containerd/nerdctl/issues/108 +// ("`run --net=host -it` fails while `run -it --net=host` works") +func TestIssue108(t *testing.T) { + base := testutil.NewBase(t) + // unbuffer(1) emulates tty, which is required by `run -t`. + // unbuffer(1) can be installed with `apt-get install expect`. + unbuffer := []string{"unbuffer"} + base.CmdWithHelper(unbuffer, "run", "-it", "--rm", "--net=host", testutil.AlpineImage, + "echo", "this was always working").AssertOK() + base.CmdWithHelper(unbuffer, "run", "--rm", "--net=host", "-it", testutil.AlpineImage, + "echo", "this was not working due to issue #108").AssertOK() +} diff --git a/pkg/clientutil/client.go b/pkg/clientutil/client.go index a52f84c8e..3db1af2a4 100644 --- a/pkg/clientutil/client.go +++ b/pkg/clientutil/client.go @@ -66,9 +66,10 @@ func NewClientWithPlatform(ctx context.Context, namespace, address, platform str return containerd.NewClient(ctx, namespace, address, clientOpts...) } -// DataStore returns a string like "/var/lib/nerdctl/1935db59". +// DataStore returns a string like "/var/lib/version.RootName/1935db59". // "1935db9" is from `$(echo -n "/run/containerd/containerd.sock" | sha256sum | cut -c1-8)` -// on Windows it will return "%PROGRAMFILES%/nerdctl/1935db59" +// on Windows it will return "%PROGRAMFILES%/version.RootName/1935db59" +// where `version.RootName` is defined at build time func DataStore(dataRoot, address string) (string, error) { if err := os.MkdirAll(dataRoot, 0o700); err != nil { return "", err diff --git a/pkg/imgutil/commit/commit.go b/pkg/imgutil/commit/commit.go index 19ee02854..2bfbd699e 100644 --- a/pkg/imgutil/commit/commit.go +++ b/pkg/imgutil/commit/commit.go @@ -49,6 +49,7 @@ import ( "github.com/containerd/nerdctl/v2/pkg/containerutil" imgutil "github.com/containerd/nerdctl/v2/pkg/imgutil" "github.com/containerd/nerdctl/v2/pkg/labels" + "github.com/containerd/nerdctl/v2/pkg/version" ) type Changes struct { @@ -106,7 +107,7 @@ func Commit(ctx context.Context, client *containerd.Client, container containerd // to commit container created by moby. baseImgWithoutPlatform, err := client.ImageService().Get(ctx, info.Image) if err != nil { - return emptyDigest, fmt.Errorf("container %q lacks image (wasn't created by nerdctl?): %w", id, err) + return emptyDigest, fmt.Errorf("container %q lacks image (wasn't created by %s?): %w", id, version.RootName, err) } platformLabel := info.Labels[labels.Platform] if platformLabel == "" { diff --git a/pkg/imgutil/dockerconfigresolver/credentialsstore_test.go b/pkg/imgutil/dockerconfigresolver/credentialsstore_test.go index f15b5e81f..f2e990dca 100644 --- a/pkg/imgutil/dockerconfigresolver/credentialsstore_test.go +++ b/pkg/imgutil/dockerconfigresolver/credentialsstore_test.go @@ -25,6 +25,8 @@ import ( "testing" "gotest.tools/v3/assert" + + "github.com/containerd/nerdctl/v2/pkg/version" ) func createTempDir(t *testing.T, mode os.FileMode) string { @@ -370,13 +372,13 @@ func TestWorkingCredentialsStore(t *testing.T) { t.Fatal(err) } - content := `{ + content := fmt.Sprintf(`{ "auths": { - "nerdctl-experimental://namespace.example:443/host/host.example:443/path": { + "%s-experimental://namespace.example:443/host/host.example:443/path": { "username": "username" } } - }` + }`, version.RootName) dir := writeContent(t, content) cs, err := NewCredentialsStore(dir) if err != nil { diff --git a/pkg/imgutil/dockerconfigresolver/registryurl_test.go b/pkg/imgutil/dockerconfigresolver/registryurl_test.go index b1f28b5a2..9d4b2707e 100644 --- a/pkg/imgutil/dockerconfigresolver/registryurl_test.go +++ b/pkg/imgutil/dockerconfigresolver/registryurl_test.go @@ -20,6 +20,8 @@ import ( "testing" "gotest.tools/v3/assert" + + "github.com/containerd/nerdctl/v2/pkg/version" ) func TestURLParsingAndID(t *testing.T) { @@ -149,9 +151,9 @@ func TestURLParsingAndID(t *testing.T) { }, { address: "https://registry-host.com/subpath/something?bar=bar&ns=registry-namespace.com&foo=foo", - identifier: "nerdctl-experimental://registry-namespace.com:443/host/registry-host.com:443/subpath/something", + identifier: version.RootName + "-experimental://registry-namespace.com:443/host/registry-host.com:443/subpath/something", allIDs: []string{ - "nerdctl-experimental://registry-namespace.com:443/host/registry-host.com:443/subpath/something", + version.RootName + "-experimental://registry-namespace.com:443/host/registry-host.com:443/subpath/something", }, }, { diff --git a/pkg/infoutil/infoutil_linux.go b/pkg/infoutil/infoutil_linux.go index 8e7e45df9..6b73f8607 100644 --- a/pkg/infoutil/infoutil_linux.go +++ b/pkg/infoutil/infoutil_linux.go @@ -29,6 +29,7 @@ import ( "github.com/containerd/nerdctl/v2/pkg/defaults" "github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat" "github.com/containerd/nerdctl/v2/pkg/rootlessutil" + "github.com/containerd/nerdctl/v2/pkg/version" ) const UnameO = "GNU/Linux" @@ -39,8 +40,8 @@ func fulfillSecurityOptions(info *dockercompat.Info) { if rootlessutil.IsRootless() && !apparmor.CanApplySpecificExistingProfile(defaults.AppArmorProfileName) { info.Warnings = append(info.Warnings, fmt.Sprintf(strings.TrimSpace(` WARNING: AppArmor profile %q is not loaded. - Use 'sudo nerdctl apparmor load' if you prefer to use AppArmor with rootless mode. - This warning is negligible if you do not intend to use AppArmor.`), defaults.AppArmorProfileName)) + Use 'sudo %s apparmor load' if you prefer to use AppArmor with rootless mode. + This warning is negligible if you do not intend to use AppArmor.`), defaults.AppArmorProfileName, version.RootName)) } } info.SecurityOptions = append(info.SecurityOptions, "name=seccomp,profile="+defaults.SeccompProfileName) diff --git a/pkg/inspecttypes/dockercompat/dockercompat.go b/pkg/inspecttypes/dockercompat/dockercompat.go index a7e818f8d..c297839b7 100644 --- a/pkg/inspecttypes/dockercompat/dockercompat.go +++ b/pkg/inspecttypes/dockercompat/dockercompat.go @@ -549,9 +549,9 @@ func NetworkFromNative(n *native.Network) (*Network, error) { return &res, nil } -func parseMounts(containersMounts string) ([]MountPoint, error) { +func parseMounts(mountList string) ([]MountPoint, error) { var mounts []MountPoint - err := json.Unmarshal([]byte(containersMounts), &mounts) + err := json.Unmarshal([]byte(mountList), &mounts) if err != nil { return nil, err } diff --git a/pkg/inspecttypes/dockercompat/info.go b/pkg/inspecttypes/dockercompat/info.go index bca4144cd..976d4f862 100644 --- a/pkg/inspecttypes/dockercompat/info.go +++ b/pkg/inspecttypes/dockercompat/info.go @@ -44,11 +44,13 @@ type Info struct { // Nfd is omitted because it does not make sense for us OomKillDisable bool // NGoroutines is omitted because it does not make sense for us - SystemTime string - LoggingDriver string - CgroupDriver cgroups.Manager - CgroupVersion string `json:",omitempty"` - // NEventsListener is omitted because it does not make sense for us + BridgeNfIptables bool + BridgeNfIP6tables bool `json:"BridgeNfIp6tables"` + SystemTime string + LoggingDriver string + CgroupDriver cgroups.Manager + CgroupVersion string `json:",omitempty"` + // NEventsListener is omitted because it does not make sense here KernelVersion string OperatingSystem string OSType string diff --git a/pkg/testutil/testutil.go b/pkg/testutil/testutil.go index 198d48364..b001bf2e1 100644 --- a/pkg/testutil/testutil.go +++ b/pkg/testutil/testutil.go @@ -540,8 +540,8 @@ func (c *Cmd) OutLines() []string { type Target = string const ( - Nerdctl = Target("nerdctl") Docker = Target("docker") + Nerdctl = Target("nerdctl") ) var (