docker_api_containers_test.go 60 KB

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