common: process user variables in non-string config decodes [GH-598]
This commit is contained in:
parent
47d47082fc
commit
4c6800f5a3
|
@ -35,6 +35,7 @@ IMPROVEMENTS:
|
|||
BUG FIXES:
|
||||
|
||||
* core: No colored output in machine-readable output. [GH-684]
|
||||
* core: User variables can now be used for non-string fields. [GH-598]
|
||||
* post-processor/vsphere: Uploads VM properly. [GH-694]
|
||||
* post-processor/vsphere: Process user variables.
|
||||
* provisioner/ansible-local: playbook paths are properly validated
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -49,8 +50,14 @@ func CheckUnusedConfig(md *mapstructure.Metadata) *packer.MultiError {
|
|||
// 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: decodeHook,
|
||||
Metadata: &md,
|
||||
Result: target,
|
||||
WeaklyTypedInput: true,
|
||||
|
@ -149,3 +156,41 @@ 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
|
||||
for _, raw := range raws {
|
||||
if err := mapstructure.Decode(raw, &pc); 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 {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -66,6 +66,32 @@ func TestDecodeConfig(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadableURL(t *testing.T) {
|
||||
// Invalid URL: has hex code in host
|
||||
_, err := DownloadableURL("http://what%20.com")
|
||||
|
|
Loading…
Reference in New Issue