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)
|
||||
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")
|
||||
|
||||
vmxTemplate := DefaultVMXTemplate
|
||||
|
|
|
@ -56,77 +56,109 @@ func DownloadableURL(original string) (string, error) {
|
|||
// for more info about valid windows URIs
|
||||
idx := strings.Index(original, ":")
|
||||
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 {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if u.Scheme == "" {
|
||||
u.Scheme = "file"
|
||||
if uri.Scheme == "" {
|
||||
uri.Scheme = "file"
|
||||
}
|
||||
|
||||
if u.Scheme == "file" {
|
||||
// Windows file handling is all sorts of tricky...
|
||||
const UNCPrefix = string(os.PathSeparator)+string(os.PathSeparator)
|
||||
if uri.Scheme == "file" {
|
||||
var ospath string // os-formatted pathname
|
||||
if runtime.GOOS == "windows" {
|
||||
// If the path is using Windows-style slashes, URL parses
|
||||
// it into the host field.
|
||||
if u.Path == "" && strings.Contains(u.Host, `\`) {
|
||||
u.Path = u.Host
|
||||
u.Host = ""
|
||||
// Move any extra path components that were mis-parsed into the Host
|
||||
// field back into the uri.Path field
|
||||
if len(uri.Host) >= len(UNCPrefix) && uri.Host[:len(UNCPrefix)] == UNCPrefix {
|
||||
idx := strings.Index(uri.Host[len(UNCPrefix):], string(os.PathSeparator))
|
||||
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
|
||||
// to actually exist.
|
||||
if _, err := os.Stat(u.Path); err == nil {
|
||||
u.Path, err = filepath.Abs(u.Path)
|
||||
// to actually exist. We don't do it on windows, because EvalSymlinks
|
||||
// won't understand how to handle UNC paths and other Windows-specific minutae.
|
||||
if _, err := os.Stat(ospath); err == nil && runtime.GOOS != "windows" {
|
||||
ospath, err = filepath.Abs(ospath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
u.Path, err = filepath.EvalSymlinks(u.Path)
|
||||
ospath, err = filepath.EvalSymlinks(ospath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
u.Path = filepath.Clean(u.Path)
|
||||
ospath = filepath.Clean(ospath)
|
||||
}
|
||||
|
||||
// now that ospath was normalized and such..
|
||||
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.
|
||||
u.Path = strings.Replace(u.Path, `\`, `/`, -1)
|
||||
// prepend absolute windows paths with "/" so that when we
|
||||
// compose u.String() below the outcome will be correct
|
||||
// file:///c/blah syntax; otherwise u.String() will only add
|
||||
// file:// which is not technically a correct windows URI
|
||||
if filepath.IsAbs(u.Path) && !strings.HasPrefix(u.Path, "/") {
|
||||
u.Path = "/" + u.Path
|
||||
uri.Host = ""
|
||||
// Check to see if our ospath is unc-prefixed, and if it is then split
|
||||
// the UNC host into uri.Host, leaving the rest in ospath.
|
||||
// This way, our UNC-uri is protected from injury in the call to uri.String()
|
||||
if len(ospath) >= len(UNCPrefix) && ospath[:len(UNCPrefix)] == UNCPrefix {
|
||||
idx := strings.Index(ospath[len(UNCPrefix):], string(os.PathSeparator))
|
||||
if idx > -1 {
|
||||
uri.Host = ospath[:len(UNCPrefix)+idx]
|
||||
ospath = ospath[len(UNCPrefix)+idx:]
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
u.Scheme = strings.ToLower(u.Scheme)
|
||||
uri.Scheme = strings.ToLower(uri.Scheme)
|
||||
|
||||
// Verify that the scheme is something we support in our common downloader.
|
||||
supported := []string{"file", "http", "https"}
|
||||
found := false
|
||||
for _, s := range supported {
|
||||
if u.Scheme == s {
|
||||
if uri.Scheme == s {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -16,6 +16,9 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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.
|
||||
log.Printf("[DEBUG] Using local file: %s", finalPath)
|
||||
|
||||
// Remove forward slash on absolute Windows file URLs before processing
|
||||
if runtime.GOOS == "windows" && len(finalPath) > 0 && finalPath[0] == '/' {
|
||||
finalPath = finalPath[1:]
|
||||
// FIXME:
|
||||
// cwd should point to the path relative to client.json, but
|
||||
// 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
|
||||
|
|
|
@ -67,7 +67,7 @@ func NewCore(c *CoreConfig) (*Core, error) {
|
|||
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.
|
||||
result.builds = make(map[string]*template.Builder)
|
||||
for _, b := range c.Template.Builders {
|
||||
|
|
Loading…
Reference in New Issue