docker_api_containers_test.go 65 KB

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