github.com/askholme/packer@v0.7.2-0.20140924152349-70d9566a6852/builder/parallels/iso/builder.go (about) 1 package iso 2 3 import ( 4 "errors" 5 "fmt" 6 "github.com/mitchellh/multistep" 7 parallelscommon "github.com/mitchellh/packer/builder/parallels/common" 8 "github.com/mitchellh/packer/common" 9 "github.com/mitchellh/packer/packer" 10 "log" 11 "strings" 12 ) 13 14 const BuilderId = "rickard-von-essen.parallels" 15 16 type Builder struct { 17 config config 18 runner multistep.Runner 19 } 20 21 type config struct { 22 common.PackerConfig `mapstructure:",squash"` 23 parallelscommon.FloppyConfig `mapstructure:",squash"` 24 parallelscommon.OutputConfig `mapstructure:",squash"` 25 parallelscommon.PrlctlConfig `mapstructure:",squash"` 26 parallelscommon.PrlctlVersionConfig `mapstructure:",squash"` 27 parallelscommon.RunConfig `mapstructure:",squash"` 28 parallelscommon.ShutdownConfig `mapstructure:",squash"` 29 parallelscommon.SSHConfig `mapstructure:",squash"` 30 parallelscommon.ToolsConfig `mapstructure:",squash"` 31 32 BootCommand []string `mapstructure:"boot_command"` 33 DiskSize uint `mapstructure:"disk_size"` 34 GuestOSType string `mapstructure:"guest_os_type"` 35 HardDriveInterface string `mapstructure:"hard_drive_interface"` 36 HostInterfaces []string `mapstructure:"host_interfaces"` 37 HTTPDir string `mapstructure:"http_directory"` 38 HTTPPortMin uint `mapstructure:"http_port_min"` 39 HTTPPortMax uint `mapstructure:"http_port_max"` 40 ISOChecksum string `mapstructure:"iso_checksum"` 41 ISOChecksumType string `mapstructure:"iso_checksum_type"` 42 ISOUrls []string `mapstructure:"iso_urls"` 43 VMName string `mapstructure:"vm_name"` 44 45 RawSingleISOUrl string `mapstructure:"iso_url"` 46 47 // Deprecated parameters 48 GuestOSDistribution string `mapstructure:"guest_os_distribution"` 49 ParallelsToolsHostPath string `mapstructure:"parallels_tools_host_path"` 50 51 tpl *packer.ConfigTemplate 52 } 53 54 func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 55 56 md, err := common.DecodeConfig(&b.config, raws...) 57 if err != nil { 58 return nil, err 59 } 60 61 b.config.tpl, err = packer.NewConfigTemplate() 62 if err != nil { 63 return nil, err 64 } 65 b.config.tpl.UserVars = b.config.PackerUserVars 66 67 // Accumulate any errors and warnings 68 errs := common.CheckUnusedConfig(md) 69 errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(b.config.tpl)...) 70 errs = packer.MultiErrorAppend( 71 errs, b.config.OutputConfig.Prepare(b.config.tpl, &b.config.PackerConfig)...) 72 errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(b.config.tpl)...) 73 errs = packer.MultiErrorAppend(errs, b.config.PrlctlConfig.Prepare(b.config.tpl)...) 74 errs = packer.MultiErrorAppend(errs, b.config.PrlctlVersionConfig.Prepare(b.config.tpl)...) 75 errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(b.config.tpl)...) 76 errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(b.config.tpl)...) 77 errs = packer.MultiErrorAppend(errs, b.config.ToolsConfig.Prepare(b.config.tpl)...) 78 warnings := make([]string, 0) 79 80 if b.config.DiskSize == 0 { 81 b.config.DiskSize = 40000 82 } 83 84 if b.config.HardDriveInterface == "" { 85 b.config.HardDriveInterface = "sata" 86 } 87 88 if b.config.GuestOSType == "" { 89 b.config.GuestOSType = "other" 90 } 91 92 if b.config.GuestOSDistribution != "" { 93 // Compatibility with older templates: 94 // Use value of 'guest_os_distribution' if it is defined. 95 b.config.GuestOSType = b.config.GuestOSDistribution 96 warnings = append(warnings, 97 "A 'guest_os_distribution' has been completely replaced with 'guest_os_type'\n"+ 98 "It is recommended to remove it and assign the previous value to 'guest_os_type'.\n"+ 99 "Run it to see all available values: `prlctl create x -d list` ") 100 } 101 102 if b.config.HTTPPortMin == 0 { 103 b.config.HTTPPortMin = 8000 104 } 105 106 if b.config.HTTPPortMax == 0 { 107 b.config.HTTPPortMax = 9000 108 } 109 110 if len(b.config.HostInterfaces) == 0 { 111 b.config.HostInterfaces = []string{"en0", "en1", "en2", "en3", "en4", "en5", "en6", "en7", 112 "en8", "en9", "ppp0", "ppp1", "ppp2"} 113 } 114 115 if b.config.VMName == "" { 116 b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName) 117 } 118 119 // Errors 120 templates := map[string]*string{ 121 "guest_os_type": &b.config.GuestOSType, 122 "hard_drive_interface": &b.config.HardDriveInterface, 123 "http_directory": &b.config.HTTPDir, 124 "iso_checksum": &b.config.ISOChecksum, 125 "iso_checksum_type": &b.config.ISOChecksumType, 126 "iso_url": &b.config.RawSingleISOUrl, 127 "vm_name": &b.config.VMName, 128 } 129 130 for n, ptr := range templates { 131 var err error 132 *ptr, err = b.config.tpl.Process(*ptr, nil) 133 if err != nil { 134 errs = packer.MultiErrorAppend( 135 errs, fmt.Errorf("Error processing %s: %s", n, err)) 136 } 137 } 138 139 for i, url := range b.config.ISOUrls { 140 var err error 141 b.config.ISOUrls[i], err = b.config.tpl.Process(url, nil) 142 if err != nil { 143 errs = packer.MultiErrorAppend( 144 errs, fmt.Errorf("Error processing iso_urls[%d]: %s", i, err)) 145 } 146 } 147 148 for i, command := range b.config.BootCommand { 149 if err := b.config.tpl.Validate(command); err != nil { 150 errs = packer.MultiErrorAppend(errs, 151 fmt.Errorf("Error processing boot_command[%d]: %s", i, err)) 152 } 153 } 154 155 if b.config.HardDriveInterface != "ide" && b.config.HardDriveInterface != "sata" && b.config.HardDriveInterface != "scsi" { 156 errs = packer.MultiErrorAppend( 157 errs, errors.New("hard_drive_interface can only be ide, sata, or scsi")) 158 } 159 160 if b.config.HTTPPortMin > b.config.HTTPPortMax { 161 errs = packer.MultiErrorAppend( 162 errs, errors.New("http_port_min must be less than http_port_max")) 163 } 164 165 if b.config.ISOChecksumType == "" { 166 errs = packer.MultiErrorAppend( 167 errs, errors.New("The iso_checksum_type must be specified.")) 168 } else { 169 b.config.ISOChecksumType = strings.ToLower(b.config.ISOChecksumType) 170 if b.config.ISOChecksumType != "none" { 171 if b.config.ISOChecksum == "" { 172 errs = packer.MultiErrorAppend( 173 errs, errors.New("Due to large file sizes, an iso_checksum is required")) 174 } else { 175 b.config.ISOChecksum = strings.ToLower(b.config.ISOChecksum) 176 } 177 178 if h := common.HashForType(b.config.ISOChecksumType); h == nil { 179 errs = packer.MultiErrorAppend( 180 errs, 181 fmt.Errorf("Unsupported checksum type: %s", b.config.ISOChecksumType)) 182 } 183 } 184 } 185 186 if b.config.RawSingleISOUrl == "" && len(b.config.ISOUrls) == 0 { 187 errs = packer.MultiErrorAppend( 188 errs, errors.New("One of iso_url or iso_urls must be specified.")) 189 } else if b.config.RawSingleISOUrl != "" && len(b.config.ISOUrls) > 0 { 190 errs = packer.MultiErrorAppend( 191 errs, errors.New("Only one of iso_url or iso_urls may be specified.")) 192 } else if b.config.RawSingleISOUrl != "" { 193 b.config.ISOUrls = []string{b.config.RawSingleISOUrl} 194 } 195 196 for i, url := range b.config.ISOUrls { 197 b.config.ISOUrls[i], err = common.DownloadableURL(url) 198 if err != nil { 199 errs = packer.MultiErrorAppend( 200 errs, fmt.Errorf("Failed to parse iso_url %d: %s", i+1, err)) 201 } 202 } 203 204 // Warnings 205 if b.config.ISOChecksumType == "none" { 206 warnings = append(warnings, 207 "A checksum type of 'none' was specified. Since ISO files are so big,\n"+ 208 "a checksum is highly recommended.") 209 } 210 211 if b.config.ShutdownCommand == "" { 212 warnings = append(warnings, 213 "A shutdown_command was not specified. Without a shutdown command, Packer\n"+ 214 "will forcibly halt the virtual machine, which may result in data loss.") 215 } 216 217 if b.config.ParallelsToolsHostPath != "" { 218 warnings = append(warnings, 219 "A 'parallels_tools_host_path' has been deprecated and not in use anymore\n"+ 220 "You can remove it from your Packer template.") 221 } 222 223 if errs != nil && len(errs.Errors) > 0 { 224 return warnings, errs 225 } 226 227 return warnings, nil 228 } 229 230 func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { 231 // Create the driver that we'll use to communicate with Parallels 232 driver, err := parallelscommon.NewDriver() 233 if err != nil { 234 return nil, fmt.Errorf("Failed creating Parallels driver: %s", err) 235 } 236 237 steps := []multistep.Step{ 238 ¶llelscommon.StepPrepareParallelsTools{ 239 ParallelsToolsFlavor: b.config.ParallelsToolsFlavor, 240 ParallelsToolsMode: b.config.ParallelsToolsMode, 241 }, 242 &common.StepDownload{ 243 Checksum: b.config.ISOChecksum, 244 ChecksumType: b.config.ISOChecksumType, 245 Description: "ISO", 246 ResultKey: "iso_path", 247 Url: b.config.ISOUrls, 248 }, 249 ¶llelscommon.StepOutputDir{ 250 Force: b.config.PackerForce, 251 Path: b.config.OutputDir, 252 }, 253 &common.StepCreateFloppy{ 254 Files: b.config.FloppyFiles, 255 }, 256 new(stepHTTPServer), 257 new(stepCreateVM), 258 new(stepCreateDisk), 259 new(stepAttachISO), 260 ¶llelscommon.StepAttachParallelsTools{ 261 ParallelsToolsMode: b.config.ParallelsToolsMode, 262 }, 263 new(parallelscommon.StepAttachFloppy), 264 ¶llelscommon.StepPrlctl{ 265 Commands: b.config.Prlctl, 266 Tpl: b.config.tpl, 267 }, 268 ¶llelscommon.StepRun{ 269 BootWait: b.config.BootWait, 270 Headless: b.config.Headless, // TODO: migth work on Enterprise Ed. 271 }, 272 ¶llelscommon.StepTypeBootCommand{ 273 BootCommand: b.config.BootCommand, 274 HostInterfaces: b.config.HostInterfaces, 275 VMName: b.config.VMName, 276 Tpl: b.config.tpl, 277 }, 278 &common.StepConnectSSH{ 279 SSHAddress: parallelscommon.SSHAddress, 280 SSHConfig: parallelscommon.SSHConfigFunc(b.config.SSHConfig), 281 SSHWaitTimeout: b.config.SSHWaitTimeout, 282 }, 283 ¶llelscommon.StepUploadVersion{ 284 Path: b.config.PrlctlVersionFile, 285 }, 286 ¶llelscommon.StepUploadParallelsTools{ 287 ParallelsToolsFlavor: b.config.ParallelsToolsFlavor, 288 ParallelsToolsGuestPath: b.config.ParallelsToolsGuestPath, 289 ParallelsToolsMode: b.config.ParallelsToolsMode, 290 Tpl: b.config.tpl, 291 }, 292 new(common.StepProvision), 293 ¶llelscommon.StepShutdown{ 294 Command: b.config.ShutdownCommand, 295 Timeout: b.config.ShutdownTimeout, 296 }, 297 new(parallelscommon.StepRemoveDevices), 298 } 299 300 // Setup the state bag 301 state := new(multistep.BasicStateBag) 302 state.Put("cache", cache) 303 state.Put("config", &b.config) 304 state.Put("driver", driver) 305 state.Put("hook", hook) 306 state.Put("ui", ui) 307 308 // Run 309 if b.config.PackerDebug { 310 b.runner = &multistep.DebugRunner{ 311 Steps: steps, 312 PauseFn: common.MultistepDebugFn(ui), 313 } 314 } else { 315 b.runner = &multistep.BasicRunner{Steps: steps} 316 } 317 318 b.runner.Run(state) 319 320 // If there was an error, return that 321 if rawErr, ok := state.GetOk("error"); ok { 322 return nil, rawErr.(error) 323 } 324 325 // If we were interrupted or cancelled, then just exit. 326 if _, ok := state.GetOk(multistep.StateCancelled); ok { 327 return nil, errors.New("Build was cancelled.") 328 } 329 330 if _, ok := state.GetOk(multistep.StateHalted); ok { 331 return nil, errors.New("Build was halted.") 332 } 333 334 return parallelscommon.NewArtifact(b.config.OutputDir) 335 } 336 337 func (b *Builder) Cancel() { 338 if b.runner != nil { 339 log.Println("Cancelling the step runner...") 340 b.runner.Cancel() 341 } 342 }