diff --git a/builder/vmware/iso/artifact.go b/builder/vmware/iso/artifact.go index a0c3ceace..026b4580f 100644 --- a/builder/vmware/iso/artifact.go +++ b/builder/vmware/iso/artifact.go @@ -4,6 +4,12 @@ import ( "fmt" ) +const ( + ArtifactConfFormat = "artifact.conf.format" + ArtifactConfKeepRegistered = "artifact.conf.keep_registered" + ArtifactConfSkipExport = "artifact.conf.skip_export" +) + // Artifact is the result of running the VMware builder, namely a set // of files associated with the resulting machine. type Artifact struct { @@ -11,6 +17,7 @@ type Artifact struct { id string dir OutputDir f []string + config map[string]string } func (a *Artifact) BuilderId() string { @@ -30,7 +37,7 @@ func (a *Artifact) String() string { } func (a *Artifact) State(name string) interface{} { - return nil + return a.config[name] } func (a *Artifact) Destroy() error { diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 44db307a7..5fa15317f 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "log" "os" + "strconv" "time" vmwcommon "github.com/hashicorp/packer/builder/vmware/common" @@ -343,7 +344,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // Compile the artifact list var files []string - if b.config.RemoteType != "" && b.config.Format != "" { + if b.config.RemoteType != "" && b.config.Format != "" && !b.config.SkipExport { dir = new(vmwcommon.LocalOutputDir) dir.SetOutputDir(exportOutputPath) files, err = dir.ListFiles() @@ -360,11 +361,17 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe builderId = BuilderIdESX } + config := make(map[string]string) + config[ArtifactConfKeepRegistered] = strconv.FormatBool(b.config.KeepRegistered) + config[ArtifactConfFormat] = b.config.Format + config[ArtifactConfSkipExport] = strconv.FormatBool(b.config.SkipExport) + return &Artifact{ builderId: builderId, id: b.config.VMName, dir: dir, f: files, + config: config, }, nil } diff --git a/builder/vmware/iso/step_register.go b/builder/vmware/iso/step_register.go index a90de5fa2..509b6e017 100644 --- a/builder/vmware/iso/step_register.go +++ b/builder/vmware/iso/step_register.go @@ -51,7 +51,7 @@ func (s *StepRegister) Cleanup(state multistep.StateBag) { } if remoteDriver, ok := driver.(RemoteDriver); ok { - if s.Format == "" { + if s.Format == "" || config.SkipExport { ui.Say("Unregistering virtual machine...") if err := remoteDriver.Unregister(s.registeredPath); err != nil { ui.Error(fmt.Sprintf("Error unregistering VM: %s", err)) diff --git a/post-processor/vsphere-template/post-processor.go b/post-processor/vsphere-template/post-processor.go index 86c9f54b4..f5fba9c4e 100644 --- a/post-processor/vsphere-template/post-processor.go +++ b/post-processor/vsphere-template/post-processor.go @@ -2,11 +2,13 @@ package vsphere_template import ( "context" + "errors" "fmt" "net/url" "strings" "time" + "github.com/hashicorp/packer/builder/vmware/iso" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -88,13 +90,14 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId()) } - source := "" - for _, path := range artifact.Files() { - if strings.HasSuffix(path, ".vmx") { - source = path - break - } + f := artifact.State(iso.ArtifactConfFormat) + k := artifact.State(iso.ArtifactConfKeepRegistered) + s := artifact.State(iso.ArtifactConfSkipExport) + + if f != "" && k != "true" && s == "false" { + return nil, false, errors.New("To use this post-processor with exporting behavior you need set keep_registered as true") } + // In some occasions the VM state is powered on and if we immediately try to mark as template // (after the ESXi creates it) it will fail. If vSphere is given a few seconds this behavior doesn't reappear. ui.Message("Waiting 10s for VMware vSphere to start") @@ -119,12 +122,10 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac }, &stepMarkAsTemplate{ VMName: artifact.Id(), - Source: source, }, } runner := common.NewRunnerWithPauseFn(steps, p.config.PackerConfig, ui, state) runner.Run(state) - if rawErr, ok := state.GetOk("error"); ok { return nil, false, rawErr.(error) } diff --git a/post-processor/vsphere-template/step_mark_as_template.go b/post-processor/vsphere-template/step_mark_as_template.go index 0e5465054..7dc9211fa 100644 --- a/post-processor/vsphere-template/step_mark_as_template.go +++ b/post-processor/vsphere-template/step_mark_as_template.go @@ -4,17 +4,18 @@ import ( "context" "fmt" "path" + "regexp" "strings" "github.com/hashicorp/packer/packer" "github.com/mitchellh/multistep" "github.com/vmware/govmomi" "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/types" ) type stepMarkAsTemplate struct { VMName string - Source string } func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction { @@ -32,6 +33,19 @@ func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction return multistep.ActionHalt } + if err := unregisterPreviousVM(cli, folder, s.VMName); err != nil { + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + dsPath, err := datastorePath(vm) + if err != nil { + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + host, err := vm.HostSystem(context.Background()) if err != nil { state.Put("error", err) @@ -45,21 +59,7 @@ func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction return multistep.ActionHalt } - source := strings.Split(s.Source, "/vmfs/volumes/")[1] - i := strings.Index(source, "/") - - path := (&object.DatastorePath{ - Datastore: source[:i], - Path: source[i:], - }).String() - - if err := unregisterPreviousVM(cli, folder, s.VMName); err != nil { - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - task, err := folder.RegisterVM(context.Background(), path, s.VMName, true, nil, host) + task, err := folder.RegisterVM(context.Background(), dsPath.String(), s.VMName, true, nil, host) if err != nil { state.Put("error", err) ui.Error(err.Error()) @@ -75,6 +75,34 @@ func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction return multistep.ActionContinue } +func datastorePath(vm *object.VirtualMachine) (*object.DatastorePath, error) { + devices, err := vm.Device(context.Background()) + if err != nil { + return nil, err + } + + disk := "" + for _, device := range devices { + if d, ok := device.(*types.VirtualDisk); ok { + if b, ok := d.Backing.(types.BaseVirtualDeviceFileBackingInfo); ok { + disk = b.GetVirtualDeviceFileBackingInfo().FileName + } + break + } + } + + if disk == "" { + return nil, fmt.Errorf("disk not found in '%v'", vm.Name()) + } + + re := regexp.MustCompile("\\[(.*?)\\]") + + datastore := re.FindStringSubmatch(disk)[1] + vmxPath := path.Join("/", path.Dir(strings.Split(disk, " ")[1]), vm.Name()+".vmx") + + return &object.DatastorePath{datastore, vmxPath}, nil +} + // We will use the virtual machine created by vmware-iso builder func findRuntimeVM(cli *govmomi.Client, dcPath, name string) (*object.VirtualMachine, error) { si := object.NewSearchIndex(cli.Client) @@ -89,7 +117,12 @@ func findRuntimeVM(cli *govmomi.Client, dcPath, name string) (*object.VirtualMac return nil, fmt.Errorf("VM at path %s not found", fullPath) } - return ref.(*object.VirtualMachine), nil + vm := ref.(*object.VirtualMachine) + if vm.InventoryPath == "" { + vm.SetInventoryPath(fullPath) + } + + return vm, nil } // If in the target folder a virtual machine or template already exists