2017-07-10 20:52:48 -04:00
|
|
|
package vsphere_template
|
2017-07-09 14:12:37 -04:00
|
|
|
|
|
|
|
import (
|
2017-07-09 15:58:42 -04:00
|
|
|
"context"
|
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/packer/common"
|
|
|
|
"github.com/hashicorp/packer/helper/config"
|
|
|
|
"github.com/hashicorp/packer/packer"
|
|
|
|
"github.com/hashicorp/packer/template/interpolate"
|
2017-07-09 14:56:39 -04:00
|
|
|
"github.com/mitchellh/multistep"
|
2017-07-09 15:58:42 -04:00
|
|
|
"github.com/vmware/govmomi"
|
2017-07-09 14:12:37 -04:00
|
|
|
)
|
2017-07-09 15:58:42 -04:00
|
|
|
|
2017-07-09 20:33:03 -04:00
|
|
|
var builtins = map[string]string{
|
|
|
|
"mitchellh.vmware-esx": "vmware",
|
|
|
|
}
|
|
|
|
|
2017-07-09 14:12:37 -04:00
|
|
|
type Config struct {
|
|
|
|
common.PackerConfig `mapstructure:",squash"`
|
|
|
|
Host string `mapstructure:"host"`
|
2017-07-09 15:58:42 -04:00
|
|
|
Insecure bool `mapstructure:"insecure"`
|
2017-07-09 14:12:37 -04:00
|
|
|
Username string `mapstructure:"username"`
|
|
|
|
Password string `mapstructure:"password"`
|
2017-07-19 00:44:18 -04:00
|
|
|
Datacenter string `mapstructure:"datacenter"`
|
2017-07-09 14:12:37 -04:00
|
|
|
VMName string `mapstructure:"vm_name"`
|
|
|
|
Folder string `mapstructure:"folder"`
|
|
|
|
|
|
|
|
ctx interpolate.Context
|
|
|
|
}
|
|
|
|
|
|
|
|
type PostProcessor struct {
|
|
|
|
config Config
|
|
|
|
url *url.URL
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *PostProcessor) Configure(raws ...interface{}) error {
|
|
|
|
err := config.Decode(&p.config, &config.DecodeOpts{
|
|
|
|
Interpolate: true,
|
|
|
|
InterpolateContext: &p.config.ctx,
|
|
|
|
InterpolateFilter: &interpolate.RenderFilter{
|
|
|
|
Exclude: []string{},
|
|
|
|
},
|
|
|
|
}, raws...)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
errs := new(packer.MultiError)
|
|
|
|
vc := map[string]*string{
|
|
|
|
"host": &p.config.Host,
|
|
|
|
"username": &p.config.Username,
|
|
|
|
"password": &p.config.Password,
|
2017-07-31 00:26:58 -04:00
|
|
|
"vm_name": &p.config.VMName,
|
2017-07-09 14:12:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
for key, ptr := range vc {
|
|
|
|
if *ptr == "" {
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
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, "/") {
|
2017-07-09 14:56:39 -04:00
|
|
|
errs = packer.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 = 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
|
|
|
|
|
2017-07-09 14:12:37 -04:00
|
|
|
if len(errs.Errors) > 0 {
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
|
2017-07-09 20:33:03 -04:00
|
|
|
if _, ok := builtins[artifact.BuilderId()]; !ok {
|
2017-07-19 00:44:18 -04:00
|
|
|
return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId())
|
2017-07-09 20:33:03 -04:00
|
|
|
}
|
2017-07-09 14:56:39 -04:00
|
|
|
|
2017-07-09 20:33:03 -04:00
|
|
|
source := ""
|
|
|
|
for _, path := range artifact.Files() {
|
|
|
|
if strings.HasSuffix(path, ".vmx") {
|
|
|
|
source = path
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2017-08-14 22:00:19 -04:00
|
|
|
// 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-07-09 14:56:39 -04:00
|
|
|
ui.Message("Waiting 10s for VMWare vSphere to start")
|
|
|
|
time.Sleep(10 * time.Second)
|
2017-07-18 23:10:05 -04:00
|
|
|
c, err := govmomi.NewClient(context.Background(), p.url, p.config.Insecure)
|
2017-07-09 15:58:42 -04:00
|
|
|
if err != nil {
|
2017-07-25 23:14:59 -04:00
|
|
|
return nil, false, fmt.Errorf("Error connecting to vSphere: %s", err)
|
2017-07-09 15:58:42 -04:00
|
|
|
}
|
|
|
|
|
2017-07-31 10:30:13 -04:00
|
|
|
defer c.Logout(context.Background())
|
|
|
|
|
2017-07-09 15:58:42 -04:00
|
|
|
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,
|
|
|
|
},
|
2017-07-25 23:14:59 -04:00
|
|
|
&stepCreateFolder{
|
|
|
|
Folder: p.config.Folder,
|
|
|
|
},
|
2017-08-14 22:00:19 -04:00
|
|
|
&stepMarkAsTemplate{
|
2017-07-09 14:56:39 -04:00
|
|
|
VMName: p.config.VMName,
|
2017-07-09 20:33:03 -04:00
|
|
|
Source: source,
|
2017-07-09 14:56:39 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
runner := &multistep.BasicRunner{Steps: steps}
|
|
|
|
runner.Run(state)
|
2017-07-09 14:12:37 -04:00
|
|
|
|
2017-07-09 14:56:39 -04:00
|
|
|
if rawErr, ok := state.GetOk("error"); ok {
|
2017-07-25 23:14:59 -04:00
|
|
|
return nil, false, rawErr.(error)
|
2017-07-09 14:56:39 -04:00
|
|
|
}
|
2017-07-09 14:12:37 -04:00
|
|
|
return artifact, true, nil
|
|
|
|
}
|