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