2013-07-19 14:59:04 -04:00
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
2013-07-19 15:09:13 -04:00
|
|
|
"fmt"
|
2013-07-29 02:51:21 -04:00
|
|
|
"net/url"
|
|
|
|
"os"
|
2013-07-29 03:09:48 -04:00
|
|
|
"path/filepath"
|
2013-08-02 17:06:06 -04:00
|
|
|
"runtime"
|
2013-07-19 15:09:13 -04:00
|
|
|
"strings"
|
2017-01-14 20:56:04 -05:00
|
|
|
"time"
|
2013-07-19 14:59:04 -04:00
|
|
|
)
|
|
|
|
|
2017-01-14 20:56:04 -05:00
|
|
|
// PackerKeyEnv is used to specify the key interval (delay) between keystrokes
|
|
|
|
// sent to the VM, typically in boot commands. This is to prevent host CPU
|
|
|
|
// utilization from causing key presses to be skipped or repeated incorrectly.
|
|
|
|
const PackerKeyEnv = "PACKER_KEY_INTERVAL"
|
|
|
|
|
|
|
|
// PackerKeyDefault 100ms is appropriate for shared build infrastructure while a
|
|
|
|
// shorter delay (e.g. 10ms) can be used on a workstation. See PackerKeyEnv.
|
|
|
|
const PackerKeyDefault = 100 * time.Millisecond
|
|
|
|
|
2013-10-11 06:50:08 -04:00
|
|
|
// ScrubConfig is a helper that returns a string representation of
|
|
|
|
// any struct with the given values stripped out.
|
|
|
|
func ScrubConfig(target interface{}, values ...string) string {
|
|
|
|
conf := fmt.Sprintf("Config: %+v", target)
|
|
|
|
for _, value := range values {
|
2015-05-18 18:13:01 -04:00
|
|
|
if value == "" {
|
|
|
|
continue
|
|
|
|
}
|
2013-10-11 06:50:08 -04:00
|
|
|
conf = strings.Replace(conf, value, "<Filtered>", -1)
|
|
|
|
}
|
|
|
|
return conf
|
|
|
|
}
|
|
|
|
|
2014-04-22 00:30:49 -04:00
|
|
|
// ChooseString returns the first non-empty value.
|
|
|
|
func ChooseString(vals ...string) string {
|
2014-04-26 14:12:43 -04:00
|
|
|
for _, el := range vals {
|
|
|
|
if el != "" {
|
|
|
|
return el
|
|
|
|
}
|
|
|
|
}
|
2014-04-22 00:30:49 -04:00
|
|
|
|
2014-04-26 14:12:43 -04:00
|
|
|
return ""
|
2014-04-22 00:30:49 -04:00
|
|
|
}
|
|
|
|
|
2013-07-29 02:51:21 -04:00
|
|
|
// 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) {
|
2013-08-15 23:16:05 -04:00
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
// If the distance to the first ":" is just one character, assume
|
|
|
|
// we're dealing with a drive letter and thus a file path.
|
2018-01-09 17:27:09 -05:00
|
|
|
// prepend with "file:///"" now so that url.Parse won't accidentally
|
|
|
|
// parse the drive letter into the url scheme.
|
|
|
|
// See https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/
|
|
|
|
// for more info about valid windows URIs
|
2013-08-15 23:16:05 -04:00
|
|
|
idx := strings.Index(original, ":")
|
|
|
|
if idx == 1 {
|
2015-11-01 22:46:14 -05:00
|
|
|
original = "file://" + filepath.ToSlash(original)
|
2013-08-15 23:16:05 -04:00
|
|
|
}
|
|
|
|
}
|
2015-11-01 22:46:14 -05:00
|
|
|
|
|
|
|
// 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)
|
2013-07-29 02:51:21 -04:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2015-11-01 22:46:14 -05:00
|
|
|
if uri.Scheme == "" {
|
|
|
|
uri.Scheme = "file"
|
2013-07-29 02:51:21 -04:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:46:14 -05:00
|
|
|
const UNCPrefix = string(os.PathSeparator)+string(os.PathSeparator)
|
|
|
|
if uri.Scheme == "file" {
|
|
|
|
var ospath string // os-formatted pathname
|
2018-01-10 13:03:36 -05:00
|
|
|
if runtime.GOOS == "windows" {
|
2015-11-01 22:46:14 -05:00
|
|
|
// 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)]
|
|
|
|
}
|
2018-01-10 13:03:36 -05:00
|
|
|
}
|
2015-11-01 22:46:14 -05:00
|
|
|
// 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
|
2018-01-10 13:03:36 -05:00
|
|
|
}
|
2013-12-06 21:31:45 -05:00
|
|
|
// Only do the filepath transformations if the file appears
|
2015-11-01 22:46:14 -05:00
|
|
|
// 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)
|
2013-12-06 21:31:45 -05:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2013-07-29 03:06:21 -04:00
|
|
|
|
2015-11-01 22:46:14 -05:00
|
|
|
ospath, err = filepath.EvalSymlinks(ospath)
|
2013-12-06 21:31:45 -05:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2013-07-29 03:06:21 -04:00
|
|
|
|
2015-11-01 22:46:14 -05:00
|
|
|
ospath = filepath.Clean(ospath)
|
2013-07-29 03:06:21 -04:00
|
|
|
}
|
2014-04-22 00:28:47 -04:00
|
|
|
|
2015-11-01 22:46:14 -05:00
|
|
|
// now that ospath was normalized and such..
|
2014-04-22 00:28:47 -04:00
|
|
|
if runtime.GOOS == "windows" {
|
2015-11-01 22:46:14 -05:00
|
|
|
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:]
|
|
|
|
}
|
2018-01-09 17:27:09 -05:00
|
|
|
}
|
2015-11-01 22:46:14 -05:00
|
|
|
// Restore the uri by re-transforming our os-formatted path
|
|
|
|
uri.Path = filepath.ToSlash(ospath)
|
|
|
|
} else {
|
|
|
|
uri.Host = ""
|
|
|
|
uri.Path = filepath.ToSlash(ospath)
|
2014-04-22 00:28:47 -04:00
|
|
|
}
|
2013-07-29 02:51:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure it is lowercased
|
2015-11-01 22:46:14 -05:00
|
|
|
uri.Scheme = strings.ToLower(uri.Scheme)
|
2013-07-29 02:51:21 -04:00
|
|
|
|
|
|
|
// Verify that the scheme is something we support in our common downloader.
|
|
|
|
supported := []string{"file", "http", "https"}
|
|
|
|
found := false
|
|
|
|
for _, s := range supported {
|
2015-11-01 22:46:14 -05:00
|
|
|
if uri.Scheme == s {
|
2013-07-29 02:51:21 -04:00
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !found {
|
2015-11-01 22:46:14 -05:00
|
|
|
return "", fmt.Errorf("Unsupported URL scheme: %s", uri.Scheme)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
2013-07-29 02:51:21 -04:00
|
|
|
}
|
2015-11-01 22:46:14 -05:00
|
|
|
return uri.String(), nil
|
2013-07-29 02:51:21 -04:00
|
|
|
}
|
2018-01-03 19:53:47 -05:00
|
|
|
|
|
|
|
// FileExistsLocally takes the URL output from DownloadableURL, and determines
|
|
|
|
// whether it is present on the file system.
|
|
|
|
// example usage:
|
|
|
|
//
|
|
|
|
// myFile, err = common.DownloadableURL(c.SourcePath)
|
|
|
|
// ...
|
2018-01-10 19:11:17 -05:00
|
|
|
// fileExists := common.StatURL(myFile)
|
2018-01-03 19:53:47 -05:00
|
|
|
// possible output:
|
2018-01-10 19:11:17 -05:00
|
|
|
// true -- should occur if the file is present, or if the file is not present,
|
|
|
|
// but is not supposed to be (e.g. the schema is http://, not file://)
|
|
|
|
// false -- should occur if there was an error stating the file, so the
|
2018-01-03 19:53:47 -05:00
|
|
|
// file is not present when it should be.
|
|
|
|
|
2018-01-10 19:11:17 -05:00
|
|
|
func FileExistsLocally(original string) bool {
|
2018-01-04 13:51:59 -05:00
|
|
|
// original should be something like file://C:/my/path.iso
|
2018-01-09 17:27:09 -05:00
|
|
|
|
|
|
|
fileURL, _ := url.Parse(original)
|
|
|
|
fileExists := false
|
2018-01-03 19:53:47 -05:00
|
|
|
|
|
|
|
if fileURL.Scheme == "file" {
|
2018-01-09 17:27:09 -05:00
|
|
|
// on windows, correct URI is file:///c:/blah/blah.iso.
|
|
|
|
// url.Parse will pull out the scheme "file://" and leave the path as
|
|
|
|
// "/c:/blah/blah/iso". Here we remove this forward slash on absolute
|
|
|
|
// Windows file URLs before processing
|
|
|
|
// see https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/
|
|
|
|
// for more info about valid windows URIs
|
|
|
|
filePath := fileURL.Path
|
|
|
|
if runtime.GOOS == "windows" && len(filePath) > 0 && filePath[0] == '/' {
|
|
|
|
filePath = filePath[1:]
|
|
|
|
}
|
2018-01-04 13:51:59 -05:00
|
|
|
_, err := os.Stat(filePath)
|
|
|
|
if err != nil {
|
2018-01-10 19:11:17 -05:00
|
|
|
return fileExists
|
2018-01-03 19:53:47 -05:00
|
|
|
} else {
|
|
|
|
fileExists = true
|
|
|
|
}
|
|
|
|
}
|
2018-01-10 19:11:17 -05:00
|
|
|
return fileExists
|
2018-01-03 19:53:47 -05:00
|
|
|
}
|