github.phpd.cn/hashicorp/packer@v1.3.2/builder/virtualbox/iso/builder.go (about) 1 package iso 2 3 import ( 4 "errors" 5 "fmt" 6 "log" 7 "strings" 8 9 vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common" 10 "github.com/hashicorp/packer/common" 11 "github.com/hashicorp/packer/common/bootcommand" 12 "github.com/hashicorp/packer/helper/communicator" 13 "github.com/hashicorp/packer/helper/config" 14 "github.com/hashicorp/packer/helper/multistep" 15 "github.com/hashicorp/packer/packer" 16 "github.com/hashicorp/packer/template/interpolate" 17 ) 18 19 const BuilderId = "mitchellh.virtualbox" 20 21 type Builder struct { 22 config Config 23 runner multistep.Runner 24 } 25 26 type Config struct { 27 common.PackerConfig `mapstructure:",squash"` 28 common.HTTPConfig `mapstructure:",squash"` 29 common.ISOConfig `mapstructure:",squash"` 30 common.FloppyConfig `mapstructure:",squash"` 31 bootcommand.BootConfig `mapstructure:",squash"` 32 vboxcommon.ExportConfig `mapstructure:",squash"` 33 vboxcommon.ExportOpts `mapstructure:",squash"` 34 vboxcommon.OutputConfig `mapstructure:",squash"` 35 vboxcommon.RunConfig `mapstructure:",squash"` 36 vboxcommon.ShutdownConfig `mapstructure:",squash"` 37 vboxcommon.SSHConfig `mapstructure:",squash"` 38 vboxcommon.VBoxManageConfig `mapstructure:",squash"` 39 vboxcommon.VBoxManagePostConfig `mapstructure:",squash"` 40 vboxcommon.VBoxVersionConfig `mapstructure:",squash"` 41 42 DiskSize uint `mapstructure:"disk_size"` 43 GuestAdditionsMode string `mapstructure:"guest_additions_mode"` 44 GuestAdditionsPath string `mapstructure:"guest_additions_path"` 45 GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"` 46 GuestAdditionsURL string `mapstructure:"guest_additions_url"` 47 GuestOSType string `mapstructure:"guest_os_type"` 48 HardDriveDiscard bool `mapstructure:"hard_drive_discard"` 49 HardDriveInterface string `mapstructure:"hard_drive_interface"` 50 SATAPortCount int `mapstructure:"sata_port_count"` 51 HardDriveNonrotational bool `mapstructure:"hard_drive_nonrotational"` 52 ISOInterface string `mapstructure:"iso_interface"` 53 KeepRegistered bool `mapstructure:"keep_registered"` 54 SkipExport bool `mapstructure:"skip_export"` 55 VMName string `mapstructure:"vm_name"` 56 57 ctx interpolate.Context 58 } 59 60 func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 61 err := config.Decode(&b.config, &config.DecodeOpts{ 62 Interpolate: true, 63 InterpolateContext: &b.config.ctx, 64 InterpolateFilter: &interpolate.RenderFilter{ 65 Exclude: []string{ 66 "boot_command", 67 "guest_additions_path", 68 "guest_additions_url", 69 "vboxmanage", 70 "vboxmanage_post", 71 }, 72 }, 73 }, raws...) 74 if err != nil { 75 return nil, err 76 } 77 78 // Accumulate any errors and warnings 79 var errs *packer.MultiError 80 warnings := make([]string, 0) 81 82 isoWarnings, isoErrs := b.config.ISOConfig.Prepare(&b.config.ctx) 83 warnings = append(warnings, isoWarnings...) 84 errs = packer.MultiErrorAppend(errs, isoErrs...) 85 86 errs = packer.MultiErrorAppend(errs, b.config.ExportConfig.Prepare(&b.config.ctx)...) 87 errs = packer.MultiErrorAppend(errs, b.config.ExportOpts.Prepare(&b.config.ctx)...) 88 errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...) 89 errs = packer.MultiErrorAppend( 90 errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...) 91 errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...) 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.VBoxManageConfig.Prepare(&b.config.ctx)...) 96 errs = packer.MultiErrorAppend(errs, b.config.VBoxManagePostConfig.Prepare(&b.config.ctx)...) 97 errs = packer.MultiErrorAppend(errs, b.config.VBoxVersionConfig.Prepare(&b.config.ctx)...) 98 errs = packer.MultiErrorAppend(errs, b.config.BootConfig.Prepare(&b.config.ctx)...) 99 100 if b.config.DiskSize == 0 { 101 b.config.DiskSize = 40000 102 } 103 104 if b.config.GuestAdditionsMode == "" { 105 b.config.GuestAdditionsMode = "upload" 106 } 107 108 if b.config.GuestAdditionsPath == "" { 109 b.config.GuestAdditionsPath = "VBoxGuestAdditions.iso" 110 } 111 112 if b.config.HardDriveInterface == "" { 113 b.config.HardDriveInterface = "ide" 114 } 115 116 if b.config.GuestOSType == "" { 117 b.config.GuestOSType = "Other" 118 } 119 120 if b.config.ISOInterface == "" { 121 b.config.ISOInterface = "ide" 122 } 123 124 if b.config.VMName == "" { 125 b.config.VMName = fmt.Sprintf( 126 "packer-%s-%d", b.config.PackerBuildName, interpolate.InitTime.Unix()) 127 } 128 129 if b.config.HardDriveInterface != "ide" && b.config.HardDriveInterface != "sata" && b.config.HardDriveInterface != "scsi" { 130 errs = packer.MultiErrorAppend( 131 errs, errors.New("hard_drive_interface can only be ide, sata, or scsi")) 132 } 133 134 if b.config.SATAPortCount == 0 { 135 b.config.SATAPortCount = 1 136 } 137 138 if b.config.SATAPortCount > 30 { 139 errs = packer.MultiErrorAppend( 140 errs, errors.New("sata_port_count cannot be greater than 30")) 141 } 142 143 if b.config.ISOInterface != "ide" && b.config.ISOInterface != "sata" { 144 errs = packer.MultiErrorAppend( 145 errs, errors.New("iso_interface can only be ide or sata")) 146 } 147 148 validMode := false 149 validModes := []string{ 150 vboxcommon.GuestAdditionsModeDisable, 151 vboxcommon.GuestAdditionsModeAttach, 152 vboxcommon.GuestAdditionsModeUpload, 153 } 154 155 for _, mode := range validModes { 156 if b.config.GuestAdditionsMode == mode { 157 validMode = true 158 break 159 } 160 } 161 162 if !validMode { 163 errs = packer.MultiErrorAppend(errs, 164 fmt.Errorf("guest_additions_mode is invalid. Must be one of: %v", validModes)) 165 } 166 167 if b.config.GuestAdditionsSHA256 != "" { 168 b.config.GuestAdditionsSHA256 = strings.ToLower(b.config.GuestAdditionsSHA256) 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 // Create the driver that we'll use to communicate with VirtualBox 187 driver, err := vboxcommon.NewDriver() 188 if err != nil { 189 return nil, fmt.Errorf("Failed creating VirtualBox driver: %s", err) 190 } 191 192 steps := []multistep.Step{ 193 &vboxcommon.StepDownloadGuestAdditions{ 194 GuestAdditionsMode: b.config.GuestAdditionsMode, 195 GuestAdditionsURL: b.config.GuestAdditionsURL, 196 GuestAdditionsSHA256: b.config.GuestAdditionsSHA256, 197 Ctx: b.config.ctx, 198 }, 199 &common.StepDownload{ 200 Checksum: b.config.ISOChecksum, 201 ChecksumType: b.config.ISOChecksumType, 202 Description: "ISO", 203 Extension: b.config.TargetExtension, 204 ResultKey: "iso_path", 205 TargetPath: b.config.TargetPath, 206 Url: b.config.ISOUrls, 207 }, 208 &vboxcommon.StepOutputDir{ 209 Force: b.config.PackerForce, 210 Path: b.config.OutputDir, 211 }, 212 &common.StepCreateFloppy{ 213 Files: b.config.FloppyConfig.FloppyFiles, 214 Directories: b.config.FloppyConfig.FloppyDirectories, 215 }, 216 &common.StepHTTPServer{ 217 HTTPDir: b.config.HTTPDir, 218 HTTPPortMin: b.config.HTTPPortMin, 219 HTTPPortMax: b.config.HTTPPortMax, 220 }, 221 new(vboxcommon.StepSuppressMessages), 222 new(stepCreateVM), 223 new(stepCreateDisk), 224 new(stepAttachISO), 225 &vboxcommon.StepAttachGuestAdditions{ 226 GuestAdditionsMode: b.config.GuestAdditionsMode, 227 }, 228 &vboxcommon.StepConfigureVRDP{ 229 VRDPBindAddress: b.config.VRDPBindAddress, 230 VRDPPortMin: b.config.VRDPPortMin, 231 VRDPPortMax: b.config.VRDPPortMax, 232 }, 233 new(vboxcommon.StepAttachFloppy), 234 &vboxcommon.StepForwardSSH{ 235 CommConfig: &b.config.SSHConfig.Comm, 236 HostPortMin: b.config.SSHHostPortMin, 237 HostPortMax: b.config.SSHHostPortMax, 238 SkipNatMapping: b.config.SSHSkipNatMapping, 239 }, 240 &vboxcommon.StepVBoxManage{ 241 Commands: b.config.VBoxManage, 242 Ctx: b.config.ctx, 243 }, 244 &vboxcommon.StepRun{ 245 Headless: b.config.Headless, 246 }, 247 &vboxcommon.StepTypeBootCommand{ 248 BootWait: b.config.BootWait, 249 BootCommand: b.config.FlatBootCommand(), 250 VMName: b.config.VMName, 251 Ctx: b.config.ctx, 252 GroupInterval: b.config.BootConfig.BootGroupInterval, 253 }, 254 &communicator.StepConnect{ 255 Config: &b.config.SSHConfig.Comm, 256 Host: vboxcommon.CommHost(b.config.SSHConfig.Comm.SSHHost), 257 SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(), 258 SSHPort: vboxcommon.SSHPort, 259 WinRMPort: vboxcommon.SSHPort, 260 }, 261 &vboxcommon.StepUploadVersion{ 262 Path: *b.config.VBoxVersionFile, 263 }, 264 &vboxcommon.StepUploadGuestAdditions{ 265 GuestAdditionsMode: b.config.GuestAdditionsMode, 266 GuestAdditionsPath: b.config.GuestAdditionsPath, 267 Ctx: b.config.ctx, 268 }, 269 new(common.StepProvision), 270 &common.StepCleanupTempKeys{ 271 Comm: &b.config.SSHConfig.Comm, 272 }, 273 &vboxcommon.StepShutdown{ 274 Command: b.config.ShutdownCommand, 275 Timeout: b.config.ShutdownTimeout, 276 Delay: b.config.PostShutdownDelay, 277 }, 278 new(vboxcommon.StepRemoveDevices), 279 &vboxcommon.StepVBoxManage{ 280 Commands: b.config.VBoxManagePost, 281 Ctx: b.config.ctx, 282 }, 283 &vboxcommon.StepExport{ 284 Format: b.config.Format, 285 OutputDir: b.config.OutputDir, 286 ExportOpts: b.config.ExportOpts.ExportOpts, 287 SkipNatMapping: b.config.SSHSkipNatMapping, 288 SkipExport: b.config.SkipExport, 289 }, 290 } 291 292 // Setup the state bag 293 state := new(multistep.BasicStateBag) 294 state.Put("cache", cache) 295 state.Put("config", &b.config) 296 state.Put("debug", b.config.PackerDebug) 297 state.Put("driver", driver) 298 state.Put("hook", hook) 299 state.Put("ui", ui) 300 301 // Run 302 b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state) 303 b.runner.Run(state) 304 305 // If there was an error, return that 306 if rawErr, ok := state.GetOk("error"); ok { 307 return nil, rawErr.(error) 308 } 309 310 // If we were interrupted or cancelled, then just exit. 311 if _, ok := state.GetOk(multistep.StateCancelled); ok { 312 return nil, errors.New("Build was cancelled.") 313 } 314 315 if _, ok := state.GetOk(multistep.StateHalted); ok { 316 return nil, errors.New("Build was halted.") 317 } 318 319 return vboxcommon.NewArtifact(b.config.OutputDir) 320 } 321 322 func (b *Builder) Cancel() { 323 if b.runner != nil { 324 log.Println("Cancelling the step runner...") 325 b.runner.Cancel() 326 } 327 }