docker_cli_build_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "os/exec"
  6. "path/filepath"
  7. "strings"
  8. "testing"
  9. "time"
  10. )
  11. func TestBuildCacheADD(t *testing.T) {
  12. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestBuildCacheADD", "1")
  13. buildCmd := exec.Command(dockerBinary, "build", "-t", "testcacheadd1", ".")
  14. buildCmd.Dir = buildDirectory
  15. exitCode, err := runCommand(buildCmd)
  16. errorOut(err, t, fmt.Sprintf("build failed to complete: %v", err))
  17. if err != nil || exitCode != 0 {
  18. t.Fatal("failed to build the image")
  19. }
  20. buildDirectory = filepath.Join(workingDirectory, "build_tests", "TestBuildCacheADD", "2")
  21. buildCmd = exec.Command(dockerBinary, "build", "-t", "testcacheadd2", ".")
  22. buildCmd.Dir = buildDirectory
  23. out, exitCode, err := runCommandWithOutput(buildCmd)
  24. errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err))
  25. if err != nil || exitCode != 0 {
  26. t.Fatal("failed to build the image")
  27. }
  28. if strings.Contains(out, "Using cache") {
  29. t.Fatal("2nd build used cache on ADD, it shouldn't")
  30. }
  31. deleteImages("testcacheadd1")
  32. deleteImages("testcacheadd2")
  33. logDone("build - build two images with ADD")
  34. }
  35. func TestBuildSixtySteps(t *testing.T) {
  36. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestBuildSixtySteps")
  37. buildCmd := exec.Command(dockerBinary, "build", "-t", "foobuildsixtysteps", ".")
  38. buildCmd.Dir = buildDirectory
  39. out, exitCode, err := runCommandWithOutput(buildCmd)
  40. errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err))
  41. if err != nil || exitCode != 0 {
  42. t.Fatal("failed to build the image")
  43. }
  44. deleteImages("foobuildsixtysteps")
  45. logDone("build - build an image with sixty build steps")
  46. }
  47. func TestAddSingleFileToRoot(t *testing.T) {
  48. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd", "SingleFileToRoot")
  49. f, err := os.OpenFile(filepath.Join(buildDirectory, "test_file"), os.O_CREATE, 0644)
  50. if err != nil {
  51. t.Fatal(err)
  52. }
  53. f.Close()
  54. buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", ".")
  55. buildCmd.Dir = buildDirectory
  56. out, exitCode, err := runCommandWithOutput(buildCmd)
  57. errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err))
  58. if err != nil || exitCode != 0 {
  59. t.Fatal("failed to build the image")
  60. }
  61. deleteImages("testaddimg")
  62. logDone("build - add single file to root")
  63. }
  64. // Issue #3960: "ADD src ." hangs
  65. func TestAddSingleFileToWorkdir(t *testing.T) {
  66. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd", "SingleFileToWorkdir")
  67. f, err := os.OpenFile(filepath.Join(buildDirectory, "test_file"), os.O_CREATE, 0644)
  68. if err != nil {
  69. t.Fatal(err)
  70. }
  71. f.Close()
  72. buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", ".")
  73. buildCmd.Dir = buildDirectory
  74. done := make(chan error)
  75. go func() {
  76. out, exitCode, err := runCommandWithOutput(buildCmd)
  77. if err != nil || exitCode != 0 {
  78. done <- fmt.Errorf("build failed to complete: %s %v", out, err)
  79. return
  80. }
  81. done <- nil
  82. }()
  83. select {
  84. case <-time.After(5 * time.Second):
  85. if err := buildCmd.Process.Kill(); err != nil {
  86. fmt.Printf("could not kill build (pid=%d): %v\n", buildCmd.Process.Pid, err)
  87. }
  88. t.Fatal("build timed out")
  89. case err := <-done:
  90. if err != nil {
  91. t.Fatal(err)
  92. }
  93. }
  94. deleteImages("testaddimg")
  95. logDone("build - add single file to workdir")
  96. }
  97. func TestAddSingleFileToExistDir(t *testing.T) {
  98. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd")
  99. buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "SingleFileToExistDir")
  100. buildCmd.Dir = buildDirectory
  101. out, exitCode, err := runCommandWithOutput(buildCmd)
  102. errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err))
  103. if err != nil || exitCode != 0 {
  104. t.Fatal("failed to build the image")
  105. }
  106. deleteImages("testaddimg")
  107. logDone("build - add single file to existing dir")
  108. }
  109. func TestAddSingleFileToNonExistDir(t *testing.T) {
  110. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd")
  111. buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "SingleFileToNonExistDir")
  112. buildCmd.Dir = buildDirectory
  113. out, exitCode, err := runCommandWithOutput(buildCmd)
  114. errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err))
  115. if err != nil || exitCode != 0 {
  116. t.Fatal("failed to build the image")
  117. }
  118. deleteImages("testaddimg")
  119. logDone("build - add single file to non-existing dir")
  120. }
  121. func TestAddDirContentToRoot(t *testing.T) {
  122. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd")
  123. buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "DirContentToRoot")
  124. buildCmd.Dir = buildDirectory
  125. out, exitCode, err := runCommandWithOutput(buildCmd)
  126. errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err))
  127. if err != nil || exitCode != 0 {
  128. t.Fatal("failed to build the image")
  129. }
  130. deleteImages("testaddimg")
  131. logDone("build - add directory contents to root")
  132. }
  133. func TestAddDirContentToExistDir(t *testing.T) {
  134. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd")
  135. buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "DirContentToExistDir")
  136. buildCmd.Dir = buildDirectory
  137. out, exitCode, err := runCommandWithOutput(buildCmd)
  138. errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err))
  139. if err != nil || exitCode != 0 {
  140. t.Fatal("failed to build the image")
  141. }
  142. deleteImages("testaddimg")
  143. logDone("build - add directory contents to existing dir")
  144. }
  145. func TestAddWholeDirToRoot(t *testing.T) {
  146. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd", "WholeDirToRoot")
  147. test_dir := filepath.Join(buildDirectory, "test_dir")
  148. if err := os.MkdirAll(test_dir, 0755); err != nil {
  149. t.Fatal(err)
  150. }
  151. f, err := os.OpenFile(filepath.Join(test_dir, "test_file"), os.O_CREATE, 0644)
  152. if err != nil {
  153. t.Fatal(err)
  154. }
  155. f.Close()
  156. buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", ".")
  157. buildCmd.Dir = buildDirectory
  158. out, exitCode, err := runCommandWithOutput(buildCmd)
  159. errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err))
  160. if err != nil || exitCode != 0 {
  161. t.Fatal("failed to build the image")
  162. }
  163. deleteImages("testaddimg")
  164. logDone("build - add whole directory to root")
  165. }
  166. func TestAddEtcToRoot(t *testing.T) {
  167. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd")
  168. buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "EtcToRoot")
  169. buildCmd.Dir = buildDirectory
  170. out, exitCode, err := runCommandWithOutput(buildCmd)
  171. errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err))
  172. if err != nil || exitCode != 0 {
  173. t.Fatal("failed to build the image")
  174. }
  175. deleteImages("testaddimg")
  176. logDone("build - add etc directory to root")
  177. }
  178. // Issue #5270 - ensure we throw a better error than "unexpected EOF"
  179. // when we can't access files in the context.
  180. func TestBuildWithInaccessibleFilesInContext(t *testing.T) {
  181. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestBuildWithInaccessibleFilesInContext")
  182. {
  183. // This is used to ensure we detect inaccessible files early during build in the cli client
  184. pathToInaccessibleFileBuildDirectory := filepath.Join(buildDirectory, "inaccessiblefile")
  185. pathToFileWithoutReadAccess := filepath.Join(pathToInaccessibleFileBuildDirectory, "fileWithoutReadAccess")
  186. err := os.Chown(pathToFileWithoutReadAccess, 0, 0)
  187. errorOut(err, t, fmt.Sprintf("failed to chown file to root: %s", err))
  188. err = os.Chmod(pathToFileWithoutReadAccess, 0700)
  189. errorOut(err, t, fmt.Sprintf("failed to chmod file to 700: %s", err))
  190. buildCommandStatement := fmt.Sprintf("%s build -t inaccessiblefiles .", dockerBinary)
  191. buildCmd := exec.Command("su", "unprivilegeduser", "-c", buildCommandStatement)
  192. buildCmd.Dir = pathToInaccessibleFileBuildDirectory
  193. out, exitCode, err := runCommandWithOutput(buildCmd)
  194. if err == nil || exitCode == 0 {
  195. t.Fatalf("build should have failed: %s %s", err, out)
  196. }
  197. // check if we've detected the failure before we started building
  198. if !strings.Contains(out, "no permission to read from ") {
  199. t.Fatalf("output should've contained the string: no permission to read from but contained: %s", out)
  200. }
  201. if !strings.Contains(out, "Error checking context is accessible") {
  202. t.Fatalf("output should've contained the string: Error checking context is accessible")
  203. }
  204. }
  205. {
  206. // This is used to ensure we detect inaccessible directories early during build in the cli client
  207. pathToInaccessibleDirectoryBuildDirectory := filepath.Join(buildDirectory, "inaccessibledirectory")
  208. pathToDirectoryWithoutReadAccess := filepath.Join(pathToInaccessibleDirectoryBuildDirectory, "directoryWeCantStat")
  209. pathToFileInDirectoryWithoutReadAccess := filepath.Join(pathToDirectoryWithoutReadAccess, "bar")
  210. err := os.Chown(pathToDirectoryWithoutReadAccess, 0, 0)
  211. errorOut(err, t, fmt.Sprintf("failed to chown directory to root: %s", err))
  212. err = os.Chmod(pathToDirectoryWithoutReadAccess, 0444)
  213. errorOut(err, t, fmt.Sprintf("failed to chmod directory to 755: %s", err))
  214. err = os.Chmod(pathToFileInDirectoryWithoutReadAccess, 0700)
  215. errorOut(err, t, fmt.Sprintf("failed to chmod file to 444: %s", err))
  216. buildCommandStatement := fmt.Sprintf("%s build -t inaccessiblefiles .", dockerBinary)
  217. buildCmd := exec.Command("su", "unprivilegeduser", "-c", buildCommandStatement)
  218. buildCmd.Dir = pathToInaccessibleDirectoryBuildDirectory
  219. out, exitCode, err := runCommandWithOutput(buildCmd)
  220. if err == nil || exitCode == 0 {
  221. t.Fatalf("build should have failed: %s %s", err, out)
  222. }
  223. // check if we've detected the failure before we started building
  224. if !strings.Contains(out, "can't stat") {
  225. t.Fatalf("output should've contained the string: can't access %s", out)
  226. }
  227. if !strings.Contains(out, "Error checking context is accessible") {
  228. t.Fatalf("output should've contained the string: Error checking context is accessible")
  229. }
  230. }
  231. {
  232. // This is used to ensure we don't follow links when checking if everything in the context is accessible
  233. // This test doesn't require that we run commands as an unprivileged user
  234. pathToDirectoryWhichContainsLinks := filepath.Join(buildDirectory, "linksdirectory")
  235. buildCmd := exec.Command(dockerBinary, "build", "-t", "testlinksok", ".")
  236. buildCmd.Dir = pathToDirectoryWhichContainsLinks
  237. out, exitCode, err := runCommandWithOutput(buildCmd)
  238. if err != nil || exitCode != 0 {
  239. t.Fatalf("build should have worked: %s %s", err, out)
  240. }
  241. deleteImages("testlinksok")
  242. }
  243. deleteImages("inaccessiblefiles")
  244. logDone("build - ADD from context with inaccessible files must fail")
  245. logDone("build - ADD from context with accessible links must work")
  246. }
  247. func TestBuildForceRm(t *testing.T) {
  248. containerCountBefore, err := getContainerCount()
  249. if err != nil {
  250. t.Fatalf("failed to get the container count: %s", err)
  251. }
  252. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestBuildForceRm")
  253. buildCmd := exec.Command(dockerBinary, "build", "--force-rm", ".")
  254. buildCmd.Dir = buildDirectory
  255. _, exitCode, err := runCommandWithOutput(buildCmd)
  256. if err == nil || exitCode == 0 {
  257. t.Fatal("failed to build the image")
  258. }
  259. containerCountAfter, err := getContainerCount()
  260. if err != nil {
  261. t.Fatalf("failed to get the container count: %s", err)
  262. }
  263. if containerCountBefore != containerCountAfter {
  264. t.Fatalf("--force-rm shouldn't have left containers behind")
  265. }
  266. logDone("build - ensure --force-rm doesn't leave containers behind")
  267. }
  268. func TestBuildRm(t *testing.T) {
  269. {
  270. containerCountBefore, err := getContainerCount()
  271. if err != nil {
  272. t.Fatalf("failed to get the container count: %s", err)
  273. }
  274. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestBuildRm")
  275. buildCmd := exec.Command(dockerBinary, "build", "--rm", "-t", "testbuildrm", ".")
  276. buildCmd.Dir = buildDirectory
  277. _, exitCode, err := runCommandWithOutput(buildCmd)
  278. if err != nil || exitCode != 0 {
  279. t.Fatal("failed to build the image")
  280. }
  281. containerCountAfter, err := getContainerCount()
  282. if err != nil {
  283. t.Fatalf("failed to get the container count: %s", err)
  284. }
  285. if containerCountBefore != containerCountAfter {
  286. t.Fatalf("-rm shouldn't have left containers behind")
  287. }
  288. deleteImages("testbuildrm")
  289. }
  290. {
  291. containerCountBefore, err := getContainerCount()
  292. if err != nil {
  293. t.Fatalf("failed to get the container count: %s", err)
  294. }
  295. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestBuildRm")
  296. buildCmd := exec.Command(dockerBinary, "build", "-t", "testbuildrm", ".")
  297. buildCmd.Dir = buildDirectory
  298. _, exitCode, err := runCommandWithOutput(buildCmd)
  299. if err != nil || exitCode != 0 {
  300. t.Fatal("failed to build the image")
  301. }
  302. containerCountAfter, err := getContainerCount()
  303. if err != nil {
  304. t.Fatalf("failed to get the container count: %s", err)
  305. }
  306. if containerCountBefore != containerCountAfter {
  307. t.Fatalf("--rm shouldn't have left containers behind")
  308. }
  309. deleteImages("testbuildrm")
  310. }
  311. {
  312. containerCountBefore, err := getContainerCount()
  313. if err != nil {
  314. t.Fatalf("failed to get the container count: %s", err)
  315. }
  316. buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestBuildRm")
  317. buildCmd := exec.Command(dockerBinary, "build", "--rm=false", "-t", "testbuildrm", ".")
  318. buildCmd.Dir = buildDirectory
  319. _, exitCode, err := runCommandWithOutput(buildCmd)
  320. if err != nil || exitCode != 0 {
  321. t.Fatal("failed to build the image")
  322. }
  323. containerCountAfter, err := getContainerCount()
  324. if err != nil {
  325. t.Fatalf("failed to get the container count: %s", err)
  326. }
  327. if containerCountBefore == containerCountAfter {
  328. t.Fatalf("--rm=false should have left containers behind")
  329. }
  330. deleteAllContainers()
  331. deleteImages("testbuildrm")
  332. }
  333. logDone("build - ensure --rm doesn't leave containers behind and that --rm=true is the default")
  334. logDone("build - ensure --rm=false overrides the default")
  335. }
  336. // TODO: TestCaching
  337. // TODO: TestADDCacheInvalidation