Skip to content

Commit 7ecd71e

Browse files
committed
Add support for remaining blkio settings
Also adds HostConfig inspect tests and skip unsupported docker tests Signed-off-by: Swagat Bora <[email protected]>
1 parent 2feac61 commit 7ecd71e

13 files changed

+825
-60
lines changed

cmd/nerdctl/container/container_create.go

+26-3
Original file line numberDiff line numberDiff line change
@@ -199,19 +199,42 @@ func createOptions(cmd *cobra.Command) (types.ContainerCreateOptions, error) {
199199
if err != nil {
200200
return opt, err
201201
}
202+
opt.Cgroupns, err = cmd.Flags().GetString("cgroupns")
203+
if err != nil {
204+
return opt, err
205+
}
206+
opt.CgroupParent, err = cmd.Flags().GetString("cgroup-parent")
207+
if err != nil {
208+
return opt, err
209+
}
210+
opt.Device, err = cmd.Flags().GetStringSlice("device")
211+
if err != nil {
212+
return opt, err
213+
}
214+
// #endregion
215+
216+
// #region for blkio flags
202217
opt.BlkioWeight, err = cmd.Flags().GetUint16("blkio-weight")
203218
if err != nil {
204219
return opt, err
205220
}
206-
opt.Cgroupns, err = cmd.Flags().GetString("cgroupns")
221+
opt.BlkioWeightDevice, err = cmd.Flags().GetStringArray("blkio-weight-device")
207222
if err != nil {
208223
return opt, err
209224
}
210-
opt.CgroupParent, err = cmd.Flags().GetString("cgroup-parent")
225+
opt.BlkioDeviceReadBps, err = cmd.Flags().GetStringArray("device-read-bps")
211226
if err != nil {
212227
return opt, err
213228
}
214-
opt.Device, err = cmd.Flags().GetStringSlice("device")
229+
opt.BlkioDeviceWriteBps, err = cmd.Flags().GetStringArray("device-write-bps")
230+
if err != nil {
231+
return opt, err
232+
}
233+
opt.BlkioDeviceReadIOps, err = cmd.Flags().GetStringArray("device-read-iops")
234+
if err != nil {
235+
return opt, err
236+
}
237+
opt.BlkioDeviceWriteIOps, err = cmd.Flags().GetStringArray("device-write-iops")
215238
if err != nil {
216239
return opt, err
217240
}

cmd/nerdctl/container/container_inspect_linux_test.go

+58-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package container
1919
import (
2020
"fmt"
2121
"os"
22+
"os/exec"
2223
"slices"
2324
"strings"
2425
"testing"
@@ -246,7 +247,6 @@ func TestContainerInspectHostConfig(t *testing.T) {
246247
base.Cmd("run", "-d", "--name", testContainer,
247248
"--cpuset-cpus", "0-1",
248249
"--cpuset-mems", "0",
249-
"--blkio-weight", "500",
250250
"--cpu-shares", "1024",
251251
"--cpu-quota", "100000",
252252
"--group-add", "1000",
@@ -266,7 +266,6 @@ func TestContainerInspectHostConfig(t *testing.T) {
266266

267267
assert.Equal(t, "0-1", inspect.HostConfig.CPUSetCPUs)
268268
assert.Equal(t, "0", inspect.HostConfig.CPUSetMems)
269-
assert.Equal(t, uint16(500), inspect.HostConfig.BlkioWeight)
270269
assert.Equal(t, uint64(1024), inspect.HostConfig.CPUShares)
271270
assert.Equal(t, int64(100000), inspect.HostConfig.CPUQuota)
272271
assert.Assert(t, slices.Contains(inspect.HostConfig.GroupAdd, "1000"), "Expected '1000' to be in GroupAdd")
@@ -311,6 +310,11 @@ func TestContainerInspectHostConfigDefaults(t *testing.T) {
311310
assert.Equal(t, "", inspect.HostConfig.CPUSetCPUs)
312311
assert.Equal(t, "", inspect.HostConfig.CPUSetMems)
313312
assert.Equal(t, uint16(0), inspect.HostConfig.BlkioWeight)
313+
assert.Equal(t, 0, len(inspect.HostConfig.BlkioWeightDevice))
314+
assert.Equal(t, 0, len(inspect.HostConfig.BlkioDeviceReadBps))
315+
assert.Equal(t, 0, len(inspect.HostConfig.BlkioDeviceReadIOps))
316+
assert.Equal(t, 0, len(inspect.HostConfig.BlkioDeviceWriteBps))
317+
assert.Equal(t, 0, len(inspect.HostConfig.BlkioDeviceWriteIOps))
314318
assert.Equal(t, uint64(0), inspect.HostConfig.CPUShares)
315319
assert.Equal(t, int64(0), inspect.HostConfig.CPUQuota)
316320
assert.Equal(t, hc.GroupAddSize, len(inspect.HostConfig.GroupAdd))
@@ -456,6 +460,58 @@ func TestContainerInspectDevices(t *testing.T) {
456460
assert.DeepEqual(t, expectedDevices, inspect.HostConfig.Devices)
457461
}
458462

463+
func TestContainerInspectBlkioSettings(t *testing.T) {
464+
testutil.DockerIncompatible(t)
465+
testContainer := testutil.Identifier(t)
466+
// Some of the blkio settings are not supported in cgroup v1.
467+
// So skip this test if running on cgroup v1
468+
if infoutil.CgroupsVersion() == "1" {
469+
t.Skip("test skipped for rootless containers or if running with cgroup v1")
470+
}
471+
472+
if rootlessutil.IsRootless() {
473+
t.Skip("test requires root privilege to create a dummy device")
474+
}
475+
476+
devPath := "/dev/dummy-zero"
477+
// a dummy zero device: mknod /dev/dummy-zero c 1 5
478+
helperCmd := exec.Command("mknod", []string{devPath, "c", "1", "5"}...)
479+
if out, err := helperCmd.CombinedOutput(); err != nil {
480+
err = fmt.Errorf("cannot create %q: %q: %w", devPath, string(out), err)
481+
t.Fatal(err)
482+
}
483+
484+
// ensure the file will be removed in case of failed in the test
485+
defer func() {
486+
exec.Command("rm", devPath).Run()
487+
}()
488+
489+
base := testutil.NewBase(t)
490+
defer base.Cmd("rm", "-f", testContainer).Run()
491+
492+
base.Cmd("run", "-d", "--name", testContainer,
493+
"--blkio-weight", "500",
494+
"--blkio-weight-device", "/dev/dummy-zero:500",
495+
"--device-read-bps", "/dev/dummy-zero:1048576",
496+
"--device-read-iops", "/dev/dummy-zero:1000",
497+
"--device-write-bps", "/dev/dummy-zero:2097152",
498+
"--device-write-iops", "/dev/dummy-zero:2000",
499+
testutil.AlpineImage, "sleep", "infinity").AssertOK()
500+
501+
inspect := base.InspectContainer(testContainer)
502+
assert.Equal(t, uint16(500), inspect.HostConfig.BlkioWeight)
503+
assert.Equal(t, 1, len(inspect.HostConfig.BlkioWeightDevice))
504+
assert.Equal(t, uint16(500), *inspect.HostConfig.BlkioWeightDevice[0].Weight)
505+
assert.Equal(t, 1, len(inspect.HostConfig.BlkioDeviceReadBps))
506+
assert.Equal(t, uint64(1048576), inspect.HostConfig.BlkioDeviceReadBps[0].Rate)
507+
assert.Equal(t, 1, len(inspect.HostConfig.BlkioDeviceWriteBps))
508+
assert.Equal(t, uint64(2097152), inspect.HostConfig.BlkioDeviceWriteBps[0].Rate)
509+
assert.Equal(t, 1, len(inspect.HostConfig.BlkioDeviceReadIOps))
510+
assert.Equal(t, uint64(1000), inspect.HostConfig.BlkioDeviceReadIOps[0].Rate)
511+
assert.Equal(t, 1, len(inspect.HostConfig.BlkioDeviceWriteIOps))
512+
assert.Equal(t, uint64(2000), inspect.HostConfig.BlkioDeviceWriteIOps[0].Rate)
513+
}
514+
459515
type hostConfigValues struct {
460516
Driver string
461517
ShmSize int64

cmd/nerdctl/container/container_run.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ func setCreateFlags(cmd *cobra.Command) {
155155
})
156156
cmd.Flags().Int64("pids-limit", -1, "Tune container pids limit (set -1 for unlimited)")
157157
cmd.Flags().StringSlice("cgroup-conf", nil, "Configure cgroup v2 (key=value)")
158-
cmd.Flags().Uint16("blkio-weight", 0, "Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0)")
159158
cmd.Flags().String("cgroupns", defaults.CgroupnsMode(), `Cgroup namespace to use, the default depends on the cgroup version ("host"|"private")`)
160159
cmd.Flags().String("cgroup-parent", "", "Optional parent cgroup for the container")
161160
cmd.RegisterFlagCompletionFunc("cgroupns", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
@@ -173,6 +172,15 @@ func setCreateFlags(cmd *cobra.Command) {
173172
cmd.Flags().String("rdt-class", "", "Name of the RDT class (or CLOS) to associate the container with")
174173
// #endregion
175174

175+
// #region blkio flags
176+
cmd.Flags().Uint16("blkio-weight", 0, "Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0)")
177+
cmd.Flags().StringArray("blkio-weight-device", []string{}, "Block IO weight (relative device weight) (default [])")
178+
cmd.Flags().StringArray("device-read-bps", []string{}, "Limit read rate (bytes per second) from a device (default [])")
179+
cmd.Flags().StringArray("device-read-iops", []string{}, "Limit read rate (IO per second) from a device (default [])")
180+
cmd.Flags().StringArray("device-write-bps", []string{}, "Limit write rate (bytes per second) to a device (default [])")
181+
cmd.Flags().StringArray("device-write-iops", []string{}, "Limit write rate (IO per second) to a device (default [])")
182+
// #endregion
183+
176184
// user flags
177185
cmd.Flags().StringP("user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
178186
cmd.Flags().String("umask", "", "Set the umask inside the container. Defaults to 0022")

cmd/nerdctl/container/container_run_cgroup_linux_test.go

+191
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import (
2121
"context"
2222
"fmt"
2323
"os"
24+
"os/exec"
2425
"path/filepath"
2526
"strconv"
27+
"strings"
2628
"testing"
2729

2830
"gotest.tools/v3/assert"
@@ -477,3 +479,192 @@ func TestRunBlkioWeightCgroupV2(t *testing.T) {
477479
base.Cmd("update", containerName, "--blkio-weight", "400").AssertOK()
478480
base.Cmd("exec", containerName, "cat", "io.bfq.weight").AssertOutExactly("default 400\n")
479481
}
482+
483+
func TestRunBlkioSettingCgroupV2(t *testing.T) {
484+
testCase := nerdtest.Setup()
485+
testCase.Require = nerdtest.Rootful
486+
487+
// Create dummy device path
488+
dummyDev := "/dev/dummy-zero"
489+
490+
testCase.Setup = func(data test.Data, helpers test.Helpers) {
491+
// Create dummy device
492+
helperCmd := exec.Command("mknod", dummyDev, "c", "1", "5")
493+
if out, err := helperCmd.CombinedOutput(); err != nil {
494+
t.Fatalf("cannot create %q: %q: %v", dummyDev, string(out), err)
495+
}
496+
}
497+
498+
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
499+
// Clean up the dummy device
500+
if err := exec.Command("rm", "-f", dummyDev).Run(); err != nil {
501+
t.Logf("failed to remove device %s: %v", dummyDev, err)
502+
}
503+
}
504+
505+
testCase.SubTests = []*test.Case{
506+
{
507+
Description: "blkio-weight",
508+
Require: nerdtest.CGroupV2,
509+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
510+
return helpers.Command("run", "-d", "--name", data.Identifier(),
511+
"--blkio-weight", "150",
512+
testutil.AlpineImage, "sleep", "infinity")
513+
},
514+
Cleanup: func(data test.Data, helpers test.Helpers) {
515+
helpers.Anyhow("rm", "-f", data.Identifier())
516+
},
517+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
518+
return &test.Expected{
519+
ExitCode: 0,
520+
Output: expect.All(
521+
func(stdout string, info string, t *testing.T) {
522+
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "{{.HostConfig.BlkioWeight}}", data.Identifier()), "150"))
523+
},
524+
),
525+
}
526+
},
527+
},
528+
{
529+
Description: "blkio-weight-device",
530+
Require: nerdtest.CGroupV2,
531+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
532+
return helpers.Command("run", "-d", "--name", data.Identifier(),
533+
"--blkio-weight-device", dummyDev+":100",
534+
testutil.AlpineImage, "sleep", "infinity")
535+
},
536+
Cleanup: func(data test.Data, helpers test.Helpers) {
537+
helpers.Anyhow("rm", "-f", data.Identifier())
538+
},
539+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
540+
return &test.Expected{
541+
ExitCode: 0,
542+
Output: expect.All(
543+
func(stdout string, info string, t *testing.T) {
544+
inspectOut := helpers.Capture("inspect", "--format", "{{range .HostConfig.BlkioWeightDevice}}{{.Weight}}{{end}}", data.Identifier())
545+
assert.Assert(t, strings.Contains(inspectOut, "100"))
546+
},
547+
),
548+
}
549+
},
550+
},
551+
{
552+
Description: "device-read-bps",
553+
Require: require.All(
554+
nerdtest.CGroupV2,
555+
// Docker cli (v26.1.3) available in github runners has a bug where some of the blkio options
556+
// do not work https://github.com/docker/cli/issues/5321. The fix has been merged to the latest releases
557+
// but not currently available in the v26 release.
558+
require.Not(nerdtest.Docker),
559+
),
560+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
561+
return helpers.Command("run", "-d", "--name", data.Identifier(),
562+
"--device-read-bps", dummyDev+":1048576",
563+
testutil.AlpineImage, "sleep", "infinity")
564+
},
565+
Cleanup: func(data test.Data, helpers test.Helpers) {
566+
helpers.Anyhow("rm", "-f", data.Identifier())
567+
},
568+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
569+
return &test.Expected{
570+
ExitCode: 0,
571+
Output: expect.All(
572+
func(stdout string, info string, t *testing.T) {
573+
inspectOut := helpers.Capture("inspect", "--format", "{{range .HostConfig.BlkioDeviceReadBps}}{{.Rate}}{{end}}", data.Identifier())
574+
assert.Assert(t, strings.Contains(inspectOut, "1048576"))
575+
},
576+
),
577+
}
578+
},
579+
},
580+
{
581+
Description: "device-write-bps",
582+
Require: require.All(
583+
nerdtest.CGroupV2,
584+
// Docker cli (v26.1.3) available in github runners has a bug where some of the blkio options
585+
// do not work https://github.com/docker/cli/issues/5321. The fix has been merged to the latest releases
586+
// but not currently available in the v26 release.
587+
require.Not(nerdtest.Docker),
588+
),
589+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
590+
return helpers.Command("run", "-d", "--name", data.Identifier(),
591+
"--device-write-bps", dummyDev+":2097152",
592+
testutil.AlpineImage, "sleep", "infinity")
593+
},
594+
Cleanup: func(data test.Data, helpers test.Helpers) {
595+
helpers.Anyhow("rm", "-f", data.Identifier())
596+
},
597+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
598+
return &test.Expected{
599+
ExitCode: 0,
600+
Output: expect.All(
601+
func(stdout string, info string, t *testing.T) {
602+
inspectOut := helpers.Capture("inspect", "--format", "{{range .HostConfig.BlkioDeviceWriteBps}}{{.Rate}}{{end}}", data.Identifier())
603+
assert.Assert(t, strings.Contains(inspectOut, "2097152"))
604+
},
605+
),
606+
}
607+
},
608+
},
609+
{
610+
Description: "device-read-iops",
611+
Require: require.All(
612+
nerdtest.CGroupV2,
613+
// Docker cli (v26.1.3) available in github runners has a bug where some of the blkio options
614+
// do not work https://github.com/docker/cli/issues/5321. The fix has been merged to the latest releases
615+
// but not currently available in the v26 release.
616+
require.Not(nerdtest.Docker),
617+
),
618+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
619+
return helpers.Command("run", "-d", "--name", data.Identifier(),
620+
"--device-read-iops", dummyDev+":1000",
621+
testutil.AlpineImage, "sleep", "infinity")
622+
},
623+
Cleanup: func(data test.Data, helpers test.Helpers) {
624+
helpers.Anyhow("rm", "-f", data.Identifier())
625+
},
626+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
627+
return &test.Expected{
628+
ExitCode: 0,
629+
Output: expect.All(
630+
func(stdout string, info string, t *testing.T) {
631+
inspectOut := helpers.Capture("inspect", "--format", "{{range .HostConfig.BlkioDeviceReadIOps}}{{.Rate}}{{end}}", data.Identifier())
632+
assert.Assert(t, strings.Contains(inspectOut, "1000"))
633+
},
634+
),
635+
}
636+
},
637+
},
638+
{
639+
Description: "device-write-iops",
640+
Require: require.All(
641+
nerdtest.CGroupV2,
642+
// Docker cli (v26.1.3) available in github runners has a bug where some of the blkio options
643+
// do not work https://github.com/docker/cli/issues/5321. The fix has been merged to the latest releases
644+
// but not currently available in the v26 release.
645+
require.Not(nerdtest.Docker),
646+
),
647+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
648+
return helpers.Command("run", "-d", "--name", data.Identifier(),
649+
"--device-write-iops", dummyDev+":2000",
650+
testutil.AlpineImage, "sleep", "infinity")
651+
},
652+
Cleanup: func(data test.Data, helpers test.Helpers) {
653+
helpers.Anyhow("rm", "-f", data.Identifier())
654+
},
655+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
656+
return &test.Expected{
657+
ExitCode: 0,
658+
Output: expect.All(
659+
func(stdout string, info string, t *testing.T) {
660+
inspectOut := helpers.Capture("inspect", "--format", "{{range .HostConfig.BlkioDeviceWriteIOps}}{{.Rate}}{{end}}", data.Identifier())
661+
assert.Assert(t, strings.Contains(inspectOut, "2000"))
662+
},
663+
),
664+
}
665+
},
666+
},
667+
}
668+
669+
testCase.Run(t)
670+
}

