From ee6a11809009b33dece40fa98e97831f52467650 Mon Sep 17 00:00:00 2001 From: Julian Phillips Date: Sun, 11 May 2014 14:36:16 +0100 Subject: [PATCH 1/4] Configure MessagePack to be a bit more like gob By default codec's MessagePack encode/decode will convert a string into a []byte. Tweak the settings so that string -> string and []byte -> []byte. --- packer/rpc/client.go | 7 +++++-- packer/rpc/server.go | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packer/rpc/client.go b/packer/rpc/client.go index 7e2ff00fc..5a338a4cf 100644 --- a/packer/rpc/client.go +++ b/packer/rpc/client.go @@ -40,8 +40,11 @@ func newClientWithMux(mux *muxBroker, streamId uint32) (*Client, error) { return nil, err } - var h codec.MsgpackHandle - clientCodec := codec.GoRpc.ClientCodec(clientConn, &h) + h := &codec.MsgpackHandle{ + RawToString: true, + WriteExt: true, + } + clientCodec := codec.GoRpc.ClientCodec(clientConn, h) return &Client{ mux: mux, diff --git a/packer/rpc/server.go b/packer/rpc/server.go index ca9691870..248fbdd0a 100644 --- a/packer/rpc/server.go +++ b/packer/rpc/server.go @@ -148,8 +148,11 @@ func (s *Server) Serve() { } defer stream.Close() - var h codec.MsgpackHandle - rpcCodec := codec.GoRpc.ServerCodec(stream, &h) + h := &codec.MsgpackHandle{ + RawToString: true, + WriteExt: true, + } + rpcCodec := codec.GoRpc.ServerCodec(stream, h) s.server.ServeCodec(rpcCodec) } From 90a57c411fba78acc3ef1f565b46f2cf00e96a83 Mon Sep 17 00:00:00 2001 From: Julian Phillips Date: Sun, 19 Jan 2014 18:32:44 +0000 Subject: [PATCH 2/4] Expand Artifact API to expose build state In order that something consuming an artifact can have access to extra builder specific data add the State method which allows the caller to ask for arbitary values by name. --- builder/amazon/common/artifact.go | 4 ++++ builder/digitalocean/artifact.go | 4 ++++ builder/docker/artifact_export.go | 4 ++++ builder/docker/artifact_import.go | 4 ++++ builder/googlecompute/artifact.go | 4 ++++ builder/null/artifact_export.go | 4 ++++ builder/openstack/artifact.go | 4 ++++ builder/parallels/common/artifact.go | 4 ++++ builder/qemu/artifact.go | 4 ++++ builder/virtualbox/common/artifact.go | 4 ++++ builder/vmware/common/artifact.go | 4 ++++ builder/vmware/iso/artifact.go | 4 ++++ packer/artifact.go | 4 ++++ packer/artifact_mock.go | 6 ++++++ packer/artifact_test.go | 6 ++++++ packer/rpc/artifact.go | 10 ++++++++++ post-processor/compress/artifact.go | 4 ++++ post-processor/vagrant-cloud/artifact.go | 4 ++++ post-processor/vagrant/artifact.go | 4 ++++ 19 files changed, 86 insertions(+) diff --git a/builder/amazon/common/artifact.go b/builder/amazon/common/artifact.go index 455994d59..8831d313b 100644 --- a/builder/amazon/common/artifact.go +++ b/builder/amazon/common/artifact.go @@ -52,6 +52,10 @@ func (a *Artifact) String() string { return fmt.Sprintf("AMIs were created:\n\n%s", strings.Join(amiStrings, "\n")) } +func (a *Artifact) State(name string) interface{} { + return nil +} + func (a *Artifact) Destroy() error { errors := make([]error, 0) diff --git a/builder/digitalocean/artifact.go b/builder/digitalocean/artifact.go index ebabdd41c..a6c8d8a7d 100644 --- a/builder/digitalocean/artifact.go +++ b/builder/digitalocean/artifact.go @@ -37,6 +37,10 @@ func (a *Artifact) String() string { return fmt.Sprintf("A snapshot was created: '%v' in region '%v'", a.snapshotName, a.regionName) } +func (a *Artifact) State(name string) interface{} { + return nil +} + func (a *Artifact) Destroy() error { log.Printf("Destroying image: %d (%s)", a.snapshotId, a.snapshotName) return a.client.DestroyImage(a.snapshotId) diff --git a/builder/docker/artifact_export.go b/builder/docker/artifact_export.go index 29cbefb48..d9eb83f67 100644 --- a/builder/docker/artifact_export.go +++ b/builder/docker/artifact_export.go @@ -27,6 +27,10 @@ func (a *ExportArtifact) String() string { return fmt.Sprintf("Exported Docker file: %s", a.path) } +func (a *ExportArtifact) State(name string) interface{} { + return nil +} + func (a *ExportArtifact) Destroy() error { return os.Remove(a.path) } diff --git a/builder/docker/artifact_import.go b/builder/docker/artifact_import.go index 4c926eb53..6ab0fe482 100644 --- a/builder/docker/artifact_import.go +++ b/builder/docker/artifact_import.go @@ -28,6 +28,10 @@ func (a *ImportArtifact) String() string { return fmt.Sprintf("Imported Docker image: %s", a.Id()) } +func (*ImportArtifact) State(name string) interface{} { + return nil +} + func (a *ImportArtifact) Destroy() error { return a.Driver.DeleteImage(a.Id()) } diff --git a/builder/googlecompute/artifact.go b/builder/googlecompute/artifact.go index a09946f9f..cb2ad88bb 100644 --- a/builder/googlecompute/artifact.go +++ b/builder/googlecompute/artifact.go @@ -37,3 +37,7 @@ func (a *Artifact) Id() string { func (a *Artifact) String() string { return fmt.Sprintf("A disk image was created: %v", a.imageName) } + +func (a *Artifact) State(name string) interface{} { + return nil +} diff --git a/builder/null/artifact_export.go b/builder/null/artifact_export.go index ad7bebc15..3962358e0 100644 --- a/builder/null/artifact_export.go +++ b/builder/null/artifact_export.go @@ -24,6 +24,10 @@ func (a *NullArtifact) String() string { return fmt.Sprintf("Did not export anything. This is the null builder") } +func (a *NullArtifact) State(name string) interface{} { + return nil +} + func (a *NullArtifact) Destroy() error { return nil } diff --git a/builder/openstack/artifact.go b/builder/openstack/artifact.go index a2ba552c5..035e245f6 100644 --- a/builder/openstack/artifact.go +++ b/builder/openstack/artifact.go @@ -35,6 +35,10 @@ func (a *Artifact) String() string { return fmt.Sprintf("An image was created: %v", a.ImageId) } +func (a *Artifact) State(name string) interface{} { + return nil +} + func (a *Artifact) Destroy() error { log.Printf("Destroying image: %d", a.ImageId) return a.Conn.DeleteImageById(a.ImageId) diff --git a/builder/parallels/common/artifact.go b/builder/parallels/common/artifact.go index 4930e0c32..8ce7755fa 100644 --- a/builder/parallels/common/artifact.go +++ b/builder/parallels/common/artifact.go @@ -66,6 +66,10 @@ func (a *artifact) String() string { return fmt.Sprintf("VM files in directory: %s", a.dir) } +func (a *artifact) State(name string) interface{} { + return nil +} + func (a *artifact) Destroy() error { return os.RemoveAll(a.dir) } diff --git a/builder/qemu/artifact.go b/builder/qemu/artifact.go index a3f1f9a46..1dea61c68 100644 --- a/builder/qemu/artifact.go +++ b/builder/qemu/artifact.go @@ -28,6 +28,10 @@ func (a *Artifact) String() string { return fmt.Sprintf("VM files in directory: %s", a.dir) } +func (a *Artifact) State(name string) interface{} { + return nil +} + func (a *Artifact) Destroy() error { return os.RemoveAll(a.dir) } diff --git a/builder/virtualbox/common/artifact.go b/builder/virtualbox/common/artifact.go index f4fc48100..9755c7a44 100644 --- a/builder/virtualbox/common/artifact.go +++ b/builder/virtualbox/common/artifact.go @@ -56,6 +56,10 @@ func (a *artifact) String() string { return fmt.Sprintf("VM files in directory: %s", a.dir) } +func (a *artifact) State(name string) interface{} { + return nil +} + func (a *artifact) Destroy() error { return os.RemoveAll(a.dir) } diff --git a/builder/vmware/common/artifact.go b/builder/vmware/common/artifact.go index ddc2b1a35..710ed9aad 100644 --- a/builder/vmware/common/artifact.go +++ b/builder/vmware/common/artifact.go @@ -56,6 +56,10 @@ func (a *localArtifact) String() string { return fmt.Sprintf("VM files in directory: %s", a.dir) } +func (a *localArtifact) State(name string) interface{} { + return nil +} + func (a *localArtifact) Destroy() error { return os.RemoveAll(a.dir) } diff --git a/builder/vmware/iso/artifact.go b/builder/vmware/iso/artifact.go index d6ddf8921..d0e1a6d8b 100644 --- a/builder/vmware/iso/artifact.go +++ b/builder/vmware/iso/artifact.go @@ -28,6 +28,10 @@ func (a *Artifact) String() string { return fmt.Sprintf("VM files in directory: %s", a.dir) } +func (a *Artifact) State(name string) interface{} { + return nil +} + func (a *Artifact) Destroy() error { return a.dir.RemoveAll() } diff --git a/packer/artifact.go b/packer/artifact.go index 486c322f6..8eaf3bcd5 100644 --- a/packer/artifact.go +++ b/packer/artifact.go @@ -25,6 +25,10 @@ type Artifact interface { // This is used for UI output. It can be multiple lines. String() string + // State allows the caller to ask for builder specific state information + // relating to the artifact instance. + State(name string) interface{} + // Destroy deletes the artifact. Packer calls this for various reasons, // such as if a post-processor has processed this artifact and it is // no longer needed. diff --git a/packer/artifact_mock.go b/packer/artifact_mock.go index a59af5331..18f4e562f 100644 --- a/packer/artifact_mock.go +++ b/packer/artifact_mock.go @@ -5,6 +5,7 @@ type MockArtifact struct { BuilderIdValue string FilesValue []string IdValue string + StateValues map[string]interface{} DestroyCalled bool } @@ -37,6 +38,11 @@ func (*MockArtifact) String() string { return "string" } +func (a *MockArtifact) State(name string) interface{} { + value, _ := a.StateValues[name] + return value +} + func (a *MockArtifact) Destroy() error { a.DestroyCalled = true return nil diff --git a/packer/artifact_test.go b/packer/artifact_test.go index ab6f39fb4..4ddc5f0ac 100644 --- a/packer/artifact_test.go +++ b/packer/artifact_test.go @@ -2,6 +2,7 @@ package packer type TestArtifact struct { id string + state map[string]interface{} destroyCalled bool } @@ -26,6 +27,11 @@ func (*TestArtifact) String() string { return "string" } +func (a *TestArtifact) State(name string) interface{} { + value, _ := a.state[name] + return value +} + func (a *TestArtifact) Destroy() error { a.destroyCalled = true return nil diff --git a/packer/rpc/artifact.go b/packer/rpc/artifact.go index c6e0a208d..3532cfbad 100644 --- a/packer/rpc/artifact.go +++ b/packer/rpc/artifact.go @@ -38,6 +38,11 @@ func (a *artifact) String() (result string) { return } +func (a *artifact) State(name string) (result interface{}) { + a.client.Call(a.endpoint+".State", name, &result) + return +} + func (a *artifact) Destroy() error { var result error if err := a.client.Call(a.endpoint+".Destroy", new(interface{}), &result); err != nil { @@ -67,6 +72,11 @@ func (s *ArtifactServer) String(args *interface{}, reply *string) error { return nil } +func (s *ArtifactServer) State(name string, reply *interface{}) error { + *reply = s.artifact.State(name) + return nil +} + func (s *ArtifactServer) Destroy(args *interface{}, reply *error) error { err := s.artifact.Destroy() if err != nil { diff --git a/post-processor/compress/artifact.go b/post-processor/compress/artifact.go index 54985d529..34a7ce8d6 100644 --- a/post-processor/compress/artifact.go +++ b/post-processor/compress/artifact.go @@ -35,6 +35,10 @@ func (self *Artifact) String() string { return fmt.Sprintf("'%s' compressing: %s", self.Provider, self.Path) } +func (*Artifact) State(name string) interface{} { + return nil +} + func (self *Artifact) Destroy() error { return os.Remove(self.Path) } diff --git a/post-processor/vagrant-cloud/artifact.go b/post-processor/vagrant-cloud/artifact.go index 775bc2998..9f1d4f4bd 100644 --- a/post-processor/vagrant-cloud/artifact.go +++ b/post-processor/vagrant-cloud/artifact.go @@ -34,6 +34,10 @@ func (a *Artifact) String() string { return fmt.Sprintf("'%s': %s", a.Provider, a.Tag) } +func (*Artifact) State(name string) interface{} { + return nil +} + func (a *Artifact) Destroy() error { return nil } diff --git a/post-processor/vagrant/artifact.go b/post-processor/vagrant/artifact.go index c4cfe394b..5974e9901 100644 --- a/post-processor/vagrant/artifact.go +++ b/post-processor/vagrant/artifact.go @@ -35,6 +35,10 @@ func (a *Artifact) String() string { return fmt.Sprintf("'%s' provider box: %s", a.Provider, a.Path) } +func (a *Artifact) State(name string) interface{} { + return nil +} + func (a *Artifact) Destroy() error { return os.Remove(a.Path) } From 60e608dfdb793a3dc546a8d391f32ff739da8d64 Mon Sep 17 00:00:00 2001 From: Julian Phillips Date: Mon, 6 Jan 2014 01:02:30 +0000 Subject: [PATCH 3/4] Add support for vagrant with qemu Once we have produced a qemu VM, we now have the option of using the vagrant post-processor to create a .box file that can be used with the vagrant-libvirt plugin. This uses the new State method of the Artifact API to get necessary information from the builder. --- builder/qemu/artifact.go | 7 +-- builder/qemu/builder.go | 6 +++ builder/qemu/step_create_disk.go | 6 ++- post-processor/vagrant/libvirt.go | 64 ++++++++++++++++++++++++ post-processor/vagrant/post-processor.go | 3 ++ 5 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 post-processor/vagrant/libvirt.go diff --git a/builder/qemu/artifact.go b/builder/qemu/artifact.go index 1dea61c68..ac841df08 100644 --- a/builder/qemu/artifact.go +++ b/builder/qemu/artifact.go @@ -8,8 +8,9 @@ import ( // Artifact is the result of running the Qemu builder, namely a set // of files associated with the resulting machine. type Artifact struct { - dir string - f []string + dir string + f []string + state map[string]interface{} } func (*Artifact) BuilderId() string { @@ -29,7 +30,7 @@ func (a *Artifact) String() string { } func (a *Artifact) State(name string) interface{} { - return nil + return a.state[name] } func (a *Artifact) Destroy() error { diff --git a/builder/qemu/builder.go b/builder/qemu/builder.go index 971613dd8..ef74fa17e 100644 --- a/builder/qemu/builder.go +++ b/builder/qemu/builder.go @@ -481,8 +481,14 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe artifact := &Artifact{ dir: b.config.OutputDir, f: files, + state: make(map[string]interface{}), } + artifact.state["diskName"] = state.Get("disk_filename").(string) + artifact.state["diskType"] = b.config.Format + artifact.state["diskSize"] = uint64(b.config.DiskSize) + artifact.state["domainType"] = b.config.Accelerator + return artifact, nil } diff --git a/builder/qemu/step_create_disk.go b/builder/qemu/step_create_disk.go index 7e6f09b7d..4cd98904a 100644 --- a/builder/qemu/step_create_disk.go +++ b/builder/qemu/step_create_disk.go @@ -16,8 +16,8 @@ func (s *stepCreateDisk) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*config) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) - path := filepath.Join(config.OutputDir, fmt.Sprintf("%s.%s", config.VMName, - strings.ToLower(config.Format))) + name := config.VMName + "." + strings.ToLower(config.Format) + path := filepath.Join(config.OutputDir, name) command := []string{ "create", @@ -34,6 +34,8 @@ func (s *stepCreateDisk) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } + state.Put("disk_filename", name) + return multistep.ActionContinue } diff --git a/post-processor/vagrant/libvirt.go b/post-processor/vagrant/libvirt.go new file mode 100644 index 000000000..6ab0b3900 --- /dev/null +++ b/post-processor/vagrant/libvirt.go @@ -0,0 +1,64 @@ +package vagrant + +import ( + "fmt" + "github.com/mitchellh/packer/packer" + "path/filepath" + "strings" +) + +type LibVirtProvider struct{} + +func (p *LibVirtProvider) KeepInputArtifact() bool { + return false +} +func (p *LibVirtProvider) Process(ui packer.Ui, artifact packer.Artifact, dir string) (vagrantfile string, metadata map[string]interface{}, err error) { + diskName := artifact.State("diskName").(string) + + // Copy the disk image into the temporary directory (as box.img) + for _, path := range artifact.Files() { + if strings.HasSuffix(path, "/"+diskName) { + ui.Message(fmt.Sprintf("Copying from artifact: %s", path)) + dstPath := filepath.Join(dir, "box.img") + if err = CopyContents(dstPath, path); err != nil { + return + } + } + } + + format := artifact.State("diskType").(string) + origSize := artifact.State("diskSize").(uint64) + size := origSize / 1024 // In MB, want GB + if origSize % 1024 > 0 { + // Make sure we don't make the size smaller + size++ + } + domainType := artifact.State("domainType").(string) + + // Convert domain type to libvirt driver + var driver string + switch domainType { + case "kvm", "qemu": + driver = domainType + default: + return "", nil, fmt.Errorf("Unknown libvirt domain type: %s", domainType) + } + + // Create the metadata + metadata = map[string]interface{}{ + "provider": "libvirt", + "format": format, + "virtual_size": size, + } + + vagrantfile = fmt.Sprintf(libvirtVagrantfile, driver) + return +} + +var libvirtVagrantfile = ` +Vagrant.configure("2") do |config| + config.vm.provider :libvirt do |libvirt| + libvirt.driver = "%s" + end +end +` diff --git a/post-processor/vagrant/post-processor.go b/post-processor/vagrant/post-processor.go index 8fb486edb..bdf1cf2cf 100644 --- a/post-processor/vagrant/post-processor.go +++ b/post-processor/vagrant/post-processor.go @@ -23,6 +23,7 @@ var builtins = map[string]string{ "pearkes.digitalocean": "digitalocean", "packer.parallels": "parallels", "MSOpenTech.hyperv": "hyperv", + "transcend.qemu": "libvirt", } type Config struct { @@ -223,6 +224,8 @@ func providerForName(name string) Provider { return new(ParallelsProvider) case "hyperv": return new(HypervProvider) + case "libvirt": + return new(LibVirtProvider) default: return nil } From 660e82e68996ecac15d80ce48ad7d5996dce0f6f Mon Sep 17 00:00:00 2001 From: Julian Phillips Date: Thu, 10 Jul 2014 20:09:21 +0100 Subject: [PATCH 4/4] Update vagrant supported builder list Update the list of supported builders in the vagrant post-processor docs to include QEMU. --- website/source/docs/post-processors/vagrant.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/source/docs/post-processors/vagrant.html.markdown b/website/source/docs/post-processors/vagrant.html.markdown index 6950b2d90..a1d4203fd 100644 --- a/website/source/docs/post-processors/vagrant.html.markdown +++ b/website/source/docs/post-processors/vagrant.html.markdown @@ -30,6 +30,7 @@ providers. * DigitalOcean * Hyper-V * Parallels +* QEMU * VirtualBox * VMware