diff --git a/artifact.go b/artifact.go index 8b71c55cd..ec5842b3b 100644 --- a/artifact.go +++ b/artifact.go @@ -1,15 +1,14 @@ package main import ( - "github.com/vmware/govmomi/object" - "context" + "github.com/jetbrains-infra/packer-builder-vsphere/driver" ) const BuilderId = "jetbrains.vsphere" type Artifact struct { Name string - VM *object.VirtualMachine + VM *driver.VirtualMachine } func (a *Artifact) BuilderId() string { @@ -33,11 +32,9 @@ func (a *Artifact) State(name string) interface{} { } func (a *Artifact) Destroy() error { - ctx := context.TODO() - task, err := a.VM.Destroy(ctx) + err := a.VM.Destroy() if err != nil { return err } - _, err = task.WaitForResult(ctx, nil) - return err + return nil } diff --git a/builder.go b/builder.go index adb8d6dc2..579c2b9c9 100644 --- a/builder.go +++ b/builder.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/packer/helper/communicator" gossh "golang.org/x/crypto/ssh" "github.com/hashicorp/packer/communicator/ssh" - "github.com/vmware/govmomi/object" + "github.com/jetbrains-infra/packer-builder-vsphere/driver" ) type Builder struct { @@ -93,7 +93,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe artifact := &Artifact{ Name: b.config.VMName, - VM: state.Get("vm").(*object.VirtualMachine), + VM: state.Get("vm").(*driver.VirtualMachine), } return artifact, nil } diff --git a/builder_acc_test.go b/builder_acc_test.go index 6e53f5cf0..99f15ce3a 100644 --- a/builder_acc_test.go +++ b/builder_acc_test.go @@ -7,7 +7,6 @@ import ( "github.com/hashicorp/packer/packer" "encoding/json" "math/rand" - "github.com/vmware/govmomi/object" "github.com/jetbrains-infra/packer-builder-vsphere/driver" ) @@ -42,7 +41,7 @@ func checkDefault(t *testing.T, name string, host string) builderT.TestCheckFunc d := testConn(t) vm := getVM(t, d, artifacts) - vmInfo, err := d.VMInfo(vm, "name", "parent", "runtime.host", "resourcePool", "layoutEx.disk") + vmInfo, err := vm.Info("name", "parent", "runtime.host", "resourcePool", "layoutEx.disk") if err != nil { t.Fatalf("Cannot read VM properties: %v", err) } @@ -52,7 +51,7 @@ func checkDefault(t *testing.T, name string, host string) builderT.TestCheckFunc } f := d.NewFolder(vmInfo.Parent) - folderPath, err := d.GetFolderPath(f) + folderPath, err := f.Path() if err != nil { t.Fatalf("Cannot read folder name: %v", err) } @@ -61,7 +60,7 @@ func checkDefault(t *testing.T, name string, host string) builderT.TestCheckFunc } h := d.NewHost(vmInfo.Runtime.Host) - hostInfo, err := d.HostInfo(h, "name") + hostInfo, err := h.Info("name") if err != nil { t.Fatal("Cannot read host properties: ", err) } @@ -71,7 +70,7 @@ func checkDefault(t *testing.T, name string, host string) builderT.TestCheckFunc } p := d.NewResourcePool(vmInfo.ResourcePool) - poolPath, err := d.GetResourcePoolPath(p) + poolPath, err := p.Path() if err != nil { t.Fatalf("Cannot read resource pool name: %v", err) } @@ -132,13 +131,13 @@ func checkFolder(t *testing.T, folder string) builderT.TestCheckFunc { d := testConn(t) vm := getVM(t, d, artifacts) - vmInfo, err := d.VMInfo(vm, "parent") + vmInfo, err := vm.Info("parent") if err != nil { t.Fatalf("Cannot read VM properties: %v", err) } f := d.NewFolder(vmInfo.Parent) - path, err := d.GetFolderPath(f) + path, err := f.Path() if err != nil { t.Fatalf("Cannot read folder name: %v", err) } @@ -170,13 +169,13 @@ func checkResourcePool(t *testing.T, pool string) builderT.TestCheckFunc { d := testConn(t) vm := getVM(t, d, artifacts) - vmInfo, err := d.VMInfo(vm, "resourcePool") + vmInfo, err := vm.Info("resourcePool") if err != nil { t.Fatalf("Cannot read VM properties: %v", err) } p := d.NewResourcePool(vmInfo.ResourcePool) - path, err := d.GetResourcePoolPath(p) + path, err := p.Path() if err != nil { t.Fatalf("Cannot read resource pool name: %v", err) } @@ -207,7 +206,7 @@ func checkLinkedClone(t *testing.T) builderT.TestCheckFunc { d := testConn(t) vm := getVM(t, d, artifacts) - vmInfo, err := d.VMInfo(vm, "layoutEx.disk") + vmInfo, err := vm.Info("layoutEx.disk") if err != nil { t.Fatalf("Cannot read VM properties: %v", err) } @@ -239,7 +238,7 @@ func checkSnapshot(t *testing.T) builderT.TestCheckFunc { d := testConn(t) vm := getVM(t, d, artifacts) - vmInfo, err := d.VMInfo(vm, "layoutEx.disk") + vmInfo, err := vm.Info("layoutEx.disk") if err != nil { t.Fatalf("Cannot read VM properties: %v", err) } @@ -273,7 +272,7 @@ func checkTemplate(t *testing.T) builderT.TestCheckFunc { d := testConn(t) vm := getVM(t, d, artifacts) - vmInfo, err := d.VMInfo(vm, "config.template") + vmInfo, err := vm.Info("config.template") if err != nil { t.Fatalf("Cannot read VM properties: %v", err) } @@ -315,7 +314,7 @@ func testConn(t *testing.T) *driver.Driver { return d } -func getVM(t *testing.T, d *driver.Driver, artifacts []packer.Artifact) *object.VirtualMachine { +func getVM(t *testing.T, d *driver.Driver, artifacts []packer.Artifact) *driver.VirtualMachine { artifactRaw := artifacts[0] artifact, _ := artifactRaw.(*Artifact) diff --git a/driver/datastore.go b/driver/datastore.go new file mode 100644 index 000000000..5e6e408cb --- /dev/null +++ b/driver/datastore.go @@ -0,0 +1,21 @@ +package driver + +import ( + "github.com/vmware/govmomi/object" +) + +type Datastore struct { + ds *object.Datastore + driver *Driver +} + +func (d *Driver) FindDatastore(name string) (*Datastore, error) { + ds, err := d.finder.Datastore(d.ctx, name) + if err != nil { + return nil, err + } + return &Datastore{ + ds: ds, + driver: d, + }, nil +} diff --git a/driver/driver.go b/driver/driver.go index 13718c226..30ddcce72 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -7,9 +7,6 @@ import ( "net/url" "fmt" "github.com/vmware/govmomi/object" - "github.com/vmware/govmomi/vim25/types" - "github.com/vmware/govmomi/vim25/mo" - "errors" "time" "github.com/vmware/govmomi/session" "github.com/vmware/govmomi/vim25/soap" @@ -31,25 +28,6 @@ type ConnectConfig struct { Datacenter string } -type CloneConfig struct { - Template string - VMName string - Folder string - Host string - ResourcePool string - Datastore string - LinkedClone bool -} - -type HardwareConfig struct { - CPUs int32 - CPUReservation int64 - CPULimit int64 - RAM int64 - RAMReservation int64 - RAMReserveAll bool -} - func NewDriver(config *ConnectConfig) (*Driver, error) { ctx := context.TODO() @@ -92,174 +70,3 @@ func NewDriver(config *ConnectConfig) (*Driver, error) { } return &d, nil } - -func (d *Driver) CloneVM(config *CloneConfig) (*object.VirtualMachine, error) { - template, err := d.finder.VirtualMachine(d.ctx, config.Template) - if err != nil { - return nil, err - } - - folder, err := d.finder.FolderOrDefault(d.ctx, fmt.Sprintf("/%v/vm/%v", d.datacenter.Name(), config.Folder)) - if err != nil { - return nil, err - } - - var relocateSpec types.VirtualMachineRelocateSpec - - pool, err := d.finder.ResourcePoolOrDefault(d.ctx, fmt.Sprintf("/%v/host/%v/Resources/%v", d.datacenter.Name(), config.Host, config.ResourcePool)) - if err != nil { - return nil, err - } - poolRef := pool.Reference() - relocateSpec.Pool = &poolRef - - if config.Datastore != "" { - datastore, err := d.finder.Datastore(d.ctx, config.Datastore) - if err != nil { - return nil, err - } - datastoreRef := datastore.Reference() - relocateSpec.Datastore = &datastoreRef - } - - var cloneSpec types.VirtualMachineCloneSpec - cloneSpec.Location = relocateSpec - cloneSpec.PowerOn = false - - if config.LinkedClone == true { - cloneSpec.Location.DiskMoveType = "createNewChildDiskBacking" - - var tpl mo.VirtualMachine - err = template.Properties(d.ctx, template.Reference(), []string{"snapshot"}, &tpl) - if err != nil { - return nil, err - } - if tpl.Snapshot == nil { - err = errors.New("`linked_clone=true`, but template has no snapshots") - return nil, err - } - cloneSpec.Snapshot = tpl.Snapshot.CurrentSnapshot - } - - task, err := template.Clone(d.ctx, folder, config.VMName, cloneSpec) - if err != nil { - return nil, err - } - - info, err := task.WaitForResult(d.ctx, nil) - if err != nil { - return nil, err - } - - vm := object.NewVirtualMachine(d.client.Client, info.Result.(types.ManagedObjectReference)) - return vm, nil -} - -func (d *Driver) DestroyVM(vm *object.VirtualMachine) error { - task, err := vm.Destroy(d.ctx) - if err != nil { - return err - } - _, err = task.WaitForResult(d.ctx, nil) - return err -} - -func (d *Driver) ConfigureVM(vm *object.VirtualMachine, config *HardwareConfig) error { - var confSpec types.VirtualMachineConfigSpec - confSpec.NumCPUs = config.CPUs - confSpec.MemoryMB = config.RAM - - var cpuSpec types.ResourceAllocationInfo - cpuSpec.Reservation = config.CPUReservation - cpuSpec.Limit = config.CPULimit - confSpec.CpuAllocation = &cpuSpec - - var ramSpec types.ResourceAllocationInfo - ramSpec.Reservation = config.RAMReservation - confSpec.MemoryAllocation = &ramSpec - - confSpec.MemoryReservationLockedToMax = &config.RAMReserveAll - - task, err := vm.Reconfigure(d.ctx, confSpec) - if err != nil { - return err - } - _, err = task.WaitForResult(d.ctx, nil) - return err -} - -func (d *Driver) PowerOn(vm *object.VirtualMachine) error { - task, err := vm.PowerOn(d.ctx) - if err != nil { - return err - } - _, err = task.WaitForResult(d.ctx, nil) - return err -} - -func (d *Driver) WaitForIP(vm *object.VirtualMachine) (string, error) { - ip, err := vm.WaitForIP(d.ctx) - if err != nil { - return "", err - } - return ip, nil -} - -func (d *Driver) PowerOff(vm *object.VirtualMachine) error { - state, err := vm.PowerState(d.ctx) - if err != nil { - return err - } - - if state == types.VirtualMachinePowerStatePoweredOff { - return nil - } - - task, err := vm.PowerOff(d.ctx) - if err != nil { - return err - } - _, err = task.WaitForResult(d.ctx, nil) - return err -} - -func (d *Driver) StartShutdown(vm *object.VirtualMachine) error { - err := vm.ShutdownGuest(d.ctx) - return err -} - -func (d *Driver) WaitForShutdown(vm *object.VirtualMachine, timeout time.Duration) error { - shutdownTimer := time.After(timeout) - for { - powerState, err := vm.PowerState(d.ctx) - if err != nil { - return err - } - if powerState == "poweredOff" { - break - } - - select { - case <-shutdownTimer: - err := errors.New("Timeout while waiting for machine to shut down.") - return err - default: - time.Sleep(1 * time.Second) - } - } - return nil -} - -func (d *Driver) CreateSnapshot(vm *object.VirtualMachine) error { - task, err := vm.CreateSnapshot(d.ctx, "Created by Packer", "", false, false) - if err != nil { - return err - } - _, err = task.WaitForResult(d.ctx, nil) - return err -} - -func (d *Driver) ConvertToTemplate(vm *object.VirtualMachine) error { - err := vm.MarkAsTemplate(d.ctx) - return err -} diff --git a/driver/folder.go b/driver/folder.go index 5c90ed4ba..5af053007 100644 --- a/driver/folder.go +++ b/driver/folder.go @@ -7,42 +7,61 @@ import ( "fmt" ) -func (d *Driver) NewFolder(ref *types.ManagedObjectReference) *object.Folder { - return object.NewFolder(d.client.Client, *ref) +type Folder struct { + driver *Driver + folder *object.Folder } -func (d *Driver) FolderInfo(folder *object.Folder, params ...string) (*mo.Folder, error) { +func (d *Driver) NewFolder(ref *types.ManagedObjectReference) *Folder { + return &Folder{ + folder: object.NewFolder(d.client.Client, *ref), + driver: d, + } +} + +func (d *Driver) FindFolder(name string) (*Folder, error) { + f, err := d.finder.Folder(d.ctx, fmt.Sprintf("/%v/vm/%v", d.datacenter.Name(), name)) + if err != nil { + return nil, err + } + return &Folder{ + folder: f, + driver: d, + }, nil +} + +func (f *Folder) Info(params ...string) (*mo.Folder, error) { var p []string if len(params) == 0 { p = []string{"*"} } else { p = params } - var folderInfo mo.Folder - err := folder.Properties(d.ctx, folder.Reference(), p, &folderInfo) + var info mo.Folder + err := f.folder.Properties(f.driver.ctx, f.folder.Reference(), p, &info) if err != nil { return nil, err } - return &folderInfo, nil + return &info, nil } -func (d *Driver) GetFolderPath(folder *object.Folder) (string, error) { - f, err := d.FolderInfo(folder, "name", "parent") +func (f *Folder) Path() (string, error) { + info, err := f.Info("name", "parent") if err != nil { return "", err } - if f.Parent.Type == "Datacenter" { + if info.Parent.Type == "Datacenter" { return "", nil } else { - parent := d.NewFolder(f.Parent) - parentPath, err := d.GetFolderPath(parent) + parent := f.driver.NewFolder(info.Parent) + path, err := parent.Path() if err != nil { return "", err } - if parentPath == "" { - return f.Name, nil + if path == "" { + return info.Name, nil } else { - return fmt.Sprintf("%v/%v", parentPath, f.Name), nil + return fmt.Sprintf("%v/%v", path, info.Name), nil } } } diff --git a/driver/host.go b/driver/host.go index 646c76b2d..084d9d3b2 100644 --- a/driver/host.go +++ b/driver/host.go @@ -2,25 +2,33 @@ package driver import ( "github.com/vmware/govmomi/object" - "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" + "github.com/vmware/govmomi/vim25/mo" ) -func (d *Driver) NewHost(ref *types.ManagedObjectReference) *object.HostSystem { - return object.NewHostSystem(d.client.Client, *ref) +type Host struct { + driver *Driver + host *object.HostSystem } -func (d *Driver) HostInfo(host *object.HostSystem, params ...string) (*mo.HostSystem, error){ +func (d *Driver) NewHost(ref *types.ManagedObjectReference) *Host { + return &Host{ + host: object.NewHostSystem(d.client.Client, *ref), + driver: d, + } +} + +func (h *Host) Info(params ...string) (*mo.HostSystem, error){ var p []string if len(params) == 0 { p = []string{"*"} } else { p = params } - var hostInfo mo.HostSystem - err := host.Properties(d.ctx, host.Reference(), p, &hostInfo) + var info mo.HostSystem + err := h.host.Properties(h.driver.ctx, h.host.Reference(), p, &info) if err != nil { return nil, err } - return &hostInfo, nil + return &info, nil } diff --git a/driver/resource_pool.go b/driver/resource_pool.go index c5f89921f..48553f661 100644 --- a/driver/resource_pool.go +++ b/driver/resource_pool.go @@ -2,47 +2,66 @@ package driver import ( "github.com/vmware/govmomi/object" - "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" + "github.com/vmware/govmomi/vim25/mo" "fmt" ) -func (d *Driver) NewResourcePool(ref *types.ManagedObjectReference) *object.ResourcePool { - return object.NewResourcePool(d.client.Client, *ref) +type ResourcePool struct { + pool *object.ResourcePool + driver *Driver } -func (d *Driver) ResourcePoolInfo(host *object.ResourcePool, params ...string) (*mo.ResourcePool, error){ - var p []string - if len(params) == 0 { - p = []string{"*"} - } else { - p = params +func (d *Driver) NewResourcePool(ref *types.ManagedObjectReference) *ResourcePool { + return &ResourcePool{ + pool: object.NewResourcePool(d.client.Client, *ref), + driver: d, } - var poolInfo mo.ResourcePool - err := host.Properties(d.ctx, host.Reference(), p, &poolInfo) +} + +func (d *Driver) FindResourcePool(host string, name string) (*ResourcePool, error) { + p, err := d.finder.ResourcePool(d.ctx, fmt.Sprintf("/%v/host/%v/Resources/%v", d.datacenter.Name(), host, name)) if err != nil { return nil, err } - return &poolInfo, nil + return &ResourcePool{ + pool: p, + driver: d, + }, nil } -func (d *Driver) GetResourcePoolPath(pool *object.ResourcePool) (string, error) { - f, err := d.ResourcePoolInfo(pool, "name", "parent") +func (p *ResourcePool) Info(params ...string) (*mo.ResourcePool, error) { + var params2 []string + if len(params) == 0 { + params2 = []string{"*"} + } else { + params2 = params + } + var info mo.ResourcePool + err := p.pool.Properties(p.driver.ctx, p.pool.Reference(), params2, &info) + if err != nil { + return nil, err + } + return &info, nil +} + +func (p *ResourcePool) Path() (string, error) { + poolInfo, err := p.Info("name", "parent") if err != nil { return "", err } - if f.Parent.Type == "ComputeResource" { + if poolInfo.Parent.Type == "ComputeResource" { return "", nil } else { - parent := d.NewResourcePool(f.Parent) - parentPath, err := d.GetResourcePoolPath(parent) + parent := p.driver.NewResourcePool(poolInfo.Parent) + parentPath, err := parent.Path() if err != nil { return "", err } if parentPath == "" { - return f.Name, nil + return poolInfo.Name, nil } else { - return fmt.Sprintf("%v/%v", parentPath, f.Name), nil + return fmt.Sprintf("%v/%v", parentPath, poolInfo.Name), nil } } } diff --git a/driver/vm.go b/driver/vm.go index 1c6196478..83c7d7d55 100644 --- a/driver/vm.go +++ b/driver/vm.go @@ -3,23 +3,229 @@ package driver import ( "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" + "errors" + "time" ) -func (d *Driver) FindVM(name string) (*object.VirtualMachine, error) { - return d.finder.VirtualMachine(d.ctx, name) +type VirtualMachine struct { + vm *object.VirtualMachine + driver *Driver } -func (d *Driver) VMInfo(vm *object.VirtualMachine, params ...string) (*mo.VirtualMachine, error){ +type CloneConfig struct { + Name string + Folder string + Host string + ResourcePool string + Datastore string + LinkedClone bool +} + +type HardwareConfig struct { + CPUs int32 + CPUReservation int64 + CPULimit int64 + RAM int64 + RAMReservation int64 + RAMReserveAll bool +} + +func (d *Driver) NewVM(ref *types.ManagedObjectReference) *VirtualMachine { + return &VirtualMachine{ + vm: object.NewVirtualMachine(d.client.Client, *ref), + driver: d, + } +} + +func (d *Driver) FindVM(name string) (*VirtualMachine, error) { + vm, err := d.finder.VirtualMachine(d.ctx, name) + if err != nil { + return nil, err + } + return &VirtualMachine{ + vm: vm, + driver: d, + }, nil +} + +func (vm *VirtualMachine) Info(params ...string) (*mo.VirtualMachine, error) { var p []string if len(params) == 0 { p = []string{"*"} } else { p = params } - var vmInfo mo.VirtualMachine - err := vm.Properties(d.ctx, vm.Reference(), p, &vmInfo) + var info mo.VirtualMachine + err := vm.vm.Properties(vm.driver.ctx, vm.vm.Reference(), p, &info) if err != nil { return nil, err } - return &vmInfo, nil + return &info, nil +} + +func (template *VirtualMachine) Clone(config *CloneConfig) (*VirtualMachine, error) { + folder, err := template.driver.FindFolder(config.Folder) + if err != nil { + return nil, err + } + + var relocateSpec types.VirtualMachineRelocateSpec + + pool, err := template.driver.FindResourcePool(config.Host, config.ResourcePool) + if err != nil { + return nil, err + } + poolRef := pool.pool.Reference() + relocateSpec.Pool = &poolRef + + if config.Datastore != "" { + datastore, err := template.driver.FindDatastore(config.Datastore) + if err != nil { + return nil, err + } + datastoreRef := datastore.ds.Reference() + relocateSpec.Datastore = &datastoreRef + } + + var cloneSpec types.VirtualMachineCloneSpec + cloneSpec.Location = relocateSpec + cloneSpec.PowerOn = false + + if config.LinkedClone == true { + cloneSpec.Location.DiskMoveType = "createNewChildDiskBacking" + + tpl, err := template.Info("snapshot") + if err != nil { + return nil, err + } + if tpl.Snapshot == nil { + err = errors.New("`linked_clone=true`, but template has no snapshots") + return nil, err + } + cloneSpec.Snapshot = tpl.Snapshot.CurrentSnapshot + } + + task, err := template.vm.Clone(template.driver.ctx, folder.folder, config.Name, cloneSpec) + if err != nil { + return nil, err + } + + info, err := task.WaitForResult(template.driver.ctx, nil) + if err != nil { + return nil, err + } + + ref := info.Result.(types.ManagedObjectReference) + vm := template.driver.NewVM(&ref) + return vm, nil +} + +func (vm *VirtualMachine) Destroy() error { + task, err := vm.vm.Destroy(vm.driver.ctx) + if err != nil { + return err + } + _, err = task.WaitForResult(vm.driver.ctx, nil) + return err +} + +func (vm *VirtualMachine) Configure(config *HardwareConfig) error { + var confSpec types.VirtualMachineConfigSpec + confSpec.NumCPUs = config.CPUs + confSpec.MemoryMB = config.RAM + + var cpuSpec types.ResourceAllocationInfo + cpuSpec.Reservation = config.CPUReservation + cpuSpec.Limit = config.CPULimit + confSpec.CpuAllocation = &cpuSpec + + var ramSpec types.ResourceAllocationInfo + ramSpec.Reservation = config.RAMReservation + confSpec.MemoryAllocation = &ramSpec + + confSpec.MemoryReservationLockedToMax = &config.RAMReserveAll + + task, err := vm.vm.Reconfigure(vm.driver.ctx, confSpec) + if err != nil { + return err + } + _, err = task.WaitForResult(vm.driver.ctx, nil) + return err +} + +func (vm *VirtualMachine) PowerOn() error { + task, err := vm.vm.PowerOn(vm.driver.ctx) + if err != nil { + return err + } + _, err = task.WaitForResult(vm.driver.ctx, nil) + return err +} + +func (vm *VirtualMachine) WaitForIP() (string, error) { + ip, err := vm.vm.WaitForIP(vm.driver.ctx) + if err != nil { + return "", err + } + return ip, nil +} + +func (vm *VirtualMachine) PowerOff() error { + state, err := vm.vm.PowerState(vm.driver.ctx) + if err != nil { + return err + } + + if state == types.VirtualMachinePowerStatePoweredOff { + return nil + } + + task, err := vm.vm.PowerOff(vm.driver.ctx) + if err != nil { + return err + } + _, err = task.WaitForResult(vm.driver.ctx, nil) + return err +} + +func (vm *VirtualMachine) StartShutdown() error { + err := vm.vm.ShutdownGuest(vm.driver.ctx) + return err +} + +func (vm *VirtualMachine) WaitForShutdown(timeout time.Duration) error { + shutdownTimer := time.After(timeout) + for { + powerState, err := vm.vm.PowerState(vm.driver.ctx) + if err != nil { + return err + } + if powerState == "poweredOff" { + break + } + + select { + case <-shutdownTimer: + err := errors.New("Timeout while waiting for machine to shut down.") + return err + default: + time.Sleep(1 * time.Second) + } + } + return nil +} + +func (vm *VirtualMachine) CreateSnapshot(name string) error { + task, err := vm.vm.CreateSnapshot(vm.driver.ctx, name, "", false, false) + if err != nil { + return err + } + _, err = task.WaitForResult(vm.driver.ctx, nil) + return err +} + +func (vm *VirtualMachine) ConvertToTemplate() error { + err := vm.vm.MarkAsTemplate(vm.driver.ctx) + return err } diff --git a/step_clone.go b/step_clone.go index b9dbc8c45..cd27ace68 100644 --- a/step_clone.go +++ b/step_clone.go @@ -2,7 +2,6 @@ package main import ( "github.com/mitchellh/multistep" - "github.com/vmware/govmomi/object" "github.com/hashicorp/packer/packer" "fmt" "github.com/jetbrains-infra/packer-builder-vsphere/driver" @@ -44,9 +43,14 @@ func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction { ui.Say("Cloning VM...") - vm, err := d.CloneVM(&driver.CloneConfig{ - Template: s.config.Template, - VMName: s.config.VMName, + template, err := d.FindVM(s.config.Template) + if err != nil { + state.Put("error", err) + return multistep.ActionHalt + } + + vm, err := template.Clone(&driver.CloneConfig{ + Name: s.config.VMName, Folder: s.config.Folder, Host: s.config.Host, ResourcePool: s.config.ResourcePool, @@ -69,15 +73,13 @@ func (s *StepCloneVM) Cleanup(state multistep.StateBag) { return } - if vm, ok := state.GetOk("vm"); ok { - ui := state.Get("ui").(packer.Ui) - d := state.Get("driver").(*driver.Driver) + ui := state.Get("ui").(packer.Ui) + vm := state.Get("vm").(*driver.VirtualMachine) - ui.Say("Destroying VM...") + ui.Say("Destroying VM...") - err := d.DestroyVM(vm.(*object.VirtualMachine)) - if err != nil { - ui.Error(err.Error()) - } + err := vm.Destroy() + if err != nil { + ui.Error(err.Error()) } } diff --git a/step_hardware.go b/step_hardware.go index 33045eda1..f62651045 100644 --- a/step_hardware.go +++ b/step_hardware.go @@ -3,7 +3,6 @@ package main import ( "github.com/mitchellh/multistep" "github.com/hashicorp/packer/packer" - "github.com/vmware/govmomi/object" "fmt" "github.com/jetbrains-infra/packer-builder-vsphere/driver" ) @@ -33,13 +32,12 @@ type StepConfigureHardware struct { func (s *StepConfigureHardware) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) - d := state.Get("driver").(*driver.Driver) - vm := state.Get("vm").(*object.VirtualMachine) + vm := state.Get("vm").(*driver.VirtualMachine) if *s.config != (HardwareConfig{}) { ui.Say("Customizing hardware parameters...") - err := d.ConfigureVM(vm, &driver.HardwareConfig{ + err := vm.Configure(&driver.HardwareConfig{ CPUs: s.config.CPUs, CPUReservation: s.config.CPUReservation, CPULimit: s.config.CPULimit, diff --git a/step_run.go b/step_run.go index 353f3df00..5fc98fec5 100644 --- a/step_run.go +++ b/step_run.go @@ -3,7 +3,6 @@ package main import ( "github.com/mitchellh/multistep" "github.com/hashicorp/packer/packer" - "github.com/vmware/govmomi/object" "fmt" "github.com/jetbrains-infra/packer-builder-vsphere/driver" ) @@ -13,18 +12,18 @@ type StepRun struct { func (s *StepRun) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) - d := state.Get("driver").(*driver.Driver) - vm := state.Get("vm").(*object.VirtualMachine) + vm := state.Get("vm").(*driver.VirtualMachine) ui.Say("Power on VM...") - err := d.PowerOn(vm) + + err := vm.PowerOn() if err != nil { state.Put("error", err) return multistep.ActionHalt } ui.Say("Waiting for IP...") - ip, err := d.WaitForIP(vm) + ip, err := vm.WaitForIP() if err != nil { state.Put("error", err) return multistep.ActionHalt @@ -43,11 +42,11 @@ func (s *StepRun) Cleanup(state multistep.StateBag) { } ui := state.Get("ui").(packer.Ui) - d := state.Get("driver").(*driver.Driver) - vm := state.Get("vm").(*object.VirtualMachine) + vm := state.Get("vm").(*driver.VirtualMachine) ui.Say("Power off VM...") - err := d.PowerOff(vm) + + err := vm.PowerOff() if err != nil { ui.Error(err.Error()) } diff --git a/step_shutdown.go b/step_shutdown.go index dc8539a13..78bf2fbe6 100644 --- a/step_shutdown.go +++ b/step_shutdown.go @@ -3,7 +3,6 @@ package main import ( "github.com/mitchellh/multistep" "github.com/hashicorp/packer/packer" - "github.com/vmware/govmomi/object" "fmt" "log" "time" @@ -41,8 +40,7 @@ type StepShutdown struct { func (s *StepShutdown) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) comm := state.Get("communicator").(packer.Communicator) - d := state.Get("driver").(*driver.Driver) - vm := state.Get("vm").(*object.VirtualMachine) + vm := state.Get("vm").(*driver.VirtualMachine) if s.config.Command != "" { ui.Say("Executing shutdown command...") @@ -62,7 +60,7 @@ func (s *StepShutdown) Run(state multistep.StateBag) multistep.StepAction { } else { ui.Say("Shut down VM...") - err := d.StartShutdown(vm) + err := vm.StartShutdown() if err != nil { state.Put("error", fmt.Errorf("Cannot shut down VM: %v", err)) return multistep.ActionHalt @@ -70,7 +68,7 @@ func (s *StepShutdown) Run(state multistep.StateBag) multistep.StepAction { } log.Printf("Waiting max %s for shutdown to complete", s.config.Timeout) - err := d.WaitForShutdown(vm, s.config.Timeout) + err := vm.WaitForShutdown(s.config.Timeout) if err != nil { state.Put("error", err) return multistep.ActionHalt diff --git a/step_snapshot.go b/step_snapshot.go index ad9965445..4b2a86355 100644 --- a/step_snapshot.go +++ b/step_snapshot.go @@ -3,7 +3,6 @@ package main import ( "github.com/mitchellh/multistep" "github.com/hashicorp/packer/packer" - "github.com/vmware/govmomi/object" "github.com/jetbrains-infra/packer-builder-vsphere/driver" ) @@ -13,13 +12,12 @@ type StepCreateSnapshot struct{ func (s *StepCreateSnapshot) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) - d := state.Get("driver").(*driver.Driver) - vm := state.Get("vm").(*object.VirtualMachine) + vm := state.Get("vm").(*driver.VirtualMachine) if s.createSnapshot { ui.Say("Creating snapshot...") - err := d.CreateSnapshot(vm) + err := vm.CreateSnapshot("Created by Packer") if err != nil { state.Put("error", err) return multistep.ActionHalt diff --git a/step_template.go b/step_template.go index 2b3d42a6b..0bed2c062 100644 --- a/step_template.go +++ b/step_template.go @@ -3,7 +3,6 @@ package main import ( "github.com/mitchellh/multistep" "github.com/hashicorp/packer/packer" - "github.com/vmware/govmomi/object" "github.com/jetbrains-infra/packer-builder-vsphere/driver" ) @@ -13,12 +12,11 @@ type StepConvertToTemplate struct{ func (s *StepConvertToTemplate) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) - d := state.Get("driver").(*driver.Driver) - vm := state.Get("vm").(*object.VirtualMachine) + vm := state.Get("vm").(*driver.VirtualMachine) if s.ConvertToTemplate { ui.Say("Convert VM into template...") - err := d.ConvertToTemplate(vm) + err := vm.ConvertToTemplate() if err != nil { state.Put("error", err) return multistep.ActionHalt