From a4de58b5f6a6dc4bf58b5bf4a8416e6d0782ff98 Mon Sep 17 00:00:00 2001 From: John Bellone Date: Sat, 26 Apr 2014 12:22:10 -0400 Subject: [PATCH 1/3] [post-processor/compress] Add support for compress. This commit adds support for a post-processor that produces a compressed archive that can be uploaded to an OpenStack cluster through either the Horizon web interface or Glance. --- plugin/post-processor-compress/main.go | 15 +++ plugin/post-processor-compress/main_test.go | 1 + post-processor/compress/artifact.go | 40 ++++++ post-processor/compress/artifact_test.go | 14 +++ post-processor/compress/post-processor.go | 118 ++++++++++++++++++ .../compress/post-processor_test.go | 6 + 6 files changed, 194 insertions(+) create mode 100644 plugin/post-processor-compress/main.go create mode 100644 plugin/post-processor-compress/main_test.go create mode 100644 post-processor/compress/artifact.go create mode 100644 post-processor/compress/artifact_test.go create mode 100644 post-processor/compress/post-processor.go create mode 100644 post-processor/compress/post-processor_test.go diff --git a/plugin/post-processor-compress/main.go b/plugin/post-processor-compress/main.go new file mode 100644 index 000000000..3acc85228 --- /dev/null +++ b/plugin/post-processor-compress/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/mitchellh/packer/packer/plugin" + "github.com/mitchellh/packer/post-processor/compress" +) + +func main() { + server, err := plugin.Server() + if err != nil { + panic(err) + } + server.RegisterPostProcessor(new(compress.PostProcessor)) + server.Serve() +} diff --git a/plugin/post-processor-compress/main_test.go b/plugin/post-processor-compress/main_test.go new file mode 100644 index 000000000..06ab7d0f9 --- /dev/null +++ b/plugin/post-processor-compress/main_test.go @@ -0,0 +1 @@ +package main diff --git a/post-processor/compress/artifact.go b/post-processor/compress/artifact.go new file mode 100644 index 000000000..bc0128d86 --- /dev/null +++ b/post-processor/compress/artifact.go @@ -0,0 +1,40 @@ +package compress + +import ( + "fmt" + "os" +) + +const BuilderId = "johnbellone.compress" + +type Artifact struct { + Path string + Provider string +} + +func NewArtifact(provider, path string) *Artifact { + return &Artifact{ + Path: path, + Provider: provider, + } +} + +func (*Artifact) BuilderId() string { + return BuilderId +} + +func (self *Artifact) Id() string { + return "" +} + +func (self *Artifact) Files() []string { + return []string{self.Path} +} + +func (self *Artifact) String() string { + return fmt.Sprintf("'%s' compressing: %s", self.Provider, self.Path) +} + +func (self *Artifact) Destroy() error { + return os.Remove(self.Path) +} diff --git a/post-processor/compress/artifact_test.go b/post-processor/compress/artifact_test.go new file mode 100644 index 000000000..108ed84e7 --- /dev/null +++ b/post-processor/compress/artifact_test.go @@ -0,0 +1,14 @@ +package compress + +import ( + "github.com/mitchellh/packer/packer" + "testing" +) + +func TestArtifact_ImplementsArtifact(t *testing.T) { + var raw interface{} + raw = &Artifact{} + if _, ok := raw.(packer.Artifact); !ok { + t.Fatalf("Artifact should be a Artifact!") + } +} diff --git a/post-processor/compress/post-processor.go b/post-processor/compress/post-processor.go new file mode 100644 index 000000000..54e2881ab --- /dev/null +++ b/post-processor/compress/post-processor.go @@ -0,0 +1,118 @@ +package compress + +import ( + "archive/tar" + "compress/gzip" + "fmt" + "io" + "os" + + "github.com/mitchellh/packer/common" + "github.com/mitchellh/packer/packer" +) + +type Config struct { + common.PackerConfig `mapstructure:",squash"` + + OutputPath string `mapstructure:"output_path"` + + tpl *packer.ConfigTemplate +} + +type PostProcessor struct { + config Config +} + +func (self *PostProcessor) Configure(raws ...interface{}) error { + _, err := common.DecodeConfig(&self.config, raws...) + if err != nil { + return err + } + + self.config.tpl, err = packer.NewConfigTemplate() + if err != nil { + return err + } + + templates := map[string]*string{ + "output_path": &self.config.OutputPath, + } + + errs := new(packer.MultiError) + for key, ptr := range templates { + if *ptr == "" { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("%s must be set", key)) + } + + *ptr, err = self.config.tpl.Process(*ptr, nil) + if err != nil { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("Error processing %s: %s", key, err)) + } + } + + if len(errs.Errors) > 0 { + return errs + } + + return nil + +} + +func (self *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { + ui.Say(fmt.Sprintf("Creating archive for '%s'", artifact.BuilderId())) + + // Create the compressed archive file at the appropriate OutputPath. + fw, err := os.Create(self.config.OutputPath) + if err != nil { + return nil, false, fmt.Errorf( + "Failed creating file for compressed archive: %s", self.config.OutputPath) + } + defer fw.Close() + + gw := gzip.NewWriter(fw) + defer gw.Close() + + // Iterate through all of the artifact's files and put them into the + // compressed archive using the tar/gzip writers. + for _, path := range artifact.Files() { + fi, err := os.Stat(path) + if err != nil { + return nil, false, fmt.Errorf( + "Failed stating file: %s", path) + } + + target, _ := os.Readlink(path) + header, err := tar.FileInfoHeader(fi, target) + if err != nil { + return nil, false, fmt.Errorf( + "Failed creating archive header: %s", path) + } + + tw := tar.NewWriter(gw) + defer tw.Close() + + // Write the header first to the archive. This takes partial data + // from the FileInfo that is grabbed by running the stat command. + if err := tw.WriteHeader(header); err != nil { + return nil, false, fmt.Errorf( + "Failed writing archive header: %s", path) + } + + // Open the target file for archiving and compressing. + fr, err := os.Open(path) + if err != nil { + return nil, false, fmt.Errorf( + "Failed opening file '%s' to write compressed archive.", path) + } + defer fr.Close() + + if _, err = io.Copy(tw, fr); err != nil { + return nil, false, fmt.Errorf( + "Failed copying file to archive: %s", path) + } + } + + return NewArtifact(artifact.BuilderId(), self.config.OutputPath), false, nil +} diff --git a/post-processor/compress/post-processor_test.go b/post-processor/compress/post-processor_test.go new file mode 100644 index 000000000..d16cf68ae --- /dev/null +++ b/post-processor/compress/post-processor_test.go @@ -0,0 +1,6 @@ +package compress + +import ( + "github.com/mitchellh/packer/packer" + "testing" +) From 7da7623d3817a50fd4122044bcfe73379bca0b60 Mon Sep 17 00:00:00 2001 From: John Bellone Date: Mon, 28 Apr 2014 20:30:36 -0400 Subject: [PATCH 2/3] Fix user variables to properly evaluate. --- post-processor/compress/post-processor.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/post-processor/compress/post-processor.go b/post-processor/compress/post-processor.go index 54e2881ab..dc732a57a 100644 --- a/post-processor/compress/post-processor.go +++ b/post-processor/compress/post-processor.go @@ -14,7 +14,7 @@ import ( type Config struct { common.PackerConfig `mapstructure:",squash"` - OutputPath string `mapstructure:"output_path"` + OutputPath string `mapstructure:"output"` tpl *packer.ConfigTemplate } @@ -33,6 +33,7 @@ func (self *PostProcessor) Configure(raws ...interface{}) error { if err != nil { return err } + self.config.tpl.UserVars = self.config.PackerUserVars templates := map[string]*string{ "output_path": &self.config.OutputPath, From a492ca7424fec29254bb4bcd2daf40afef5e363c Mon Sep 17 00:00:00 2001 From: John Bellone Date: Thu, 12 Jun 2014 16:45:37 -0400 Subject: [PATCH 3/3] Fix warnings about not utilizing test module. Make these tests green adn them figure out how to test the actual post-processor. --- post-processor/compress/post-processor.go | 7 +++---- post-processor/compress/post-processor_test.go | 5 +---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/post-processor/compress/post-processor.go b/post-processor/compress/post-processor.go index dc732a57a..2dc5e6d94 100644 --- a/post-processor/compress/post-processor.go +++ b/post-processor/compress/post-processor.go @@ -4,11 +4,10 @@ import ( "archive/tar" "compress/gzip" "fmt" - "io" - "os" - "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" + "io" + "os" ) type Config struct { @@ -36,7 +35,7 @@ func (self *PostProcessor) Configure(raws ...interface{}) error { self.config.tpl.UserVars = self.config.PackerUserVars templates := map[string]*string{ - "output_path": &self.config.OutputPath, + "output": &self.config.OutputPath, } errs := new(packer.MultiError) diff --git a/post-processor/compress/post-processor_test.go b/post-processor/compress/post-processor_test.go index d16cf68ae..92cbfc4b3 100644 --- a/post-processor/compress/post-processor_test.go +++ b/post-processor/compress/post-processor_test.go @@ -1,6 +1,3 @@ package compress -import ( - "github.com/mitchellh/packer/packer" - "testing" -) +import ()