github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/cf/appfiles/zipper_test.go (about) 1 package appfiles_test 2 3 import ( 4 "archive/zip" 5 "bytes" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "os" 10 "path" 11 "path/filepath" 12 "runtime" 13 "strings" 14 15 . "code.cloudfoundry.org/cli/cf/appfiles" 16 "code.cloudfoundry.org/gofileutils/fileutils" 17 18 . "github.com/onsi/ginkgo" 19 . "github.com/onsi/gomega" 20 ) 21 22 func readFile(file *os.File) []byte { 23 bytes, err := ioutil.ReadAll(file) 24 Expect(err).NotTo(HaveOccurred()) 25 return bytes 26 } 27 28 // Thanks to Svett Ralchev 29 // http://blog.ralch.com/tutorial/golang-working-with-zip/ 30 func zipit(source, target, prefix string) error { 31 zipfile, err := os.Create(target) 32 if err != nil { 33 return err 34 } 35 defer zipfile.Close() 36 37 if prefix != "" { 38 _, err = io.WriteString(zipfile, prefix) 39 if err != nil { 40 return err 41 } 42 } 43 44 archive := zip.NewWriter(zipfile) 45 defer archive.Close() 46 47 err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error { 48 if err != nil { 49 return err 50 } 51 52 header, err := zip.FileInfoHeader(info) 53 if err != nil { 54 return err 55 } 56 57 header.Name = strings.TrimPrefix(path, source) 58 59 if info.IsDir() { 60 header.Name += string(os.PathSeparator) 61 } else { 62 header.Method = zip.Deflate 63 } 64 65 writer, err := archive.CreateHeader(header) 66 if err != nil { 67 return err 68 } 69 70 if info.IsDir() { 71 return nil 72 } 73 74 file, err := os.Open(path) 75 if err != nil { 76 return err 77 } 78 defer file.Close() 79 80 _, err = io.Copy(writer, file) 81 return err 82 }) 83 84 return err 85 } 86 87 func readFileInZip(index int, reader *zip.Reader) (string, string) { 88 buf := &bytes.Buffer{} 89 file := reader.File[index] 90 fReader, err := file.Open() 91 _, err = io.Copy(buf, fReader) 92 93 Expect(err).NotTo(HaveOccurred()) 94 95 return file.Name, string(buf.Bytes()) 96 } 97 98 var _ = Describe("Zipper", func() { 99 Describe("Zip", func() { 100 var zipFile *os.File 101 var filesInZip = []string{ 102 "foo.txt", 103 "fooDir/", 104 "fooDir/bar/", 105 "largeblankfile/", 106 "largeblankfile/file.txt", 107 "lastDir/", 108 "subDir/", 109 "subDir/bar.txt", 110 "subDir/otherDir/", 111 "subDir/otherDir/file.txt", 112 } 113 var zipper ApplicationZipper 114 115 BeforeEach(func() { 116 var err error 117 zipFile, err = ioutil.TempFile("", "zip_test") 118 Expect(err).NotTo(HaveOccurred()) 119 120 zipper = ApplicationZipper{} 121 }) 122 123 AfterEach(func() { 124 zipFile.Close() 125 os.Remove(zipFile.Name()) 126 }) 127 128 It("creates a zip with all files and directories from the source directory", func() { 129 workingDir, err := os.Getwd() 130 Expect(err).NotTo(HaveOccurred()) 131 132 dir := filepath.Join(workingDir, "../../fixtures/zip/") 133 err = zipper.Zip(dir, zipFile) 134 Expect(err).NotTo(HaveOccurred()) 135 136 fileStat, err := zipFile.Stat() 137 Expect(err).NotTo(HaveOccurred()) 138 139 reader, err := zip.NewReader(zipFile, fileStat.Size()) 140 Expect(err).NotTo(HaveOccurred()) 141 142 filenames := []string{} 143 for _, file := range reader.File { 144 filenames = append(filenames, file.Name) 145 } 146 Expect(filenames).To(Equal(filesInZip)) 147 148 name, contents := readFileInZip(0, reader) 149 Expect(name).To(Equal("foo.txt")) 150 Expect(contents).To(Equal("This is a simple text file.")) 151 }) 152 153 It("creates a zip with the original file modes", func() { 154 if runtime.GOOS == "windows" { 155 Skip("This test does not run on Windows") 156 } 157 158 workingDir, err := os.Getwd() 159 Expect(err).NotTo(HaveOccurred()) 160 161 dir := filepath.Join(workingDir, "../../fixtures/zip/") 162 err = os.Chmod(filepath.Join(dir, "subDir/bar.txt"), 0666) 163 Expect(err).NotTo(HaveOccurred()) 164 165 err = zipper.Zip(dir, zipFile) 166 Expect(err).NotTo(HaveOccurred()) 167 168 fileStat, err := zipFile.Stat() 169 Expect(err).NotTo(HaveOccurred()) 170 171 reader, err := zip.NewReader(zipFile, fileStat.Size()) 172 Expect(err).NotTo(HaveOccurred()) 173 174 readFileInZip(7, reader) 175 Expect(reader.File[7].FileInfo().Mode()).To(Equal(os.FileMode(0666))) 176 }) 177 178 It("creates a zip with executable file modes", func() { 179 if runtime.GOOS != "windows" { 180 Skip("This test only runs on Windows") 181 } 182 183 workingDir, err := os.Getwd() 184 Expect(err).NotTo(HaveOccurred()) 185 186 dir := filepath.Join(workingDir, "../../fixtures/zip/") 187 err = os.Chmod(filepath.Join(dir, "subDir/bar.txt"), 0666) 188 Expect(err).NotTo(HaveOccurred()) 189 190 err = zipper.Zip(dir, zipFile) 191 Expect(err).NotTo(HaveOccurred()) 192 193 fileStat, err := zipFile.Stat() 194 Expect(err).NotTo(HaveOccurred()) 195 196 reader, err := zip.NewReader(zipFile, fileStat.Size()) 197 Expect(err).NotTo(HaveOccurred()) 198 199 readFileInZip(7, reader) 200 Expect(fmt.Sprintf("%o", reader.File[7].FileInfo().Mode())).To(Equal("766")) 201 }) 202 203 It("is a no-op for a zipfile", func() { 204 dir, err := os.Getwd() 205 Expect(err).NotTo(HaveOccurred()) 206 207 zipper := ApplicationZipper{} 208 fixture := filepath.Join(dir, "../../fixtures/applications/example-app.zip") 209 err = zipper.Zip(fixture, zipFile) 210 Expect(err).NotTo(HaveOccurred()) 211 212 zippedFile, err := os.Open(fixture) 213 Expect(err).NotTo(HaveOccurred()) 214 Expect(readFile(zipFile)).To(Equal(readFile(zippedFile))) 215 }) 216 217 It("compresses the files", func() { 218 workingDir, err := os.Getwd() 219 Expect(err).NotTo(HaveOccurred()) 220 221 dir := filepath.Join(workingDir, "../../fixtures/zip/largeblankfile/") 222 fileStat, err := os.Stat(filepath.Join(dir, "file.txt")) 223 Expect(err).NotTo(HaveOccurred()) 224 originalFileSize := fileStat.Size() 225 226 err = zipper.Zip(dir, zipFile) 227 Expect(err).NotTo(HaveOccurred()) 228 229 fileStat, err = zipFile.Stat() 230 Expect(err).NotTo(HaveOccurred()) 231 232 compressedFileSize := fileStat.Size() 233 Expect(compressedFileSize).To(BeNumerically("<", originalFileSize)) 234 }) 235 236 It("returns an error when zipping fails", func() { 237 zipper := ApplicationZipper{} 238 err := zipper.Zip("/a/bogus/directory", zipFile) 239 Expect(err).To(HaveOccurred()) 240 Expect(err.Error()).To(ContainSubstring("open /a/bogus/directory")) 241 }) 242 243 It("returns an error when the directory is empty", func() { 244 fileutils.TempDir("zip_test", func(emptyDir string, err error) { 245 zipper := ApplicationZipper{} 246 err = zipper.Zip(emptyDir, zipFile) 247 Expect(err).To(HaveOccurred()) 248 Expect(err.Error()).To(ContainSubstring("is empty")) 249 }) 250 }) 251 }) 252 253 Describe("IsZipFile", func() { 254 var ( 255 inDir, outDir string 256 zipper ApplicationZipper 257 ) 258 259 AfterEach(func() { 260 os.RemoveAll(inDir) 261 os.RemoveAll(outDir) 262 }) 263 264 Context("when given a zip without prefix bytes", func() { 265 BeforeEach(func() { 266 var err error 267 inDir, err = ioutil.TempDir("", "zipper-unzip-in") 268 Expect(err).NotTo(HaveOccurred()) 269 270 err = ioutil.WriteFile(path.Join(inDir, "file1"), []byte("file-1-contents"), 0664) 271 Expect(err).NotTo(HaveOccurred()) 272 273 outDir, err = ioutil.TempDir("", "zipper-unzip-out") 274 Expect(err).NotTo(HaveOccurred()) 275 276 err = zipit(path.Join(inDir, "/"), path.Join(outDir, "out.zip"), "") 277 Expect(err).NotTo(HaveOccurred()) 278 279 zipper = ApplicationZipper{} 280 }) 281 282 It("returns true", func() { 283 Expect(zipper.IsZipFile(path.Join(outDir, "out.zip"))).To(BeTrue()) 284 }) 285 }) 286 287 Context("when given a zip with prefix bytes", func() { 288 BeforeEach(func() { 289 var err error 290 inDir, err = ioutil.TempDir("", "zipper-unzip-in") 291 Expect(err).NotTo(HaveOccurred()) 292 293 err = ioutil.WriteFile(path.Join(inDir, "file1"), []byte("file-1-contents"), 0664) 294 Expect(err).NotTo(HaveOccurred()) 295 296 outDir, err = ioutil.TempDir("", "zipper-unzip-out") 297 Expect(err).NotTo(HaveOccurred()) 298 299 err = zipit(path.Join(inDir, "/"), path.Join(outDir, "out.zip"), "prefix-bytes") 300 Expect(err).NotTo(HaveOccurred()) 301 302 zipper = ApplicationZipper{} 303 }) 304 305 It("returns true", func() { 306 Expect(zipper.IsZipFile(path.Join(outDir, "out.zip"))).To(BeTrue()) 307 }) 308 }) 309 310 Context("when given a file that is not a zip", func() { 311 var fileName string 312 313 BeforeEach(func() { 314 f, err := ioutil.TempFile("", "zipper-test") 315 Expect(err).NotTo(HaveOccurred()) 316 fileName = f.Name() 317 }) 318 319 AfterEach(func() { 320 os.RemoveAll(fileName) 321 }) 322 323 It("returns false", func() { 324 Expect(zipper.IsZipFile(fileName)).To(BeFalse()) 325 }) 326 }) 327 328 Context("when given a directory", func() { 329 var dirName string 330 331 BeforeEach(func() { 332 var err error 333 dirName, err = ioutil.TempDir("", "zipper-test") 334 Expect(err).NotTo(HaveOccurred()) 335 }) 336 337 AfterEach(func() { 338 defer os.RemoveAll(dirName) 339 }) 340 341 It("returns false", func() { 342 Expect(zipper.IsZipFile(dirName)).To(BeFalse()) 343 }) 344 }) 345 }) 346 347 Describe(".Unzip", func() { 348 var ( 349 inDir, outDir string 350 zipper ApplicationZipper 351 ) 352 353 AfterEach(func() { 354 os.RemoveAll(inDir) 355 os.RemoveAll(outDir) 356 }) 357 358 Context("when the zipfile has prefix bytes", func() { 359 BeforeEach(func() { 360 var err error 361 inDir, err = ioutil.TempDir("", "zipper-unzip-in") 362 Expect(err).NotTo(HaveOccurred()) 363 364 err = ioutil.WriteFile(path.Join(inDir, "file1"), []byte("file-1-contents"), 0664) 365 Expect(err).NotTo(HaveOccurred()) 366 367 outDir, err = ioutil.TempDir("", "zipper-unzip-out") 368 Expect(err).NotTo(HaveOccurred()) 369 370 err = zipit(path.Join(inDir, "/"), path.Join(outDir, "out.zip"), "prefix-bytes") 371 Expect(err).NotTo(HaveOccurred()) 372 373 zipper = ApplicationZipper{} 374 }) 375 376 It("successfully extracts the zip", func() { 377 destDir, err := ioutil.TempDir("", "dest-dir") 378 Expect(err).NotTo(HaveOccurred()) 379 380 defer os.RemoveAll(destDir) 381 382 err = zipper.Unzip(path.Join(outDir, "out.zip"), destDir) 383 Expect(err).NotTo(HaveOccurred()) 384 385 _, err = os.Stat(filepath.Join(destDir, "file1")) 386 Expect(err).NotTo(HaveOccurred()) 387 }) 388 }) 389 390 Context("when the zipfile has an empty directory", func() { 391 BeforeEach(func() { 392 var err error 393 inDir, err = ioutil.TempDir("", "zipper-unzip-in") 394 Expect(err).NotTo(HaveOccurred()) 395 396 err = ioutil.WriteFile(path.Join(inDir, "file1"), []byte("file-1-contents"), 0664) 397 Expect(err).NotTo(HaveOccurred()) 398 399 err = os.MkdirAll(path.Join(inDir, "dir1"), os.ModeDir|os.ModePerm) 400 Expect(err).NotTo(HaveOccurred()) 401 402 err = ioutil.WriteFile(path.Join(inDir, "dir1", "file2"), []byte("file-2-contents"), 0644) 403 Expect(err).NotTo(HaveOccurred()) 404 405 err = os.MkdirAll(path.Join(inDir, "dir2"), os.ModeDir|os.ModePerm) 406 Expect(err).NotTo(HaveOccurred()) 407 408 outDir, err = ioutil.TempDir("", "zipper-unzip-out") 409 Expect(err).NotTo(HaveOccurred()) 410 411 err = zipit(path.Join(inDir, "/"), path.Join(outDir, "out.zip"), "") 412 Expect(err).NotTo(HaveOccurred()) 413 414 zipper = ApplicationZipper{} 415 }) 416 417 It("includes all entries from the zip file in the destination", func() { 418 destDir, err := ioutil.TempDir("", "dest-dir") 419 Expect(err).NotTo(HaveOccurred()) 420 421 defer os.RemoveAll(destDir) 422 423 err = zipper.Unzip(path.Join(outDir, "out.zip"), destDir) 424 Expect(err).NotTo(HaveOccurred()) 425 426 expected := []string{ 427 "file1", 428 "dir1/", 429 "dir1/file2", 430 "dir2", 431 } 432 433 for _, f := range expected { 434 _, err := os.Stat(filepath.Join(destDir, f)) 435 Expect(err).NotTo(HaveOccurred()) 436 } 437 }) 438 }) 439 }) 440 441 Describe(".GetZipSize", func() { 442 var zipper = ApplicationZipper{} 443 444 It("returns the size of the zip file", func() { 445 dir, err := os.Getwd() 446 Expect(err).NotTo(HaveOccurred()) 447 zipFile := filepath.Join(dir, "../../fixtures/applications/example-app.zip") 448 449 file, err := os.Open(zipFile) 450 Expect(err).NotTo(HaveOccurred()) 451 452 fileSize, err := zipper.GetZipSize(file) 453 Expect(err).NotTo(HaveOccurred()) 454 Expect(fileSize).To(Equal(int64(1803))) 455 }) 456 457 It("returns an error if the zip file cannot be found", func() { 458 tmpFile, _ := os.Open("fooBar") 459 _, sizeErr := zipper.GetZipSize(tmpFile) 460 Expect(sizeErr).To(HaveOccurred()) 461 }) 462 }) 463 })