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 }