From 8db5390553e5d7c41670c09fbea893f62f367d10 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 28 Jul 2013 23:51:21 -0700 Subject: [PATCH] builder/common: add DownloadableURL to remove duplication parsing URLs --- builder/common/config.go | 49 +++++++++++++++++++++ builder/common/config_test.go | 81 +++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/builder/common/config.go b/builder/common/config.go index c69031779..16df8c257 100644 --- a/builder/common/config.go +++ b/builder/common/config.go @@ -4,6 +4,8 @@ import ( "fmt" "github.com/mitchellh/mapstructure" "github.com/mitchellh/packer/packer" + "net/url" + "os" "sort" "strings" ) @@ -55,3 +57,50 @@ func DecodeConfig(target interface{}, raws ...interface{}) (*mapstructure.Metada return &md, nil } + +// 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" +// which isn't a valid URL. DownloadableURL will return "file:///local/file.iso" +func DownloadableURL(original string) (string, error) { + url, err := url.Parse(original) + if err != nil { + return "", err + } + + if url.Scheme == "" { + url.Scheme = "file" + } + + if url.Scheme == "file" { + if _, err := os.Stat(url.Path); err != nil { + return "", err + } + } + + // Make sure it is lowercased + url.Scheme = strings.ToLower(url.Scheme) + + // This is to work around issue #5927. This can safely be removed once + // we distribute with a version of Go that fixes that bug. + // + // See: https://code.google.com/p/go/issues/detail?id=5927 + if url.Path[0] != '/' { + url.Path = "/" + url.Path + } + + // Verify that the scheme is something we support in our common downloader. + supported := []string{"file", "http", "https"} + found := false + for _, s := range supported { + if url.Scheme == s { + found = true + break + } + } + + if !found { + return "", fmt.Errorf("Unsupported URL scheme: %s", url.Scheme) + } + + return url.String(), nil +} diff --git a/builder/common/config_test.go b/builder/common/config_test.go index 1f40c9af3..8a6517250 100644 --- a/builder/common/config_test.go +++ b/builder/common/config_test.go @@ -1,7 +1,11 @@ package common import ( + "fmt" "github.com/mitchellh/mapstructure" + "io/ioutil" + "os" + "path/filepath" "reflect" "testing" ) @@ -61,3 +65,80 @@ func TestDecodeConfig(t *testing.T) { t.Fatalf("unused: %#v", md.Unused) } } + +func TestDownloadableURL(t *testing.T) { + // Invalid URL: has hex code in host + _, err := DownloadableURL("http://what%20.com") + if err == nil { + t.Fatal("expected err") + } + + // Invalid: unsupported scheme + _, err = DownloadableURL("ftp://host.com/path") + if err == nil { + t.Fatal("expected err") + } + + // Valid: http + u, err := DownloadableURL("HTTP://packer.io/path") + if err != nil { + t.Fatalf("err: %s", err) + } + + if u != "http://packer.io/path" { + t.Fatalf("bad: %s", u) + } +} + +func TestDownloadableURL_FilePaths(t *testing.T) { + tf, err := ioutil.TempFile("", "packer") + if err != nil { + t.Fatalf("tempfile err: %s", err) + } + defer os.Remove(tf.Name()) + tf.Close() + + // Relative filepath. We run this test in a func so that + // the defers run right away. + func() { + wd, err := os.Getwd() + if err != nil { + t.Fatalf("getwd err: %s", err) + } + + err = os.Chdir(filepath.Dir(tf.Name())) + if err != nil { + t.Fatalf("chdir err: %s", err) + } + defer os.Chdir(wd) + + filename := filepath.Base(tf.Name()) + u, err := DownloadableURL(filename) + if err != nil { + t.Fatalf("err: %s", err) + } + + if u != fmt.Sprintf("file:///%s", filename) { + t.Fatalf("unexpected: %s", u) + } + }() + + // Test some cases with and without a schema prefix + for _, prefix := range []string{"", "file://"} { + // Nonexistent file + _, err = DownloadableURL(prefix + "i/dont/exist") + if err == nil { + t.Fatal("expected err") + } + + // Good file + u, err := DownloadableURL(prefix + tf.Name()) + if err != nil { + t.Fatalf("err: %s", err) + } + + if u != fmt.Sprintf("file://%s", tf.Name()) { + t.Fatalf("unexpected: %s", u) + } + } +}