Separate configs for connection & cloning
This commit is contained in:
parent
228ac12366
commit
22d7851e1f
|
@ -5,3 +5,4 @@ packer-builder-vsphere
|
|||
packer-builder-vsphere.exe
|
||||
bin/
|
||||
test*.json
|
||||
crash.log
|
||||
|
|
38
builder.go
38
builder.go
|
@ -10,12 +10,8 @@ import (
|
|||
"github.com/hashicorp/packer/helper/communicator"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
"github.com/hashicorp/packer/communicator/ssh"
|
||||
"github.com/vmware/govmomi"
|
||||
"context"
|
||||
"net/url"
|
||||
"github.com/vmware/govmomi/find"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Builder struct {
|
||||
|
@ -37,38 +33,14 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
state := new(multistep.BasicStateBag)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
ctx := context.TODO()
|
||||
state.Put("ctx", ctx)
|
||||
|
||||
vcenter_url, err := url.Parse(fmt.Sprintf("https://%v/sdk", b.config.VCenterServer))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vcenter_url.User = url.UserPassword(b.config.Username, b.config.Password)
|
||||
client, err := govmomi.NewClient(ctx, vcenter_url, b.config.InsecureConnection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state.Put("client", client)
|
||||
|
||||
finder := find.NewFinder(client.Client, false)
|
||||
datacenter, err := finder.DatacenterOrDefault(ctx, b.config.Datacenter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finder.SetDatacenter(datacenter)
|
||||
state.Put("finder", finder)
|
||||
state.Put("datacenter", datacenter)
|
||||
|
||||
vmSrc, err := finder.VirtualMachine(ctx, b.config.Template)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state.Put("vmSrc", vmSrc)
|
||||
state.Put("ctx", context.TODO())
|
||||
|
||||
steps := []multistep.Step{
|
||||
&StepConnect{
|
||||
config: &b.config.ConnectConfig,
|
||||
},
|
||||
&StepCloneVM{
|
||||
config: b.config,
|
||||
config: &b.config.CloneConfig,
|
||||
},
|
||||
&StepConfigureHardware{
|
||||
config: &b.config.HardwareConfig,
|
||||
|
|
65
config.go
65
config.go
|
@ -1,8 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
|
@ -11,34 +9,14 @@ import (
|
|||
)
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
// Connection
|
||||
VCenterServer string `mapstructure:"vcenter_server"`
|
||||
Datacenter string `mapstructure:"datacenter"`
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
InsecureConnection bool `mapstructure:"insecure_connection"`
|
||||
|
||||
// Location
|
||||
Template string `mapstructure:"template"`
|
||||
FolderName string `mapstructure:"folder"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
Host string `mapstructure:"host"`
|
||||
ResourcePool string `mapstructure:"resource_pool"`
|
||||
Datastore string `mapstructure:"datastore"`
|
||||
LinkedClone bool `mapstructure:"linked_clone"`
|
||||
|
||||
// Customization
|
||||
HardwareConfig `mapstructure:",squash"`
|
||||
|
||||
// Provisioning
|
||||
communicator.Config `mapstructure:",squash"`
|
||||
|
||||
// Post-processing
|
||||
ShutdownConfig `mapstructure:",squash"`
|
||||
CreateSnapshot bool `mapstructure:"create_snapshot"`
|
||||
ConvertToTemplate bool `mapstructure:"convert_to_template"`
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
ConnectConfig `mapstructure:",squash"`
|
||||
CloneConfig `mapstructure:",squash"`
|
||||
HardwareConfig `mapstructure:",squash"`
|
||||
communicator.Config `mapstructure:",squash"`
|
||||
ShutdownConfig `mapstructure:",squash"`
|
||||
CreateSnapshot bool `mapstructure:"create_snapshot"`
|
||||
ConvertToTemplate bool `mapstructure:"convert_to_template"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
@ -56,34 +34,15 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
}
|
||||
|
||||
errs := new(packer.MultiError)
|
||||
var warnings []string
|
||||
errs = packer.MultiErrorAppend(errs, c.Config.Prepare(&c.ctx)...)
|
||||
|
||||
if c.VCenterServer == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("vCenter hostname is required"))
|
||||
}
|
||||
if c.Username == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Username is required"))
|
||||
}
|
||||
if c.Password == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Password is required"))
|
||||
}
|
||||
if c.Template == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Template name is required"))
|
||||
}
|
||||
if c.VMName == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Target VM name is required"))
|
||||
}
|
||||
if c.Host == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("vSphere host is required"))
|
||||
}
|
||||
|
||||
errs = packer.MultiErrorAppend(errs, c.ConnectConfig.Prepare()...)
|
||||
errs = packer.MultiErrorAppend(errs, c.CloneConfig.Prepare()...)
|
||||
errs = packer.MultiErrorAppend(errs, c.HardwareConfig.Prepare()...)
|
||||
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare()...)
|
||||
|
||||
if len(errs.Errors) > 0 {
|
||||
return nil, warnings, errs
|
||||
return nil, nil, errs
|
||||
}
|
||||
|
||||
return c, warnings, nil
|
||||
return c, nil, nil
|
||||
}
|
||||
|
|
|
@ -12,6 +12,32 @@ import (
|
|||
"errors"
|
||||
)
|
||||
|
||||
type CloneConfig struct {
|
||||
Template string `mapstructure:"template"`
|
||||
FolderName string `mapstructure:"folder"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
Host string `mapstructure:"host"`
|
||||
ResourcePool string `mapstructure:"resource_pool"`
|
||||
Datastore string `mapstructure:"datastore"`
|
||||
LinkedClone bool `mapstructure:"linked_clone"`
|
||||
}
|
||||
|
||||
func (c *CloneConfig) Prepare() []error {
|
||||
var errs []error
|
||||
|
||||
if c.Template == "" {
|
||||
errs = append(errs, fmt.Errorf("Template name is required"))
|
||||
}
|
||||
if c.VMName == "" {
|
||||
errs = append(errs, fmt.Errorf("Target VM name is required"))
|
||||
}
|
||||
if c.Host == "" {
|
||||
errs = append(errs, fmt.Errorf("vSphere host is required"))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
type CloneParameters struct {
|
||||
ctx context.Context
|
||||
vmSrc *object.VirtualMachine
|
||||
|
@ -23,33 +49,36 @@ type CloneParameters struct {
|
|||
}
|
||||
|
||||
type StepCloneVM struct {
|
||||
config *Config
|
||||
success bool
|
||||
config *CloneConfig
|
||||
}
|
||||
|
||||
func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ctx := state.Get("ctx").(context.Context)
|
||||
finder := state.Get("finder").(*find.Finder)
|
||||
datacenter := state.Get("datacenter").(*object.Datacenter)
|
||||
vmSrc := state.Get("vmSrc").(*object.VirtualMachine)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
vmSrc, err := finder.VirtualMachine(ctx, s.config.Template)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put("vmSrc", vmSrc)
|
||||
|
||||
ui.Say("start cloning...")
|
||||
|
||||
// Get folder
|
||||
folder, err := finder.FolderOrDefault(ctx, fmt.Sprintf("/%v/vm/%v", datacenter.Name(), s.config.FolderName))
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Get resource pool
|
||||
pool, err := finder.ResourcePoolOrDefault(ctx, fmt.Sprintf("/%v/host/%v/Resources/%v", datacenter.Name(), s.config.Host, s.config.ResourcePool))
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Get datastore
|
||||
var datastore *object.Datastore = nil
|
||||
if s.config.Datastore != "" {
|
||||
datastore, err = finder.Datastore(ctx, s.config.Datastore)
|
||||
|
@ -74,26 +103,23 @@ func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction {
|
|||
}
|
||||
|
||||
state.Put("vm", vm)
|
||||
s.success = true
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepCloneVM) Cleanup(state multistep.StateBag) {
|
||||
if !s.success {
|
||||
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||
_, halted := state.GetOk(multistep.StateHalted)
|
||||
if !cancelled && !halted {
|
||||
return
|
||||
}
|
||||
|
||||
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||
_, halted := state.GetOk(multistep.StateHalted)
|
||||
|
||||
if cancelled || halted {
|
||||
vm := state.Get("vm").(*object.VirtualMachine)
|
||||
if vm, ok := state.GetOk("vm"); ok {
|
||||
ctx := state.Get("ctx").(context.Context)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("destroying VM...")
|
||||
ui.Say("Destroying VM...")
|
||||
|
||||
task, err := vm.Destroy(ctx)
|
||||
task, err := vm.(*object.VirtualMachine).Destroy(ctx)
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
return
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"github.com/vmware/govmomi"
|
||||
"github.com/vmware/govmomi/find"
|
||||
)
|
||||
|
||||
type ConnectConfig struct {
|
||||
VCenterServer string `mapstructure:"vcenter_server"`
|
||||
Datacenter string `mapstructure:"datacenter"`
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
InsecureConnection bool `mapstructure:"insecure_connection"`
|
||||
}
|
||||
|
||||
func (c *ConnectConfig) Prepare() []error {
|
||||
var errs []error
|
||||
|
||||
if c.VCenterServer == "" {
|
||||
errs = append(errs, fmt.Errorf("vCenter hostname is required"))
|
||||
}
|
||||
if c.Username == "" {
|
||||
errs = append(errs, fmt.Errorf("Username is required"))
|
||||
}
|
||||
if c.Password == "" {
|
||||
errs = append(errs, fmt.Errorf("Password is required"))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
type StepConnect struct {
|
||||
config *ConnectConfig
|
||||
}
|
||||
|
||||
func (s *StepConnect) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ctx := state.Get("ctx").(context.Context)
|
||||
|
||||
vcenter_url, err := url.Parse(fmt.Sprintf("https://%v/sdk", s.config.VCenterServer))
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
vcenter_url.User = url.UserPassword(s.config.Username, s.config.Password)
|
||||
client, err := govmomi.NewClient(ctx, vcenter_url, s.config.InsecureConnection)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put("client", client)
|
||||
|
||||
finder := find.NewFinder(client.Client, false)
|
||||
datacenter, err := finder.DatacenterOrDefault(ctx, s.config.Datacenter)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
finder.SetDatacenter(datacenter)
|
||||
state.Put("finder", finder)
|
||||
state.Put("datacenter", datacenter)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepConnect) Cleanup(multistep.StateBag) {}
|
Loading…
Reference in New Issue