Changes requested applied

This commit is contained in:
bugbuilder 2017-07-18 23:10:05 -04:00
parent 2ddc07d3b9
commit fa10616f57
8 changed files with 73 additions and 101 deletions

View File

@ -25,7 +25,7 @@ type Config struct {
Insecure bool `mapstructure:"insecure"` Insecure bool `mapstructure:"insecure"`
Username string `mapstructure:"username"` Username string `mapstructure:"username"`
Password string `mapstructure:"password"` Password string `mapstructure:"password"`
Datacenter string `mapstructure:"datacenter"` Datacenter string `mapstructure:"Datacenter"`
VMName string `mapstructure:"vm_name"` VMName string `mapstructure:"vm_name"`
Folder string `mapstructure:"folder"` Folder string `mapstructure:"folder"`
@ -64,11 +64,20 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
} }
} }
if err := p.configureURL(); err != nil { if p.config.Folder != "" && !strings.HasPrefix(p.config.Folder, "/") {
errs = packer.MultiErrorAppend( errs = packer.MultiErrorAppend(
errs, err) errs, fmt.Errorf("Folder must be bound to the root"))
} }
sdk, err := url.Parse(fmt.Sprintf("https://%v/sdk", p.config.Host))
if err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Error invalid vSphere sdk endpoint: %s", err))
}
sdk.User = url.UserPassword(p.config.Username, p.config.Password)
p.url = sdk
if len(errs.Errors) > 0 { if len(errs.Errors) > 0 {
return errs return errs
} }
@ -77,7 +86,7 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
if _, ok := builtins[artifact.BuilderId()]; !ok { if _, ok := builtins[artifact.BuilderId()]; !ok {
return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId()) return nil, false, fmt.Errorf("Unknown artifact type, can't build template: %s", artifact.BuilderId())
} }
source := "" source := ""
@ -87,34 +96,33 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
break break
} }
} }
//We give a vSphere-ESXI 10s to sync // In some occasions when the VM is mark as template it loses its configuration if it's done immediately
// after the ESXi creates it. If vSphere is given a few seconds this behavior doesn't reappear.
ui.Message("Waiting 10s for VMWare vSphere to start") ui.Message("Waiting 10s for VMWare vSphere to start")
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
ctx := context.Background() c, err := govmomi.NewClient(context.Background(), p.url, p.config.Insecure)
c, err := govmomi.NewClient(ctx, p.url, p.config.Insecure)
if err != nil { if err != nil {
return artifact, true, fmt.Errorf("Error trying to connect: %s", err) return nil, true, fmt.Errorf("Error connecting to vSphere: %s", err)
} }
state := new(multistep.BasicStateBag) state := new(multistep.BasicStateBag)
state.Put("ui", ui) state.Put("ui", ui)
state.Put("context", ctx)
state.Put("client", c) state.Put("client", c)
steps := []multistep.Step{ steps := []multistep.Step{
&StepChooseDatacenter{ &stepChooseDatacenter{
Datacenter: p.config.Datacenter, Datacenter: p.config.Datacenter,
}, },
&StepFetchVm{ &stepFetchVm{
VMName: p.config.VMName, VMName: p.config.VMName,
Source: source, Source: source,
}, },
&StepCreateFolder{ &stepCreateFolder{
Folder: p.config.Folder, Folder: p.config.Folder,
}, },
&StepMarkAsTemplate{}, &stepMarkAsTemplate{},
&StepMoveTemplate{ &stepMoveTemplate{
Folder: p.config.Folder, Folder: p.config.Folder,
}, },
} }
@ -123,18 +131,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
runner.Run(state) runner.Run(state)
if rawErr, ok := state.GetOk("error"); ok { if rawErr, ok := state.GetOk("error"); ok {
return artifact, true, rawErr.(error) return nil, true, rawErr.(error)
} }
return artifact, true, nil return artifact, true, nil
} }
func (p *PostProcessor) configureURL() error {
sdk, err := url.Parse(fmt.Sprintf("https://%v/sdk", p.config.Host))
if err != nil {
return nil
}
sdk.User = url.UserPassword(p.config.Username, p.config.Password)
p.url = sdk
return nil
}

