moby/runconfig/hostconfig_test.go
Kir Kolyshkin afbeaf6f29 pkg/sysinfo: rm duplicates
The CPU CFS cgroup-aware scheduler is one single kernel feature, not
two, so it does not make sense to have two separate booleans
(CPUCfsQuota and CPUCfsPeriod). Merge these into CPUCfs.

Same for CPU realtime.

For compatibility reasons, /info stays the same for now.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2020-06-26 16:19:52 -07:00

293 lines
11 KiB
Go

// +build !windows
package runconfig // import "github.com/docker/docker/runconfig"
import (
"bytes"
"fmt"
"io/ioutil"
"testing"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/sysinfo"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestCgroupnsModeTest(t *testing.T) {
cgroupNsModes := map[container.CgroupnsMode][]bool{
// private, host, empty, valid
"": {false, false, true, true},
"something:weird": {false, false, false, false},
"host": {false, true, false, true},
"host:name": {false, false, false, false},
"private": {true, false, false, true},
"private:name": {false, false, false, false},
}
for cgroupNsMode, state := range cgroupNsModes {
if cgroupNsMode.IsPrivate() != state[0] {
t.Fatalf("CgroupnsMode.IsPrivate for %v should have been %v but was %v", cgroupNsMode, state[0], cgroupNsMode.IsPrivate())
}
if cgroupNsMode.IsHost() != state[1] {
t.Fatalf("CgroupnsMode.IsHost for %v should have been %v but was %v", cgroupNsMode, state[1], cgroupNsMode.IsHost())
}
if cgroupNsMode.IsEmpty() != state[2] {
t.Fatalf("CgroupnsMode.Valid for %v should have been %v but was %v", cgroupNsMode, state[2], cgroupNsMode.Valid())
}
if cgroupNsMode.Valid() != state[3] {
t.Fatalf("CgroupnsMode.Valid for %v should have been %v but was %v", cgroupNsMode, state[2], cgroupNsMode.Valid())
}
}
}
// TODO Windows: This will need addressing for a Windows daemon.
func TestNetworkModeTest(t *testing.T) {
networkModes := map[container.NetworkMode][]bool{
// private, bridge, host, container, none, default
"": {true, false, false, false, false, false},
"something:weird": {true, false, false, false, false, false},
"bridge": {true, true, false, false, false, false},
DefaultDaemonNetworkMode(): {true, true, false, false, false, false},
"host": {false, false, true, false, false, false},
"container:name": {false, false, false, true, false, false},
"none": {true, false, false, false, true, false},
"default": {true, false, false, false, false, true},
}
networkModeNames := map[container.NetworkMode]string{
"": "",
"something:weird": "something:weird",
"bridge": "bridge",
DefaultDaemonNetworkMode(): "bridge",
"host": "host",
"container:name": "container",
"none": "none",
"default": "default",
}
for networkMode, state := range networkModes {
if networkMode.IsPrivate() != state[0] {
t.Fatalf("NetworkMode.IsPrivate for %v should have been %v but was %v", networkMode, state[0], networkMode.IsPrivate())
}
if networkMode.IsBridge() != state[1] {
t.Fatalf("NetworkMode.IsBridge for %v should have been %v but was %v", networkMode, state[1], networkMode.IsBridge())
}
if networkMode.IsHost() != state[2] {
t.Fatalf("NetworkMode.IsHost for %v should have been %v but was %v", networkMode, state[2], networkMode.IsHost())
}
if networkMode.IsContainer() != state[3] {
t.Fatalf("NetworkMode.IsContainer for %v should have been %v but was %v", networkMode, state[3], networkMode.IsContainer())
}
if networkMode.IsNone() != state[4] {
t.Fatalf("NetworkMode.IsNone for %v should have been %v but was %v", networkMode, state[4], networkMode.IsNone())
}
if networkMode.IsDefault() != state[5] {
t.Fatalf("NetworkMode.IsDefault for %v should have been %v but was %v", networkMode, state[5], networkMode.IsDefault())
}
if networkMode.NetworkName() != networkModeNames[networkMode] {
t.Fatalf("Expected name %v, got %v", networkModeNames[networkMode], networkMode.NetworkName())
}
}
}
func TestIpcModeTest(t *testing.T) {
ipcModes := map[container.IpcMode]struct {
private bool
host bool
container bool
shareable bool
valid bool
ctrName string
}{
"": {valid: true},
"private": {private: true, valid: true},
"something:weird": {},
":weird": {},
"host": {host: true, valid: true},
"container": {},
"container:": {container: true, valid: true, ctrName: ""},
"container:name": {container: true, valid: true, ctrName: "name"},
"container:name1:name2": {container: true, valid: true, ctrName: "name1:name2"},
"shareable": {shareable: true, valid: true},
}
for ipcMode, state := range ipcModes {
assert.Check(t, is.Equal(state.private, ipcMode.IsPrivate()), "IpcMode.IsPrivate() parsing failed for %q", ipcMode)
assert.Check(t, is.Equal(state.host, ipcMode.IsHost()), "IpcMode.IsHost() parsing failed for %q", ipcMode)
assert.Check(t, is.Equal(state.container, ipcMode.IsContainer()), "IpcMode.IsContainer() parsing failed for %q", ipcMode)
assert.Check(t, is.Equal(state.shareable, ipcMode.IsShareable()), "IpcMode.IsShareable() parsing failed for %q", ipcMode)
assert.Check(t, is.Equal(state.valid, ipcMode.Valid()), "IpcMode.Valid() parsing failed for %q", ipcMode)
assert.Check(t, is.Equal(state.ctrName, ipcMode.Container()), "IpcMode.Container() parsing failed for %q", ipcMode)
}
}
func TestUTSModeTest(t *testing.T) {
utsModes := map[container.UTSMode][]bool{
// private, host, valid
"": {true, false, true},
"something:weird": {true, false, false},
"host": {false, true, true},
"host:name": {true, false, true},
}
for utsMode, state := range utsModes {
if utsMode.IsPrivate() != state[0] {
t.Fatalf("UtsMode.IsPrivate for %v should have been %v but was %v", utsMode, state[0], utsMode.IsPrivate())
}
if utsMode.IsHost() != state[1] {
t.Fatalf("UtsMode.IsHost for %v should have been %v but was %v", utsMode, state[1], utsMode.IsHost())
}
if utsMode.Valid() != state[2] {
t.Fatalf("UtsMode.Valid for %v should have been %v but was %v", utsMode, state[2], utsMode.Valid())
}
}
}
func TestUsernsModeTest(t *testing.T) {
usrensMode := map[container.UsernsMode][]bool{
// private, host, valid
"": {true, false, true},
"something:weird": {true, false, false},
"host": {false, true, true},
"host:name": {true, false, true},
}
for usernsMode, state := range usrensMode {
if usernsMode.IsPrivate() != state[0] {
t.Fatalf("UsernsMode.IsPrivate for %v should have been %v but was %v", usernsMode, state[0], usernsMode.IsPrivate())
}
if usernsMode.IsHost() != state[1] {
t.Fatalf("UsernsMode.IsHost for %v should have been %v but was %v", usernsMode, state[1], usernsMode.IsHost())
}
if usernsMode.Valid() != state[2] {
t.Fatalf("UsernsMode.Valid for %v should have been %v but was %v", usernsMode, state[2], usernsMode.Valid())
}
}
}
func TestPidModeTest(t *testing.T) {
pidModes := map[container.PidMode][]bool{
// private, host, valid
"": {true, false, true},
"something:weird": {true, false, false},
"host": {false, true, true},
"host:name": {true, false, true},
}
for pidMode, state := range pidModes {
if pidMode.IsPrivate() != state[0] {
t.Fatalf("PidMode.IsPrivate for %v should have been %v but was %v", pidMode, state[0], pidMode.IsPrivate())
}
if pidMode.IsHost() != state[1] {
t.Fatalf("PidMode.IsHost for %v should have been %v but was %v", pidMode, state[1], pidMode.IsHost())
}
if pidMode.Valid() != state[2] {
t.Fatalf("PidMode.Valid for %v should have been %v but was %v", pidMode, state[2], pidMode.Valid())
}
}
}
func TestRestartPolicy(t *testing.T) {
restartPolicies := map[container.RestartPolicy][]bool{
// none, always, failure
{}: {true, false, false},
{Name: "something", MaximumRetryCount: 0}: {false, false, false},
{Name: "no", MaximumRetryCount: 0}: {true, false, false},
{Name: "always", MaximumRetryCount: 0}: {false, true, false},
{Name: "on-failure", MaximumRetryCount: 0}: {false, false, true},
}
for restartPolicy, state := range restartPolicies {
if restartPolicy.IsNone() != state[0] {
t.Fatalf("RestartPolicy.IsNone for %v should have been %v but was %v", restartPolicy, state[0], restartPolicy.IsNone())
}
if restartPolicy.IsAlways() != state[1] {
t.Fatalf("RestartPolicy.IsAlways for %v should have been %v but was %v", restartPolicy, state[1], restartPolicy.IsAlways())
}
if restartPolicy.IsOnFailure() != state[2] {
t.Fatalf("RestartPolicy.IsOnFailure for %v should have been %v but was %v", restartPolicy, state[2], restartPolicy.IsOnFailure())
}
}
}
func TestDecodeHostConfig(t *testing.T) {
fixtures := []struct {
file string
}{
{"fixtures/unix/container_hostconfig_1_14.json"},
{"fixtures/unix/container_hostconfig_1_19.json"},
}
for _, f := range fixtures {
b, err := ioutil.ReadFile(f.file)
if err != nil {
t.Fatal(err)
}
c, err := decodeHostConfig(bytes.NewReader(b))
if err != nil {
t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err))
}
assert.Check(t, !c.Privileged)
if l := len(c.Binds); l != 1 {
t.Fatalf("Expected 1 bind, found %d\n", l)
}
if len(c.CapAdd) != 1 && c.CapAdd[0] != "NET_ADMIN" {
t.Fatalf("Expected CapAdd NET_ADMIN, got %v", c.CapAdd)
}
if len(c.CapDrop) != 1 && c.CapDrop[0] != "NET_ADMIN" {
t.Fatalf("Expected CapDrop NET_ADMIN, got %v", c.CapDrop)
}
}
}
func TestValidateResources(t *testing.T) {
type resourceTest struct {
ConfigCPURealtimePeriod int64
ConfigCPURealtimeRuntime int64
SysInfoCPURealtime bool
ErrorExpected bool
FailureMsg string
}
tests := []resourceTest{
{
ConfigCPURealtimePeriod: 1000,
ConfigCPURealtimeRuntime: 1000,
SysInfoCPURealtime: true,
ErrorExpected: false,
FailureMsg: "Expected valid configuration",
},
{
ConfigCPURealtimePeriod: 5000,
ConfigCPURealtimeRuntime: 5000,
SysInfoCPURealtime: false,
ErrorExpected: true,
FailureMsg: "Expected failure when cpu-rt-period is set but kernel doesn't support it",
},
{
ConfigCPURealtimePeriod: 5000,
ConfigCPURealtimeRuntime: 5000,
SysInfoCPURealtime: false,
ErrorExpected: true,
FailureMsg: "Expected failure when cpu-rt-runtime is set but kernel doesn't support it",
},
{
ConfigCPURealtimePeriod: 5000,
ConfigCPURealtimeRuntime: 10000,
SysInfoCPURealtime: true,
ErrorExpected: true,
FailureMsg: "Expected failure when cpu-rt-runtime is greater than cpu-rt-period",
},
}
for _, rt := range tests {
var hc container.HostConfig
hc.Resources.CPURealtimePeriod = rt.ConfigCPURealtimePeriod
hc.Resources.CPURealtimeRuntime = rt.ConfigCPURealtimeRuntime
var si sysinfo.SysInfo
si.CPURealtime = rt.SysInfoCPURealtime
if err := validateResources(&hc, &si); (err != nil) != rt.ErrorExpected {
t.Fatal(rt.FailureMsg, err)
}
}
}