Files

154 lines
4.9 KiB
Go
Raw Permalink Normal View History

2019-10-14 16:43:59 +02:00
//go:generate mapstructure-to-hcl2 -type Config
package vsphere_template
2017-07-09 14:12:37 -04:00
import (
"context"
"errors"
2017-07-09 14:12:37 -04:00
"fmt"
"net/url"
2017-07-09 20:33:03 -04:00
"strings"
2017-07-09 22:13:31 -04:00
"time"
2017-07-09 14:12:37 -04:00
"github.com/hashicorp/hcl/v2/hcldec"
2020-12-17 13:29:25 -08:00
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
vsphere "github.com/hashicorp/packer/builder/vsphere/common"
"github.com/hashicorp/packer/post-processor/artifice"
vspherepost "github.com/hashicorp/packer/post-processor/vsphere"
"github.com/vmware/govmomi"
2017-07-09 14:12:37 -04:00
)
2017-07-09 20:33:03 -04:00
var builtins = map[string]string{
vspherepost.BuilderId: "vmware",
vmwcommon.BuilderIdESX: "vmware",
vsphere.BuilderId: "vsphere",
artifice.BuilderId: "artifice",
2017-07-09 20:33:03 -04:00
}
2017-07-09 14:12:37 -04:00
type Config struct {
common.PackerConfig `mapstructure:",squash"`
Host string `mapstructure:"host"`
Insecure bool `mapstructure:"insecure"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
Datacenter string `mapstructure:"datacenter"`
Folder string `mapstructure:"folder"`
SnapshotEnable bool `mapstructure:"snapshot_enable"`
SnapshotName string `mapstructure:"snapshot_name"`
SnapshotDescription string `mapstructure:"snapshot_description"`
ReregisterVM config.Trilean `mapstructure:"reregister_vm"`
2017-07-09 14:12:37 -04:00
ctx interpolate.Context
}
type PostProcessor struct {
config Config
url *url.URL
}
2019-12-17 11:25:56 +01:00
func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }
2017-07-09 14:12:37 -04:00
func (p *PostProcessor) Configure(raws ...interface{}) error {
err := config.Decode(&p.config, &config.DecodeOpts{
PluginType: vsphere.BuilderId,
2017-07-09 14:12:37 -04:00
Interpolate: true,
InterpolateContext: &p.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{},
},
}, raws...)
if err != nil {
return err
}
errs := new(packersdk.MultiError)
2017-07-09 14:12:37 -04:00
vc := map[string]*string{
"host": &p.config.Host,
"username": &p.config.Username,
"password": &p.config.Password,
}
for key, ptr := range vc {
if *ptr == "" {
errs = packersdk.MultiErrorAppend(
2017-07-09 14:12:37 -04:00
errs, fmt.Errorf("%s must be set", key))
}
}
2017-07-18 23:10:05 -04:00
if p.config.Folder != "" && !strings.HasPrefix(p.config.Folder, "/") {
errs = packersdk.MultiErrorAppend(
2017-07-18 23:10:05 -04:00
errs, fmt.Errorf("Folder must be bound to the root"))
2017-07-09 14:56:39 -04:00
}
2017-07-18 23:10:05 -04:00
sdk, err := url.Parse(fmt.Sprintf("https://%v/sdk", p.config.Host))
if err != nil {
errs = packersdk.MultiErrorAppend(
2017-07-18 23:10:05 -04:00
errs, fmt.Errorf("Error invalid vSphere sdk endpoint: %s", err))
2018-07-23 16:12:21 -07:00
return errs
2017-07-18 23:10:05 -04:00
}
sdk.User = url.UserPassword(p.config.Username, p.config.Password)
p.url = sdk
2017-07-09 14:12:37 -04:00
if len(errs.Errors) > 0 {
return errs
}
return nil
}
2020-11-19 12:17:11 -08:00
func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifact packersdk.Artifact) (packersdk.Artifact, bool, bool, error) {
2017-07-09 20:33:03 -04:00
if _, ok := builtins[artifact.BuilderId()]; !ok {
2019-04-02 16:51:58 -07:00
return nil, false, false, fmt.Errorf("The Packer vSphere Template post-processor "+
"can only take an artifact from the VMware-iso builder, built on "+
"ESXi (i.e. remote) or an artifact from the vSphere post-processor. "+
"Artifact type %s does not fit this requirement", artifact.BuilderId())
2017-07-09 20:33:03 -04:00
}
2017-07-09 14:56:39 -04:00
f := artifact.State(vmwcommon.ArtifactConfFormat)
k := artifact.State(vmwcommon.ArtifactConfKeepRegistered)
s := artifact.State(vmwcommon.ArtifactConfSkipExport)
if f != "" && k != "true" && s == "false" {
2019-04-02 16:51:58 -07:00
return nil, false, false, errors.New("To use this post-processor with exporting behavior you need set keep_registered as true")
}
// 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.
2017-08-24 22:46:35 -03:00
ui.Message("Waiting 10s for VMware vSphere to start")
2017-07-09 14:56:39 -04:00
time.Sleep(10 * time.Second)
2017-07-18 23:10:05 -04:00
c, err := govmomi.NewClient(context.Background(), p.url, p.config.Insecure)
if err != nil {
2019-04-02 16:51:58 -07:00
return nil, false, false, fmt.Errorf("Error connecting to vSphere: %s", err)
}
2017-07-31 10:30:13 -04:00
defer c.Logout(context.Background())
state := new(multistep.BasicStateBag)
state.Put("ui", ui)
state.Put("client", c)
2017-07-09 14:56:39 -04:00
steps := []multistep.Step{
2017-07-18 23:10:05 -04:00
&stepChooseDatacenter{
2017-07-09 14:56:39 -04:00
Datacenter: p.config.Datacenter,
},
&stepCreateFolder{
Folder: p.config.Folder,
},
NewStepCreateSnapshot(artifact, p),
NewStepMarkAsTemplate(artifact, p),
2017-07-09 14:56:39 -04:00
}
runner := commonsteps.NewRunnerWithPauseFn(steps, p.config.PackerConfig, ui, state)
2019-03-22 14:56:02 +01:00
runner.Run(ctx, state)
2017-07-09 14:56:39 -04:00
if rawErr, ok := state.GetOk("error"); ok {
2019-04-02 16:51:58 -07:00
return nil, false, false, rawErr.(error)
2017-07-09 14:56:39 -04:00
}
2019-04-02 16:51:58 -07:00
return artifact, true, true, nil
2017-07-09 14:12:37 -04:00
}