func (b *Builder) Prepare(raws ...interface{}) error { var md mapstructure.Metadata decoderConfig := &mapstructure.DecoderConfig{ Metadata: &md, Result: &b.config, } decoder, err := mapstructure.NewDecoder(decoderConfig) if err != nil { return err } for _, raw := range raws { err := decoder.Decode(raw) if err != nil { return err } } // Accumulate any errors errs := make([]error, 0) // Unused keys are errors if len(md.Unused) > 0 { sort.Strings(md.Unused) for _, unused := range md.Unused { if unused != "type" && !strings.HasPrefix(unused, "packer_") { errs = append( errs, fmt.Errorf("Unknown configuration key: %s", unused)) } } } if b.config.DiskName == "" { b.config.DiskName = "disk" } if b.config.DiskSize == 0 { b.config.DiskSize = 40000 } if b.config.FloppyFiles == nil { b.config.FloppyFiles = make([]string, 0) } if b.config.GuestOSType == "" { b.config.GuestOSType = "other" } if b.config.VMName == "" { b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName) } if b.config.HTTPPortMin == 0 { b.config.HTTPPortMin = 8000 } if b.config.HTTPPortMax == 0 { b.config.HTTPPortMax = 9000 } if b.config.RawBootWait == "" { b.config.RawBootWait = "10s" } if b.config.VNCPortMin == 0 { b.config.VNCPortMin = 5900 } if b.config.VNCPortMax == 0 { b.config.VNCPortMax = 6000 } if b.config.OutputDir == "" { b.config.OutputDir = fmt.Sprintf("output-%s", b.config.PackerBuildName) } if b.config.SSHPort == 0 { b.config.SSHPort = 22 } if b.config.ToolsUploadPath == "" { b.config.ToolsUploadPath = "{{ .Flavor }}.iso" } if b.config.HTTPPortMin > b.config.HTTPPortMax { errs = append(errs, errors.New("http_port_min must be less than http_port_max")) } if b.config.ISOChecksum == "" { errs = append(errs, errors.New("Due to large file sizes, an iso_checksum is required")) } else { b.config.ISOChecksum = strings.ToLower(b.config.ISOChecksum) } if b.config.ISOChecksumType == "" { errs = append(errs, errors.New("The iso_checksum_type must be specified.")) } else { b.config.ISOChecksumType = strings.ToLower(b.config.ISOChecksumType) if h := common.HashForType(b.config.ISOChecksumType); h == nil { errs = append( errs, fmt.Errorf("Unsupported checksum type: %s", b.config.ISOChecksumType)) } } if b.config.ISOUrl == "" { errs = append(errs, errors.New("An iso_url must be specified.")) } else { url, err := url.Parse(b.config.ISOUrl) if err != nil { errs = append(errs, fmt.Errorf("iso_url is not a valid URL: %s", err)) } else { if url.Scheme == "" { url.Scheme = "file" } if url.Scheme == "file" { if _, err := os.Stat(url.Path); err != nil { errs = append(errs, fmt.Errorf("iso_url points to bad file: %s", err)) } } else { supportedSchemes := []string{"file", "http", "https"} scheme := strings.ToLower(url.Scheme) found := false for _, supported := range supportedSchemes { if scheme == supported { found = true break } } if !found { errs = append(errs, fmt.Errorf("Unsupported URL scheme in iso_url: %s", scheme)) } } } if len(errs) == 0 { // Put the URL back together since we may have modified it b.config.ISOUrl = url.String() } } if !b.config.PackerForce { if _, err := os.Stat(b.config.OutputDir); err == nil { errs = append( errs, errors.New("Output directory already exists. It must not exist.")) } } if b.config.SSHUser == "" { errs = append(errs, errors.New("An ssh_username must be specified.")) } if b.config.RawBootWait != "" { b.config.bootWait, err = time.ParseDuration(b.config.RawBootWait) if err != nil { errs = append(errs, fmt.Errorf("Failed parsing boot_wait: %s", err)) } } if b.config.RawShutdownTimeout == "" { b.config.RawShutdownTimeout = "5m" } b.config.shutdownTimeout, err = time.ParseDuration(b.config.RawShutdownTimeout) if err != nil { errs = append(errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err)) } if b.config.RawSSHWaitTimeout == "" { b.config.RawSSHWaitTimeout = "20m" } b.config.sshWaitTimeout, err = time.ParseDuration(b.config.RawSSHWaitTimeout) if err != nil { errs = append(errs, fmt.Errorf("Failed parsing ssh_wait_timeout: %s", err)) } if _, err := template.New("path").Parse(b.config.ToolsUploadPath); err != nil { errs = append(errs, fmt.Errorf("tools_upload_path invalid: %s", err)) } if b.config.VNCPortMin > b.config.VNCPortMax { errs = append(errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max")) } b.driver, err = NewDriver() if err != nil { errs = append(errs, fmt.Errorf("Failed creating VMware driver: %s", err)) } if len(errs) > 0 { return &packer.MultiError{errs} } return nil }
func (s stepDownloadISO) Run(state map[string]interface{}) multistep.StepAction { cache := state["cache"].(packer.Cache) config := state["config"].(*config) ui := state["ui"].(packer.Ui) checksum, err := hex.DecodeString(config.ISOChecksum) if err != nil { state["error"] = fmt.Errorf("Error parsing checksum: %s", err) return multistep.ActionHalt } log.Printf("Acquiring lock to download the ISO.") cachePath := cache.Lock(config.ISOUrl) defer cache.Unlock(config.ISOUrl) downloadConfig := &common.DownloadConfig{ Url: config.ISOUrl, TargetPath: cachePath, CopyFile: false, Hash: common.HashForType(config.ISOChecksumType), Checksum: checksum, } download := common.NewDownloadClient(downloadConfig) downloadCompleteCh := make(chan error, 1) go func() { ui.Say("Copying or downloading ISO. Progress will be reported periodically.") cachePath, err = download.Get() downloadCompleteCh <- err }() progressTicker := time.NewTicker(5 * time.Second) defer progressTicker.Stop() DownloadWaitLoop: for { select { case err := <-downloadCompleteCh: if err != nil { err := fmt.Errorf("Error downloading ISO: %s", err) state["error"] = err ui.Error(err.Error()) return multistep.ActionHalt } break DownloadWaitLoop case <-progressTicker.C: ui.Message(fmt.Sprintf("Download progress: %d%%", download.PercentProgress())) case <-time.After(1 * time.Second): if _, ok := state[multistep.StateCancelled]; ok { ui.Say("Interrupt received. Cancelling download...") return multistep.ActionHalt } } } log.Printf("Path to ISO on disk: %s", cachePath) state["iso_path"] = cachePath return multistep.ActionContinue }
func (s *stepDownloadISO) Run(state map[string]interface{}) multistep.StepAction { cache := state["cache"].(packer.Cache) config := state["config"].(*config) ui := state["ui"].(packer.Ui) checksum, err := hex.DecodeString(config.ISOChecksum) if err != nil { state["error"] = fmt.Errorf("Error parsing checksum: %s", err) return multistep.ActionHalt } log.Printf("Acquiring lock to download the ISO.") cachePath := cache.Lock(config.ISOUrl) defer cache.Unlock(config.ISOUrl) downloadConfig := &common.DownloadConfig{ Url: config.ISOUrl, TargetPath: cachePath, CopyFile: false, Hash: common.HashForType(config.ISOChecksumType), Checksum: checksum, } download := common.NewDownloadClient(downloadConfig) downloadCompleteCh := make(chan error, 1) go func() { ui.Say("Copying or downloading ISO. Progress will be reported periodically.") cachePath, err = download.Get() downloadCompleteCh <- err }() progressTicker := time.NewTicker(5 * time.Second) defer progressTicker.Stop() DownloadWaitLoop: for { select { case err := <-downloadCompleteCh: if err != nil { err := fmt.Errorf("Error downloading ISO: %s", err) state["error"] = err ui.Error(err.Error()) return multistep.ActionHalt } break DownloadWaitLoop case <-progressTicker.C: ui.Message(fmt.Sprintf("Download progress: %d%%", download.PercentProgress())) case <-time.After(1 * time.Second): if _, ok := state[multistep.StateCancelled]; ok { ui.Say("Interrupt received. Cancelling download...") return multistep.ActionHalt } } } // VirtualBox is really dumb and can't figure out that the file is an // ISO unless it has a ".iso" extension. We can't modify the cache // filenames so we just do a copy. tempdir, err := ioutil.TempDir("", "packer") if err != nil { state["error"] = fmt.Errorf("Error copying ISO: %s", err) return multistep.ActionHalt } s.isoCopyDir = tempdir f, err := os.Create(filepath.Join(tempdir, "image.iso")) if err != nil { state["error"] = fmt.Errorf("Error copying ISO: %s", err) return multistep.ActionHalt } defer f.Close() sourceF, err := os.Open(cachePath) if err != nil { state["error"] = fmt.Errorf("Error copying ISO: %s", err) return multistep.ActionHalt } defer sourceF.Close() log.Printf("Copying ISO to temp location: %s", tempdir) if _, err := io.Copy(f, sourceF); err != nil { state["error"] = fmt.Errorf("Error copying ISO: %s", err) return multistep.ActionHalt } log.Printf("Path to ISO on disk: %s", cachePath) state["iso_path"] = f.Name() return multistep.ActionContinue }
func (b *Builder) Prepare(raws ...interface{}) error { md, err := common.DecodeConfig(&b.config, raws...) if err != nil { return err } // Accumulate any errors errs := common.CheckUnusedConfig(md) if b.config.DiskSize == 0 { b.config.DiskSize = 40000 } if b.config.FloppyFiles == nil { b.config.FloppyFiles = make([]string, 0) } if b.config.GuestAdditionsPath == "" { b.config.GuestAdditionsPath = "VBoxGuestAdditions.iso" } if b.config.GuestOSType == "" { b.config.GuestOSType = "Other" } if b.config.HTTPPortMin == 0 { b.config.HTTPPortMin = 8000 } if b.config.HTTPPortMax == 0 { b.config.HTTPPortMax = 9000 } if b.config.OutputDir == "" { b.config.OutputDir = fmt.Sprintf("output-%s", b.config.PackerBuildName) } if b.config.RawBootWait == "" { b.config.RawBootWait = "10s" } if b.config.SSHHostPortMin == 0 { b.config.SSHHostPortMin = 2222 } if b.config.SSHHostPortMax == 0 { b.config.SSHHostPortMax = 4444 } if b.config.SSHPort == 0 { b.config.SSHPort = 22 } if b.config.VBoxManage == nil { b.config.VBoxManage = make([][]string, 0) } if b.config.VBoxVersionFile == "" { b.config.VBoxVersionFile = ".vbox_version" } if b.config.VMName == "" { b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName) } if b.config.HTTPPortMin > b.config.HTTPPortMax { errs = packer.MultiErrorAppend( errs, errors.New("http_port_min must be less than http_port_max")) } if b.config.ISOChecksum == "" { errs = packer.MultiErrorAppend( errs, errors.New("Due to large file sizes, an iso_checksum is required")) } else { b.config.ISOChecksum = strings.ToLower(b.config.ISOChecksum) } if b.config.ISOChecksumType == "" { errs = packer.MultiErrorAppend( errs, errors.New("The iso_checksum_type must be specified.")) } else { b.config.ISOChecksumType = strings.ToLower(b.config.ISOChecksumType) if h := common.HashForType(b.config.ISOChecksumType); h == nil { errs = packer.MultiErrorAppend( errs, fmt.Errorf("Unsupported checksum type: %s", b.config.ISOChecksumType)) } } if b.config.ISOUrl == "" { errs = packer.MultiErrorAppend( errs, errors.New("An iso_url must be specified.")) } else { url, err := url.Parse(b.config.ISOUrl) if err != nil { errs = packer.MultiErrorAppend( errs, fmt.Errorf("iso_url is not a valid URL: %s", err)) } else { if url.Scheme == "" { url.Scheme = "file" } if url.Scheme == "file" { if _, err := os.Stat(url.Path); err != nil { errs = packer.MultiErrorAppend( errs, fmt.Errorf("iso_url points to bad file: %s", err)) } } else { supportedSchemes := []string{"file", "http", "https"} scheme := strings.ToLower(url.Scheme) found := false for _, supported := range supportedSchemes { if scheme == supported { found = true break } } if !found { errs = packer.MultiErrorAppend( errs, fmt.Errorf("Unsupported URL scheme in iso_url: %s", scheme)) } } } if errs == nil || len(errs.Errors) == 0 { // Put the URL back together since we may have modified it b.config.ISOUrl = url.String() } } if b.config.GuestAdditionsSHA256 != "" { b.config.GuestAdditionsSHA256 = strings.ToLower(b.config.GuestAdditionsSHA256) } if b.config.GuestAdditionsURL != "" { url, err := url.Parse(b.config.GuestAdditionsURL) if err != nil { errs = packer.MultiErrorAppend( errs, fmt.Errorf("guest_additions_url is not a valid URL: %s", err)) } else { if url.Scheme == "" { url.Scheme = "file" } if url.Scheme == "file" { if _, err := os.Stat(url.Path); err != nil { errs = packer.MultiErrorAppend( errs, fmt.Errorf("guest_additions_url points to bad file: %s", err)) } } else { supportedSchemes := []string{"file", "http", "https"} scheme := strings.ToLower(url.Scheme) found := false for _, supported := range supportedSchemes { if scheme == supported { found = true break } } if !found { errs = packer.MultiErrorAppend( errs, fmt.Errorf("Unsupported URL scheme in guest_additions_url: %s", scheme)) } } } if errs == nil || len(errs.Errors) == 0 { // Put the URL back together since we may have modified it b.config.GuestAdditionsURL = url.String() } } if !b.config.PackerForce { if _, err := os.Stat(b.config.OutputDir); err == nil { errs = packer.MultiErrorAppend( errs, fmt.Errorf("Output directory '%s' already exists. It must not exist.", b.config.OutputDir)) } } b.config.bootWait, err = time.ParseDuration(b.config.RawBootWait) if err != nil { errs = packer.MultiErrorAppend( errs, fmt.Errorf("Failed parsing boot_wait: %s", err)) } if b.config.RawShutdownTimeout == "" { b.config.RawShutdownTimeout = "5m" } if b.config.RawSSHWaitTimeout == "" { b.config.RawSSHWaitTimeout = "20m" } b.config.shutdownTimeout, err = time.ParseDuration(b.config.RawShutdownTimeout) if err != nil { errs = packer.MultiErrorAppend( errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err)) } if b.config.SSHHostPortMin > b.config.SSHHostPortMax { errs = packer.MultiErrorAppend( errs, errors.New("ssh_host_port_min must be less than ssh_host_port_max")) } if b.config.SSHUser == "" { errs = packer.MultiErrorAppend( errs, errors.New("An ssh_username must be specified.")) } b.config.sshWaitTimeout, err = time.ParseDuration(b.config.RawSSHWaitTimeout) if err != nil { errs = packer.MultiErrorAppend( errs, fmt.Errorf("Failed parsing ssh_wait_timeout: %s", err)) } b.driver, err = b.newDriver() if err != nil { errs = packer.MultiErrorAppend( errs, fmt.Errorf("Failed creating VirtualBox driver: %s", err)) } if errs != nil && len(errs.Errors) > 0 { return errs } return nil }