View File

@ -1,19 +0,0 @@
package vsphere_template
import (
"testing"
)
func TestConfigureURL(t *testing.T) {
var p PostProcessor
p.config.Username = "me"
p.config.Password = "notpassword"
p.config.Host = "myhost"
p.config.Datacenter = "mydc"
p.config.VMName = "my vm"
p.config.Insecure = true
if err := p.configureURL(); err != nil {
t.Errorf("Error: %s", err)
}
}

View File

@ -9,17 +9,16 @@ import (
"github.com/vmware/govmomi/find" "github.com/vmware/govmomi/find"
) )
type StepChooseDatacenter struct { type stepChooseDatacenter struct {
Datacenter string Datacenter string
} }
func (s *StepChooseDatacenter) Run(state multistep.StateBag) multistep.StepAction { func (s *stepChooseDatacenter) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
cli := state.Get("client").(*govmomi.Client) cli := state.Get("client").(*govmomi.Client)
ctx := state.Get("context").(context.Context)
finder := find.NewFinder(cli.Client, false) finder := find.NewFinder(cli.Client, false)
datacenter, err := finder.DatacenterOrDefault(ctx, s.Datacenter) datacenter, err := finder.DatacenterOrDefault(context.Background(), s.Datacenter)
if err != nil { if err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
@ -28,9 +27,9 @@ func (s *StepChooseDatacenter) Run(state multistep.StateBag) multistep.StepActio
} }
finder.SetDatacenter(datacenter) finder.SetDatacenter(datacenter)
state.Put("datacenter", datacenter.Name()) state.Put("Datacenter", datacenter.Name())
state.Put("finder", finder) state.Put("finder", finder)
return multistep.ActionContinue return multistep.ActionContinue
} }
func (s *StepChooseDatacenter) Cleanup(multistep.StateBag) {} func (s *stepChooseDatacenter) Cleanup(multistep.StateBag) {}

View File

