docker_api_containers_test.go 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618
  1. package main
  2. import (
  3. "archive/tar"
  4. "bytes"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "net/http/httputil"
  10. "net/url"
  11. "os"
  12. "regexp"
  13. "strconv"
  14. "strings"
  15. "time"
  16. "github.com/docker/docker/pkg/integration"
  17. "github.com/docker/docker/pkg/integration/checker"
  18. "github.com/docker/docker/pkg/stringid"
  19. "github.com/docker/engine-api/types"
  20. containertypes "github.com/docker/engine-api/types/container"
  21. networktypes "github.com/docker/engine-api/types/network"
  22. "github.com/go-check/check"
  23. )
  24. func (s *DockerSuite) TestContainerApiGetAll(c *check.C) {
  25. startCount, err := getContainerCount()
  26. c.Assert(err, checker.IsNil, check.Commentf("Cannot query container count"))
  27. name := "getall"
  28. dockerCmd(c, "run", "--name", name, "busybox", "true")
  29. status, body, err := sockRequest("GET", "/containers/json?all=1", nil)
  30. c.Assert(err, checker.IsNil)
  31. c.Assert(status, checker.Equals, http.StatusOK)
  32. var inspectJSON []struct {
  33. Names []string
  34. }
  35. err = json.Unmarshal(body, &inspectJSON)
  36. c.Assert(err, checker.IsNil, check.Commentf("unable to unmarshal response body"))
  37. c.Assert(inspectJSON, checker.HasLen, startCount+1)
  38. actual := inspectJSON[0].Names[0]
  39. c.Assert(actual, checker.Equals, "/"+name)
  40. }
  41. // regression test for empty json field being omitted #13691
  42. func (s *DockerSuite) TestContainerApiGetJSONNoFieldsOmitted(c *check.C) {
  43. dockerCmd(c, "run", "busybox", "true")
  44. status, body, err := sockRequest("GET", "/containers/json?all=1", nil)
  45. c.Assert(err, checker.IsNil)
  46. c.Assert(status, checker.Equals, http.StatusOK)
  47. // empty Labels field triggered this bug, make sense to check for everything
  48. // cause even Ports for instance can trigger this bug
  49. // better safe than sorry..
  50. fields := []string{
  51. "Id",
  52. "Names",
  53. "Image",
  54. "Command",
  55. "Created",
  56. "Ports",
  57. "Labels",
  58. "Status",
  59. "NetworkSettings",
  60. }
  61. // decoding into types.Container do not work since it eventually unmarshal
  62. // and empty field to an empty go map, so we just check for a string
  63. for _, f := range fields {
  64. if !strings.Contains(string(body), f) {
  65. c.Fatalf("Field %s is missing and it shouldn't", f)
  66. }
  67. }
  68. }
  69. type containerPs struct {
  70. Names []string
  71. Ports []map[string]interface{}
  72. }
  73. // regression test for non-empty fields from #13901
  74. func (s *DockerSuite) TestContainerApiPsOmitFields(c *check.C) {
  75. // Problematic for Windows porting due to networking not yet being passed back
  76. testRequires(c, DaemonIsLinux)
  77. name := "pstest"
  78. port := 80
  79. runSleepingContainer(c, "--name", name, "--expose", strconv.Itoa(port))
  80. status, body, err := sockRequest("GET", "/containers/json?all=1", nil)
  81. c.Assert(err, checker.IsNil)
  82. c.Assert(status, checker.Equals, http.StatusOK)
  83. var resp []containerPs
  84. err = json.Unmarshal(body, &resp)
  85. c.Assert(err, checker.IsNil)
  86. var foundContainer *containerPs
  87. for _, container := range resp {
  88. for _, testName := range container.Names {
  89. if "/"+name == testName {
  90. foundContainer = &container
  91. break
  92. }
  93. }
  94. }
  95. c.Assert(foundContainer.Ports, checker.HasLen, 1)
  96. c.Assert(foundContainer.Ports[0]["PrivatePort"], checker.Equals, float64(port))
  97. _, ok := foundContainer.Ports[0]["PublicPort"]
  98. c.Assert(ok, checker.Not(checker.Equals), true)
  99. _, ok = foundContainer.Ports[0]["IP"]
  100. c.Assert(ok, checker.Not(checker.Equals), true)
  101. }
  102. func (s *DockerSuite) TestContainerApiGetExport(c *check.C) {
  103. // TODO: Investigate why this fails on Windows to Windows CI
  104. testRequires(c, DaemonIsLinux)
  105. name := "exportcontainer"
  106. dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test")
  107. status, body, err := sockRequest("GET", "/containers/"+name+"/export", nil)
  108. c.Assert(err, checker.IsNil)
  109. c.Assert(status, checker.Equals, http.StatusOK)
  110. found := false
  111. for tarReader := tar.NewReader(bytes.NewReader(body)); ; {
  112. h, err := tarReader.Next()
  113. if err != nil && err == io.EOF {
  114. break
  115. }
  116. if h.Name == "test" {
  117. found = true
  118. break
  119. }
  120. }
  121. c.Assert(found, checker.True, check.Commentf("The created test file has not been found in the exported image"))
  122. }
  123. func (s *DockerSuite) TestContainerApiGetChanges(c *check.C) {
  124. // Not supported on Windows as Windows does not support docker diff (/containers/name/changes)
  125. testRequires(c, DaemonIsLinux)
  126. name := "changescontainer"
  127. dockerCmd(c, "run", "--name", name, "busybox", "rm", "/etc/passwd")
  128. status, body, err := sockRequest("GET", "/containers/"+name+"/changes", nil)
  129. c.Assert(err, checker.IsNil)
  130. c.Assert(status, checker.Equals, http.StatusOK)
  131. changes := []struct {
  132. Kind int
  133. Path string
  134. }{}
  135. c.Assert(json.Unmarshal(body, &changes), checker.IsNil, check.Commentf("unable to unmarshal response body"))
  136. // Check the changelog for removal of /etc/passwd
  137. success := false
  138. for _, elem := range changes {
  139. if elem.Path == "/etc/passwd" && elem.Kind == 2 {
  140. success = true
  141. }
  142. }
  143. c.Assert(success, checker.True, check.Commentf("/etc/passwd has been removed but is not present in the diff"))
  144. }
  145. func (s *DockerSuite) TestContainerApiStartVolumeBinds(c *check.C) {
  146. // TODO Windows CI: Investigate further why this fails on Windows to Windows CI.
  147. testRequires(c, DaemonIsLinux)
  148. path := "/foo"
  149. if daemonPlatform == "windows" {
  150. path = `c:\foo`
  151. }
  152. name := "testing"
  153. config := map[string]interface{}{
  154. "Image": "busybox",
  155. "Volumes": map[string]struct{}{path: {}},
  156. }
  157. status, _, err := sockRequest("POST", "/containers/create?name="+name, config)
  158. c.Assert(err, checker.IsNil)
  159. c.Assert(status, checker.Equals, http.StatusCreated)
  160. bindPath := randomTmpDirPath("test", daemonPlatform)
  161. config = map[string]interface{}{
  162. "Binds": []string{bindPath + ":" + path},
  163. }
  164. status, _, err = sockRequest("POST", "/containers/"+name+"/start", config)
  165. c.Assert(err, checker.IsNil)
  166. c.Assert(status, checker.Equals, http.StatusNoContent)
  167. pth, err := inspectMountSourceField(name, path)
  168. c.Assert(err, checker.IsNil)
  169. c.Assert(pth, checker.Equals, bindPath, check.Commentf("expected volume host path to be %s, got %s", bindPath, pth))
  170. }
  171. // Test for GH#10618
  172. func (s *DockerSuite) TestContainerApiStartDupVolumeBinds(c *check.C) {
  173. // TODO Windows to Windows CI - Port this
  174. testRequires(c, DaemonIsLinux)
  175. name := "testdups"
  176. config := map[string]interface{}{
  177. "Image": "busybox",
  178. "Volumes": map[string]struct{}{"/tmp": {}},
  179. }
  180. status, _, err := sockRequest("POST", "/containers/create?name="+name, config)
  181. c.Assert(err, checker.IsNil)
  182. c.Assert(status, checker.Equals, http.StatusCreated)
  183. bindPath1 := randomTmpDirPath("test1", daemonPlatform)
  184. bindPath2 := randomTmpDirPath("test2", daemonPlatform)
  185. config = map[string]interface{}{
  186. "Binds": []string{bindPath1 + ":/tmp", bindPath2 + ":/tmp"},
  187. }
  188. status, body, err := sockRequest("POST", "/containers/"+name+"/start", config)
  189. c.Assert(err, checker.IsNil)
  190. c.Assert(status, checker.Equals, http.StatusInternalServerError)
  191. c.Assert(string(body), checker.Contains, "Duplicate mount point", check.Commentf("Expected failure due to duplicate bind mounts to same path, instead got: %q with error: %v", string(body), err))
  192. }
  193. func (s *DockerSuite) TestContainerApiStartVolumesFrom(c *check.C) {
  194. // TODO Windows to Windows CI - Port this
  195. testRequires(c, DaemonIsLinux)
  196. volName := "voltst"
  197. volPath := "/tmp"
  198. dockerCmd(c, "run", "-d", "--name", volName, "-v", volPath, "busybox")
  199. name := "TestContainerApiStartVolumesFrom"
  200. config := map[string]interface{}{
  201. "Image": "busybox",
  202. "Volumes": map[string]struct{}{volPath: {}},
  203. }
  204. status, _, err := sockRequest("POST", "/containers/create?name="+name, config)
  205. c.Assert(err, checker.IsNil)
  206. c.Assert(status, checker.Equals, http.StatusCreated)
  207. config = map[string]interface{}{
  208. "VolumesFrom": []string{volName},
  209. }
  210. status, _, err = sockRequest("POST", "/containers/"+name+"/start", config)
  211. c.Assert(err, checker.IsNil)
  212. c.Assert(status, checker.Equals, http.StatusNoContent)
  213. pth, err := inspectMountSourceField(name, volPath)
  214. c.Assert(err, checker.IsNil)
  215. pth2, err := inspectMountSourceField(volName, volPath)
  216. c.Assert(err, checker.IsNil)
  217. c.Assert(pth, checker.Equals, pth2, check.Commentf("expected volume host path to be %s, got %s", pth, pth2))
  218. }
  219. func (s *DockerSuite) TestGetContainerStats(c *check.C) {
  220. // Problematic on Windows as Windows does not support stats
  221. testRequires(c, DaemonIsLinux)
  222. var (
  223. name = "statscontainer"
  224. )
  225. dockerCmd(c, "run", "-d", "--name", name, "busybox", "top")
  226. type b struct {
  227. status int
  228. body []byte
  229. err error
  230. }
  231. bc := make(chan b, 1)
  232. go func() {
  233. status, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil)
  234. bc <- b{status, body, err}
  235. }()
  236. // allow some time to stream the stats from the container
  237. time.Sleep(4 * time.Second)
  238. dockerCmd(c, "rm", "-f", name)
  239. // collect the results from the stats stream or timeout and fail
  240. // if the stream was not disconnected.
  241. select {
  242. case <-time.After(2 * time.Second):
  243. c.Fatal("stream was not closed after container was removed")
  244. case sr := <-bc:
  245. c.Assert(sr.err, checker.IsNil)
  246. c.Assert(sr.status, checker.Equals, http.StatusOK)
  247. dec := json.NewDecoder(bytes.NewBuffer(sr.body))
  248. var s *types.Stats
  249. // decode only one object from the stream
  250. c.Assert(dec.Decode(&s), checker.IsNil)
  251. }
  252. }
  253. func (s *DockerSuite) TestGetContainerStatsRmRunning(c *check.C) {
  254. // Problematic on Windows as Windows does not support stats
  255. testRequires(c, DaemonIsLinux)
  256. out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
  257. id := strings.TrimSpace(out)
  258. buf := &integration.ChannelBuffer{make(chan []byte, 1)}
  259. defer buf.Close()
  260. chErr := make(chan error, 1)
  261. go func() {
  262. _, body, err := sockRequestRaw("GET", "/containers/"+id+"/stats?stream=1", nil, "application/json")
  263. if err != nil {
  264. chErr <- err
  265. }
  266. defer body.Close()
  267. _, err = io.Copy(buf, body)
  268. chErr <- err
  269. }()
  270. defer func() {
  271. select {
  272. case err := <-chErr:
  273. c.Assert(err, checker.IsNil)
  274. default:
  275. return
  276. }
  277. }()
  278. b := make([]byte, 32)
  279. // make sure we've got some stats
  280. _, err := buf.ReadTimeout(b, 2*time.Second)
  281. c.Assert(err, checker.IsNil)
  282. // Now remove without `-f` and make sure we are still pulling stats
  283. _, _, err = dockerCmdWithError("rm", id)
  284. c.Assert(err, checker.Not(checker.IsNil), check.Commentf("rm should have failed but didn't"))
  285. _, err = buf.ReadTimeout(b, 2*time.Second)
  286. c.Assert(err, checker.IsNil)
  287. dockerCmd(c, "kill", id)
  288. }
  289. // regression test for gh13421
  290. // previous test was just checking one stat entry so it didn't fail (stats with
  291. // stream false always return one stat)
  292. func (s *DockerSuite) TestGetContainerStatsStream(c *check.C) {
  293. // Problematic on Windows as Windows does not support stats
  294. testRequires(c, DaemonIsLinux)
  295. name := "statscontainer"
  296. dockerCmd(c, "run", "-d", "--name", name, "busybox", "top")
  297. type b struct {
  298. status int
  299. body []byte
  300. err error
  301. }
  302. bc := make(chan b, 1)
  303. go func() {
  304. status, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil)
  305. bc <- b{status, body, err}
  306. }()
  307. // allow some time to stream the stats from the container
  308. time.Sleep(4 * time.Second)
  309. dockerCmd(c, "rm", "-f", name)
  310. // collect the results from the stats stream or timeout and fail
  311. // if the stream was not disconnected.
  312. select {
  313. case <-time.After(2 * time.Second):
  314. c.Fatal("stream was not closed after container was removed")
  315. case sr := <-bc:
  316. c.Assert(sr.err, checker.IsNil)
  317. c.Assert(sr.status, checker.Equals, http.StatusOK)
  318. s := string(sr.body)
  319. // count occurrences of "read" of types.Stats
  320. if l := strings.Count(s, "read"); l < 2 {
  321. c.Fatalf("Expected more than one stat streamed, got %d", l)
  322. }
  323. }
  324. }
  325. func (s *DockerSuite) TestGetContainerStatsNoStream(c *check.C) {
  326. // Problematic on Windows as Windows does not support stats
  327. testRequires(c, DaemonIsLinux)
  328. name := "statscontainer"
  329. dockerCmd(c, "run", "-d", "--name", name, "busybox", "top")
  330. type b struct {
  331. status int
  332. body []byte
  333. err error
  334. }
  335. bc := make(chan b, 1)
  336. go func() {
  337. status, body, err := sockRequest("GET", "/containers/"+name+"/stats?stream=0", nil)
  338. bc <- b{status, body, err}
  339. }()
  340. // allow some time to stream the stats from the container
  341. time.Sleep(4 * time.Second)
  342. dockerCmd(c, "rm", "-f", name)
  343. // collect the results from the stats stream or timeout and fail
  344. // if the stream was not disconnected.
  345. select {
  346. case <-time.After(2 * time.Second):
  347. c.Fatal("stream was not closed after container was removed")
  348. case sr := <-bc:
  349. c.Assert(sr.err, checker.IsNil)
  350. c.Assert(sr.status, checker.Equals, http.StatusOK)
  351. s := string(sr.body)
  352. // count occurrences of "read" of types.Stats
  353. c.Assert(strings.Count(s, "read"), checker.Equals, 1, check.Commentf("Expected only one stat streamed, got %d", strings.Count(s, "read")))
  354. }
  355. }
  356. func (s *DockerSuite) TestGetStoppedContainerStats(c *check.C) {
  357. // Problematic on Windows as Windows does not support stats
  358. testRequires(c, DaemonIsLinux)
  359. name := "statscontainer"
  360. dockerCmd(c, "create", "--name", name, "busybox", "top")
  361. type stats struct {
  362. status int
  363. err error
  364. }
  365. chResp := make(chan stats)
  366. // We expect an immediate response, but if it's not immediate, the test would hang, so put it in a goroutine
  367. // below we'll check this on a timeout.
  368. go func() {
  369. resp, body, err := sockRequestRaw("GET", "/containers/"+name+"/stats", nil, "")
  370. body.Close()
  371. chResp <- stats{resp.StatusCode, err}
  372. }()
  373. select {
  374. case r := <-chResp:
  375. c.Assert(r.err, checker.IsNil)
  376. c.Assert(r.status, checker.Equals, http.StatusOK)
  377. case <-time.After(10 * time.Second):
  378. c.Fatal("timeout waiting for stats response for stopped container")
  379. }
  380. }
  381. // #9981 - Allow a docker created volume (ie, one in /var/lib/docker/volumes) to be used to overwrite (via passing in Binds on api start) an existing volume
  382. func (s *DockerSuite) TestPostContainerBindNormalVolume(c *check.C) {
  383. // TODO Windows to Windows CI - Port this
  384. testRequires(c, DaemonIsLinux)
  385. dockerCmd(c, "create", "-v", "/foo", "--name=one", "busybox")
  386. fooDir, err := inspectMountSourceField("one", "/foo")
  387. c.Assert(err, checker.IsNil)
  388. dockerCmd(c, "create", "-v", "/foo", "--name=two", "busybox")
  389. bindSpec := map[string][]string{"Binds": {fooDir + ":/foo"}}
  390. status, _, err := sockRequest("POST", "/containers/two/start", bindSpec)
  391. c.Assert(err, checker.IsNil)
  392. c.Assert(status, checker.Equals, http.StatusNoContent)
  393. fooDir2, err := inspectMountSourceField("two", "/foo")
  394. c.Assert(err, checker.IsNil)
  395. c.Assert(fooDir2, checker.Equals, fooDir, check.Commentf("expected volume path to be %s, got: %s", fooDir, fooDir2))
  396. }
  397. func (s *DockerSuite) TestContainerApiPause(c *check.C) {
  398. // Problematic on Windows as Windows does not support pause
  399. testRequires(c, DaemonIsLinux)
  400. defer unpauseAllContainers()
  401. out, _ := dockerCmd(c, "run", "-d", "busybox", "sleep", "30")
  402. ContainerID := strings.TrimSpace(out)
  403. status, _, err := sockRequest("POST", "/containers/"+ContainerID+"/pause", nil)
  404. c.Assert(err, checker.IsNil)
  405. c.Assert(status, checker.Equals, http.StatusNoContent)
  406. pausedContainers, err := getSliceOfPausedContainers()
  407. c.Assert(err, checker.IsNil, check.Commentf("error thrown while checking if containers were paused"))
  408. if len(pausedContainers) != 1 || stringid.TruncateID(ContainerID) != pausedContainers[0] {
  409. c.Fatalf("there should be one paused container and not %d", len(pausedContainers))
  410. }
  411. status, _, err = sockRequest("POST", "/containers/"+ContainerID+"/unpause", nil)
  412. c.Assert(err, checker.IsNil)
  413. c.Assert(status, checker.Equals, http.StatusNoContent)
  414. pausedContainers, err = getSliceOfPausedContainers()
  415. c.Assert(err, checker.IsNil, check.Commentf("error thrown while checking if containers were paused"))
  416. c.Assert(pausedContainers, checker.IsNil, check.Commentf("There should be no paused container."))
  417. }
  418. func (s *DockerSuite) TestContainerApiTop(c *check.C) {
  419. // Problematic on Windows as Windows does not support top
  420. testRequires(c, DaemonIsLinux)
  421. out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "top")
  422. id := strings.TrimSpace(string(out))
  423. c.Assert(waitRun(id), checker.IsNil)
  424. type topResp struct {
  425. Titles []string
  426. Processes [][]string
  427. }
  428. var top topResp
  429. status, b, err := sockRequest("GET", "/containers/"+id+"/top?ps_args=aux", nil)
  430. c.Assert(err, checker.IsNil)
  431. c.Assert(status, checker.Equals, http.StatusOK)
  432. c.Assert(json.Unmarshal(b, &top), checker.IsNil)
  433. c.Assert(top.Titles, checker.HasLen, 11, check.Commentf("expected 11 titles, found %d: %v", len(top.Titles), top.Titles))
  434. if top.Titles[0] != "USER" || top.Titles[10] != "COMMAND" {
  435. c.Fatalf("expected `USER` at `Titles[0]` and `COMMAND` at Titles[10]: %v", top.Titles)
  436. }
  437. c.Assert(top.Processes, checker.HasLen, 2, check.Commentf("expected 2 processes, found %d: %v", len(top.Processes), top.Processes))
  438. c.Assert(top.Processes[0][10], checker.Equals, "/bin/sh -c top")
  439. c.Assert(top.Processes[1][10], checker.Equals, "top")
  440. }
  441. func (s *DockerSuite) TestContainerApiCommit(c *check.C) {
  442. cName := "testapicommit"
  443. dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test")
  444. name := "testcontainerapicommit"
  445. status, b, err := sockRequest("POST", "/commit?repo="+name+"&testtag=tag&container="+cName, nil)
  446. c.Assert(err, checker.IsNil)
  447. c.Assert(status, checker.Equals, http.StatusCreated)
  448. type resp struct {
  449. ID string
  450. }
  451. var img resp
  452. c.Assert(json.Unmarshal(b, &img), checker.IsNil)
  453. cmd := inspectField(c, img.ID, "Config.Cmd")
  454. c.Assert(cmd, checker.Equals, "{[/bin/sh -c touch /test]}", check.Commentf("got wrong Cmd from commit: %q", cmd))
  455. // sanity check, make sure the image is what we think it is
  456. dockerCmd(c, "run", img.ID, "ls", "/test")
  457. }
  458. func (s *DockerSuite) TestContainerApiCommitWithLabelInConfig(c *check.C) {
  459. cName := "testapicommitwithconfig"
  460. dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test")
  461. config := map[string]interface{}{
  462. "Labels": map[string]string{"key1": "value1", "key2": "value2"},
  463. }
  464. name := "testcontainerapicommitwithconfig"
  465. status, b, err := sockRequest("POST", "/commit?repo="+name+"&container="+cName, config)
  466. c.Assert(err, checker.IsNil)
  467. c.Assert(status, checker.Equals, http.StatusCreated)
  468. type resp struct {
  469. ID string
  470. }
  471. var img resp
  472. c.Assert(json.Unmarshal(b, &img), checker.IsNil)
  473. label1 := inspectFieldMap(c, img.ID, "Config.Labels", "key1")
  474. c.Assert(label1, checker.Equals, "value1")
  475. label2 := inspectFieldMap(c, img.ID, "Config.Labels", "key2")
  476. c.Assert(label2, checker.Equals, "value2")
  477. cmd := inspectField(c, img.ID, "Config.Cmd")
  478. c.Assert(cmd, checker.Equals, "{[/bin/sh -c touch /test]}", check.Commentf("got wrong Cmd from commit: %q", cmd))
  479. // sanity check, make sure the image is what we think it is
  480. dockerCmd(c, "run", img.ID, "ls", "/test")
  481. }
  482. func (s *DockerSuite) TestContainerApiBadPort(c *check.C) {
  483. // TODO Windows to Windows CI - Port this test
  484. testRequires(c, DaemonIsLinux)
  485. config := map[string]interface{}{
  486. "Image": "busybox",
  487. "Cmd": []string{"/bin/sh", "-c", "echo test"},
  488. "PortBindings": map[string]interface{}{
  489. "8080/tcp": []map[string]interface{}{
  490. {
  491. "HostIP": "",
  492. "HostPort": "aa80",
  493. },
  494. },
  495. },
  496. }
  497. jsonData := bytes.NewBuffer(nil)
  498. json.NewEncoder(jsonData).Encode(config)
  499. status, b, err := sockRequest("POST", "/containers/create", config)
  500. c.Assert(err, checker.IsNil)
  501. c.Assert(status, checker.Equals, http.StatusInternalServerError)
  502. c.Assert(strings.TrimSpace(string(b)), checker.Equals, `Invalid port specification: "aa80"`, check.Commentf("Incorrect error msg: %s", string(b)))
  503. }
  504. func (s *DockerSuite) TestContainerApiCreate(c *check.C) {
  505. config := map[string]interface{}{
  506. "Image": "busybox",
  507. "Cmd": []string{"/bin/sh", "-c", "touch /test && ls /test"},
  508. }
  509. status, b, err := sockRequest("POST", "/containers/create", config)
  510. c.Assert(err, checker.IsNil)
  511. c.Assert(status, checker.Equals, http.StatusCreated)
  512. type createResp struct {
  513. ID string
  514. }
  515. var container createResp
  516. c.Assert(json.Unmarshal(b, &container), checker.IsNil)
  517. out, _ := dockerCmd(c, "start", "-a", container.ID)
  518. c.Assert(strings.TrimSpace(out), checker.Equals, "/test")
  519. }
  520. func (s *DockerSuite) TestContainerApiCreateEmptyConfig(c *check.C) {
  521. config := map[string]interface{}{}
  522. status, b, err := sockRequest("POST", "/containers/create", config)
  523. c.Assert(err, checker.IsNil)
  524. c.Assert(status, checker.Equals, http.StatusInternalServerError)
  525. expected := "Config cannot be empty in order to create a container\n"
  526. c.Assert(string(b), checker.Equals, expected)
  527. }
  528. func (s *DockerSuite) TestContainerApiCreateMultipleNetworksConfig(c *check.C) {
  529. // Container creation must fail if client specified configurations for more than one network
  530. config := map[string]interface{}{
  531. "Image": "busybox",
  532. "NetworkingConfig": networktypes.NetworkingConfig{
  533. EndpointsConfig: map[string]*networktypes.EndpointSettings{
  534. "net1": {},
  535. "net2": {},
  536. "net3": {},
  537. },
  538. },
  539. }
  540. status, b, err := sockRequest("POST", "/containers/create", config)
  541. c.Assert(err, checker.IsNil)
  542. c.Assert(status, checker.Equals, http.StatusBadRequest)
  543. // network name order in error message is not deterministic
  544. c.Assert(string(b), checker.Contains, "Container cannot be connected to network endpoints")
  545. c.Assert(string(b), checker.Contains, "net1")
  546. c.Assert(string(b), checker.Contains, "net2")
  547. c.Assert(string(b), checker.Contains, "net3")
  548. }
  549. func (s *DockerSuite) TestContainerApiCreateWithHostName(c *check.C) {
  550. // TODO Windows: Port this test once hostname is supported
  551. testRequires(c, DaemonIsLinux)
  552. hostName := "test-host"
  553. config := map[string]interface{}{
  554. "Image": "busybox",
  555. "Hostname": hostName,
  556. }
  557. status, body, err := sockRequest("POST", "/containers/create", config)
  558. c.Assert(err, checker.IsNil)
  559. c.Assert(status, checker.Equals, http.StatusCreated)
  560. var container types.ContainerCreateResponse
  561. c.Assert(json.Unmarshal(body, &container), checker.IsNil)
  562. status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  563. c.Assert(err, checker.IsNil)
  564. c.Assert(status, checker.Equals, http.StatusOK)
  565. var containerJSON types.ContainerJSON
  566. c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil)
  567. c.Assert(containerJSON.Config.Hostname, checker.Equals, hostName, check.Commentf("Mismatched Hostname"))
  568. }
  569. func (s *DockerSuite) TestContainerApiCreateWithDomainName(c *check.C) {
  570. // TODO Windows: Port this test once domain name is supported
  571. testRequires(c, DaemonIsLinux)
  572. domainName := "test-domain"
  573. config := map[string]interface{}{
  574. "Image": "busybox",
  575. "Domainname": domainName,
  576. }
  577. status, body, err := sockRequest("POST", "/containers/create", config)
  578. c.Assert(err, checker.IsNil)
  579. c.Assert(status, checker.Equals, http.StatusCreated)
  580. var container types.ContainerCreateResponse
  581. c.Assert(json.Unmarshal(body, &container), checker.IsNil)
  582. status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  583. c.Assert(err, checker.IsNil)
  584. c.Assert(status, checker.Equals, http.StatusOK)
  585. var containerJSON types.ContainerJSON
  586. c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil)
  587. c.Assert(containerJSON.Config.Domainname, checker.Equals, domainName, check.Commentf("Mismatched Domainname"))
  588. }
  589. func (s *DockerSuite) TestContainerApiCreateBridgeNetworkMode(c *check.C) {
  590. // Windows does not support bridge
  591. testRequires(c, DaemonIsLinux)
  592. UtilCreateNetworkMode(c, "bridge")
  593. }
  594. func (s *DockerSuite) TestContainerApiCreateOtherNetworkModes(c *check.C) {
  595. // Windows does not support these network modes
  596. testRequires(c, DaemonIsLinux, NotUserNamespace)
  597. UtilCreateNetworkMode(c, "host")
  598. UtilCreateNetworkMode(c, "container:web1")
  599. }
  600. func UtilCreateNetworkMode(c *check.C, networkMode string) {
  601. config := map[string]interface{}{
  602. "Image": "busybox",
  603. "HostConfig": map[string]interface{}{"NetworkMode": networkMode},
  604. }
  605. status, body, err := sockRequest("POST", "/containers/create", config)
  606. c.Assert(err, checker.IsNil)
  607. c.Assert(status, checker.Equals, http.StatusCreated)
  608. var container types.ContainerCreateResponse
  609. c.Assert(json.Unmarshal(body, &container), checker.IsNil)
  610. status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  611. c.Assert(err, checker.IsNil)
  612. c.Assert(status, checker.Equals, http.StatusOK)
  613. var containerJSON types.ContainerJSON
  614. c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil)
  615. c.Assert(containerJSON.HostConfig.NetworkMode, checker.Equals, containertypes.NetworkMode(networkMode), check.Commentf("Mismatched NetworkMode"))
  616. }
  617. func (s *DockerSuite) TestContainerApiCreateWithCpuSharesCpuset(c *check.C) {
  618. // TODO Windows to Windows CI. The CpuShares part could be ported.
  619. testRequires(c, DaemonIsLinux)
  620. config := map[string]interface{}{
  621. "Image": "busybox",
  622. "CpuShares": 512,
  623. "CpusetCpus": "0",
  624. }
  625. status, body, err := sockRequest("POST", "/containers/create", config)
  626. c.Assert(err, checker.IsNil)
  627. c.Assert(status, checker.Equals, http.StatusCreated)
  628. var container types.ContainerCreateResponse
  629. c.Assert(json.Unmarshal(body, &container), checker.IsNil)
  630. status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  631. c.Assert(err, checker.IsNil)
  632. c.Assert(status, checker.Equals, http.StatusOK)
  633. var containerJSON types.ContainerJSON
  634. c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil)
  635. out := inspectField(c, containerJSON.ID, "HostConfig.CpuShares")
  636. c.Assert(out, checker.Equals, "512")
  637. outCpuset := inspectField(c, containerJSON.ID, "HostConfig.CpusetCpus")
  638. c.Assert(outCpuset, checker.Equals, "0")
  639. }
  640. func (s *DockerSuite) TestContainerApiVerifyHeader(c *check.C) {
  641. config := map[string]interface{}{
  642. "Image": "busybox",
  643. }
  644. create := func(ct string) (*http.Response, io.ReadCloser, error) {
  645. jsonData := bytes.NewBuffer(nil)
  646. c.Assert(json.NewEncoder(jsonData).Encode(config), checker.IsNil)
  647. return sockRequestRaw("POST", "/containers/create", jsonData, ct)
  648. }
  649. // Try with no content-type
  650. res, body, err := create("")
  651. c.Assert(err, checker.IsNil)
  652. c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
  653. body.Close()
  654. // Try with wrong content-type
  655. res, body, err = create("application/xml")
  656. c.Assert(err, checker.IsNil)
  657. c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
  658. body.Close()
  659. // now application/json
  660. res, body, err = create("application/json")
  661. c.Assert(err, checker.IsNil)
  662. c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
  663. body.Close()
  664. }
  665. //Issue 14230. daemon should return 500 for invalid port syntax
  666. func (s *DockerSuite) TestContainerApiInvalidPortSyntax(c *check.C) {
  667. config := `{
  668. "Image": "busybox",
  669. "HostConfig": {
  670. "NetworkMode": "default",
  671. "PortBindings": {
  672. "19039;1230": [
  673. {}
  674. ]
  675. }
  676. }
  677. }`
  678. res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
  679. c.Assert(err, checker.IsNil)
  680. c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
  681. b, err := readBody(body)
  682. c.Assert(err, checker.IsNil)
  683. c.Assert(string(b[:]), checker.Contains, "Invalid port")
  684. }
  685. // Issue 7941 - test to make sure a "null" in JSON is just ignored.
  686. // W/o this fix a null in JSON would be parsed into a string var as "null"
  687. func (s *DockerSuite) TestContainerApiPostCreateNull(c *check.C) {
  688. // TODO Windows to Windows CI. Bit of this with alternate fields checked
  689. // can probably be ported.
  690. testRequires(c, DaemonIsLinux)
  691. config := `{
  692. "Hostname":"",
  693. "Domainname":"",
  694. "Memory":0,
  695. "MemorySwap":0,
  696. "CpuShares":0,
  697. "Cpuset":null,
  698. "AttachStdin":true,
  699. "AttachStdout":true,
  700. "AttachStderr":true,
  701. "ExposedPorts":{},
  702. "Tty":true,
  703. "OpenStdin":true,
  704. "StdinOnce":true,
  705. "Env":[],
  706. "Cmd":"ls",
  707. "Image":"busybox",
  708. "Volumes":{},
  709. "WorkingDir":"",
  710. "Entrypoint":null,
  711. "NetworkDisabled":false,
  712. "OnBuild":null}`
  713. res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
  714. c.Assert(err, checker.IsNil)
  715. c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
  716. b, err := readBody(body)
  717. c.Assert(err, checker.IsNil)
  718. type createResp struct {
  719. ID string
  720. }
  721. var container createResp
  722. c.Assert(json.Unmarshal(b, &container), checker.IsNil)
  723. out := inspectField(c, container.ID, "HostConfig.CpusetCpus")
  724. c.Assert(out, checker.Equals, "")
  725. outMemory := inspectField(c, container.ID, "HostConfig.Memory")
  726. c.Assert(outMemory, checker.Equals, "0")
  727. outMemorySwap := inspectField(c, container.ID, "HostConfig.MemorySwap")
  728. c.Assert(outMemorySwap, checker.Equals, "0")
  729. }
  730. func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *check.C) {
  731. // TODO Windows: Port once memory is supported
  732. testRequires(c, DaemonIsLinux)
  733. config := `{
  734. "Image": "busybox",
  735. "Cmd": "ls",
  736. "OpenStdin": true,
  737. "CpuShares": 100,
  738. "Memory": 524287
  739. }`
  740. res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
  741. c.Assert(err, checker.IsNil)
  742. b, err2 := readBody(body)
  743. c.Assert(err2, checker.IsNil)
  744. c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
  745. c.Assert(string(b), checker.Contains, "Minimum memory limit allowed is 4MB")
  746. }
  747. func (s *DockerSuite) TestStartWithTooLowMemoryLimit(c *check.C) {
  748. // TODO Windows: Port once memory is supported
  749. testRequires(c, DaemonIsLinux)
  750. out, _ := dockerCmd(c, "create", "busybox")
  751. containerID := strings.TrimSpace(out)
  752. config := `{
  753. "CpuShares": 100,
  754. "Memory": 524287
  755. }`
  756. res, body, err := sockRequestRaw("POST", "/containers/"+containerID+"/start", strings.NewReader(config), "application/json")
  757. c.Assert(err, checker.IsNil)
  758. b, err2 := readBody(body)
  759. c.Assert(err2, checker.IsNil)
  760. c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
  761. c.Assert(string(b), checker.Contains, "Minimum memory limit allowed is 4MB")
  762. }
  763. func (s *DockerSuite) TestContainerApiRename(c *check.C) {
  764. // TODO Windows: Enable for TP5. Fails on TP4.
  765. testRequires(c, DaemonIsLinux)
  766. out, _ := dockerCmd(c, "run", "--name", "TestContainerApiRename", "-d", "busybox", "sh")
  767. containerID := strings.TrimSpace(out)
  768. newName := "TestContainerApiRenameNew"
  769. statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/rename?name="+newName, nil)
  770. c.Assert(err, checker.IsNil)
  771. // 204 No Content is expected, not 200
  772. c.Assert(statusCode, checker.Equals, http.StatusNoContent)
  773. name := inspectField(c, containerID, "Name")
  774. c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container"))
  775. }
  776. func (s *DockerSuite) TestContainerApiKill(c *check.C) {
  777. name := "test-api-kill"
  778. runSleepingContainer(c, "-i", "--name", name)
  779. status, _, err := sockRequest("POST", "/containers/"+name+"/kill", nil)
  780. c.Assert(err, checker.IsNil)
  781. c.Assert(status, checker.Equals, http.StatusNoContent)
  782. state := inspectField(c, name, "State.Running")
  783. c.Assert(state, checker.Equals, "false", check.Commentf("got wrong State from container %s: %q", name, state))
  784. }
  785. func (s *DockerSuite) TestContainerApiRestart(c *check.C) {
  786. // TODO Windows to Windows CI. This is flaky due to the timing
  787. testRequires(c, DaemonIsLinux)
  788. name := "test-api-restart"
  789. dockerCmd(c, "run", "-di", "--name", name, "busybox", "top")
  790. status, _, err := sockRequest("POST", "/containers/"+name+"/restart?t=1", nil)
  791. c.Assert(err, checker.IsNil)
  792. c.Assert(status, checker.Equals, http.StatusNoContent)
  793. c.Assert(waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 5*time.Second), checker.IsNil)
  794. }
  795. func (s *DockerSuite) TestContainerApiRestartNotimeoutParam(c *check.C) {
  796. // TODO Windows to Windows CI. This is flaky due to the timing
  797. testRequires(c, DaemonIsLinux)
  798. name := "test-api-restart-no-timeout-param"
  799. out, _ := dockerCmd(c, "run", "-di", "--name", name, "busybox", "top")
  800. id := strings.TrimSpace(out)
  801. c.Assert(waitRun(id), checker.IsNil)
  802. status, _, err := sockRequest("POST", "/containers/"+name+"/restart", nil)
  803. c.Assert(err, checker.IsNil)
  804. c.Assert(status, checker.Equals, http.StatusNoContent)
  805. c.Assert(waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 5*time.Second), checker.IsNil)
  806. }
  807. func (s *DockerSuite) TestContainerApiStart(c *check.C) {
  808. name := "testing-start"
  809. config := map[string]interface{}{
  810. "Image": "busybox",
  811. "Cmd": append([]string{"/bin/sh", "-c"}, defaultSleepCommand...),
  812. "OpenStdin": true,
  813. }
  814. status, _, err := sockRequest("POST", "/containers/create?name="+name, config)
  815. c.Assert(err, checker.IsNil)
  816. c.Assert(status, checker.Equals, http.StatusCreated)
  817. conf := make(map[string]interface{})
  818. status, _, err = sockRequest("POST", "/containers/"+name+"/start", conf)
  819. c.Assert(err, checker.IsNil)
  820. c.Assert(status, checker.Equals, http.StatusNoContent)
  821. // second call to start should give 304
  822. status, _, err = sockRequest("POST", "/containers/"+name+"/start", conf)
  823. c.Assert(err, checker.IsNil)
  824. // TODO(tibor): figure out why this doesn't work on windows
  825. if isLocalDaemon {
  826. c.Assert(status, checker.Equals, http.StatusNotModified)
  827. }
  828. }
  829. func (s *DockerSuite) TestContainerApiStop(c *check.C) {
  830. name := "test-api-stop"
  831. runSleepingContainer(c, "-i", "--name", name)
  832. status, _, err := sockRequest("POST", "/containers/"+name+"/stop?t=30", nil)
  833. c.Assert(err, checker.IsNil)
  834. c.Assert(status, checker.Equals, http.StatusNoContent)
  835. c.Assert(waitInspect(name, "{{ .State.Running }}", "false", 60*time.Second), checker.IsNil)
  836. // second call to start should give 304
  837. status, _, err = sockRequest("POST", "/containers/"+name+"/stop?t=30", nil)
  838. c.Assert(err, checker.IsNil)
  839. c.Assert(status, checker.Equals, http.StatusNotModified)
  840. }
  841. func (s *DockerSuite) TestContainerApiWait(c *check.C) {
  842. name := "test-api-wait"
  843. sleepCmd := "/bin/sleep"
  844. if daemonPlatform == "windows" {
  845. sleepCmd = "sleep"
  846. }
  847. dockerCmd(c, "run", "--name", name, "busybox", sleepCmd, "5")
  848. status, body, err := sockRequest("POST", "/containers/"+name+"/wait", nil)
  849. c.Assert(err, checker.IsNil)
  850. c.Assert(status, checker.Equals, http.StatusOK)
  851. c.Assert(waitInspect(name, "{{ .State.Running }}", "false", 60*time.Second), checker.IsNil)
  852. var waitres types.ContainerWaitResponse
  853. c.Assert(json.Unmarshal(body, &waitres), checker.IsNil)
  854. c.Assert(waitres.StatusCode, checker.Equals, 0)
  855. }
  856. func (s *DockerSuite) TestContainerApiCopy(c *check.C) {
  857. // TODO Windows to Windows CI. This can be ported.
  858. testRequires(c, DaemonIsLinux)
  859. name := "test-container-api-copy"
  860. dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
  861. postData := types.CopyConfig{
  862. Resource: "/test.txt",
  863. }
  864. status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData)
  865. c.Assert(err, checker.IsNil)
  866. c.Assert(status, checker.Equals, http.StatusOK)
  867. found := false
  868. for tarReader := tar.NewReader(bytes.NewReader(body)); ; {
  869. h, err := tarReader.Next()
  870. if err != nil {
  871. if err == io.EOF {
  872. break
  873. }
  874. c.Fatal(err)
  875. }
  876. if h.Name == "test.txt" {
  877. found = true
  878. break
  879. }
  880. }
  881. c.Assert(found, checker.True)
  882. }
  883. func (s *DockerSuite) TestContainerApiCopyResourcePathEmpty(c *check.C) {
  884. // TODO Windows to Windows CI. This can be ported.
  885. testRequires(c, DaemonIsLinux)
  886. name := "test-container-api-copy-resource-empty"
  887. dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
  888. postData := types.CopyConfig{
  889. Resource: "",
  890. }
  891. status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData)
  892. c.Assert(err, checker.IsNil)
  893. c.Assert(status, checker.Equals, http.StatusInternalServerError)
  894. c.Assert(string(body), checker.Matches, "Path cannot be empty\n")
  895. }
  896. func (s *DockerSuite) TestContainerApiCopyResourcePathNotFound(c *check.C) {
  897. // TODO Windows to Windows CI. This can be ported.
  898. testRequires(c, DaemonIsLinux)
  899. name := "test-container-api-copy-resource-not-found"
  900. dockerCmd(c, "run", "--name", name, "busybox")
  901. postData := types.CopyConfig{
  902. Resource: "/notexist",
  903. }
  904. status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData)
  905. c.Assert(err, checker.IsNil)
  906. c.Assert(status, checker.Equals, http.StatusInternalServerError)
  907. c.Assert(string(body), checker.Matches, "Could not find the file /notexist in container "+name+"\n")
  908. }
  909. func (s *DockerSuite) TestContainerApiCopyContainerNotFound(c *check.C) {
  910. postData := types.CopyConfig{
  911. Resource: "/something",
  912. }
  913. status, _, err := sockRequest("POST", "/containers/notexists/copy", postData)
  914. c.Assert(err, checker.IsNil)
  915. c.Assert(status, checker.Equals, http.StatusNotFound)
  916. }
  917. func (s *DockerSuite) TestContainerApiDelete(c *check.C) {
  918. out, _ := runSleepingContainer(c)
  919. id := strings.TrimSpace(out)
  920. c.Assert(waitRun(id), checker.IsNil)
  921. dockerCmd(c, "stop", id)
  922. status, _, err := sockRequest("DELETE", "/containers/"+id, nil)
  923. c.Assert(err, checker.IsNil)
  924. c.Assert(status, checker.Equals, http.StatusNoContent)
  925. }
  926. func (s *DockerSuite) TestContainerApiDeleteNotExist(c *check.C) {
  927. status, body, err := sockRequest("DELETE", "/containers/doesnotexist", nil)
  928. c.Assert(err, checker.IsNil)
  929. c.Assert(status, checker.Equals, http.StatusNotFound)
  930. c.Assert(string(body), checker.Matches, "No such container: doesnotexist\n")
  931. }
  932. func (s *DockerSuite) TestContainerApiDeleteForce(c *check.C) {
  933. out, _ := runSleepingContainer(c)
  934. id := strings.TrimSpace(out)
  935. c.Assert(waitRun(id), checker.IsNil)
  936. status, _, err := sockRequest("DELETE", "/containers/"+id+"?force=1", nil)
  937. c.Assert(err, checker.IsNil)
  938. c.Assert(status, checker.Equals, http.StatusNoContent)
  939. }
  940. func (s *DockerSuite) TestContainerApiDeleteRemoveLinks(c *check.C) {
  941. // Windows does not support links
  942. testRequires(c, DaemonIsLinux)
  943. out, _ := dockerCmd(c, "run", "-d", "--name", "tlink1", "busybox", "top")
  944. id := strings.TrimSpace(out)
  945. c.Assert(waitRun(id), checker.IsNil)
  946. out, _ = dockerCmd(c, "run", "--link", "tlink1:tlink1", "--name", "tlink2", "-d", "busybox", "top")
  947. id2 := strings.TrimSpace(out)
  948. c.Assert(waitRun(id2), checker.IsNil)
  949. links := inspectFieldJSON(c, id2, "HostConfig.Links")
  950. c.Assert(links, checker.Equals, "[\"/tlink1:/tlink2/tlink1\"]", check.Commentf("expected to have links between containers"))
  951. status, b, err := sockRequest("DELETE", "/containers/tlink2/tlink1?link=1", nil)
  952. c.Assert(err, check.IsNil)
  953. c.Assert(status, check.Equals, http.StatusNoContent, check.Commentf(string(b)))
  954. linksPostRm := inspectFieldJSON(c, id2, "HostConfig.Links")
  955. c.Assert(linksPostRm, checker.Equals, "null", check.Commentf("call to api deleteContainer links should have removed the specified links"))
  956. }
  957. func (s *DockerSuite) TestContainerApiDeleteConflict(c *check.C) {
  958. out, _ := runSleepingContainer(c)
  959. id := strings.TrimSpace(out)
  960. c.Assert(waitRun(id), checker.IsNil)
  961. status, _, err := sockRequest("DELETE", "/containers/"+id, nil)
  962. c.Assert(err, checker.IsNil)
  963. c.Assert(status, checker.Equals, http.StatusConflict)
  964. }
  965. func (s *DockerSuite) TestContainerApiDeleteRemoveVolume(c *check.C) {
  966. testRequires(c, SameHostDaemon)
  967. vol := "/testvolume"
  968. if daemonPlatform == "windows" {
  969. vol = `c:\testvolume`
  970. }
  971. out, _ := runSleepingContainer(c, "-v", vol)
  972. id := strings.TrimSpace(out)
  973. c.Assert(waitRun(id), checker.IsNil)
  974. source, err := inspectMountSourceField(id, vol)
  975. _, err = os.Stat(source)
  976. c.Assert(err, checker.IsNil)
  977. status, _, err := sockRequest("DELETE", "/containers/"+id+"?v=1&force=1", nil)
  978. c.Assert(err, checker.IsNil)
  979. c.Assert(status, checker.Equals, http.StatusNoContent)
  980. _, err = os.Stat(source)
  981. c.Assert(os.IsNotExist(err), checker.True, check.Commentf("expected to get ErrNotExist error, got %v", err))
  982. }
  983. // Regression test for https://github.com/docker/docker/issues/6231
  984. func (s *DockerSuite) TestContainerApiChunkedEncoding(c *check.C) {
  985. // TODO Windows CI: This can be ported
  986. testRequires(c, DaemonIsLinux)
  987. out, _ := dockerCmd(c, "create", "-v", "/foo", "busybox", "true")
  988. id := strings.TrimSpace(out)
  989. conn, err := sockConn(time.Duration(10 * time.Second))
  990. c.Assert(err, checker.IsNil)
  991. client := httputil.NewClientConn(conn, nil)
  992. defer client.Close()
  993. bindCfg := strings.NewReader(`{"Binds": ["/tmp:/foo"]}`)
  994. req, err := http.NewRequest("POST", "/containers/"+id+"/start", bindCfg)
  995. c.Assert(err, checker.IsNil)
  996. req.Header.Set("Content-Type", "application/json")
  997. // This is a cheat to make the http request do chunked encoding
  998. // Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite
  999. // https://golang.org/src/pkg/net/http/request.go?s=11980:12172
  1000. req.ContentLength = -1
  1001. resp, err := client.Do(req)
  1002. c.Assert(err, checker.IsNil, check.Commentf("error starting container with chunked encoding"))
  1003. resp.Body.Close()
  1004. c.Assert(resp.StatusCode, checker.Equals, 204)
  1005. out = inspectFieldJSON(c, id, "HostConfig.Binds")
  1006. var binds []string
  1007. c.Assert(json.NewDecoder(strings.NewReader(out)).Decode(&binds), checker.IsNil)
  1008. c.Assert(binds, checker.HasLen, 1, check.Commentf("Got unexpected binds: %v", binds))
  1009. expected := "/tmp:/foo"
  1010. c.Assert(binds[0], checker.Equals, expected, check.Commentf("got incorrect bind spec"))
  1011. }
  1012. func (s *DockerSuite) TestContainerApiPostContainerStop(c *check.C) {
  1013. out, _ := runSleepingContainer(c)
  1014. containerID := strings.TrimSpace(out)
  1015. c.Assert(waitRun(containerID), checker.IsNil)
  1016. statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/stop", nil)
  1017. c.Assert(err, checker.IsNil)
  1018. // 204 No Content is expected, not 200
  1019. c.Assert(statusCode, checker.Equals, http.StatusNoContent)
  1020. c.Assert(waitInspect(containerID, "{{ .State.Running }}", "false", 5*time.Second), checker.IsNil)
  1021. }
  1022. // #14170
  1023. func (s *DockerSuite) TestPostContainerApiCreateWithStringOrSliceEntrypoint(c *check.C) {
  1024. config := struct {
  1025. Image string
  1026. Entrypoint string
  1027. Cmd []string
  1028. }{"busybox", "echo", []string{"hello", "world"}}
  1029. _, _, err := sockRequest("POST", "/containers/create?name=echotest", config)
  1030. c.Assert(err, checker.IsNil)
  1031. out, _ := dockerCmd(c, "start", "-a", "echotest")
  1032. c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
  1033. config2 := struct {
  1034. Image string
  1035. Entrypoint []string
  1036. Cmd []string
  1037. }{"busybox", []string{"echo"}, []string{"hello", "world"}}
  1038. _, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2)
  1039. c.Assert(err, checker.IsNil)
  1040. out, _ = dockerCmd(c, "start", "-a", "echotest2")
  1041. c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
  1042. }
  1043. // #14170
  1044. func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *check.C) {
  1045. config := struct {
  1046. Image string
  1047. Entrypoint string
  1048. Cmd string
  1049. }{"busybox", "echo", "hello world"}
  1050. _, _, err := sockRequest("POST", "/containers/create?name=echotest", config)
  1051. c.Assert(err, checker.IsNil)
  1052. out, _ := dockerCmd(c, "start", "-a", "echotest")
  1053. c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
  1054. config2 := struct {
  1055. Image string
  1056. Cmd []string
  1057. }{"busybox", []string{"echo", "hello", "world"}}
  1058. _, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2)
  1059. c.Assert(err, checker.IsNil)
  1060. out, _ = dockerCmd(c, "start", "-a", "echotest2")
  1061. c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
  1062. }
  1063. // regression #14318
  1064. func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *check.C) {
  1065. // Windows doesn't support CapAdd/CapDrop
  1066. testRequires(c, DaemonIsLinux)
  1067. config := struct {
  1068. Image string
  1069. CapAdd string
  1070. CapDrop string
  1071. }{"busybox", "NET_ADMIN", "SYS_ADMIN"}
  1072. status, _, err := sockRequest("POST", "/containers/create?name=capaddtest0", config)
  1073. c.Assert(err, checker.IsNil)
  1074. c.Assert(status, checker.Equals, http.StatusCreated)
  1075. config2 := struct {
  1076. Image string
  1077. CapAdd []string
  1078. CapDrop []string
  1079. }{"busybox", []string{"NET_ADMIN", "SYS_ADMIN"}, []string{"SETGID"}}
  1080. status, _, err = sockRequest("POST", "/containers/create?name=capaddtest1", config2)
  1081. c.Assert(err, checker.IsNil)
  1082. c.Assert(status, checker.Equals, http.StatusCreated)
  1083. }
  1084. // #14640
  1085. func (s *DockerSuite) TestPostContainersStartWithoutLinksInHostConfig(c *check.C) {
  1086. // TODO Windows: Windows doesn't support supplying a hostconfig on start.
  1087. // An alternate test could be written to validate the negative testing aspect of this
  1088. testRequires(c, DaemonIsLinux)
  1089. name := "test-host-config-links"
  1090. dockerCmd(c, append([]string{"create", "--name", name, "busybox"}, defaultSleepCommand...)...)
  1091. hc := inspectFieldJSON(c, name, "HostConfig")
  1092. config := `{"HostConfig":` + hc + `}`
  1093. res, b, err := sockRequestRaw("POST", "/containers/"+name+"/start", strings.NewReader(config), "application/json")
  1094. c.Assert(err, checker.IsNil)
  1095. c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent)
  1096. b.Close()
  1097. }
  1098. // #14640
  1099. func (s *DockerSuite) TestPostContainersStartWithLinksInHostConfig(c *check.C) {
  1100. // TODO Windows: Windows doesn't support supplying a hostconfig on start.
  1101. // An alternate test could be written to validate the negative testing aspect of this
  1102. testRequires(c, DaemonIsLinux)
  1103. name := "test-host-config-links"
  1104. dockerCmd(c, "run", "--name", "foo", "-d", "busybox", "top")
  1105. dockerCmd(c, "create", "--name", name, "--link", "foo:bar", "busybox", "top")
  1106. hc := inspectFieldJSON(c, name, "HostConfig")
  1107. config := `{"HostConfig":` + hc + `}`
  1108. res, b, err := sockRequestRaw("POST", "/containers/"+name+"/start", strings.NewReader(config), "application/json")
  1109. c.Assert(err, checker.IsNil)
  1110. c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent)
  1111. b.Close()
  1112. }
  1113. // #14640
  1114. func (s *DockerSuite) TestPostContainersStartWithLinksInHostConfigIdLinked(c *check.C) {
  1115. // Windows does not support links
  1116. testRequires(c, DaemonIsLinux)
  1117. name := "test-host-config-links"
  1118. out, _ := dockerCmd(c, "run", "--name", "link0", "-d", "busybox", "top")
  1119. id := strings.TrimSpace(out)
  1120. dockerCmd(c, "create", "--name", name, "--link", id, "busybox", "top")
  1121. hc := inspectFieldJSON(c, name, "HostConfig")
  1122. config := `{"HostConfig":` + hc + `}`
  1123. res, b, err := sockRequestRaw("POST", "/containers/"+name+"/start", strings.NewReader(config), "application/json")
  1124. c.Assert(err, checker.IsNil)
  1125. c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent)
  1126. b.Close()
  1127. }
  1128. // #14915
  1129. func (s *DockerSuite) TestContainerApiCreateNoHostConfig118(c *check.C) {
  1130. config := struct {
  1131. Image string
  1132. }{"busybox"}
  1133. status, _, err := sockRequest("POST", "/v1.18/containers/create", config)
  1134. c.Assert(err, checker.IsNil)
  1135. c.Assert(status, checker.Equals, http.StatusCreated)
  1136. }
  1137. // Ensure an error occurs when you have a container read-only rootfs but you
  1138. // extract an archive to a symlink in a writable volume which points to a
  1139. // directory outside of the volume.
  1140. func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *check.C) {
  1141. // Windows does not support read-only rootfs
  1142. // Requires local volume mount bind.
  1143. // --read-only + userns has remount issues
  1144. testRequires(c, SameHostDaemon, NotUserNamespace, DaemonIsLinux)
  1145. testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-")
  1146. defer os.RemoveAll(testVol)
  1147. makeTestContentInDir(c, testVol)
  1148. cID := makeTestContainer(c, testContainerOptions{
  1149. readOnly: true,
  1150. volumes: defaultVolumes(testVol), // Our bind mount is at /vol2
  1151. })
  1152. defer deleteContainer(cID)
  1153. // Attempt to extract to a symlink in the volume which points to a
  1154. // directory outside the volume. This should cause an error because the
  1155. // rootfs is read-only.
  1156. query := make(url.Values, 1)
  1157. query.Set("path", "/vol2/symlinkToAbsDir")
  1158. urlPath := fmt.Sprintf("/v1.20/containers/%s/archive?%s", cID, query.Encode())
  1159. statusCode, body, err := sockRequest("PUT", urlPath, nil)
  1160. c.Assert(err, checker.IsNil)
  1161. if !isCpCannotCopyReadOnly(fmt.Errorf(string(body))) {
  1162. c.Fatalf("expected ErrContainerRootfsReadonly error, but got %d: %s", statusCode, string(body))
  1163. }
  1164. }
  1165. func (s *DockerSuite) TestContainerApiGetContainersJSONEmpty(c *check.C) {
  1166. status, body, err := sockRequest("GET", "/containers/json?all=1", nil)
  1167. c.Assert(err, checker.IsNil)
  1168. c.Assert(status, checker.Equals, http.StatusOK)
  1169. c.Assert(string(body), checker.Equals, "[]\n")
  1170. }
  1171. func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C) {
  1172. // Not supported on Windows
  1173. testRequires(c, DaemonIsLinux)
  1174. c1 := struct {
  1175. Image string
  1176. CpusetCpus string
  1177. }{"busybox", "1-42,,"}
  1178. name := "wrong-cpuset-cpus"
  1179. status, body, err := sockRequest("POST", "/containers/create?name="+name, c1)
  1180. c.Assert(err, checker.IsNil)
  1181. c.Assert(status, checker.Equals, http.StatusInternalServerError)
  1182. expected := "Invalid value 1-42,, for cpuset cpus.\n"
  1183. c.Assert(string(body), checker.Equals, expected)
  1184. c2 := struct {
  1185. Image string
  1186. CpusetMems string
  1187. }{"busybox", "42-3,1--"}
  1188. name = "wrong-cpuset-mems"
  1189. status, body, err = sockRequest("POST", "/containers/create?name="+name, c2)
  1190. c.Assert(err, checker.IsNil)
  1191. c.Assert(status, checker.Equals, http.StatusInternalServerError)
  1192. expected = "Invalid value 42-3,1-- for cpuset mems.\n"
  1193. c.Assert(string(body), checker.Equals, expected)
  1194. }
  1195. func (s *DockerSuite) TestStartWithNilDNS(c *check.C) {
  1196. // TODO Windows: Add once DNS is supported
  1197. testRequires(c, DaemonIsLinux)
  1198. out, _ := dockerCmd(c, "create", "busybox")
  1199. containerID := strings.TrimSpace(out)
  1200. config := `{"HostConfig": {"Dns": null}}`
  1201. res, b, err := sockRequestRaw("POST", "/containers/"+containerID+"/start", strings.NewReader(config), "application/json")
  1202. c.Assert(err, checker.IsNil)
  1203. c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent)
  1204. b.Close()
  1205. dns := inspectFieldJSON(c, containerID, "HostConfig.Dns")
  1206. c.Assert(dns, checker.Equals, "[]")
  1207. }
  1208. func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) {
  1209. // ShmSize is not supported on Windows
  1210. testRequires(c, DaemonIsLinux)
  1211. config := map[string]interface{}{
  1212. "Image": "busybox",
  1213. "HostConfig": map[string]interface{}{"ShmSize": -1},
  1214. }
  1215. status, body, err := sockRequest("POST", "/containers/create", config)
  1216. c.Assert(err, check.IsNil)
  1217. c.Assert(status, check.Equals, http.StatusInternalServerError)
  1218. c.Assert(string(body), checker.Contains, "SHM size must be greater then 0")
  1219. }
  1220. func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.C) {
  1221. // ShmSize is not supported on Windows
  1222. testRequires(c, DaemonIsLinux)
  1223. var defaultSHMSize int64 = 67108864
  1224. config := map[string]interface{}{
  1225. "Image": "busybox",
  1226. "Cmd": "mount",
  1227. }
  1228. status, body, err := sockRequest("POST", "/containers/create", config)
  1229. c.Assert(err, check.IsNil)
  1230. c.Assert(status, check.Equals, http.StatusCreated)
  1231. var container types.ContainerCreateResponse
  1232. c.Assert(json.Unmarshal(body, &container), check.IsNil)
  1233. status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  1234. c.Assert(err, check.IsNil)
  1235. c.Assert(status, check.Equals, http.StatusOK)
  1236. var containerJSON types.ContainerJSON
  1237. c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
  1238. c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, defaultSHMSize)
  1239. out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1240. shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
  1241. if !shmRegexp.MatchString(out) {
  1242. c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
  1243. }
  1244. }
  1245. func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) {
  1246. // ShmSize is not supported on Windows
  1247. testRequires(c, DaemonIsLinux)
  1248. config := map[string]interface{}{
  1249. "Image": "busybox",
  1250. "HostConfig": map[string]interface{}{},
  1251. "Cmd": "mount",
  1252. }
  1253. status, body, err := sockRequest("POST", "/containers/create", config)
  1254. c.Assert(err, check.IsNil)
  1255. c.Assert(status, check.Equals, http.StatusCreated)
  1256. var container types.ContainerCreateResponse
  1257. c.Assert(json.Unmarshal(body, &container), check.IsNil)
  1258. status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  1259. c.Assert(err, check.IsNil)
  1260. c.Assert(status, check.Equals, http.StatusOK)
  1261. var containerJSON types.ContainerJSON
  1262. c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
  1263. c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(67108864))
  1264. out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1265. shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
  1266. if !shmRegexp.MatchString(out) {
  1267. c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
  1268. }
  1269. }
  1270. func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) {
  1271. // ShmSize is not supported on Windows
  1272. testRequires(c, DaemonIsLinux)
  1273. config := map[string]interface{}{
  1274. "Image": "busybox",
  1275. "Cmd": "mount",
  1276. "HostConfig": map[string]interface{}{"ShmSize": 1073741824},
  1277. }
  1278. status, body, err := sockRequest("POST", "/containers/create", config)
  1279. c.Assert(err, check.IsNil)
  1280. c.Assert(status, check.Equals, http.StatusCreated)
  1281. var container types.ContainerCreateResponse
  1282. c.Assert(json.Unmarshal(body, &container), check.IsNil)
  1283. status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  1284. c.Assert(err, check.IsNil)
  1285. c.Assert(status, check.Equals, http.StatusOK)
  1286. var containerJSON types.ContainerJSON
  1287. c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
  1288. c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(1073741824))
  1289. out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1290. shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`)
  1291. if !shmRegex.MatchString(out) {
  1292. c.Fatalf("Expected shm of 1GB in mount command, got %v", out)
  1293. }
  1294. }
  1295. func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *check.C) {
  1296. // Swappiness is not supported on Windows
  1297. testRequires(c, DaemonIsLinux)
  1298. config := map[string]interface{}{
  1299. "Image": "busybox",
  1300. }
  1301. status, body, err := sockRequest("POST", "/containers/create", config)
  1302. c.Assert(err, check.IsNil)
  1303. c.Assert(status, check.Equals, http.StatusCreated)
  1304. var container types.ContainerCreateResponse
  1305. c.Assert(json.Unmarshal(body, &container), check.IsNil)
  1306. status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  1307. c.Assert(err, check.IsNil)
  1308. c.Assert(status, check.Equals, http.StatusOK)
  1309. var containerJSON types.ContainerJSON
  1310. c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
  1311. c.Assert(*containerJSON.HostConfig.MemorySwappiness, check.Equals, int64(-1))
  1312. }
  1313. // check validation is done daemon side and not only in cli
  1314. func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *check.C) {
  1315. // OomScoreAdj is not supported on Windows
  1316. testRequires(c, DaemonIsLinux)
  1317. config := struct {
  1318. Image string
  1319. OomScoreAdj int
  1320. }{"busybox", 1001}
  1321. name := "oomscoreadj-over"
  1322. status, b, err := sockRequest("POST", "/containers/create?name="+name, config)
  1323. c.Assert(err, check.IsNil)
  1324. c.Assert(status, check.Equals, http.StatusInternalServerError)
  1325. expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]."
  1326. if !strings.Contains(string(b), expected) {
  1327. c.Fatalf("Expected output to contain %q, got %q", expected, string(b))
  1328. }
  1329. config = struct {
  1330. Image string
  1331. OomScoreAdj int
  1332. }{"busybox", -1001}
  1333. name = "oomscoreadj-low"
  1334. status, b, err = sockRequest("POST", "/containers/create?name="+name, config)
  1335. c.Assert(err, check.IsNil)
  1336. c.Assert(status, check.Equals, http.StatusInternalServerError)
  1337. expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]."
  1338. if !strings.Contains(string(b), expected) {
  1339. c.Fatalf("Expected output to contain %q, got %q", expected, string(b))
  1340. }
  1341. }