Separate configs for connection & cloning

This commit is contained in:
Michael Kuzmin 2017-07-02 03:52:10 +03:00
parent 228ac12366
commit 22d7851e1f
5 changed files with 128 additions and 101 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ packer-builder-vsphere
packer-builder-vsphere.exe
bin/
test*.json
crash.log

View File

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

View File

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

View File

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

69
step_connect.go Normal file
View File

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