@ -12,36 +12,33 @@ import (
"github.com/vmware/govmomi/object" "github.com/vmware/govmomi/object"
) )
type StepCreateFolder struct { type stepCreateFolder struct {
Folder string Folder string
} }
func (s *StepCreateFolder) Run(state multistep.StateBag) multistep.StepAction { func (s *stepCreateFolder) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
ctx := state.Get("context").(context.Context) finder := state.Get("finder").(*find.Finder)
f := state.Get("finder").(*find.Finder) dc := state.Get("Datacenter").(string)
d := state.Get("datacenter").(string)
if s.Folder != "" { if s.Folder != "" {
ui.Say("Creating or checking destination folders...") ui.Say("Creating or checking destination folders...")
if !strings.HasPrefix(s.Folder, "/") {
s.Folder = filepath.Join("/", s.Folder)
}
path := s.Folder path := s.Folder
base := filepath.Join("/", d, "vm") base := filepath.Join("/", dc, "vm")
var folders []string var folders []string
var root *object.Folder var root *object.Folder
var err error var err error
// We iterate over the path starting with full path
// If we don't find it, we save the folder name and continue with the previous path
// The iteration ends when we find an existing path
for { for {
root, err = f.Folder(ctx, filepath.ToSlash(filepath.Join(base, path))) root, err = finder.Folder(context.Background(), filepath.ToSlash(filepath.Join(base, path)))
if err != nil { if err != nil {
_, folder := filepath.Split(path) _, folder := filepath.Split(path)
folders = append(folders, folder) folders = append(folders, folder)
if i := strings.LastIndex(path, "/"); i == 0 { if i := strings.LastIndex(path, "/"); i == 0 {
root, err = f.Folder(ctx, filepath.ToSlash(base)) root, err = finder.Folder(context.Background(), filepath.ToSlash(base))
if err != nil { if err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
@ -58,7 +55,7 @@ func (s *StepCreateFolder) Run(state multistep.StateBag) multistep.StepAction {
for i := len(folders) - 1; i >= 0; i-- { for i := len(folders) - 1; i >= 0; i-- {
ui.Message(fmt.Sprintf("Creating folder: %v", folders[i])) ui.Message(fmt.Sprintf("Creating folder: %v", folders[i]))
root, err = root.CreateFolder(ctx, folders[i]) root, err = root.CreateFolder(context.Background(), folders[i])
if err != nil { if err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
@ -69,4 +66,4 @@ func (s *StepCreateFolder) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionContinue return multistep.ActionContinue
} }
func (s *StepCreateFolder) Cleanup(multistep.StateBag) {} func (s *stepCreateFolder) Cleanup(multistep.StateBag) {}

View File

@ -9,19 +9,18 @@ import (
"github.com/vmware/govmomi/find" "github.com/vmware/govmomi/find"
) )
type StepFetchVm struct { type stepFetchVm struct {
VMName string VMName string
Source string Source string
} }
func (s *StepFetchVm) Run(state multistep.StateBag) multistep.StepAction { func (s *stepFetchVm) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
ctx := state.Get("context").(context.Context)
f := state.Get("finder").(*find.Finder) f := state.Get("finder").(*find.Finder)
ui.Say("Fetching VM...") ui.Say("Fetching VM...")
if err := avoidOrphaned(ctx, f, s.VMName); err != nil { if err := avoidOrphaned(f, s.VMName); err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt
@ -32,41 +31,41 @@ func (s *StepFetchVm) Run(state multistep.StateBag) multistep.StepAction {
storage := path[:i] storage := path[:i]
vmx := path[i:] vmx := path[i:]
ds, err := f.DatastoreOrDefault(ctx, storage) ds, err := f.DatastoreOrDefault(context.Background(), storage)
if err != nil { if err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt
} }
folder, err := f.DefaultFolder(ctx) folder, err := f.DefaultFolder(context.Background())
if err != nil { if err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt
} }
pool, err := f.DefaultResourcePool(ctx) pool, err := f.DefaultResourcePool(context.Background())
if err != nil { if err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt
} }
task, err := folder.RegisterVM(ctx, ds.Path(vmx), s.VMName, false, pool, nil) task, err := folder.RegisterVM(context.Background(), ds.Path(vmx), s.VMName, false, pool, nil)
if err != nil { if err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt
} }
if err = task.Wait(ctx); err != nil { if err = task.Wait(context.Background()); err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt
} }
vm, err := f.VirtualMachine(ctx, s.VMName) vm, err := f.VirtualMachine(context.Background(), s.VMName)
if err != nil { if err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
@ -77,13 +76,13 @@ func (s *StepFetchVm) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionContinue return multistep.ActionContinue
} }
// When ESXI remove the VM, vSphere keep the VM as orphaned // When ESXi remove the VM, vSphere keep the VM as orphaned
func avoidOrphaned(ctx context.Context, f *find.Finder, vm_name string) error { func avoidOrphaned(f *find.Finder, vm_name string) error {
vm, err := f.VirtualMachine(ctx, vm_name) vm, err := f.VirtualMachine(context.Background(), vm_name)
if err != nil { if err != nil {
return err return err
} }
return vm.Unregister(ctx) return vm.Unregister(context.Background())
} }
func (s *StepFetchVm) Cleanup(multistep.StateBag) {} func (s *stepFetchVm) Cleanup(multistep.StateBag) {}

View File

@ -8,16 +8,15 @@ import (
"github.com/vmware/govmomi/object" "github.com/vmware/govmomi/object"
) )
type StepMarkAsTemplate struct{} type stepMarkAsTemplate struct{}
func (s *StepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction { func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
ctx := state.Get("context").(context.Context)
vm := state.Get("vm").(*object.VirtualMachine) vm := state.Get("vm").(*object.VirtualMachine)
ui.Say("Marking as a template...") ui.Say("Marking as a template...")
if err := vm.MarkAsTemplate(ctx); err != nil { if err := vm.MarkAsTemplate(context.Background()); err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt
@ -25,4 +24,4 @@ func (s *StepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction
return multistep.ActionContinue return multistep.ActionContinue
} }
func (s *StepMarkAsTemplate) Cleanup(multistep.StateBag) {} func (s *stepMarkAsTemplate) Cleanup(multistep.StateBag) {}

View File

@ -11,21 +11,20 @@ import (
"github.com/vmware/govmomi/vim25/types" "github.com/vmware/govmomi/vim25/types"
) )
type StepMoveTemplate struct { type stepMoveTemplate struct {
Folder string Folder string
} }
func (s *StepMoveTemplate) Run(state multistep.StateBag) multistep.StepAction { func (s *stepMoveTemplate) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
ctx := state.Get("context").(context.Context)
finder := state.Get("finder").(*find.Finder) finder := state.Get("finder").(*find.Finder)
d := state.Get("datacenter").(string) dc := state.Get("Datacenter").(string)
vm := state.Get("vm").(*object.VirtualMachine) vm := state.Get("vm").(*object.VirtualMachine)
if s.Folder != "" { if s.Folder != "" {
ui.Say("Moving template...") ui.Say("Moving template...")
folder, err := finder.Folder(ctx, filepath.ToSlash(filepath.Join("/", d, "vm", s.Folder))) folder, err := finder.Folder(context.Background(), filepath.ToSlash(filepath.Join("/", dc, "vm", s.Folder)))
if err != nil { if err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
@ -33,13 +32,13 @@ func (s *StepMoveTemplate) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionHalt return multistep.ActionHalt
} }
task, err := folder.MoveInto(ctx, []types.ManagedObjectReference{vm.Reference()}) task, err := folder.MoveInto(context.Background(), []types.ManagedObjectReference{vm.Reference()})
if err != nil { if err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt
} }
if err = task.Wait(ctx); err != nil { if err = task.Wait(context.Background()); err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt
@ -48,4 +47,4 @@ func (s *StepMoveTemplate) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionContinue return multistep.ActionContinue
} }
func (s *StepMoveTemplate) Cleanup(multistep.StateBag) {} func (s *stepMoveTemplate) Cleanup(multistep.StateBag) {}

View File

@ -1,6 +1,6 @@
--- ---
description: | description: |
The Packer vSphere Template post-processor takes an artifact from the VMware-iso builder -**only if remote ESXI was chosen**- The Packer vSphere Template post-processor takes an artifact from the VMware-iso builder built on ESXi (i.e. remote)
and allows to mark a VM as a template and leaving it in a path of choice. and allows to mark a VM as a template and leaving it in a path of choice.
layout: docs layout: docs
page_title: 'vSphere Template - Post-Processors' page_title: 'vSphere Template - Post-Processors'
@ -11,7 +11,7 @@ sidebar_current: 'docs-post-processors-vSphere-template'
Type: `vsphere-template` Type: `vsphere-template`
The Packer vSphere template post-processor takes an artifact from the VMware-iso builder -**only if remote ESXI was chosen**- The Packer vSphere template post-processor takes an artifact from the VMware-iso builder built on ESXi (i.e. remote)
allows to mark a VM as a template and leaving it in a path of choice. allows to mark a VM as a template and leaving it in a path of choice.
## Example ## Example
@ -22,10 +22,11 @@ An example is shown below, showing only the post-processor configuration:
{ {
"type": "vsphere-template", "type": "vsphere-template",
"host": "vcenter.local", "host": "vcenter.local",
"insecure": true,
"username": "root", "username": "root",
"password": "secret", "password": "secret",
"datacenter": "murlock",
"vm_name": "distro-7.3", "vm_name": "distro-7.3",
"datacenter": "mydatacenter",
"folder": "/packer-templates/os/distro-7" "folder": "/packer-templates/os/distro-7"
} }
``` ```
@ -53,6 +54,6 @@ Required:
Optional: Optional:
- `folder` (string) - Target path where the template will be created. - `datacenter` (string) - If you have more than one, you will need to specify which one the ESXi used.
- `datacenter` (string) - If you have more than one, you will need to specify which one the ESXI used. - `folder` (string) - Target path where the template will be created.