diff --git a/drivers/docker/config.go b/drivers/docker/config.go index 34eb46d60..3b31f0da6 100644 --- a/drivers/docker/config.go +++ b/drivers/docker/config.go @@ -276,7 +276,10 @@ var ( "cap_drop": hclspec.NewAttr("cap_drop", "list(string)", false), "command": hclspec.NewAttr("command", "string", false), "cpu_hard_limit": hclspec.NewAttr("cpu_hard_limit", "bool", false), - "cpu_cfs_period": hclspec.NewAttr("cpu_cfs_period", "number", false), + "cpu_cfs_period": hclspec.NewDefault( + hclspec.NewAttr("cpu_cfs_period", "number", false), + hclspec.NewLiteral(`100000`), + ), "devices": hclspec.NewBlockList("devices", hclspec.NewObject(map[string]*hclspec.Spec{ "host_path": hclspec.NewAttr("host_path", "string", false), "container_path": hclspec.NewAttr("container_path", "string", false), diff --git a/drivers/docker/config_test.go b/drivers/docker/config_test.go index 452844371..57f6725d8 100644 --- a/drivers/docker/config_test.go +++ b/drivers/docker/config_test.go @@ -20,9 +20,10 @@ func TestConfig_ParseHCL(t *testing.T) { image = "redis:3.2" }`, &TaskConfig{ - Image: "redis:3.2", - Devices: []DockerDevice{}, - Mounts: []DockerMount{}, + Image: "redis:3.2", + Devices: []DockerDevice{}, + Mounts: []DockerMount{}, + CPUCFSPeriod: 100000, }, }, } @@ -51,36 +52,40 @@ func TestConfig_ParseJSON(t *testing.T) { name: "nil values for blocks are safe", input: `{"Config": {"image": "bash:3", "mounts": null}}`, expected: TaskConfig{ - Image: "bash:3", - Mounts: []DockerMount{}, - Devices: []DockerDevice{}, + Image: "bash:3", + Mounts: []DockerMount{}, + Devices: []DockerDevice{}, + CPUCFSPeriod: 100000, }, }, { name: "nil values for 'volumes' field are safe", input: `{"Config": {"image": "bash:3", "volumes": null}}`, expected: TaskConfig{ - Image: "bash:3", - Mounts: []DockerMount{}, - Devices: []DockerDevice{}, + Image: "bash:3", + Mounts: []DockerMount{}, + Devices: []DockerDevice{}, + CPUCFSPeriod: 100000, }, }, { name: "nil values for 'args' field are safe", input: `{"Config": {"image": "bash:3", "args": null}}`, expected: TaskConfig{ - Image: "bash:3", - Mounts: []DockerMount{}, - Devices: []DockerDevice{}, + Image: "bash:3", + Mounts: []DockerMount{}, + Devices: []DockerDevice{}, + CPUCFSPeriod: 100000, }, }, { name: "nil values for string fields are safe", input: `{"Config": {"image": "bash:3", "command": null}}`, expected: TaskConfig{ - Image: "bash:3", - Mounts: []DockerMount{}, - Devices: []DockerDevice{}, + Image: "bash:3", + Mounts: []DockerMount{}, + Devices: []DockerDevice{}, + CPUCFSPeriod: 100000, }, }, } diff --git a/drivers/docker/driver_test.go b/drivers/docker/driver_test.go index ebcc80ea2..0aa8ccb2b 100644 --- a/drivers/docker/driver_test.go +++ b/drivers/docker/driver_test.go @@ -19,6 +19,8 @@ import ( "github.com/hashicorp/nomad/client/taskenv" "github.com/hashicorp/nomad/client/testutil" "github.com/hashicorp/nomad/devices/gpu/nvidia" + "github.com/hashicorp/nomad/helper/pluginutils/hclspecutils" + "github.com/hashicorp/nomad/helper/pluginutils/hclutils" "github.com/hashicorp/nomad/helper/pluginutils/loader" "github.com/hashicorp/nomad/helper/testlog" "github.com/hashicorp/nomad/helper/uuid" @@ -103,6 +105,7 @@ func dockerTask(t *testing.T) (*drivers.TaskConfig, *TaskConfig, []int) { LinuxResources: &drivers.LinuxResources{ CPUShares: 512, MemoryLimitBytes: 256 * 1024 * 1024, + PercentTicks: float64(512) / float64(4096), }, }, } @@ -2498,3 +2501,30 @@ func TestDockerDriver_CreationIdempotent(t *testing.T) { require.NoError(t, err) }) } + +// TestDockerDriver_CreateContainerConfig_CPUHardLimit asserts that a default +// CPU quota and period are set when cpu_hard_limit = true. +func TestDockerDriver_CreateContainerConfig_CPUHardLimit(t *testing.T) { + t.Parallel() + + task, _, _ := dockerTask(t) + + dh := dockerDriverHarness(t, nil) + driver := dh.Impl().(*Driver) + schema, _ := driver.TaskConfigSchema() + spec, _ := hclspecutils.Convert(schema) + + val, _, _ := hclutils.ParseHclInterface(map[string]interface{}{ + "image": "foo/bar", + "cpu_hard_limit": true, + }, spec, nil) + + require.NoError(t, task.EncodeDriverConfig(val)) + cfg := &TaskConfig{} + require.NoError(t, task.DecodeDriverConfig(cfg)) + c, err := driver.createContainerConfig(task, cfg, "org/repo:0.1") + require.NoError(t, err) + + require.NotZero(t, c.HostConfig.CPUQuota) + require.NotZero(t, c.HostConfig.CPUPeriod) +} diff --git a/helper/pluginutils/hclutils/util_test.go b/helper/pluginutils/hclutils/util_test.go index 4faae2738..42a2a3f5d 100644 --- a/helper/pluginutils/hclutils/util_test.go +++ b/helper/pluginutils/hclutils/util_test.go @@ -42,9 +42,10 @@ func TestParseHclInterface_Hcl(t *testing.T) { }`), spec: dockerDecSpec, expected: &docker.TaskConfig{ - Image: "redis:3.2", - Devices: []docker.DockerDevice{}, - Mounts: []docker.DockerMount{}, + Image: "redis:3.2", + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -58,9 +59,10 @@ func TestParseHclInterface_Hcl(t *testing.T) { }`), spec: dockerDecSpec, expected: &docker.TaskConfig{ - Image: "redis:3.2", - Devices: []docker.DockerDevice{}, - Mounts: []docker.DockerMount{}, + Image: "redis:3.2", + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -73,10 +75,11 @@ func TestParseHclInterface_Hcl(t *testing.T) { }`), spec: dockerDecSpec, expected: &docker.TaskConfig{ - Image: "redis:3.2", - PidsLimit: 2, - Devices: []docker.DockerDevice{}, - Mounts: []docker.DockerMount{}, + Image: "redis:3.2", + PidsLimit: 2, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -91,10 +94,11 @@ func TestParseHclInterface_Hcl(t *testing.T) { }`), spec: dockerDecSpec, expected: &docker.TaskConfig{ - Image: "redis:3.2", - PidsLimit: 2, - Devices: []docker.DockerDevice{}, - Mounts: []docker.DockerMount{}, + Image: "redis:3.2", + PidsLimit: 2, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -107,10 +111,11 @@ func TestParseHclInterface_Hcl(t *testing.T) { }`), spec: dockerDecSpec, expected: &docker.TaskConfig{ - Image: "redis:3.2", - PidsLimit: 4, - Devices: []docker.DockerDevice{}, - Mounts: []docker.DockerMount{}, + Image: "redis:3.2", + PidsLimit: 4, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -125,10 +130,11 @@ func TestParseHclInterface_Hcl(t *testing.T) { }`), spec: dockerDecSpec, expected: &docker.TaskConfig{ - Image: "redis:3.2", - PidsLimit: 4, - Devices: []docker.DockerDevice{}, - Mounts: []docker.DockerMount{}, + Image: "redis:3.2", + PidsLimit: 4, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -141,10 +147,11 @@ func TestParseHclInterface_Hcl(t *testing.T) { }`), spec: dockerDecSpec, expected: &docker.TaskConfig{ - Image: "redis:3.2", - Args: []string{"foo", "bar"}, - Devices: []docker.DockerDevice{}, - Mounts: []docker.DockerMount{}, + Image: "redis:3.2", + Args: []string{"foo", "bar"}, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -159,10 +166,11 @@ func TestParseHclInterface_Hcl(t *testing.T) { }`), spec: dockerDecSpec, expected: &docker.TaskConfig{ - Image: "redis:3.2", - Args: []string{"foo", "bar"}, - Devices: []docker.DockerDevice{}, - Mounts: []docker.DockerMount{}, + Image: "redis:3.2", + Args: []string{"foo", "bar"}, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -177,11 +185,12 @@ func TestParseHclInterface_Hcl(t *testing.T) { spec: dockerDecSpec, vars: vars, expected: &docker.TaskConfig{ - Image: "redis:3.2", - Args: []string{"world", "2"}, - PidsLimit: 4, - Devices: []docker.DockerDevice{}, - Mounts: []docker.DockerMount{}, + Image: "redis:3.2", + Args: []string{"world", "2"}, + PidsLimit: 4, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -196,10 +205,11 @@ func TestParseHclInterface_Hcl(t *testing.T) { }`), spec: dockerDecSpec, expected: &docker.TaskConfig{ - Image: "redis:3.2", - Args: []string{"foo", "bar"}, - Devices: []docker.DockerDevice{}, - Mounts: []docker.DockerMount{}, + Image: "redis:3.2", + Args: []string{"foo", "bar"}, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -220,8 +230,9 @@ func TestParseHclInterface_Hcl(t *testing.T) { "foo": 1234, "bar": 5678, }, - Devices: []docker.DockerDevice{}, - Mounts: []docker.DockerMount{}, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -244,8 +255,9 @@ func TestParseHclInterface_Hcl(t *testing.T) { "foo": 1234, "bar": 5678, }, - Devices: []docker.DockerDevice{}, - Mounts: []docker.DockerMount{}, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -280,7 +292,8 @@ func TestParseHclInterface_Hcl(t *testing.T) { ContainerPath: "/dev/xvdd", }, }, - Mounts: []docker.DockerMount{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -309,8 +322,9 @@ func TestParseHclInterface_Hcl(t *testing.T) { "tag": "driver-test", }, }, - Devices: []docker.DockerDevice{}, - Mounts: []docker.DockerMount{}, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, }, @@ -347,7 +361,8 @@ func TestParseHclInterface_Hcl(t *testing.T) { ContainerPath: "/dev/xvdd", }, }, - Mounts: []docker.DockerMount{}, + Mounts: []docker.DockerMount{}, + CPUCFSPeriod: 100000, }, expectedType: &docker.TaskConfig{}, },