docker_api_containers_test.go 66 KB

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