builder/vmware: Validate MD5
This commit is contained in:
parent
4b46181f69
commit
520a2706ea
|
@ -49,10 +49,9 @@ type config struct {
|
||||||
RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"`
|
RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) Prepare(raw interface{}) (err error) {
|
func (b *Builder) Prepare(raw interface{}) error {
|
||||||
err = mapstructure.Decode(raw, &b.config)
|
if err := mapstructure.Decode(raw, &b.config); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.config.DiskName == "" {
|
if b.config.DiskName == "" {
|
||||||
|
@ -88,6 +87,7 @@ func (b *Builder) Prepare(raw interface{}) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accumulate any errors
|
// Accumulate any errors
|
||||||
|
var err error
|
||||||
errs := make([]error, 0)
|
errs := make([]error, 0)
|
||||||
|
|
||||||
if b.config.HTTPPortMin > b.config.HTTPPortMax {
|
if b.config.HTTPPortMin > b.config.HTTPPortMax {
|
||||||
|
@ -96,6 +96,8 @@ func (b *Builder) Prepare(raw interface{}) (err error) {
|
||||||
|
|
||||||
if b.config.ISOMD5 == "" {
|
if b.config.ISOMD5 == "" {
|
||||||
errs = append(errs, errors.New("Due to large file sizes, an iso_md5 is required"))
|
errs = append(errs, errors.New("Due to large file sizes, an iso_md5 is required"))
|
||||||
|
} else {
|
||||||
|
b.config.ISOMD5 = strings.ToLower(b.config.ISOMD5)
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.config.ISOUrl == "" {
|
if b.config.ISOUrl == "" {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
func testConfig() map[string]interface{} {
|
func testConfig() map[string]interface{} {
|
||||||
return map[string]interface{}{
|
return map[string]interface{}{
|
||||||
"iso_md5": "foo",
|
"iso_md5": "foo",
|
||||||
"iso_url": "http://www.packer.io",
|
"iso_url": "http://www.packer.io",
|
||||||
"ssh_username": "foo",
|
"ssh_username": "foo",
|
||||||
}
|
}
|
||||||
|
@ -108,11 +108,15 @@ func TestBuilderPrepare_ISOMD5(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test good
|
// Test good
|
||||||
config["iso_md5"] = "foo"
|
config["iso_md5"] = "FOo"
|
||||||
err = b.Prepare(config)
|
err = b.Prepare(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("should not have error: %s", err)
|
t.Fatalf("should not have error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.config.ISOMD5 != "foo" {
|
||||||
|
t.Fatalf("should've lowercased: %s", b.config.ISOMD5)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuilderPrepare_ISOUrl(t *testing.T) {
|
func TestBuilderPrepare_ISOUrl(t *testing.T) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package vmware
|
package vmware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/md5"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
|
@ -9,6 +10,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,43 +34,59 @@ func (s stepDownloadISO) Run(state map[string]interface{}) multistep.StepAction
|
||||||
cachePath := cache.Lock(config.ISOUrl)
|
cachePath := cache.Lock(config.ISOUrl)
|
||||||
defer cache.Unlock(config.ISOUrl)
|
defer cache.Unlock(config.ISOUrl)
|
||||||
|
|
||||||
url, err := url.Parse(config.ISOUrl)
|
err := s.checkMD5(cachePath, config.ISOMD5)
|
||||||
|
haveFile := err == nil
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ui.Error(fmt.Sprintf("Error parsing iso_url: %s", err))
|
if !os.IsNotExist(err) {
|
||||||
return multistep.ActionHalt
|
ui.Say(fmt.Sprintf("Error validating MD5 of ISO: %s", err))
|
||||||
}
|
return multistep.ActionHalt
|
||||||
|
|
||||||
// Start the download in a goroutine so that we cancel it and such.
|
|
||||||
var progress uint
|
|
||||||
downloadComplete := make(chan bool, 1)
|
|
||||||
go func() {
|
|
||||||
ui.Say("Copying or downloading ISO. Progress will be shown periodically.")
|
|
||||||
cachePath, err = s.downloadUrl(cachePath, url, &progress)
|
|
||||||
downloadComplete <- true
|
|
||||||
}()
|
|
||||||
|
|
||||||
progressTimer := time.NewTicker(15 * time.Second)
|
|
||||||
defer progressTimer.Stop()
|
|
||||||
|
|
||||||
DownloadWaitLoop:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-downloadComplete:
|
|
||||||
log.Println("Download of ISO completed.")
|
|
||||||
break DownloadWaitLoop
|
|
||||||
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 {
|
if !haveFile {
|
||||||
ui.Error(fmt.Sprintf("Error downloading ISO: %s", err))
|
url, err := url.Parse(config.ISOUrl)
|
||||||
return multistep.ActionHalt
|
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.
|
||||||
|
var progress uint
|
||||||
|
downloadComplete := make(chan bool, 1)
|
||||||
|
go func() {
|
||||||
|
ui.Say("Copying or downloading ISO. Progress will be shown periodically.")
|
||||||
|
cachePath, err = s.downloadUrl(cachePath, url, &progress)
|
||||||
|
downloadComplete <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
progressTimer := time.NewTicker(15 * time.Second)
|
||||||
|
defer progressTimer.Stop()
|
||||||
|
|
||||||
|
DownloadWaitLoop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-downloadComplete:
|
||||||
|
log.Println("Download of ISO completed.")
|
||||||
|
break DownloadWaitLoop
|
||||||
|
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 {
|
||||||
|
ui.Error(fmt.Sprintf("Error downloading ISO: %s", err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = s.checkMD5(cachePath, config.ISOMD5); err != nil {
|
||||||
|
ui.Say(fmt.Sprintf("Error validating MD5 of ISO: %s", err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Path to ISO on disk: %s", cachePath)
|
log.Printf("Path to ISO on disk: %s", cachePath)
|
||||||
|
@ -79,6 +97,22 @@ DownloadWaitLoop:
|
||||||
|
|
||||||
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(string(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) {
|
func (stepDownloadISO) downloadUrl(path string, url *url.URL, progress *uint) (string, error) {
|
||||||
if url.Scheme == "file" {
|
if url.Scheme == "file" {
|
||||||
// If it is just a file URL, then we already have the ISO
|
// If it is just a file URL, then we already have the ISO
|
||||||
|
|
Loading…
Reference in New Issue