docker_api_containers_test.go 65 KB

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