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  }