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