buildfile_test.go 22 KB

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