buildfile_test.go 24 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028
  1. package docker
  2. import (
  3. "fmt"
  4. "github.com/dotcloud/docker/archive"
  5. "github.com/dotcloud/docker/engine"
  6. "github.com/dotcloud/docker/image"
  7. "github.com/dotcloud/docker/nat"
  8. "github.com/dotcloud/docker/server"
  9. "github.com/dotcloud/docker/utils"
  10. "io/ioutil"
  11. "net"
  12. "net/http"
  13. "net/http/httptest"
  14. "strings"
  15. "testing"
  16. )
  17. // A testContextTemplate describes a build context and how to test it
  18. type testContextTemplate struct {
  19. // Contents of the Dockerfile
  20. dockerfile string
  21. // Additional files in the context, eg [][2]string{"./passwd", "gordon"}
  22. files [][2]string
  23. // Additional remote files to host on a local HTTP server.
  24. remoteFiles [][2]string
  25. }
  26. func (context testContextTemplate) Archive(dockerfile string, t *testing.T) archive.Archive {
  27. input := []string{"Dockerfile", dockerfile}
  28. for _, pair := range context.files {
  29. input = append(input, pair[0], pair[1])
  30. }
  31. a, err := archive.Generate(input...)
  32. if err != nil {
  33. t.Fatal(err)
  34. }
  35. return a
  36. }
  37. // A table of all the contexts to build and test.
  38. // A new docker runtime will be created and torn down for each context.
  39. var testContexts = []testContextTemplate{
  40. {
  41. `
  42. from {IMAGE}
  43. run sh -c 'echo root:testpass > /tmp/passwd'
  44. run mkdir -p /var/run/sshd
  45. run [ "$(cat /tmp/passwd)" = "root:testpass" ]
  46. run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
  47. `,
  48. nil,
  49. nil,
  50. },
  51. // Exactly the same as above, except uses a line split with a \ to test
  52. // multiline support.
  53. {
  54. `
  55. from {IMAGE}
  56. run sh -c 'echo root:testpass \
  57. > /tmp/passwd'
  58. run mkdir -p /var/run/sshd
  59. run [ "$(cat /tmp/passwd)" = "root:testpass" ]
  60. run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
  61. `,
  62. nil,
  63. nil,
  64. },
  65. // Line containing literal "\n"
  66. {
  67. `
  68. from {IMAGE}
  69. run sh -c 'echo root:testpass > /tmp/passwd'
  70. run echo "foo \n bar"; echo "baz"
  71. run mkdir -p /var/run/sshd
  72. run [ "$(cat /tmp/passwd)" = "root:testpass" ]
  73. run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
  74. `,
  75. nil,
  76. nil,
  77. },
  78. {
  79. `
  80. from {IMAGE}
  81. add foo /usr/lib/bla/bar
  82. run [ "$(cat /usr/lib/bla/bar)" = 'hello' ]
  83. add http://{SERVERADDR}/baz /usr/lib/baz/quux
  84. run [ "$(cat /usr/lib/baz/quux)" = 'world!' ]
  85. `,
  86. [][2]string{{"foo", "hello"}},
  87. [][2]string{{"/baz", "world!"}},
  88. },
  89. {
  90. `
  91. from {IMAGE}
  92. add f /
  93. run [ "$(cat /f)" = "hello" ]
  94. add f /abc
  95. run [ "$(cat /abc)" = "hello" ]
  96. add f /x/y/z
  97. run [ "$(cat /x/y/z)" = "hello" ]
  98. add f /x/y/d/
  99. run [ "$(cat /x/y/d/f)" = "hello" ]
  100. add d /
  101. run [ "$(cat /ga)" = "bu" ]
  102. add d /somewhere
  103. run [ "$(cat /somewhere/ga)" = "bu" ]
  104. add d /anotherplace/
  105. run [ "$(cat /anotherplace/ga)" = "bu" ]
  106. add d /somewheeeere/over/the/rainbooow
  107. run [ "$(cat /somewheeeere/over/the/rainbooow/ga)" = "bu" ]
  108. `,
  109. [][2]string{
  110. {"f", "hello"},
  111. {"d/ga", "bu"},
  112. },
  113. nil,
  114. },
  115. {
  116. `
  117. from {IMAGE}
  118. add http://{SERVERADDR}/x /a/b/c
  119. run [ "$(cat /a/b/c)" = "hello" ]
  120. add http://{SERVERADDR}/x?foo=bar /
  121. run [ "$(cat /x)" = "hello" ]
  122. add http://{SERVERADDR}/x /d/
  123. run [ "$(cat /d/x)" = "hello" ]
  124. add http://{SERVERADDR} /e
  125. run [ "$(cat /e)" = "blah" ]
  126. `,
  127. nil,
  128. [][2]string{{"/x", "hello"}, {"/", "blah"}},
  129. },
  130. // Comments, shebangs, and executability, oh my!
  131. {
  132. `
  133. FROM {IMAGE}
  134. # This is an ordinary comment.
  135. RUN { echo '#!/bin/sh'; echo 'echo hello world'; } > /hello.sh
  136. RUN [ ! -x /hello.sh ]
  137. RUN chmod +x /hello.sh
  138. RUN [ -x /hello.sh ]
  139. RUN [ "$(cat /hello.sh)" = $'#!/bin/sh\necho hello world' ]
  140. RUN [ "$(/hello.sh)" = "hello world" ]
  141. `,
  142. nil,
  143. nil,
  144. },
  145. // Users and groups
  146. {
  147. `
  148. FROM {IMAGE}
  149. # Make sure our defaults work
  150. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)" = '0:0/root:root' ]
  151. # TODO decide if "args.user = strconv.Itoa(syscall.Getuid())" is acceptable behavior for changeUser in sysvinit instead of "return nil" when "USER" isn't specified (so that we get the proper group list even if that is the empty list, even in the default case of not supplying an explicit USER to run as, which implies USER 0)
  152. USER root
  153. RUN [ "$(id -G):$(id -Gn)" = '0:root' ]
  154. # Setup dockerio user and group
  155. RUN echo 'dockerio:x:1000:1000::/bin:/bin/false' >> /etc/passwd
  156. RUN echo 'dockerio:x:1000:' >> /etc/group
  157. # Make sure we can switch to our user and all the information is exactly as we expect it to be
  158. USER dockerio
  159. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1000:1000/dockerio:dockerio/1000:dockerio' ]
  160. # Switch back to root and double check that worked exactly as we might expect it to
  161. USER root
  162. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '0:0/root:root/0:root' ]
  163. # Add a "supplementary" group for our dockerio user
  164. RUN echo 'supplementary:x:1001:dockerio' >> /etc/group
  165. # ... and then go verify that we get it like we expect
  166. USER dockerio
  167. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1000:1000/dockerio:dockerio/1000 1001:dockerio supplementary' ]
  168. USER 1000
  169. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1000:1000/dockerio:dockerio/1000 1001:dockerio supplementary' ]
  170. # super test the new "user:group" syntax
  171. USER dockerio:dockerio
  172. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1000:1000/dockerio:dockerio/1000:dockerio' ]
  173. USER 1000:dockerio
  174. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1000:1000/dockerio:dockerio/1000:dockerio' ]
  175. USER dockerio:1000
  176. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1000:1000/dockerio:dockerio/1000:dockerio' ]
  177. USER 1000:1000
  178. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1000:1000/dockerio:dockerio/1000:dockerio' ]
  179. USER dockerio:supplementary
  180. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1000:1001/dockerio:supplementary/1001:supplementary' ]
  181. USER dockerio:1001
  182. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1000:1001/dockerio:supplementary/1001:supplementary' ]
  183. USER 1000:supplementary
  184. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1000:1001/dockerio:supplementary/1001:supplementary' ]
  185. USER 1000:1001
  186. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1000:1001/dockerio:supplementary/1001:supplementary' ]
  187. # make sure unknown uid/gid still works properly
  188. USER 1042:1043
  189. RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1042:1043/1042:1043/1043:1043' ]
  190. `,
  191. nil,
  192. nil,
  193. },
  194. // Environment variable
  195. {
  196. `
  197. from {IMAGE}
  198. env FOO BAR
  199. run [ "$FOO" = "BAR" ]
  200. `,
  201. nil,
  202. nil,
  203. },
  204. // Environment overwriting
  205. {
  206. `
  207. from {IMAGE}
  208. env FOO BAR
  209. run [ "$FOO" = "BAR" ]
  210. env FOO BAZ
  211. run [ "$FOO" = "BAZ" ]
  212. `,
  213. nil,
  214. nil,
  215. },
  216. {
  217. `
  218. from {IMAGE}
  219. ENTRYPOINT /bin/echo
  220. CMD Hello world
  221. `,
  222. nil,
  223. nil,
  224. },
  225. {
  226. `
  227. from {IMAGE}
  228. VOLUME /test
  229. CMD Hello world
  230. `,
  231. nil,
  232. nil,
  233. },
  234. {
  235. `
  236. from {IMAGE}
  237. env FOO /foo/baz
  238. env BAR /bar
  239. env BAZ $BAR
  240. env FOOPATH $PATH:$FOO
  241. run [ "$BAR" = "$BAZ" ]
  242. run [ "$FOOPATH" = "$PATH:/foo/baz" ]
  243. `,
  244. nil,
  245. nil,
  246. },
  247. {
  248. `
  249. from {IMAGE}
  250. env FOO /bar
  251. env TEST testdir
  252. env BAZ /foobar
  253. add testfile $BAZ/
  254. add $TEST $FOO
  255. run [ "$(cat /foobar/testfile)" = "test1" ]
  256. run [ "$(cat /bar/withfile)" = "test2" ]
  257. `,
  258. [][2]string{
  259. {"testfile", "test1"},
  260. {"testdir/withfile", "test2"},
  261. },
  262. nil,
  263. },
  264. // JSON!
  265. {
  266. `
  267. FROM {IMAGE}
  268. RUN ["/bin/echo","hello","world"]
  269. CMD ["/bin/true"]
  270. ENTRYPOINT ["/bin/echo","your command -->"]
  271. `,
  272. nil,
  273. nil,
  274. },
  275. {
  276. `
  277. FROM {IMAGE}
  278. ADD test /test
  279. RUN ["chmod","+x","/test"]
  280. RUN ["/test"]
  281. RUN [ "$(cat /testfile)" = 'test!' ]
  282. `,
  283. [][2]string{
  284. {"test", "#!/bin/sh\necho 'test!' > /testfile"},
  285. },
  286. nil,
  287. },
  288. {
  289. `
  290. FROM {IMAGE}
  291. # what \
  292. RUN mkdir /testing
  293. RUN touch /testing/other
  294. `,
  295. nil,
  296. nil,
  297. },
  298. }
  299. // FIXME: test building with 2 successive overlapping ADD commands
  300. func constructDockerfile(template string, ip net.IP, port string) string {
  301. serverAddr := fmt.Sprintf("%s:%s", ip, port)
  302. replacer := strings.NewReplacer("{IMAGE}", unitTestImageID, "{SERVERADDR}", serverAddr)
  303. return replacer.Replace(template)
  304. }
  305. func mkTestingFileServer(files [][2]string) (*httptest.Server, error) {
  306. mux := http.NewServeMux()
  307. for _, file := range files {
  308. name, contents := file[0], file[1]
  309. mux.HandleFunc(name, func(w http.ResponseWriter, r *http.Request) {
  310. w.Write([]byte(contents))
  311. })
  312. }
  313. // This is how httptest.NewServer sets up a net.Listener, except that our listener must accept remote
  314. // connections (from the container).
  315. listener, err := net.Listen("tcp", ":0")
  316. if err != nil {
  317. return nil, err
  318. }
  319. s := httptest.NewUnstartedServer(mux)
  320. s.Listener = listener
  321. s.Start()
  322. return s, nil
  323. }
  324. func TestBuild(t *testing.T) {
  325. for _, ctx := range testContexts {
  326. _, err := buildImage(ctx, t, nil, true)
  327. if err != nil {
  328. t.Fatal(err)
  329. }
  330. }
  331. }
  332. func buildImage(context testContextTemplate, t *testing.T, eng *engine.Engine, useCache bool) (*image.Image, error) {
  333. if eng == nil {
  334. eng = NewTestEngine(t)
  335. runtime := mkDaemonFromEngine(eng, t)
  336. // FIXME: we might not need runtime, why not simply nuke
  337. // the engine?
  338. defer nuke(runtime)
  339. }
  340. srv := mkServerFromEngine(eng, t)
  341. httpServer, err := mkTestingFileServer(context.remoteFiles)
  342. if err != nil {
  343. t.Fatal(err)
  344. }
  345. defer httpServer.Close()
  346. idx := strings.LastIndex(httpServer.URL, ":")
  347. if idx < 0 {
  348. t.Fatalf("could not get port from test http server address %s", httpServer.URL)
  349. }
  350. port := httpServer.URL[idx+1:]
  351. iIP := eng.Hack_GetGlobalVar("httpapi.bridgeIP")
  352. if iIP == nil {
  353. t.Fatal("Legacy bridgeIP field not set in engine")
  354. }
  355. ip, ok := iIP.(net.IP)
  356. if !ok {
  357. panic("Legacy bridgeIP field in engine does not cast to net.IP")
  358. }
  359. dockerfile := constructDockerfile(context.dockerfile, ip, port)
  360. buildfile := server.NewBuildFile(srv, ioutil.Discard, ioutil.Discard, false, useCache, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil)
  361. id, err := buildfile.Build(context.Archive(dockerfile, t))
  362. if err != nil {
  363. return nil, err
  364. }
  365. return srv.ImageInspect(id)
  366. }
  367. func TestVolume(t *testing.T) {
  368. img, err := buildImage(testContextTemplate{`
  369. from {IMAGE}
  370. volume /test
  371. cmd Hello world
  372. `, nil, nil}, t, nil, true)
  373. if err != nil {
  374. t.Fatal(err)
  375. }
  376. if len(img.Config.Volumes) == 0 {
  377. t.Fail()
  378. }
  379. for key := range img.Config.Volumes {
  380. if key != "/test" {
  381. t.Fail()
  382. }
  383. }
  384. }
  385. func TestBuildMaintainer(t *testing.T) {
  386. img, err := buildImage(testContextTemplate{`
  387. from {IMAGE}
  388. maintainer dockerio
  389. `, nil, nil}, t, nil, true)
  390. if err != nil {
  391. t.Fatal(err)
  392. }
  393. if img.Author != "dockerio" {
  394. t.Fail()
  395. }
  396. }
  397. func TestBuildUser(t *testing.T) {
  398. img, err := buildImage(testContextTemplate{`
  399. from {IMAGE}
  400. user dockerio
  401. `, nil, nil}, t, nil, true)
  402. if err != nil {
  403. t.Fatal(err)
  404. }
  405. if img.Config.User != "dockerio" {
  406. t.Fail()
  407. }
  408. }
  409. func TestBuildRelativeWorkdir(t *testing.T) {
  410. img, err := buildImage(testContextTemplate{`
  411. FROM {IMAGE}
  412. RUN [ "$PWD" = '/' ]
  413. WORKDIR test1
  414. RUN [ "$PWD" = '/test1' ]
  415. WORKDIR /test2
  416. RUN [ "$PWD" = '/test2' ]
  417. WORKDIR test3
  418. RUN [ "$PWD" = '/test2/test3' ]
  419. `, nil, nil}, t, nil, true)
  420. if err != nil {
  421. t.Fatal(err)
  422. }
  423. if img.Config.WorkingDir != "/test2/test3" {
  424. t.Fatalf("Expected workdir to be '/test2/test3', received '%s'", img.Config.WorkingDir)
  425. }
  426. }
  427. func TestBuildEnv(t *testing.T) {
  428. img, err := buildImage(testContextTemplate{`
  429. from {IMAGE}
  430. env port 4243
  431. `,
  432. nil, nil}, t, nil, true)
  433. if err != nil {
  434. t.Fatal(err)
  435. }
  436. hasEnv := false
  437. for _, envVar := range img.Config.Env {
  438. if envVar == "port=4243" {
  439. hasEnv = true
  440. break
  441. }
  442. }
  443. if !hasEnv {
  444. t.Fail()
  445. }
  446. }
  447. func TestBuildCmd(t *testing.T) {
  448. img, err := buildImage(testContextTemplate{`
  449. from {IMAGE}
  450. cmd ["/bin/echo", "Hello World"]
  451. `,
  452. nil, nil}, t, nil, true)
  453. if err != nil {
  454. t.Fatal(err)
  455. }
  456. if img.Config.Cmd[0] != "/bin/echo" {
  457. t.Log(img.Config.Cmd[0])
  458. t.Fail()
  459. }
  460. if img.Config.Cmd[1] != "Hello World" {
  461. t.Log(img.Config.Cmd[1])
  462. t.Fail()
  463. }
  464. }
  465. func TestBuildExpose(t *testing.T) {
  466. img, err := buildImage(testContextTemplate{`
  467. from {IMAGE}
  468. expose 4243
  469. `,
  470. nil, nil}, t, nil, true)
  471. if err != nil {
  472. t.Fatal(err)
  473. }
  474. if _, exists := img.Config.ExposedPorts[nat.NewPort("tcp", "4243")]; !exists {
  475. t.Fail()
  476. }
  477. }
  478. func TestBuildEntrypoint(t *testing.T) {
  479. img, err := buildImage(testContextTemplate{`
  480. from {IMAGE}
  481. entrypoint ["/bin/echo"]
  482. `,
  483. nil, nil}, t, nil, true)
  484. if err != nil {
  485. t.Fatal(err)
  486. }
  487. if img.Config.Entrypoint[0] != "/bin/echo" {
  488. t.Log(img.Config.Entrypoint[0])
  489. t.Fail()
  490. }
  491. }
  492. // testing #1405 - config.Cmd does not get cleaned up if
  493. // utilizing cache
  494. func TestBuildEntrypointRunCleanup(t *testing.T) {
  495. eng := NewTestEngine(t)
  496. defer nuke(mkDaemonFromEngine(eng, t))
  497. img, err := buildImage(testContextTemplate{`
  498. from {IMAGE}
  499. run echo "hello"
  500. `,
  501. nil, nil}, t, eng, true)
  502. if err != nil {
  503. t.Fatal(err)
  504. }
  505. img, err = buildImage(testContextTemplate{`
  506. from {IMAGE}
  507. run echo "hello"
  508. add foo /foo
  509. entrypoint ["/bin/echo"]
  510. `,
  511. [][2]string{{"foo", "HEYO"}}, nil}, t, eng, true)
  512. if err != nil {
  513. t.Fatal(err)
  514. }
  515. if len(img.Config.Cmd) != 0 {
  516. t.Fail()
  517. }
  518. }
  519. func checkCacheBehavior(t *testing.T, template testContextTemplate, expectHit bool) (imageId string) {
  520. eng := NewTestEngine(t)
  521. defer nuke(mkDaemonFromEngine(eng, t))
  522. img, err := buildImage(template, t, eng, true)
  523. if err != nil {
  524. t.Fatal(err)
  525. }
  526. imageId = img.ID
  527. img, err = buildImage(template, t, eng, expectHit)
  528. if err != nil {
  529. t.Fatal(err)
  530. }
  531. if hit := imageId == img.ID; hit != expectHit {
  532. t.Fatalf("Cache misbehavior, got hit=%t, expected hit=%t: (first: %s, second %s)", hit, expectHit, imageId, img.ID)
  533. }
  534. return
  535. }
  536. func checkCacheBehaviorFromEngime(t *testing.T, template testContextTemplate, expectHit bool, eng *engine.Engine) (imageId string) {
  537. img, err := buildImage(template, t, eng, true)
  538. if err != nil {
  539. t.Fatal(err)
  540. }
  541. imageId = img.ID
  542. img, err = buildImage(template, t, eng, expectHit)
  543. if err != nil {
  544. t.Fatal(err)
  545. }
  546. if hit := imageId == img.ID; hit != expectHit {
  547. t.Fatalf("Cache misbehavior, got hit=%t, expected hit=%t: (first: %s, second %s)", hit, expectHit, imageId, img.ID)
  548. }
  549. return
  550. }
  551. func TestBuildImageWithCache(t *testing.T) {
  552. template := testContextTemplate{`
  553. from {IMAGE}
  554. maintainer dockerio
  555. `,
  556. nil, nil}
  557. checkCacheBehavior(t, template, true)
  558. }
  559. func TestBuildExposeWithCache(t *testing.T) {
  560. template := testContextTemplate{`
  561. from {IMAGE}
  562. maintainer dockerio
  563. expose 80
  564. run echo hello
  565. `,
  566. nil, nil}
  567. checkCacheBehavior(t, template, true)
  568. }
  569. func TestBuildImageWithoutCache(t *testing.T) {
  570. template := testContextTemplate{`
  571. from {IMAGE}
  572. maintainer dockerio
  573. `,
  574. nil, nil}
  575. checkCacheBehavior(t, template, false)
  576. }
  577. func TestBuildADDLocalFileWithCache(t *testing.T) {
  578. template := testContextTemplate{`
  579. from {IMAGE}
  580. maintainer dockerio
  581. run echo "first"
  582. add foo /usr/lib/bla/bar
  583. run [ "$(cat /usr/lib/bla/bar)" = "hello" ]
  584. run echo "second"
  585. add . /src/
  586. run [ "$(cat /src/foo)" = "hello" ]
  587. `,
  588. [][2]string{
  589. {"foo", "hello"},
  590. },
  591. nil}
  592. eng := NewTestEngine(t)
  593. defer nuke(mkDaemonFromEngine(eng, t))
  594. id1 := checkCacheBehaviorFromEngime(t, template, true, eng)
  595. template.files = append(template.files, [2]string{"bar", "hello2"})
  596. id2 := checkCacheBehaviorFromEngime(t, template, true, eng)
  597. if id1 == id2 {
  598. t.Fatal("The cache should have been invalided but hasn't.")
  599. }
  600. id3 := checkCacheBehaviorFromEngime(t, template, true, eng)
  601. if id2 != id3 {
  602. t.Fatal("The cache should have been used but hasn't.")
  603. }
  604. template.files[1][1] = "hello3"
  605. id4 := checkCacheBehaviorFromEngime(t, template, true, eng)
  606. if id3 == id4 {
  607. t.Fatal("The cache should have been invalided but hasn't.")
  608. }
  609. template.dockerfile += `
  610. add ./bar /src2/
  611. run ls /src2/bar
  612. `
  613. id5 := checkCacheBehaviorFromEngime(t, template, true, eng)
  614. if id4 == id5 {
  615. t.Fatal("The cache should have been invalided but hasn't.")
  616. }
  617. template.files[1][1] = "hello4"
  618. id6 := checkCacheBehaviorFromEngime(t, template, true, eng)
  619. if id5 == id6 {
  620. t.Fatal("The cache should have been invalided but hasn't.")
  621. }
  622. template.dockerfile += `
  623. add bar /src2/bar2
  624. add /bar /src2/bar3
  625. run ls /src2/bar2 /src2/bar3
  626. `
  627. id7 := checkCacheBehaviorFromEngime(t, template, true, eng)
  628. if id6 == id7 {
  629. t.Fatal("The cache should have been invalided but hasn't.")
  630. }
  631. template.files[1][1] = "hello5"
  632. id8 := checkCacheBehaviorFromEngime(t, template, true, eng)
  633. if id7 == id8 {
  634. t.Fatal("The cache should have been invalided but hasn't.")
  635. }
  636. }
  637. func TestBuildADDLocalFileWithoutCache(t *testing.T) {
  638. template := testContextTemplate{`
  639. from {IMAGE}
  640. maintainer dockerio
  641. run echo "first"
  642. add foo /usr/lib/bla/bar
  643. run echo "second"
  644. `,
  645. [][2]string{{"foo", "hello"}},
  646. nil}
  647. checkCacheBehavior(t, template, false)
  648. }
  649. func TestBuildADDCurrentDirectoryWithCache(t *testing.T) {
  650. template := testContextTemplate{`
  651. from {IMAGE}
  652. maintainer dockerio
  653. add . /usr/lib/bla
  654. `,
  655. nil, nil}
  656. checkCacheBehavior(t, template, true)
  657. }
  658. func TestBuildADDCurrentDirectoryWithoutCache(t *testing.T) {
  659. template := testContextTemplate{`
  660. from {IMAGE}
  661. maintainer dockerio
  662. add . /usr/lib/bla
  663. `,
  664. nil, nil}
  665. checkCacheBehavior(t, template, false)
  666. }
  667. func TestBuildADDRemoteFileWithCache(t *testing.T) {
  668. template := testContextTemplate{`
  669. from {IMAGE}
  670. maintainer dockerio
  671. run echo "first"
  672. add http://{SERVERADDR}/baz /usr/lib/baz/quux
  673. run echo "second"
  674. `,
  675. nil,
  676. [][2]string{{"/baz", "world!"}}}
  677. checkCacheBehavior(t, template, true)
  678. }
  679. func TestBuildADDRemoteFileWithoutCache(t *testing.T) {
  680. template := testContextTemplate{`
  681. from {IMAGE}
  682. maintainer dockerio
  683. run echo "first"
  684. add http://{SERVERADDR}/baz /usr/lib/baz/quux
  685. run echo "second"
  686. `,
  687. nil,
  688. [][2]string{{"/baz", "world!"}}}
  689. checkCacheBehavior(t, template, false)
  690. }
  691. func TestBuildADDLocalAndRemoteFilesWithCache(t *testing.T) {
  692. template := testContextTemplate{`
  693. from {IMAGE}
  694. maintainer dockerio
  695. run echo "first"
  696. add foo /usr/lib/bla/bar
  697. add http://{SERVERADDR}/baz /usr/lib/baz/quux
  698. run echo "second"
  699. `,
  700. [][2]string{{"foo", "hello"}},
  701. [][2]string{{"/baz", "world!"}}}
  702. checkCacheBehavior(t, template, true)
  703. }
  704. func TestBuildADDLocalAndRemoteFilesWithoutCache(t *testing.T) {
  705. template := testContextTemplate{`
  706. from {IMAGE}
  707. maintainer dockerio
  708. run echo "first"
  709. add foo /usr/lib/bla/bar
  710. add http://{SERVERADDR}/baz /usr/lib/baz/quux
  711. run echo "second"
  712. `,
  713. [][2]string{{"foo", "hello"}},
  714. [][2]string{{"/baz", "world!"}}}
  715. checkCacheBehavior(t, template, false)
  716. }
  717. func TestForbiddenContextPath(t *testing.T) {
  718. eng := NewTestEngine(t)
  719. defer nuke(mkDaemonFromEngine(eng, t))
  720. srv := mkServerFromEngine(eng, t)
  721. context := testContextTemplate{`
  722. from {IMAGE}
  723. maintainer dockerio
  724. add ../../ test/
  725. `,
  726. [][2]string{{"test.txt", "test1"}, {"other.txt", "other"}}, nil}
  727. httpServer, err := mkTestingFileServer(context.remoteFiles)
  728. if err != nil {
  729. t.Fatal(err)
  730. }
  731. defer httpServer.Close()
  732. idx := strings.LastIndex(httpServer.URL, ":")
  733. if idx < 0 {
  734. t.Fatalf("could not get port from test http server address %s", httpServer.URL)
  735. }
  736. port := httpServer.URL[idx+1:]
  737. iIP := eng.Hack_GetGlobalVar("httpapi.bridgeIP")
  738. if iIP == nil {
  739. t.Fatal("Legacy bridgeIP field not set in engine")
  740. }
  741. ip, ok := iIP.(net.IP)
  742. if !ok {
  743. panic("Legacy bridgeIP field in engine does not cast to net.IP")
  744. }
  745. dockerfile := constructDockerfile(context.dockerfile, ip, port)
  746. buildfile := server.NewBuildFile(srv, ioutil.Discard, ioutil.Discard, false, true, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil)
  747. _, err = buildfile.Build(context.Archive(dockerfile, t))
  748. if err == nil {
  749. t.Log("Error should not be nil")
  750. t.Fail()
  751. }
  752. if err.Error() != "Forbidden path outside the build context: ../../ (/)" {
  753. t.Logf("Error message is not expected: %s", err.Error())
  754. t.Fail()
  755. }
  756. }
  757. func TestBuildADDFileNotFound(t *testing.T) {
  758. eng := NewTestEngine(t)
  759. defer nuke(mkDaemonFromEngine(eng, t))
  760. context := testContextTemplate{`
  761. from {IMAGE}
  762. add foo /usr/local/bar
  763. `,
  764. nil, nil}
  765. httpServer, err := mkTestingFileServer(context.remoteFiles)
  766. if err != nil {
  767. t.Fatal(err)
  768. }
  769. defer httpServer.Close()
  770. idx := strings.LastIndex(httpServer.URL, ":")
  771. if idx < 0 {
  772. t.Fatalf("could not get port from test http server address %s", httpServer.URL)
  773. }
  774. port := httpServer.URL[idx+1:]
  775. iIP := eng.Hack_GetGlobalVar("httpapi.bridgeIP")
  776. if iIP == nil {
  777. t.Fatal("Legacy bridgeIP field not set in engine")
  778. }
  779. ip, ok := iIP.(net.IP)
  780. if !ok {
  781. panic("Legacy bridgeIP field in engine does not cast to net.IP")
  782. }
  783. dockerfile := constructDockerfile(context.dockerfile, ip, port)
  784. buildfile := server.NewBuildFile(mkServerFromEngine(eng, t), ioutil.Discard, ioutil.Discard, false, true, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil)
  785. _, err = buildfile.Build(context.Archive(dockerfile, t))
  786. if err == nil {
  787. t.Log("Error should not be nil")
  788. t.Fail()
  789. }
  790. if err.Error() != "foo: no such file or directory" {
  791. t.Logf("Error message is not expected: %s", err.Error())
  792. t.Fail()
  793. }
  794. }
  795. func TestBuildInheritance(t *testing.T) {
  796. eng := NewTestEngine(t)
  797. defer nuke(mkDaemonFromEngine(eng, t))
  798. img, err := buildImage(testContextTemplate{`
  799. from {IMAGE}
  800. expose 4243
  801. `,
  802. nil, nil}, t, eng, true)
  803. if err != nil {
  804. t.Fatal(err)
  805. }
  806. img2, _ := buildImage(testContextTemplate{fmt.Sprintf(`
  807. from %s
  808. entrypoint ["/bin/echo"]
  809. `, img.ID),
  810. nil, nil}, t, eng, true)
  811. if err != nil {
  812. t.Fatal(err)
  813. }
  814. // from child
  815. if img2.Config.Entrypoint[0] != "/bin/echo" {
  816. t.Fail()
  817. }
  818. // from parent
  819. if _, exists := img.Config.ExposedPorts[nat.NewPort("tcp", "4243")]; !exists {
  820. t.Fail()
  821. }
  822. }
  823. func TestBuildFails(t *testing.T) {
  824. _, err := buildImage(testContextTemplate{`
  825. from {IMAGE}
  826. run sh -c "exit 23"
  827. `,
  828. nil, nil}, t, nil, true)
  829. if err == nil {
  830. t.Fatal("Error should not be nil")
  831. }
  832. sterr, ok := err.(*utils.JSONError)
  833. if !ok {
  834. t.Fatalf("Error should be utils.JSONError")
  835. }
  836. if sterr.Code != 23 {
  837. t.Fatalf("StatusCode %d unexpected, should be 23", sterr.Code)
  838. }
  839. }
  840. func TestBuildFailsDockerfileEmpty(t *testing.T) {
  841. _, err := buildImage(testContextTemplate{``, nil, nil}, t, nil, true)
  842. if err != server.ErrDockerfileEmpty {
  843. t.Fatal("Expected: %v, got: %v", server.ErrDockerfileEmpty, err)
  844. }
  845. }
  846. func TestBuildOnBuildTrigger(t *testing.T) {
  847. _, err := buildImage(testContextTemplate{`
  848. from {IMAGE}
  849. onbuild run echo here is the trigger
  850. onbuild run touch foobar
  851. `,
  852. nil, nil,
  853. },
  854. t, nil, true,
  855. )
  856. if err != nil {
  857. t.Fatal(err)
  858. }
  859. // FIXME: test that the 'foobar' file was created in the final build.
  860. }
  861. func TestBuildOnBuildForbiddenChainedTrigger(t *testing.T) {
  862. _, err := buildImage(testContextTemplate{`
  863. from {IMAGE}
  864. onbuild onbuild run echo test
  865. `,
  866. nil, nil,
  867. },
  868. t, nil, true,
  869. )
  870. if err == nil {
  871. t.Fatal("Error should not be nil")
  872. }
  873. }
  874. func TestBuildOnBuildForbiddenFromTrigger(t *testing.T) {
  875. _, err := buildImage(testContextTemplate{`
  876. from {IMAGE}
  877. onbuild from {IMAGE}
  878. `,
  879. nil, nil,
  880. },
  881. t, nil, true,
  882. )
  883. if err == nil {
  884. t.Fatal("Error should not be nil")
  885. }
  886. }
  887. func TestBuildOnBuildForbiddenMaintainerTrigger(t *testing.T) {
  888. _, err := buildImage(testContextTemplate{`
  889. from {IMAGE}
  890. onbuild maintainer test
  891. `,
  892. nil, nil,
  893. },
  894. t, nil, true,
  895. )
  896. if err == nil {
  897. t.Fatal("Error should not be nil")
  898. }
  899. }
  900. // gh #2446
  901. func TestBuildAddToSymlinkDest(t *testing.T) {
  902. eng := NewTestEngine(t)
  903. defer nuke(mkDaemonFromEngine(eng, t))
  904. _, err := buildImage(testContextTemplate{`
  905. from {IMAGE}
  906. run mkdir /foo
  907. run ln -s /foo /bar
  908. add foo /bar/
  909. run stat /bar/foo
  910. `,
  911. [][2]string{{"foo", "HEYO"}}, nil}, t, eng, true)
  912. if err != nil {
  913. t.Fatal(err)
  914. }
  915. }