common: Add new StepDownload to DRY up downloads
This commit is contained in:
parent
245b569c9b
commit
bb352e5011
|
@ -295,7 +295,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
|
||||
steps := []multistep.Step{
|
||||
new(stepDownloadGuestAdditions),
|
||||
new(stepDownloadISO),
|
||||
&common.StepDownload{
|
||||
Checksum: b.config.ISOChecksum,
|
||||
ChecksumType: b.config.ISOChecksumType,
|
||||
Description: "ISO",
|
||||
ResultKey: "iso_path",
|
||||
Url: []string{b.config.ISOUrl},
|
||||
},
|
||||
new(stepPrepareOutputDir),
|
||||
&common.StepCreateFloppy{
|
||||
Files: b.config.FloppyFiles,
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
package virtualbox
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This step downloads the ISO specified.
|
||||
//
|
||||
// Uses:
|
||||
// cache packer.Cache
|
||||
// config *config
|
||||
// ui packer.Ui
|
||||
//
|
||||
// Produces:
|
||||
// iso_path string
|
||||
type stepDownloadISO struct{}
|
||||
|
||||
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:
|
||||
progress := download.PercentProgress()
|
||||
if progress >= 0 {
|
||||
ui.Message(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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Path to ISO on disk: %s", cachePath)
|
||||
state["iso_path"] = cachePath
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (stepDownloadISO) Cleanup(map[string]interface{}) {}
|
|
@ -0,0 +1,126 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// StepDownload downloads a remote file using the download client within
|
||||
// this package. This step handles setting up the download configuration,
|
||||
// progress reporting, interrupt handling, etc.
|
||||
//
|
||||
// Uses:
|
||||
// cache packer.Cache
|
||||
// ui packer.Ui
|
||||
type StepDownload struct {
|
||||
// The checksum and the type of the checksum for the download
|
||||
Checksum string
|
||||
ChecksumType string
|
||||
|
||||
// A short description of the type of download being done. Example:
|
||||
// "ISO" or "Guest Additions"
|
||||
Description string
|
||||
|
||||
// The name of the key where the final path of the ISO will be put
|
||||
// into the state.
|
||||
ResultKey string
|
||||
|
||||
// A list of URLs to attempt to download this thing.
|
||||
Url []string
|
||||
}
|
||||
|
||||
func (s *StepDownload) Run(state map[string]interface{}) multistep.StepAction {
|
||||
cache := state["cache"].(packer.Cache)
|
||||
ui := state["ui"].(packer.Ui)
|
||||
|
||||
checksum, err := hex.DecodeString(s.Checksum)
|
||||
if err != nil {
|
||||
state["error"] = fmt.Errorf("Error parsing checksum: %s", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Downloading or copying %s", s.Description))
|
||||
|
||||
var finalPath string
|
||||
for _, url := range s.Url {
|
||||
ui.Message(fmt.Sprintf("Downloading or copying: %s", url))
|
||||
log.Printf("Acquiring lock to download: %s", url)
|
||||
cachePath := cache.Lock(url)
|
||||
defer cache.Unlock(url)
|
||||
|
||||
config := &DownloadConfig{
|
||||
Url: url,
|
||||
TargetPath: cachePath,
|
||||
CopyFile: false,
|
||||
Hash: HashForType(s.ChecksumType),
|
||||
Checksum: checksum,
|
||||
}
|
||||
|
||||
path, err, retry := s.download(config, state)
|
||||
if err != nil {
|
||||
ui.Message(fmt.Sprintf("Error downloading: %s", err))
|
||||
}
|
||||
|
||||
if !retry {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
finalPath = path
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if finalPath == "" {
|
||||
err := fmt.Errorf("%s download failed.", s.Description)
|
||||
state["error"] = err
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state[s.ResultKey] = finalPath
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepDownload) Cleanup(map[string]interface{}) {}
|
||||
|
||||
func (s *StepDownload) download(config *DownloadConfig, state map[string]interface{}) (string, error, bool) {
|
||||
var path string
|
||||
ui := state["ui"].(packer.Ui)
|
||||
download := NewDownloadClient(config)
|
||||
|
||||
downloadCompleteCh := make(chan error, 1)
|
||||
go func() {
|
||||
var err error
|
||||
path, err = download.Get()
|
||||
downloadCompleteCh <- err
|
||||
}()
|
||||
|
||||
progressTicker := time.NewTicker(5 * time.Second)
|
||||
defer progressTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-downloadCompleteCh:
|
||||
if err != nil {
|
||||
return "", err, true
|
||||
}
|
||||
|
||||
return path, nil, true
|
||||
case <-progressTicker.C:
|
||||
progress := download.PercentProgress()
|
||||
if progress >= 0 {
|
||||
ui.Message(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 "", nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStepDownload_Impl(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = new(StepDownload)
|
||||
if _, ok := raw.(multistep.Step); !ok {
|
||||
t.Fatalf("download should be a step")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue