2013-06-26 21:55:11 -04:00
|
|
|
package vagrant
|
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/tar"
|
2013-11-18 13:38:19 -05:00
|
|
|
"compress/flate"
|
2013-06-26 21:55:11 -04:00
|
|
|
"encoding/json"
|
2013-08-20 01:38:32 -04:00
|
|
|
"fmt"
|
2013-06-26 21:55:11 -04:00
|
|
|
"io"
|
2013-06-27 17:06:14 -04:00
|
|
|
"log"
|
2013-06-26 21:55:11 -04:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2015-08-12 15:34:52 -04:00
|
|
|
"runtime"
|
|
|
|
|
2017-04-04 16:39:01 -04:00
|
|
|
"github.com/hashicorp/packer/packer"
|
2015-08-12 15:34:52 -04:00
|
|
|
"github.com/klauspost/pgzip"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ErrInvalidCompressionLevel is returned when the compression level passed
|
|
|
|
// to gzip is not in the expected range. See compress/flate for details.
|
|
|
|
ErrInvalidCompressionLevel = fmt.Errorf(
|
|
|
|
"Invalid compression level. Expected an integer from -1 to 9.")
|
2013-06-26 21:55:11 -04:00
|
|
|
)
|
|
|
|
|
2013-07-07 20:44:13 -04:00
|
|
|
// Copies a file by copying the contents of the file to another place.
|
|
|
|
func CopyContents(dst, src string) error {
|
|
|
|
srcF, err := os.Open(src)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer srcF.Close()
|
|
|
|
|
2014-04-30 02:03:52 -04:00
|
|
|
dstDir, _ := filepath.Split(dst)
|
|
|
|
if dstDir != "" {
|
2017-09-02 08:06:04 -04:00
|
|
|
err := os.MkdirAll(dstDir, 0755)
|
2014-04-30 02:03:52 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-07 20:44:13 -04:00
|
|
|
dstF, err := os.Create(dst)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer dstF.Close()
|
|
|
|
|
|
|
|
if _, err := io.Copy(dstF, srcF); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-08-02 07:27:22 -04:00
|
|
|
// Creates a (hard) link to a file, ensuring that all parent directories also exist.
|
|
|
|
func LinkFile(dst, src string) error {
|
|
|
|
dstDir, _ := filepath.Split(dst)
|
|
|
|
if dstDir != "" {
|
2017-09-02 08:06:04 -04:00
|
|
|
err := os.MkdirAll(dstDir, 0755)
|
2017-08-02 07:27:22 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.Link(src, dst); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2013-06-26 21:55:11 -04:00
|
|
|
// DirToBox takes the directory and compresses it into a Vagrant-compatible
|
|
|
|
// box. This function does not perform checks to verify that dir is
|
|
|
|
// actually a proper box. This is an expected precondition.
|
2013-10-08 00:59:26 -04:00
|
|
|
func DirToBox(dst, dir string, ui packer.Ui, level int) error {
|
2013-07-05 14:11:54 -04:00
|
|
|
log.Printf("Turning dir into box: %s => %s", dir, dst)
|
2014-04-24 23:32:42 -04:00
|
|
|
|
|
|
|
// Make the containing directory, if it does not already exist
|
|
|
|
err := os.MkdirAll(filepath.Dir(dst), 0755)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2013-06-26 21:55:11 -04:00
|
|
|
dstF, err := os.Create(dst)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer dstF.Close()
|
|
|
|
|
2015-08-12 15:34:52 -04:00
|
|
|
var dstWriter io.WriteCloser = dstF
|
2013-11-18 13:38:19 -05:00
|
|
|
if level != flate.NoCompression {
|
2013-11-18 18:38:54 -05:00
|
|
|
log.Printf("Compressing with gzip compression level: %d", level)
|
2015-08-12 15:34:52 -04:00
|
|
|
gzipWriter, err := makePgzipWriter(dstWriter, level)
|
2013-11-18 13:38:19 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer gzipWriter.Close()
|
2013-11-18 18:38:54 -05:00
|
|
|
|
|
|
|
dstWriter = gzipWriter
|
2013-10-08 00:59:26 -04:00
|
|
|
}
|
2013-06-26 21:55:11 -04:00
|
|
|
|
2013-11-18 18:38:54 -05:00
|
|
|
tarWriter := tar.NewWriter(dstWriter)
|
2013-06-26 21:55:11 -04:00
|
|
|
defer tarWriter.Close()
|
|
|
|
|
|
|
|
// This is the walk func that tars each of the files in the dir
|
|
|
|
tarWalk := func(path string, info os.FileInfo, prevErr error) error {
|
2013-07-02 11:46:00 -04:00
|
|
|
// If there was a prior error, return it
|
|
|
|
if prevErr != nil {
|
|
|
|
return prevErr
|
|
|
|
}
|
|
|
|
|
2013-06-27 17:06:14 -04:00
|
|
|
// Skip directories
|
|
|
|
if info.IsDir() {
|
2013-07-05 14:00:18 -04:00
|
|
|
log.Printf("Skipping directory '%s' for box '%s'", path, dst)
|
2013-06-27 17:06:14 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("Box add: '%s' to '%s'", path, dst)
|
2013-06-26 21:55:11 -04:00
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
header, err := tar.FileInfoHeader(info, "")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have to set the Name explicitly because it is supposed to
|
|
|
|
// be a relative path to the root. Otherwise, the tar ends up
|
|
|
|
// being a bunch of files in the root, even if they're actually
|
|
|
|
// nested in a dir in the original "dir" param.
|
|
|
|
header.Name, err = filepath.Rel(dir, path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2013-08-20 01:38:32 -04:00
|
|
|
if ui != nil {
|
|
|
|
ui.Message(fmt.Sprintf("Compressing: %s", header.Name))
|
|
|
|
}
|
|
|
|
|
2013-06-26 21:55:11 -04:00
|
|
|
if err := tarWriter.WriteHeader(header); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := io.Copy(tarWriter, f); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tar.gz everything up
|
|
|
|
return filepath.Walk(dir, tarWalk)
|
|
|
|
}
|
|
|
|
|
|
|
|
// WriteMetadata writes the "metadata.json" file for a Vagrant box.
|
|
|
|
func WriteMetadata(dir string, contents interface{}) error {
|
2014-07-24 09:39:21 -04:00
|
|
|
if _, err := os.Stat(filepath.Join(dir, "metadata.json")); os.IsNotExist(err) {
|
|
|
|
f, err := os.Create(filepath.Join(dir, "metadata.json"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
2013-06-26 21:55:11 -04:00
|
|
|
|
2014-07-24 09:39:21 -04:00
|
|
|
enc := json.NewEncoder(f)
|
|
|
|
return enc.Encode(contents)
|
|
|
|
}
|
2014-09-02 12:54:55 -04:00
|
|
|
|
2014-07-24 09:39:21 -04:00
|
|
|
return nil
|
2013-06-26 21:55:11 -04:00
|
|
|
}
|
2015-08-12 15:34:52 -04:00
|
|
|
|
|
|
|
func makePgzipWriter(output io.WriteCloser, compressionLevel int) (io.WriteCloser, error) {
|
|
|
|
gzipWriter, err := pgzip.NewWriterLevel(output, compressionLevel)
|
|
|
|
if err != nil {
|
|
|
|
return nil, ErrInvalidCompressionLevel
|
|
|
|
}
|
|
|
|
gzipWriter.SetConcurrency(500000, runtime.GOMAXPROCS(-1))
|
|
|
|
return gzipWriter, nil
|
|
|
|
}
|