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