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