github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/post-processor/vsphere-template/post-processor.go (about)

     1  package vsphere_template
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/url"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/hashicorp/packer/common"
    11  	"github.com/hashicorp/packer/helper/config"
    12  	"github.com/hashicorp/packer/packer"
    13  	"github.com/hashicorp/packer/template/interpolate"
    14  	"github.com/mitchellh/multistep"
    15  	"github.com/vmware/govmomi"
    16  )
    17  
    18  var builtins = map[string]string{
    19  	"mitchellh.vmware-esx": "vmware",
    20  }
    21  
    22  type Config struct {
    23  	common.PackerConfig `mapstructure:",squash"`
    24  	Host                string `mapstructure:"host"`
    25  	Insecure            bool   `mapstructure:"insecure"`
    26  	Username            string `mapstructure:"username"`
    27  	Password            string `mapstructure:"password"`
    28  	Datacenter          string `mapstructure:"datacenter"`
    29  	Folder              string `mapstructure:"folder"`
    30  
    31  	ctx interpolate.Context
    32  }
    33  
    34  type PostProcessor struct {
    35  	config Config
    36  	url    *url.URL
    37  }
    38  
    39  func (p *PostProcessor) Configure(raws ...interface{}) error {
    40  	err := config.Decode(&p.config, &config.DecodeOpts{
    41  		Interpolate:        true,
    42  		InterpolateContext: &p.config.ctx,
    43  		InterpolateFilter: &interpolate.RenderFilter{
    44  			Exclude: []string{},
    45  		},
    46  	}, raws...)
    47  
    48  	if err != nil {
    49  		return err
    50  	}
    51  
    52  	errs := new(packer.MultiError)
    53  	vc := map[string]*string{
    54  		"host":     &p.config.Host,
    55  		"username": &p.config.Username,
    56  		"password": &p.config.Password,
    57  	}
    58  
    59  	for key, ptr := range vc {
    60  		if *ptr == "" {
    61  			errs = packer.MultiErrorAppend(
    62  				errs, fmt.Errorf("%s must be set", key))
    63  		}
    64  	}
    65  
    66  	if p.config.Folder != "" && !strings.HasPrefix(p.config.Folder, "/") {
    67  		errs = packer.MultiErrorAppend(
    68  			errs, fmt.Errorf("Folder must be bound to the root"))
    69  	}
    70  
    71  	sdk, err := url.Parse(fmt.Sprintf("https://%v/sdk", p.config.Host))
    72  	if err != nil {
    73  		errs = packer.MultiErrorAppend(
    74  			errs, fmt.Errorf("Error invalid vSphere sdk endpoint: %s", err))
    75  	}
    76  
    77  	sdk.User = url.UserPassword(p.config.Username, p.config.Password)
    78  	p.url = sdk
    79  
    80  	if len(errs.Errors) > 0 {
    81  		return errs
    82  	}
    83  	return nil
    84  }
    85  
    86  func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
    87  	if _, ok := builtins[artifact.BuilderId()]; !ok {
    88  		return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId())
    89  	}
    90  
    91  	source := ""
    92  	for _, path := range artifact.Files() {
    93  		if strings.HasSuffix(path, ".vmx") {
    94  			source = path
    95  			break
    96  		}
    97  	}
    98  	// In some occasions the VM state is powered on and if we immediately try to mark as template
    99  	// (after the ESXi creates it) it will fail. If vSphere is given a few seconds this behavior doesn't reappear.
   100  	ui.Message("Waiting 10s for VMware vSphere to start")
   101  	time.Sleep(10 * time.Second)
   102  	c, err := govmomi.NewClient(context.Background(), p.url, p.config.Insecure)
   103  	if err != nil {
   104  		return nil, false, fmt.Errorf("Error connecting to vSphere: %s", err)
   105  	}
   106  
   107  	defer c.Logout(context.Background())
   108  
   109  	state := new(multistep.BasicStateBag)
   110  	state.Put("ui", ui)
   111  	state.Put("client", c)
   112  
   113  	steps := []multistep.Step{
   114  		&stepChooseDatacenter{
   115  			Datacenter: p.config.Datacenter,
   116  		},
   117  		&stepCreateFolder{
   118  			Folder: p.config.Folder,
   119  		},
   120  		&stepMarkAsTemplate{
   121  			VMName: artifact.Id(),
   122  			Source: source,
   123  		},
   124  	}
   125  	runner := common.NewRunnerWithPauseFn(steps, p.config.PackerConfig, ui, state)
   126  	runner.Run(state)
   127  
   128  	if rawErr, ok := state.GetOk("error"); ok {
   129  		return nil, false, rawErr.(error)
   130  	}
   131  	return artifact, true, nil
   132  }