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