Added proper support for downloading via a Windows UNC path or a relative uri.
Added proper support for validating a downloadableURL containing a UNC or relative uri. Removed the workaround for an earlier Go issue that had remained dormant in common/download.go (issue #5927). When building a .vmx file via the vmware-iso builder, transform the path to the correct os-formatted one (using filepath.FromSlash).
This commit is contained in:
parent
e222d60b5a
commit
281dd1258a
|
@ -43,6 +43,11 @@ func (s *stepCreateVMX) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
isoPath := state.Get("iso_path").(string)
|
isoPath := state.Get("iso_path").(string)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
// Convert the iso_path into a path relative to the .vmx file if possible
|
||||||
|
if relativeIsoPath,err := filepath.Rel(config.VMXTemplatePath, filepath.FromSlash(isoPath)); err == nil {
|
||||||
|
isoPath = relativeIsoPath
|
||||||
|
}
|
||||||
|
|
||||||
ui.Say("Building and writing VMX file")
|
ui.Say("Building and writing VMX file")
|
||||||
|
|
||||||
vmxTemplate := DefaultVMXTemplate
|
vmxTemplate := DefaultVMXTemplate
|
||||||
|
|
|
@ -56,77 +56,109 @@ func DownloadableURL(original string) (string, error) {
|
||||||
// for more info about valid windows URIs
|
// for more info about valid windows URIs
|
||||||
idx := strings.Index(original, ":")
|
idx := strings.Index(original, ":")
|
||||||
if idx == 1 {
|
if idx == 1 {
|
||||||
original = "file:///" + original
|
original = "file://" + filepath.ToSlash(original)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u, err := url.Parse(original)
|
|
||||||
|
// XXX: The validation here is later re-parsed in common/download.go and
|
||||||
|
// thus any modifications here must remain consistent over there too.
|
||||||
|
uri, err := url.Parse(original)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Scheme == "" {
|
if uri.Scheme == "" {
|
||||||
u.Scheme = "file"
|
uri.Scheme = "file"
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Scheme == "file" {
|
const UNCPrefix = string(os.PathSeparator)+string(os.PathSeparator)
|
||||||
// Windows file handling is all sorts of tricky...
|
if uri.Scheme == "file" {
|
||||||
|
var ospath string // os-formatted pathname
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
// If the path is using Windows-style slashes, URL parses
|
// Move any extra path components that were mis-parsed into the Host
|
||||||
// it into the host field.
|
// field back into the uri.Path field
|
||||||
if u.Path == "" && strings.Contains(u.Host, `\`) {
|
if len(uri.Host) >= len(UNCPrefix) && uri.Host[:len(UNCPrefix)] == UNCPrefix {
|
||||||
u.Path = u.Host
|
idx := strings.Index(uri.Host[len(UNCPrefix):], string(os.PathSeparator))
|
||||||
u.Host = ""
|
if idx > -1 {
|
||||||
|
uri.Path = filepath.ToSlash(uri.Host[idx+len(UNCPrefix):]) + uri.Path
|
||||||
|
uri.Host = uri.Host[:idx+len(UNCPrefix)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Now all we need to do to convert the uri to a platform-specific path
|
||||||
|
// is to trade it's slashes for some os.PathSeparator ones.
|
||||||
|
ospath = uri.Host + filepath.FromSlash(uri.Path)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Since we're already using sane paths on a sane platform, anything in
|
||||||
|
// uri.Host can be assumed that the user is describing a relative uri.
|
||||||
|
// This means that if we concatenate it with uri.Path, the filepath
|
||||||
|
// transform will still open the file correctly.
|
||||||
|
// i.e. file://localdirectory/filename -> localdirectory/filename
|
||||||
|
ospath = uri.Host + uri.Path
|
||||||
|
}
|
||||||
// Only do the filepath transformations if the file appears
|
// Only do the filepath transformations if the file appears
|
||||||
// to actually exist.
|
// to actually exist. We don't do it on windows, because EvalSymlinks
|
||||||
if _, err := os.Stat(u.Path); err == nil {
|
// won't understand how to handle UNC paths and other Windows-specific minutae.
|
||||||
u.Path, err = filepath.Abs(u.Path)
|
if _, err := os.Stat(ospath); err == nil && runtime.GOOS != "windows" {
|
||||||
|
ospath, err = filepath.Abs(ospath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Path, err = filepath.EvalSymlinks(u.Path)
|
ospath, err = filepath.EvalSymlinks(ospath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Path = filepath.Clean(u.Path)
|
ospath = filepath.Clean(ospath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now that ospath was normalized and such..
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
// Also replace all backslashes with forwardslashes since Windows
|
uri.Host = ""
|
||||||
// users are likely to do this but the URL should actually only
|
// Check to see if our ospath is unc-prefixed, and if it is then split
|
||||||
// contain forward slashes.
|
// the UNC host into uri.Host, leaving the rest in ospath.
|
||||||
u.Path = strings.Replace(u.Path, `\`, `/`, -1)
|
// This way, our UNC-uri is protected from injury in the call to uri.String()
|
||||||
// prepend absolute windows paths with "/" so that when we
|
if len(ospath) >= len(UNCPrefix) && ospath[:len(UNCPrefix)] == UNCPrefix {
|
||||||
// compose u.String() below the outcome will be correct
|
idx := strings.Index(ospath[len(UNCPrefix):], string(os.PathSeparator))
|
||||||
// file:///c/blah syntax; otherwise u.String() will only add
|
if idx > -1 {
|
||||||
// file:// which is not technically a correct windows URI
|
uri.Host = ospath[:len(UNCPrefix)+idx]
|
||||||
if filepath.IsAbs(u.Path) && !strings.HasPrefix(u.Path, "/") {
|
ospath = ospath[len(UNCPrefix)+idx:]
|
||||||
u.Path = "/" + u.Path
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// Restore the uri by re-transforming our os-formatted path
|
||||||
|
uri.Path = filepath.ToSlash(ospath)
|
||||||
|
} else {
|
||||||
|
uri.Host = ""
|
||||||
|
uri.Path = filepath.ToSlash(ospath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure it is lowercased
|
// Make sure it is lowercased
|
||||||
u.Scheme = strings.ToLower(u.Scheme)
|
uri.Scheme = strings.ToLower(uri.Scheme)
|
||||||
|
|
||||||
// Verify that the scheme is something we support in our common downloader.
|
// Verify that the scheme is something we support in our common downloader.
|
||||||
supported := []string{"file", "http", "https"}
|
supported := []string{"file", "http", "https"}
|
||||||
found := false
|
found := false
|
||||||
for _, s := range supported {
|
for _, s := range supported {
|
||||||
if u.Scheme == s {
|
if uri.Scheme == s {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
return "", fmt.Errorf("Unsupported URL scheme: %s", u.Scheme)
|
return "", fmt.Errorf("Unsupported URL scheme: %s", uri.Scheme)
|
||||||
}
|
}
|
||||||
return u.String(), nil
|
|
||||||
|
// explicit check to see if we need to manually replace the uri host with a UNC one
|
||||||
|
if runtime.GOOS == "windows" && uri.Scheme == "file" {
|
||||||
|
if len(uri.Host) >= len(UNCPrefix) && uri.Host[:len(UNCPrefix)] == UNCPrefix {
|
||||||
|
escapedHost := url.QueryEscape(uri.Host)
|
||||||
|
return strings.Replace(uri.String(), escapedHost, uri.Host, 1), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uri.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileExistsLocally takes the URL output from DownloadableURL, and determines
|
// FileExistsLocally takes the URL output from DownloadableURL, and determines
|
||||||
|
|
|
@ -16,6 +16,9 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DownloadConfig is the configuration given to instantiate a new
|
// DownloadConfig is the configuration given to instantiate a new
|
||||||
|
@ -130,9 +133,49 @@ func (d *DownloadClient) Get() (string, error) {
|
||||||
// locally and we don't make a copy. Normally we would copy or download.
|
// locally and we don't make a copy. Normally we would copy or download.
|
||||||
log.Printf("[DEBUG] Using local file: %s", finalPath)
|
log.Printf("[DEBUG] Using local file: %s", finalPath)
|
||||||
|
|
||||||
// Remove forward slash on absolute Windows file URLs before processing
|
// FIXME:
|
||||||
if runtime.GOOS == "windows" && len(finalPath) > 0 && finalPath[0] == '/' {
|
// cwd should point to the path relative to client.json, but
|
||||||
finalPath = finalPath[1:]
|
// since this isn't exposed to us anywhere, we use os.Getwd()
|
||||||
|
// to figure it out.
|
||||||
|
cwd,err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Unable to get working directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the actual file uri to a windowsy path
|
||||||
|
// (this logic must correspond to the same logic in common/config.go)
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
const UNCPrefix = string(os.PathSeparator)+string(os.PathSeparator)
|
||||||
|
|
||||||
|
// move any extra path components that were parsed into Host due
|
||||||
|
// to UNC into the url.Path field so that it's PathSeparators get
|
||||||
|
// normalized
|
||||||
|
if len(url.Host) >= len(UNCPrefix) && url.Host[:len(UNCPrefix)] == UNCPrefix {
|
||||||
|
idx := strings.Index(url.Host[len(UNCPrefix):], string(os.PathSeparator))
|
||||||
|
if idx > -1 {
|
||||||
|
url.Path = filepath.ToSlash(url.Host[idx+len(UNCPrefix):]) + url.Path
|
||||||
|
url.Host = url.Host[:idx+len(UNCPrefix)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up backward-slashes since they only matter when part of a unc path
|
||||||
|
urlPath := filepath.ToSlash(url.Path)
|
||||||
|
|
||||||
|
// semi-absolute path (current drive letter) -- file:///absolute/path
|
||||||
|
if url.Host == "" && len(urlPath) > 0 && urlPath[0] == '/' {
|
||||||
|
finalPath = path.Join(filepath.VolumeName(cwd), urlPath)
|
||||||
|
|
||||||
|
// relative path -- file://./relative/path
|
||||||
|
// file://relative/path
|
||||||
|
} else if url.Host == "" || (len(url.Host) > 0 && url.Host[0] == '.') {
|
||||||
|
finalPath = path.Join(filepath.ToSlash(cwd), urlPath)
|
||||||
|
|
||||||
|
// absolute path
|
||||||
|
} else {
|
||||||
|
// UNC -- file://\\host/share/whatever
|
||||||
|
// drive -- file://c:/absolute/path
|
||||||
|
finalPath = path.Join(url.Host, urlPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep track of the source so we can make sure not to delete this later
|
// Keep track of the source so we can make sure not to delete this later
|
||||||
|
|
|
@ -67,7 +67,7 @@ func NewCore(c *CoreConfig) (*Core, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go through and interpolate all the build names. We shuld be able
|
// Go through and interpolate all the build names. We should be able
|
||||||
// to do this at this point with the variables.
|
// to do this at this point with the variables.
|
||||||
result.builds = make(map[string]*template.Builder)
|
result.builds = make(map[string]*template.Builder)
|
||||||
for _, b := range c.Template.Builders {
|
for _, b := range c.Template.Builders {
|
||||||
|
|
Loading…
Reference in New Issue