github.com/vijayrajah/packer@v1.3.2/post-processor/vsphere-template/post-processor.go (about)

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