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