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  })