diff --git a/go.mod b/go.mod index 09880a6c..683e9b61 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/vultr/packer-plugin-vultr go 1.16 require ( - github.com/hashicorp/hcl/v2 v2.10.0 + github.com/hashicorp/hcl/v2 v2.10.1 github.com/hashicorp/packer-plugin-sdk v0.2.3 github.com/vultr/govultr/v2 v2.5.1 github.com/zclconf/go-cty v1.9.0 diff --git a/go.sum b/go.sum index f3c72c74..2ebad75b 100644 --- a/go.sum +++ b/go.sum @@ -231,8 +231,9 @@ github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8 github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/hcl/v2 v2.10.0 h1:1S1UnuhDGlv3gRFV4+0EdwB+znNP5HmcGbIqwnSCByg= github.com/hashicorp/hcl/v2 v2.10.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= +github.com/hashicorp/hcl/v2 v2.10.1 h1:h4Xx4fsrRE26ohAk/1iGF/JBqRQbyUqu5Lvj60U54ys= +github.com/hashicorp/hcl/v2 v2.10.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= diff --git a/vendor/github.com/hashicorp/hcl/v2/CHANGELOG.md b/vendor/github.com/hashicorp/hcl/v2/CHANGELOG.md index 449187ca..816f2092 100644 --- a/vendor/github.com/hashicorp/hcl/v2/CHANGELOG.md +++ b/vendor/github.com/hashicorp/hcl/v2/CHANGELOG.md @@ -1,5 +1,12 @@ # HCL Changelog +## v2.10.1 (July 21, 2021) + +* dynblock: Decode unknown dynamic blocks in order to obtain any diagnostics even though the decoded value is not used ([#476](https://github.com/hashicorp/hcl/pull/476)) +* hclsyntax: Calling functions is now more robust in the face of an incorrectly-implemented function which returns a `function.ArgError` whose argument index is out of range for the length of the arguments. Previously this would often lead to a panic, but now it'll return a less-precice error message instead. Functions that return out-of-bounds argument indices still ought to be fixed so that the resulting error diagnostics can be as precise as possible. ([#472](https://github.com/hashicorp/hcl/pull/472)) +* hclsyntax: Ensure marks on unknown values are maintained when processing string templates. ([#478](https://github.com/hashicorp/hcl/pull/478)) +* hcl: Improved error messages for various common error situtions in `hcl.Index` and `hcl.GetAttr`. These are part of the implementation of indexing and attribute lookup in the native syntax expression language too, so the new error messages will apply to problems using those operators. ([#474](https://github.com/hashicorp/hcl/pull/474)) + ## v2.10.0 (April 20, 2021) ### Enhancements diff --git a/vendor/github.com/hashicorp/hcl/v2/hcldec/spec.go b/vendor/github.com/hashicorp/hcl/v2/hcldec/spec.go index afc06b67..42cb070d 100644 --- a/vendor/github.com/hashicorp/hcl/v2/hcldec/spec.go +++ b/vendor/github.com/hashicorp/hcl/v2/hcldec/spec.go @@ -468,6 +468,9 @@ func (s *BlockListSpec) decode(content *hcl.BodyContent, blockLabels []blockLabe continue } + val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false) + diags = append(diags, childDiags...) + if u, ok := childBlock.Body.(UnknownBody); ok { if u.Unknown() { // If any block Body is unknown, then the entire block value @@ -476,8 +479,6 @@ func (s *BlockListSpec) decode(content *hcl.BodyContent, blockLabels []blockLabe } } - val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false) - diags = append(diags, childDiags...) elems = append(elems, val) sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)) } @@ -629,6 +630,9 @@ func (s *BlockTupleSpec) decode(content *hcl.BodyContent, blockLabels []blockLab continue } + val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false) + diags = append(diags, childDiags...) + if u, ok := childBlock.Body.(UnknownBody); ok { if u.Unknown() { // If any block Body is unknown, then the entire block value @@ -637,8 +641,6 @@ func (s *BlockTupleSpec) decode(content *hcl.BodyContent, blockLabels []blockLab } } - val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false) - diags = append(diags, childDiags...) elems = append(elems, val) sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)) } @@ -751,6 +753,9 @@ func (s *BlockSetSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel continue } + val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false) + diags = append(diags, childDiags...) + if u, ok := childBlock.Body.(UnknownBody); ok { if u.Unknown() { // If any block Body is unknown, then the entire block value @@ -759,8 +764,6 @@ func (s *BlockSetSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel } } - val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false) - diags = append(diags, childDiags...) elems = append(elems, val) sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)) } diff --git a/vendor/github.com/hashicorp/hcl/v2/ops.go b/vendor/github.com/hashicorp/hcl/v2/ops.go index 597571d5..47dc02ad 100644 --- a/vendor/github.com/hashicorp/hcl/v2/ops.go +++ b/vendor/github.com/hashicorp/hcl/v2/ops.go @@ -21,6 +21,8 @@ import ( // though nil can be provided if the calling application is going to // ignore the subject of the returned diagnostics anyway. func Index(collection, key cty.Value, srcRange *Range) (cty.Value, Diagnostics) { + const invalidIndex = "Invalid index" + if collection.IsNull() { return cty.DynamicVal, Diagnostics{ { @@ -35,7 +37,7 @@ func Index(collection, key cty.Value, srcRange *Range) (cty.Value, Diagnostics) return cty.DynamicVal, Diagnostics{ { Severity: DiagError, - Summary: "Invalid index", + Summary: invalidIndex, Detail: "Can't use a null value as an indexing key.", Subject: srcRange, }, @@ -66,7 +68,7 @@ func Index(collection, key cty.Value, srcRange *Range) (cty.Value, Diagnostics) return cty.DynamicVal, Diagnostics{ { Severity: DiagError, - Summary: "Invalid index", + Summary: invalidIndex, Detail: fmt.Sprintf( "The given key does not identify an element in this collection value: %s.", keyErr.Error(), @@ -88,32 +90,84 @@ func Index(collection, key cty.Value, srcRange *Range) (cty.Value, Diagnostics) } } if has.False() { - // We have a more specialized error message for the situation of - // using a fractional number to index into a sequence, because - // that will tend to happen if the user is trying to use division - // to calculate an index and not realizing that HCL does float - // division rather than integer division. if (ty.IsListType() || ty.IsTupleType()) && key.Type().Equals(cty.Number) { if key.IsKnown() && !key.IsNull() { + // NOTE: we don't know what any marks might've represented + // up at the calling application layer, so we must avoid + // showing the literal number value in these error messages + // in case the mark represents something important, such as + // a value being "sensitive". key, _ := key.Unmark() bf := key.AsBigFloat() if _, acc := bf.Int(nil); acc != big.Exact { + // We have a more specialized error message for the + // situation of using a fractional number to index into + // a sequence, because that will tend to happen if the + // user is trying to use division to calculate an index + // and not realizing that HCL does float division + // rather than integer division. + return cty.DynamicVal, Diagnostics{ + { + Severity: DiagError, + Summary: invalidIndex, + Detail: "The given key does not identify an element in this collection value: indexing a sequence requires a whole number, but the given index has a fractional part.", + Subject: srcRange, + }, + } + } + + if bf.Sign() < 0 { + // Some other languages allow negative indices to + // select "backwards" from the end of the sequence, + // but HCL doesn't do that in order to give better + // feedback if a dynamic index is calculated + // incorrectly. return cty.DynamicVal, Diagnostics{ { Severity: DiagError, - Summary: "Invalid index", - Detail: fmt.Sprintf("The given key does not identify an element in this collection value: indexing a sequence requires a whole number, but the given index (%g) has a fractional part.", bf), + Summary: invalidIndex, + Detail: "The given key does not identify an element in this collection value: a negative number is not a valid index for a sequence.", Subject: srcRange, }, } } + if lenVal := collection.Length(); lenVal.IsKnown() && !lenVal.IsMarked() { + // Length always returns a number, and we already + // checked that it's a known number, so this is safe. + lenBF := lenVal.AsBigFloat() + var result big.Float + result.Sub(bf, lenBF) + if result.Sign() < 1 { + if lenBF.Sign() == 0 { + return cty.DynamicVal, Diagnostics{ + { + Severity: DiagError, + Summary: invalidIndex, + Detail: "The given key does not identify an element in this collection value: the collection has no elements.", + Subject: srcRange, + }, + } + } else { + return cty.DynamicVal, Diagnostics{ + { + Severity: DiagError, + Summary: invalidIndex, + Detail: "The given key does not identify an element in this collection value: the given index is greater than or equal to the length of the collection.", + Subject: srcRange, + }, + } + } + } + } } } + // If this is not one of the special situations we handled above + // then we'll fall back on a very generic message. return cty.DynamicVal, Diagnostics{ { Severity: DiagError, - Summary: "Invalid index", + Summary: invalidIndex, Detail: "The given key does not identify an element in this collection value.", Subject: srcRange, }, @@ -123,12 +177,13 @@ func Index(collection, key cty.Value, srcRange *Range) (cty.Value, Diagnostics) return collection.Index(key), nil case ty.IsObjectType(): + wasNumber := key.Type() == cty.Number key, keyErr := convert.Convert(key, cty.String) if keyErr != nil { return cty.DynamicVal, Diagnostics{ { Severity: DiagError, - Summary: "Invalid index", + Summary: invalidIndex, Detail: fmt.Sprintf( "The given key does not identify an element in this collection value: %s.", keyErr.Error(), @@ -148,11 +203,20 @@ func Index(collection, key cty.Value, srcRange *Range) (cty.Value, Diagnostics) attrName := key.AsString() if !ty.HasAttribute(attrName) { + var suggestion string + if wasNumber { + // We note this only as an addendum to an error we would've + // already returned anyway, because it is valid (albeit weird) + // to have an attribute whose name is just decimal digits + // and then access that attribute using a number whose + // decimal representation is the same digits. + suggestion = " An object only supports looking up attributes by name, not by numeric index." + } return cty.DynamicVal, Diagnostics{ { Severity: DiagError, - Summary: "Invalid index", - Detail: "The given key does not identify an element in this collection value.", + Summary: invalidIndex, + Detail: fmt.Sprintf("The given key does not identify an element in this collection value.%s", suggestion), Subject: srcRange, }, } @@ -160,11 +224,21 @@ func Index(collection, key cty.Value, srcRange *Range) (cty.Value, Diagnostics) return collection.GetAttr(attrName), nil + case ty.IsSetType(): + return cty.DynamicVal, Diagnostics{ + { + Severity: DiagError, + Summary: invalidIndex, + Detail: "Elements of a set are identified only by their value and don't have any separate index or key to select with, so it's only possible to perform operations across all elements of the set.", + Subject: srcRange, + }, + } + default: return cty.DynamicVal, Diagnostics{ { Severity: DiagError, - Summary: "Invalid index", + Summary: invalidIndex, Detail: "This value does not have any indices.", Subject: srcRange, }, @@ -197,6 +271,8 @@ func GetAttr(obj cty.Value, attrName string, srcRange *Range) (cty.Value, Diagno } } + const unsupportedAttr = "Unsupported attribute" + ty := obj.Type() switch { case ty.IsObjectType(): @@ -204,7 +280,7 @@ func GetAttr(obj cty.Value, attrName string, srcRange *Range) (cty.Value, Diagno return cty.DynamicVal, Diagnostics{ { Severity: DiagError, - Summary: "Unsupported attribute", + Summary: unsupportedAttr, Detail: fmt.Sprintf("This object does not have an attribute named %q.", attrName), Subject: srcRange, }, @@ -241,11 +317,69 @@ func GetAttr(obj cty.Value, attrName string, srcRange *Range) (cty.Value, Diagno return obj.Index(idx), nil case ty == cty.DynamicPseudoType: return cty.DynamicVal, nil + case ty.IsListType() && ty.ElementType().IsObjectType(): + // It seems a common mistake to try to access attributes on a whole + // list of objects rather than on a specific individual element, so + // we have some extra hints for that case. + + switch { + case ty.ElementType().HasAttribute(attrName): + // This is a very strong indication that the user mistook the list + // of objects for a single object, so we can be a little more + // direct in our suggestion here. + return cty.DynamicVal, Diagnostics{ + { + Severity: DiagError, + Summary: unsupportedAttr, + Detail: fmt.Sprintf("Can't access attributes on a list of objects. Did you mean to access attribute %q for a specific element of the list, or across all elements of the list?", attrName), + Subject: srcRange, + }, + } + default: + return cty.DynamicVal, Diagnostics{ + { + Severity: DiagError, + Summary: unsupportedAttr, + Detail: "Can't access attributes on a list of objects. Did you mean to access an attribute for a specific element of the list, or across all elements of the list?", + Subject: srcRange, + }, + } + } + + case ty.IsSetType() && ty.ElementType().IsObjectType(): + // This is similar to the previous case, but we can't give such a + // direct suggestion because there is no mechanism to select a single + // item from a set. + // We could potentially suggest using a for expression or splat + // operator here, but we typically don't get into syntax specifics + // in hcl.GetAttr suggestions because it's a general function used in + // various other situations, such as in application-specific operations + // that might have a more constraint set of alternative approaches. + + return cty.DynamicVal, Diagnostics{ + { + Severity: DiagError, + Summary: unsupportedAttr, + Detail: "Can't access attributes on a set of objects. Did you mean to access an attribute across all elements of the set?", + Subject: srcRange, + }, + } + + case ty.IsPrimitiveType(): + return cty.DynamicVal, Diagnostics{ + { + Severity: DiagError, + Summary: unsupportedAttr, + Detail: fmt.Sprintf("Can't access attributes on a primitive-typed value (%s).", ty.FriendlyName()), + Subject: srcRange, + }, + } + default: return cty.DynamicVal, Diagnostics{ { Severity: DiagError, - Summary: "Unsupported attribute", + Summary: unsupportedAttr, Detail: "This value does not have any attributes.", Subject: srcRange, }, diff --git a/vendor/modules.txt b/vendor/modules.txt index 600c3124..9a2a27b1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -152,7 +152,7 @@ github.com/hashicorp/hcl/hcl/token github.com/hashicorp/hcl/json/parser github.com/hashicorp/hcl/json/scanner github.com/hashicorp/hcl/json/token -# github.com/hashicorp/hcl/v2 v2.10.0 +# github.com/hashicorp/hcl/v2 v2.10.1 ## explicit github.com/hashicorp/hcl/v2 github.com/hashicorp/hcl/v2/ext/customdecode