github.com/amanya/packer@v0.12.1-0.20161117214323-902ac5ab2eb6/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 common.FloppyConfig `mapstructure:",squash"` 32 vmwcommon.DriverConfig `mapstructure:",squash"` 33 vmwcommon.OutputConfig `mapstructure:",squash"` 34 vmwcommon.RunConfig `mapstructure:",squash"` 35 vmwcommon.ShutdownConfig `mapstructure:",squash"` 36 vmwcommon.SSHConfig `mapstructure:",squash"` 37 vmwcommon.ToolsConfig `mapstructure:",squash"` 38 vmwcommon.VMXConfig `mapstructure:",squash"` 39 40 AdditionalDiskSize []uint `mapstructure:"disk_additional_size"` 41 DiskName string `mapstructure:"vmdk_name"` 42 DiskSize uint `mapstructure:"disk_size"` 43 DiskTypeId string `mapstructure:"disk_type_id"` 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 CommConfig communicator.Config `mapstructure:",squash"` 65 66 ctx interpolate.Context 67 } 68 69 func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 70 err := config.Decode(&b.config, &config.DecodeOpts{ 71 Interpolate: true, 72 InterpolateContext: &b.config.ctx, 73 InterpolateFilter: &interpolate.RenderFilter{ 74 Exclude: []string{ 75 "boot_command", 76 "tools_upload_path", 77 }, 78 }, 79 }, raws...) 80 if err != nil { 81 return nil, err 82 } 83 84 // Accumulate any errors and warnings 85 var errs *packer.MultiError 86 warnings := make([]string, 0) 87 88 isoWarnings, isoErrs := b.config.ISOConfig.Prepare(&b.config.ctx) 89 warnings = append(warnings, isoWarnings...) 90 errs = packer.MultiErrorAppend(errs, isoErrs...) 91 errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...) 92 errs = packer.MultiErrorAppend(errs, b.config.DriverConfig.Prepare(&b.config.ctx)...) 93 errs = packer.MultiErrorAppend(errs, 94 b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...) 95 errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) 96 errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...) 97 errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...) 98 errs = packer.MultiErrorAppend(errs, b.config.ToolsConfig.Prepare(&b.config.ctx)...) 99 errs = packer.MultiErrorAppend(errs, b.config.VMXConfig.Prepare(&b.config.ctx)...) 100 errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...) 101 102 if b.config.DiskName == "" { 103 b.config.DiskName = "disk" 104 } 105 106 if b.config.DiskSize == 0 { 107 b.config.DiskSize = 40000 108 } 109 110 if b.config.DiskTypeId == "" { 111 // Default is growable virtual disk split in 2GB files. 112 b.config.DiskTypeId = "1" 113 114 if b.config.RemoteType == "esx5" { 115 b.config.DiskTypeId = "zeroedthick" 116 } 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 if b.config.VMXTemplatePath != "" { 151 if err := b.validateVMXTemplatePath(); err != nil { 152 errs = packer.MultiErrorAppend( 153 errs, fmt.Errorf("vmx_template_path is invalid: %s", err)) 154 } 155 156 } 157 158 // Remote configuration validation 159 if b.config.RemoteType != "" { 160 if b.config.RemoteHost == "" { 161 errs = packer.MultiErrorAppend(errs, 162 fmt.Errorf("remote_host must be specified")) 163 } 164 } 165 166 if b.config.Format != "" { 167 if !(b.config.Format == "ova" || b.config.Format == "ovf" || b.config.Format == "vmx") { 168 errs = packer.MultiErrorAppend(errs, 169 fmt.Errorf("format must be one of ova, ovf, or vmx")) 170 } 171 } 172 173 // Warnings 174 if b.config.ShutdownCommand == "" { 175 warnings = append(warnings, 176 "A shutdown_command was not specified. Without a shutdown command, Packer\n"+ 177 "will forcibly halt the virtual machine, which may result in data loss.") 178 } 179 180 if errs != nil && len(errs.Errors) > 0 { 181 return warnings, errs 182 } 183 184 return warnings, nil 185 } 186 187 func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { 188 driver, err := NewDriver(&b.config) 189 if err != nil { 190 return nil, fmt.Errorf("Failed creating VMware driver: %s", err) 191 } 192 193 // Determine the output dir implementation 194 var dir OutputDir 195 switch d := driver.(type) { 196 case OutputDir: 197 dir = d 198 default: 199 dir = new(vmwcommon.LocalOutputDir) 200 } 201 if b.config.RemoteType != "" && b.config.Format != "" { 202 b.config.OutputDir = b.config.VMName 203 } 204 dir.SetOutputDir(b.config.OutputDir) 205 206 // Setup the state bag 207 state := new(multistep.BasicStateBag) 208 state.Put("cache", cache) 209 state.Put("config", &b.config) 210 state.Put("debug", b.config.PackerDebug) 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.FloppyConfig.FloppyFiles, 235 Directories: b.config.FloppyConfig.FloppyDirectories, 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 VNCBindAddress: b.config.VNCBindAddress, 258 VNCPortMin: b.config.VNCPortMin, 259 VNCPortMax: b.config.VNCPortMax, 260 VNCDisablePassword: b.config.VNCDisablePassword, 261 }, 262 &StepRegister{ 263 Format: b.config.Format, 264 }, 265 &vmwcommon.StepRun{ 266 BootWait: b.config.BootWait, 267 DurationBeforeStop: 5 * time.Second, 268 Headless: b.config.Headless, 269 }, 270 &vmwcommon.StepTypeBootCommand{ 271 BootCommand: b.config.BootCommand, 272 VMName: b.config.VMName, 273 Ctx: b.config.ctx, 274 }, 275 &communicator.StepConnect{ 276 Config: &b.config.SSHConfig.Comm, 277 Host: driver.CommHost, 278 SSHConfig: vmwcommon.SSHConfigFunc(&b.config.SSHConfig), 279 }, 280 &vmwcommon.StepUploadTools{ 281 RemoteType: b.config.RemoteType, 282 ToolsUploadFlavor: b.config.ToolsUploadFlavor, 283 ToolsUploadPath: b.config.ToolsUploadPath, 284 Ctx: b.config.ctx, 285 }, 286 &common.StepProvision{}, 287 &vmwcommon.StepShutdown{ 288 Command: b.config.ShutdownCommand, 289 Timeout: b.config.ShutdownTimeout, 290 }, 291 &vmwcommon.StepCleanFiles{}, 292 &vmwcommon.StepCompactDisk{ 293 Skip: b.config.SkipCompaction, 294 }, 295 &vmwcommon.StepConfigureVMX{ 296 CustomData: b.config.VMXDataPost, 297 SkipFloppy: true, 298 }, 299 &vmwcommon.StepCleanVMX{}, 300 &StepUploadVMX{ 301 RemoteType: b.config.RemoteType, 302 }, 303 &StepExport{ 304 Format: b.config.Format, 305 }, 306 } 307 308 // Run! 309 b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state) 310 b.runner.Run(state) 311 312 // If there was an error, return that 313 if rawErr, ok := state.GetOk("error"); ok { 314 return nil, rawErr.(error) 315 } 316 317 // If we were interrupted or cancelled, then just exit. 318 if _, ok := state.GetOk(multistep.StateCancelled); ok { 319 return nil, errors.New("Build was cancelled.") 320 } 321 322 if _, ok := state.GetOk(multistep.StateHalted); ok { 323 return nil, errors.New("Build was halted.") 324 } 325 326 // Compile the artifact list 327 var files []string 328 if b.config.RemoteType != "" && b.config.Format != "" { 329 dir = new(vmwcommon.LocalOutputDir) 330 dir.SetOutputDir(b.config.OutputDir) 331 files, err = dir.ListFiles() 332 } else { 333 files, err = state.Get("dir").(OutputDir).ListFiles() 334 } 335 if err != nil { 336 return nil, err 337 } 338 339 // Set the proper builder ID 340 builderId := vmwcommon.BuilderId 341 if b.config.RemoteType != "" { 342 builderId = BuilderIdESX 343 } 344 345 return &Artifact{ 346 builderId: builderId, 347 dir: dir, 348 f: files, 349 }, nil 350 } 351 352 func (b *Builder) Cancel() { 353 if b.runner != nil { 354 log.Println("Cancelling the step runner...") 355 b.runner.Cancel() 356 } 357 } 358 359 func (b *Builder) validateVMXTemplatePath() error { 360 f, err := os.Open(b.config.VMXTemplatePath) 361 if err != nil { 362 return err 363 } 364 defer f.Close() 365 366 data, err := ioutil.ReadAll(f) 367 if err != nil { 368 return err 369 } 370 371 return interpolate.Validate(string(data), &b.config.ctx) 372 }