Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HostConfig to nerdctl inspect response #3812

Merged
Merged
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
238 changes: 236 additions & 2 deletions cmd/nerdctl/container/container_inspect_linux_test.go
Original file line number Diff line number Diff line change
@@ -18,14 +18,18 @@ package container

import (
"fmt"
"os"
"slices"
"strings"
"testing"

"github.com/docker/go-connections/nat"
"gotest.tools/v3/assert"

"github.com/containerd/nerdctl/v2/pkg/infoutil"
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
"github.com/containerd/nerdctl/v2/pkg/labels"
"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
"github.com/containerd/nerdctl/v2/pkg/testutil"
)

@@ -68,13 +72,12 @@ func TestContainerInspectContainsMounts(t *testing.T) {
testutil.NginxAlpineImage).AssertOK()

inspect := base.InspectContainer(testContainer)

// convert array to map to get by key of Destination
actual := make(map[string]dockercompat.MountPoint)
for i := range inspect.Mounts {
actual[inspect.Mounts[i].Destination] = inspect.Mounts[i]
}

t.Logf("actual in TestContainerInspectContainsMounts: %+v", actual)
const localDriver = "local"

expected := []struct {
@@ -229,3 +232,234 @@ func TestContainerInspectState(t *testing.T) {
}

}

func TestContainerInspectHostConfig(t *testing.T) {
testContainer := testutil.Identifier(t)
if rootlessutil.IsRootless() && infoutil.CgroupsVersion() == "1" {
t.Skip("test skipped for rootless containers on cgroup v1")
}

base := testutil.NewBase(t)
defer base.Cmd("rm", "-f", testContainer).Run()

// Run a container with various HostConfig options
base.Cmd("run", "-d", "--name", testContainer,
"--cpuset-cpus", "0-1",
"--cpuset-mems", "0",
"--blkio-weight", "500",
"--cpu-shares", "1024",
"--cpu-quota", "100000",
"--group-add", "1000",
"--group-add", "2000",
"--add-host", "host1:10.0.0.1",
"--add-host", "host2:10.0.0.2",
"--ipc", "host",
"--memory", "512m",
"--read-only",
"--shm-size", "256m",
"--uts", "host",
"--sysctl", "net.core.somaxconn=1024",
"--runtime", "io.containerd.runc.v2",
testutil.AlpineImage, "sleep", "infinity").AssertOK()

inspect := base.InspectContainer(testContainer)

assert.Equal(t, "0-1", inspect.HostConfig.CPUSetCPUs)
assert.Equal(t, "0", inspect.HostConfig.CPUSetMems)
assert.Equal(t, uint16(500), inspect.HostConfig.BlkioWeight)
assert.Equal(t, uint64(1024), inspect.HostConfig.CPUShares)
assert.Equal(t, int64(100000), inspect.HostConfig.CPUQuota)
assert.Assert(t, slices.Contains(inspect.HostConfig.GroupAdd, "1000"), "Expected '1000' to be in GroupAdd")
assert.Assert(t, slices.Contains(inspect.HostConfig.GroupAdd, "2000"), "Expected '2000' to be in GroupAdd")
expectedExtraHosts := []string{"host1:10.0.0.1", "host2:10.0.0.2"}
assert.DeepEqual(t, expectedExtraHosts, inspect.HostConfig.ExtraHosts)
assert.Equal(t, "host", inspect.HostConfig.IpcMode)
assert.Equal(t, int64(536870912), inspect.HostConfig.Memory)
assert.Equal(t, int64(1073741824), inspect.HostConfig.MemorySwap)
assert.Equal(t, true, inspect.HostConfig.ReadonlyRootfs)
assert.Equal(t, "host", inspect.HostConfig.UTSMode)
assert.Equal(t, int64(268435456), inspect.HostConfig.ShmSize)
}

func TestContainerInspectHostConfigDefaults(t *testing.T) {
testContainer := testutil.Identifier(t)

base := testutil.NewBase(t)
defer base.Cmd("rm", "-f", testContainer).Run()

var hc hostConfigValues

// Hostconfig default values differ with Docker.
// This is because we directly retrieve the configured values instead of using preset defaults.
if testutil.GetTarget() == testutil.Docker {
hc.Driver = ""
hc.GroupAddSize = 0
hc.ShmSize = int64(67108864) // Docker default 64M
hc.Runtime = "runc"
} else {
hc.GroupAddSize = 10
hc.Driver = "json-file"
hc.ShmSize = int64(0)
hc.Runtime = "io.containerd.runc.v2"
}

// Run a container without specifying HostConfig options
base.Cmd("run", "-d", "--name", testContainer, testutil.AlpineImage, "sleep", "infinity").AssertOK()

inspect := base.InspectContainer(testContainer)
t.Logf("HostConfig in TestContainerInspectHostConfigDefaults: %+v", inspect.HostConfig)
assert.Equal(t, "", inspect.HostConfig.CPUSetCPUs)
assert.Equal(t, "", inspect.HostConfig.CPUSetMems)
assert.Equal(t, uint16(0), inspect.HostConfig.BlkioWeight)
assert.Equal(t, uint64(0), inspect.HostConfig.CPUShares)
assert.Equal(t, int64(0), inspect.HostConfig.CPUQuota)
assert.Equal(t, hc.GroupAddSize, len(inspect.HostConfig.GroupAdd))
assert.Equal(t, 0, len(inspect.HostConfig.ExtraHosts))
assert.Equal(t, "private", inspect.HostConfig.IpcMode)
assert.Equal(t, hc.Driver, inspect.HostConfig.LogConfig.Driver)
assert.Equal(t, int64(0), inspect.HostConfig.Memory)
assert.Equal(t, int64(0), inspect.HostConfig.MemorySwap)
assert.Equal(t, bool(false), inspect.HostConfig.OomKillDisable)
assert.Equal(t, bool(false), inspect.HostConfig.ReadonlyRootfs)
assert.Equal(t, "", inspect.HostConfig.UTSMode)
assert.Equal(t, hc.ShmSize, inspect.HostConfig.ShmSize)
assert.Equal(t, hc.Runtime, inspect.HostConfig.Runtime)
assert.Equal(t, 0, len(inspect.HostConfig.Sysctls))
assert.Equal(t, 0, len(inspect.HostConfig.Devices))
}

