Skip to content

Instantly share code, notes, and snippets.

@StefanScherer
Last active March 12, 2018 14:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save StefanScherer/c55fd0ae752687c806f8d5fb40cadc78 to your computer and use it in GitHub Desktop.
Save StefanScherer/c55fd0ae752687c806f8d5fb40cadc78 to your computer and use it in GitHub Desktop.
packer 1.2.1 produces 0 byte vmdk
#!/bin/bash
docker build -t gotar .
docker create gotar
docker cp $(docker ps -ql):/go/src/app/dirtobox .
package main
import (
"archive/tar"
"compress/flate"
"encoding/json"
"fmt"
"io"
"log"
"os"
"path/filepath"
"runtime"
"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.")
)
// 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()
dstDir, _ := filepath.Split(dst)
if dstDir != "" {
err := os.MkdirAll(dstDir, 0755)
if err != nil {
return err
}
}
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
}
// 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 != "" {
err := os.MkdirAll(dstDir, 0755)
if err != nil {
return err
}
}
if err := os.Link(src, dst); err != nil {
return err
}
return nil
}
// 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.
func DirToBox(dst, dir string, level int) error {
log.Printf("Turning dir into box: %s => %s", dir, dst)
// Make the containing directory, if it does not already exist
err := os.MkdirAll(filepath.Dir(dst), 0755)
if err != nil {
return err
}
dstF, err := os.Create(dst)
if err != nil {
return err
}
defer dstF.Close()
var dstWriter io.WriteCloser = dstF
if level != flate.NoCompression {
log.Printf("Compressing with gzip compression level: %d", level)
gzipWriter, err := makePgzipWriter(dstWriter, level)
if err != nil {
return err
}
defer gzipWriter.Close()
dstWriter = gzipWriter
}
tarWriter := tar.NewWriter(dstWriter)
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 {
// If there was a prior error, return it
if prevErr != nil {
return prevErr
}
// Skip directories
if info.IsDir() {
log.Printf("Skipping directory '%s' for box '%s'", path, dst)
return nil
}
log.Printf("Box add: '%s' to '%s'", path, dst)
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
}
fmt.Println("Compressing: %s", header.Name)
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 {
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()
enc := json.NewEncoder(f)
return enc.Encode(contents)
}
return nil
}
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
}
func main() {
DirToBox("out.tar", "in", 0)
}
FROM golang:1.10
WORKDIR /go/src/app
COPY . .
RUN go get -d -v ./...
RUN GOOS=darwin go build dirtobox.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment