diff --git a/common/config.go b/common/config.go index 72b3bdd27..8b1761ceb 100644 --- a/common/config.go +++ b/common/config.go @@ -2,14 +2,10 @@ package common import ( "fmt" - "github.com/mitchellh/mapstructure" - "github.com/mitchellh/packer/packer" "net/url" "os" "path/filepath" - "reflect" "runtime" - "sort" "strings" ) @@ -23,28 +19,6 @@ func ScrubConfig(target interface{}, values ...string) string { return conf } -// CheckUnusedConfig is a helper that makes sure that the there are no -// unused configuration keys, properly ignoring keys that don't matter. -func CheckUnusedConfig(md *mapstructure.Metadata) *packer.MultiError { - errs := make([]error, 0) - - if md.Unused != nil && len(md.Unused) > 0 { - sort.Strings(md.Unused) - for _, unused := range md.Unused { - if unused != "type" && !strings.HasPrefix(unused, "packer_") { - errs = append( - errs, fmt.Errorf("Unknown configuration key: %q", unused)) - } - } - } - - if len(errs) > 0 { - return &packer.MultiError{errs} - } - - return nil -} - // ChooseString returns the first non-empty value. func ChooseString(vals ...string) string { for _, el := range vals { @@ -56,42 +30,6 @@ func ChooseString(vals ...string) string { return "" } -// DecodeConfig is a helper that handles decoding raw configuration using -// mapstructure. It returns the metadata and any errors that may happen. -// If you need extra configuration for mapstructure, you should configure -// it manually and not use this helper function. -func DecodeConfig(target interface{}, raws ...interface{}) (*mapstructure.Metadata, error) { - decodeHook, err := decodeConfigHook(raws) - if err != nil { - return nil, err - } - - var md mapstructure.Metadata - decoderConfig := &mapstructure.DecoderConfig{ - DecodeHook: mapstructure.ComposeDecodeHookFunc( - decodeHook, - mapstructure.StringToSliceHookFunc(","), - ), - Metadata: &md, - Result: target, - WeaklyTypedInput: true, - } - - decoder, err := mapstructure.NewDecoder(decoderConfig) - if err != nil { - return nil, err - } - - for _, raw := range raws { - err := decoder.Decode(raw) - if err != nil { - return nil, err - } - } - - return &md, nil -} - // 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" @@ -182,62 +120,3 @@ func DownloadableURL(original string) (string, error) { return url.String(), nil } - -// This returns a mapstructure.DecodeHookFunc that automatically template -// processes any configuration values that aren't strings but have been -// provided as strings. -// -// For example: "image_id" wants an int and the user uses a string with -// a user variable like "{{user `image_id`}}". This decode hook makes that -// work. -func decodeConfigHook(raws []interface{}) (mapstructure.DecodeHookFunc, error) { - // First thing we do is decode PackerConfig so that we can have access - // to the user variables so that we can process some templates. - var pc PackerConfig - - decoderConfig := &mapstructure.DecoderConfig{ - Result: &pc, - WeaklyTypedInput: true, - } - decoder, err := mapstructure.NewDecoder(decoderConfig) - if err != nil { - return nil, err - } - for _, raw := range raws { - if err := decoder.Decode(raw); err != nil { - return nil, err - } - } - - tpl, err := packer.NewConfigTemplate() - if err != nil { - return nil, err - } - tpl.UserVars = pc.PackerUserVars - - return func(f reflect.Kind, t reflect.Kind, v interface{}) (interface{}, error) { - if t != reflect.String { - // We need to convert []uint8 to string. We have to do this - // because internally Packer uses MsgPack for RPC and the MsgPack - // codec turns strings into []uint8 - if f == reflect.Slice { - dataVal := reflect.ValueOf(v) - dataType := dataVal.Type() - elemKind := dataType.Elem().Kind() - if elemKind == reflect.Uint8 { - v = string(dataVal.Interface().([]uint8)) - } - } - - if sv, ok := v.(string); ok { - var err error - v, err = tpl.Process(sv, nil) - if err != nil { - return nil, err - } - } - } - - return v, nil - }, nil -} diff --git a/common/config_test.go b/common/config_test.go index a93feb729..92a7316a3 100644 --- a/common/config_test.go +++ b/common/config_test.go @@ -2,33 +2,14 @@ package common import ( "fmt" - "github.com/mitchellh/mapstructure" "io/ioutil" "os" "path/filepath" - "reflect" "runtime" "strings" "testing" ) -func TestCheckUnusedConfig(t *testing.T) { - md := &mapstructure.Metadata{ - Unused: make([]string, 0), - } - - err := CheckUnusedConfig(md) - if err != nil { - t.Fatalf("err: %s", err) - } - - md.Unused = []string{"foo", "bar"} - err = CheckUnusedConfig(md) - if err == nil { - t.Fatal("should have error") - } -} - func TestChooseString(t *testing.T) { cases := []struct { Input []string @@ -56,129 +37,6 @@ func TestChooseString(t *testing.T) { } } -func TestDecodeConfig(t *testing.T) { - type Local struct { - Foo string - Bar string - } - - raws := []interface{}{ - map[string]interface{}{ - "foo": "bar", - }, - map[string]interface{}{ - "bar": "baz", - "baz": "what", - }, - } - - var result Local - md, err := DecodeConfig(&result, raws...) - if err != nil { - t.Fatalf("err: %s", err) - } - - if result.Foo != "bar" { - t.Fatalf("invalid: %#v", result.Foo) - } - - if result.Bar != "baz" { - t.Fatalf("invalid: %#v", result.Bar) - } - - if md == nil { - t.Fatal("metadata should not be nil") - } - - if !reflect.DeepEqual(md.Unused, []string{"baz"}) { - t.Fatalf("unused: %#v", md.Unused) - } -} - -// This test tests the case that a user var is used for an integer -// configuration. -func TestDecodeConfig_stringToSlice(t *testing.T) { - type Local struct { - Val []string - EmptyVal []string - } - - raw := map[string]interface{}{ - "packer_user_variables": map[string]string{ - "foo": "bar", - }, - - "val": "foo,{{user `foo`}}", - "emptyval": "", - } - - var result Local - _, err := DecodeConfig(&result, raw) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := []string{"foo", "bar"} - if !reflect.DeepEqual(result.Val, expected) { - t.Fatalf("invalid: %#v", result.Val) - } - if len(result.EmptyVal) > 0 { - t.Fatalf("invalid: %#v", result.EmptyVal) - } -} - -// This test tests the case that a user var is used for an integer -// configuration. -func TestDecodeConfig_userVarConversion(t *testing.T) { - type Local struct { - Val int - } - - raw := map[string]interface{}{ - "packer_user_variables": map[string]string{ - "foo": "42", - }, - - "val": "{{user `foo`}}", - } - - var result Local - _, err := DecodeConfig(&result, raw) - if err != nil { - t.Fatalf("err: %s", err) - } - - if result.Val != 42 { - t.Fatalf("invalid: %#v", result.Val) - } -} - -// This tests the way MessagePack decodes strings (into []uint8) and -// that we can still decode into the proper types. -func TestDecodeConfig_userVarConversionUInt8(t *testing.T) { - type Local struct { - Val int - } - - raw := map[string]interface{}{ - "packer_user_variables": map[string]string{ - "foo": "42", - }, - - "val": []uint8("{{user `foo`}}"), - } - - var result Local - _, err := DecodeConfig(&result, raw) - if err != nil { - t.Fatalf("err: %s", err) - } - - if result.Val != 42 { - t.Fatalf("invalid: %#v", result.Val) - } -} - func TestDownloadableURL(t *testing.T) { // Invalid URL: has hex code in host _, err := DownloadableURL("http://what%20.com")