github.com/rothwerx/packer@v0.9.0/post-processor/vagrant/util.go (about) 1 package vagrant 2 3 import ( 4 "archive/tar" 5 "compress/flate" 6 "encoding/json" 7 "fmt" 8 "io" 9 "log" 10 "os" 11 "path/filepath" 12 "runtime" 13 14 "github.com/klauspost/pgzip" 15 "github.com/mitchellh/packer/packer" 16 ) 17 18 var ( 19 // ErrInvalidCompressionLevel is returned when the compression level passed 20 // to gzip is not in the expected range. See compress/flate for details. 21 ErrInvalidCompressionLevel = fmt.Errorf( 22 "Invalid compression level. Expected an integer from -1 to 9.") 23 ) 24 25 // Copies a file by copying the contents of the file to another place. 26 func CopyContents(dst, src string) error { 27 srcF, err := os.Open(src) 28 if err != nil { 29 return err 30 } 31 defer srcF.Close() 32 33 dstDir, _ := filepath.Split(dst) 34 if dstDir != "" { 35 err := os.MkdirAll(dstDir, os.ModePerm) 36 if err != nil { 37 return err 38 } 39 } 40 41 dstF, err := os.Create(dst) 42 if err != nil { 43 return err 44 } 45 defer dstF.Close() 46 47 if _, err := io.Copy(dstF, srcF); err != nil { 48 return err 49 } 50 51 return nil 52 } 53 54 // DirToBox takes the directory and compresses it into a Vagrant-compatible 55 // box. This function does not perform checks to verify that dir is 56 // actually a proper box. This is an expected precondition. 57 func DirToBox(dst, dir string, ui packer.Ui, level int) error { 58 log.Printf("Turning dir into box: %s => %s", dir, dst) 59 60 // Make the containing directory, if it does not already exist 61 err := os.MkdirAll(filepath.Dir(dst), 0755) 62 if err != nil { 63 return err 64 } 65 66 dstF, err := os.Create(dst) 67 if err != nil { 68 return err 69 } 70 defer dstF.Close() 71 72 var dstWriter io.WriteCloser = dstF 73 if level != flate.NoCompression { 74 log.Printf("Compressing with gzip compression level: %d", level) 75 gzipWriter, err := makePgzipWriter(dstWriter, level) 76 if err != nil { 77 return err 78 } 79 defer gzipWriter.Close() 80 81 dstWriter = gzipWriter 82 } 83 84 tarWriter := tar.NewWriter(dstWriter) 85 defer tarWriter.Close() 86 87 // This is the walk func that tars each of the files in the dir 88 tarWalk := func(path string, info os.FileInfo, prevErr error) error { 89 // If there was a prior error, return it 90 if prevErr != nil { 91 return prevErr 92 } 93 94 // Skip directories 95 if info.IsDir() { 96 log.Printf("Skipping directory '%s' for box '%s'", path, dst) 97 return nil 98 } 99 100 log.Printf("Box add: '%s' to '%s'", path, dst) 101 f, err := os.Open(path) 102 if err != nil { 103 return err 104 } 105 defer f.Close() 106 107 header, err := tar.FileInfoHeader(info, "") 108 if err != nil { 109 return err 110 } 111 112 // We have to set the Name explicitly because it is supposed to 113 // be a relative path to the root. Otherwise, the tar ends up 114 // being a bunch of files in the root, even if they're actually 115 // nested in a dir in the original "dir" param. 116 header.Name, err = filepath.Rel(dir, path) 117 if err != nil { 118 return err 119 } 120 121 if ui != nil { 122 ui.Message(fmt.Sprintf("Compressing: %s", header.Name)) 123 } 124 125 if err := tarWriter.WriteHeader(header); err != nil { 126 return err 127 } 128 129 if _, err := io.Copy(tarWriter, f); err != nil { 130 return err 131 } 132 133 return nil 134 } 135 136 // Tar.gz everything up 137 return filepath.Walk(dir, tarWalk) 138 } 139 140 // WriteMetadata writes the "metadata.json" file for a Vagrant box. 141 func WriteMetadata(dir string, contents interface{}) error { 142 if _, err := os.Stat(filepath.Join(dir, "metadata.json")); os.IsNotExist(err) { 143 f, err := os.Create(filepath.Join(dir, "metadata.json")) 144 if err != nil { 145 return err 146 } 147 defer f.Close() 148 149 enc := json.NewEncoder(f) 150 return enc.Encode(contents) 151 } 152 153 return nil 154 } 155 156 func makePgzipWriter(output io.WriteCloser, compressionLevel int) (io.WriteCloser, error) { 157 gzipWriter, err := pgzip.NewWriterLevel(output, compressionLevel) 158 if err != nil { 159 return nil, ErrInvalidCompressionLevel 160 } 161 gzipWriter.SetConcurrency(500000, runtime.GOMAXPROCS(-1)) 162 return gzipWriter, nil 163 }