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 527713e94..3f756a05d 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 131f1a481..f55ff8727 100644 --- a/builder/openstack/artifact.go +++ b/builder/openstack/artifact.go @@ -36,6 +36,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..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 { @@ -28,6 +29,10 @@ func (a *Artifact) String() string { return fmt.Sprintf("VM files in directory: %s", a.dir) } +func (a *Artifact) State(name string) interface{} { + return a.state[name] +} + func (a *Artifact) Destroy() error { return os.RemoveAll(a.dir) } diff --git a/builder/qemu/builder.go b/builder/qemu/builder.go index 79c2aa5ce..63ef873c0 100644 --- a/builder/qemu/builder.go +++ b/builder/qemu/builder.go @@ -502,8 +502,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 7545a19f0..0f51110d0 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", @@ -38,6 +38,8 @@ func (s *stepCreateDisk) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } + state.Put("disk_filename", name) + return multistep.ActionContinue } 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/packer/rpc/client.go b/packer/rpc/client.go index fcc15fe56..c2523868e 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 764f7c72e..a8b4e2a15 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) } 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) } 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 1365a3c3d..60690b936 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 { @@ -228,6 +229,8 @@ func providerForName(name string) Provider { return new(ParallelsProvider) case "hyperv": return new(HypervProvider) + case "libvirt": + return new(LibVirtProvider) default: return nil } diff --git a/website/source/docs/post-processors/vagrant.html.markdown b/website/source/docs/post-processors/vagrant.html.markdown index ab4ca2f86..d5be196a6 100644 --- a/website/source/docs/post-processors/vagrant.html.markdown +++ b/website/source/docs/post-processors/vagrant.html.markdown @@ -32,6 +32,7 @@ providers. * DigitalOcean * Hyper-V * Parallels +* QEMU * VirtualBox * VMware