buildfile_test.go 22 KB

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