docker_api_containers_test.go 65 KB

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