diff options
author | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
---|---|---|
committer | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
commit | 107c1cdb09c575aa2f61d97f48d8587eb6bada4c (patch) | |
tree | ca7d008643efc555c388baeaf1d986e0b6b3e28c /vendor/github.com/hashicorp/terraform/config | |
parent | 844b5a68d8af4791755b8f0ad293cc99f5959183 (diff) | |
download | terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.gz terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.zst terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.zip |
Upgrade to 0.12
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config')
13 files changed, 1128 insertions, 458 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/decoder_spec.go b/vendor/github.com/hashicorp/terraform/config/configschema/decoder_spec.go deleted file mode 100644 index 2b1b0ca..0000000 --- a/vendor/github.com/hashicorp/terraform/config/configschema/decoder_spec.go +++ /dev/null | |||
@@ -1,97 +0,0 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "github.com/hashicorp/hcl2/hcldec" | ||
5 | "github.com/zclconf/go-cty/cty" | ||
6 | ) | ||
7 | |||
8 | var mapLabelNames = []string{"key"} | ||
9 | |||
10 | // DecoderSpec returns a hcldec.Spec that can be used to decode a HCL Body | ||
11 | // using the facilities in the hcldec package. | ||
12 | // | ||
13 | // The returned specification is guaranteed to return a value of the same type | ||
14 | // returned by method ImpliedType, but it may contain null or unknown values if | ||
15 | // any of the block attributes are defined as optional and/or computed | ||
16 | // respectively. | ||
17 | func (b *Block) DecoderSpec() hcldec.Spec { | ||
18 | ret := hcldec.ObjectSpec{} | ||
19 | if b == nil { | ||
20 | return ret | ||
21 | } | ||
22 | |||
23 | for name, attrS := range b.Attributes { | ||
24 | switch { | ||
25 | case attrS.Computed && attrS.Optional: | ||
26 | // In this special case we use an unknown value as a default | ||
27 | // to get the intended behavior that the result is computed | ||
28 | // unless it has been explicitly set in config. | ||
29 | ret[name] = &hcldec.DefaultSpec{ | ||
30 | Primary: &hcldec.AttrSpec{ | ||
31 | Name: name, | ||
32 | Type: attrS.Type, | ||
33 | }, | ||
34 | Default: &hcldec.LiteralSpec{ | ||
35 | Value: cty.UnknownVal(attrS.Type), | ||
36 | }, | ||
37 | } | ||
38 | case attrS.Computed: | ||
39 | ret[name] = &hcldec.LiteralSpec{ | ||
40 | Value: cty.UnknownVal(attrS.Type), | ||
41 | } | ||
42 | default: | ||
43 | ret[name] = &hcldec.AttrSpec{ | ||
44 | Name: name, | ||
45 | Type: attrS.Type, | ||
46 | Required: attrS.Required, | ||
47 | } | ||
48 | } | ||
49 | } | ||
50 | |||
51 | for name, blockS := range b.BlockTypes { | ||
52 | if _, exists := ret[name]; exists { | ||
53 | // This indicates an invalid schema, since it's not valid to | ||
54 | // define both an attribute and a block type of the same name. | ||
55 | // However, we don't raise this here since it's checked by | ||
56 | // InternalValidate. | ||
57 | continue | ||
58 | } | ||
59 | |||
60 | childSpec := blockS.Block.DecoderSpec() | ||
61 | |||
62 | switch blockS.Nesting { | ||
63 | case NestingSingle: | ||
64 | ret[name] = &hcldec.BlockSpec{ | ||
65 | TypeName: name, | ||
66 | Nested: childSpec, | ||
67 | Required: blockS.MinItems == 1 && blockS.MaxItems >= 1, | ||
68 | } | ||
69 | case NestingList: | ||
70 | ret[name] = &hcldec.BlockListSpec{ | ||
71 | TypeName: name, | ||
72 | Nested: childSpec, | ||
73 | MinItems: blockS.MinItems, | ||
74 | MaxItems: blockS.MaxItems, | ||
75 | } | ||
76 | case NestingSet: | ||
77 | ret[name] = &hcldec.BlockSetSpec{ | ||
78 | TypeName: name, | ||
79 | Nested: childSpec, | ||
80 | MinItems: blockS.MinItems, | ||
81 | MaxItems: blockS.MaxItems, | ||
82 | } | ||
83 | case NestingMap: | ||
84 | ret[name] = &hcldec.BlockMapSpec{ | ||
85 | TypeName: name, | ||
86 | Nested: childSpec, | ||
87 | LabelNames: mapLabelNames, | ||
88 | } | ||
89 | default: | ||
90 | // Invalid nesting type is just ignored. It's checked by | ||
91 | // InternalValidate. | ||
92 | continue | ||
93 | } | ||
94 | } | ||
95 | |||
96 | return ret | ||
97 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/doc.go b/vendor/github.com/hashicorp/terraform/config/configschema/doc.go deleted file mode 100644 index caf8d73..0000000 --- a/vendor/github.com/hashicorp/terraform/config/configschema/doc.go +++ /dev/null | |||
@@ -1,14 +0,0 @@ | |||
1 | // Package configschema contains types for describing the expected structure | ||
2 | // of a configuration block whose shape is not known until runtime. | ||
3 | // | ||
4 | // For example, this is used to describe the expected contents of a resource | ||
5 | // configuration block, which is defined by the corresponding provider plugin | ||
6 | // and thus not compiled into Terraform core. | ||
7 | // | ||
8 | // A configschema primarily describes the shape of configuration, but it is | ||
9 | // also suitable for use with other structures derived from the configuration, | ||
10 | // such as the cached state of a resource or a resource diff. | ||
11 | // | ||
12 | // This package should not be confused with the package helper/schema, which | ||
13 | // is the higher-level helper library used to implement providers themselves. | ||
14 | package configschema | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/implied_type.go b/vendor/github.com/hashicorp/terraform/config/configschema/implied_type.go deleted file mode 100644 index 67324eb..0000000 --- a/vendor/github.com/hashicorp/terraform/config/configschema/implied_type.go +++ /dev/null | |||
@@ -1,21 +0,0 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "github.com/hashicorp/hcl2/hcldec" | ||
5 | "github.com/zclconf/go-cty/cty" | ||
6 | ) | ||
7 | |||
8 | // ImpliedType returns the cty.Type that would result from decoding a | ||
9 | // configuration block using the receiving block schema. | ||
10 | // | ||
11 | // ImpliedType always returns a result, even if the given schema is | ||
12 | // inconsistent. Code that creates configschema.Block objects should be | ||
13 | // tested using the InternalValidate method to detect any inconsistencies | ||
14 | // that would cause this method to fall back on defaults and assumptions. | ||
15 | func (b *Block) ImpliedType() cty.Type { | ||
16 | if b == nil { | ||
17 | return cty.EmptyObject | ||
18 | } | ||
19 | |||
20 | return hcldec.ImpliedType(b.DecoderSpec()) | ||
21 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/internal_validate.go b/vendor/github.com/hashicorp/terraform/config/configschema/internal_validate.go deleted file mode 100644 index 33cbe88..0000000 --- a/vendor/github.com/hashicorp/terraform/config/configschema/internal_validate.go +++ /dev/null | |||
@@ -1,92 +0,0 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "regexp" | ||
6 | |||
7 | "github.com/zclconf/go-cty/cty" | ||
8 | |||
9 | multierror "github.com/hashicorp/go-multierror" | ||
10 | ) | ||
11 | |||
12 | var validName = regexp.MustCompile(`^[a-z0-9_]+$`) | ||
13 | |||
14 | // InternalValidate returns an error if the receiving block and its child | ||
15 | // schema definitions have any consistencies with the documented rules for | ||
16 | // valid schema. | ||
17 | // | ||
18 | // This is intended to be used within unit tests to detect when a given | ||
19 | // schema is invalid. | ||
20 | func (b *Block) InternalValidate() error { | ||
21 | if b == nil { | ||
22 | return fmt.Errorf("top-level block schema is nil") | ||
23 | } | ||
24 | return b.internalValidate("", nil) | ||
25 | |||
26 | } | ||
27 | |||
28 | func (b *Block) internalValidate(prefix string, err error) error { | ||
29 | for name, attrS := range b.Attributes { | ||
30 | if attrS == nil { | ||
31 | err = multierror.Append(err, fmt.Errorf("%s%s: attribute schema is nil", prefix, name)) | ||
32 | continue | ||
33 | } | ||
34 | if !validName.MatchString(name) { | ||
35 | err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name)) | ||
36 | } | ||
37 | if attrS.Optional == false && attrS.Required == false && attrS.Computed == false { | ||
38 | err = multierror.Append(err, fmt.Errorf("%s%s: must set Optional, Required or Computed", prefix, name)) | ||
39 | } | ||
40 | if attrS.Optional && attrS.Required { | ||
41 | err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Optional and Required", prefix, name)) | ||
42 | } | ||
43 | if attrS.Computed && attrS.Required { | ||
44 | err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Computed and Required", prefix, name)) | ||
45 | } | ||
46 | if attrS.Type == cty.NilType { | ||
47 | err = multierror.Append(err, fmt.Errorf("%s%s: Type must be set to something other than cty.NilType", prefix, name)) | ||
48 | } | ||
49 | } | ||
50 | |||
51 | for name, blockS := range b.BlockTypes { | ||
52 | if blockS == nil { | ||
53 | err = multierror.Append(err, fmt.Errorf("%s%s: block schema is nil", prefix, name)) | ||
54 | continue | ||
55 | } | ||
56 | |||
57 | if _, isAttr := b.Attributes[name]; isAttr { | ||
58 | err = multierror.Append(err, fmt.Errorf("%s%s: name defined as both attribute and child block type", prefix, name)) | ||
59 | } else if !validName.MatchString(name) { | ||
60 | err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name)) | ||
61 | } | ||
62 | |||
63 | if blockS.MinItems < 0 || blockS.MaxItems < 0 { | ||
64 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must both be greater than zero", prefix, name)) | ||
65 | } | ||
66 | |||
67 | switch blockS.Nesting { | ||
68 | case NestingSingle: | ||
69 | switch { | ||
70 | case blockS.MinItems != blockS.MaxItems: | ||
71 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must match in NestingSingle mode", prefix, name)) | ||
72 | case blockS.MinItems < 0 || blockS.MinItems > 1: | ||
73 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must be set to either 0 or 1 in NestingSingle mode", prefix, name)) | ||
74 | } | ||
75 | case NestingList, NestingSet: | ||
76 | if blockS.MinItems > blockS.MaxItems && blockS.MaxItems != 0 { | ||
77 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems must be less than or equal to MaxItems in %s mode", prefix, name, blockS.Nesting)) | ||
78 | } | ||
79 | case NestingMap: | ||
80 | if blockS.MinItems != 0 || blockS.MaxItems != 0 { | ||
81 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must both be 0 in NestingMap mode", prefix, name)) | ||
82 | } | ||
83 | default: | ||
84 | err = multierror.Append(err, fmt.Errorf("%s%s: invalid nesting mode %s", prefix, name, blockS.Nesting)) | ||
85 | } | ||
86 | |||
87 | subPrefix := prefix + name + "." | ||
88 | err = blockS.Block.internalValidate(subPrefix, err) | ||
89 | } | ||
90 | |||
91 | return err | ||
92 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/nestingmode_string.go b/vendor/github.com/hashicorp/terraform/config/configschema/nestingmode_string.go deleted file mode 100644 index 6cb9313..0000000 --- a/vendor/github.com/hashicorp/terraform/config/configschema/nestingmode_string.go +++ /dev/null | |||
@@ -1,16 +0,0 @@ | |||
1 | // Code generated by "stringer -type=NestingMode"; DO NOT EDIT. | ||
2 | |||
3 | package configschema | ||
4 | |||
5 | import "strconv" | ||
6 | |||
7 | const _NestingMode_name = "nestingModeInvalidNestingSingleNestingListNestingSetNestingMap" | ||
8 | |||
9 | var _NestingMode_index = [...]uint8{0, 18, 31, 42, 52, 62} | ||
10 | |||
11 | func (i NestingMode) String() string { | ||
12 | if i < 0 || i >= NestingMode(len(_NestingMode_index)-1) { | ||
13 | return "NestingMode(" + strconv.FormatInt(int64(i), 10) + ")" | ||
14 | } | ||
15 | return _NestingMode_name[_NestingMode_index[i]:_NestingMode_index[i+1]] | ||
16 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/schema.go b/vendor/github.com/hashicorp/terraform/config/configschema/schema.go deleted file mode 100644 index 9a8ee55..0000000 --- a/vendor/github.com/hashicorp/terraform/config/configschema/schema.go +++ /dev/null | |||
@@ -1,107 +0,0 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "github.com/zclconf/go-cty/cty" | ||
5 | ) | ||
6 | |||
7 | // Block represents a configuration block. | ||
8 | // | ||
9 | // "Block" here is a logical grouping construct, though it happens to map | ||
10 | // directly onto the physical block syntax of Terraform's native configuration | ||
11 | // syntax. It may be a more a matter of convention in other syntaxes, such as | ||
12 | // JSON. | ||
13 | // | ||
14 | // When converted to a value, a Block always becomes an instance of an object | ||
15 | // type derived from its defined attributes and nested blocks | ||
16 | type Block struct { | ||
17 | // Attributes describes any attributes that may appear directly inside | ||
18 | // the block. | ||
19 | Attributes map[string]*Attribute | ||
20 | |||
21 | // BlockTypes describes any nested block types that may appear directly | ||
22 | // inside the block. | ||
23 | BlockTypes map[string]*NestedBlock | ||
24 | } | ||
25 | |||
26 | // Attribute represents a configuration attribute, within a block. | ||
27 | type Attribute struct { | ||
28 | // Type is a type specification that the attribute's value must conform to. | ||
29 | Type cty.Type | ||
30 | |||
31 | // Required, if set to true, specifies that an omitted or null value is | ||
32 | // not permitted. | ||
33 | Required bool | ||
34 | |||
35 | // Optional, if set to true, specifies that an omitted or null value is | ||
36 | // permitted. This field conflicts with Required. | ||
37 | Optional bool | ||
38 | |||
39 | // Computed, if set to true, specifies that the value comes from the | ||
40 | // provider rather than from configuration. If combined with Optional, | ||
41 | // then the config may optionally provide an overridden value. | ||
42 | Computed bool | ||
43 | |||
44 | // Sensitive, if set to true, indicates that an attribute may contain | ||
45 | // sensitive information. | ||
46 | // | ||
47 | // At present nothing is done with this information, but callers are | ||
48 | // encouraged to set it where appropriate so that it may be used in the | ||
49 | // future to help Terraform mask sensitive information. (Terraform | ||
50 | // currently achieves this in a limited sense via other mechanisms.) | ||
51 | Sensitive bool | ||
52 | } | ||
53 | |||
54 | // NestedBlock represents the embedding of one block within another. | ||
55 | type NestedBlock struct { | ||
56 | // Block is the description of the block that's nested. | ||
57 | Block | ||
58 | |||
59 | // Nesting provides the nesting mode for the child block, which determines | ||
60 | // how many instances of the block are allowed, how many labels it expects, | ||
61 | // and how the resulting data will be converted into a data structure. | ||
62 | Nesting NestingMode | ||
63 | |||
64 | // MinItems and MaxItems set, for the NestingList and NestingSet nesting | ||
65 | // modes, lower and upper limits on the number of child blocks allowed | ||
66 | // of the given type. If both are left at zero, no limit is applied. | ||
67 | // | ||
68 | // As a special case, both values can be set to 1 for NestingSingle in | ||
69 | // order to indicate that a particular single block is required. | ||
70 | // | ||
71 | // These fields are ignored for other nesting modes and must both be left | ||
72 | // at zero. | ||
73 | MinItems, MaxItems int | ||
74 | } | ||
75 | |||
76 | // NestingMode is an enumeration of modes for nesting blocks inside other | ||
77 | // blocks. | ||
78 | type NestingMode int | ||
79 | |||
80 | //go:generate stringer -type=NestingMode | ||
81 | |||
82 | const ( | ||
83 | nestingModeInvalid NestingMode = iota | ||
84 | |||
85 | // NestingSingle indicates that only a single instance of a given | ||
86 | // block type is permitted, with no labels, and its content should be | ||
87 | // provided directly as an object value. | ||
88 | NestingSingle | ||
89 | |||
90 | // NestingList indicates that multiple blocks of the given type are | ||
91 | // permitted, with no labels, and that their corresponding objects should | ||
92 | // be provided in a list. | ||
93 | NestingList | ||
94 | |||
95 | // NestingSet indicates that multiple blocks of the given type are | ||
96 | // permitted, with no labels, and that their corresponding objects should | ||
97 | // be provided in a set. | ||
98 | NestingSet | ||
99 | |||
100 | // NestingMap indicates that multiple blocks of the given type are | ||
101 | // permitted, each with a single label, and that their corresponding | ||
102 | // objects should be provided in a map whose keys are the labels. | ||
103 | // | ||
104 | // It's an error, therefore, to use the same label value on multiple | ||
105 | // blocks. | ||
106 | NestingMap | ||
107 | ) | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/hcl2shim/flatmap.go b/vendor/github.com/hashicorp/terraform/config/hcl2shim/flatmap.go new file mode 100644 index 0000000..bb4228d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/hcl2shim/flatmap.go | |||
@@ -0,0 +1,424 @@ | |||
1 | package hcl2shim | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "strconv" | ||
6 | "strings" | ||
7 | |||
8 | "github.com/zclconf/go-cty/cty/convert" | ||
9 | |||
10 | "github.com/zclconf/go-cty/cty" | ||
11 | ) | ||
12 | |||
13 | // FlatmapValueFromHCL2 converts a value from HCL2 (really, from the cty dynamic | ||
14 | // types library that HCL2 uses) to a map compatible with what would be | ||
15 | // produced by the "flatmap" package. | ||
16 | // | ||
17 | // The type of the given value informs the structure of the resulting map. | ||
18 | // The value must be of an object type or this function will panic. | ||
19 | // | ||
20 | // Flatmap values can only represent maps when they are of primitive types, | ||
21 | // so the given value must not have any maps of complex types or the result | ||
22 | // is undefined. | ||
23 | func FlatmapValueFromHCL2(v cty.Value) map[string]string { | ||
24 | if v.IsNull() { | ||
25 | return nil | ||
26 | } | ||
27 | |||
28 | if !v.Type().IsObjectType() { | ||
29 | panic(fmt.Sprintf("HCL2ValueFromFlatmap called on %#v", v.Type())) | ||
30 | } | ||
31 | |||
32 | m := make(map[string]string) | ||
33 | flatmapValueFromHCL2Map(m, "", v) | ||
34 | return m | ||
35 | } | ||
36 | |||
37 | func flatmapValueFromHCL2Value(m map[string]string, key string, val cty.Value) { | ||
38 | ty := val.Type() | ||
39 | switch { | ||
40 | case ty.IsPrimitiveType() || ty == cty.DynamicPseudoType: | ||
41 | flatmapValueFromHCL2Primitive(m, key, val) | ||
42 | case ty.IsObjectType() || ty.IsMapType(): | ||
43 | flatmapValueFromHCL2Map(m, key+".", val) | ||
44 | case ty.IsTupleType() || ty.IsListType() || ty.IsSetType(): | ||
45 | flatmapValueFromHCL2Seq(m, key+".", val) | ||
46 | default: | ||
47 | panic(fmt.Sprintf("cannot encode %s to flatmap", ty.FriendlyName())) | ||
48 | } | ||
49 | } | ||
50 | |||
51 | func flatmapValueFromHCL2Primitive(m map[string]string, key string, val cty.Value) { | ||
52 | if !val.IsKnown() { | ||
53 | m[key] = UnknownVariableValue | ||
54 | return | ||
55 | } | ||
56 | if val.IsNull() { | ||
57 | // Omit entirely | ||
58 | return | ||
59 | } | ||
60 | |||
61 | var err error | ||
62 | val, err = convert.Convert(val, cty.String) | ||
63 | if err != nil { | ||
64 | // Should not be possible, since all primitive types can convert to string. | ||
65 | panic(fmt.Sprintf("invalid primitive encoding to flatmap: %s", err)) | ||
66 | } | ||
67 | m[key] = val.AsString() | ||
68 | } | ||
69 | |||
70 | func flatmapValueFromHCL2Map(m map[string]string, prefix string, val cty.Value) { | ||
71 | if val.IsNull() { | ||
72 | // Omit entirely | ||
73 | return | ||
74 | } | ||
75 | if !val.IsKnown() { | ||
76 | switch { | ||
77 | case val.Type().IsObjectType(): | ||
78 | // Whole objects can't be unknown in flatmap, so instead we'll | ||
79 | // just write all of the attribute values out as unknown. | ||
80 | for name, aty := range val.Type().AttributeTypes() { | ||
81 | flatmapValueFromHCL2Value(m, prefix+name, cty.UnknownVal(aty)) | ||
82 | } | ||
83 | default: | ||
84 | m[prefix+"%"] = UnknownVariableValue | ||
85 | } | ||
86 | return | ||
87 | } | ||
88 | |||
89 | len := 0 | ||
90 | for it := val.ElementIterator(); it.Next(); { | ||
91 | ak, av := it.Element() | ||
92 | name := ak.AsString() | ||
93 | flatmapValueFromHCL2Value(m, prefix+name, av) | ||
94 | len++ | ||
95 | } | ||
96 | if !val.Type().IsObjectType() { // objects don't have an explicit count included, since their attribute count is fixed | ||
97 | m[prefix+"%"] = strconv.Itoa(len) | ||
98 | } | ||
99 | } | ||
100 | |||
101 | func flatmapValueFromHCL2Seq(m map[string]string, prefix string, val cty.Value) { | ||
102 | if val.IsNull() { | ||
103 | // Omit entirely | ||
104 | return | ||
105 | } | ||
106 | if !val.IsKnown() { | ||
107 | m[prefix+"#"] = UnknownVariableValue | ||
108 | return | ||
109 | } | ||
110 | |||
111 | // For sets this won't actually generate exactly what helper/schema would've | ||
112 | // generated, because we don't have access to the set key function it | ||
113 | // would've used. However, in practice it doesn't actually matter what the | ||
114 | // keys are as long as they are unique, so we'll just generate sequential | ||
115 | // indexes for them as if it were a list. | ||
116 | // | ||
117 | // An important implication of this, however, is that the set ordering will | ||
118 | // not be consistent across mutations and so different keys may be assigned | ||
119 | // to the same value when round-tripping. Since this shim is intended to | ||
120 | // be short-lived and not used for round-tripping, we accept this. | ||
121 | i := 0 | ||
122 | for it := val.ElementIterator(); it.Next(); { | ||
123 | _, av := it.Element() | ||
124 | key := prefix + strconv.Itoa(i) | ||
125 | flatmapValueFromHCL2Value(m, key, av) | ||
126 | i++ | ||
127 | } | ||
128 | m[prefix+"#"] = strconv.Itoa(i) | ||
129 | } | ||
130 | |||
131 | // HCL2ValueFromFlatmap converts a map compatible with what would be produced | ||
132 | // by the "flatmap" package to a HCL2 (really, the cty dynamic types library | ||
133 | // that HCL2 uses) object type. | ||
134 | // | ||
135 | // The intended result type must be provided in order to guide how the | ||
136 | // map contents are decoded. This must be an object type or this function | ||
137 | // will panic. | ||
138 | // | ||
139 | // Flatmap values can only represent maps when they are of primitive types, | ||
140 | // so the given type must not have any maps of complex types or the result | ||
141 | // is undefined. | ||
142 | // | ||
143 | // The result may contain null values if the given map does not contain keys | ||
144 | // for all of the different key paths implied by the given type. | ||
145 | func HCL2ValueFromFlatmap(m map[string]string, ty cty.Type) (cty.Value, error) { | ||
146 | if m == nil { | ||
147 | return cty.NullVal(ty), nil | ||
148 | } | ||
149 | if !ty.IsObjectType() { | ||
150 | panic(fmt.Sprintf("HCL2ValueFromFlatmap called on %#v", ty)) | ||
151 | } | ||
152 | |||
153 | return hcl2ValueFromFlatmapObject(m, "", ty.AttributeTypes()) | ||
154 | } | ||
155 | |||
156 | func hcl2ValueFromFlatmapValue(m map[string]string, key string, ty cty.Type) (cty.Value, error) { | ||
157 | var val cty.Value | ||
158 | var err error | ||
159 | switch { | ||
160 | case ty.IsPrimitiveType(): | ||
161 | val, err = hcl2ValueFromFlatmapPrimitive(m, key, ty) | ||
162 | case ty.IsObjectType(): | ||
163 | val, err = hcl2ValueFromFlatmapObject(m, key+".", ty.AttributeTypes()) | ||
164 | case ty.IsTupleType(): | ||
165 | val, err = hcl2ValueFromFlatmapTuple(m, key+".", ty.TupleElementTypes()) | ||
166 | case ty.IsMapType(): | ||
167 | val, err = hcl2ValueFromFlatmapMap(m, key+".", ty) | ||
168 | case ty.IsListType(): | ||
169 | val, err = hcl2ValueFromFlatmapList(m, key+".", ty) | ||
170 | case ty.IsSetType(): | ||
171 | val, err = hcl2ValueFromFlatmapSet(m, key+".", ty) | ||
172 | default: | ||
173 | err = fmt.Errorf("cannot decode %s from flatmap", ty.FriendlyName()) | ||
174 | } | ||
175 | |||
176 | if err != nil { | ||
177 | return cty.DynamicVal, err | ||
178 | } | ||
179 | return val, nil | ||
180 | } | ||
181 | |||
182 | func hcl2ValueFromFlatmapPrimitive(m map[string]string, key string, ty cty.Type) (cty.Value, error) { | ||
183 | rawVal, exists := m[key] | ||
184 | if !exists { | ||
185 | return cty.NullVal(ty), nil | ||
186 | } | ||
187 | if rawVal == UnknownVariableValue { | ||
188 | return cty.UnknownVal(ty), nil | ||
189 | } | ||
190 | |||
191 | var err error | ||
192 | val := cty.StringVal(rawVal) | ||
193 | val, err = convert.Convert(val, ty) | ||
194 | if err != nil { | ||
195 | // This should never happen for _valid_ input, but flatmap data might | ||
196 | // be tampered with by the user and become invalid. | ||
197 | return cty.DynamicVal, fmt.Errorf("invalid value for %q in state: %s", key, err) | ||
198 | } | ||
199 | |||
200 | return val, nil | ||
201 | } | ||
202 | |||
203 | func hcl2ValueFromFlatmapObject(m map[string]string, prefix string, atys map[string]cty.Type) (cty.Value, error) { | ||
204 | vals := make(map[string]cty.Value) | ||
205 | for name, aty := range atys { | ||
206 | val, err := hcl2ValueFromFlatmapValue(m, prefix+name, aty) | ||
207 | if err != nil { | ||
208 | return cty.DynamicVal, err | ||
209 | } | ||
210 | vals[name] = val | ||
211 | } | ||
212 | return cty.ObjectVal(vals), nil | ||
213 | } | ||
214 | |||
215 | func hcl2ValueFromFlatmapTuple(m map[string]string, prefix string, etys []cty.Type) (cty.Value, error) { | ||
216 | var vals []cty.Value | ||
217 | |||
218 | // if the container is unknown, there is no count string | ||
219 | listName := strings.TrimRight(prefix, ".") | ||
220 | if m[listName] == UnknownVariableValue { | ||
221 | return cty.UnknownVal(cty.Tuple(etys)), nil | ||
222 | } | ||
223 | |||
224 | countStr, exists := m[prefix+"#"] | ||
225 | if !exists { | ||
226 | return cty.NullVal(cty.Tuple(etys)), nil | ||
227 | } | ||
228 | if countStr == UnknownVariableValue { | ||
229 | return cty.UnknownVal(cty.Tuple(etys)), nil | ||
230 | } | ||
231 | |||
232 | count, err := strconv.Atoi(countStr) | ||
233 | if err != nil { | ||
234 | return cty.DynamicVal, fmt.Errorf("invalid count value for %q in state: %s", prefix, err) | ||
235 | } | ||
236 | if count != len(etys) { | ||
237 | return cty.DynamicVal, fmt.Errorf("wrong number of values for %q in state: got %d, but need %d", prefix, count, len(etys)) | ||
238 | } | ||
239 | |||
240 | vals = make([]cty.Value, len(etys)) | ||
241 | for i, ety := range etys { | ||
242 | key := prefix + strconv.Itoa(i) | ||
243 | val, err := hcl2ValueFromFlatmapValue(m, key, ety) | ||
244 | if err != nil { | ||
245 | return cty.DynamicVal, err | ||
246 | } | ||
247 | vals[i] = val | ||
248 | } | ||
249 | return cty.TupleVal(vals), nil | ||
250 | } | ||
251 | |||
252 | func hcl2ValueFromFlatmapMap(m map[string]string, prefix string, ty cty.Type) (cty.Value, error) { | ||
253 | vals := make(map[string]cty.Value) | ||
254 | ety := ty.ElementType() | ||
255 | |||
256 | // if the container is unknown, there is no count string | ||
257 | listName := strings.TrimRight(prefix, ".") | ||
258 | if m[listName] == UnknownVariableValue { | ||
259 | return cty.UnknownVal(ty), nil | ||
260 | } | ||
261 | |||
262 | // We actually don't really care about the "count" of a map for our | ||
263 | // purposes here, but we do need to check if it _exists_ in order to | ||
264 | // recognize the difference between null (not set at all) and empty. | ||
265 | if strCount, exists := m[prefix+"%"]; !exists { | ||
266 | return cty.NullVal(ty), nil | ||
267 | } else if strCount == UnknownVariableValue { | ||
268 | return cty.UnknownVal(ty), nil | ||
269 | } | ||
270 | |||
271 | for fullKey := range m { | ||
272 | if !strings.HasPrefix(fullKey, prefix) { | ||
273 | continue | ||
274 | } | ||
275 | |||
276 | // The flatmap format doesn't allow us to distinguish between keys | ||
277 | // that contain periods and nested objects, so by convention a | ||
278 | // map is only ever of primitive type in flatmap, and we just assume | ||
279 | // that the remainder of the raw key (dots and all) is the key we | ||
280 | // want in the result value. | ||
281 | key := fullKey[len(prefix):] | ||
282 | if key == "%" { | ||
283 | // Ignore the "count" key | ||
284 | continue | ||
285 | } | ||
286 | |||
287 | val, err := hcl2ValueFromFlatmapValue(m, fullKey, ety) | ||
288 | if err != nil { | ||
289 | return cty.DynamicVal, err | ||
290 | } | ||
291 | vals[key] = val | ||
292 | } | ||
293 | |||
294 | if len(vals) == 0 { | ||
295 | return cty.MapValEmpty(ety), nil | ||
296 | } | ||
297 | return cty.MapVal(vals), nil | ||
298 | } | ||
299 | |||
300 | func hcl2ValueFromFlatmapList(m map[string]string, prefix string, ty cty.Type) (cty.Value, error) { | ||
301 | var vals []cty.Value | ||
302 | |||
303 | // if the container is unknown, there is no count string | ||
304 | listName := strings.TrimRight(prefix, ".") | ||
305 | if m[listName] == UnknownVariableValue { | ||
306 | return cty.UnknownVal(ty), nil | ||
307 | } | ||
308 | |||
309 | countStr, exists := m[prefix+"#"] | ||
310 | if !exists { | ||
311 | return cty.NullVal(ty), nil | ||
312 | } | ||
313 | if countStr == UnknownVariableValue { | ||
314 | return cty.UnknownVal(ty), nil | ||
315 | } | ||
316 | |||
317 | count, err := strconv.Atoi(countStr) | ||
318 | if err != nil { | ||
319 | return cty.DynamicVal, fmt.Errorf("invalid count value for %q in state: %s", prefix, err) | ||
320 | } | ||
321 | |||
322 | ety := ty.ElementType() | ||
323 | if count == 0 { | ||
324 | return cty.ListValEmpty(ety), nil | ||
325 | } | ||
326 | |||
327 | vals = make([]cty.Value, count) | ||
328 | for i := 0; i < count; i++ { | ||
329 | key := prefix + strconv.Itoa(i) | ||
330 | val, err := hcl2ValueFromFlatmapValue(m, key, ety) | ||
331 | if err != nil { | ||
332 | return cty.DynamicVal, err | ||
333 | } | ||
334 | vals[i] = val | ||
335 | } | ||
336 | |||
337 | return cty.ListVal(vals), nil | ||
338 | } | ||
339 | |||
340 | func hcl2ValueFromFlatmapSet(m map[string]string, prefix string, ty cty.Type) (cty.Value, error) { | ||
341 | var vals []cty.Value | ||
342 | ety := ty.ElementType() | ||
343 | |||
344 | // if the container is unknown, there is no count string | ||
345 | listName := strings.TrimRight(prefix, ".") | ||
346 | if m[listName] == UnknownVariableValue { | ||
347 | return cty.UnknownVal(ty), nil | ||
348 | } | ||
349 | |||
350 | strCount, exists := m[prefix+"#"] | ||
351 | if !exists { | ||
352 | return cty.NullVal(ty), nil | ||
353 | } else if strCount == UnknownVariableValue { | ||
354 | return cty.UnknownVal(ty), nil | ||
355 | } | ||
356 | |||
357 | // Keep track of keys we've seen, se we don't add the same set value | ||
358 | // multiple times. The cty.Set will normally de-duplicate values, but we may | ||
359 | // have unknown values that would not show as equivalent. | ||
360 | seen := map[string]bool{} | ||
361 | |||
362 | for fullKey := range m { | ||
363 | if !strings.HasPrefix(fullKey, prefix) { | ||
364 | continue | ||
365 | } | ||
366 | subKey := fullKey[len(prefix):] | ||
367 | if subKey == "#" { | ||
368 | // Ignore the "count" key | ||
369 | continue | ||
370 | } | ||
371 | key := fullKey | ||
372 | if dot := strings.IndexByte(subKey, '.'); dot != -1 { | ||
373 | key = fullKey[:dot+len(prefix)] | ||
374 | } | ||
375 | |||
376 | if seen[key] { | ||
377 | continue | ||
378 | } | ||
379 | |||
380 | seen[key] = true | ||
381 | |||
382 | // The flatmap format doesn't allow us to distinguish between keys | ||
383 | // that contain periods and nested objects, so by convention a | ||
384 | // map is only ever of primitive type in flatmap, and we just assume | ||
385 | // that the remainder of the raw key (dots and all) is the key we | ||
386 | // want in the result value. | ||
387 | |||
388 | val, err := hcl2ValueFromFlatmapValue(m, key, ety) | ||
389 | if err != nil { | ||
390 | return cty.DynamicVal, err | ||
391 | } | ||
392 | vals = append(vals, val) | ||
393 | } | ||
394 | |||
395 | if len(vals) == 0 && strCount == "1" { | ||
396 | // An empty set wouldn't be represented in the flatmap, so this must be | ||
397 | // a single empty object since the count is actually 1. | ||
398 | // Add an appropriately typed null value to the set. | ||
399 | var val cty.Value | ||
400 | switch { | ||
401 | case ety.IsMapType(): | ||
402 | val = cty.MapValEmpty(ety) | ||
403 | case ety.IsListType(): | ||
404 | val = cty.ListValEmpty(ety) | ||
405 | case ety.IsSetType(): | ||
406 | val = cty.SetValEmpty(ety) | ||
407 | case ety.IsObjectType(): | ||
408 | // TODO: cty.ObjectValEmpty | ||
409 | objectMap := map[string]cty.Value{} | ||
410 | for attr, ty := range ety.AttributeTypes() { | ||
411 | objectMap[attr] = cty.NullVal(ty) | ||
412 | } | ||
413 | val = cty.ObjectVal(objectMap) | ||
414 | default: | ||
415 | val = cty.NullVal(ety) | ||
416 | } | ||
417 | vals = append(vals, val) | ||
418 | |||
419 | } else if len(vals) == 0 { | ||
420 | return cty.SetValEmpty(ety), nil | ||
421 | } | ||
422 | |||
423 | return cty.SetVal(vals), nil | ||
424 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/hcl2shim/paths.go b/vendor/github.com/hashicorp/terraform/config/hcl2shim/paths.go new file mode 100644 index 0000000..3403c02 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/hcl2shim/paths.go | |||
@@ -0,0 +1,276 @@ | |||
1 | package hcl2shim | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "reflect" | ||
6 | "strconv" | ||
7 | "strings" | ||
8 | |||
9 | "github.com/zclconf/go-cty/cty" | ||
10 | ) | ||
11 | |||
12 | // RequiresReplace takes a list of flatmapped paths from a | ||
13 | // InstanceDiff.Attributes along with the corresponding cty.Type, and returns | ||
14 | // the list of the cty.Paths that are flagged as causing the resource | ||
15 | // replacement (RequiresNew). | ||
16 | // This will filter out redundant paths, paths that refer to flatmapped indexes | ||
17 | // (e.g. "#", "%"), and will return any changes within a set as the path to the | ||
18 | // set itself. | ||
19 | func RequiresReplace(attrs []string, ty cty.Type) ([]cty.Path, error) { | ||
20 | var paths []cty.Path | ||
21 | |||
22 | for _, attr := range attrs { | ||
23 | p, err := requiresReplacePath(attr, ty) | ||
24 | if err != nil { | ||
25 | return nil, err | ||
26 | } | ||
27 | |||
28 | paths = append(paths, p) | ||
29 | } | ||
30 | |||
31 | // now trim off any trailing paths that aren't GetAttrSteps, since only an | ||
32 | // attribute itself can require replacement | ||
33 | paths = trimPaths(paths) | ||
34 | |||
35 | // There may be redundant paths due to set elements or index attributes | ||
36 | // Do some ugly n^2 filtering, but these are always fairly small sets. | ||
37 | for i := 0; i < len(paths)-1; i++ { | ||
38 | for j := i + 1; j < len(paths); j++ { | ||
39 | if reflect.DeepEqual(paths[i], paths[j]) { | ||
40 | // swap the tail and slice it off | ||
41 | paths[j], paths[len(paths)-1] = paths[len(paths)-1], paths[j] | ||
42 | paths = paths[:len(paths)-1] | ||
43 | j-- | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | |||
48 | return paths, nil | ||
49 | } | ||
50 | |||
51 | // trimPaths removes any trailing steps that aren't of type GetAttrSet, since | ||
52 | // only an attribute itself can require replacement | ||
53 | func trimPaths(paths []cty.Path) []cty.Path { | ||
54 | var trimmed []cty.Path | ||
55 | for _, path := range paths { | ||
56 | path = trimPath(path) | ||
57 | if len(path) > 0 { | ||
58 | trimmed = append(trimmed, path) | ||
59 | } | ||
60 | } | ||
61 | return trimmed | ||
62 | } | ||
63 | |||
64 | func trimPath(path cty.Path) cty.Path { | ||
65 | for len(path) > 0 { | ||
66 | _, isGetAttr := path[len(path)-1].(cty.GetAttrStep) | ||
67 | if isGetAttr { | ||
68 | break | ||
69 | } | ||
70 | path = path[:len(path)-1] | ||
71 | } | ||
72 | return path | ||
73 | } | ||
74 | |||
75 | // requiresReplacePath takes a key from a flatmap along with the cty.Type | ||
76 | // describing the structure, and returns the cty.Path that would be used to | ||
77 | // reference the nested value in the data structure. | ||
78 | // This is used specifically to record the RequiresReplace attributes from a | ||
79 | // ResourceInstanceDiff. | ||
80 | func requiresReplacePath(k string, ty cty.Type) (cty.Path, error) { | ||
81 | if k == "" { | ||
82 | return nil, nil | ||
83 | } | ||
84 | if !ty.IsObjectType() { | ||
85 | panic(fmt.Sprintf("requires replace path on non-object type: %#v", ty)) | ||
86 | } | ||
87 | |||
88 | path, err := pathFromFlatmapKeyObject(k, ty.AttributeTypes()) | ||
89 | if err != nil { | ||
90 | return path, fmt.Errorf("[%s] %s", k, err) | ||
91 | } | ||
92 | return path, nil | ||
93 | } | ||
94 | |||
95 | func pathSplit(p string) (string, string) { | ||
96 | parts := strings.SplitN(p, ".", 2) | ||
97 | head := parts[0] | ||
98 | rest := "" | ||
99 | if len(parts) > 1 { | ||
100 | rest = parts[1] | ||
101 | } | ||
102 | return head, rest | ||
103 | } | ||
104 | |||
105 | func pathFromFlatmapKeyObject(key string, atys map[string]cty.Type) (cty.Path, error) { | ||
106 | k, rest := pathSplit(key) | ||
107 | |||
108 | path := cty.Path{cty.GetAttrStep{Name: k}} | ||
109 | |||
110 | ty, ok := atys[k] | ||
111 | if !ok { | ||
112 | return path, fmt.Errorf("attribute %q not found", k) | ||
113 | } | ||
114 | |||
115 | if rest == "" { | ||
116 | return path, nil | ||
117 | } | ||
118 | |||
119 | p, err := pathFromFlatmapKeyValue(rest, ty) | ||
120 | if err != nil { | ||
121 | return path, err | ||
122 | } | ||
123 | |||
124 | return append(path, p...), nil | ||
125 | } | ||
126 | |||
127 | func pathFromFlatmapKeyValue(key string, ty cty.Type) (cty.Path, error) { | ||
128 | var path cty.Path | ||
129 | var err error | ||
130 | |||
131 | switch { | ||
132 | case ty.IsPrimitiveType(): | ||
133 | err = fmt.Errorf("invalid step %q with type %#v", key, ty) | ||
134 | case ty.IsObjectType(): | ||
135 | path, err = pathFromFlatmapKeyObject(key, ty.AttributeTypes()) | ||
136 | case ty.IsTupleType(): | ||
137 | path, err = pathFromFlatmapKeyTuple(key, ty.TupleElementTypes()) | ||
138 | case ty.IsMapType(): | ||
139 | path, err = pathFromFlatmapKeyMap(key, ty) | ||
140 | case ty.IsListType(): | ||
141 | path, err = pathFromFlatmapKeyList(key, ty) | ||
142 | case ty.IsSetType(): | ||
143 | path, err = pathFromFlatmapKeySet(key, ty) | ||
144 | default: | ||
145 | err = fmt.Errorf("unrecognized type: %s", ty.FriendlyName()) | ||
146 | } | ||
147 | |||
148 | if err != nil { | ||
149 | return path, err | ||
150 | } | ||
151 | |||
152 | return path, nil | ||
153 | } | ||
154 | |||
155 | func pathFromFlatmapKeyTuple(key string, etys []cty.Type) (cty.Path, error) { | ||
156 | var path cty.Path | ||
157 | var err error | ||
158 | |||
159 | k, rest := pathSplit(key) | ||
160 | |||
161 | // we don't need to convert the index keys to paths | ||
162 | if k == "#" { | ||
163 | return path, nil | ||
164 | } | ||
165 | |||
166 | idx, err := strconv.Atoi(k) | ||
167 | if err != nil { | ||
168 | return path, err | ||
169 | } | ||
170 | |||
171 | path = cty.Path{cty.IndexStep{Key: cty.NumberIntVal(int64(idx))}} | ||
172 | |||
173 | if idx >= len(etys) { | ||
174 | return path, fmt.Errorf("index %s out of range in %#v", key, etys) | ||
175 | } | ||
176 | |||
177 | if rest == "" { | ||
178 | return path, nil | ||
179 | } | ||
180 | |||
181 | ty := etys[idx] | ||
182 | |||
183 | p, err := pathFromFlatmapKeyValue(rest, ty.ElementType()) | ||
184 | if err != nil { | ||
185 | return path, err | ||
186 | } | ||
187 | |||
188 | return append(path, p...), nil | ||
189 | } | ||
190 | |||
191 | func pathFromFlatmapKeyMap(key string, ty cty.Type) (cty.Path, error) { | ||
192 | var path cty.Path | ||
193 | var err error | ||
194 | |||
195 | k, rest := key, "" | ||
196 | if !ty.ElementType().IsPrimitiveType() { | ||
197 | k, rest = pathSplit(key) | ||
198 | } | ||
199 | |||
200 | // we don't need to convert the index keys to paths | ||
201 | if k == "%" { | ||
202 | return path, nil | ||
203 | } | ||
204 | |||
205 | path = cty.Path{cty.IndexStep{Key: cty.StringVal(k)}} | ||
206 | |||
207 | if rest == "" { | ||
208 | return path, nil | ||
209 | } | ||
210 | |||
211 | p, err := pathFromFlatmapKeyValue(rest, ty.ElementType()) | ||
212 | if err != nil { | ||
213 | return path, err | ||
214 | } | ||
215 | |||
216 | return append(path, p...), nil | ||
217 | } | ||
218 | |||
219 | func pathFromFlatmapKeyList(key string, ty cty.Type) (cty.Path, error) { | ||
220 | var path cty.Path | ||
221 | var err error | ||
222 | |||
223 | k, rest := pathSplit(key) | ||
224 | |||
225 | // we don't need to convert the index keys to paths | ||
226 | if key == "#" { | ||
227 | return path, nil | ||
228 | } | ||
229 | |||
230 | idx, err := strconv.Atoi(k) | ||
231 | if err != nil { | ||
232 | return path, err | ||
233 | } | ||
234 | |||
235 | path = cty.Path{cty.IndexStep{Key: cty.NumberIntVal(int64(idx))}} | ||
236 | |||
237 | if rest == "" { | ||
238 | return path, nil | ||
239 | } | ||
240 | |||
241 | p, err := pathFromFlatmapKeyValue(rest, ty.ElementType()) | ||
242 | if err != nil { | ||
243 | return path, err | ||
244 | } | ||
245 | |||
246 | return append(path, p...), nil | ||
247 | } | ||
248 | |||
249 | func pathFromFlatmapKeySet(key string, ty cty.Type) (cty.Path, error) { | ||
250 | // once we hit a set, we can't return consistent paths, so just mark the | ||
251 | // set as a whole changed. | ||
252 | return nil, nil | ||
253 | } | ||
254 | |||
255 | // FlatmapKeyFromPath returns the flatmap equivalent of the given cty.Path for | ||
256 | // use in generating legacy style diffs. | ||
257 | func FlatmapKeyFromPath(path cty.Path) string { | ||
258 | var parts []string | ||
259 | |||
260 | for _, step := range path { | ||
261 | switch step := step.(type) { | ||
262 | case cty.GetAttrStep: | ||
263 | parts = append(parts, step.Name) | ||
264 | case cty.IndexStep: | ||
265 | switch ty := step.Key.Type(); { | ||
266 | case ty == cty.String: | ||
267 | parts = append(parts, step.Key.AsString()) | ||
268 | case ty == cty.Number: | ||
269 | i, _ := step.Key.AsBigFloat().Int64() | ||
270 | parts = append(parts, strconv.Itoa(int(i))) | ||
271 | } | ||
272 | } | ||
273 | } | ||
274 | |||
275 | return strings.Join(parts, ".") | ||
276 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/hcl2shim/values.go b/vendor/github.com/hashicorp/terraform/config/hcl2shim/values.go index 0b697a5..daeb0b8 100644 --- a/vendor/github.com/hashicorp/terraform/config/hcl2shim/values.go +++ b/vendor/github.com/hashicorp/terraform/config/hcl2shim/values.go | |||
@@ -6,6 +6,8 @@ import ( | |||
6 | 6 | ||
7 | "github.com/hashicorp/hil/ast" | 7 | "github.com/hashicorp/hil/ast" |
8 | "github.com/zclconf/go-cty/cty" | 8 | "github.com/zclconf/go-cty/cty" |
9 | |||
10 | "github.com/hashicorp/terraform/configs/configschema" | ||
9 | ) | 11 | ) |
10 | 12 | ||
11 | // UnknownVariableValue is a sentinel value that can be used | 13 | // UnknownVariableValue is a sentinel value that can be used |
@@ -14,6 +16,108 @@ import ( | |||
14 | // unknown keys. | 16 | // unknown keys. |
15 | const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66" | 17 | const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66" |
16 | 18 | ||
19 | // ConfigValueFromHCL2Block is like ConfigValueFromHCL2 but it works only for | ||
20 | // known object values and uses the provided block schema to perform some | ||
21 | // additional normalization to better mimic the shape of value that the old | ||
22 | // HCL1/HIL-based codepaths would've produced. | ||
23 | // | ||
24 | // In particular, it discards the collections that we use to represent nested | ||
25 | // blocks (other than NestingSingle) if they are empty, which better mimics | ||
26 | // the HCL1 behavior because HCL1 had no knowledge of the schema and so didn't | ||
27 | // know that an unspecified block _could_ exist. | ||
28 | // | ||
29 | // The given object value must conform to the schema's implied type or this | ||
30 | // function will panic or produce incorrect results. | ||
31 | // | ||
32 | // This is primarily useful for the final transition from new-style values to | ||
33 | // terraform.ResourceConfig before calling to a legacy provider, since | ||
34 | // helper/schema (the old provider SDK) is particularly sensitive to these | ||
35 | // subtle differences within its validation code. | ||
36 | func ConfigValueFromHCL2Block(v cty.Value, schema *configschema.Block) map[string]interface{} { | ||
37 | if v.IsNull() { | ||
38 | return nil | ||
39 | } | ||
40 | if !v.IsKnown() { | ||
41 | panic("ConfigValueFromHCL2Block used with unknown value") | ||
42 | } | ||
43 | if !v.Type().IsObjectType() { | ||
44 | panic(fmt.Sprintf("ConfigValueFromHCL2Block used with non-object value %#v", v)) | ||
45 | } | ||
46 | |||
47 | atys := v.Type().AttributeTypes() | ||
48 | ret := make(map[string]interface{}) | ||
49 | |||
50 | for name := range schema.Attributes { | ||
51 | if _, exists := atys[name]; !exists { | ||
52 | continue | ||
53 | } | ||
54 | |||
55 | av := v.GetAttr(name) | ||
56 | if av.IsNull() { | ||
57 | // Skip nulls altogether, to better mimic how HCL1 would behave | ||
58 | continue | ||
59 | } | ||
60 | ret[name] = ConfigValueFromHCL2(av) | ||
61 | } | ||
62 | |||
63 | for name, blockS := range schema.BlockTypes { | ||
64 | if _, exists := atys[name]; !exists { | ||
65 | continue | ||
66 | } | ||
67 | bv := v.GetAttr(name) | ||
68 | if !bv.IsKnown() { | ||
69 | ret[name] = UnknownVariableValue | ||
70 | continue | ||
71 | } | ||
72 | if bv.IsNull() { | ||
73 | continue | ||
74 | } | ||
75 | |||
76 | switch blockS.Nesting { | ||
77 | |||
78 | case configschema.NestingSingle, configschema.NestingGroup: | ||
79 | ret[name] = ConfigValueFromHCL2Block(bv, &blockS.Block) | ||
80 | |||
81 | case configschema.NestingList, configschema.NestingSet: | ||
82 | l := bv.LengthInt() | ||
83 | if l == 0 { | ||
84 | // skip empty collections to better mimic how HCL1 would behave | ||
85 | continue | ||
86 | } | ||
87 | |||
88 | elems := make([]interface{}, 0, l) | ||
89 | for it := bv.ElementIterator(); it.Next(); { | ||
90 | _, ev := it.Element() | ||
91 | if !ev.IsKnown() { | ||
92 | elems = append(elems, UnknownVariableValue) | ||
93 | continue | ||
94 | } | ||
95 | elems = append(elems, ConfigValueFromHCL2Block(ev, &blockS.Block)) | ||
96 | } | ||
97 | ret[name] = elems | ||
98 | |||
99 | case configschema.NestingMap: | ||
100 | if bv.LengthInt() == 0 { | ||
101 | // skip empty collections to better mimic how HCL1 would behave | ||
102 | continue | ||
103 | } | ||
104 | |||
105 | elems := make(map[string]interface{}) | ||
106 | for it := bv.ElementIterator(); it.Next(); { | ||
107 | ek, ev := it.Element() | ||
108 | if !ev.IsKnown() { | ||
109 | elems[ek.AsString()] = UnknownVariableValue | ||
110 | continue | ||
111 | } | ||
112 | elems[ek.AsString()] = ConfigValueFromHCL2Block(ev, &blockS.Block) | ||
113 | } | ||
114 | ret[name] = elems | ||
115 | } | ||
116 | } | ||
117 | |||
118 | return ret | ||
119 | } | ||
120 | |||
17 | // ConfigValueFromHCL2 converts a value from HCL2 (really, from the cty dynamic | 121 | // ConfigValueFromHCL2 converts a value from HCL2 (really, from the cty dynamic |
18 | // types library that HCL2 uses) to a value type that matches what would've | 122 | // types library that HCL2 uses) to a value type that matches what would've |
19 | // been produced from the HCL-based interpolator for an equivalent structure. | 123 | // been produced from the HCL-based interpolator for an equivalent structure. |
@@ -73,7 +177,10 @@ func ConfigValueFromHCL2(v cty.Value) interface{} { | |||
73 | it := v.ElementIterator() | 177 | it := v.ElementIterator() |
74 | for it.Next() { | 178 | for it.Next() { |
75 | ek, ev := it.Element() | 179 | ek, ev := it.Element() |
76 | l[ek.AsString()] = ConfigValueFromHCL2(ev) | 180 | cv := ConfigValueFromHCL2(ev) |
181 | if cv != nil { | ||
182 | l[ek.AsString()] = cv | ||
183 | } | ||
77 | } | 184 | } |
78 | return l | 185 | return l |
79 | } | 186 | } |
diff --git a/vendor/github.com/hashicorp/terraform/config/hcl2shim/values_equiv.go b/vendor/github.com/hashicorp/terraform/config/hcl2shim/values_equiv.go new file mode 100644 index 0000000..92f0213 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/hcl2shim/values_equiv.go | |||
@@ -0,0 +1,214 @@ | |||
1 | package hcl2shim | ||
2 | |||
3 | import ( | ||
4 | "github.com/zclconf/go-cty/cty" | ||
5 | ) | ||
6 | |||
7 | // ValuesSDKEquivalent returns true if both of the given values seem equivalent | ||
8 | // as far as the legacy SDK diffing code would be concerned. | ||
9 | // | ||
10 | // Since SDK diffing is a fuzzy, inexact operation, this function is also | ||
11 | // fuzzy and inexact. It will err on the side of returning false if it | ||
12 | // encounters an ambiguous situation. Ambiguity is most common in the presence | ||
13 | // of sets because in practice it is impossible to exactly correlate | ||
14 | // nonequal-but-equivalent set elements because they have no identity separate | ||
15 | // from their value. | ||
16 | // | ||
17 | // This must be used _only_ for comparing values for equivalence within the | ||
18 | // SDK planning code. It is only meaningful to compare the "prior state" | ||
19 | // provided by Terraform Core with the "planned new state" produced by the | ||
20 | // legacy SDK code via shims. In particular it is not valid to use this | ||
21 | // function with their the config value or the "proposed new state" value | ||
22 | // because they contain only the subset of data that Terraform Core itself is | ||
23 | // able to determine. | ||
24 | func ValuesSDKEquivalent(a, b cty.Value) bool { | ||
25 | if a == cty.NilVal || b == cty.NilVal { | ||
26 | // We don't generally expect nils to appear, but we'll allow them | ||
27 | // for robustness since the data structures produced by legacy SDK code | ||
28 | // can sometimes be non-ideal. | ||
29 | return a == b // equivalent if they are _both_ nil | ||
30 | } | ||
31 | if a.RawEquals(b) { | ||
32 | // Easy case. We use RawEquals because we want two unknowns to be | ||
33 | // considered equal here, whereas "Equals" would return unknown. | ||
34 | return true | ||
35 | } | ||
36 | if !a.IsKnown() || !b.IsKnown() { | ||
37 | // Two unknown values are equivalent regardless of type. A known is | ||
38 | // never equivalent to an unknown. | ||
39 | return a.IsKnown() == b.IsKnown() | ||
40 | } | ||
41 | if aZero, bZero := valuesSDKEquivalentIsNullOrZero(a), valuesSDKEquivalentIsNullOrZero(b); aZero || bZero { | ||
42 | // Two null/zero values are equivalent regardless of type. A non-zero is | ||
43 | // never equivalent to a zero. | ||
44 | return aZero == bZero | ||
45 | } | ||
46 | |||
47 | // If we get down here then we are guaranteed that both a and b are known, | ||
48 | // non-null values. | ||
49 | |||
50 | aTy := a.Type() | ||
51 | bTy := b.Type() | ||
52 | switch { | ||
53 | case aTy.IsSetType() && bTy.IsSetType(): | ||
54 | return valuesSDKEquivalentSets(a, b) | ||
55 | case aTy.IsListType() && bTy.IsListType(): | ||
56 | return valuesSDKEquivalentSequences(a, b) | ||
57 | case aTy.IsTupleType() && bTy.IsTupleType(): | ||
58 | return valuesSDKEquivalentSequences(a, b) | ||
59 | case aTy.IsMapType() && bTy.IsMapType(): | ||
60 | return valuesSDKEquivalentMappings(a, b) | ||
61 | case aTy.IsObjectType() && bTy.IsObjectType(): | ||
62 | return valuesSDKEquivalentMappings(a, b) | ||
63 | case aTy == cty.Number && bTy == cty.Number: | ||
64 | return valuesSDKEquivalentNumbers(a, b) | ||
65 | default: | ||
66 | // We've now covered all the interesting cases, so anything that falls | ||
67 | // down here cannot be equivalent. | ||
68 | return false | ||
69 | } | ||
70 | } | ||
71 | |||
72 | // valuesSDKEquivalentIsNullOrZero returns true if the given value is either | ||
73 | // null or is the "zero value" (in the SDK/Go sense) for its type. | ||
74 | func valuesSDKEquivalentIsNullOrZero(v cty.Value) bool { | ||
75 | if v == cty.NilVal { | ||
76 | return true | ||
77 | } | ||
78 | |||
79 | ty := v.Type() | ||
80 | switch { | ||
81 | case !v.IsKnown(): | ||
82 | return false | ||
83 | case v.IsNull(): | ||
84 | return true | ||
85 | |||
86 | // After this point, v is always known and non-null | ||
87 | case ty.IsListType() || ty.IsSetType() || ty.IsMapType() || ty.IsObjectType() || ty.IsTupleType(): | ||
88 | return v.LengthInt() == 0 | ||
89 | case ty == cty.String: | ||
90 | return v.RawEquals(cty.StringVal("")) | ||
91 | case ty == cty.Number: | ||
92 | return v.RawEquals(cty.Zero) | ||
93 | case ty == cty.Bool: | ||
94 | return v.RawEquals(cty.False) | ||
95 | default: | ||
96 | // The above is exhaustive, but for robustness we'll consider anything | ||
97 | // else to _not_ be zero unless it is null. | ||
98 | return false | ||
99 | } | ||
100 | } | ||
101 | |||
102 | // valuesSDKEquivalentSets returns true only if each of the elements in a can | ||
103 | // be correlated with at least one equivalent element in b and vice-versa. | ||
104 | // This is a fuzzy operation that prefers to signal non-equivalence if it cannot | ||
105 | // be certain that all elements are accounted for. | ||
106 | func valuesSDKEquivalentSets(a, b cty.Value) bool { | ||
107 | if aLen, bLen := a.LengthInt(), b.LengthInt(); aLen != bLen { | ||
108 | return false | ||
109 | } | ||
110 | |||
111 | // Our methodology here is a little tricky, to deal with the fact that | ||
112 | // it's impossible to directly correlate two non-equal set elements because | ||
113 | // they don't have identities separate from their values. | ||
114 | // The approach is to count the number of equivalent elements each element | ||
115 | // of a has in b and vice-versa, and then return true only if each element | ||
116 | // in both sets has at least one equivalent. | ||
117 | as := a.AsValueSlice() | ||
118 | bs := b.AsValueSlice() | ||
119 | aeqs := make([]bool, len(as)) | ||
120 | beqs := make([]bool, len(bs)) | ||
121 | for ai, av := range as { | ||
122 | for bi, bv := range bs { | ||
123 | if ValuesSDKEquivalent(av, bv) { | ||
124 | aeqs[ai] = true | ||
125 | beqs[bi] = true | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | |||
130 | for _, eq := range aeqs { | ||
131 | if !eq { | ||
132 | return false | ||
133 | } | ||
134 | } | ||
135 | for _, eq := range beqs { | ||
136 | if !eq { | ||
137 | return false | ||
138 | } | ||
139 | } | ||
140 | return true | ||
141 | } | ||
142 | |||
143 | // valuesSDKEquivalentSequences decides equivalence for two sequence values | ||
144 | // (lists or tuples). | ||
145 | func valuesSDKEquivalentSequences(a, b cty.Value) bool { | ||
146 | as := a.AsValueSlice() | ||
147 | bs := b.AsValueSlice() | ||
148 | if len(as) != len(bs) { | ||
149 | return false | ||
150 | } | ||
151 | |||
152 | for i := range as { | ||
153 | if !ValuesSDKEquivalent(as[i], bs[i]) { | ||
154 | return false | ||
155 | } | ||
156 | } | ||
157 | return true | ||
158 | } | ||
159 | |||
160 | // valuesSDKEquivalentMappings decides equivalence for two mapping values | ||
161 | // (maps or objects). | ||
162 | func valuesSDKEquivalentMappings(a, b cty.Value) bool { | ||
163 | as := a.AsValueMap() | ||
164 | bs := b.AsValueMap() | ||
165 | if len(as) != len(bs) { | ||
166 | return false | ||
167 | } | ||
168 | |||
169 | for k, av := range as { | ||
170 | bv, ok := bs[k] | ||
171 | if !ok { | ||
172 | return false | ||
173 | } | ||
174 | if !ValuesSDKEquivalent(av, bv) { | ||
175 | return false | ||
176 | } | ||
177 | } | ||
178 | return true | ||
179 | } | ||
180 | |||
181 | // valuesSDKEquivalentNumbers decides equivalence for two number values based | ||
182 | // on the fact that the SDK uses int and float64 representations while | ||
183 | // cty (and thus Terraform Core) uses big.Float, and so we expect to lose | ||
184 | // precision in the round-trip. | ||
185 | // | ||
186 | // This does _not_ attempt to allow for an epsilon difference that may be | ||
187 | // caused by accumulated innacuracy in a float calculation, under the | ||
188 | // expectation that providers generally do not actually do compuations on | ||
189 | // floats and instead just pass string representations of them on verbatim | ||
190 | // to remote APIs. A remote API _itself_ may introduce inaccuracy, but that's | ||
191 | // a problem for the provider itself to deal with, based on its knowledge of | ||
192 | // the remote system, e.g. using DiffSuppressFunc. | ||
193 | func valuesSDKEquivalentNumbers(a, b cty.Value) bool { | ||
194 | if a.RawEquals(b) { | ||
195 | return true // easy | ||
196 | } | ||
197 | |||
198 | af := a.AsBigFloat() | ||
199 | bf := b.AsBigFloat() | ||
200 | |||
201 | if af.IsInt() != bf.IsInt() { | ||
202 | return false | ||
203 | } | ||
204 | if af.IsInt() && bf.IsInt() { | ||
205 | return false // a.RawEquals(b) test above is good enough for integers | ||
206 | } | ||
207 | |||
208 | // The SDK supports only int and float64, so if it's not an integer | ||
209 | // we know that only a float64-level of precision can possibly be | ||
210 | // significant. | ||
211 | af64, _ := af.Float64() | ||
212 | bf64, _ := bf.Float64() | ||
213 | return af64 == bf64 | ||
214 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go b/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go index 421edb0..6a2050c 100644 --- a/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go +++ b/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go | |||
@@ -47,6 +47,20 @@ func stringSliceToVariableValue(values []string) []ast.Variable { | |||
47 | return output | 47 | return output |
48 | } | 48 | } |
49 | 49 | ||
50 | // listVariableSliceToVariableValue converts a list of lists into the value | ||
51 | // required to be returned from interpolation functions which return TypeList. | ||
52 | func listVariableSliceToVariableValue(values [][]ast.Variable) []ast.Variable { | ||
53 | output := make([]ast.Variable, len(values)) | ||
54 | |||
55 | for index, value := range values { | ||
56 | output[index] = ast.Variable{ | ||
57 | Type: ast.TypeList, | ||
58 | Value: value, | ||
59 | } | ||
60 | } | ||
61 | return output | ||
62 | } | ||
63 | |||
50 | func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) { | 64 | func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) { |
51 | output := make([]string, len(values)) | 65 | output := make([]string, len(values)) |
52 | for index, value := range values { | 66 | for index, value := range values { |
@@ -61,74 +75,69 @@ func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) { | |||
61 | // Funcs is the mapping of built-in functions for configuration. | 75 | // Funcs is the mapping of built-in functions for configuration. |
62 | func Funcs() map[string]ast.Function { | 76 | func Funcs() map[string]ast.Function { |
63 | return map[string]ast.Function{ | 77 | return map[string]ast.Function{ |
64 | "abs": interpolationFuncAbs(), | 78 | "abs": interpolationFuncAbs(), |
65 | "basename": interpolationFuncBasename(), | 79 | "basename": interpolationFuncBasename(), |
66 | "base64decode": interpolationFuncBase64Decode(), | 80 | "base64decode": interpolationFuncBase64Decode(), |
67 | "base64encode": interpolationFuncBase64Encode(), | 81 | "base64encode": interpolationFuncBase64Encode(), |
68 | "base64gzip": interpolationFuncBase64Gzip(), | 82 | "base64gzip": interpolationFuncBase64Gzip(), |
69 | "base64sha256": interpolationFuncBase64Sha256(), | 83 | "base64sha256": interpolationFuncBase64Sha256(), |
70 | "base64sha512": interpolationFuncBase64Sha512(), | 84 | "base64sha512": interpolationFuncBase64Sha512(), |
71 | "bcrypt": interpolationFuncBcrypt(), | 85 | "bcrypt": interpolationFuncBcrypt(), |
72 | "ceil": interpolationFuncCeil(), | 86 | "ceil": interpolationFuncCeil(), |
73 | "chomp": interpolationFuncChomp(), | 87 | "chomp": interpolationFuncChomp(), |
74 | "cidrhost": interpolationFuncCidrHost(), | 88 | "cidrhost": interpolationFuncCidrHost(), |
75 | "cidrnetmask": interpolationFuncCidrNetmask(), | 89 | "cidrnetmask": interpolationFuncCidrNetmask(), |
76 | "cidrsubnet": interpolationFuncCidrSubnet(), | 90 | "cidrsubnet": interpolationFuncCidrSubnet(), |
77 | "coalesce": interpolationFuncCoalesce(), | 91 | "coalesce": interpolationFuncCoalesce(), |
78 | "coalescelist": interpolationFuncCoalesceList(), | 92 | "coalescelist": interpolationFuncCoalesceList(), |
79 | "compact": interpolationFuncCompact(), | 93 | "compact": interpolationFuncCompact(), |
80 | "concat": interpolationFuncConcat(), | 94 | "concat": interpolationFuncConcat(), |
81 | "contains": interpolationFuncContains(), | 95 | "contains": interpolationFuncContains(), |
82 | "dirname": interpolationFuncDirname(), | 96 | "dirname": interpolationFuncDirname(), |
83 | "distinct": interpolationFuncDistinct(), | 97 | "distinct": interpolationFuncDistinct(), |
84 | "element": interpolationFuncElement(), | 98 | "element": interpolationFuncElement(), |
85 | "chunklist": interpolationFuncChunklist(), | 99 | "chunklist": interpolationFuncChunklist(), |
86 | "file": interpolationFuncFile(), | 100 | "file": interpolationFuncFile(), |
87 | "filebase64sha256": interpolationFuncMakeFileHash(interpolationFuncBase64Sha256()), | 101 | "matchkeys": interpolationFuncMatchKeys(), |
88 | "filebase64sha512": interpolationFuncMakeFileHash(interpolationFuncBase64Sha512()), | 102 | "flatten": interpolationFuncFlatten(), |
89 | "filemd5": interpolationFuncMakeFileHash(interpolationFuncMd5()), | 103 | "floor": interpolationFuncFloor(), |
90 | "filesha1": interpolationFuncMakeFileHash(interpolationFuncSha1()), | 104 | "format": interpolationFuncFormat(), |
91 | "filesha256": interpolationFuncMakeFileHash(interpolationFuncSha256()), | 105 | "formatlist": interpolationFuncFormatList(), |
92 | "filesha512": interpolationFuncMakeFileHash(interpolationFuncSha512()), | 106 | "indent": interpolationFuncIndent(), |
93 | "matchkeys": interpolationFuncMatchKeys(), | 107 | "index": interpolationFuncIndex(), |
94 | "flatten": interpolationFuncFlatten(), | 108 | "join": interpolationFuncJoin(), |
95 | "floor": interpolationFuncFloor(), | 109 | "jsonencode": interpolationFuncJSONEncode(), |
96 | "format": interpolationFuncFormat(), | 110 | "length": interpolationFuncLength(), |
97 | "formatlist": interpolationFuncFormatList(), | 111 | "list": interpolationFuncList(), |
98 | "indent": interpolationFuncIndent(), | 112 | "log": interpolationFuncLog(), |
99 | "index": interpolationFuncIndex(), | 113 | "lower": interpolationFuncLower(), |
100 | "join": interpolationFuncJoin(), | 114 | "map": interpolationFuncMap(), |
101 | "jsonencode": interpolationFuncJSONEncode(), | 115 | "max": interpolationFuncMax(), |
102 | "length": interpolationFuncLength(), | 116 | "md5": interpolationFuncMd5(), |
103 | "list": interpolationFuncList(), | 117 | "merge": interpolationFuncMerge(), |
104 | "log": interpolationFuncLog(), | 118 | "min": interpolationFuncMin(), |
105 | "lower": interpolationFuncLower(), | 119 | "pathexpand": interpolationFuncPathExpand(), |
106 | "map": interpolationFuncMap(), | 120 | "pow": interpolationFuncPow(), |
107 | "max": interpolationFuncMax(), | 121 | "uuid": interpolationFuncUUID(), |
108 | "md5": interpolationFuncMd5(), | 122 | "replace": interpolationFuncReplace(), |
109 | "merge": interpolationFuncMerge(), | 123 | "reverse": interpolationFuncReverse(), |
110 | "min": interpolationFuncMin(), | 124 | "rsadecrypt": interpolationFuncRsaDecrypt(), |
111 | "pathexpand": interpolationFuncPathExpand(), | 125 | "sha1": interpolationFuncSha1(), |
112 | "pow": interpolationFuncPow(), | 126 | "sha256": interpolationFuncSha256(), |
113 | "uuid": interpolationFuncUUID(), | 127 | "sha512": interpolationFuncSha512(), |
114 | "replace": interpolationFuncReplace(), | 128 | "signum": interpolationFuncSignum(), |
115 | "rsadecrypt": interpolationFuncRsaDecrypt(), | 129 | "slice": interpolationFuncSlice(), |
116 | "sha1": interpolationFuncSha1(), | 130 | "sort": interpolationFuncSort(), |
117 | "sha256": interpolationFuncSha256(), | 131 | "split": interpolationFuncSplit(), |
118 | "sha512": interpolationFuncSha512(), | 132 | "substr": interpolationFuncSubstr(), |
119 | "signum": interpolationFuncSignum(), | 133 | "timestamp": interpolationFuncTimestamp(), |
120 | "slice": interpolationFuncSlice(), | 134 | "timeadd": interpolationFuncTimeAdd(), |
121 | "sort": interpolationFuncSort(), | 135 | "title": interpolationFuncTitle(), |
122 | "split": interpolationFuncSplit(), | 136 | "transpose": interpolationFuncTranspose(), |
123 | "substr": interpolationFuncSubstr(), | 137 | "trimspace": interpolationFuncTrimSpace(), |
124 | "timestamp": interpolationFuncTimestamp(), | 138 | "upper": interpolationFuncUpper(), |
125 | "timeadd": interpolationFuncTimeAdd(), | 139 | "urlencode": interpolationFuncURLEncode(), |
126 | "title": interpolationFuncTitle(), | 140 | "zipmap": interpolationFuncZipMap(), |
127 | "transpose": interpolationFuncTranspose(), | ||
128 | "trimspace": interpolationFuncTrimSpace(), | ||
129 | "upper": interpolationFuncUpper(), | ||
130 | "urlencode": interpolationFuncURLEncode(), | ||
131 | "zipmap": interpolationFuncZipMap(), | ||
132 | } | 141 | } |
133 | } | 142 | } |
134 | 143 | ||
@@ -947,6 +956,25 @@ func interpolationFuncReplace() ast.Function { | |||
947 | } | 956 | } |
948 | } | 957 | } |
949 | 958 | ||
959 | // interpolationFuncReverse implements the "reverse" function that does list reversal | ||
960 | func interpolationFuncReverse() ast.Function { | ||
961 | return ast.Function{ | ||
962 | ArgTypes: []ast.Type{ast.TypeList}, | ||
963 | ReturnType: ast.TypeList, | ||
964 | Variadic: false, | ||
965 | Callback: func(args []interface{}) (interface{}, error) { | ||
966 | inputList := args[0].([]ast.Variable) | ||
967 | |||
968 | reversedList := make([]ast.Variable, len(inputList)) | ||
969 | for idx := range inputList { | ||
970 | reversedList[len(inputList)-1-idx] = inputList[idx] | ||
971 | } | ||
972 | |||
973 | return reversedList, nil | ||
974 | }, | ||
975 | } | ||
976 | } | ||
977 | |||
950 | func interpolationFuncLength() ast.Function { | 978 | func interpolationFuncLength() ast.Function { |
951 | return ast.Function{ | 979 | return ast.Function{ |
952 | ArgTypes: []ast.Type{ast.TypeAny}, | 980 | ArgTypes: []ast.Type{ast.TypeAny}, |
@@ -1731,24 +1759,3 @@ func interpolationFuncRsaDecrypt() ast.Function { | |||
1731 | }, | 1759 | }, |
1732 | } | 1760 | } |
1733 | } | 1761 | } |
1734 | |||
1735 | // interpolationFuncMakeFileHash constructs a function that hashes the contents | ||
1736 | // of a file by combining the implementations of the file(...) function and | ||
1737 | // a given other function that is assumed to take a single string argument and | ||
1738 | // return a hash value. | ||
1739 | func interpolationFuncMakeFileHash(hashFunc ast.Function) ast.Function { | ||
1740 | fileFunc := interpolationFuncFile() | ||
1741 | |||
1742 | return ast.Function{ | ||
1743 | ArgTypes: []ast.Type{ast.TypeString}, | ||
1744 | ReturnType: ast.TypeString, | ||
1745 | Callback: func(args []interface{}) (interface{}, error) { | ||
1746 | filename := args[0].(string) | ||
1747 | contents, err := fileFunc.Callback([]interface{}{filename}) | ||
1748 | if err != nil { | ||
1749 | return nil, err | ||
1750 | } | ||
1751 | return hashFunc.Callback([]interface{}{contents}) | ||
1752 | }, | ||
1753 | } | ||
1754 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/storage.go b/vendor/github.com/hashicorp/terraform/config/module/storage.go index 58e3a10..7734cbc 100644 --- a/vendor/github.com/hashicorp/terraform/config/module/storage.go +++ b/vendor/github.com/hashicorp/terraform/config/module/storage.go | |||
@@ -7,7 +7,6 @@ import ( | |||
7 | "log" | 7 | "log" |
8 | "os" | 8 | "os" |
9 | "path/filepath" | 9 | "path/filepath" |
10 | "strings" | ||
11 | 10 | ||
12 | getter "github.com/hashicorp/go-getter" | 11 | getter "github.com/hashicorp/go-getter" |
13 | "github.com/hashicorp/terraform/registry" | 12 | "github.com/hashicorp/terraform/registry" |
@@ -101,21 +100,6 @@ func (s Storage) loadManifest() (moduleManifest, error) { | |||
101 | if err := json.Unmarshal(data, &manifest); err != nil { | 100 | if err := json.Unmarshal(data, &manifest); err != nil { |
102 | return manifest, err | 101 | return manifest, err |
103 | } | 102 | } |
104 | |||
105 | for i, rec := range manifest.Modules { | ||
106 | // If the path was recorded before we changed to always using a | ||
107 | // slash as separator, we delete the record from the manifest so | ||
108 | // it can be discovered again and will be recorded using a slash. | ||
109 | if strings.Contains(rec.Dir, "\\") { | ||
110 | manifest.Modules[i] = manifest.Modules[len(manifest.Modules)-1] | ||
111 | manifest.Modules = manifest.Modules[:len(manifest.Modules)-1] | ||
112 | continue | ||
113 | } | ||
114 | |||
115 | // Make sure we use the correct path separator. | ||
116 | rec.Dir = filepath.FromSlash(rec.Dir) | ||
117 | } | ||
118 | |||
119 | return manifest, nil | 103 | return manifest, nil |
120 | } | 104 | } |
121 | 105 | ||
@@ -146,9 +130,6 @@ func (s Storage) recordModule(rec moduleRecord) error { | |||
146 | } | 130 | } |
147 | } | 131 | } |
148 | 132 | ||
149 | // Make sure we always use a slash separator. | ||
150 | rec.Dir = filepath.ToSlash(rec.Dir) | ||
151 | |||
152 | manifest.Modules = append(manifest.Modules, rec) | 133 | manifest.Modules = append(manifest.Modules, rec) |
153 | 134 | ||
154 | js, err := json.Marshal(manifest) | 135 | js, err := json.Marshal(manifest) |
@@ -331,7 +312,7 @@ func (s Storage) findRegistryModule(mSource, constraint string) (moduleRecord, e | |||
331 | // we need to lookup available versions | 312 | // we need to lookup available versions |
332 | // Only on Get if it's not found, on unconditionally on Update | 313 | // Only on Get if it's not found, on unconditionally on Update |
333 | if (s.Mode == GetModeGet && !found) || (s.Mode == GetModeUpdate) { | 314 | if (s.Mode == GetModeGet && !found) || (s.Mode == GetModeUpdate) { |
334 | resp, err := s.registry.Versions(mod) | 315 | resp, err := s.registry.ModuleVersions(mod) |
335 | if err != nil { | 316 | if err != nil { |
336 | return rec, err | 317 | return rec, err |
337 | } | 318 | } |
@@ -351,7 +332,7 @@ func (s Storage) findRegistryModule(mSource, constraint string) (moduleRecord, e | |||
351 | 332 | ||
352 | rec.Version = match.Version | 333 | rec.Version = match.Version |
353 | 334 | ||
354 | rec.url, err = s.registry.Location(mod, rec.Version) | 335 | rec.url, err = s.registry.ModuleLocation(mod, rec.Version) |
355 | if err != nil { | 336 | if err != nil { |
356 | return rec, err | 337 | return rec, err |
357 | } | 338 | } |
diff --git a/vendor/github.com/hashicorp/terraform/config/resource_mode_string.go b/vendor/github.com/hashicorp/terraform/config/resource_mode_string.go index 8a55e06..0105278 100644 --- a/vendor/github.com/hashicorp/terraform/config/resource_mode_string.go +++ b/vendor/github.com/hashicorp/terraform/config/resource_mode_string.go | |||
@@ -4,6 +4,14 @@ package config | |||
4 | 4 | ||
5 | import "strconv" | 5 | import "strconv" |
6 | 6 | ||
7 | func _() { | ||
8 | // An "invalid array index" compiler error signifies that the constant values have changed. | ||
9 | // Re-run the stringer command to generate them again. | ||
10 | var x [1]struct{} | ||
11 | _ = x[ManagedResourceMode-0] | ||
12 | _ = x[DataResourceMode-1] | ||
13 | } | ||
14 | |||
7 | const _ResourceMode_name = "ManagedResourceModeDataResourceMode" | 15 | const _ResourceMode_name = "ManagedResourceModeDataResourceMode" |
8 | 16 | ||
9 | var _ResourceMode_index = [...]uint8{0, 19, 35} | 17 | var _ResourceMode_index = [...]uint8{0, 19, 35} |