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