internals_test.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. package dockerfile
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "runtime"
  7. "testing"
  8. "github.com/docker/docker/api/types"
  9. "github.com/docker/docker/api/types/backend"
  10. "github.com/docker/docker/api/types/container"
  11. "github.com/docker/docker/builder"
  12. "github.com/docker/docker/builder/remotecontext"
  13. "github.com/docker/docker/pkg/archive"
  14. "github.com/docker/docker/pkg/idtools"
  15. "github.com/stretchr/testify/assert"
  16. "github.com/stretchr/testify/require"
  17. )
  18. func TestEmptyDockerfile(t *testing.T) {
  19. contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
  20. defer cleanup()
  21. createTestTempFile(t, contextDir, builder.DefaultDockerfileName, "", 0777)
  22. readAndCheckDockerfile(t, "emptyDockerfile", contextDir, "", "the Dockerfile (Dockerfile) cannot be empty")
  23. }
  24. func TestSymlinkDockerfile(t *testing.T) {
  25. contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
  26. defer cleanup()
  27. createTestSymlink(t, contextDir, builder.DefaultDockerfileName, "/etc/passwd")
  28. // The reason the error is "Cannot locate specified Dockerfile" is because
  29. // in the builder, the symlink is resolved within the context, therefore
  30. // Dockerfile -> /etc/passwd becomes etc/passwd from the context which is
  31. // a nonexistent file.
  32. expectedError := fmt.Sprintf("Cannot locate specified Dockerfile: %s", builder.DefaultDockerfileName)
  33. readAndCheckDockerfile(t, "symlinkDockerfile", contextDir, builder.DefaultDockerfileName, expectedError)
  34. }
  35. func TestDockerfileOutsideTheBuildContext(t *testing.T) {
  36. contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
  37. defer cleanup()
  38. expectedError := "Forbidden path outside the build context: ../../Dockerfile ()"
  39. readAndCheckDockerfile(t, "DockerfileOutsideTheBuildContext", contextDir, "../../Dockerfile", expectedError)
  40. }
  41. func TestNonExistingDockerfile(t *testing.T) {
  42. contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
  43. defer cleanup()
  44. expectedError := "Cannot locate specified Dockerfile: Dockerfile"
  45. readAndCheckDockerfile(t, "NonExistingDockerfile", contextDir, "Dockerfile", expectedError)
  46. }
  47. func readAndCheckDockerfile(t *testing.T, testName, contextDir, dockerfilePath, expectedError string) {
  48. tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
  49. require.NoError(t, err)
  50. defer func() {
  51. if err = tarStream.Close(); err != nil {
  52. t.Fatalf("Error when closing tar stream: %s", err)
  53. }
  54. }()
  55. if dockerfilePath == "" { // handled in BuildWithContext
  56. dockerfilePath = builder.DefaultDockerfileName
  57. }
  58. config := backend.BuildConfig{
  59. Options: &types.ImageBuildOptions{Dockerfile: dockerfilePath},
  60. Source: tarStream,
  61. }
  62. _, _, err = remotecontext.Detect(config)
  63. assert.EqualError(t, err, expectedError)
  64. }
  65. func TestCopyRunConfig(t *testing.T) {
  66. defaultEnv := []string{"foo=1"}
  67. defaultCmd := []string{"old"}
  68. var testcases = []struct {
  69. doc string
  70. modifiers []runConfigModifier
  71. expected *container.Config
  72. }{
  73. {
  74. doc: "Set the command",
  75. modifiers: []runConfigModifier{withCmd([]string{"new"})},
  76. expected: &container.Config{
  77. Cmd: []string{"new"},
  78. Env: defaultEnv,
  79. },
  80. },
  81. {
  82. doc: "Set the command to a comment",
  83. modifiers: []runConfigModifier{withCmdComment("comment", runtime.GOOS)},
  84. expected: &container.Config{
  85. Cmd: append(defaultShellForPlatform(runtime.GOOS), "#(nop) ", "comment"),
  86. Env: defaultEnv,
  87. },
  88. },
  89. {
  90. doc: "Set the command and env",
  91. modifiers: []runConfigModifier{
  92. withCmd([]string{"new"}),
  93. withEnv([]string{"one", "two"}),
  94. },
  95. expected: &container.Config{
  96. Cmd: []string{"new"},
  97. Env: []string{"one", "two"},
  98. },
  99. },
  100. }
  101. for _, testcase := range testcases {
  102. runConfig := &container.Config{
  103. Cmd: defaultCmd,
  104. Env: defaultEnv,
  105. }
  106. runConfigCopy := copyRunConfig(runConfig, testcase.modifiers...)
  107. assert.Equal(t, testcase.expected, runConfigCopy, testcase.doc)
  108. // Assert the original was not modified
  109. assert.NotEqual(t, runConfig, runConfigCopy, testcase.doc)
  110. }
  111. }
  112. func TestChownFlagParsing(t *testing.T) {
  113. testFiles := map[string]string{
  114. "passwd": `root:x:0:0::/bin:/bin/false
  115. bin:x:1:1::/bin:/bin/false
  116. wwwwww:x:21:33::/bin:/bin/false
  117. unicorn:x:1001:1002::/bin:/bin/false
  118. `,
  119. "group": `root:x:0:
  120. bin:x:1:
  121. wwwwww:x:33:
  122. unicorn:x:1002:
  123. somegrp:x:5555:
  124. othergrp:x:6666:
  125. `,
  126. }
  127. // test mappings for validating use of maps
  128. idMaps := []idtools.IDMap{
  129. {
  130. ContainerID: 0,
  131. HostID: 100000,
  132. Size: 65536,
  133. },
  134. }
  135. remapped := idtools.NewIDMappingsFromMaps(idMaps, idMaps)
  136. unmapped := &idtools.IDMappings{}
  137. contextDir, cleanup := createTestTempDir(t, "", "builder-chown-parse-test")
  138. defer cleanup()
  139. if err := os.Mkdir(filepath.Join(contextDir, "etc"), 0755); err != nil {
  140. t.Fatalf("error creating test directory: %v", err)
  141. }
  142. for filename, content := range testFiles {
  143. createTestTempFile(t, filepath.Join(contextDir, "etc"), filename, content, 0644)
  144. }
  145. // positive tests
  146. for _, testcase := range []struct {
  147. name string
  148. chownStr string
  149. idMapping *idtools.IDMappings
  150. expected idtools.IDPair
  151. }{
  152. {
  153. name: "UIDNoMap",
  154. chownStr: "1",
  155. idMapping: unmapped,
  156. expected: idtools.IDPair{UID: 1, GID: 1},
  157. },
  158. {
  159. name: "UIDGIDNoMap",
  160. chownStr: "0:1",
  161. idMapping: unmapped,
  162. expected: idtools.IDPair{UID: 0, GID: 1},
  163. },
  164. {
  165. name: "UIDWithMap",
  166. chownStr: "0",
  167. idMapping: remapped,
  168. expected: idtools.IDPair{UID: 100000, GID: 100000},
  169. },
  170. {
  171. name: "UIDGIDWithMap",
  172. chownStr: "1:33",
  173. idMapping: remapped,
  174. expected: idtools.IDPair{UID: 100001, GID: 100033},
  175. },
  176. {
  177. name: "UserNoMap",
  178. chownStr: "bin:5555",
  179. idMapping: unmapped,
  180. expected: idtools.IDPair{UID: 1, GID: 5555},
  181. },
  182. {
  183. name: "GroupWithMap",
  184. chownStr: "0:unicorn",
  185. idMapping: remapped,
  186. expected: idtools.IDPair{UID: 100000, GID: 101002},
  187. },
  188. {
  189. name: "UserOnlyWithMap",
  190. chownStr: "unicorn",
  191. idMapping: remapped,
  192. expected: idtools.IDPair{UID: 101001, GID: 101002},
  193. },
  194. } {
  195. t.Run(testcase.name, func(t *testing.T) {
  196. idPair, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
  197. require.NoError(t, err, "Failed to parse chown flag: %q", testcase.chownStr)
  198. assert.Equal(t, testcase.expected, idPair, "chown flag mapping failure")
  199. })
  200. }
  201. // error tests
  202. for _, testcase := range []struct {
  203. name string
  204. chownStr string
  205. idMapping *idtools.IDMappings
  206. descr string
  207. }{
  208. {
  209. name: "BadChownFlagFormat",
  210. chownStr: "bob:1:555",
  211. idMapping: unmapped,
  212. descr: "invalid chown string format: bob:1:555",
  213. },
  214. {
  215. name: "UserNoExist",
  216. chownStr: "bob",
  217. idMapping: unmapped,
  218. descr: "can't find uid for user bob: no such user: bob",
  219. },
  220. {
  221. name: "GroupNoExist",
  222. chownStr: "root:bob",
  223. idMapping: unmapped,
  224. descr: "can't find gid for group bob: no such group: bob",
  225. },
  226. } {
  227. t.Run(testcase.name, func(t *testing.T) {
  228. _, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
  229. assert.EqualError(t, err, testcase.descr, "Expected error string doesn't match")
  230. })
  231. }
  232. }