From 644ac5b367b10d0dd1b82dd84bd3176a804ed739 Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Wed, 20 Sep 2017 22:50:37 -0300 Subject: [PATCH 1/8] enable vsphere-template to work with local builders --- .../vsphere-template/post-processor.go | 17 ++++++--- .../vsphere-template/step_mark_as_template.go | 34 ++++++++++++++--- post-processor/vsphere/artifact.go | 38 +++++++++++++++++++ post-processor/vsphere/post-processor.go | 9 ++++- 4 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 post-processor/vsphere/artifact.go diff --git a/post-processor/vsphere-template/post-processor.go b/post-processor/vsphere-template/post-processor.go index 86c9f54b4..e68e2d21c 100644 --- a/post-processor/vsphere-template/post-processor.go +++ b/post-processor/vsphere-template/post-processor.go @@ -7,16 +7,19 @@ import ( "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" + "github.com/hashicorp/packer/post-processor/vsphere" "github.com/hashicorp/packer/template/interpolate" "github.com/mitchellh/multistep" "github.com/vmware/govmomi" ) var builtins = map[string]string{ - "mitchellh.vmware-esx": "vmware", + vsphere.BuilderId: "vmware", + iso.BuilderIdESX: "vmware", } type Config struct { @@ -90,11 +93,16 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac source := "" for _, path := range artifact.Files() { - if strings.HasSuffix(path, ".vmx") { + if strings.HasSuffix(path, ".vmx") || strings.HasSuffix(path, ".ovf") || strings.HasSuffix(path, ".ova") { source = path break } } + + if source == "" { + return nil, false, fmt.Errorf("VMX, OVF or OVA file not found") + } + // 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") @@ -117,10 +125,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac &stepCreateFolder{ Folder: p.config.Folder, }, - &stepMarkAsTemplate{ - VMName: artifact.Id(), - Source: source, - }, + NewStepMarkAsTemplate(artifact.Id(), source), } runner := common.NewRunnerWithPauseFn(steps, p.config.PackerConfig, ui, state) runner.Run(state) diff --git a/post-processor/vsphere-template/step_mark_as_template.go b/post-processor/vsphere-template/step_mark_as_template.go index 0e5465054..dda3984a6 100644 --- a/post-processor/vsphere-template/step_mark_as_template.go +++ b/post-processor/vsphere-template/step_mark_as_template.go @@ -13,8 +13,30 @@ import ( ) type stepMarkAsTemplate struct { - VMName string - Source string + VMName string + Source string + RemoteFolder string +} + +func NewStepMarkAsTemplate(vmname, source string) *stepMarkAsTemplate { + remoteFolder := "Discovered virtual machine" + + if strings.Contains(vmname, "::") { + local := strings.Split(vmname, "::") + + datastore := local[0] + remoteFolder = local[1] + vmname = local[2] + + source = path.Join("/vmfs/volumes/", datastore, vmname, path.Base(source)) + + } + + return &stepMarkAsTemplate{ + VMName: vmname, + Source: source, + RemoteFolder: remoteFolder, + } } func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction { @@ -25,7 +47,7 @@ func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction ui.Message("Marking as a template...") - vm, err := findRuntimeVM(cli, dcPath, s.VMName) + vm, err := findRuntimeVM(cli, dcPath, s.VMName, s.RemoteFolder) if err != nil { state.Put("error", err) ui.Error(err.Error()) @@ -75,10 +97,10 @@ func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction return multistep.ActionContinue } -// We will use the virtual machine created by vmware-iso builder -func findRuntimeVM(cli *govmomi.Client, dcPath, name string) (*object.VirtualMachine, error) { +// We will use the virtual machine created/uploaded by vmware builder (remote or local) +func findRuntimeVM(cli *govmomi.Client, dcPath, name, remoteFolder string) (*object.VirtualMachine, error) { si := object.NewSearchIndex(cli.Client) - fullPath := path.Join(dcPath, "vm", "Discovered virtual machine", name) + fullPath := path.Join(dcPath, "vm", remoteFolder, name) ref, err := si.FindByInventoryPath(context.Background(), fullPath) if err != nil { diff --git a/post-processor/vsphere/artifact.go b/post-processor/vsphere/artifact.go new file mode 100644 index 000000000..d8ef75491 --- /dev/null +++ b/post-processor/vsphere/artifact.go @@ -0,0 +1,38 @@ +package vsphere + +import ( + "fmt" +) + +const BuilderId = "packer.post-processor.vsphere" + +type Artifact struct { + files []string + datastore string + vmfolder string + vmname string +} + +func (*Artifact) BuilderId() string { + return BuilderId +} + +func (a *Artifact) Files() []string { + return a.files +} + +func (a *Artifact) Id() string { + return fmt.Sprintf("%s::%s::%s", a.datastore, a.vmfolder, a.vmname) +} + +func (a *Artifact) String() string { + return fmt.Sprintf("VM: %s Folder: %s Datastore: %s", a.vmname, a.vmfolder, a.datastore) +} + +func (*Artifact) State(name string) interface{} { + return nil +} + +func (a *Artifact) Destroy() error { + return nil +} diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index e97147334..ead3b50fd 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -142,7 +142,14 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac return nil, false, fmt.Errorf("Failed: %s\n", err) } - return artifact, false, nil + artifact = &Artifact{ + datastore: p.config.Datastore, + files: artifact.Files(), + vmfolder: p.config.VMFolder, + vmname: p.config.VMName, + } + + return artifact, true, nil } func (p *PostProcessor) BuildArgs(source, ovftool_uri string) ([]string, error) { From 24a8fddf035ef2360bd5dd4bb846fcb5507d8233 Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Fri, 22 Sep 2017 13:54:11 -0300 Subject: [PATCH 2/8] showing artifact info in packer UI --- post-processor/vsphere-template/post-processor.go | 1 + post-processor/vsphere/post-processor.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/post-processor/vsphere-template/post-processor.go b/post-processor/vsphere-template/post-processor.go index e68e2d21c..5cec3bda6 100644 --- a/post-processor/vsphere-template/post-processor.go +++ b/post-processor/vsphere-template/post-processor.go @@ -87,6 +87,7 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { } func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { + ui.Say(fmt.Sprintf("Artifact %s with id %s and builderId %s", artifact, artifact.Id(), artifact.BuilderId())) if _, ok := builtins[artifact.BuilderId()]; !ok { return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId()) } diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index ead3b50fd..0a6e4a6a3 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -149,6 +149,8 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac vmname: p.config.VMName, } + ui.Say(fmt.Sprintf("New artifact created %s with id %s and builderId %s", artifact, artifact.Id(), artifact.BuilderId())) + return artifact, true, nil } From 99dd19ccfdec298992f57355804181bc1b7353c2 Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Fri, 22 Sep 2017 23:37:27 -0300 Subject: [PATCH 3/8] Adding correct reference to VM remote path --- post-processor/vsphere-template/post-processor.go | 1 - post-processor/vsphere-template/step_mark_as_template.go | 2 +- post-processor/vsphere/post-processor.go | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/post-processor/vsphere-template/post-processor.go b/post-processor/vsphere-template/post-processor.go index 5cec3bda6..e68e2d21c 100644 --- a/post-processor/vsphere-template/post-processor.go +++ b/post-processor/vsphere-template/post-processor.go @@ -87,7 +87,6 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { } func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { - ui.Say(fmt.Sprintf("Artifact %s with id %s and builderId %s", artifact, artifact.Id(), artifact.BuilderId())) if _, ok := builtins[artifact.BuilderId()]; !ok { return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId()) } diff --git a/post-processor/vsphere-template/step_mark_as_template.go b/post-processor/vsphere-template/step_mark_as_template.go index dda3984a6..60780dd0d 100644 --- a/post-processor/vsphere-template/step_mark_as_template.go +++ b/post-processor/vsphere-template/step_mark_as_template.go @@ -28,7 +28,7 @@ func NewStepMarkAsTemplate(vmname, source string) *stepMarkAsTemplate { remoteFolder = local[1] vmname = local[2] - source = path.Join("/vmfs/volumes/", datastore, vmname, path.Base(source)) + source = path.Join("/vmfs/volumes/", datastore, vmname, vmname+path.Ext(source)) } diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index 0a6e4a6a3..ead3b50fd 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -149,8 +149,6 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac vmname: p.config.VMName, } - ui.Say(fmt.Sprintf("New artifact created %s with id %s and builderId %s", artifact, artifact.Id(), artifact.BuilderId())) - return artifact, true, nil } From f1773a57f8d624d39bcb84bcdf09f7ebd3179ede Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Sat, 23 Sep 2017 03:01:35 -0300 Subject: [PATCH 4/8] using vmx extension as default --- post-processor/vsphere-template/step_mark_as_template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post-processor/vsphere-template/step_mark_as_template.go b/post-processor/vsphere-template/step_mark_as_template.go index 60780dd0d..82b26a7b5 100644 --- a/post-processor/vsphere-template/step_mark_as_template.go +++ b/post-processor/vsphere-template/step_mark_as_template.go @@ -28,7 +28,7 @@ func NewStepMarkAsTemplate(vmname, source string) *stepMarkAsTemplate { remoteFolder = local[1] vmname = local[2] - source = path.Join("/vmfs/volumes/", datastore, vmname, vmname+path.Ext(source)) + source = path.Join("/vmfs/volumes/", datastore, vmname, vmname+".vmx") } From 75a4ca7351899a0018fee74b8ab621b40546628f Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Sat, 23 Sep 2017 15:43:57 -0300 Subject: [PATCH 5/8] adding artifact testing and using builder id --- .../vsphere-template/post-processor.go | 2 +- .../vsphere-template/step_mark_as_template.go | 17 +++++++-------- post-processor/vsphere/artifact.go | 9 ++++++++ post-processor/vsphere/artifact_test.go | 21 +++++++++++++++++++ post-processor/vsphere/post-processor.go | 7 +------ 5 files changed, 40 insertions(+), 16 deletions(-) create mode 100644 post-processor/vsphere/artifact_test.go diff --git a/post-processor/vsphere-template/post-processor.go b/post-processor/vsphere-template/post-processor.go index e68e2d21c..6014180c7 100644 --- a/post-processor/vsphere-template/post-processor.go +++ b/post-processor/vsphere-template/post-processor.go @@ -125,7 +125,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac &stepCreateFolder{ Folder: p.config.Folder, }, - NewStepMarkAsTemplate(artifact.Id(), source), + NewStepMarkAsTemplate(artifact, source), } runner := common.NewRunnerWithPauseFn(steps, p.config.PackerConfig, ui, state) runner.Run(state) diff --git a/post-processor/vsphere-template/step_mark_as_template.go b/post-processor/vsphere-template/step_mark_as_template.go index 82b26a7b5..78949d2d9 100644 --- a/post-processor/vsphere-template/step_mark_as_template.go +++ b/post-processor/vsphere-template/step_mark_as_template.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/post-processor/vsphere" "github.com/mitchellh/multistep" "github.com/vmware/govmomi" "github.com/vmware/govmomi/object" @@ -18,18 +19,16 @@ type stepMarkAsTemplate struct { RemoteFolder string } -func NewStepMarkAsTemplate(vmname, source string) *stepMarkAsTemplate { +func NewStepMarkAsTemplate(artifact packer.Artifact, source string) *stepMarkAsTemplate { remoteFolder := "Discovered virtual machine" + vmname := artifact.Id() - if strings.Contains(vmname, "::") { - local := strings.Split(vmname, "::") - - datastore := local[0] - remoteFolder = local[1] - vmname = local[2] - + if artifact.BuilderId() == vsphere.BuilderId { + id := strings.Split(artifact.Id(), "::") + datastore := id[0] + remoteFolder = id[1] + vmname = id[2] source = path.Join("/vmfs/volumes/", datastore, vmname, vmname+".vmx") - } return &stepMarkAsTemplate{ diff --git a/post-processor/vsphere/artifact.go b/post-processor/vsphere/artifact.go index d8ef75491..90e475a28 100644 --- a/post-processor/vsphere/artifact.go +++ b/post-processor/vsphere/artifact.go @@ -13,6 +13,15 @@ type Artifact struct { vmname string } +func NewArtifact(datastore, vmfolder, vmname string, files []string) *Artifact { + return &Artifact{ + files: files, + datastore: datastore, + vmfolder: vmfolder, + vmname: vmname, + } +} + func (*Artifact) BuilderId() string { return BuilderId } diff --git a/post-processor/vsphere/artifact_test.go b/post-processor/vsphere/artifact_test.go new file mode 100644 index 000000000..b4f17c759 --- /dev/null +++ b/post-processor/vsphere/artifact_test.go @@ -0,0 +1,21 @@ +package vsphere + +import ( + "github.com/hashicorp/packer/packer" + "testing" +) + +func TestArtifact_ImplementsArtifact(t *testing.T) { + var raw interface{} + raw = &Artifact{} + if _, ok := raw.(packer.Artifact); !ok { + t.Fatalf("Artifact should be a Artifact") + } +} + +func TestArtifact_Id(t *testing.T) { + artifact := NewArtifact("datastore", "vmfolder", "vmname", nil) + if artifact.Id() != "datastore::vmfolder::vmname" { + t.Fatalf("must return datastore, vmfolder and vmname splitted by :: as Id") + } +} diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index ead3b50fd..a022cb23f 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -142,12 +142,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac return nil, false, fmt.Errorf("Failed: %s\n", err) } - artifact = &Artifact{ - datastore: p.config.Datastore, - files: artifact.Files(), - vmfolder: p.config.VMFolder, - vmname: p.config.VMName, - } + artifact = NewArtifact(p.config.Datastore, p.config.VMFolder, p.config.VMName, artifact.Files()) return artifact, true, nil } From b3a0e51fe50b0caedc6ff88bfebfb53c6193745c Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Sun, 24 Sep 2017 01:42:28 -0300 Subject: [PATCH 6/8] adding documentation --- .../post-processors/vsphere-template.html.md | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/website/source/docs/post-processors/vsphere-template.html.md b/website/source/docs/post-processors/vsphere-template.html.md index e72dc19cc..511224cfd 100644 --- a/website/source/docs/post-processors/vsphere-template.html.md +++ b/website/source/docs/post-processors/vsphere-template.html.md @@ -1,7 +1,7 @@ --- description: | - 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. + The Packer vSphere Template post-processor takes an artifact from the VMware-iso builder, built on ESXi (i.e. remote) + or an artifact from the vSphere post-processor and allows to mark a VM as a template and leaving it in a path of choice. layout: docs page_title: 'vSphere Template - Post-Processors' sidebar_current: 'docs-post-processors-vSphere-template' @@ -11,8 +11,9 @@ sidebar_current: 'docs-post-processors-vSphere-template' Type: `vsphere-template` -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. +The Packer vSphere Template post-processor takes an artifact from the VMware-iso builder, built on ESXi (i.e. remote) +or an artifact from the [vSphere](/docs/post-processors/vsphere.html) post-processor and allows to mark a VM as a +template and leaving it in a path of choice. ## Example @@ -51,3 +52,31 @@ Optional: - `folder` (string) - Target path where the template will be created. - `insecure` (boolean) - If it's true skip verification of server certificate. Default is false + +## Using the vSphere Template with local builders + +Once the [vSphere](/docs/post-processors/vsphere.html) takes an artifact from the VMware builder and uploads it +to a vSphere endpoint, you will likely want to mark that VM as template. Packer can do this for you automatically +using a sequence definition (a collection of post-processors that are treated as as single pipeline, see +[Post-Processors](/docs/templates/post-processors.html) for more information): + +``` json +{ + "post-processors": [ + [ + { + "type": "vsphere", + ... + }, + { + "type": "vsphere-template", + ... + } + ] + ] +} +``` + +In the example above, the result of each builder is passed through the defined sequence of post-processors starting +with the `vsphere` post-processor which will upload the artifact to a vSphere endpoint. The resulting artifact is then +passed on to the `vsphere-template` post-processor which handles marking a VM as a template. From 50904064e19053d18fc61c4f3b26bcd3db339f9a Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Sun, 24 Sep 2017 21:56:35 -0300 Subject: [PATCH 7/8] doesn't keep the original artifact --- post-processor/vsphere/post-processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index a022cb23f..d8b40d654 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -144,7 +144,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac artifact = NewArtifact(p.config.Datastore, p.config.VMFolder, p.config.VMName, artifact.Files()) - return artifact, true, nil + return artifact, false, nil } func (p *PostProcessor) BuildArgs(source, ovftool_uri string) ([]string, error) { From b754b715191920cc40e2a2e69e20b2f491cf903e Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Fri, 10 Nov 2017 22:57:39 -0300 Subject: [PATCH 8/8] return vsphere artifact to can build template --- .../vsphere-template/step_mark_as_template.go | 13 +++++++------ post-processor/vsphere/post-processor.go | 2 ++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/post-processor/vsphere-template/step_mark_as_template.go b/post-processor/vsphere-template/step_mark_as_template.go index 8f95ba4d6..2ffe46956 100644 --- a/post-processor/vsphere-template/step_mark_as_template.go +++ b/post-processor/vsphere-template/step_mark_as_template.go @@ -27,6 +27,7 @@ func NewStepMarkAsTemplate(artifact packer.Artifact) *stepMarkAsTemplate { if artifact.BuilderId() == vsphere.BuilderId { id := strings.Split(artifact.Id(), "::") remoteFolder = id[1] + vmname = id[2] } return &stepMarkAsTemplate{ @@ -50,12 +51,6 @@ 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) @@ -76,6 +71,12 @@ 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 + } + task, err := folder.RegisterVM(context.Background(), dsPath.String(), s.VMName, true, nil, host) if err != nil { state.Put("error", err) diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index c028cd087..b9702fe82 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -140,6 +140,8 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac ui.Message(p.filterLog(out.String())) + artifact = NewArtifact(p.config.Datastore, p.config.VMFolder, p.config.VMName, artifact.Files()) + return artifact, false, nil }