diff --git a/CHANGELOG.md b/CHANGELOG.md index 95b4a7949..cb71b7d81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ IMPROVEMENTS: BUG FIXES: * core: Errors are properly shown when adding bad floppy files. [GH-1043] + * core: Fix some URL parsing issues on Windows. * builder/amazon-instance: Use S3Endpoint for ec2-upload-bundle arg, which works for every region. [GH-904] * builder/digitalocean: updated default image_id [GH-1032] diff --git a/common/config.go b/common/config.go index dab95d6a9..eaf9caa02 100644 --- a/common/config.go +++ b/common/config.go @@ -101,15 +101,20 @@ func DownloadableURL(original string) (string, error) { } if url.Scheme == "file" { - // For Windows absolute file paths, remove leading / prior to processing - // since net/url turns "C:/" into "/C:/" - if runtime.GOOS == "windows" && url.Path[0] == '/' { - url.Path = url.Path[1:len(url.Path)] + // Windows file handling is all sorts of tricky... + if runtime.GOOS == "windows" { + // If the path is using Windows-style slashes, URL parses + // it into the host field. + if url.Path == "" && strings.Contains(url.Host, `\`) { + url.Path = url.Host + url.Host = "" + } - // Also replace all backslashes with forwardslashes since Windows - // users are likely to do this but the URL should actually only - // contain forward slashes. - url.Path = strings.Replace(url.Path, `\`, `/`, -1) + // For Windows absolute file paths, remove leading / prior to processing + // since net/url turns "C:/" into "/C:/" + if len(url.Path) > 0 && url.Path[0] == '/' { + url.Path = url.Path[1:len(url.Path)] + } } // Only do the filepath transformations if the file appears @@ -127,6 +132,13 @@ func DownloadableURL(original string) (string, error) { url.Path = filepath.Clean(url.Path) } + + if runtime.GOOS == "windows" { + // Also replace all backslashes with forwardslashes since Windows + // users are likely to do this but the URL should actually only + // contain forward slashes. + url.Path = strings.Replace(url.Path, `\`, `/`, -1) + } } // Make sure it is lowercased @@ -195,11 +207,13 @@ func decodeConfigHook(raws []interface{}) (mapstructure.DecodeHookFunc, error) { }, nil } -func CoalesceVals(vals ...string) string { +// ChooseString returns the first non-empty value. +func ChooseString(vals ...string) string { for _, el := range vals { if el != "" { return el } } + return "" } diff --git a/common/config_test.go b/common/config_test.go index 783c95fb2..e7cfe0627 100644 --- a/common/config_test.go +++ b/common/config_test.go @@ -7,6 +7,8 @@ import ( "os" "path/filepath" "reflect" + "runtime" + "strings" "testing" ) @@ -141,6 +143,11 @@ func TestDownloadableURL_FilePaths(t *testing.T) { tfPath = filepath.Clean(tfPath) + filePrefix := "file://" + if runtime.GOOS == "windows" { + filePrefix += "/" + } + // Relative filepath. We run this test in a func so that // the defers run right away. func() { @@ -161,8 +168,11 @@ func TestDownloadableURL_FilePaths(t *testing.T) { t.Fatalf("err: %s", err) } - if u != fmt.Sprintf("file://%s", tfPath) { - t.Fatalf("unexpected: %s", u) + expected := fmt.Sprintf("%s%s", + filePrefix, + strings.Replace(tfPath, `\`, `/`, -1)) + if u != expected { + t.Fatalf("unexpected: %#v != %#v", u, expected) } }() @@ -180,8 +190,11 @@ func TestDownloadableURL_FilePaths(t *testing.T) { t.Fatalf("err: %s", err) } - if u != fmt.Sprintf("file://%s", tfPath) { - t.Fatalf("unexpected: %s", u) + expected := fmt.Sprintf("%s%s", + filePrefix, + strings.Replace(tfPath, `\`, `/`, -1)) + if u != expected { + t.Fatalf("unexpected: %s != %s", u, expected) } } }