github.com/yoctocloud/packer@v0.6.2-0.20160520224004-e11a0a18423f/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 KeepRegistered bool `mapstructure:"keep_registered"` 50 SkipCompaction bool `mapstructure:"skip_compaction"` 51 VMXTemplatePath string `mapstructure:"vmx_template_path"` 52 VMXDiskTemplatePath string `mapstructure:"vmx_disk_template_path"` 53 54 RemoteType string `mapstructure:"remote_type"` 55 RemoteDatastore string `mapstructure:"remote_datastore"` 56 RemoteCacheDatastore string `mapstructure:"remote_cache_datastore"` 57 RemoteCacheDirectory string `mapstructure:"remote_cache_directory"` 58 RemoteHost string `mapstructure:"remote_host"` 59 RemotePort uint `mapstructure:"remote_port"` 60 RemoteUser string `mapstructure:"remote_username"` 61 RemotePassword string `mapstructure:"remote_password"` 62 RemotePrivateKey string `mapstructure:"remote_private_key_file"` 63 64 ctx interpolate.Context 65 } 66 67 func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 68 err := config.Decode(&b.config, &config.DecodeOpts{ 69 Interpolate: true, 70 InterpolateContext: &b.config.ctx, 71 InterpolateFilter: &interpolate.RenderFilter{ 72 Exclude: []string{ 73 "boot_command", 74 "tools_upload_path", 75 }, 76 }, 77 }, raws...) 78 if err != nil { 79 return nil, err 80 } 81 82 // Accumulate any errors and warnings 83 var errs *packer.MultiError 84 warnings := make([]string, 0) 85 86 isoWarnings, isoErrs := b.config.ISOConfig.Prepare(&b.config.ctx) 87 warnings = append(warnings, isoWarnings...) 88 errs = packer.MultiErrorAppend(errs, isoErrs...) 89 errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...) 90 errs = packer.MultiErrorAppend(errs, b.config.DriverConfig.Prepare(&b.config.ctx)...) 91 errs = packer.MultiErrorAppend(errs, 92 b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...) 93 errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) 94 errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...) 95 errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...) 96 errs = packer.MultiErrorAppend(errs, b.config.ToolsConfig.Prepare(&b.config.ctx)...) 97 errs = packer.MultiErrorAppend(errs, b.config.VMXConfig.Prepare(&b.config.ctx)...) 98 99 if b.config.DiskName == "" { 100 b.config.DiskName = "disk" 101 } 102 103 if b.config.DiskSize == 0 { 104 b.config.DiskSize = 40000 105 } 106 107 if b.config.DiskTypeId == "" { 108 // Default is growable virtual disk split in 2GB files. 109 b.config.DiskTypeId = "1" 110 111 if b.config.RemoteType == "esx5" { 112 b.config.DiskTypeId = "zeroedthick" 113 } 114 } 115 116 if b.config.FloppyFiles == nil { 117 b.config.FloppyFiles = make([]string, 0) 118 } 119 120 if b.config.GuestOSType == "" { 121 b.config.GuestOSType = "other" 122 } 123 124 if b.config.VMName == "" { 125 b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName) 126 } 127 128 if b.config.Version == "" { 129 b.config.Version = "9" 130 } 131 132 if b.config.RemoteUser == "" { 133 b.config.RemoteUser = "root" 134 } 135 136 if b.config.RemoteDatastore == "" { 137 b.config.RemoteDatastore = "datastore1" 138 } 139 140 if b.config.RemoteCacheDatastore == "" { 141 b.config.RemoteCacheDatastore = b.config.RemoteDatastore 142 } 143 144 if b.config.RemoteCacheDirectory == "" { 145 b.config.RemoteCacheDirectory = "packer_cache" 146 } 147 148 if b.config.RemotePort == 0 { 149 b.config.RemotePort = 22 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("debug", b.config.PackerDebug) 212 state.Put("dir", dir) 213 state.Put("driver", driver) 214 state.Put("hook", hook) 215 state.Put("ui", ui) 216 217 steps := []multistep.Step{ 218 &vmwcommon.StepPrepareTools{ 219 RemoteType: b.config.RemoteType, 220 ToolsUploadFlavor: b.config.ToolsUploadFlavor, 221 }, 222 &common.StepDownload{ 223 Checksum: b.config.ISOChecksum, 224 ChecksumType: b.config.ISOChecksumType, 225 Description: "ISO", 226 Extension: "iso", 227 ResultKey: "iso_path", 228 TargetPath: b.config.TargetPath, 229 Url: b.config.ISOUrls, 230 }, 231 &vmwcommon.StepOutputDir{ 232 Force: b.config.PackerForce, 233 }, 234 &common.StepCreateFloppy{ 235 Files: b.config.FloppyFiles, 236 }, 237 &stepRemoteUpload{ 238 Key: "floppy_path", 239 Message: "Uploading Floppy to remote machine...", 240 }, 241 &stepRemoteUpload{ 242 Key: "iso_path", 243 Message: "Uploading ISO to remote machine...", 244 }, 245 &stepCreateDisk{}, 246 &stepCreateVMX{}, 247 &vmwcommon.StepConfigureVMX{ 248 CustomData: b.config.VMXData, 249 }, 250 &vmwcommon.StepSuppressMessages{}, 251 &common.StepHTTPServer{ 252 HTTPDir: b.config.HTTPDir, 253 HTTPPortMin: b.config.HTTPPortMin, 254 HTTPPortMax: b.config.HTTPPortMax, 255 }, 256 &vmwcommon.StepConfigureVNC{ 257 VNCPortMin: b.config.VNCPortMin, 258 VNCPortMax: b.config.VNCPortMax, 259 }, 260 &StepRegister{ 261 Format: b.config.Format, 262 }, 263 &vmwcommon.StepRun{ 264 BootWait: b.config.BootWait, 265 DurationBeforeStop: 5 * time.Second, 266 Headless: b.config.Headless, 267 }, 268 &vmwcommon.StepTypeBootCommand{ 269 BootCommand: b.config.BootCommand, 270 VMName: b.config.VMName, 271 Ctx: b.config.ctx, 272 }, 273 &communicator.StepConnect{ 274 Config: &b.config.SSHConfig.Comm, 275 Host: driver.CommHost, 276 SSHConfig: vmwcommon.SSHConfigFunc(&b.config.SSHConfig), 277 }, 278 &vmwcommon.StepUploadTools{ 279 RemoteType: b.config.RemoteType, 280 ToolsUploadFlavor: b.config.ToolsUploadFlavor, 281 ToolsUploadPath: b.config.ToolsUploadPath, 282 Ctx: b.config.ctx, 283 }, 284 &common.StepProvision{}, 285 &vmwcommon.StepShutdown{ 286 Command: b.config.ShutdownCommand, 287 Timeout: b.config.ShutdownTimeout, 288 }, 289 &vmwcommon.StepCleanFiles{}, 290 &vmwcommon.StepCompactDisk{ 291 Skip: b.config.SkipCompaction, 292 }, 293 &vmwcommon.StepConfigureVMX{ 294 CustomData: b.config.VMXDataPost, 295 SkipFloppy: true, 296 }, 297 &vmwcommon.StepCleanVMX{}, 298 &StepUploadVMX{ 299 RemoteType: b.config.RemoteType, 300 }, 301 &StepExport{ 302 Format: b.config.Format, 303 }, 304 } 305 306 // Run! 307 if b.config.PackerDebug { 308 pauseFn := common.MultistepDebugFn(ui) 309 state.Put("pauseFn", pauseFn) 310 b.runner = &multistep.DebugRunner{ 311 Steps: steps, 312 PauseFn: pauseFn, 313 } 314 } else { 315 b.runner = &multistep.BasicRunner{Steps: steps} 316 } 317 318 b.runner.Run(state) 319 320 // If there was an error, return that 321 if rawErr, ok := state.GetOk("error"); ok { 322 return nil, rawErr.(error) 323 } 324 325 // If we were interrupted or cancelled, then just exit. 326 if _, ok := state.GetOk(multistep.StateCancelled); ok { 327 return nil, errors.New("Build was cancelled.") 328 } 329 330 if _, ok := state.GetOk(multistep.StateHalted); ok { 331 return nil, errors.New("Build was halted.") 332 } 333 334 // Compile the artifact list 335 var files []string 336 if b.config.RemoteType != "" && b.config.Format != "" { 337 dir = new(vmwcommon.LocalOutputDir) 338 dir.SetOutputDir(b.config.OutputDir) 339 files, err = dir.ListFiles() 340 } else { 341 files, err = state.Get("dir").(OutputDir).ListFiles() 342 } 343 if err != nil { 344 return nil, err 345 } 346 347 // Set the proper builder ID 348 builderId := vmwcommon.BuilderId 349 if b.config.RemoteType != "" { 350 builderId = BuilderIdESX 351 } 352 353 return &Artifact{ 354 builderId: builderId, 355 dir: dir, 356 f: files, 357 }, nil 358 } 359 360 func (b *Builder) Cancel() { 361 if b.runner != nil { 362 log.Println("Cancelling the step runner...") 363 b.runner.Cancel() 364 } 365 } 366 367 func (b *Builder) validateVMXTemplatePath() error { 368 f, err := os.Open(b.config.VMXTemplatePath) 369 if err != nil { 370 return err 371 } 372 defer f.Close() 373 374 data, err := ioutil.ReadAll(f) 375 if err != nil { 376 return err 377 } 378 379 return interpolate.Validate(string(data), &b.config.ctx) 380 }