github.com/rothwerx/packer@v0.9.0/builder/vmware/iso/builder.go (about) 1 package iso 2 3 import ( 4 "errors" 5 "fmt" 6 "io/ioutil" 7 "log" 8 "os" 9 "time" 10 11 "github.com/mitchellh/multistep" 12 vmwcommon "github.com/mitchellh/packer/builder/vmware/common" 13 "github.com/mitchellh/packer/common" 14 "github.com/mitchellh/packer/helper/communicator" 15 "github.com/mitchellh/packer/helper/config" 16 "github.com/mitchellh/packer/packer" 17 "github.com/mitchellh/packer/template/interpolate" 18 ) 19 20 const BuilderIdESX = "mitchellh.vmware-esx" 21 22 type Builder struct { 23 config Config 24 runner multistep.Runner 25 } 26 27 type Config struct { 28 common.PackerConfig `mapstructure:",squash"` 29 common.HTTPConfig `mapstructure:",squash"` 30 common.ISOConfig `mapstructure:",squash"` 31 vmwcommon.DriverConfig `mapstructure:",squash"` 32 vmwcommon.OutputConfig `mapstructure:",squash"` 33 vmwcommon.RunConfig `mapstructure:",squash"` 34 vmwcommon.ShutdownConfig `mapstructure:",squash"` 35 vmwcommon.SSHConfig `mapstructure:",squash"` 36 vmwcommon.ToolsConfig `mapstructure:",squash"` 37 vmwcommon.VMXConfig `mapstructure:",squash"` 38 39 AdditionalDiskSize []uint `mapstructure:"disk_additional_size"` 40 DiskName string `mapstructure:"vmdk_name"` 41 DiskSize uint `mapstructure:"disk_size"` 42 DiskTypeId string `mapstructure:"disk_type_id"` 43 FloppyFiles []string `mapstructure:"floppy_files"` 44 Format string `mapstructure:"format"` 45 GuestOSType string `mapstructure:"guest_os_type"` 46 Version string `mapstructure:"version"` 47 VMName string `mapstructure:"vm_name"` 48 BootCommand []string `mapstructure:"boot_command"` 49 SkipCompaction bool `mapstructure:"skip_compaction"` 50 VMXTemplatePath string `mapstructure:"vmx_template_path"` 51 VMXDiskTemplatePath string `mapstructure:"vmx_disk_template_path"` 52 53 RemoteType string `mapstructure:"remote_type"` 54 RemoteDatastore string `mapstructure:"remote_datastore"` 55 RemoteCacheDatastore string `mapstructure:"remote_cache_datastore"` 56 RemoteCacheDirectory string `mapstructure:"remote_cache_directory"` 57 RemoteHost string `mapstructure:"remote_host"` 58 RemotePort uint `mapstructure:"remote_port"` 59 RemoteUser string `mapstructure:"remote_username"` 60 RemotePassword string `mapstructure:"remote_password"` 61 RemotePrivateKey string `mapstructure:"remote_private_key_file"` 62 63 ctx interpolate.Context 64 } 65 66 func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 67 err := config.Decode(&b.config, &config.DecodeOpts{ 68 Interpolate: true, 69 InterpolateContext: &b.config.ctx, 70 InterpolateFilter: &interpolate.RenderFilter{ 71 Exclude: []string{ 72 "boot_command", 73 "tools_upload_path", 74 }, 75 }, 76 }, raws...) 77 if err != nil { 78 return nil, err 79 } 80 81 // Accumulate any errors and warnings 82 var errs *packer.MultiError 83 warnings := make([]string, 0) 84 85 isoWarnings, isoErrs := b.config.ISOConfig.Prepare(&b.config.ctx) 86 warnings = append(warnings, isoWarnings...) 87 errs = packer.MultiErrorAppend(errs, isoErrs...) 88 errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...) 89 errs = packer.MultiErrorAppend(errs, b.config.DriverConfig.Prepare(&b.config.ctx)...) 90 errs = packer.MultiErrorAppend(errs, 91 b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...) 92 errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) 93 errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...) 94 errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...) 95 errs = packer.MultiErrorAppend(errs, b.config.ToolsConfig.Prepare(&b.config.ctx)...) 96 errs = packer.MultiErrorAppend(errs, b.config.VMXConfig.Prepare(&b.config.ctx)...) 97 98 if b.config.DiskName == "" { 99 b.config.DiskName = "disk" 100 } 101 102 if b.config.DiskSize == 0 { 103 b.config.DiskSize = 40000 104 } 105 106 if b.config.DiskTypeId == "" { 107 // Default is growable virtual disk split in 2GB files. 108 b.config.DiskTypeId = "1" 109 110 if b.config.RemoteType == "esx5" { 111 b.config.DiskTypeId = "zeroedthick" 112 } 113 } 114 115 if b.config.FloppyFiles == nil { 116 b.config.FloppyFiles = make([]string, 0) 117 } 118 119 if b.config.GuestOSType == "" { 120 b.config.GuestOSType = "other" 121 } 122 123 if b.config.VMName == "" { 124 b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName) 125 } 126 127 if b.config.Version == "" { 128 b.config.Version = "9" 129 } 130 131 if b.config.RemoteUser == "" { 132 b.config.RemoteUser = "root" 133 } 134 135 if b.config.RemoteDatastore == "" { 136 b.config.RemoteDatastore = "datastore1" 137 } 138 139 if b.config.RemoteCacheDatastore == "" { 140 b.config.RemoteCacheDatastore = b.config.RemoteDatastore 141 } 142 143 if b.config.RemoteCacheDirectory == "" { 144 b.config.RemoteCacheDirectory = "packer_cache" 145 } 146 147 if b.config.RemotePort == 0 { 148 b.config.RemotePort = 22 149 } 150 151 if b.config.VMXTemplatePath != "" { 152 if err := b.validateVMXTemplatePath(); err != nil { 153 errs = packer.MultiErrorAppend( 154 errs, fmt.Errorf("vmx_template_path is invalid: %s", err)) 155 } 156 157 } 158 159 // Remote configuration validation 160 if b.config.RemoteType != "" { 161 if b.config.RemoteHost == "" { 162 errs = packer.MultiErrorAppend(errs, 163 fmt.Errorf("remote_host must be specified")) 164 } 165 } 166 167 if b.config.Format != "" { 168 if !(b.config.Format == "ova" || b.config.Format == "ovf" || b.config.Format == "vmx") { 169 errs = packer.MultiErrorAppend(errs, 170 fmt.Errorf("format must be one of ova, ovf, or vmx")) 171 } 172 } 173 174 // Warnings 175 if b.config.ShutdownCommand == "" { 176 warnings = append(warnings, 177 "A shutdown_command was not specified. Without a shutdown command, Packer\n"+ 178 "will forcibly halt the virtual machine, which may result in data loss.") 179 } 180 181 if errs != nil && len(errs.Errors) > 0 { 182 return warnings, errs 183 } 184 185 return warnings, nil 186 } 187 188 func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { 189 driver, err := NewDriver(&b.config) 190 if err != nil { 191 return nil, fmt.Errorf("Failed creating VMware driver: %s", err) 192 } 193 194 // Determine the output dir implementation 195 var dir OutputDir 196 switch d := driver.(type) { 197 case OutputDir: 198 dir = d 199 default: 200 dir = new(vmwcommon.LocalOutputDir) 201 } 202 if b.config.RemoteType != "" && b.config.Format != "" { 203 b.config.OutputDir = b.config.VMName 204 } 205 dir.SetOutputDir(b.config.OutputDir) 206 207 // Setup the state bag 208 state := new(multistep.BasicStateBag) 209 state.Put("cache", cache) 210 state.Put("config", &b.config) 211 state.Put("dir", dir) 212 state.Put("driver", driver) 213 state.Put("hook", hook) 214 state.Put("ui", ui) 215 216 steps := []multistep.Step{ 217 &vmwcommon.StepPrepareTools{ 218 RemoteType: b.config.RemoteType, 219 ToolsUploadFlavor: b.config.ToolsUploadFlavor, 220 }, 221 &common.StepDownload{ 222 Checksum: b.config.ISOChecksum, 223 ChecksumType: b.config.ISOChecksumType, 224 Description: "ISO", 225 Extension: "iso", 226 ResultKey: "iso_path", 227 TargetPath: b.config.TargetPath, 228 Url: b.config.ISOUrls, 229 }, 230 &vmwcommon.StepOutputDir{ 231 Force: b.config.PackerForce, 232 }, 233 &common.StepCreateFloppy{ 234 Files: b.config.FloppyFiles, 235 }, 236 &stepRemoteUpload{ 237 Key: "floppy_path", 238 Message: "Uploading Floppy to remote machine...", 239 }, 240 &stepRemoteUpload{ 241 Key: "iso_path", 242 Message: "Uploading ISO to remote machine...", 243 }, 244 &stepCreateDisk{}, 245 &stepCreateVMX{}, 246 &vmwcommon.StepConfigureVMX{ 247 CustomData: b.config.VMXData, 248 }, 249 &vmwcommon.StepSuppressMessages{}, 250 &common.StepHTTPServer{ 251 HTTPDir: b.config.HTTPDir, 252 HTTPPortMin: b.config.HTTPPortMin, 253 HTTPPortMax: b.config.HTTPPortMax, 254 }, 255 &vmwcommon.StepConfigureVNC{ 256 VNCPortMin: b.config.VNCPortMin, 257 VNCPortMax: b.config.VNCPortMax, 258 }, 259 &StepRegister{ 260 Format: b.config.Format, 261 }, 262 &vmwcommon.StepRun{ 263 BootWait: b.config.BootWait, 264 DurationBeforeStop: 5 * time.Second, 265 Headless: b.config.Headless, 266 }, 267 &vmwcommon.StepTypeBootCommand{ 268 BootCommand: b.config.BootCommand, 269 VMName: b.config.VMName, 270 Ctx: b.config.ctx, 271 }, 272 &communicator.StepConnect{ 273 Config: &b.config.SSHConfig.Comm, 274 Host: driver.CommHost, 275 SSHConfig: vmwcommon.SSHConfigFunc(&b.config.SSHConfig), 276 }, 277 &vmwcommon.StepUploadTools{ 278 RemoteType: b.config.RemoteType, 279 ToolsUploadFlavor: b.config.ToolsUploadFlavor, 280 ToolsUploadPath: b.config.ToolsUploadPath, 281 Ctx: b.config.ctx, 282 }, 283 &common.StepProvision{}, 284 &vmwcommon.StepShutdown{ 285 Command: b.config.ShutdownCommand, 286 Timeout: b.config.ShutdownTimeout, 287 }, 288 &vmwcommon.StepCleanFiles{}, 289 &vmwcommon.StepCompactDisk{ 290 Skip: b.config.SkipCompaction, 291 }, 292 &vmwcommon.StepConfigureVMX{ 293 CustomData: b.config.VMXDataPost, 294 SkipFloppy: true, 295 }, 296 &vmwcommon.StepCleanVMX{}, 297 &StepUploadVMX{ 298 RemoteType: b.config.RemoteType, 299 }, 300 &StepExport{ 301 Format: b.config.Format, 302 }, 303 } 304 305 // Run! 306 if b.config.PackerDebug { 307 b.runner = &multistep.DebugRunner{ 308 Steps: steps, 309 PauseFn: common.MultistepDebugFn(ui), 310 } 311 } else { 312 b.runner = &multistep.BasicRunner{Steps: steps} 313 } 314 315 b.runner.Run(state) 316 317 // If there was an error, return that 318 if rawErr, ok := state.GetOk("error"); ok { 319 return nil, rawErr.(error) 320 } 321 322 // If we were interrupted or cancelled, then just exit. 323 if _, ok := state.GetOk(multistep.StateCancelled); ok { 324 return nil, errors.New("Build was cancelled.") 325 } 326 327 if _, ok := state.GetOk(multistep.StateHalted); ok { 328 return nil, errors.New("Build was halted.") 329 } 330 331 // Compile the artifact list 332 var files []string 333 if b.config.RemoteType != "" { 334 dir = new(vmwcommon.LocalOutputDir) 335 dir.SetOutputDir(b.config.OutputDir) 336 files, err = dir.ListFiles() 337 } else { 338 files, err = state.Get("dir").(OutputDir).ListFiles() 339 } 340 if err != nil { 341 return nil, err 342 } 343 344 // Set the proper builder ID 345 builderId := vmwcommon.BuilderId 346 if b.config.RemoteType != "" { 347 builderId = BuilderIdESX 348 } 349 350 return &Artifact{ 351 builderId: builderId, 352 dir: dir, 353 f: files, 354 }, nil 355 } 356 357 func (b *Builder) Cancel() { 358 if b.runner != nil { 359 log.Println("Cancelling the step runner...") 360 b.runner.Cancel() 361 } 362 } 363 364 func (b *Builder) validateVMXTemplatePath() error { 365 f, err := os.Open(b.config.VMXTemplatePath) 366 if err != nil { 367 return err 368 } 369 defer f.Close() 370 371 data, err := ioutil.ReadAll(f) 372 if err != nil { 373 return err 374 } 375 376 return interpolate.Validate(string(data), &b.config.ctx) 377 }