2014-04-26 12:22:10 -04:00
|
|
|
package compress
|
|
|
|
|
|
|
|
import (
|
2015-06-10 17:04:24 -04:00
|
|
|
"archive/tar"
|
|
|
|
"compress/gzip"
|
2014-04-26 12:22:10 -04:00
|
|
|
"fmt"
|
2014-06-12 16:45:37 -04:00
|
|
|
"io"
|
|
|
|
"os"
|
2014-09-08 13:28:16 -04:00
|
|
|
|
|
|
|
"github.com/mitchellh/packer/common"
|
2015-06-10 16:33:50 -04:00
|
|
|
"github.com/mitchellh/packer/helper/config"
|
2014-09-08 13:28:16 -04:00
|
|
|
"github.com/mitchellh/packer/packer"
|
2015-06-10 16:33:50 -04:00
|
|
|
"github.com/mitchellh/packer/template/interpolate"
|
2014-04-26 12:22:10 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
type Config struct {
|
|
|
|
common.PackerConfig `mapstructure:",squash"`
|
|
|
|
|
2015-06-16 15:10:28 -04:00
|
|
|
OutputPath string `mapstructure:"output"`
|
|
|
|
|
|
|
|
ctx interpolate.Context
|
2014-04-26 12:22:10 -04:00
|
|
|
}
|
|
|
|
|
2015-06-10 16:46:21 -04:00
|
|
|
type PostProcessor struct {
|
2015-06-12 20:25:09 -04:00
|
|
|
config Config
|
2014-04-26 12:22:10 -04:00
|
|
|
}
|
|
|
|
|
2015-06-10 16:46:21 -04:00
|
|
|
func (p *PostProcessor) Configure(raws ...interface{}) error {
|
2015-06-12 20:25:09 -04:00
|
|
|
p.config.Compression = -1
|
|
|
|
err := config.Decode(&p.config, &config.DecodeOpts{
|
2015-06-10 16:33:50 -04:00
|
|
|
Interpolate: true,
|
|
|
|
InterpolateFilter: &interpolate.RenderFilter{
|
2015-06-12 20:25:09 -04:00
|
|
|
Exclude: []string{},
|
2015-06-10 16:33:50 -04:00
|
|
|
},
|
|
|
|
}, raws...)
|
2014-04-26 12:22:10 -04:00
|
|
|
|
2015-06-10 15:30:18 -04:00
|
|
|
errs := new(packer.MultiError)
|
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
if p.config.OutputPath == "" {
|
|
|
|
p.config.OutputPath = "packer_{{.BuildName}}_{{.Provider}}"
|
2015-06-10 15:30:18 -04:00
|
|
|
}
|
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
if err = interpolate.Validate(p.config.OutputPath, p.config.ctx); err != nil {
|
2015-06-10 15:30:18 -04:00
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
errs, fmt.Errorf("Error parsing target template: %s", err))
|
|
|
|
}
|
|
|
|
|
|
|
|
templates := map[string]*string{
|
2015-06-12 20:25:09 -04:00
|
|
|
"output": &p.config.OutputPath,
|
2015-06-10 15:30:18 -04:00
|
|
|
}
|
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
if p.config.Compression > flate.BestCompression {
|
|
|
|
p.config.Compression = flate.BestCompression
|
2015-06-10 15:30:18 -04:00
|
|
|
}
|
2015-06-12 20:25:09 -04:00
|
|
|
if p.config.Compression == -1 {
|
|
|
|
p.config.Compression = flate.DefaultCompression
|
2015-06-10 15:30:18 -04:00
|
|
|
}
|
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
if p.config.NumCPU < 1 {
|
|
|
|
p.config.NumCPU = runtime.NumCPU()
|
2015-06-10 15:30:18 -04:00
|
|
|
}
|
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
runtime.GOMAXPROCS(p.config.NumCPU)
|
2015-06-10 15:30:18 -04:00
|
|
|
|
|
|
|
for key, ptr := range templates {
|
|
|
|
if *ptr == "" {
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
errs, fmt.Errorf("%s must be set", key))
|
|
|
|
}
|
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
*ptr, err = interpolate.Render(p.config.OutputPath, p.config.ctx)
|
2015-06-10 15:30:18 -04:00
|
|
|
if err != nil {
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
errs, fmt.Errorf("Error processing %s: %s", key, err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(errs.Errors) > 0 {
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
2014-04-26 12:22:10 -04:00
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-06-10 16:46:21 -04:00
|
|
|
func (p *PostProcessor) fillMetadata(metadata Metadata, files []string) Metadata {
|
2015-06-10 15:30:18 -04:00
|
|
|
// layout shows by example how the reference time should be represented.
|
|
|
|
const layout = "2006-01-02_15-04-05"
|
|
|
|
t := time.Now()
|
2014-04-26 12:22:10 -04:00
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
if !p.config.Metadata {
|
2015-06-10 15:30:18 -04:00
|
|
|
return metadata
|
|
|
|
}
|
|
|
|
for _, f := range files {
|
|
|
|
if fi, err := os.Stat(f); err != nil {
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
if i, ok := metadata[filepath.Base(f)]; !ok {
|
2015-06-12 20:25:09 -04:00
|
|
|
metadata[filepath.Base(f)] = Metaitem{CompType: p.config.Format, OrigSize: fi.Size(), CompDate: t.Format(layout)}
|
2015-06-10 15:30:18 -04:00
|
|
|
} else {
|
|
|
|
i.CompSize = fi.Size()
|
|
|
|
i.CompDate = t.Format(layout)
|
|
|
|
metadata[filepath.Base(f)] = i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return metadata
|
|
|
|
}
|
|
|
|
|
2015-06-10 16:46:21 -04:00
|
|
|
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
|
2015-06-12 20:25:09 -04:00
|
|
|
newartifact := &Artifact{Path: p.config.OutputPath}
|
|
|
|
metafile := filepath.Join(p.config.OutputPath, "metadata")
|
2015-06-10 15:30:18 -04:00
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
ui.Say(fmt.Sprintf("[CBEDNARSKI] Creating archive at %s", newartifact.Path))
|
|
|
|
_, err := os.Stat(newartifact.Path)
|
2015-06-10 15:30:18 -04:00
|
|
|
if err == nil {
|
2015-06-12 20:25:09 -04:00
|
|
|
return nil, false, fmt.Errorf("output dir %s must not exists", newartifact.Path)
|
2015-06-10 15:30:18 -04:00
|
|
|
}
|
2015-06-12 20:25:09 -04:00
|
|
|
err = os.MkdirAll(newartifact.Path, 0755)
|
2014-04-26 12:22:10 -04:00
|
|
|
if err != nil {
|
2015-06-10 15:30:18 -04:00
|
|
|
return nil, false, fmt.Errorf("failed to create output: %s", err)
|
|
|
|
}
|
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
p.config.Format += "tar.gzip"
|
|
|
|
formats := strings.Split(p.config.Format, ".")
|
|
|
|
ui.Say(fmt.Sprintf("[CBEDNARSKI] Formats length %d", len(formats)))
|
|
|
|
if len(p.config.Format) == 0 {
|
|
|
|
ui.Say("[CBEDNARSKI] Formats is empty")
|
|
|
|
formats[0] = "tar.gzip"
|
|
|
|
}
|
2015-06-10 15:30:18 -04:00
|
|
|
files := artifact.Files()
|
|
|
|
|
|
|
|
metadata := make(Metadata, 0)
|
|
|
|
metadata = p.fillMetadata(metadata, files)
|
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
ui.Say(fmt.Sprintf("[CBEDNARSKI] Formats %#v", formats))
|
|
|
|
|
2015-06-10 15:30:18 -04:00
|
|
|
for _, compress := range formats {
|
|
|
|
switch compress {
|
|
|
|
case "tar":
|
2015-06-12 20:25:09 -04:00
|
|
|
files, err = p.cmpTAR(files, filepath.Join(p.config.OutputPath, p.config.OutputFile))
|
2015-06-10 15:30:18 -04:00
|
|
|
metadata = p.fillMetadata(metadata, files)
|
|
|
|
case "zip":
|
2015-06-12 20:25:09 -04:00
|
|
|
files, err = p.cmpZIP(files, filepath.Join(p.config.OutputPath, p.config.OutputFile))
|
2015-06-10 15:30:18 -04:00
|
|
|
metadata = p.fillMetadata(metadata, files)
|
|
|
|
case "pgzip":
|
2015-06-12 20:25:09 -04:00
|
|
|
files, err = p.cmpPGZIP(files, p.config.OutputPath)
|
2015-06-10 15:30:18 -04:00
|
|
|
metadata = p.fillMetadata(metadata, files)
|
|
|
|
case "gzip":
|
2015-06-12 20:25:09 -04:00
|
|
|
files, err = p.cmpGZIP(files, p.config.OutputPath)
|
2015-06-10 15:30:18 -04:00
|
|
|
metadata = p.fillMetadata(metadata, files)
|
|
|
|
case "bgzf":
|
2015-06-12 20:25:09 -04:00
|
|
|
files, err = p.cmpBGZF(files, p.config.OutputPath)
|
2015-06-10 15:30:18 -04:00
|
|
|
metadata = p.fillMetadata(metadata, files)
|
|
|
|
case "lz4":
|
2015-06-12 20:25:09 -04:00
|
|
|
files, err = p.cmpLZ4(files, p.config.OutputPath)
|
2015-06-10 15:30:18 -04:00
|
|
|
metadata = p.fillMetadata(metadata, files)
|
|
|
|
case "e2fs":
|
2015-06-12 20:25:09 -04:00
|
|
|
files, err = p.cmpE2FS(files, filepath.Join(p.config.OutputPath, p.config.OutputFile))
|
2015-06-10 15:30:18 -04:00
|
|
|
metadata = p.fillMetadata(metadata, files)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, false, fmt.Errorf("Failed to compress: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
if p.config.Metadata {
|
2015-06-10 15:30:18 -04:00
|
|
|
fp, err := os.Create(metafile)
|
|
|
|
if err != nil {
|
|
|
|
return nil, false, err
|
|
|
|
}
|
|
|
|
if buf, err := yaml.Marshal(metadata); err != nil {
|
|
|
|
fp.Close()
|
|
|
|
return nil, false, err
|
|
|
|
} else {
|
|
|
|
if _, err = fp.Write(buf); err != nil {
|
|
|
|
fp.Close()
|
|
|
|
return nil, false, err
|
|
|
|
}
|
|
|
|
fp.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
newartifact.files = append(newartifact.files, files...)
|
|
|
|
if p.config.Metadata {
|
|
|
|
newartifact.files = append(newartifact.files, metafile)
|
2015-06-10 15:30:18 -04:00
|
|
|
}
|
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
return newartifact, p.config.KeepInputArtifact, nil
|
2015-06-10 15:30:18 -04:00
|
|
|
}
|
|
|
|
|
2015-06-10 16:46:21 -04:00
|
|
|
func (p *PostProcessor) cmpTAR(src []string, dst string) ([]string, error) {
|
2015-06-10 15:30:18 -04:00
|
|
|
fw, err := os.Create(dst)
|
|
|
|
if err != nil {
|
2015-06-12 20:25:09 -04:00
|
|
|
return nil, fmt.Errorf("tar error creating tar %s: %s", dst, err)
|
2014-04-26 12:22:10 -04:00
|
|
|
}
|
|
|
|
defer fw.Close()
|
|
|
|
|
2015-06-10 15:30:18 -04:00
|
|
|
tw := tar.NewWriter(fw)
|
|
|
|
defer tw.Close()
|
2014-04-26 12:22:10 -04:00
|
|
|
|
2015-06-10 15:30:18 -04:00
|
|
|
for _, name := range src {
|
|
|
|
fi, err := os.Stat(name)
|
2014-04-26 12:22:10 -04:00
|
|
|
if err != nil {
|
2015-06-12 20:25:09 -04:00
|
|
|
return nil, fmt.Errorf("tar error on stat of %s: %s", name, err)
|
2014-04-26 12:22:10 -04:00
|
|
|
}
|
|
|
|
|
2015-06-16 15:10:28 -04:00
|
|
|
target, _ := os.Readlink(path)
|
2014-04-26 12:22:10 -04:00
|
|
|
header, err := tar.FileInfoHeader(fi, target)
|
|
|
|
if err != nil {
|
2015-06-12 20:25:09 -04:00
|
|
|
return nil, fmt.Errorf("tar error reading info for %s: %s", name, err)
|
2014-04-26 12:22:10 -04:00
|
|
|
}
|
|
|
|
|
2015-06-10 15:30:18 -04:00
|
|
|
if err = tw.WriteHeader(header); err != nil {
|
2015-06-12 20:25:09 -04:00
|
|
|
return nil, fmt.Errorf("tar error writing header for %s: %s", name, err)
|
2014-04-26 12:22:10 -04:00
|
|
|
}
|
|
|
|
|
2015-06-10 15:30:18 -04:00
|
|
|
fr, err := os.Open(name)
|
2014-04-26 12:22:10 -04:00
|
|
|
if err != nil {
|
2015-06-12 20:25:09 -04:00
|
|
|
return nil, fmt.Errorf("tar error opening file %s: %s", name, err)
|
2014-04-26 12:22:10 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if _, err = io.Copy(tw, fr); err != nil {
|
2015-06-10 15:30:18 -04:00
|
|
|
fr.Close()
|
2015-06-12 20:25:09 -04:00
|
|
|
return nil, fmt.Errorf("tar error copying contents of %s: %s", name, err)
|
2015-06-10 15:30:18 -04:00
|
|
|
}
|
|
|
|
fr.Close()
|
|
|
|
}
|
|
|
|
return []string{dst}, nil
|
|
|
|
}
|
|
|
|
|
2015-06-10 16:46:21 -04:00
|
|
|
func (p *PostProcessor) cmpGZIP(src []string, dst string) ([]string, error) {
|
2015-06-10 15:30:18 -04:00
|
|
|
var res []string
|
|
|
|
for _, name := range src {
|
|
|
|
filename := filepath.Join(dst, filepath.Base(name))
|
|
|
|
fw, err := os.Create(filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("gzip error: %s", err)
|
|
|
|
}
|
2015-06-12 20:25:09 -04:00
|
|
|
cw, err := gzip.NewWriterLevel(fw, p.config.Compression)
|
2015-06-10 15:30:18 -04:00
|
|
|
if err != nil {
|
|
|
|
fw.Close()
|
|
|
|
return nil, fmt.Errorf("gzip error: %s", err)
|
|
|
|
}
|
|
|
|
fr, err := os.Open(name)
|
|
|
|
if err != nil {
|
|
|
|
cw.Close()
|
|
|
|
fw.Close()
|
|
|
|
return nil, fmt.Errorf("gzip error: %s", err)
|
|
|
|
}
|
|
|
|
if _, err = io.Copy(cw, fr); err != nil {
|
|
|
|
cw.Close()
|
|
|
|
fr.Close()
|
|
|
|
fw.Close()
|
|
|
|
return nil, fmt.Errorf("gzip error: %s", err)
|
|
|
|
}
|
|
|
|
cw.Close()
|
|
|
|
fr.Close()
|
|
|
|
fw.Close()
|
|
|
|
res = append(res, filename)
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
2015-06-10 16:46:21 -04:00
|
|
|
func (p *PostProcessor) cmpPGZIP(src []string, dst string) ([]string, error) {
|
2015-06-10 15:30:18 -04:00
|
|
|
var res []string
|
|
|
|
for _, name := range src {
|
|
|
|
filename := filepath.Join(dst, filepath.Base(name))
|
|
|
|
fw, err := os.Create(filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("pgzip error: %s", err)
|
|
|
|
}
|
2015-06-12 20:25:09 -04:00
|
|
|
cw, err := pgzip.NewWriterLevel(fw, p.config.Compression)
|
2015-06-10 15:30:18 -04:00
|
|
|
if err != nil {
|
|
|
|
fw.Close()
|
|
|
|
return nil, fmt.Errorf("pgzip error: %s", err)
|
|
|
|
}
|
|
|
|
fr, err := os.Open(name)
|
|
|
|
if err != nil {
|
|
|
|
cw.Close()
|
|
|
|
fw.Close()
|
|
|
|
return nil, fmt.Errorf("pgzip error: %s", err)
|
|
|
|
}
|
|
|
|
if _, err = io.Copy(cw, fr); err != nil {
|
|
|
|
cw.Close()
|
|
|
|
fr.Close()
|
|
|
|
fw.Close()
|
|
|
|
return nil, fmt.Errorf("pgzip error: %s", err)
|
|
|
|
}
|
|
|
|
cw.Close()
|
|
|
|
fr.Close()
|
|
|
|
fw.Close()
|
|
|
|
res = append(res, filename)
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
2015-06-10 16:46:21 -04:00
|
|
|
func (p *PostProcessor) cmpLZ4(src []string, dst string) ([]string, error) {
|
2015-06-10 15:30:18 -04:00
|
|
|
var res []string
|
|
|
|
for _, name := range src {
|
|
|
|
filename := filepath.Join(dst, filepath.Base(name))
|
|
|
|
fw, err := os.Create(filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("lz4 error: %s", err)
|
|
|
|
}
|
|
|
|
cw := lz4.NewWriter(fw)
|
|
|
|
if err != nil {
|
|
|
|
fw.Close()
|
|
|
|
return nil, fmt.Errorf("lz4 error: %s", err)
|
|
|
|
}
|
2015-06-12 20:25:09 -04:00
|
|
|
if p.config.Compression > flate.DefaultCompression {
|
2015-06-10 15:30:18 -04:00
|
|
|
cw.Header.HighCompression = true
|
|
|
|
}
|
|
|
|
fr, err := os.Open(name)
|
|
|
|
if err != nil {
|
|
|
|
cw.Close()
|
|
|
|
fw.Close()
|
|
|
|
return nil, fmt.Errorf("lz4 error: %s", err)
|
|
|
|
}
|
|
|
|
if _, err = io.Copy(cw, fr); err != nil {
|
|
|
|
cw.Close()
|
|
|
|
fr.Close()
|
|
|
|
fw.Close()
|
|
|
|
return nil, fmt.Errorf("lz4 error: %s", err)
|
|
|
|
}
|
|
|
|
cw.Close()
|
|
|
|
fr.Close()
|
|
|
|
fw.Close()
|
|
|
|
res = append(res, filename)
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
2015-06-10 16:46:21 -04:00
|
|
|
func (p *PostProcessor) cmpBGZF(src []string, dst string) ([]string, error) {
|
2015-06-10 15:30:18 -04:00
|
|
|
var res []string
|
|
|
|
for _, name := range src {
|
|
|
|
filename := filepath.Join(dst, filepath.Base(name))
|
|
|
|
fw, err := os.Create(filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("bgzf error: %s", err)
|
|
|
|
}
|
|
|
|
|
2015-06-12 20:25:09 -04:00
|
|
|
cw, err := bgzf.NewWriterLevel(fw, p.config.Compression, runtime.NumCPU())
|
2015-06-10 15:30:18 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("bgzf error: %s", err)
|
|
|
|
}
|
|
|
|
fr, err := os.Open(name)
|
|
|
|
if err != nil {
|
|
|
|
cw.Close()
|
|
|
|
fw.Close()
|
|
|
|
return nil, fmt.Errorf("bgzf error: %s", err)
|
|
|
|
}
|
|
|
|
if _, err = io.Copy(cw, fr); err != nil {
|
|
|
|
cw.Close()
|
|
|
|
fr.Close()
|
|
|
|
fw.Close()
|
|
|
|
return nil, fmt.Errorf("bgzf error: %s", err)
|
|
|
|
}
|
|
|
|
cw.Close()
|
|
|
|
fr.Close()
|
|
|
|
fw.Close()
|
|
|
|
res = append(res, filename)
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
2015-06-10 16:46:21 -04:00
|
|
|
func (p *PostProcessor) cmpE2FS(src []string, dst string) ([]string, error) {
|
2015-06-10 15:30:18 -04:00
|
|
|
panic("not implemented")
|
|
|
|
}
|
|
|
|
|
2015-06-10 16:46:21 -04:00
|
|
|
func (p *PostProcessor) cmpZIP(src []string, dst string) ([]string, error) {
|
2015-06-10 15:30:18 -04:00
|
|
|
fw, err := os.Create(dst)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("zip error: %s", err)
|
|
|
|
}
|
|
|
|
defer fw.Close()
|
|
|
|
|
|
|
|
zw := zip.NewWriter(fw)
|
|
|
|
defer zw.Close()
|
|
|
|
|
|
|
|
for _, name := range src {
|
|
|
|
header, err := zw.Create(name)
|
|
|
|
if err != nil {
|
2015-06-10 16:46:21 -04:00
|
|
|
return nil, fmt.Errorf("zip error: %s", err)
|
2015-06-10 15:30:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fr, err := os.Open(name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("zip error: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err = io.Copy(header, fr); err != nil {
|
|
|
|
fr.Close()
|
|
|
|
return nil, fmt.Errorf("zip error: %s", err)
|
2014-04-26 12:22:10 -04:00
|
|
|
}
|
2015-06-10 15:30:18 -04:00
|
|
|
fr.Close()
|
2014-04-26 12:22:10 -04:00
|
|
|
}
|
2015-06-10 15:30:18 -04:00
|
|
|
return []string{dst}, nil
|
2014-04-26 12:22:10 -04:00
|
|
|
|
|
|
|
}
|