Merge pull request #5567 from bennu/vm-template-with-export-vm

enable vsphere-template post processor to work with export behavior
This commit is contained in:
Matthew Hooker 2017-11-09 15:24:41 -08:00 committed by GitHub
commit f146e5903f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 76 additions and 28 deletions

View File

@ -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 {

View File

@ -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
}

View File

@ -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))

View File

@ -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)
}

View File

@ -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