func TestContainerInspectHostConfigDNS(t *testing.T) {
testContainer := testutil.Identifier(t)

base := testutil.NewBase(t)
defer base.Cmd("rm", "-f", testContainer).Run()

// Run a container with DNS options
base.Cmd("run", "-d", "--name", testContainer,
"--dns", "8.8.8.8",
"--dns", "1.1.1.1",
"--dns-search", "example.com",
"--dns-search", "test.local",
"--dns-option", "ndots:5",
"--dns-option", "timeout:3",
testutil.AlpineImage, "sleep", "infinity").AssertOK()

inspect := base.InspectContainer(testContainer)

// Check DNS servers
expectedDNSServers := []string{"8.8.8.8", "1.1.1.1"}
assert.DeepEqual(t, expectedDNSServers, inspect.HostConfig.DNS)

// Check DNS search domains
expectedDNSSearch := []string{"example.com", "test.local"}
assert.DeepEqual(t, expectedDNSSearch, inspect.HostConfig.DNSSearch)

// Check DNS options
expectedDNSOptions := []string{"ndots:5", "timeout:3"}
assert.DeepEqual(t, expectedDNSOptions, inspect.HostConfig.DNSOptions)
}

func TestContainerInspectHostConfigDNSDefaults(t *testing.T) {
testContainer := testutil.Identifier(t)

base := testutil.NewBase(t)
defer base.Cmd("rm", "-f", testContainer).Run()

// Run a container without specifying DNS options
base.Cmd("run", "-d", "--name", testContainer, testutil.AlpineImage, "sleep", "infinity").AssertOK()

inspect := base.InspectContainer(testContainer)

// Check that DNS settings are empty by default
assert.Equal(t, 0, len(inspect.HostConfig.DNS))
assert.Equal(t, 0, len(inspect.HostConfig.DNSSearch))
assert.Equal(t, 0, len(inspect.HostConfig.DNSOptions))
}

func TestContainerInspectHostConfigPID(t *testing.T) {
testContainer1 := testutil.Identifier(t) + "-container1"
testContainer2 := testutil.Identifier(t) + "-container2"

base := testutil.NewBase(t)
defer base.Cmd("rm", "-f", testContainer1, testContainer2).Run()

// Run the first container
base.Cmd("run", "-d", "--name", testContainer1, testutil.AlpineImage, "sleep", "infinity").AssertOK()

containerID1 := strings.TrimSpace(base.Cmd("inspect", "-f", "{{.Id}}", testContainer1).Out())

var hc hostConfigValues

if testutil.GetTarget() == testutil.Docker {
hc.PidMode = "container:" + containerID1
} else {
hc.PidMode = containerID1
}

base.Cmd("run", "-d", "--name", testContainer2,
"--pid", fmt.Sprintf("container:%s", testContainer1),
testutil.AlpineImage, "sleep", "infinity").AssertOK()

inspect := base.InspectContainer(testContainer2)

assert.Equal(t, hc.PidMode, inspect.HostConfig.PidMode)

}

func TestContainerInspectHostConfigPIDDefaults(t *testing.T) {
testContainer := testutil.Identifier(t)

base := testutil.NewBase(t)
defer base.Cmd("rm", "-f", testContainer).Run()

base.Cmd("run", "-d", "--name", testContainer, testutil.AlpineImage, "sleep", "infinity").AssertOK()

inspect := base.InspectContainer(testContainer)

assert.Equal(t, "", inspect.HostConfig.PidMode)
}

func TestContainerInspectDevices(t *testing.T) {
testContainer := testutil.Identifier(t)

base := testutil.NewBase(t)
defer base.Cmd("rm", "-f", testContainer).Run()

if rootlessutil.IsRootless() && infoutil.CgroupsVersion() == "1" {
t.Skip("test skipped for rootless containers on cgroup v1")
}

// Create a temporary directory
dir, err := os.MkdirTemp(t.TempDir(), "device-dir")
if err != nil {
t.Fatal(err)
}

if testutil.GetTarget() == testutil.Docker {
dir = "/dev/zero"
}

// Run the container with the directory mapped as a device
base.Cmd("run", "-d", "--name", testContainer,
"--device", dir+":/dev/xvda",
testutil.AlpineImage, "sleep", "infinity").AssertOK()

inspect := base.InspectContainer(testContainer)

expectedDevices := []dockercompat.DeviceMapping{
{
PathOnHost: dir,
PathInContainer: "/dev/xvda",
CgroupPermissions: "rwm",
},
}
assert.DeepEqual(t, expectedDevices, inspect.HostConfig.Devices)
}

type hostConfigValues struct {
Driver string
ShmSize int64
PidMode string
GroupAddSize int
Runtime string
}
Loading