Split up DownloadableURL() into it's individual components: SupportedURL(), DownloadableURL(), and ValidatedURL(). Updated all instances of DownloadableURL() to point to ValidatedURL(). Reverted the tests that are based on un-supported protocols.
This commit is contained in:
parent
3cf448f6ec
commit
c17f827e1d
|
@ -121,7 +121,7 @@ func (s *StepDownloadGuestAdditions) Run(state multistep.StateBag) multistep.Ste
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the file/url to an actual URL for step_download to process.
|
// Convert the file/url to an actual URL for step_download to process.
|
||||||
url, err = common.DownloadableURL(url)
|
url, err = common.ValidatedURL(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error preparing guest additions url: %s", err)
|
err := fmt.Errorf("Error preparing guest additions url: %s", err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
|
|
|
@ -97,7 +97,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
||||||
if c.SourcePath == "" {
|
if c.SourcePath == "" {
|
||||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required"))
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required"))
|
||||||
} else {
|
} else {
|
||||||
c.SourcePath, err = common.DownloadableURL(c.SourcePath)
|
c.SourcePath, err = common.ValidatedURL(c.SourcePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is invalid: %s", err))
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is invalid: %s", err))
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,17 @@ func TestNewConfig_sourcePath(t *testing.T) {
|
||||||
t.Fatalf("Nonexistant file should throw a validation error!")
|
t.Fatalf("Nonexistant file should throw a validation error!")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bad
|
||||||
|
c = testConfig(t)
|
||||||
|
c["source_path"] = "ftp://i/dont/exist"
|
||||||
|
_, warns, err = NewConfig(c)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("should error")
|
||||||
|
}
|
||||||
|
|
||||||
// Good
|
// Good
|
||||||
tf := getTempFile(t)
|
tf := getTempFile(t)
|
||||||
defer os.Remove(tf.Name())
|
defer os.Remove(tf.Name())
|
||||||
|
|
111
common/config.go
111
common/config.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -42,58 +43,98 @@ func ChooseString(vals ...string) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SupportedURL verifies that the url passed is actually supported or not
|
||||||
|
// This will also validate that the protocol is one that's actually implemented.
|
||||||
|
func SupportedURL(u *url.URL) bool {
|
||||||
|
// url.Parse shouldn't return nil except on error....but it can.
|
||||||
|
if u == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// build a dummy NewDownloadClient since this is the only place that valid
|
||||||
|
// protocols are actually exposed.
|
||||||
|
cli := NewDownloadClient(&DownloadConfig{})
|
||||||
|
|
||||||
|
// Iterate through each downloader to see if a protocol was found.
|
||||||
|
ok := false
|
||||||
|
for scheme, _ := range cli.config.DownloaderMap {
|
||||||
|
if strings.ToLower(u.Scheme) == strings.ToLower(scheme) {
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
// DownloadableURL processes a URL that may also be a file path and returns
|
// DownloadableURL processes a URL that may also be a file path and returns
|
||||||
// a completely valid URL. For example, the original URL might be "local/file.iso"
|
// a completely valid URL representing the requested file. For example,
|
||||||
// which isn't a valid URL. DownloadableURL will return "file:///local/file.iso"
|
// the original URL might be "local/file.iso" which isn't a valid URL,
|
||||||
|
// and so DownloadableURL will return "file://local/file.iso"
|
||||||
|
// No other transformations are done to the path.
|
||||||
func DownloadableURL(original string) (string, error) {
|
func DownloadableURL(original string) (string, error) {
|
||||||
|
var result string
|
||||||
|
|
||||||
// Verify that the scheme is something we support in our common downloader.
|
// Fix the url if it's using bad characters commonly mistaken with a path.
|
||||||
supported := []string{"file", "http", "https", "smb"}
|
original = filepath.ToSlash(original)
|
||||||
found := false
|
|
||||||
for _, s := range supported {
|
// Check to see that this is a parseable URL with a scheme. If so, then just pass it through.
|
||||||
if strings.HasPrefix(strings.ToLower(original), s+"://") {
|
if u, err := url.Parse(original); err == nil && u.Scheme != "" && u.Host != "" {
|
||||||
found = true
|
return filepath.ToSlash(original), nil
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's properly prefixed with something we support, then we don't need
|
// Since it's not a url, this might be a path. So, check that the file exists,
|
||||||
// to make it a uri.
|
// then make it an absolute path so we can make a proper uri.
|
||||||
if found {
|
if _, err := os.Stat(original); err == nil {
|
||||||
original = filepath.ToSlash(original)
|
result, err = filepath.Abs(filepath.FromSlash(original))
|
||||||
|
|
||||||
// make sure that it can be parsed though..
|
|
||||||
uri, err := url.Parse(original)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
uri.Scheme = strings.ToLower(uri.Scheme)
|
result, err = filepath.EvalSymlinks(result)
|
||||||
|
|
||||||
return uri.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the file exists, then make it an absolute path
|
|
||||||
_, err := os.Stat(original)
|
|
||||||
if err == nil {
|
|
||||||
original, err = filepath.Abs(filepath.FromSlash(original))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
original, err = filepath.EvalSymlinks(original)
|
result = filepath.Clean(result)
|
||||||
if err != nil {
|
result = filepath.ToSlash(result)
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
original = filepath.Clean(original)
|
// We have no idea what this might be, so we'll leave it as is.
|
||||||
original = filepath.ToSlash(original)
|
} else {
|
||||||
|
result = filepath.ToSlash(original)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since it wasn't properly prefixed, let's make it into a well-formed
|
// We should have a path that can just turn into a file:// scheme'd url.
|
||||||
// file:// uri.
|
return fmt.Sprintf("file://%s", result), nil
|
||||||
|
}
|
||||||
|
|
||||||
return "file://" + original, nil
|
// Force the parameter into a url. This will transform the parameter into
|
||||||
|
// a proper url, removing slashes, adding the proper prefix, etc.
|
||||||
|
func ValidatedURL(original string) (string, error) {
|
||||||
|
|
||||||
|
// See if the user failed to give a url
|
||||||
|
if ok, _ := regexp.MatchString("(?m)^[^[:punct:]]+://", original); !ok {
|
||||||
|
|
||||||
|
// So since no magic was found, this must be a path.
|
||||||
|
result, err := DownloadableURL(original)
|
||||||
|
if err == nil {
|
||||||
|
return ValidatedURL(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the url is parseable...just in case.
|
||||||
|
u, err := url.Parse(original)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should now have a url, so verify that it's a protocol we support.
|
||||||
|
if !SupportedURL(u) {
|
||||||
|
return "", fmt.Errorf("Unsupported protocol scheme! (%#v)", u)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should now have a properly formatted and supported url
|
||||||
|
return u.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileExistsLocally takes the URL output from DownloadableURL, and determines
|
// FileExistsLocally takes the URL output from DownloadableURL, and determines
|
||||||
|
|
|
@ -36,15 +36,21 @@ func TestChooseString(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDownloadableURL(t *testing.T) {
|
func TestValidatedURL(t *testing.T) {
|
||||||
// Invalid URL: has hex code in host
|
// Invalid URL: has hex code in host
|
||||||
_, err := DownloadableURL("http://what%20.com")
|
_, err := ValidatedURL("http://what%20.com")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected err : %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid: unsupported scheme
|
||||||
|
_, err = ValidatedURL("ftp://host.com/path")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected err : %s", err)
|
t.Fatalf("expected err : %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid: http
|
// Valid: http
|
||||||
u, err := DownloadableURL("HTTP://packer.io/path")
|
u, err := ValidatedURL("HTTP://packer.io/path")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -69,7 +75,7 @@ func TestDownloadableURL(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
u, err := DownloadableURL(tc.InputString)
|
u, err := ValidatedURL(tc.InputString)
|
||||||
if u != tc.OutputURL {
|
if u != tc.OutputURL {
|
||||||
t.Fatal(fmt.Sprintf("Error with URL %s: got %s but expected %s",
|
t.Fatal(fmt.Sprintf("Error with URL %s: got %s but expected %s",
|
||||||
tc.InputString, tc.OutputURL, u))
|
tc.InputString, tc.OutputURL, u))
|
||||||
|
@ -77,11 +83,11 @@ func TestDownloadableURL(t *testing.T) {
|
||||||
if (err != nil) != tc.ErrExpected {
|
if (err != nil) != tc.ErrExpected {
|
||||||
if tc.ErrExpected == true {
|
if tc.ErrExpected == true {
|
||||||
t.Fatal(fmt.Sprintf("Error with URL %s: we expected "+
|
t.Fatal(fmt.Sprintf("Error with URL %s: we expected "+
|
||||||
"DownloadableURL to return an error but didn't get one.",
|
"ValidatedURL to return an error but didn't get one.",
|
||||||
tc.InputString))
|
tc.InputString))
|
||||||
} else {
|
} else {
|
||||||
t.Fatal(fmt.Sprintf("Error with URL %s: we did not expect an "+
|
t.Fatal(fmt.Sprintf("Error with URL %s: we did not expect an "+
|
||||||
" error from DownloadableURL but we got: %s",
|
" error from ValidatedURL but we got: %s",
|
||||||
tc.InputString, err))
|
tc.InputString, err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,8 +86,6 @@ func NewDownloadClient(c *DownloadConfig) *DownloadClient {
|
||||||
|
|
||||||
// Create downloader map if it hasn't been specified already.
|
// Create downloader map if it hasn't been specified already.
|
||||||
if c.DownloaderMap == nil {
|
if c.DownloaderMap == nil {
|
||||||
// XXX: Make sure you add any new protocols you implement
|
|
||||||
// to the DownloadableURL implementation in config.go!
|
|
||||||
c.DownloaderMap = map[string]Downloader{
|
c.DownloaderMap = map[string]Downloader{
|
||||||
"file": &FileDownloader{bufferSize: nil},
|
"file": &FileDownloader{bufferSize: nil},
|
||||||
"http": &HTTPDownloader{userAgent: c.UserAgent},
|
"http": &HTTPDownloader{userAgent: c.UserAgent},
|
||||||
|
|
|
@ -111,7 +111,7 @@ func (c *ISOConfig) Prepare(ctx *interpolate.Context) (warnings []string, errs [
|
||||||
c.ISOChecksum = strings.ToLower(c.ISOChecksum)
|
c.ISOChecksum = strings.ToLower(c.ISOChecksum)
|
||||||
|
|
||||||
for i, url := range c.ISOUrls {
|
for i, url := range c.ISOUrls {
|
||||||
url, err := DownloadableURL(url)
|
url, err := ValidatedURL(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(
|
errs = append(
|
||||||
errs, fmt.Errorf("Failed to parse iso_url %d: %s", i+1, err))
|
errs, fmt.Errorf("Failed to parse iso_url %d: %s", i+1, err))
|
||||||
|
|
Loading…
Reference in New Issue