context_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. package build
  2. import (
  3. "archive/tar"
  4. "bytes"
  5. "io"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "runtime"
  10. "strings"
  11. "testing"
  12. "github.com/docker/docker/pkg/archive"
  13. )
  14. const dockerfileContents = "FROM busybox"
  15. var prepareEmpty = func(t *testing.T) (string, func()) {
  16. return "", func() {}
  17. }
  18. var prepareNoFiles = func(t *testing.T) (string, func()) {
  19. return createTestTempDir(t, "", "builder-context-test")
  20. }
  21. var prepareOneFile = func(t *testing.T) (string, func()) {
  22. contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
  23. createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
  24. return contextDir, cleanup
  25. }
  26. func testValidateContextDirectory(t *testing.T, prepare func(t *testing.T) (string, func()), excludes []string) {
  27. contextDir, cleanup := prepare(t)
  28. defer cleanup()
  29. err := ValidateContextDirectory(contextDir, excludes)
  30. if err != nil {
  31. t.Fatalf("Error should be nil, got: %s", err)
  32. }
  33. }
  34. func TestGetContextFromLocalDirNoDockerfile(t *testing.T) {
  35. contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
  36. defer cleanup()
  37. absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "")
  38. if err == nil {
  39. t.Fatalf("Error should not be nil")
  40. }
  41. if absContextDir != "" {
  42. t.Fatalf("Absolute directory path should be empty, got: %s", absContextDir)
  43. }
  44. if relDockerfile != "" {
  45. t.Fatalf("Relative path to Dockerfile should be empty, got: %s", relDockerfile)
  46. }
  47. }
  48. func TestGetContextFromLocalDirNotExistingDir(t *testing.T) {
  49. contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
  50. defer cleanup()
  51. fakePath := filepath.Join(contextDir, "fake")
  52. absContextDir, relDockerfile, err := GetContextFromLocalDir(fakePath, "")
  53. if err == nil {
  54. t.Fatalf("Error should not be nil")
  55. }
  56. if absContextDir != "" {
  57. t.Fatalf("Absolute directory path should be empty, got: %s", absContextDir)
  58. }
  59. if relDockerfile != "" {
  60. t.Fatalf("Relative path to Dockerfile should be empty, got: %s", relDockerfile)
  61. }
  62. }
  63. func TestGetContextFromLocalDirNotExistingDockerfile(t *testing.T) {
  64. contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
  65. defer cleanup()
  66. fakePath := filepath.Join(contextDir, "fake")
  67. absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, fakePath)
  68. if err == nil {
  69. t.Fatalf("Error should not be nil")
  70. }
  71. if absContextDir != "" {
  72. t.Fatalf("Absolute directory path should be empty, got: %s", absContextDir)
  73. }
  74. if relDockerfile != "" {
  75. t.Fatalf("Relative path to Dockerfile should be empty, got: %s", relDockerfile)
  76. }
  77. }
  78. func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) {
  79. contextDir, dirCleanup := createTestTempDir(t, "", "builder-context-test")
  80. defer dirCleanup()
  81. createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
  82. chdirCleanup := chdir(t, contextDir)
  83. defer chdirCleanup()
  84. absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "")
  85. if err != nil {
  86. t.Fatalf("Error when getting context from local dir: %s", err)
  87. }
  88. if absContextDir != contextDir {
  89. t.Fatalf("Absolute directory path should be equal to %s, got: %s", contextDir, absContextDir)
  90. }
  91. if relDockerfile != DefaultDockerfileName {
  92. t.Fatalf("Relative path to dockerfile should be equal to %s, got: %s", DefaultDockerfileName, relDockerfile)
  93. }
  94. }
  95. func TestGetContextFromLocalDirWithDockerfile(t *testing.T) {
  96. contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
  97. defer cleanup()
  98. createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
  99. absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "")
  100. if err != nil {
  101. t.Fatalf("Error when getting context from local dir: %s", err)
  102. }
  103. if absContextDir != contextDir {
  104. t.Fatalf("Absolute directory path should be equal to %s, got: %s", contextDir, absContextDir)
  105. }
  106. if relDockerfile != DefaultDockerfileName {
  107. t.Fatalf("Relative path to dockerfile should be equal to %s, got: %s", DefaultDockerfileName, relDockerfile)
  108. }
  109. }
  110. func TestGetContextFromLocalDirLocalFile(t *testing.T) {
  111. contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
  112. defer cleanup()
  113. createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
  114. testFilename := createTestTempFile(t, contextDir, "tmpTest", "test", 0777)
  115. absContextDir, relDockerfile, err := GetContextFromLocalDir(testFilename, "")
  116. if err == nil {
  117. t.Fatalf("Error should not be nil")
  118. }
  119. if absContextDir != "" {
  120. t.Fatalf("Absolute directory path should be empty, got: %s", absContextDir)
  121. }
  122. if relDockerfile != "" {
  123. t.Fatalf("Relative path to Dockerfile should be empty, got: %s", relDockerfile)
  124. }
  125. }
  126. func TestGetContextFromLocalDirWithCustomDockerfile(t *testing.T) {
  127. contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
  128. defer cleanup()
  129. chdirCleanup := chdir(t, contextDir)
  130. defer chdirCleanup()
  131. createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
  132. absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, DefaultDockerfileName)
  133. if err != nil {
  134. t.Fatalf("Error when getting context from local dir: %s", err)
  135. }
  136. if absContextDir != contextDir {
  137. t.Fatalf("Absolute directory path should be equal to %s, got: %s", contextDir, absContextDir)
  138. }
  139. if relDockerfile != DefaultDockerfileName {
  140. t.Fatalf("Relative path to dockerfile should be equal to %s, got: %s", DefaultDockerfileName, relDockerfile)
  141. }
  142. }
  143. func TestGetContextFromReaderString(t *testing.T) {
  144. tarArchive, relDockerfile, err := GetContextFromReader(ioutil.NopCloser(strings.NewReader(dockerfileContents)), "")
  145. if err != nil {
  146. t.Fatalf("Error when executing GetContextFromReader: %s", err)
  147. }
  148. tarReader := tar.NewReader(tarArchive)
  149. _, err = tarReader.Next()
  150. if err != nil {
  151. t.Fatalf("Error when reading tar archive: %s", err)
  152. }
  153. buff := new(bytes.Buffer)
  154. buff.ReadFrom(tarReader)
  155. contents := buff.String()
  156. _, err = tarReader.Next()
  157. if err != io.EOF {
  158. t.Fatalf("Tar stream too long: %s", err)
  159. }
  160. if err = tarArchive.Close(); err != nil {
  161. t.Fatalf("Error when closing tar stream: %s", err)
  162. }
  163. if dockerfileContents != contents {
  164. t.Fatalf("Uncompressed tar archive does not equal: %s, got: %s", dockerfileContents, contents)
  165. }
  166. if relDockerfile != DefaultDockerfileName {
  167. t.Fatalf("Relative path not equals %s, got: %s", DefaultDockerfileName, relDockerfile)
  168. }
  169. }
  170. func TestGetContextFromReaderTar(t *testing.T) {
  171. contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
  172. defer cleanup()
  173. createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
  174. tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
  175. if err != nil {
  176. t.Fatalf("Error when creating tar: %s", err)
  177. }
  178. tarArchive, relDockerfile, err := GetContextFromReader(tarStream, DefaultDockerfileName)
  179. if err != nil {
  180. t.Fatalf("Error when executing GetContextFromReader: %s", err)
  181. }
  182. tarReader := tar.NewReader(tarArchive)
  183. header, err := tarReader.Next()
  184. if err != nil {
  185. t.Fatalf("Error when reading tar archive: %s", err)
  186. }
  187. if header.Name != DefaultDockerfileName {
  188. t.Fatalf("Dockerfile name should be: %s, got: %s", DefaultDockerfileName, header.Name)
  189. }
  190. buff := new(bytes.Buffer)
  191. buff.ReadFrom(tarReader)
  192. contents := buff.String()
  193. _, err = tarReader.Next()
  194. if err != io.EOF {
  195. t.Fatalf("Tar stream too long: %s", err)
  196. }
  197. if err = tarArchive.Close(); err != nil {
  198. t.Fatalf("Error when closing tar stream: %s", err)
  199. }
  200. if dockerfileContents != contents {
  201. t.Fatalf("Uncompressed tar archive does not equal: %s, got: %s", dockerfileContents, contents)
  202. }
  203. if relDockerfile != DefaultDockerfileName {
  204. t.Fatalf("Relative path not equals %s, got: %s", DefaultDockerfileName, relDockerfile)
  205. }
  206. }
  207. func TestValidateContextDirectoryEmptyContext(t *testing.T) {
  208. // This isn't a valid test on Windows. See https://play.golang.org/p/RR6z6jxR81.
  209. // The test will ultimately end up calling filepath.Abs(""). On Windows,
  210. // golang will error. On Linux, golang will return /. Due to there being
  211. // drive letters on Windows, this is probably the correct behaviour for
  212. // Windows.
  213. if runtime.GOOS == "windows" {
  214. t.Skip("Invalid test on Windows")
  215. }
  216. testValidateContextDirectory(t, prepareEmpty, []string{})
  217. }
  218. func TestValidateContextDirectoryContextWithNoFiles(t *testing.T) {
  219. testValidateContextDirectory(t, prepareNoFiles, []string{})
  220. }
  221. func TestValidateContextDirectoryWithOneFile(t *testing.T) {
  222. testValidateContextDirectory(t, prepareOneFile, []string{})
  223. }
  224. func TestValidateContextDirectoryWithOneFileExcludes(t *testing.T) {
  225. testValidateContextDirectory(t, prepareOneFile, []string{DefaultDockerfileName})
  226. }
  227. // createTestTempDir creates a temporary directory for testing.
  228. // It returns the created path and a cleanup function which is meant to be used as deferred call.
  229. // When an error occurs, it terminates the test.
  230. func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) {
  231. path, err := ioutil.TempDir(dir, prefix)
  232. if err != nil {
  233. t.Fatalf("Error when creating directory %s with prefix %s: %s", dir, prefix, err)
  234. }
  235. return path, func() {
  236. err = os.RemoveAll(path)
  237. if err != nil {
  238. t.Fatalf("Error when removing directory %s: %s", path, err)
  239. }
  240. }
  241. }
  242. // createTestTempSubdir creates a temporary directory for testing.
  243. // It returns the created path but doesn't provide a cleanup function,
  244. // so createTestTempSubdir should be used only for creating temporary subdirectories
  245. // whose parent directories are properly cleaned up.
  246. // When an error occurs, it terminates the test.
  247. func createTestTempSubdir(t *testing.T, dir, prefix string) string {
  248. path, err := ioutil.TempDir(dir, prefix)
  249. if err != nil {
  250. t.Fatalf("Error when creating directory %s with prefix %s: %s", dir, prefix, err)
  251. }
  252. return path
  253. }
  254. // createTestTempFile creates a temporary file within dir with specific contents and permissions.
  255. // When an error occurs, it terminates the test
  256. func createTestTempFile(t *testing.T, dir, filename, contents string, perm os.FileMode) string {
  257. filePath := filepath.Join(dir, filename)
  258. err := ioutil.WriteFile(filePath, []byte(contents), perm)
  259. if err != nil {
  260. t.Fatalf("Error when creating %s file: %s", filename, err)
  261. }
  262. return filePath
  263. }
  264. // chdir changes current working directory to dir.
  265. // It returns a function which changes working directory back to the previous one.
  266. // This function is meant to be executed as a deferred call.
  267. // When an error occurs, it terminates the test.
  268. func chdir(t *testing.T, dir string) func() {
  269. workingDirectory, err := os.Getwd()
  270. if err != nil {
  271. t.Fatalf("Error when retrieving working directory: %s", err)
  272. }
  273. err = os.Chdir(dir)
  274. if err != nil {
  275. t.Fatalf("Error when changing directory to %s: %s", dir, err)
  276. }
  277. return func() {
  278. err = os.Chdir(workingDirectory)
  279. if err != nil {
  280. t.Fatalf("Error when changing back to working directory (%s): %s", workingDirectory, err)
  281. }
  282. }
  283. }