buildfile_test.go 24 KB

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