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