docs/command-reference.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,11 @@ Resource flags:
213213
- :whale: `--pids-limit`: Tune container pids limit
214214
- :nerd_face: `--cgroup-conf`: Configure cgroup v2 (key=value)
215215
- :whale: `--blkio-weight`: Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0)
216+
- :whale: `--blkio-weight-device`: Block IO weight (relative device weight)
217+
- :whale: `--device-read-bps`: Limit read rate (bytes per second) from a device
218+
- :whale: `--device-read-iops`: Limit read rate (IO per second) from a device
219+
- :whale: `--device-write-bps`: Limit write rate (bytes per second) to a device
220+
- :whale: `--device-write-iops`: Limit write rate (IO per second) to a device
216221
- :whale: `--cgroupns=(host|private)`: Cgroup namespace to use
217222
- Default: "private" on cgroup v2 hosts, "host" on cgroup v1 hosts
218223
- :whale: `--cgroup-parent`: Optional parent cgroup for the container
@@ -414,7 +419,7 @@ IPFS flags:
414419
- :nerd_face: `--ipfs-address`: Multiaddr of IPFS API (default uses `$IPFS_PATH` env variable if defined or local directory `~/.ipfs`)
415420

416421
Unimplemented `docker run` flags:
417-
`--blkio-weight-device`, `--cpu-rt-*`, `--device-*`,
422+
`--cpu-rt-*`, `--device-cgroup-rule`,
418423
`--disable-content-trust`, `--expose`, `--health-*`, `--isolation`, `--no-healthcheck`,
419424
`--link*`, `--publish-all`, `--storage-opt`,
420425
`--userns`, `--volume-driver`

0 commit comments

Comments
 (0)