From da09dcfc798a2d4efc88574d82fb05ffcd10df2d Mon Sep 17 00:00:00 2001 From: James Bardin Date: Thu, 27 Oct 2016 11:34:00 -0400 Subject: [PATCH] update github.com/hashicorp/hil --- vendor/github.com/hashicorp/hil/ast/ast.go | 9 ++++ .../github.com/hashicorp/hil/ast/unknown.go | 30 +++++++++++++ .../hashicorp/hil/ast/variables_helper.go | 11 +++++ .../github.com/hashicorp/hil/check_types.go | 42 ++++++++++++++---- vendor/github.com/hashicorp/hil/convert.go | 11 +++++ vendor/github.com/hashicorp/hil/eval.go | 43 ++++++++++++++++--- vendor/github.com/hashicorp/hil/eval_type.go | 1 + vendor/vendor.json | 12 +++--- 8 files changed, 139 insertions(+), 20 deletions(-) create mode 100644 vendor/github.com/hashicorp/hil/ast/unknown.go diff --git a/vendor/github.com/hashicorp/hil/ast/ast.go b/vendor/github.com/hashicorp/hil/ast/ast.go index 5d8d7555a1..54564f5477 100644 --- a/vendor/github.com/hashicorp/hil/ast/ast.go +++ b/vendor/github.com/hashicorp/hil/ast/ast.go @@ -54,6 +54,13 @@ const ( TypeFloat TypeList TypeMap + + // This is a special type used by Terraform to mark "unknown" values. + // It is impossible for this type to be introduced into your HIL programs + // unless you explicitly set a variable to this value. In that case, + // any operation including the variable will return "TypeUnknown" as the + // type. + TypeUnknown ) func (t Type) Printable() string { @@ -72,6 +79,8 @@ func (t Type) Printable() string { return "type list" case TypeMap: return "type map" + case TypeUnknown: + return "type unknown" default: return "unknown type" } diff --git a/vendor/github.com/hashicorp/hil/ast/unknown.go b/vendor/github.com/hashicorp/hil/ast/unknown.go new file mode 100644 index 0000000000..d6ddaecc78 --- /dev/null +++ b/vendor/github.com/hashicorp/hil/ast/unknown.go @@ -0,0 +1,30 @@ +package ast + +// IsUnknown reports whether a variable is unknown or contains any value +// that is unknown. This will recurse into lists and maps and so on. +func IsUnknown(v Variable) bool { + // If it is unknown itself, return true + if v.Type == TypeUnknown { + return true + } + + // If it is a container type, check the values + switch v.Type { + case TypeList: + for _, el := range v.Value.([]Variable) { + if IsUnknown(el) { + return true + } + } + case TypeMap: + for _, el := range v.Value.(map[string]Variable) { + if IsUnknown(el) { + return true + } + } + default: + } + + // Not a container type or survive the above checks + return false +} diff --git a/vendor/github.com/hashicorp/hil/ast/variables_helper.go b/vendor/github.com/hashicorp/hil/ast/variables_helper.go index 4b32841987..40aa534cf6 100644 --- a/vendor/github.com/hashicorp/hil/ast/variables_helper.go +++ b/vendor/github.com/hashicorp/hil/ast/variables_helper.go @@ -5,6 +5,11 @@ import "fmt" func VariableListElementTypesAreHomogenous(variableName string, list []Variable) (Type, error) { listTypes := make(map[Type]struct{}) for _, v := range list { + // Allow unknown + if v.Type == TypeUnknown { + continue + } + if _, ok := listTypes[v.Type]; ok { continue } @@ -25,9 +30,15 @@ func VariableListElementTypesAreHomogenous(variableName string, list []Variable) func VariableMapValueTypesAreHomogenous(variableName string, vmap map[string]Variable) (Type, error) { valueTypes := make(map[Type]struct{}) for _, v := range vmap { + // Allow unknown + if v.Type == TypeUnknown { + continue + } + if _, ok := valueTypes[v.Type]; ok { continue } + valueTypes[v.Type] = struct{}{} } diff --git a/vendor/github.com/hashicorp/hil/check_types.go b/vendor/github.com/hashicorp/hil/check_types.go index 1c8cb0a07e..ec9ae7bbc5 100644 --- a/vendor/github.com/hashicorp/hil/check_types.go +++ b/vendor/github.com/hashicorp/hil/check_types.go @@ -44,6 +44,12 @@ func (v *TypeCheck) Visit(root ast.Node) error { defer v.lock.Unlock() defer v.reset() root.Accept(v.visit) + + // If the resulting type is unknown, then just let the whole thing go. + if v.err == errExitUnknown { + v.err = nil + } + return v.err } @@ -89,6 +95,10 @@ func (v *TypeCheck) visit(raw ast.Node) ast.Node { pos.Column, pos.Line, err) } + if v.StackPeek() == ast.TypeUnknown { + v.err = errExitUnknown + } + return result } @@ -242,15 +252,14 @@ func (tc *typeCheckOutput) TypeCheck(v *TypeCheck) (ast.Node, error) { } // If there is only one argument and it is a list, we evaluate to a list - if len(types) == 1 && types[0] == ast.TypeList { - v.StackPush(ast.TypeList) - return n, nil - } - - // If there is only one argument and it is a map, we evaluate to a map - if len(types) == 1 && types[0] == ast.TypeMap { - v.StackPush(ast.TypeMap) - return n, nil + if len(types) == 1 { + switch t := types[0]; t { + case ast.TypeList: + fallthrough + case ast.TypeMap: + v.StackPush(t) + return n, nil + } } // Otherwise, all concat args must be strings, so validate that @@ -294,6 +303,13 @@ func (tc *typeCheckVariableAccess) TypeCheck(v *TypeCheck) (ast.Node, error) { "unknown variable accessed: %s", tc.n.Name) } + // Check if the variable contains any unknown types. If so, then + // mark it as unknown. + if ast.IsUnknown(variable) { + v.StackPush(ast.TypeUnknown) + return tc.n, nil + } + // Add the type to the stack v.StackPush(variable.Type) @@ -399,3 +415,11 @@ func (v *TypeCheck) StackPop() ast.Type { x, v.Stack = v.Stack[len(v.Stack)-1], v.Stack[:len(v.Stack)-1] return x } + +func (v *TypeCheck) StackPeek() ast.Type { + if len(v.Stack) == 0 { + return ast.TypeInvalid + } + + return v.Stack[len(v.Stack)-1] +} diff --git a/vendor/github.com/hashicorp/hil/convert.go b/vendor/github.com/hashicorp/hil/convert.go index 3841d1fb35..983b66baf9 100644 --- a/vendor/github.com/hashicorp/hil/convert.go +++ b/vendor/github.com/hashicorp/hil/convert.go @@ -8,6 +8,11 @@ import ( "github.com/mitchellh/mapstructure" ) +// UnknownValue is a sentinel value that can be used to denote +// that a value of a variable (or map element, list element, etc.) +// is unknown. This will always have the type ast.TypeUnknown. +const UnknownValue = "74D93920-ED26-11E3-AC10-0800200C9A66" + var hilMapstructureDecodeHookSlice []interface{} var hilMapstructureDecodeHookStringSlice []string var hilMapstructureDecodeHookMap map[string]interface{} @@ -48,6 +53,12 @@ func InterfaceToVariable(input interface{}) (ast.Variable, error) { var stringVal string if err := hilMapstructureWeakDecode(input, &stringVal); err == nil { + // Special case the unknown value to turn into "unknown" + if stringVal == UnknownValue { + return ast.Variable{Type: ast.TypeUnknown}, nil + } + + // Otherwise return the string value return ast.Variable{ Type: ast.TypeString, Value: stringVal, diff --git a/vendor/github.com/hashicorp/hil/eval.go b/vendor/github.com/hashicorp/hil/eval.go index de999480dc..f04e4e42af 100644 --- a/vendor/github.com/hashicorp/hil/eval.go +++ b/vendor/github.com/hashicorp/hil/eval.go @@ -2,6 +2,7 @@ package hil import ( "bytes" + "errors" "fmt" "sync" @@ -42,6 +43,10 @@ type EvaluationResult struct { // The error is described out of band in the accompanying error return value. var InvalidResult = EvaluationResult{Type: TypeInvalid, Value: nil} +// errExitUnknown is an internal error that when returned means the result +// is an unknown value. We use this for early exit. +var errExitUnknown = errors.New("unknown value") + func Eval(root ast.Node, config *EvalConfig) (EvaluationResult, error) { output, outputType, err := internalEval(root, config) if err != nil { @@ -72,6 +77,11 @@ func Eval(root ast.Node, config *EvalConfig) (EvaluationResult, error) { Type: TypeString, Value: output, }, nil + case ast.TypeUnknown: + return EvaluationResult{ + Type: TypeUnknown, + Value: UnknownValue, + }, nil default: return InvalidResult, fmt.Errorf("unknown type %s as interpolation output", outputType) } @@ -154,6 +164,12 @@ func (v *evalVisitor) Visit(root ast.Node) (interface{}, ast.Type, error) { result = new(ast.LiteralNode) } resultErr := v.err + if resultErr == errExitUnknown { + // This means the return value is unknown and we used the error + // as an early exit mechanism. Reset since the value on the stack + // should be the unknown value. + resultErr = nil + } // Clear everything else so we aren't just dangling v.Stack.Reset() @@ -188,6 +204,13 @@ func (v *evalVisitor) visit(raw ast.Node) ast.Node { Value: out, Typex: outType, }) + + if outType == ast.TypeUnknown { + // Halt immediately + v.err = errExitUnknown + return raw + } + return raw } @@ -330,11 +353,15 @@ func (v *evalOutput) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, } // Special case the single list and map - if len(nodes) == 1 && nodes[0].Typex == ast.TypeList { - return nodes[0].Value, ast.TypeList, nil - } - if len(nodes) == 1 && nodes[0].Typex == ast.TypeMap { - return nodes[0].Value, ast.TypeMap, nil + if len(nodes) == 1 { + switch t := nodes[0].Typex; t { + case ast.TypeList: + fallthrough + case ast.TypeMap: + fallthrough + case ast.TypeUnknown: + return nodes[0].Value, t, nil + } } // Otherwise concatenate the strings @@ -362,5 +389,11 @@ func (v *evalVariableAccess) Eval(scope ast.Scope, _ *ast.Stack) (interface{}, a "unknown variable accessed: %s", v.Name) } + // Check if the variable contains any unknown types. If so, then + // mark it as unknown and return that type. + if ast.IsUnknown(variable) { + return nil, ast.TypeUnknown, nil + } + return variable.Value, variable.Type, nil } diff --git a/vendor/github.com/hashicorp/hil/eval_type.go b/vendor/github.com/hashicorp/hil/eval_type.go index c78a25bbde..faac4959b0 100644 --- a/vendor/github.com/hashicorp/hil/eval_type.go +++ b/vendor/github.com/hashicorp/hil/eval_type.go @@ -11,4 +11,5 @@ const ( TypeString EvalType = 1 << iota TypeList TypeMap + TypeUnknown ) diff --git a/vendor/vendor.json b/vendor/vendor.json index 1b8f0428d4..143945a178 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1386,16 +1386,16 @@ "revisionTime": "2016-11-09T00:00:27Z" }, { - "checksumSHA1": "RYz/9y1RMZfg+oMgEyJIWiSl1dU=", + "checksumSHA1": "mZhRldYjh9MAXzdi3zihMX0A/JU=", "path": "github.com/hashicorp/hil", - "revision": "3e00ff29065d64c0f8e9ef7efed82686bbda81ca", - "revisionTime": "2016-10-14T17:08:44Z" + "revision": "ce4ab742a9dd2bb6e55050337333b2c56666e5a0", + "revisionTime": "2016-10-27T15:25:34Z" }, { - "checksumSHA1": "WYIQ+nJPa191qpQIUsauF4wXYSw=", + "checksumSHA1": "FFroNUb6Nn6xUQJMsVDTb4Cqzo4=", "path": "github.com/hashicorp/hil/ast", - "revision": "3e00ff29065d64c0f8e9ef7efed82686bbda81ca", - "revisionTime": "2016-10-14T17:08:44Z" + "revision": "ce4ab742a9dd2bb6e55050337333b2c56666e5a0", + "revisionTime": "2016-10-27T15:25:34Z" }, { "path": "github.com/hashicorp/logutils",