builder/virtualbox: Use the common downloader

This commit is contained in:
Mitchell Hashimoto 2013-06-12 17:42:18 -07:00
parent a0a78b68e8
commit 52391bb1f3
1 changed files with 36 additions and 108 deletions

View File

@ -5,13 +5,9 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
"github.com/mitchellh/packer/builder/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"io"
"log" "log"
"net/http"
"net/url"
"os"
"strings"
"time" "time"
) )
@ -31,62 +27,52 @@ func (s stepDownloadISO) Run(state map[string]interface{}) multistep.StepAction
config := state["config"].(*config) config := state["config"].(*config)
ui := state["ui"].(packer.Ui) ui := state["ui"].(packer.Ui)
checksum, err := hex.DecodeString(config.ISOMD5)
if err != nil {
ui.Error(fmt.Sprintf("Error parsing checksum: %s", err))
return multistep.ActionHalt
}
log.Printf("Acquiring lock to download the ISO.") log.Printf("Acquiring lock to download the ISO.")
cachePath := cache.Lock(config.ISOUrl) cachePath := cache.Lock(config.ISOUrl)
defer cache.Unlock(config.ISOUrl) defer cache.Unlock(config.ISOUrl)
err := s.checkMD5(cachePath, config.ISOMD5) downloadConfig := &common.DownloadConfig{
haveFile := err == nil Url: config.ISOUrl,
if err != nil { TargetPath: cachePath,
if !os.IsNotExist(err) { CopyFile: false,
ui.Say(fmt.Sprintf("Error validating MD5 of ISO: %s", err)) Hash: md5.New(),
return multistep.ActionHalt Checksum: checksum,
}
} }
if !haveFile { download := common.NewDownloadClient(downloadConfig)
url, err := url.Parse(config.ISOUrl)
if err != nil {
ui.Error(fmt.Sprintf("Error parsing iso_url: %s", err))
return multistep.ActionHalt
}
// Start the download in a goroutine so that we cancel it and such. downloadCompleteCh := make(chan error, 1)
var progress uint go func() {
downloadComplete := make(chan bool, 1) ui.Say("Copying or downloading ISO. Progress will be reported periodically.")
go func() { cachePath, err = download.Get()
ui.Say("Copying or downloading ISO. Progress will be shown periodically.") downloadCompleteCh <- err
cachePath, err = s.downloadUrl(cachePath, url, &progress) }()
downloadComplete <- true
}()
progressTimer := time.NewTicker(15 * time.Second) progressTicker := time.NewTicker(5 * time.Second)
defer progressTimer.Stop() defer progressTicker.Stop()
DownloadWaitLoop: DownloadWaitLoop:
for { for {
select { select {
case <-downloadComplete: case err := <-downloadCompleteCh:
log.Println("Download of ISO completed.") if err != nil {
break DownloadWaitLoop ui.Error(fmt.Sprintf("Error downloading ISO: %s", err))
case <-progressTimer.C:
ui.Say(fmt.Sprintf("Download progress: %d%%", progress))
case <-time.After(1 * time.Second):
if _, ok := state[multistep.StateCancelled]; ok {
ui.Say("Interrupt received. Cancelling download...")
return multistep.ActionHalt
}
} }
}
if err != nil { break DownloadWaitLoop
ui.Error(fmt.Sprintf("Error downloading ISO: %s", err)) case <-progressTicker.C:
return multistep.ActionHalt ui.Say(fmt.Sprintf("Download progress: %d%%", download.PercentProgress()))
} case <-time.After(1 * time.Second):
if _, ok := state[multistep.StateCancelled]; ok {
if err = s.checkMD5(cachePath, config.ISOMD5); err != nil { ui.Say("Interrupt received. Cancelling download...")
ui.Say(fmt.Sprintf("Error validating MD5 of ISO: %s", err)) return multistep.ActionHalt
return multistep.ActionHalt }
} }
} }
@ -97,61 +83,3 @@ func (s stepDownloadISO) Run(state map[string]interface{}) multistep.StepAction
} }
func (stepDownloadISO) Cleanup(map[string]interface{}) {} func (stepDownloadISO) Cleanup(map[string]interface{}) {}
func (stepDownloadISO) checkMD5(path string, expected string) error {
f, err := os.Open(path)
if err != nil {
return err
}
hash := md5.New()
io.Copy(hash, f)
result := strings.ToLower(hex.EncodeToString(hash.Sum(nil)))
if result != expected {
return fmt.Errorf("result != expected: %s != %s", result, expected)
}
return nil
}
func (stepDownloadISO) downloadUrl(path string, url *url.URL, progress *uint) (string, error) {
if url.Scheme == "file" {
// If it is just a file URL, then we already have the ISO
return url.Path, nil
}
// Otherwise, it is an HTTP URL, and we must download it.
f, err := os.Create(path)
if err != nil {
return "", err
}
defer f.Close()
log.Printf("Beginning download of ISO: %s", url.String())
resp, err := http.Get(url.String())
if err != nil {
return "", err
}
var buffer [4096]byte
var totalRead int64
for {
n, err := resp.Body.Read(buffer[:])
if err != nil && err != io.EOF {
return "", err
}
totalRead += int64(n)
*progress = uint((float64(totalRead) / float64(resp.ContentLength)) * 100)
if _, werr := f.Write(buffer[:n]); werr != nil {
return "", werr
}
if err == io.EOF {
break
}
}
return path, nil
}