github.com/jerryclinesmith/packer@v0.3.7/common/config.go (about) 1 package common 2 3 import ( 4 "fmt" 5 "github.com/mitchellh/mapstructure" 6 "github.com/mitchellh/packer/packer" 7 "net/url" 8 "os" 9 "path/filepath" 10 "runtime" 11 "sort" 12 "strings" 13 ) 14 15 // CheckUnusedConfig is a helper that makes sure that the there are no 16 // unused configuration keys, properly ignoring keys that don't matter. 17 func CheckUnusedConfig(md *mapstructure.Metadata) *packer.MultiError { 18 errs := make([]error, 0) 19 20 if md.Unused != nil && len(md.Unused) > 0 { 21 sort.Strings(md.Unused) 22 for _, unused := range md.Unused { 23 if unused != "type" && !strings.HasPrefix(unused, "packer_") { 24 errs = append( 25 errs, fmt.Errorf("Unknown configuration key: %s", unused)) 26 } 27 } 28 } 29 30 if len(errs) > 0 { 31 return &packer.MultiError{errs} 32 } 33 34 return nil 35 } 36 37 // DecodeConfig is a helper that handles decoding raw configuration using 38 // mapstructure. It returns the metadata and any errors that may happen. 39 // If you need extra configuration for mapstructure, you should configure 40 // it manually and not use this helper function. 41 func DecodeConfig(target interface{}, raws ...interface{}) (*mapstructure.Metadata, error) { 42 var md mapstructure.Metadata 43 decoderConfig := &mapstructure.DecoderConfig{ 44 Metadata: &md, 45 Result: target, 46 } 47 48 decoder, err := mapstructure.NewDecoder(decoderConfig) 49 if err != nil { 50 return nil, err 51 } 52 53 for _, raw := range raws { 54 err := decoder.Decode(raw) 55 if err != nil { 56 return nil, err 57 } 58 } 59 60 return &md, nil 61 } 62 63 // DownloadableURL processes a URL that may also be a file path and returns 64 // a completely valid URL. For example, the original URL might be "local/file.iso" 65 // which isn't a valid URL. DownloadableURL will return "file:///local/file.iso" 66 func DownloadableURL(original string) (string, error) { 67 if runtime.GOOS == "windows" { 68 // If the distance to the first ":" is just one character, assume 69 // we're dealing with a drive letter and thus a file path. 70 idx := strings.Index(original, ":") 71 if idx == 1 { 72 original = "file:///" + original 73 } 74 } 75 76 url, err := url.Parse(original) 77 if err != nil { 78 return "", err 79 } 80 81 if url.Scheme == "" { 82 url.Scheme = "file" 83 } 84 85 if url.Scheme == "file" { 86 // For Windows absolute file paths, remove leading / prior to processing 87 // since net/url turns "C:/" into "/C:/" 88 if runtime.GOOS == "windows" && url.Path[0] == '/' { 89 url.Path = url.Path[1:len(url.Path)] 90 91 // Also replace all backslashes with forwardslashes since Windows 92 // users are likely to do this but the URL should actually only 93 // contain forward slashes. 94 url.Path = strings.Replace(url.Path, `\`, `/`, -1) 95 } 96 97 if _, err := os.Stat(url.Path); err != nil { 98 return "", err 99 } 100 101 url.Path, err = filepath.Abs(url.Path) 102 if err != nil { 103 return "", err 104 } 105 106 url.Path, err = filepath.EvalSymlinks(url.Path) 107 if err != nil { 108 return "", err 109 } 110 111 url.Path = filepath.Clean(url.Path) 112 } 113 114 // Make sure it is lowercased 115 url.Scheme = strings.ToLower(url.Scheme) 116 117 // This is to work around issue #5927. This can safely be removed once 118 // we distribute with a version of Go that fixes that bug. 119 // 120 // See: https://code.google.com/p/go/issues/detail?id=5927 121 if url.Path != "" && url.Path[0] != '/' { 122 url.Path = "/" + url.Path 123 } 124 125 // Verify that the scheme is something we support in our common downloader. 126 supported := []string{"file", "http", "https"} 127 found := false 128 for _, s := range supported { 129 if url.Scheme == s { 130 found = true 131 break 132 } 133 } 134 135 if !found { 136 return "", fmt.Errorf("Unsupported URL scheme: %s", url.Scheme) 137 } 138 139 return url.String(), nil 140 }