github.com/kikitux/packer@v0.10.1-0.20160322154024-6237df566f9f/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("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 }