daemon_unix_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. // +build !windows
  2. package daemon // import "github.com/docker/docker/daemon"
  3. import (
  4. "errors"
  5. "io/ioutil"
  6. "os"
  7. "testing"
  8. containertypes "github.com/docker/docker/api/types/container"
  9. "github.com/docker/docker/container"
  10. "github.com/docker/docker/daemon/config"
  11. "github.com/docker/docker/pkg/sysinfo"
  12. "gotest.tools/assert"
  13. is "gotest.tools/assert/cmp"
  14. )
  15. type fakeContainerGetter struct {
  16. containers map[string]*container.Container
  17. }
  18. func (f *fakeContainerGetter) GetContainer(cid string) (*container.Container, error) {
  19. container, ok := f.containers[cid]
  20. if !ok {
  21. return nil, errors.New("container not found")
  22. }
  23. return container, nil
  24. }
  25. // Unix test as uses settings which are not available on Windows
  26. func TestAdjustSharedNamespaceContainerName(t *testing.T) {
  27. fakeID := "abcdef1234567890"
  28. hostConfig := &containertypes.HostConfig{
  29. IpcMode: containertypes.IpcMode("container:base"),
  30. PidMode: containertypes.PidMode("container:base"),
  31. NetworkMode: containertypes.NetworkMode("container:base"),
  32. }
  33. containerStore := &fakeContainerGetter{}
  34. containerStore.containers = make(map[string]*container.Container)
  35. containerStore.containers["base"] = &container.Container{
  36. ID: fakeID,
  37. }
  38. adaptSharedNamespaceContainer(containerStore, hostConfig)
  39. if hostConfig.IpcMode != containertypes.IpcMode("container:"+fakeID) {
  40. t.Errorf("Expected IpcMode to be container:%s", fakeID)
  41. }
  42. if hostConfig.PidMode != containertypes.PidMode("container:"+fakeID) {
  43. t.Errorf("Expected PidMode to be container:%s", fakeID)
  44. }
  45. if hostConfig.NetworkMode != containertypes.NetworkMode("container:"+fakeID) {
  46. t.Errorf("Expected NetworkMode to be container:%s", fakeID)
  47. }
  48. }
  49. // Unix test as uses settings which are not available on Windows
  50. func TestAdjustCPUShares(t *testing.T) {
  51. tmp, err := ioutil.TempDir("", "docker-daemon-unix-test-")
  52. if err != nil {
  53. t.Fatal(err)
  54. }
  55. defer os.RemoveAll(tmp)
  56. daemon := &Daemon{
  57. repository: tmp,
  58. root: tmp,
  59. }
  60. hostConfig := &containertypes.HostConfig{
  61. Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
  62. }
  63. daemon.adaptContainerSettings(hostConfig, true)
  64. if hostConfig.CPUShares != linuxMinCPUShares {
  65. t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares)
  66. }
  67. hostConfig.CPUShares = linuxMaxCPUShares + 1
  68. daemon.adaptContainerSettings(hostConfig, true)
  69. if hostConfig.CPUShares != linuxMaxCPUShares {
  70. t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares)
  71. }
  72. hostConfig.CPUShares = 0
  73. daemon.adaptContainerSettings(hostConfig, true)
  74. if hostConfig.CPUShares != 0 {
  75. t.Error("Expected CPUShares to be unchanged")
  76. }
  77. hostConfig.CPUShares = 1024
  78. daemon.adaptContainerSettings(hostConfig, true)
  79. if hostConfig.CPUShares != 1024 {
  80. t.Error("Expected CPUShares to be unchanged")
  81. }
  82. }
  83. // Unix test as uses settings which are not available on Windows
  84. func TestAdjustCPUSharesNoAdjustment(t *testing.T) {
  85. tmp, err := ioutil.TempDir("", "docker-daemon-unix-test-")
  86. if err != nil {
  87. t.Fatal(err)
  88. }
  89. defer os.RemoveAll(tmp)
  90. daemon := &Daemon{
  91. repository: tmp,
  92. root: tmp,
  93. }
  94. hostConfig := &containertypes.HostConfig{
  95. Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
  96. }
  97. daemon.adaptContainerSettings(hostConfig, false)
  98. if hostConfig.CPUShares != linuxMinCPUShares-1 {
  99. t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares-1)
  100. }
  101. hostConfig.CPUShares = linuxMaxCPUShares + 1
  102. daemon.adaptContainerSettings(hostConfig, false)
  103. if hostConfig.CPUShares != linuxMaxCPUShares+1 {
  104. t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares+1)
  105. }
  106. hostConfig.CPUShares = 0
  107. daemon.adaptContainerSettings(hostConfig, false)
  108. if hostConfig.CPUShares != 0 {
  109. t.Error("Expected CPUShares to be unchanged")
  110. }
  111. hostConfig.CPUShares = 1024
  112. daemon.adaptContainerSettings(hostConfig, false)
  113. if hostConfig.CPUShares != 1024 {
  114. t.Error("Expected CPUShares to be unchanged")
  115. }
  116. }
  117. // Unix test as uses settings which are not available on Windows
  118. func TestParseSecurityOptWithDeprecatedColon(t *testing.T) {
  119. container := &container.Container{}
  120. config := &containertypes.HostConfig{}
  121. // test apparmor
  122. config.SecurityOpt = []string{"apparmor=test_profile"}
  123. if err := parseSecurityOpt(container, config); err != nil {
  124. t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
  125. }
  126. if container.AppArmorProfile != "test_profile" {
  127. t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", container.AppArmorProfile)
  128. }
  129. // test seccomp
  130. sp := "/path/to/seccomp_test.json"
  131. config.SecurityOpt = []string{"seccomp=" + sp}
  132. if err := parseSecurityOpt(container, config); err != nil {
  133. t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
  134. }
  135. if container.SeccompProfile != sp {
  136. t.Fatalf("Unexpected AppArmorProfile, expected: %q, got %q", sp, container.SeccompProfile)
  137. }
  138. // test valid label
  139. config.SecurityOpt = []string{"label=user:USER"}
  140. if err := parseSecurityOpt(container, config); err != nil {
  141. t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
  142. }
  143. // test invalid label
  144. config.SecurityOpt = []string{"label"}
  145. if err := parseSecurityOpt(container, config); err == nil {
  146. t.Fatal("Expected parseSecurityOpt error, got nil")
  147. }
  148. // test invalid opt
  149. config.SecurityOpt = []string{"test"}
  150. if err := parseSecurityOpt(container, config); err == nil {
  151. t.Fatal("Expected parseSecurityOpt error, got nil")
  152. }
  153. }
  154. func TestParseSecurityOpt(t *testing.T) {
  155. container := &container.Container{}
  156. config := &containertypes.HostConfig{}
  157. // test apparmor
  158. config.SecurityOpt = []string{"apparmor=test_profile"}
  159. if err := parseSecurityOpt(container, config); err != nil {
  160. t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
  161. }
  162. if container.AppArmorProfile != "test_profile" {
  163. t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", container.AppArmorProfile)
  164. }
  165. // test seccomp
  166. sp := "/path/to/seccomp_test.json"
  167. config.SecurityOpt = []string{"seccomp=" + sp}
  168. if err := parseSecurityOpt(container, config); err != nil {
  169. t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
  170. }
  171. if container.SeccompProfile != sp {
  172. t.Fatalf("Unexpected SeccompProfile, expected: %q, got %q", sp, container.SeccompProfile)
  173. }
  174. // test valid label
  175. config.SecurityOpt = []string{"label=user:USER"}
  176. if err := parseSecurityOpt(container, config); err != nil {
  177. t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
  178. }
  179. // test invalid label
  180. config.SecurityOpt = []string{"label"}
  181. if err := parseSecurityOpt(container, config); err == nil {
  182. t.Fatal("Expected parseSecurityOpt error, got nil")
  183. }
  184. // test invalid opt
  185. config.SecurityOpt = []string{"test"}
  186. if err := parseSecurityOpt(container, config); err == nil {
  187. t.Fatal("Expected parseSecurityOpt error, got nil")
  188. }
  189. }
  190. func TestParseNNPSecurityOptions(t *testing.T) {
  191. daemon := &Daemon{
  192. configStore: &config.Config{NoNewPrivileges: true},
  193. }
  194. container := &container.Container{}
  195. config := &containertypes.HostConfig{}
  196. // test NNP when "daemon:true" and "no-new-privileges=false""
  197. config.SecurityOpt = []string{"no-new-privileges=false"}
  198. if err := daemon.parseSecurityOpt(container, config); err != nil {
  199. t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
  200. }
  201. if container.NoNewPrivileges {
  202. t.Fatalf("container.NoNewPrivileges should be FALSE: %v", container.NoNewPrivileges)
  203. }
  204. // test NNP when "daemon:false" and "no-new-privileges=true""
  205. daemon.configStore.NoNewPrivileges = false
  206. config.SecurityOpt = []string{"no-new-privileges=true"}
  207. if err := daemon.parseSecurityOpt(container, config); err != nil {
  208. t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
  209. }
  210. if !container.NoNewPrivileges {
  211. t.Fatalf("container.NoNewPrivileges should be TRUE: %v", container.NoNewPrivileges)
  212. }
  213. }
  214. func TestNetworkOptions(t *testing.T) {
  215. daemon := &Daemon{}
  216. dconfigCorrect := &config.Config{
  217. CommonConfig: config.CommonConfig{
  218. ClusterStore: "consul://localhost:8500",
  219. ClusterAdvertise: "192.168.0.1:8000",
  220. },
  221. }
  222. if _, err := daemon.networkOptions(dconfigCorrect, nil, nil); err != nil {
  223. t.Fatalf("Expect networkOptions success, got error: %v", err)
  224. }
  225. dconfigWrong := &config.Config{
  226. CommonConfig: config.CommonConfig{
  227. ClusterStore: "consul://localhost:8500://test://bbb",
  228. },
  229. }
  230. if _, err := daemon.networkOptions(dconfigWrong, nil, nil); err == nil {
  231. t.Fatal("Expected networkOptions error, got nil")
  232. }
  233. }
  234. func TestVerifyPlatformContainerResources(t *testing.T) {
  235. t.Parallel()
  236. var (
  237. no = false
  238. yes = true
  239. )
  240. withMemoryLimit := func(si *sysinfo.SysInfo) {
  241. si.MemoryLimit = true
  242. }
  243. withSwapLimit := func(si *sysinfo.SysInfo) {
  244. si.SwapLimit = true
  245. }
  246. withOomKillDisable := func(si *sysinfo.SysInfo) {
  247. si.OomKillDisable = true
  248. }
  249. tests := []struct {
  250. name string
  251. resources containertypes.Resources
  252. sysInfo sysinfo.SysInfo
  253. update bool
  254. expectedWarnings []string
  255. }{
  256. {
  257. name: "no-oom-kill-disable",
  258. resources: containertypes.Resources{},
  259. sysInfo: sysInfo(t, withMemoryLimit),
  260. expectedWarnings: []string{},
  261. },
  262. {
  263. name: "oom-kill-disable-disabled",
  264. resources: containertypes.Resources{
  265. OomKillDisable: &no,
  266. },
  267. sysInfo: sysInfo(t, withMemoryLimit),
  268. expectedWarnings: []string{},
  269. },
  270. {
  271. name: "oom-kill-disable-not-supported",
  272. resources: containertypes.Resources{
  273. OomKillDisable: &yes,
  274. },
  275. sysInfo: sysInfo(t, withMemoryLimit),
  276. expectedWarnings: []string{
  277. "Your kernel does not support OomKillDisable. OomKillDisable discarded.",
  278. },
  279. },
  280. {
  281. name: "oom-kill-disable-without-memory-constraints",
  282. resources: containertypes.Resources{
  283. OomKillDisable: &yes,
  284. Memory: 0,
  285. },
  286. sysInfo: sysInfo(t, withMemoryLimit, withOomKillDisable, withSwapLimit),
  287. expectedWarnings: []string{
  288. "OOM killer is disabled for the container, but no memory limit is set, this can result in the system running out of resources.",
  289. },
  290. },
  291. {
  292. name: "oom-kill-disable-with-memory-constraints-but-no-memory-limit-support",
  293. resources: containertypes.Resources{
  294. OomKillDisable: &yes,
  295. Memory: linuxMinMemory,
  296. },
  297. sysInfo: sysInfo(t, withOomKillDisable),
  298. expectedWarnings: []string{
  299. "Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.",
  300. "OOM killer is disabled for the container, but no memory limit is set, this can result in the system running out of resources.",
  301. },
  302. },
  303. {
  304. name: "oom-kill-disable-with-memory-constraints",
  305. resources: containertypes.Resources{
  306. OomKillDisable: &yes,
  307. Memory: linuxMinMemory,
  308. },
  309. sysInfo: sysInfo(t, withMemoryLimit, withOomKillDisable, withSwapLimit),
  310. expectedWarnings: []string{},
  311. },
  312. }
  313. for _, tc := range tests {
  314. t.Run(tc.name, func(t *testing.T) {
  315. t.Parallel()
  316. warnings, err := verifyPlatformContainerResources(&tc.resources, &tc.sysInfo, tc.update)
  317. assert.NilError(t, err)
  318. for _, w := range tc.expectedWarnings {
  319. assert.Assert(t, is.Contains(warnings, w))
  320. }
  321. })
  322. }
  323. }
  324. func sysInfo(t *testing.T, opts ...func(*sysinfo.SysInfo)) sysinfo.SysInfo {
  325. t.Helper()
  326. si := sysinfo.SysInfo{}
  327. for _, opt := range opts {
  328. opt(&si)
  329. }
  330. if si.OomKillDisable {
  331. t.Log(t.Name(), "OOM disable supported")
  332. }
  333. return si
  334. }