docker_api_containers_test.go 50 KB

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