aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/lang
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/lang')
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/blocktoattr/doc.go5
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/blocktoattr/fixup.go187
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/blocktoattr/schema.go145
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/blocktoattr/variables.go43
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/data.go33
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/doc.go5
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/eval.go477
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/funcs/cidr.go129
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/funcs/collection.go1511
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go87
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/funcs/crypto.go285
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/funcs/datetime.go70
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/funcs/encoding.go140
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/funcs/filesystem.go345
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/funcs/number.go155
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/funcs/string.go280
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/functions.go147
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/references.go81
-rw-r--r--vendor/github.com/hashicorp/terraform/lang/scope.go34
19 files changed, 4159 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/lang/blocktoattr/doc.go b/vendor/github.com/hashicorp/terraform/lang/blocktoattr/doc.go
new file mode 100644
index 0000000..8f89909
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/blocktoattr/doc.go
@@ -0,0 +1,5 @@
1// Package blocktoattr includes some helper functions that can perform
2// preprocessing on a HCL body where a configschema.Block schema is available
3// in order to allow list and set attributes defined in the schema to be
4// optionally written by the user as block syntax.
5package blocktoattr
diff --git a/vendor/github.com/hashicorp/terraform/lang/blocktoattr/fixup.go b/vendor/github.com/hashicorp/terraform/lang/blocktoattr/fixup.go
new file mode 100644
index 0000000..d8c2e77
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/blocktoattr/fixup.go
@@ -0,0 +1,187 @@
1package blocktoattr
2
3import (
4 "github.com/hashicorp/hcl2/hcl"
5 "github.com/hashicorp/hcl2/hcldec"
6 "github.com/hashicorp/terraform/configs/configschema"
7 "github.com/zclconf/go-cty/cty"
8)
9
10// FixUpBlockAttrs takes a raw HCL body and adds some additional normalization
11// functionality to allow attributes that are specified as having list or set
12// type in the schema to be written with HCL block syntax as multiple nested
13// blocks with the attribute name as the block type.
14//
15// This partially restores some of the block/attribute confusion from HCL 1
16// so that existing patterns that depended on that confusion can continue to
17// be used in the short term while we settle on a longer-term strategy.
18//
19// Most of the fixup work is actually done when the returned body is
20// subsequently decoded, so while FixUpBlockAttrs always succeeds, the eventual
21// decode of the body might not, if the content of the body is so ambiguous
22// that there's no safe way to map it to the schema.
23func FixUpBlockAttrs(body hcl.Body, schema *configschema.Block) hcl.Body {
24 // The schema should never be nil, but in practice it seems to be sometimes
25 // in the presence of poorly-configured test mocks, so we'll be robust
26 // by synthesizing an empty one.
27 if schema == nil {
28 schema = &configschema.Block{}
29 }
30
31 return &fixupBody{
32 original: body,
33 schema: schema,
34 names: ambiguousNames(schema),
35 }
36}
37
38type fixupBody struct {
39 original hcl.Body
40 schema *configschema.Block
41 names map[string]struct{}
42}
43
44// Content decodes content from the body. The given schema must be the lower-level
45// representation of the same schema that was previously passed to FixUpBlockAttrs,
46// or else the result is undefined.
47func (b *fixupBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
48 schema = b.effectiveSchema(schema)
49 content, diags := b.original.Content(schema)
50 return b.fixupContent(content), diags
51}
52
53func (b *fixupBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
54 schema = b.effectiveSchema(schema)
55 content, remain, diags := b.original.PartialContent(schema)
56 remain = &fixupBody{
57 original: remain,
58 schema: b.schema,
59 names: b.names,
60 }
61 return b.fixupContent(content), remain, diags
62}
63
64func (b *fixupBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
65 // FixUpBlockAttrs is not intended to be used in situations where we'd use
66 // JustAttributes, so we just pass this through verbatim to complete our
67 // implementation of hcl.Body.
68 return b.original.JustAttributes()
69}
70
71func (b *fixupBody) MissingItemRange() hcl.Range {
72 return b.original.MissingItemRange()
73}
74
75// effectiveSchema produces a derived *hcl.BodySchema by sniffing the body's
76// content to determine whether the author has used attribute or block syntax
77// for each of the ambigious attributes where both are permitted.
78//
79// The resulting schema will always contain all of the same names that are
80// in the given schema, but some attribute schemas may instead be replaced by
81// block header schemas.
82func (b *fixupBody) effectiveSchema(given *hcl.BodySchema) *hcl.BodySchema {
83 return effectiveSchema(given, b.original, b.names, true)
84}
85
86func (b *fixupBody) fixupContent(content *hcl.BodyContent) *hcl.BodyContent {
87 var ret hcl.BodyContent
88 ret.Attributes = make(hcl.Attributes)
89 for name, attr := range content.Attributes {
90 ret.Attributes[name] = attr
91 }
92 blockAttrVals := make(map[string][]*hcl.Block)
93 for _, block := range content.Blocks {
94 if _, exists := b.names[block.Type]; exists {
95 // If we get here then we've found a block type whose instances need
96 // to be re-interpreted as a list-of-objects attribute. We'll gather
97 // those up and fix them up below.
98 blockAttrVals[block.Type] = append(blockAttrVals[block.Type], block)
99 continue
100 }
101
102 // We need to now re-wrap our inner body so it will be subject to the
103 // same attribute-as-block fixup when recursively decoded.
104 retBlock := *block // shallow copy
105 if blockS, ok := b.schema.BlockTypes[block.Type]; ok {
106 // Would be weird if not ok, but we'll allow it for robustness; body just won't be fixed up, then
107 retBlock.Body = FixUpBlockAttrs(retBlock.Body, &blockS.Block)
108 }
109
110 ret.Blocks = append(ret.Blocks, &retBlock)
111 }
112 // No we'll install synthetic attributes for each of our fixups. We can't
113 // do this exactly because HCL's information model expects an attribute
114 // to be a single decl but we have multiple separate blocks. We'll
115 // approximate things, then, by using only our first block for the source
116 // location information. (We are guaranteed at least one by the above logic.)
117 for name, blocks := range blockAttrVals {
118 ret.Attributes[name] = &hcl.Attribute{
119 Name: name,
120 Expr: &fixupBlocksExpr{
121 blocks: blocks,
122 ety: b.schema.Attributes[name].Type.ElementType(),
123 },
124
125 Range: blocks[0].DefRange,
126 NameRange: blocks[0].TypeRange,
127 }
128 }
129 return &ret
130}
131
132type fixupBlocksExpr struct {
133 blocks hcl.Blocks
134 ety cty.Type
135}
136
137func (e *fixupBlocksExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
138 // In order to produce a suitable value for our expression we need to
139 // now decode the whole descendent block structure under each of our block
140 // bodies.
141 //
142 // That requires us to do something rather strange: we must construct a
143 // synthetic block type schema derived from the element type of the
144 // attribute, thus inverting our usual direction of lowering a schema
145 // into an implied type. Because a type is less detailed than a schema,
146 // the result is imprecise and in particular will just consider all
147 // the attributes to be optional and let the provider eventually decide
148 // whether to return errors if they turn out to be null when required.
149 schema := SchemaForCtyElementType(e.ety) // this schema's ImpliedType will match e.ety
150 spec := schema.DecoderSpec()
151
152 vals := make([]cty.Value, len(e.blocks))
153 var diags hcl.Diagnostics
154 for i, block := range e.blocks {
155 body := FixUpBlockAttrs(block.Body, schema)
156 val, blockDiags := hcldec.Decode(body, spec, ctx)
157 diags = append(diags, blockDiags...)
158 if val == cty.NilVal {
159 val = cty.UnknownVal(e.ety)
160 }
161 vals[i] = val
162 }
163 if len(vals) == 0 {
164 return cty.ListValEmpty(e.ety), diags
165 }
166 return cty.ListVal(vals), diags
167}
168
169func (e *fixupBlocksExpr) Variables() []hcl.Traversal {
170 var ret []hcl.Traversal
171 schema := SchemaForCtyElementType(e.ety)
172 spec := schema.DecoderSpec()
173 for _, block := range e.blocks {
174 ret = append(ret, hcldec.Variables(block.Body, spec)...)
175 }
176 return ret
177}
178
179func (e *fixupBlocksExpr) Range() hcl.Range {
180 // This is not really an appropriate range for the expression but it's
181 // the best we can do from here.
182 return e.blocks[0].DefRange
183}
184
185func (e *fixupBlocksExpr) StartRange() hcl.Range {
186 return e.blocks[0].DefRange
187}
diff --git a/vendor/github.com/hashicorp/terraform/lang/blocktoattr/schema.go b/vendor/github.com/hashicorp/terraform/lang/blocktoattr/schema.go
new file mode 100644
index 0000000..2f2463a
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/blocktoattr/schema.go
@@ -0,0 +1,145 @@
1package blocktoattr
2
3import (
4 "github.com/hashicorp/hcl2/hcl"
5 "github.com/hashicorp/terraform/configs/configschema"
6 "github.com/zclconf/go-cty/cty"
7)
8
9func ambiguousNames(schema *configschema.Block) map[string]struct{} {
10 if schema == nil {
11 return nil
12 }
13 ambiguousNames := make(map[string]struct{})
14 for name, attrS := range schema.Attributes {
15 aty := attrS.Type
16 if (aty.IsListType() || aty.IsSetType()) && aty.ElementType().IsObjectType() {
17 ambiguousNames[name] = struct{}{}
18 }
19 }
20 return ambiguousNames
21}
22
23func effectiveSchema(given *hcl.BodySchema, body hcl.Body, ambiguousNames map[string]struct{}, dynamicExpanded bool) *hcl.BodySchema {
24 ret := &hcl.BodySchema{}
25
26 appearsAsBlock := make(map[string]struct{})
27 {
28 // We'll construct some throwaway schemas here just to probe for
29 // whether each of our ambiguous names seems to be being used as
30 // an attribute or a block. We need to check both because in JSON
31 // syntax we rely on the schema to decide between attribute or block
32 // interpretation and so JSON will always answer yes to both of
33 // these questions and we want to prefer the attribute interpretation
34 // in that case.
35 var probeSchema hcl.BodySchema
36
37 for name := range ambiguousNames {
38 probeSchema = hcl.BodySchema{
39 Attributes: []hcl.AttributeSchema{
40 {
41 Name: name,
42 },
43 },
44 }
45 content, _, _ := body.PartialContent(&probeSchema)
46 if _, exists := content.Attributes[name]; exists {
47 // Can decode as an attribute, so we'll go with that.
48 continue
49 }
50 probeSchema = hcl.BodySchema{
51 Blocks: []hcl.BlockHeaderSchema{
52 {
53 Type: name,
54 },
55 },
56 }
57 content, _, _ = body.PartialContent(&probeSchema)
58 if len(content.Blocks) > 0 {
59 // No attribute present and at least one block present, so
60 // we'll need to rewrite this one as a block for a successful
61 // result.
62 appearsAsBlock[name] = struct{}{}
63 }
64 }
65 if !dynamicExpanded {
66 // If we're deciding for a context where dynamic blocks haven't
67 // been expanded yet then we need to probe for those too.
68 probeSchema = hcl.BodySchema{
69 Blocks: []hcl.BlockHeaderSchema{
70 {
71 Type: "dynamic",
72 LabelNames: []string{"type"},
73 },
74 },
75 }
76 content, _, _ := body.PartialContent(&probeSchema)
77 for _, block := range content.Blocks {
78 if _, exists := ambiguousNames[block.Labels[0]]; exists {
79 appearsAsBlock[block.Labels[0]] = struct{}{}
80 }
81 }
82 }
83 }
84
85 for _, attrS := range given.Attributes {
86 if _, exists := appearsAsBlock[attrS.Name]; exists {
87 ret.Blocks = append(ret.Blocks, hcl.BlockHeaderSchema{
88 Type: attrS.Name,
89 })
90 } else {
91 ret.Attributes = append(ret.Attributes, attrS)
92 }
93 }
94
95 // Anything that is specified as a block type in the input schema remains
96 // that way by just passing through verbatim.
97 ret.Blocks = append(ret.Blocks, given.Blocks...)
98
99 return ret
100}
101
102// SchemaForCtyElementType converts a cty object type into an
103// approximately-equivalent configschema.Block representing the element of
104// a list or set. If the given type is not an object type then this
105// function will panic.
106func SchemaForCtyElementType(ty cty.Type) *configschema.Block {
107 atys := ty.AttributeTypes()
108 ret := &configschema.Block{
109 Attributes: make(map[string]*configschema.Attribute, len(atys)),
110 }
111 for name, aty := range atys {
112 ret.Attributes[name] = &configschema.Attribute{
113 Type: aty,
114 Optional: true,
115 }
116 }
117 return ret
118}
119
120// SchemaForCtyContainerType converts a cty list-of-object or set-of-object type
121// into an approximately-equivalent configschema.NestedBlock. If the given type
122// is not of the expected kind then this function will panic.
123func SchemaForCtyContainerType(ty cty.Type) *configschema.NestedBlock {
124 var nesting configschema.NestingMode
125 switch {
126 case ty.IsListType():
127 nesting = configschema.NestingList
128 case ty.IsSetType():
129 nesting = configschema.NestingSet
130 default:
131 panic("unsuitable type")
132 }
133 nested := SchemaForCtyElementType(ty.ElementType())
134 return &configschema.NestedBlock{
135 Nesting: nesting,
136 Block: *nested,
137 }
138}
139
140// TypeCanBeBlocks returns true if the given type is a list-of-object or
141// set-of-object type, and would thus be subject to the blocktoattr fixup
142// if used as an attribute type.
143func TypeCanBeBlocks(ty cty.Type) bool {
144 return (ty.IsListType() || ty.IsSetType()) && ty.ElementType().IsObjectType()
145}
diff --git a/vendor/github.com/hashicorp/terraform/lang/blocktoattr/variables.go b/vendor/github.com/hashicorp/terraform/lang/blocktoattr/variables.go
new file mode 100644
index 0000000..e123b8a
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/blocktoattr/variables.go
@@ -0,0 +1,43 @@
1package blocktoattr
2
3import (
4 "github.com/hashicorp/hcl2/ext/dynblock"
5 "github.com/hashicorp/hcl2/hcl"
6 "github.com/hashicorp/hcl2/hcldec"
7 "github.com/hashicorp/terraform/configs/configschema"
8)
9
10// ExpandedVariables finds all of the global variables referenced in the
11// given body with the given schema while taking into account the possibilities
12// both of "dynamic" blocks being expanded and the possibility of certain
13// attributes being written instead as nested blocks as allowed by the
14// FixUpBlockAttrs function.
15//
16// This function exists to allow variables to be analyzed prior to dynamic
17// block expansion while also dealing with the fact that dynamic block expansion
18// might in turn produce nested blocks that are subject to FixUpBlockAttrs.
19//
20// This is intended as a drop-in replacement for dynblock.VariablesHCLDec,
21// which is itself a drop-in replacement for hcldec.Variables.
22func ExpandedVariables(body hcl.Body, schema *configschema.Block) []hcl.Traversal {
23 rootNode := dynblock.WalkVariables(body)
24 return walkVariables(rootNode, body, schema)
25}
26
27func walkVariables(node dynblock.WalkVariablesNode, body hcl.Body, schema *configschema.Block) []hcl.Traversal {
28 givenRawSchema := hcldec.ImpliedSchema(schema.DecoderSpec())
29 ambiguousNames := ambiguousNames(schema)
30 effectiveRawSchema := effectiveSchema(givenRawSchema, body, ambiguousNames, false)
31 vars, children := node.Visit(effectiveRawSchema)
32
33 for _, child := range children {
34 if blockS, exists := schema.BlockTypes[child.BlockTypeName]; exists {
35 vars = append(vars, walkVariables(child.Node, child.Body(), &blockS.Block)...)
36 } else if attrS, exists := schema.Attributes[child.BlockTypeName]; exists {
37 synthSchema := SchemaForCtyElementType(attrS.Type.ElementType())
38 vars = append(vars, walkVariables(child.Node, child.Body(), synthSchema)...)
39 }
40 }
41
42 return vars
43}
diff --git a/vendor/github.com/hashicorp/terraform/lang/data.go b/vendor/github.com/hashicorp/terraform/lang/data.go
new file mode 100644
index 0000000..80313d6
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/data.go
@@ -0,0 +1,33 @@
1package lang
2
3import (
4 "github.com/hashicorp/terraform/addrs"
5 "github.com/hashicorp/terraform/tfdiags"
6 "github.com/zclconf/go-cty/cty"
7)
8
9// Data is an interface whose implementations can provide cty.Value
10// representations of objects identified by referenceable addresses from
11// the addrs package.
12//
13// This interface will grow each time a new type of reference is added, and so
14// implementations outside of the Terraform codebases are not advised.
15//
16// Each method returns a suitable value and optionally some diagnostics. If the
17// returned diagnostics contains errors then the type of the returned value is
18// used to construct an unknown value of the same type which is then used in
19// place of the requested object so that type checking can still proceed. In
20// cases where it's not possible to even determine a suitable result type,
21// cty.DynamicVal is returned along with errors describing the problem.
22type Data interface {
23 StaticValidateReferences(refs []*addrs.Reference, self addrs.Referenceable) tfdiags.Diagnostics
24
25 GetCountAttr(addrs.CountAttr, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics)
26 GetResourceInstance(addrs.ResourceInstance, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics)
27 GetLocalValue(addrs.LocalValue, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics)
28 GetModuleInstance(addrs.ModuleCallInstance, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics)
29 GetModuleInstanceOutput(addrs.ModuleCallOutput, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics)
30 GetPathAttr(addrs.PathAttr, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics)
31 GetTerraformAttr(addrs.TerraformAttr, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics)
32 GetInputVariable(addrs.InputVariable, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics)
33}
diff --git a/vendor/github.com/hashicorp/terraform/lang/doc.go b/vendor/github.com/hashicorp/terraform/lang/doc.go
new file mode 100644
index 0000000..af5c5ca
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/doc.go
@@ -0,0 +1,5 @@
1// Package lang deals with the runtime aspects of Terraform's configuration
2// language, with concerns such as expression evaluation. It is closely related
3// to sibling package "configs", which is responsible for configuration
4// parsing and static validation.
5package lang
diff --git a/vendor/github.com/hashicorp/terraform/lang/eval.go b/vendor/github.com/hashicorp/terraform/lang/eval.go
new file mode 100644
index 0000000..a3fb363
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/eval.go
@@ -0,0 +1,477 @@
1package lang
2
3import (
4 "fmt"
5 "log"
6 "strconv"
7
8 "github.com/hashicorp/hcl2/ext/dynblock"
9 "github.com/hashicorp/hcl2/hcl"
10 "github.com/hashicorp/hcl2/hcldec"
11 "github.com/hashicorp/terraform/addrs"
12 "github.com/hashicorp/terraform/configs/configschema"
13 "github.com/hashicorp/terraform/lang/blocktoattr"
14 "github.com/hashicorp/terraform/tfdiags"
15 "github.com/zclconf/go-cty/cty"
16 "github.com/zclconf/go-cty/cty/convert"
17)
18
19// ExpandBlock expands any "dynamic" blocks present in the given body. The
20// result is a body with those blocks expanded, ready to be evaluated with
21// EvalBlock.
22//
23// If the returned diagnostics contains errors then the result may be
24// incomplete or invalid.
25func (s *Scope) ExpandBlock(body hcl.Body, schema *configschema.Block) (hcl.Body, tfdiags.Diagnostics) {
26 spec := schema.DecoderSpec()
27
28 traversals := dynblock.ExpandVariablesHCLDec(body, spec)
29 refs, diags := References(traversals)
30
31 ctx, ctxDiags := s.EvalContext(refs)
32 diags = diags.Append(ctxDiags)
33
34 return dynblock.Expand(body, ctx), diags
35}
36
37// EvalBlock evaluates the given body using the given block schema and returns
38// a cty object value representing its contents. The type of the result conforms
39// to the implied type of the given schema.
40//
41// This function does not automatically expand "dynamic" blocks within the
42// body. If that is desired, first call the ExpandBlock method to obtain
43// an expanded body to pass to this method.
44//
45// If the returned diagnostics contains errors then the result may be
46// incomplete or invalid.
47func (s *Scope) EvalBlock(body hcl.Body, schema *configschema.Block) (cty.Value, tfdiags.Diagnostics) {
48 spec := schema.DecoderSpec()
49
50 refs, diags := ReferencesInBlock(body, schema)
51
52 ctx, ctxDiags := s.EvalContext(refs)
53 diags = diags.Append(ctxDiags)
54 if diags.HasErrors() {
55 // We'll stop early if we found problems in the references, because
56 // it's likely evaluation will produce redundant copies of the same errors.
57 return cty.UnknownVal(schema.ImpliedType()), diags
58 }
59
60 // HACK: In order to remain compatible with some assumptions made in
61 // Terraform v0.11 and earlier about the approximate equivalence of
62 // attribute vs. block syntax, we do a just-in-time fixup here to allow
63 // any attribute in the schema that has a list-of-objects or set-of-objects
64 // kind to potentially be populated instead by one or more nested blocks
65 // whose type is the attribute name.
66 body = blocktoattr.FixUpBlockAttrs(body, schema)
67
68 val, evalDiags := hcldec.Decode(body, spec, ctx)
69 diags = diags.Append(evalDiags)
70
71 return val, diags
72}
73
74// EvalExpr evaluates a single expression in the receiving context and returns
75// the resulting value. The value will be converted to the given type before
76// it is returned if possible, or else an error diagnostic will be produced
77// describing the conversion error.
78//
79// Pass an expected type of cty.DynamicPseudoType to skip automatic conversion
80// and just obtain the returned value directly.
81//
82// If the returned diagnostics contains errors then the result may be
83// incomplete, but will always be of the requested type.
84func (s *Scope) EvalExpr(expr hcl.Expression, wantType cty.Type) (cty.Value, tfdiags.Diagnostics) {
85 refs, diags := ReferencesInExpr(expr)
86
87 ctx, ctxDiags := s.EvalContext(refs)
88 diags = diags.Append(ctxDiags)
89 if diags.HasErrors() {
90 // We'll stop early if we found problems in the references, because
91 // it's likely evaluation will produce redundant copies of the same errors.
92 return cty.UnknownVal(wantType), diags
93 }
94
95 val, evalDiags := expr.Value(ctx)
96 diags = diags.Append(evalDiags)
97
98 if wantType != cty.DynamicPseudoType {
99 var convErr error
100 val, convErr = convert.Convert(val, wantType)
101 if convErr != nil {
102 val = cty.UnknownVal(wantType)
103 diags = diags.Append(&hcl.Diagnostic{
104 Severity: hcl.DiagError,
105 Summary: "Incorrect value type",
106 Detail: fmt.Sprintf("Invalid expression value: %s.", tfdiags.FormatError(convErr)),
107 Subject: expr.Range().Ptr(),
108 })
109 }
110 }
111
112 return val, diags
113}
114
115// EvalReference evaluates the given reference in the receiving scope and
116// returns the resulting value. The value will be converted to the given type before
117// it is returned if possible, or else an error diagnostic will be produced
118// describing the conversion error.
119//
120// Pass an expected type of cty.DynamicPseudoType to skip automatic conversion
121// and just obtain the returned value directly.
122//
123// If the returned diagnostics contains errors then the result may be
124// incomplete, but will always be of the requested type.
125func (s *Scope) EvalReference(ref *addrs.Reference, wantType cty.Type) (cty.Value, tfdiags.Diagnostics) {
126 var diags tfdiags.Diagnostics
127
128 // We cheat a bit here and just build an EvalContext for our requested
129 // reference with the "self" address overridden, and then pull the "self"
130 // result out of it to return.
131 ctx, ctxDiags := s.evalContext([]*addrs.Reference{ref}, ref.Subject)
132 diags = diags.Append(ctxDiags)
133 val := ctx.Variables["self"]
134 if val == cty.NilVal {
135 val = cty.DynamicVal
136 }
137
138 var convErr error
139 val, convErr = convert.Convert(val, wantType)
140 if convErr != nil {
141 val = cty.UnknownVal(wantType)
142 diags = diags.Append(&hcl.Diagnostic{
143 Severity: hcl.DiagError,
144 Summary: "Incorrect value type",
145 Detail: fmt.Sprintf("Invalid expression value: %s.", tfdiags.FormatError(convErr)),
146 Subject: ref.SourceRange.ToHCL().Ptr(),
147 })
148 }
149
150 return val, diags
151}
152
153// EvalContext constructs a HCL expression evaluation context whose variable
154// scope contains sufficient values to satisfy the given set of references.
155//
156// Most callers should prefer to use the evaluation helper methods that
157// this type offers, but this is here for less common situations where the
158// caller will handle the evaluation calls itself.
159func (s *Scope) EvalContext(refs []*addrs.Reference) (*hcl.EvalContext, tfdiags.Diagnostics) {
160 return s.evalContext(refs, s.SelfAddr)
161}
162
163func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceable) (*hcl.EvalContext, tfdiags.Diagnostics) {
164 if s == nil {
165 panic("attempt to construct EvalContext for nil Scope")
166 }
167
168 var diags tfdiags.Diagnostics
169 vals := make(map[string]cty.Value)
170 funcs := s.Functions()
171 ctx := &hcl.EvalContext{
172 Variables: vals,
173 Functions: funcs,
174 }
175
176 if len(refs) == 0 {
177 // Easy path for common case where there are no references at all.
178 return ctx, diags
179 }
180
181 // First we'll do static validation of the references. This catches things
182 // early that might otherwise not get caught due to unknown values being
183 // present in the scope during planning.
184 if staticDiags := s.Data.StaticValidateReferences(refs, selfAddr); staticDiags.HasErrors() {
185 diags = diags.Append(staticDiags)
186 return ctx, diags
187 }
188
189 // The reference set we are given has not been de-duped, and so there can
190 // be redundant requests in it for two reasons:
191 // - The same item is referenced multiple times
192 // - Both an item and that item's container are separately referenced.
193 // We will still visit every reference here and ask our data source for
194 // it, since that allows us to gather a full set of any errors and
195 // warnings, but once we've gathered all the data we'll then skip anything
196 // that's redundant in the process of populating our values map.
197 dataResources := map[string]map[string]map[addrs.InstanceKey]cty.Value{}
198 managedResources := map[string]map[string]map[addrs.InstanceKey]cty.Value{}
199 wholeModules := map[string]map[addrs.InstanceKey]cty.Value{}
200 moduleOutputs := map[string]map[addrs.InstanceKey]map[string]cty.Value{}
201 inputVariables := map[string]cty.Value{}
202 localValues := map[string]cty.Value{}
203 pathAttrs := map[string]cty.Value{}
204 terraformAttrs := map[string]cty.Value{}
205 countAttrs := map[string]cty.Value{}
206 var self cty.Value
207
208 for _, ref := range refs {
209 rng := ref.SourceRange
210 isSelf := false
211
212 rawSubj := ref.Subject
213 if rawSubj == addrs.Self {
214 if selfAddr == nil {
215 diags = diags.Append(&hcl.Diagnostic{
216 Severity: hcl.DiagError,
217 Summary: `Invalid "self" reference`,
218 // This detail message mentions some current practice that
219 // this codepath doesn't really "know about". If the "self"
220 // object starts being supported in more contexts later then
221 // we'll need to adjust this message.
222 Detail: `The "self" object is not available in this context. This object can be used only in resource provisioner and connection blocks.`,
223 Subject: ref.SourceRange.ToHCL().Ptr(),
224 })
225 continue
226 }
227
228 // Treat "self" as an alias for the configured self address.
229 rawSubj = selfAddr
230 isSelf = true
231
232 if rawSubj == addrs.Self {
233 // Programming error: the self address cannot alias itself.
234 panic("scope SelfAddr attempting to alias itself")
235 }
236 }
237
238 // This type switch must cover all of the "Referenceable" implementations
239 // in package addrs.
240 switch subj := rawSubj.(type) {
241
242 case addrs.ResourceInstance:
243 var into map[string]map[string]map[addrs.InstanceKey]cty.Value
244 switch subj.Resource.Mode {
245 case addrs.ManagedResourceMode:
246 into = managedResources
247 case addrs.DataResourceMode:
248 into = dataResources
249 default:
250 panic(fmt.Errorf("unsupported ResourceMode %s", subj.Resource.Mode))
251 }
252
253 val, valDiags := normalizeRefValue(s.Data.GetResourceInstance(subj, rng))
254 diags = diags.Append(valDiags)
255
256 r := subj.Resource
257 if into[r.Type] == nil {
258 into[r.Type] = make(map[string]map[addrs.InstanceKey]cty.Value)
259 }
260 if into[r.Type][r.Name] == nil {
261 into[r.Type][r.Name] = make(map[addrs.InstanceKey]cty.Value)
262 }
263 into[r.Type][r.Name][subj.Key] = val
264 if isSelf {
265 self = val
266 }
267
268 case addrs.ModuleCallInstance:
269 val, valDiags := normalizeRefValue(s.Data.GetModuleInstance(subj, rng))
270 diags = diags.Append(valDiags)
271
272 if wholeModules[subj.Call.Name] == nil {
273 wholeModules[subj.Call.Name] = make(map[addrs.InstanceKey]cty.Value)
274 }
275 wholeModules[subj.Call.Name][subj.Key] = val
276 if isSelf {
277 self = val
278 }
279
280 case addrs.ModuleCallOutput:
281 val, valDiags := normalizeRefValue(s.Data.GetModuleInstanceOutput(subj, rng))
282 diags = diags.Append(valDiags)
283
284 callName := subj.Call.Call.Name
285 callKey := subj.Call.Key
286 if moduleOutputs[callName] == nil {
287 moduleOutputs[callName] = make(map[addrs.InstanceKey]map[string]cty.Value)
288 }
289 if moduleOutputs[callName][callKey] == nil {
290 moduleOutputs[callName][callKey] = make(map[string]cty.Value)
291 }
292 moduleOutputs[callName][callKey][subj.Name] = val
293 if isSelf {
294 self = val
295 }
296
297 case addrs.InputVariable:
298 val, valDiags := normalizeRefValue(s.Data.GetInputVariable(subj, rng))
299 diags = diags.Append(valDiags)
300 inputVariables[subj.Name] = val
301 if isSelf {
302 self = val
303 }
304
305 case addrs.LocalValue:
306 val, valDiags := normalizeRefValue(s.Data.GetLocalValue(subj, rng))
307 diags = diags.Append(valDiags)
308 localValues[subj.Name] = val
309 if isSelf {
310 self = val
311 }
312
313 case addrs.PathAttr:
314 val, valDiags := normalizeRefValue(s.Data.GetPathAttr(subj, rng))
315 diags = diags.Append(valDiags)
316 pathAttrs[subj.Name] = val
317 if isSelf {
318 self = val
319 }
320
321 case addrs.TerraformAttr:
322 val, valDiags := normalizeRefValue(s.Data.GetTerraformAttr(subj, rng))
323 diags = diags.Append(valDiags)
324 terraformAttrs[subj.Name] = val
325 if isSelf {
326 self = val
327 }
328
329 case addrs.CountAttr:
330 val, valDiags := normalizeRefValue(s.Data.GetCountAttr(subj, rng))
331 diags = diags.Append(valDiags)
332 countAttrs[subj.Name] = val
333 if isSelf {
334 self = val
335 }
336
337 default:
338 // Should never happen
339 panic(fmt.Errorf("Scope.buildEvalContext cannot handle address type %T", rawSubj))
340 }
341 }
342
343 for k, v := range buildResourceObjects(managedResources) {
344 vals[k] = v
345 }
346 vals["data"] = cty.ObjectVal(buildResourceObjects(dataResources))
347 vals["module"] = cty.ObjectVal(buildModuleObjects(wholeModules, moduleOutputs))
348 vals["var"] = cty.ObjectVal(inputVariables)
349 vals["local"] = cty.ObjectVal(localValues)
350 vals["path"] = cty.ObjectVal(pathAttrs)
351 vals["terraform"] = cty.ObjectVal(terraformAttrs)
352 vals["count"] = cty.ObjectVal(countAttrs)
353 if self != cty.NilVal {
354 vals["self"] = self
355 }
356
357 return ctx, diags
358}
359
360func buildResourceObjects(resources map[string]map[string]map[addrs.InstanceKey]cty.Value) map[string]cty.Value {
361 vals := make(map[string]cty.Value)
362 for typeName, names := range resources {
363 nameVals := make(map[string]cty.Value)
364 for name, keys := range names {
365 nameVals[name] = buildInstanceObjects(keys)
366 }
367 vals[typeName] = cty.ObjectVal(nameVals)
368 }
369 return vals
370}
371
372func buildModuleObjects(wholeModules map[string]map[addrs.InstanceKey]cty.Value, moduleOutputs map[string]map[addrs.InstanceKey]map[string]cty.Value) map[string]cty.Value {
373 vals := make(map[string]cty.Value)
374
375 for name, keys := range wholeModules {
376 vals[name] = buildInstanceObjects(keys)
377 }
378
379 for name, keys := range moduleOutputs {
380 if _, exists := wholeModules[name]; exists {
381 // If we also have a whole module value for this name then we'll
382 // skip this since the individual outputs are embedded in that result.
383 continue
384 }
385
386 // The shape of this collection isn't compatible with buildInstanceObjects,
387 // but rather than replicating most of the buildInstanceObjects logic
388 // here we'll instead first transform the structure to be what that
389 // function expects and then use it. This is a little wasteful, but
390 // we do not expect this these maps to be large and so the extra work
391 // here should not hurt too much.
392 flattened := make(map[addrs.InstanceKey]cty.Value, len(keys))
393 for k, vals := range keys {
394 flattened[k] = cty.ObjectVal(vals)
395 }
396 vals[name] = buildInstanceObjects(flattened)
397 }
398
399 return vals
400}
401
402func buildInstanceObjects(keys map[addrs.InstanceKey]cty.Value) cty.Value {
403 if val, exists := keys[addrs.NoKey]; exists {
404 // If present, a "no key" value supersedes all other values,
405 // since they should be embedded inside it.
406 return val
407 }
408
409 // If we only have individual values then we need to construct
410 // either a list or a map, depending on what sort of keys we
411 // have.
412 haveInt := false
413 haveString := false
414 maxInt := 0
415
416 for k := range keys {
417 switch tk := k.(type) {
418 case addrs.IntKey:
419 haveInt = true
420 if int(tk) > maxInt {
421 maxInt = int(tk)
422 }
423 case addrs.StringKey:
424 haveString = true
425 }
426 }
427
428 // We should either have ints or strings and not both, but
429 // if we have both then we'll prefer strings and let the
430 // language interpreter try to convert the int keys into
431 // strings in a map.
432 switch {
433 case haveString:
434 vals := make(map[string]cty.Value)
435 for k, v := range keys {
436 switch tk := k.(type) {
437 case addrs.StringKey:
438 vals[string(tk)] = v
439 case addrs.IntKey:
440 sk := strconv.Itoa(int(tk))
441 vals[sk] = v
442 }
443 }
444 return cty.ObjectVal(vals)
445 case haveInt:
446 // We'll make a tuple that is long enough for our maximum
447 // index value. It doesn't matter if we end up shorter than
448 // the number of instances because if length(...) were
449 // being evaluated we would've got a NoKey reference and
450 // thus not ended up in this codepath at all.
451 vals := make([]cty.Value, maxInt+1)
452 for i := range vals {
453 if v, exists := keys[addrs.IntKey(i)]; exists {
454 vals[i] = v
455 } else {
456 // Just a placeholder, since nothing will access this anyway
457 vals[i] = cty.DynamicVal
458 }
459 }
460 return cty.TupleVal(vals)
461 default:
462 // Should never happen because there are no other key types.
463 log.Printf("[ERROR] strange makeInstanceObjects call with no supported key types")
464 return cty.EmptyObjectVal
465 }
466}
467
468func normalizeRefValue(val cty.Value, diags tfdiags.Diagnostics) (cty.Value, tfdiags.Diagnostics) {
469 if diags.HasErrors() {
470 // If there are errors then we will force an unknown result so that
471 // we can still evaluate and catch type errors but we'll avoid
472 // producing redundant re-statements of the same errors we've already
473 // dealt with here.
474 return cty.UnknownVal(val.Type()), diags
475 }
476 return val, diags
477}
diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/cidr.go b/vendor/github.com/hashicorp/terraform/lang/funcs/cidr.go
new file mode 100644
index 0000000..6ce8aa9
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/funcs/cidr.go
@@ -0,0 +1,129 @@
1package funcs
2
3import (
4 "fmt"
5 "net"
6
7 "github.com/apparentlymart/go-cidr/cidr"
8 "github.com/zclconf/go-cty/cty"
9 "github.com/zclconf/go-cty/cty/function"
10 "github.com/zclconf/go-cty/cty/gocty"
11)
12
13// CidrHostFunc contructs a function that calculates a full host IP address
14// within a given IP network address prefix.
15var CidrHostFunc = function.New(&function.Spec{
16 Params: []function.Parameter{
17 {
18 Name: "prefix",
19 Type: cty.String,
20 },
21 {
22 Name: "hostnum",
23 Type: cty.Number,
24 },
25 },
26 Type: function.StaticReturnType(cty.String),
27 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
28 var hostNum int
29 if err := gocty.FromCtyValue(args[1], &hostNum); err != nil {
30 return cty.UnknownVal(cty.String), err
31 }
32 _, network, err := net.ParseCIDR(args[0].AsString())
33 if err != nil {
34 return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
35 }
36
37 ip, err := cidr.Host(network, hostNum)
38 if err != nil {
39 return cty.UnknownVal(cty.String), err
40 }
41
42 return cty.StringVal(ip.String()), nil
43 },
44})
45
46// CidrNetmaskFunc contructs a function that converts an IPv4 address prefix given
47// in CIDR notation into a subnet mask address.
48var CidrNetmaskFunc = function.New(&function.Spec{
49 Params: []function.Parameter{
50 {
51 Name: "prefix",
52 Type: cty.String,
53 },
54 },
55 Type: function.StaticReturnType(cty.String),
56 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
57 _, network, err := net.ParseCIDR(args[0].AsString())
58 if err != nil {
59 return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
60 }
61
62 return cty.StringVal(net.IP(network.Mask).String()), nil
63 },
64})
65
66// CidrSubnetFunc contructs a function that calculates a subnet address within
67// a given IP network address prefix.
68var CidrSubnetFunc = function.New(&function.Spec{
69 Params: []function.Parameter{
70 {
71 Name: "prefix",
72 Type: cty.String,
73 },
74 {
75 Name: "newbits",
76 Type: cty.Number,
77 },
78 {
79 Name: "netnum",
80 Type: cty.Number,
81 },
82 },
83 Type: function.StaticReturnType(cty.String),
84 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
85 var newbits int
86 if err := gocty.FromCtyValue(args[1], &newbits); err != nil {
87 return cty.UnknownVal(cty.String), err
88 }
89 var netnum int
90 if err := gocty.FromCtyValue(args[2], &netnum); err != nil {
91 return cty.UnknownVal(cty.String), err
92 }
93
94 _, network, err := net.ParseCIDR(args[0].AsString())
95 if err != nil {
96 return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
97 }
98
99 // For portability with 32-bit systems where the subnet number
100 // will be a 32-bit int, we only allow extension of 32 bits in
101 // one call even if we're running on a 64-bit machine.
102 // (Of course, this is significant only for IPv6.)
103 if newbits > 32 {
104 return cty.UnknownVal(cty.String), fmt.Errorf("may not extend prefix by more than 32 bits")
105 }
106
107 newNetwork, err := cidr.Subnet(network, newbits, netnum)
108 if err != nil {
109 return cty.UnknownVal(cty.String), err
110 }
111
112 return cty.StringVal(newNetwork.String()), nil
113 },
114})
115
116// CidrHost calculates a full host IP address within a given IP network address prefix.
117func CidrHost(prefix, hostnum cty.Value) (cty.Value, error) {
118 return CidrHostFunc.Call([]cty.Value{prefix, hostnum})
119}
120
121// CidrNetmask converts an IPv4 address prefix given in CIDR notation into a subnet mask address.
122func CidrNetmask(prefix cty.Value) (cty.Value, error) {
123 return CidrNetmaskFunc.Call([]cty.Value{prefix})
124}
125
126// CidrSubnet calculates a subnet address within a given IP network address prefix.
127func CidrSubnet(prefix, newbits, netnum cty.Value) (cty.Value, error) {
128 return CidrSubnetFunc.Call([]cty.Value{prefix, newbits, netnum})
129}
diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/collection.go b/vendor/github.com/hashicorp/terraform/lang/funcs/collection.go
new file mode 100644
index 0000000..71b7a84
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/funcs/collection.go
@@ -0,0 +1,1511 @@
1package funcs
2
3import (
4 "errors"
5 "fmt"
6 "sort"
7
8 "github.com/zclconf/go-cty/cty"
9 "github.com/zclconf/go-cty/cty/convert"
10 "github.com/zclconf/go-cty/cty/function"
11 "github.com/zclconf/go-cty/cty/function/stdlib"
12 "github.com/zclconf/go-cty/cty/gocty"
13)
14
15var ElementFunc = function.New(&function.Spec{
16 Params: []function.Parameter{
17 {
18 Name: "list",
19 Type: cty.DynamicPseudoType,
20 },
21 {
22 Name: "index",
23 Type: cty.Number,
24 },
25 },
26 Type: func(args []cty.Value) (cty.Type, error) {
27 list := args[0]
28 listTy := list.Type()
29 switch {
30 case listTy.IsListType():
31 return listTy.ElementType(), nil
32 case listTy.IsTupleType():
33 if !args[1].IsKnown() {
34 // If the index isn't known yet then we can't predict the
35 // result type since each tuple element can have its own type.
36 return cty.DynamicPseudoType, nil
37 }
38
39 etys := listTy.TupleElementTypes()
40 var index int
41 err := gocty.FromCtyValue(args[1], &index)
42 if err != nil {
43 // e.g. fractional number where whole number is required
44 return cty.DynamicPseudoType, fmt.Errorf("invalid index: %s", err)
45 }
46 if len(etys) == 0 {
47 return cty.DynamicPseudoType, errors.New("cannot use element function with an empty list")
48 }
49 index = index % len(etys)
50 return etys[index], nil
51 default:
52 return cty.DynamicPseudoType, fmt.Errorf("cannot read elements from %s", listTy.FriendlyName())
53 }
54 },
55 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
56 var index int
57 err := gocty.FromCtyValue(args[1], &index)
58 if err != nil {
59 // can't happen because we checked this in the Type function above
60 return cty.DynamicVal, fmt.Errorf("invalid index: %s", err)
61 }
62
63 if !args[0].IsKnown() {
64 return cty.UnknownVal(retType), nil
65 }
66
67 l := args[0].LengthInt()
68 if l == 0 {
69 return cty.DynamicVal, errors.New("cannot use element function with an empty list")
70 }
71 index = index % l
72
73 // We did all the necessary type checks in the type function above,
74 // so this is guaranteed not to fail.
75 return args[0].Index(cty.NumberIntVal(int64(index))), nil
76 },
77})
78
79var LengthFunc = function.New(&function.Spec{
80 Params: []function.Parameter{
81 {
82 Name: "value",
83 Type: cty.DynamicPseudoType,
84 AllowDynamicType: true,
85 AllowUnknown: true,
86 },
87 },
88 Type: func(args []cty.Value) (cty.Type, error) {
89 collTy := args[0].Type()
90 switch {
91 case collTy == cty.String || collTy.IsTupleType() || collTy.IsObjectType() || collTy.IsListType() || collTy.IsMapType() || collTy.IsSetType() || collTy == cty.DynamicPseudoType:
92 return cty.Number, nil
93 default:
94 return cty.Number, errors.New("argument must be a string, a collection type, or a structural type")
95 }
96 },
97 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
98 coll := args[0]
99 collTy := args[0].Type()
100 switch {
101 case collTy == cty.DynamicPseudoType:
102 return cty.UnknownVal(cty.Number), nil
103 case collTy.IsTupleType():
104 l := len(collTy.TupleElementTypes())
105 return cty.NumberIntVal(int64(l)), nil
106 case collTy.IsObjectType():
107 l := len(collTy.AttributeTypes())
108 return cty.NumberIntVal(int64(l)), nil
109 case collTy == cty.String:
110 // We'll delegate to the cty stdlib strlen function here, because
111 // it deals with all of the complexities of tokenizing unicode
112 // grapheme clusters.
113 return stdlib.Strlen(coll)
114 case collTy.IsListType() || collTy.IsSetType() || collTy.IsMapType():
115 return coll.Length(), nil
116 default:
117 // Should never happen, because of the checks in our Type func above
118 return cty.UnknownVal(cty.Number), errors.New("impossible value type for length(...)")
119 }
120 },
121})
122
123// CoalesceFunc constructs a function that takes any number of arguments and
124// returns the first one that isn't empty. This function was copied from go-cty
125// stdlib and modified so that it returns the first *non-empty* non-null element
126// from a sequence, instead of merely the first non-null.
127var CoalesceFunc = function.New(&function.Spec{
128 Params: []function.Parameter{},
129 VarParam: &function.Parameter{
130 Name: "vals",
131 Type: cty.DynamicPseudoType,
132 AllowUnknown: true,
133 AllowDynamicType: true,
134 AllowNull: true,
135 },
136 Type: func(args []cty.Value) (ret cty.Type, err error) {
137 argTypes := make([]cty.Type, len(args))
138 for i, val := range args {
139 argTypes[i] = val.Type()
140 }
141 retType, _ := convert.UnifyUnsafe(argTypes)
142 if retType == cty.NilType {
143 return cty.NilType, errors.New("all arguments must have the same type")
144 }
145 return retType, nil
146 },
147 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
148 for _, argVal := range args {
149 // We already know this will succeed because of the checks in our Type func above
150 argVal, _ = convert.Convert(argVal, retType)
151 if !argVal.IsKnown() {
152 return cty.UnknownVal(retType), nil
153 }
154 if argVal.IsNull() {
155 continue
156 }
157 if retType == cty.String && argVal.RawEquals(cty.StringVal("")) {
158 continue
159 }
160
161 return argVal, nil
162 }
163 return cty.NilVal, errors.New("no non-null, non-empty-string arguments")
164 },
165})
166
167// CoalesceListFunc constructs a function that takes any number of list arguments
168// and returns the first one that isn't empty.
169var CoalesceListFunc = function.New(&function.Spec{
170 Params: []function.Parameter{},
171 VarParam: &function.Parameter{
172 Name: "vals",
173 Type: cty.DynamicPseudoType,
174 AllowUnknown: true,
175 AllowDynamicType: true,
176 AllowNull: true,
177 },
178 Type: func(args []cty.Value) (ret cty.Type, err error) {
179 if len(args) == 0 {
180 return cty.NilType, errors.New("at least one argument is required")
181 }
182
183 argTypes := make([]cty.Type, len(args))
184
185 for i, arg := range args {
186 // if any argument is unknown, we can't be certain know which type we will return
187 if !arg.IsKnown() {
188 return cty.DynamicPseudoType, nil
189 }
190 ty := arg.Type()
191
192 if !ty.IsListType() && !ty.IsTupleType() {
193 return cty.NilType, errors.New("coalescelist arguments must be lists or tuples")
194 }
195
196 argTypes[i] = arg.Type()
197 }
198
199 last := argTypes[0]
200 // If there are mixed types, we have to return a dynamic type.
201 for _, next := range argTypes[1:] {
202 if !next.Equals(last) {
203 return cty.DynamicPseudoType, nil
204 }
205 }
206
207 return last, nil
208 },
209 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
210 for _, arg := range args {
211 if !arg.IsKnown() {
212 // If we run into an unknown list at some point, we can't
213 // predict the final result yet. (If there's a known, non-empty
214 // arg before this then we won't get here.)
215 return cty.UnknownVal(retType), nil
216 }
217
218 if arg.LengthInt() > 0 {
219 return arg, nil
220 }
221 }
222
223 return cty.NilVal, errors.New("no non-null arguments")
224 },
225})
226
227// CompactFunc constructs a function that takes a list of strings and returns a new list
228// with any empty string elements removed.
229var CompactFunc = function.New(&function.Spec{
230 Params: []function.Parameter{
231 {
232 Name: "list",
233 Type: cty.List(cty.String),
234 },
235 },
236 Type: function.StaticReturnType(cty.List(cty.String)),
237 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
238 listVal := args[0]
239 if !listVal.IsWhollyKnown() {
240 // If some of the element values aren't known yet then we
241 // can't yet return a compacted list
242 return cty.UnknownVal(retType), nil
243 }
244
245 var outputList []cty.Value
246
247 for it := listVal.ElementIterator(); it.Next(); {
248 _, v := it.Element()
249 if v.AsString() == "" {
250 continue
251 }
252 outputList = append(outputList, v)
253 }
254
255 if len(outputList) == 0 {
256 return cty.ListValEmpty(cty.String), nil
257 }
258
259 return cty.ListVal(outputList), nil
260 },
261})
262
263// ContainsFunc constructs a function that determines whether a given list or
264// set contains a given single value as one of its elements.
265var ContainsFunc = function.New(&function.Spec{
266 Params: []function.Parameter{
267 {
268 Name: "list",
269 Type: cty.DynamicPseudoType,
270 },
271 {
272 Name: "value",
273 Type: cty.DynamicPseudoType,
274 },
275 },
276 Type: function.StaticReturnType(cty.Bool),
277 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
278 arg := args[0]
279 ty := arg.Type()
280
281 if !ty.IsListType() && !ty.IsTupleType() && !ty.IsSetType() {
282 return cty.NilVal, errors.New("argument must be list, tuple, or set")
283 }
284
285 _, err = Index(cty.TupleVal(arg.AsValueSlice()), args[1])
286 if err != nil {
287 return cty.False, nil
288 }
289
290 return cty.True, nil
291 },
292})
293
294// IndexFunc constructs a function that finds the element index for a given value in a list.
295var IndexFunc = function.New(&function.Spec{
296 Params: []function.Parameter{
297 {
298 Name: "list",
299 Type: cty.DynamicPseudoType,
300 },
301 {
302 Name: "value",
303 Type: cty.DynamicPseudoType,
304 },
305 },
306 Type: function.StaticReturnType(cty.Number),
307 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
308 if !(args[0].Type().IsListType() || args[0].Type().IsTupleType()) {
309 return cty.NilVal, errors.New("argument must be a list or tuple")
310 }
311
312 if !args[0].IsKnown() {
313 return cty.UnknownVal(cty.Number), nil
314 }
315
316 if args[0].LengthInt() == 0 { // Easy path
317 return cty.NilVal, errors.New("cannot search an empty list")
318 }
319
320 for it := args[0].ElementIterator(); it.Next(); {
321 i, v := it.Element()
322 eq, err := stdlib.Equal(v, args[1])
323 if err != nil {
324 return cty.NilVal, err
325 }
326 if !eq.IsKnown() {
327 return cty.UnknownVal(cty.Number), nil
328 }
329 if eq.True() {
330 return i, nil
331 }
332 }
333 return cty.NilVal, errors.New("item not found")
334
335 },
336})
337
338// DistinctFunc constructs a function that takes a list and returns a new list
339// with any duplicate elements removed.
340var DistinctFunc = function.New(&function.Spec{
341 Params: []function.Parameter{
342 {
343 Name: "list",
344 Type: cty.List(cty.DynamicPseudoType),
345 },
346 },
347 Type: func(args []cty.Value) (cty.Type, error) {
348 return args[0].Type(), nil
349 },
350 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
351 listVal := args[0]
352
353 if !listVal.IsWhollyKnown() {
354 return cty.UnknownVal(retType), nil
355 }
356 var list []cty.Value
357
358 for it := listVal.ElementIterator(); it.Next(); {
359 _, v := it.Element()
360 list, err = appendIfMissing(list, v)
361 if err != nil {
362 return cty.NilVal, err
363 }
364 }
365
366 return cty.ListVal(list), nil
367 },
368})
369
370// ChunklistFunc constructs a function that splits a single list into fixed-size chunks,
371// returning a list of lists.
372var ChunklistFunc = function.New(&function.Spec{
373 Params: []function.Parameter{
374 {
375 Name: "list",
376 Type: cty.List(cty.DynamicPseudoType),
377 },
378 {
379 Name: "size",
380 Type: cty.Number,
381 },
382 },
383 Type: func(args []cty.Value) (cty.Type, error) {
384 return cty.List(args[0].Type()), nil
385 },
386 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
387 listVal := args[0]
388 if !listVal.IsKnown() {
389 return cty.UnknownVal(retType), nil
390 }
391
392 var size int
393 err = gocty.FromCtyValue(args[1], &size)
394 if err != nil {
395 return cty.NilVal, fmt.Errorf("invalid index: %s", err)
396 }
397
398 if size < 0 {
399 return cty.NilVal, errors.New("the size argument must be positive")
400 }
401
402 output := make([]cty.Value, 0)
403
404 // if size is 0, returns a list made of the initial list
405 if size == 0 {
406 output = append(output, listVal)
407 return cty.ListVal(output), nil
408 }
409
410 chunk := make([]cty.Value, 0)
411
412 l := args[0].LengthInt()
413 i := 0
414
415 for it := listVal.ElementIterator(); it.Next(); {
416 _, v := it.Element()
417 chunk = append(chunk, v)
418
419 // Chunk when index isn't 0, or when reaching the values's length
420 if (i+1)%size == 0 || (i+1) == l {
421 output = append(output, cty.ListVal(chunk))
422 chunk = make([]cty.Value, 0)
423 }
424 i++
425 }
426
427 return cty.ListVal(output), nil
428 },
429})
430
431// FlattenFunc constructs a function that takes a list and replaces any elements
432// that are lists with a flattened sequence of the list contents.
433var FlattenFunc = function.New(&function.Spec{
434 Params: []function.Parameter{
435 {
436 Name: "list",
437 Type: cty.DynamicPseudoType,
438 },
439 },
440 Type: func(args []cty.Value) (cty.Type, error) {
441 if !args[0].IsWhollyKnown() {
442 return cty.DynamicPseudoType, nil
443 }
444
445 argTy := args[0].Type()
446 if !argTy.IsListType() && !argTy.IsSetType() && !argTy.IsTupleType() {
447 return cty.NilType, errors.New("can only flatten lists, sets and tuples")
448 }
449
450 retVal, known := flattener(args[0])
451 if !known {
452 return cty.DynamicPseudoType, nil
453 }
454
455 tys := make([]cty.Type, len(retVal))
456 for i, ty := range retVal {
457 tys[i] = ty.Type()
458 }
459 return cty.Tuple(tys), nil
460 },
461 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
462 inputList := args[0]
463 if inputList.LengthInt() == 0 {
464 return cty.EmptyTupleVal, nil
465 }
466
467 out, known := flattener(inputList)
468 if !known {
469 return cty.UnknownVal(retType), nil
470 }
471
472 return cty.TupleVal(out), nil
473 },
474})
475
476// Flatten until it's not a cty.List, and return whether the value is known.
477// We can flatten lists with unknown values, as long as they are not
478// lists themselves.
479func flattener(flattenList cty.Value) ([]cty.Value, bool) {
480 out := make([]cty.Value, 0)
481 for it := flattenList.ElementIterator(); it.Next(); {
482 _, val := it.Element()
483 if val.Type().IsListType() || val.Type().IsSetType() || val.Type().IsTupleType() {
484 if !val.IsKnown() {
485 return out, false
486 }
487
488 res, known := flattener(val)
489 if !known {
490 return res, known
491 }
492 out = append(out, res...)
493 } else {
494 out = append(out, val)
495 }
496 }
497 return out, true
498}
499
500// KeysFunc constructs a function that takes a map and returns a sorted list of the map keys.
501var KeysFunc = function.New(&function.Spec{
502 Params: []function.Parameter{
503 {
504 Name: "inputMap",
505 Type: cty.DynamicPseudoType,
506 AllowUnknown: true,
507 },
508 },
509 Type: func(args []cty.Value) (cty.Type, error) {
510 ty := args[0].Type()
511 switch {
512 case ty.IsMapType():
513 return cty.List(cty.String), nil
514 case ty.IsObjectType():
515 atys := ty.AttributeTypes()
516 if len(atys) == 0 {
517 return cty.EmptyTuple, nil
518 }
519 // All of our result elements will be strings, and atys just
520 // decides how many there are.
521 etys := make([]cty.Type, len(atys))
522 for i := range etys {
523 etys[i] = cty.String
524 }
525 return cty.Tuple(etys), nil
526 default:
527 return cty.DynamicPseudoType, function.NewArgErrorf(0, "must have map or object type")
528 }
529 },
530 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
531 m := args[0]
532 var keys []cty.Value
533
534 switch {
535 case m.Type().IsObjectType():
536 // In this case we allow unknown values so we must work only with
537 // the attribute _types_, not with the value itself.
538 var names []string
539 for name := range m.Type().AttributeTypes() {
540 names = append(names, name)
541 }
542 sort.Strings(names) // same ordering guaranteed by cty's ElementIterator
543 if len(names) == 0 {
544 return cty.EmptyTupleVal, nil
545 }
546 keys = make([]cty.Value, len(names))
547 for i, name := range names {
548 keys[i] = cty.StringVal(name)
549 }
550 return cty.TupleVal(keys), nil
551 default:
552 if !m.IsKnown() {
553 return cty.UnknownVal(retType), nil
554 }
555
556 // cty guarantees that ElementIterator will iterate in lexicographical
557 // order by key.
558 for it := args[0].ElementIterator(); it.Next(); {
559 k, _ := it.Element()
560 keys = append(keys, k)
561 }
562 if len(keys) == 0 {
563 return cty.ListValEmpty(cty.String), nil
564 }
565 return cty.ListVal(keys), nil
566 }
567 },
568})
569
570// ListFunc constructs a function that takes an arbitrary number of arguments
571// and returns a list containing those values in the same order.
572//
573// This function is deprecated in Terraform v0.12
574var ListFunc = function.New(&function.Spec{
575 Params: []function.Parameter{},
576 VarParam: &function.Parameter{
577 Name: "vals",
578 Type: cty.DynamicPseudoType,
579 AllowUnknown: true,
580 AllowDynamicType: true,
581 AllowNull: true,
582 },
583 Type: func(args []cty.Value) (ret cty.Type, err error) {
584 if len(args) == 0 {
585 return cty.NilType, errors.New("at least one argument is required")
586 }
587
588 argTypes := make([]cty.Type, len(args))
589
590 for i, arg := range args {
591 argTypes[i] = arg.Type()
592 }
593
594 retType, _ := convert.UnifyUnsafe(argTypes)
595 if retType == cty.NilType {
596 return cty.NilType, errors.New("all arguments must have the same type")
597 }
598
599 return cty.List(retType), nil
600 },
601 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
602 newList := make([]cty.Value, 0, len(args))
603
604 for _, arg := range args {
605 // We already know this will succeed because of the checks in our Type func above
606 arg, _ = convert.Convert(arg, retType.ElementType())
607 newList = append(newList, arg)
608 }
609
610 return cty.ListVal(newList), nil
611 },
612})
613
614// LookupFunc constructs a function that performs dynamic lookups of map types.
615var LookupFunc = function.New(&function.Spec{
616 Params: []function.Parameter{
617 {
618 Name: "inputMap",
619 Type: cty.DynamicPseudoType,
620 },
621 {
622 Name: "key",
623 Type: cty.String,
624 },
625 },
626 VarParam: &function.Parameter{
627 Name: "default",
628 Type: cty.DynamicPseudoType,
629 AllowUnknown: true,
630 AllowDynamicType: true,
631 AllowNull: true,
632 },
633 Type: func(args []cty.Value) (ret cty.Type, err error) {
634 if len(args) < 1 || len(args) > 3 {
635 return cty.NilType, fmt.Errorf("lookup() takes two or three arguments, got %d", len(args))
636 }
637
638 ty := args[0].Type()
639
640 switch {
641 case ty.IsObjectType():
642 if !args[1].IsKnown() {
643 return cty.DynamicPseudoType, nil
644 }
645
646 key := args[1].AsString()
647 if ty.HasAttribute(key) {
648 return args[0].GetAttr(key).Type(), nil
649 } else if len(args) == 3 {
650 // if the key isn't found but a default is provided,
651 // return the default type
652 return args[2].Type(), nil
653 }
654 return cty.DynamicPseudoType, function.NewArgErrorf(0, "the given object has no attribute %q", key)
655 case ty.IsMapType():
656 return ty.ElementType(), nil
657 default:
658 return cty.NilType, function.NewArgErrorf(0, "lookup() requires a map as the first argument")
659 }
660 },
661 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
662 var defaultVal cty.Value
663 defaultValueSet := false
664
665 if len(args) == 3 {
666 defaultVal = args[2]
667 defaultValueSet = true
668 }
669
670 mapVar := args[0]
671 lookupKey := args[1].AsString()
672
673 if !mapVar.IsWhollyKnown() {
674 return cty.UnknownVal(retType), nil
675 }
676
677 if mapVar.Type().IsObjectType() {
678 if mapVar.Type().HasAttribute(lookupKey) {
679 return mapVar.GetAttr(lookupKey), nil
680 }
681 } else if mapVar.HasIndex(cty.StringVal(lookupKey)) == cty.True {
682 v := mapVar.Index(cty.StringVal(lookupKey))
683 if ty := v.Type(); !ty.Equals(cty.NilType) {
684 switch {
685 case ty.Equals(cty.String):
686 return cty.StringVal(v.AsString()), nil
687 case ty.Equals(cty.Number):
688 return cty.NumberVal(v.AsBigFloat()), nil
689 default:
690 return cty.NilVal, errors.New("lookup() can only be used with flat lists")
691 }
692 }
693 }
694
695 if defaultValueSet {
696 defaultVal, err = convert.Convert(defaultVal, retType)
697 if err != nil {
698 return cty.NilVal, err
699 }
700 return defaultVal, nil
701 }
702
703 return cty.UnknownVal(cty.DynamicPseudoType), fmt.Errorf(
704 "lookup failed to find '%s'", lookupKey)
705 },
706})
707
708// MapFunc constructs a function that takes an even number of arguments and
709// returns a map whose elements are constructed from consecutive pairs of arguments.
710//
711// This function is deprecated in Terraform v0.12
712var MapFunc = function.New(&function.Spec{
713 Params: []function.Parameter{},
714 VarParam: &function.Parameter{
715 Name: "vals",
716 Type: cty.DynamicPseudoType,
717 AllowUnknown: true,
718 AllowDynamicType: true,
719 AllowNull: true,
720 },
721 Type: func(args []cty.Value) (ret cty.Type, err error) {
722 if len(args) < 2 || len(args)%2 != 0 {
723 return cty.NilType, fmt.Errorf("map requires an even number of two or more arguments, got %d", len(args))
724 }
725
726 argTypes := make([]cty.Type, len(args)/2)
727 index := 0
728
729 for i := 0; i < len(args); i += 2 {
730 argTypes[index] = args[i+1].Type()
731 index++
732 }
733
734 valType, _ := convert.UnifyUnsafe(argTypes)
735 if valType == cty.NilType {
736 return cty.NilType, errors.New("all arguments must have the same type")
737 }
738
739 return cty.Map(valType), nil
740 },
741 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
742 for _, arg := range args {
743 if !arg.IsWhollyKnown() {
744 return cty.UnknownVal(retType), nil
745 }
746 }
747
748 outputMap := make(map[string]cty.Value)
749
750 for i := 0; i < len(args); i += 2 {
751
752 key := args[i].AsString()
753
754 err := gocty.FromCtyValue(args[i], &key)
755 if err != nil {
756 return cty.NilVal, err
757 }
758
759 val := args[i+1]
760
761 var variable cty.Value
762 err = gocty.FromCtyValue(val, &variable)
763 if err != nil {
764 return cty.NilVal, err
765 }
766
767 // We already know this will succeed because of the checks in our Type func above
768 variable, _ = convert.Convert(variable, retType.ElementType())
769
770 // Check for duplicate keys
771 if _, ok := outputMap[key]; ok {
772 return cty.NilVal, fmt.Errorf("argument %d is a duplicate key: %q", i+1, key)
773 }
774 outputMap[key] = variable
775 }
776
777 return cty.MapVal(outputMap), nil
778 },
779})
780
781// MatchkeysFunc constructs a function that constructs a new list by taking a
782// subset of elements from one list whose indexes match the corresponding
783// indexes of values in another list.
784var MatchkeysFunc = function.New(&function.Spec{
785 Params: []function.Parameter{
786 {
787 Name: "values",
788 Type: cty.List(cty.DynamicPseudoType),
789 },
790 {
791 Name: "keys",
792 Type: cty.List(cty.DynamicPseudoType),
793 },
794 {
795 Name: "searchset",
796 Type: cty.List(cty.DynamicPseudoType),
797 },
798 },
799 Type: func(args []cty.Value) (cty.Type, error) {
800 if !args[1].Type().Equals(args[2].Type()) {
801 return cty.NilType, errors.New("lists must be of the same type")
802 }
803
804 return args[0].Type(), nil
805 },
806 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
807 if !args[0].IsKnown() {
808 return cty.UnknownVal(cty.List(retType.ElementType())), nil
809 }
810
811 if args[0].LengthInt() != args[1].LengthInt() {
812 return cty.ListValEmpty(retType.ElementType()), errors.New("length of keys and values should be equal")
813 }
814
815 output := make([]cty.Value, 0)
816
817 values := args[0]
818 keys := args[1]
819 searchset := args[2]
820
821 // if searchset is empty, return an empty list.
822 if searchset.LengthInt() == 0 {
823 return cty.ListValEmpty(retType.ElementType()), nil
824 }
825
826 if !values.IsWhollyKnown() || !keys.IsWhollyKnown() {
827 return cty.UnknownVal(retType), nil
828 }
829
830 i := 0
831 for it := keys.ElementIterator(); it.Next(); {
832 _, key := it.Element()
833 for iter := searchset.ElementIterator(); iter.Next(); {
834 _, search := iter.Element()
835 eq, err := stdlib.Equal(key, search)
836 if err != nil {
837 return cty.NilVal, err
838 }
839 if !eq.IsKnown() {
840 return cty.ListValEmpty(retType.ElementType()), nil
841 }
842 if eq.True() {
843 v := values.Index(cty.NumberIntVal(int64(i)))
844 output = append(output, v)
845 break
846 }
847 }
848 i++
849 }
850
851 // if we haven't matched any key, then output is an empty list.
852 if len(output) == 0 {
853 return cty.ListValEmpty(retType.ElementType()), nil
854 }
855 return cty.ListVal(output), nil
856 },
857})
858
859// MergeFunc constructs a function that takes an arbitrary number of maps and
860// returns a single map that contains a merged set of elements from all of the maps.
861//
862// If more than one given map defines the same key then the one that is later in
863// the argument sequence takes precedence.
864var MergeFunc = function.New(&function.Spec{
865 Params: []function.Parameter{},
866 VarParam: &function.Parameter{
867 Name: "maps",
868 Type: cty.DynamicPseudoType,
869 AllowDynamicType: true,
870 AllowNull: true,
871 },
872 Type: function.StaticReturnType(cty.DynamicPseudoType),
873 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
874 outputMap := make(map[string]cty.Value)
875
876 for _, arg := range args {
877 if !arg.IsWhollyKnown() {
878 return cty.UnknownVal(retType), nil
879 }
880 if !arg.Type().IsObjectType() && !arg.Type().IsMapType() {
881 return cty.NilVal, fmt.Errorf("arguments must be maps or objects, got %#v", arg.Type().FriendlyName())
882 }
883 for it := arg.ElementIterator(); it.Next(); {
884 k, v := it.Element()
885 outputMap[k.AsString()] = v
886 }
887 }
888 return cty.ObjectVal(outputMap), nil
889 },
890})
891
892// ReverseFunc takes a sequence and produces a new sequence of the same length
893// with all of the same elements as the given sequence but in reverse order.
894var ReverseFunc = function.New(&function.Spec{
895 Params: []function.Parameter{
896 {
897 Name: "list",
898 Type: cty.DynamicPseudoType,
899 },
900 },
901 Type: func(args []cty.Value) (cty.Type, error) {
902 argTy := args[0].Type()
903 switch {
904 case argTy.IsTupleType():
905 argTys := argTy.TupleElementTypes()
906 retTys := make([]cty.Type, len(argTys))
907 for i, ty := range argTys {
908 retTys[len(retTys)-i-1] = ty
909 }
910 return cty.Tuple(retTys), nil
911 case argTy.IsListType(), argTy.IsSetType(): // We accept sets here to mimic the usual behavior of auto-converting to list
912 return cty.List(argTy.ElementType()), nil
913 default:
914 return cty.NilType, function.NewArgErrorf(0, "can only reverse list or tuple values, not %s", argTy.FriendlyName())
915 }
916 },
917 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
918 in := args[0].AsValueSlice()
919 outVals := make([]cty.Value, len(in))
920 for i, v := range in {
921 outVals[len(outVals)-i-1] = v
922 }
923 switch {
924 case retType.IsTupleType():
925 return cty.TupleVal(outVals), nil
926 default:
927 if len(outVals) == 0 {
928 return cty.ListValEmpty(retType.ElementType()), nil
929 }
930 return cty.ListVal(outVals), nil
931 }
932 },
933})
934
935// SetProductFunc calculates the cartesian product of two or more sets or
936// sequences. If the arguments are all lists then the result is a list of tuples,
937// preserving the ordering of all of the input lists. Otherwise the result is a
938// set of tuples.
939var SetProductFunc = function.New(&function.Spec{
940 Params: []function.Parameter{},
941 VarParam: &function.Parameter{
942 Name: "sets",
943 Type: cty.DynamicPseudoType,
944 },
945 Type: func(args []cty.Value) (retType cty.Type, err error) {
946 if len(args) < 2 {
947 return cty.NilType, errors.New("at least two arguments are required")
948 }
949
950 listCount := 0
951 elemTys := make([]cty.Type, len(args))
952 for i, arg := range args {
953 aty := arg.Type()
954 switch {
955 case aty.IsSetType():
956 elemTys[i] = aty.ElementType()
957 case aty.IsListType():
958 elemTys[i] = aty.ElementType()
959 listCount++
960 case aty.IsTupleType():
961 // We can accept a tuple type only if there's some common type
962 // that all of its elements can be converted to.
963 allEtys := aty.TupleElementTypes()
964 if len(allEtys) == 0 {
965 elemTys[i] = cty.DynamicPseudoType
966 listCount++
967 break
968 }
969 ety, _ := convert.UnifyUnsafe(allEtys)
970 if ety == cty.NilType {
971 return cty.NilType, function.NewArgErrorf(i, "all elements must be of the same type")
972 }
973 elemTys[i] = ety
974 listCount++
975 default:
976 return cty.NilType, function.NewArgErrorf(i, "a set or a list is required")
977 }
978 }
979
980 if listCount == len(args) {
981 return cty.List(cty.Tuple(elemTys)), nil
982 }
983 return cty.Set(cty.Tuple(elemTys)), nil
984 },
985 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
986 ety := retType.ElementType()
987
988 total := 1
989 for _, arg := range args {
990 // Because of our type checking function, we are guaranteed that
991 // all of the arguments are known, non-null values of types that
992 // support LengthInt.
993 total *= arg.LengthInt()
994 }
995
996 if total == 0 {
997 // If any of the arguments was an empty collection then our result
998 // is also an empty collection, which we'll short-circuit here.
999 if retType.IsListType() {
1000 return cty.ListValEmpty(ety), nil
1001 }
1002 return cty.SetValEmpty(ety), nil
1003 }
1004
1005 subEtys := ety.TupleElementTypes()
1006 product := make([][]cty.Value, total)
1007
1008 b := make([]cty.Value, total*len(args))
1009 n := make([]int, len(args))
1010 s := 0
1011 argVals := make([][]cty.Value, len(args))
1012 for i, arg := range args {
1013 argVals[i] = arg.AsValueSlice()
1014 }
1015
1016 for i := range product {
1017 e := s + len(args)
1018 pi := b[s:e]
1019 product[i] = pi
1020 s = e
1021
1022 for j, n := range n {
1023 val := argVals[j][n]
1024 ty := subEtys[j]
1025 if !val.Type().Equals(ty) {
1026 var err error
1027 val, err = convert.Convert(val, ty)
1028 if err != nil {
1029 // Should never happen since we checked this in our
1030 // type-checking function.
1031 return cty.NilVal, fmt.Errorf("failed to convert argVals[%d][%d] to %s; this is a bug in Terraform", j, n, ty.FriendlyName())
1032 }
1033 }
1034 pi[j] = val
1035 }
1036
1037 for j := len(n) - 1; j >= 0; j-- {
1038 n[j]++
1039 if n[j] < len(argVals[j]) {
1040 break
1041 }
1042 n[j] = 0
1043 }
1044 }
1045
1046 productVals := make([]cty.Value, total)
1047 for i, vals := range product {
1048 productVals[i] = cty.TupleVal(vals)
1049 }
1050
1051 if retType.IsListType() {
1052 return cty.ListVal(productVals), nil
1053 }
1054 return cty.SetVal(productVals), nil
1055 },
1056})
1057
1058// SliceFunc constructs a function that extracts some consecutive elements
1059// from within a list.
1060var SliceFunc = function.New(&function.Spec{
1061 Params: []function.Parameter{
1062 {
1063 Name: "list",
1064 Type: cty.DynamicPseudoType,
1065 },
1066 {
1067 Name: "start_index",
1068 Type: cty.Number,
1069 },
1070 {
1071 Name: "end_index",
1072 Type: cty.Number,
1073 },
1074 },
1075 Type: func(args []cty.Value) (cty.Type, error) {
1076 arg := args[0]
1077 argTy := arg.Type()
1078
1079 if argTy.IsSetType() {
1080 return cty.NilType, function.NewArgErrorf(0, "cannot slice a set, because its elements do not have indices; use the tolist function to force conversion to list if the ordering of the result is not important")
1081 }
1082 if !argTy.IsListType() && !argTy.IsTupleType() {
1083 return cty.NilType, function.NewArgErrorf(0, "must be a list or tuple value")
1084 }
1085
1086 startIndex, endIndex, idxsKnown, err := sliceIndexes(args)
1087 if err != nil {
1088 return cty.NilType, err
1089 }
1090
1091 if argTy.IsListType() {
1092 return argTy, nil
1093 }
1094
1095 if !idxsKnown {
1096 // If we don't know our start/end indices then we can't predict
1097 // the result type if we're planning to return a tuple.
1098 return cty.DynamicPseudoType, nil
1099 }
1100 return cty.Tuple(argTy.TupleElementTypes()[startIndex:endIndex]), nil
1101 },
1102 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
1103 inputList := args[0]
1104
1105 if retType == cty.DynamicPseudoType {
1106 return cty.DynamicVal, nil
1107 }
1108
1109 // we ignore idxsKnown return value here because the indices are always
1110 // known here, or else the call would've short-circuited.
1111 startIndex, endIndex, _, err := sliceIndexes(args)
1112 if err != nil {
1113 return cty.NilVal, err
1114 }
1115
1116 if endIndex-startIndex == 0 {
1117 if retType.IsTupleType() {
1118 return cty.EmptyTupleVal, nil
1119 }
1120 return cty.ListValEmpty(retType.ElementType()), nil
1121 }
1122
1123 outputList := inputList.AsValueSlice()[startIndex:endIndex]
1124
1125 if retType.IsTupleType() {
1126 return cty.TupleVal(outputList), nil
1127 }
1128
1129 return cty.ListVal(outputList), nil
1130 },
1131})
1132
1133func sliceIndexes(args []cty.Value) (int, int, bool, error) {
1134 var startIndex, endIndex, length int
1135 var startKnown, endKnown, lengthKnown bool
1136
1137 if args[0].Type().IsTupleType() || args[0].IsKnown() { // if it's a tuple then we always know the length by the type, but lists must be known
1138 length = args[0].LengthInt()
1139 lengthKnown = true
1140 }
1141
1142 if args[1].IsKnown() {
1143 if err := gocty.FromCtyValue(args[1], &startIndex); err != nil {
1144 return 0, 0, false, function.NewArgErrorf(1, "invalid start index: %s", err)
1145 }
1146 if startIndex < 0 {
1147 return 0, 0, false, function.NewArgErrorf(1, "start index must not be less than zero")
1148 }
1149 if lengthKnown && startIndex > length {
1150 return 0, 0, false, function.NewArgErrorf(1, "start index must not be greater than the length of the list")
1151 }
1152 startKnown = true
1153 }
1154 if args[2].IsKnown() {
1155 if err := gocty.FromCtyValue(args[2], &endIndex); err != nil {
1156 return 0, 0, false, function.NewArgErrorf(2, "invalid end index: %s", err)
1157 }
1158 if endIndex < 0 {
1159 return 0, 0, false, function.NewArgErrorf(2, "end index must not be less than zero")
1160 }
1161 if lengthKnown && endIndex > length {
1162 return 0, 0, false, function.NewArgErrorf(2, "end index must not be greater than the length of the list")
1163 }
1164 endKnown = true
1165 }
1166 if startKnown && endKnown {
1167 if startIndex > endIndex {
1168 return 0, 0, false, function.NewArgErrorf(1, "start index must not be greater than end index")
1169 }
1170 }
1171 return startIndex, endIndex, startKnown && endKnown, nil
1172}
1173
1174// TransposeFunc contructs a function that takes a map of lists of strings and
1175// TransposeFunc constructs a function that takes a map of lists of strings and
1176// swaps the keys and values to produce a new map of lists of strings.
1177var TransposeFunc = function.New(&function.Spec{
1178 Params: []function.Parameter{
1179 {
1180 Name: "values",
1181 Type: cty.Map(cty.List(cty.String)),
1182 },
1183 },
1184 Type: function.StaticReturnType(cty.Map(cty.List(cty.String))),
1185 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
1186 inputMap := args[0]
1187 if !inputMap.IsWhollyKnown() {
1188 return cty.UnknownVal(retType), nil
1189 }
1190
1191 outputMap := make(map[string]cty.Value)
1192 tmpMap := make(map[string][]string)
1193
1194 for it := inputMap.ElementIterator(); it.Next(); {
1195 inKey, inVal := it.Element()
1196 for iter := inVal.ElementIterator(); iter.Next(); {
1197 _, val := iter.Element()
1198 if !val.Type().Equals(cty.String) {
1199 return cty.MapValEmpty(cty.List(cty.String)), errors.New("input must be a map of lists of strings")
1200 }
1201
1202 outKey := val.AsString()
1203 if _, ok := tmpMap[outKey]; !ok {
1204 tmpMap[outKey] = make([]string, 0)
1205 }
1206 outVal := tmpMap[outKey]
1207 outVal = append(outVal, inKey.AsString())
1208 sort.Strings(outVal)
1209 tmpMap[outKey] = outVal
1210 }
1211 }
1212
1213 for outKey, outVal := range tmpMap {
1214 values := make([]cty.Value, 0)
1215 for _, v := range outVal {
1216 values = append(values, cty.StringVal(v))
1217 }
1218 outputMap[outKey] = cty.ListVal(values)
1219 }
1220
1221 return cty.MapVal(outputMap), nil
1222 },
1223})
1224
1225// ValuesFunc constructs a function that returns a list of the map values,
1226// in the order of the sorted keys.
1227var ValuesFunc = function.New(&function.Spec{
1228 Params: []function.Parameter{
1229 {
1230 Name: "values",
1231 Type: cty.DynamicPseudoType,
1232 },
1233 },
1234 Type: func(args []cty.Value) (ret cty.Type, err error) {
1235 ty := args[0].Type()
1236 if ty.IsMapType() {
1237 return cty.List(ty.ElementType()), nil
1238 } else if ty.IsObjectType() {
1239 // The result is a tuple type with all of the same types as our
1240 // object type's attributes, sorted in lexicographical order by the
1241 // keys. (This matches the sort order guaranteed by ElementIterator
1242 // on a cty object value.)
1243 atys := ty.AttributeTypes()
1244 if len(atys) == 0 {
1245 return cty.EmptyTuple, nil
1246 }
1247 attrNames := make([]string, 0, len(atys))
1248 for name := range atys {
1249 attrNames = append(attrNames, name)
1250 }
1251 sort.Strings(attrNames)
1252
1253 tys := make([]cty.Type, len(attrNames))
1254 for i, name := range attrNames {
1255 tys[i] = atys[name]
1256 }
1257 return cty.Tuple(tys), nil
1258 }
1259 return cty.NilType, errors.New("values() requires a map as the first argument")
1260 },
1261 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
1262 mapVar := args[0]
1263
1264 // We can just iterate the map/object value here because cty guarantees
1265 // that these types always iterate in key lexicographical order.
1266 var values []cty.Value
1267 for it := mapVar.ElementIterator(); it.Next(); {
1268 _, val := it.Element()
1269 values = append(values, val)
1270 }
1271
1272 if retType.IsTupleType() {
1273 return cty.TupleVal(values), nil
1274 }
1275 if len(values) == 0 {
1276 return cty.ListValEmpty(retType.ElementType()), nil
1277 }
1278 return cty.ListVal(values), nil
1279 },
1280})
1281
1282// ZipmapFunc constructs a function that constructs a map from a list of keys
1283// and a corresponding list of values.
1284var ZipmapFunc = function.New(&function.Spec{
1285 Params: []function.Parameter{
1286 {
1287 Name: "keys",
1288 Type: cty.List(cty.String),
1289 },
1290 {
1291 Name: "values",
1292 Type: cty.DynamicPseudoType,
1293 },
1294 },
1295 Type: func(args []cty.Value) (ret cty.Type, err error) {
1296 keys := args[0]
1297 values := args[1]
1298 valuesTy := values.Type()
1299
1300 switch {
1301 case valuesTy.IsListType():
1302 return cty.Map(values.Type().ElementType()), nil
1303 case valuesTy.IsTupleType():
1304 if !keys.IsWhollyKnown() {
1305 // Since zipmap with a tuple produces an object, we need to know
1306 // all of the key names before we can predict our result type.
1307 return cty.DynamicPseudoType, nil
1308 }
1309
1310 keysRaw := keys.AsValueSlice()
1311 valueTypesRaw := valuesTy.TupleElementTypes()
1312 if len(keysRaw) != len(valueTypesRaw) {
1313 return cty.NilType, fmt.Errorf("number of keys (%d) does not match number of values (%d)", len(keysRaw), len(valueTypesRaw))
1314 }
1315 atys := make(map[string]cty.Type, len(valueTypesRaw))
1316 for i, keyVal := range keysRaw {
1317 if keyVal.IsNull() {
1318 return cty.NilType, fmt.Errorf("keys list has null value at index %d", i)
1319 }
1320 key := keyVal.AsString()
1321 atys[key] = valueTypesRaw[i]
1322 }
1323 return cty.Object(atys), nil
1324
1325 default:
1326 return cty.NilType, errors.New("values argument must be a list or tuple value")
1327 }
1328 },
1329 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
1330 keys := args[0]
1331 values := args[1]
1332
1333 if !keys.IsWhollyKnown() {
1334 // Unknown map keys and object attributes are not supported, so
1335 // our entire result must be unknown in this case.
1336 return cty.UnknownVal(retType), nil
1337 }
1338
1339 // both keys and values are guaranteed to be shallowly-known here,
1340 // because our declared params above don't allow unknown or null values.
1341 if keys.LengthInt() != values.LengthInt() {
1342 return cty.NilVal, fmt.Errorf("number of keys (%d) does not match number of values (%d)", keys.LengthInt(), values.LengthInt())
1343 }
1344
1345 output := make(map[string]cty.Value)
1346
1347 i := 0
1348 for it := keys.ElementIterator(); it.Next(); {
1349 _, v := it.Element()
1350 val := values.Index(cty.NumberIntVal(int64(i)))
1351 output[v.AsString()] = val
1352 i++
1353 }
1354
1355 switch {
1356 case retType.IsMapType():
1357 if len(output) == 0 {
1358 return cty.MapValEmpty(retType.ElementType()), nil
1359 }
1360 return cty.MapVal(output), nil
1361 case retType.IsObjectType():
1362 return cty.ObjectVal(output), nil
1363 default:
1364 // Should never happen because the type-check function should've
1365 // caught any other case.
1366 return cty.NilVal, fmt.Errorf("internally selected incorrect result type %s (this is a bug)", retType.FriendlyName())
1367 }
1368 },
1369})
1370
1371// helper function to add an element to a list, if it does not already exist
1372func appendIfMissing(slice []cty.Value, element cty.Value) ([]cty.Value, error) {
1373 for _, ele := range slice {
1374 eq, err := stdlib.Equal(ele, element)
1375 if err != nil {
1376 return slice, err
1377 }
1378 if eq.True() {
1379 return slice, nil
1380 }
1381 }
1382 return append(slice, element), nil
1383}
1384
1385// Element returns a single element from a given list at the given index. If
1386// index is greater than the length of the list then it is wrapped modulo
1387// the list length.
1388func Element(list, index cty.Value) (cty.Value, error) {
1389 return ElementFunc.Call([]cty.Value{list, index})
1390}
1391
1392// Length returns the number of elements in the given collection or number of
1393// Unicode characters in the given string.
1394func Length(collection cty.Value) (cty.Value, error) {
1395 return LengthFunc.Call([]cty.Value{collection})
1396}
1397
1398// Coalesce takes any number of arguments and returns the first one that isn't empty.
1399func Coalesce(args ...cty.Value) (cty.Value, error) {
1400 return CoalesceFunc.Call(args)
1401}
1402
1403// CoalesceList takes any number of list arguments and returns the first one that isn't empty.
1404func CoalesceList(args ...cty.Value) (cty.Value, error) {
1405 return CoalesceListFunc.Call(args)
1406}
1407
1408// Compact takes a list of strings and returns a new list
1409// with any empty string elements removed.
1410func Compact(list cty.Value) (cty.Value, error) {
1411 return CompactFunc.Call([]cty.Value{list})
1412}
1413
1414// Contains determines whether a given list contains a given single value
1415// as one of its elements.
1416func Contains(list, value cty.Value) (cty.Value, error) {
1417 return ContainsFunc.Call([]cty.Value{list, value})
1418}
1419
1420// Index finds the element index for a given value in a list.
1421func Index(list, value cty.Value) (cty.Value, error) {
1422 return IndexFunc.Call([]cty.Value{list, value})
1423}
1424
1425// Distinct takes a list and returns a new list with any duplicate elements removed.
1426func Distinct(list cty.Value) (cty.Value, error) {
1427 return DistinctFunc.Call([]cty.Value{list})
1428}
1429
1430// Chunklist splits a single list into fixed-size chunks, returning a list of lists.
1431func Chunklist(list, size cty.Value) (cty.Value, error) {
1432 return ChunklistFunc.Call([]cty.Value{list, size})
1433}
1434
1435// Flatten takes a list and replaces any elements that are lists with a flattened
1436// sequence of the list contents.
1437func Flatten(list cty.Value) (cty.Value, error) {
1438 return FlattenFunc.Call([]cty.Value{list})
1439}
1440
1441// Keys takes a map and returns a sorted list of the map keys.
1442func Keys(inputMap cty.Value) (cty.Value, error) {
1443 return KeysFunc.Call([]cty.Value{inputMap})
1444}
1445
1446// List takes any number of list arguments and returns a list containing those
1447// values in the same order.
1448func List(args ...cty.Value) (cty.Value, error) {
1449 return ListFunc.Call(args)
1450}
1451
1452// Lookup performs a dynamic lookup into a map.
1453// There are two required arguments, map and key, plus an optional default,
1454// which is a value to return if no key is found in map.
1455func Lookup(args ...cty.Value) (cty.Value, error) {
1456 return LookupFunc.Call(args)
1457}
1458
1459// Map takes an even number of arguments and returns a map whose elements are constructed
1460// from consecutive pairs of arguments.
1461func Map(args ...cty.Value) (cty.Value, error) {
1462 return MapFunc.Call(args)
1463}
1464
1465// Matchkeys constructs a new list by taking a subset of elements from one list
1466// whose indexes match the corresponding indexes of values in another list.
1467func Matchkeys(values, keys, searchset cty.Value) (cty.Value, error) {
1468 return MatchkeysFunc.Call([]cty.Value{values, keys, searchset})
1469}
1470
1471// Merge takes an arbitrary number of maps and returns a single map that contains
1472// a merged set of elements from all of the maps.
1473//
1474// If more than one given map defines the same key then the one that is later in
1475// the argument sequence takes precedence.
1476func Merge(maps ...cty.Value) (cty.Value, error) {
1477 return MergeFunc.Call(maps)
1478}
1479
1480// Reverse takes a sequence and produces a new sequence of the same length
1481// with all of the same elements as the given sequence but in reverse order.
1482func Reverse(list cty.Value) (cty.Value, error) {
1483 return ReverseFunc.Call([]cty.Value{list})
1484}
1485
1486// SetProduct computes the cartesian product of sets or sequences.
1487func SetProduct(sets ...cty.Value) (cty.Value, error) {
1488 return SetProductFunc.Call(sets)
1489}
1490
1491// Slice extracts some consecutive elements from within a list.
1492func Slice(list, start, end cty.Value) (cty.Value, error) {
1493 return SliceFunc.Call([]cty.Value{list, start, end})
1494}
1495
1496// Transpose takes a map of lists of strings and swaps the keys and values to
1497// produce a new map of lists of strings.
1498func Transpose(values cty.Value) (cty.Value, error) {
1499 return TransposeFunc.Call([]cty.Value{values})
1500}
1501
1502// Values returns a list of the map values, in the order of the sorted keys.
1503// This function only works on flat maps.
1504func Values(values cty.Value) (cty.Value, error) {
1505 return ValuesFunc.Call([]cty.Value{values})
1506}
1507
1508// Zipmap constructs a map from a list of keys and a corresponding list of values.
1509func Zipmap(keys, values cty.Value) (cty.Value, error) {
1510 return ZipmapFunc.Call([]cty.Value{keys, values})
1511}
diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go b/vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go
new file mode 100644
index 0000000..83f8597
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go
@@ -0,0 +1,87 @@
1package funcs
2
3import (
4 "strconv"
5
6 "github.com/zclconf/go-cty/cty"
7 "github.com/zclconf/go-cty/cty/convert"
8 "github.com/zclconf/go-cty/cty/function"
9)
10
11// MakeToFunc constructs a "to..." function, like "tostring", which converts
12// its argument to a specific type or type kind.
13//
14// The given type wantTy can be any type constraint that cty's "convert" package
15// would accept. In particular, this means that you can pass
16// cty.List(cty.DynamicPseudoType) to mean "list of any single type", which
17// will then cause cty to attempt to unify all of the element types when given
18// a tuple.
19func MakeToFunc(wantTy cty.Type) function.Function {
20 return function.New(&function.Spec{
21 Params: []function.Parameter{
22 {
23 Name: "v",
24 // We use DynamicPseudoType rather than wantTy here so that
25 // all values will pass through the function API verbatim and
26 // we can handle the conversion logic within the Type and
27 // Impl functions. This allows us to customize the error
28 // messages to be more appropriate for an explicit type
29 // conversion, whereas the cty function system produces
30 // messages aimed at _implicit_ type conversions.
31 Type: cty.DynamicPseudoType,
32 AllowNull: true,
33 },
34 },
35 Type: func(args []cty.Value) (cty.Type, error) {
36 gotTy := args[0].Type()
37 if gotTy.Equals(wantTy) {
38 return wantTy, nil
39 }
40 conv := convert.GetConversionUnsafe(args[0].Type(), wantTy)
41 if conv == nil {
42 // We'll use some specialized errors for some trickier cases,
43 // but most we can handle in a simple way.
44 switch {
45 case gotTy.IsTupleType() && wantTy.IsTupleType():
46 return cty.NilType, function.NewArgErrorf(0, "incompatible tuple type for conversion: %s", convert.MismatchMessage(gotTy, wantTy))
47 case gotTy.IsObjectType() && wantTy.IsObjectType():
48 return cty.NilType, function.NewArgErrorf(0, "incompatible object type for conversion: %s", convert.MismatchMessage(gotTy, wantTy))
49 default:
50 return cty.NilType, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint())
51 }
52 }
53 // If a conversion is available then everything is fine.
54 return wantTy, nil
55 },
56 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
57 // We didn't set "AllowUnknown" on our argument, so it is guaranteed
58 // to be known here but may still be null.
59 ret, err := convert.Convert(args[0], retType)
60 if err != nil {
61 // Because we used GetConversionUnsafe above, conversion can
62 // still potentially fail in here. For example, if the user
63 // asks to convert the string "a" to bool then we'll
64 // optimistically permit it during type checking but fail here
65 // once we note that the value isn't either "true" or "false".
66 gotTy := args[0].Type()
67 switch {
68 case gotTy == cty.String && wantTy == cty.Bool:
69 what := "string"
70 if !args[0].IsNull() {
71 what = strconv.Quote(args[0].AsString())
72 }
73 return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to bool; only the strings "true" or "false" are allowed`, what)
74 case gotTy == cty.String && wantTy == cty.Number:
75 what := "string"
76 if !args[0].IsNull() {
77 what = strconv.Quote(args[0].AsString())
78 }
79 return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to number; given string must be a decimal representation of a number`, what)
80 default:
81 return cty.NilVal, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint())
82 }
83 }
84 return ret, nil
85 },
86 })
87}
diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/crypto.go b/vendor/github.com/hashicorp/terraform/lang/funcs/crypto.go
new file mode 100644
index 0000000..5cb4bc5
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/funcs/crypto.go
@@ -0,0 +1,285 @@
1package funcs
2
3import (
4 "crypto/md5"
5 "crypto/rsa"
6 "crypto/sha1"
7 "crypto/sha256"
8 "crypto/sha512"
9 "crypto/x509"
10 "encoding/base64"
11 "encoding/hex"
12 "encoding/pem"
13 "fmt"
14 "hash"
15
16 uuid "github.com/hashicorp/go-uuid"
17 "github.com/zclconf/go-cty/cty"
18 "github.com/zclconf/go-cty/cty/function"
19 "github.com/zclconf/go-cty/cty/gocty"
20 "golang.org/x/crypto/bcrypt"
21)
22
23var UUIDFunc = function.New(&function.Spec{
24 Params: []function.Parameter{},
25 Type: function.StaticReturnType(cty.String),
26 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
27 result, err := uuid.GenerateUUID()
28 if err != nil {
29 return cty.UnknownVal(cty.String), err
30 }
31 return cty.StringVal(result), nil
32 },
33})
34
35// Base64Sha256Func constructs a function that computes the SHA256 hash of a given string
36// and encodes it with Base64.
37var Base64Sha256Func = makeStringHashFunction(sha256.New, base64.StdEncoding.EncodeToString)
38
39// MakeFileBase64Sha256Func constructs a function that is like Base64Sha256Func but reads the
40// contents of a file rather than hashing a given literal string.
41func MakeFileBase64Sha256Func(baseDir string) function.Function {
42 return makeFileHashFunction(baseDir, sha256.New, base64.StdEncoding.EncodeToString)
43}
44
45// Base64Sha512Func constructs a function that computes the SHA256 hash of a given string
46// and encodes it with Base64.
47var Base64Sha512Func = makeStringHashFunction(sha512.New, base64.StdEncoding.EncodeToString)
48
49// MakeFileBase64Sha512Func constructs a function that is like Base64Sha512Func but reads the
50// contents of a file rather than hashing a given literal string.
51func MakeFileBase64Sha512Func(baseDir string) function.Function {
52 return makeFileHashFunction(baseDir, sha512.New, base64.StdEncoding.EncodeToString)
53}
54
55// BcryptFunc constructs a function that computes a hash of the given string using the Blowfish cipher.
56var BcryptFunc = function.New(&function.Spec{
57 Params: []function.Parameter{
58 {
59 Name: "str",
60 Type: cty.String,
61 },
62 },
63 VarParam: &function.Parameter{
64 Name: "cost",
65 Type: cty.Number,
66 },
67 Type: function.StaticReturnType(cty.String),
68 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
69 defaultCost := 10
70
71 if len(args) > 1 {
72 var val int
73 if err := gocty.FromCtyValue(args[1], &val); err != nil {
74 return cty.UnknownVal(cty.String), err
75 }
76 defaultCost = val
77 }
78
79 if len(args) > 2 {
80 return cty.UnknownVal(cty.String), fmt.Errorf("bcrypt() takes no more than two arguments")
81 }
82
83 input := args[0].AsString()
84 out, err := bcrypt.GenerateFromPassword([]byte(input), defaultCost)
85 if err != nil {
86 return cty.UnknownVal(cty.String), fmt.Errorf("error occured generating password %s", err.Error())
87 }
88
89 return cty.StringVal(string(out)), nil
90 },
91})
92
93// Md5Func constructs a function that computes the MD5 hash of a given string and encodes it with hexadecimal digits.
94var Md5Func = makeStringHashFunction(md5.New, hex.EncodeToString)
95
96// MakeFileMd5Func constructs a function that is like Md5Func but reads the
97// contents of a file rather than hashing a given literal string.
98func MakeFileMd5Func(baseDir string) function.Function {
99 return makeFileHashFunction(baseDir, md5.New, hex.EncodeToString)
100}
101
102// RsaDecryptFunc constructs a function that decrypts an RSA-encrypted ciphertext.
103var RsaDecryptFunc = function.New(&function.Spec{
104 Params: []function.Parameter{
105 {
106 Name: "ciphertext",
107 Type: cty.String,
108 },
109 {
110 Name: "privatekey",
111 Type: cty.String,
112 },
113 },
114 Type: function.StaticReturnType(cty.String),
115 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
116 s := args[0].AsString()
117 key := args[1].AsString()
118
119 b, err := base64.StdEncoding.DecodeString(s)
120 if err != nil {
121 return cty.UnknownVal(cty.String), fmt.Errorf("failed to decode input %q: cipher text must be base64-encoded", s)
122 }
123
124 block, _ := pem.Decode([]byte(key))
125 if block == nil {
126 return cty.UnknownVal(cty.String), fmt.Errorf("failed to parse key: no key found")
127 }
128 if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
129 return cty.UnknownVal(cty.String), fmt.Errorf(
130 "failed to parse key: password protected keys are not supported. Please decrypt the key prior to use",
131 )
132 }
133
134 x509Key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
135 if err != nil {
136 return cty.UnknownVal(cty.String), err
137 }
138
139 out, err := rsa.DecryptPKCS1v15(nil, x509Key, b)
140 if err != nil {
141 return cty.UnknownVal(cty.String), err
142 }
143
144 return cty.StringVal(string(out)), nil
145 },
146})
147
148// Sha1Func contructs a function that computes the SHA1 hash of a given string
149// and encodes it with hexadecimal digits.
150var Sha1Func = makeStringHashFunction(sha1.New, hex.EncodeToString)
151
152// MakeFileSha1Func constructs a function that is like Sha1Func but reads the
153// contents of a file rather than hashing a given literal string.
154func MakeFileSha1Func(baseDir string) function.Function {
155 return makeFileHashFunction(baseDir, sha1.New, hex.EncodeToString)
156}
157
158// Sha256Func contructs a function that computes the SHA256 hash of a given string
159// and encodes it with hexadecimal digits.
160var Sha256Func = makeStringHashFunction(sha256.New, hex.EncodeToString)
161
162// MakeFileSha256Func constructs a function that is like Sha256Func but reads the
163// contents of a file rather than hashing a given literal string.
164func MakeFileSha256Func(baseDir string) function.Function {
165 return makeFileHashFunction(baseDir, sha256.New, hex.EncodeToString)
166}
167
168// Sha512Func contructs a function that computes the SHA512 hash of a given string
169// and encodes it with hexadecimal digits.
170var Sha512Func = makeStringHashFunction(sha512.New, hex.EncodeToString)
171
172// MakeFileSha512Func constructs a function that is like Sha512Func but reads the
173// contents of a file rather than hashing a given literal string.
174func MakeFileSha512Func(baseDir string) function.Function {
175 return makeFileHashFunction(baseDir, sha512.New, hex.EncodeToString)
176}
177
178func makeStringHashFunction(hf func() hash.Hash, enc func([]byte) string) function.Function {
179 return function.New(&function.Spec{
180 Params: []function.Parameter{
181 {
182 Name: "str",
183 Type: cty.String,
184 },
185 },
186 Type: function.StaticReturnType(cty.String),
187 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
188 s := args[0].AsString()
189 h := hf()
190 h.Write([]byte(s))
191 rv := enc(h.Sum(nil))
192 return cty.StringVal(rv), nil
193 },
194 })
195}
196
197func makeFileHashFunction(baseDir string, hf func() hash.Hash, enc func([]byte) string) function.Function {
198 return function.New(&function.Spec{
199 Params: []function.Parameter{
200 {
201 Name: "path",
202 Type: cty.String,
203 },
204 },
205 Type: function.StaticReturnType(cty.String),
206 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
207 path := args[0].AsString()
208 src, err := readFileBytes(baseDir, path)
209 if err != nil {
210 return cty.UnknownVal(cty.String), err
211 }
212
213 h := hf()
214 h.Write(src)
215 rv := enc(h.Sum(nil))
216 return cty.StringVal(rv), nil
217 },
218 })
219}
220
221// UUID generates and returns a Type-4 UUID in the standard hexadecimal string
222// format.
223//
224// This is not a pure function: it will generate a different result for each
225// call. It must therefore be registered as an impure function in the function
226// table in the "lang" package.
227func UUID() (cty.Value, error) {
228 return UUIDFunc.Call(nil)
229}
230
231// Base64Sha256 computes the SHA256 hash of a given string and encodes it with
232// Base64.
233//
234// The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied
235// as defined in RFC 4634. The raw hash is then encoded with Base64 before returning.
236// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
237func Base64Sha256(str cty.Value) (cty.Value, error) {
238 return Base64Sha256Func.Call([]cty.Value{str})
239}
240
241// Base64Sha512 computes the SHA512 hash of a given string and encodes it with
242// Base64.
243//
244// The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied
245// as defined in RFC 4634. The raw hash is then encoded with Base64 before returning.
246// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4
247func Base64Sha512(str cty.Value) (cty.Value, error) {
248 return Base64Sha512Func.Call([]cty.Value{str})
249}
250
251// Bcrypt computes a hash of the given string using the Blowfish cipher,
252// returning a string in the Modular Crypt Format
253// usually expected in the shadow password file on many Unix systems.
254func Bcrypt(str cty.Value, cost ...cty.Value) (cty.Value, error) {
255 args := make([]cty.Value, len(cost)+1)
256 args[0] = str
257 copy(args[1:], cost)
258 return BcryptFunc.Call(args)
259}
260
261// Md5 computes the MD5 hash of a given string and encodes it with hexadecimal digits.
262func Md5(str cty.Value) (cty.Value, error) {
263 return Md5Func.Call([]cty.Value{str})
264}
265
266// RsaDecrypt decrypts an RSA-encrypted ciphertext, returning the corresponding
267// cleartext.
268func RsaDecrypt(ciphertext, privatekey cty.Value) (cty.Value, error) {
269 return RsaDecryptFunc.Call([]cty.Value{ciphertext, privatekey})
270}
271
272// Sha1 computes the SHA1 hash of a given string and encodes it with hexadecimal digits.
273func Sha1(str cty.Value) (cty.Value, error) {
274 return Sha1Func.Call([]cty.Value{str})
275}
276
277// Sha256 computes the SHA256 hash of a given string and encodes it with hexadecimal digits.
278func Sha256(str cty.Value) (cty.Value, error) {
279 return Sha256Func.Call([]cty.Value{str})
280}
281
282// Sha512 computes the SHA512 hash of a given string and encodes it with hexadecimal digits.
283func Sha512(str cty.Value) (cty.Value, error) {
284 return Sha512Func.Call([]cty.Value{str})
285}
diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/datetime.go b/vendor/github.com/hashicorp/terraform/lang/funcs/datetime.go
new file mode 100644
index 0000000..5dae198
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/funcs/datetime.go
@@ -0,0 +1,70 @@
1package funcs
2
3import (
4 "time"
5
6 "github.com/zclconf/go-cty/cty"
7 "github.com/zclconf/go-cty/cty/function"
8)
9
10// TimestampFunc constructs a function that returns a string representation of the current date and time.
11var TimestampFunc = function.New(&function.Spec{
12 Params: []function.Parameter{},
13 Type: function.StaticReturnType(cty.String),
14 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
15 return cty.StringVal(time.Now().UTC().Format(time.RFC3339)), nil
16 },
17})
18
19// TimeAddFunc constructs a function that adds a duration to a timestamp, returning a new timestamp.
20var TimeAddFunc = function.New(&function.Spec{
21 Params: []function.Parameter{
22 {
23 Name: "timestamp",
24 Type: cty.String,
25 },
26 {
27 Name: "duration",
28 Type: cty.String,
29 },
30 },
31 Type: function.StaticReturnType(cty.String),
32 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
33 ts, err := time.Parse(time.RFC3339, args[0].AsString())
34 if err != nil {
35 return cty.UnknownVal(cty.String), err
36 }
37 duration, err := time.ParseDuration(args[1].AsString())
38 if err != nil {
39 return cty.UnknownVal(cty.String), err
40 }
41
42 return cty.StringVal(ts.Add(duration).Format(time.RFC3339)), nil
43 },
44})
45
46// Timestamp returns a string representation of the current date and time.
47//
48// In the Terraform language, timestamps are conventionally represented as
49// strings using RFC 3339 "Date and Time format" syntax, and so timestamp
50// returns a string in this format.
51func Timestamp() (cty.Value, error) {
52 return TimestampFunc.Call([]cty.Value{})
53}
54
55// TimeAdd adds a duration to a timestamp, returning a new timestamp.
56//
57// In the Terraform language, timestamps are conventionally represented as
58// strings using RFC 3339 "Date and Time format" syntax. Timeadd requires
59// the timestamp argument to be a string conforming to this syntax.
60//
61// `duration` is a string representation of a time difference, consisting of
62// sequences of number and unit pairs, like `"1.5h"` or `1h30m`. The accepted
63// units are `ns`, `us` (or `µs`), `"ms"`, `"s"`, `"m"`, and `"h"`. The first
64// number may be negative to indicate a negative duration, like `"-2h5m"`.
65//
66// The result is a string, also in RFC 3339 format, representing the result
67// of adding the given direction to the given timestamp.
68func TimeAdd(timestamp cty.Value, duration cty.Value) (cty.Value, error) {
69 return TimeAddFunc.Call([]cty.Value{timestamp, duration})
70}
diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/encoding.go b/vendor/github.com/hashicorp/terraform/lang/funcs/encoding.go
new file mode 100644
index 0000000..af93f08
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/funcs/encoding.go
@@ -0,0 +1,140 @@
1package funcs
2
3import (
4 "bytes"
5 "compress/gzip"
6 "encoding/base64"
7 "fmt"
8 "log"
9 "net/url"
10 "unicode/utf8"
11
12 "github.com/zclconf/go-cty/cty"
13 "github.com/zclconf/go-cty/cty/function"
14)
15
16// Base64DecodeFunc constructs a function that decodes a string containing a base64 sequence.
17var Base64DecodeFunc = function.New(&function.Spec{
18 Params: []function.Parameter{
19 {
20 Name: "str",
21 Type: cty.String,
22 },
23 },
24 Type: function.StaticReturnType(cty.String),
25 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
26 s := args[0].AsString()
27 sDec, err := base64.StdEncoding.DecodeString(s)
28 if err != nil {
29 return cty.UnknownVal(cty.String), fmt.Errorf("failed to decode base64 data '%s'", s)
30 }
31 if !utf8.Valid([]byte(sDec)) {
32 log.Printf("[DEBUG] the result of decoding the the provided string is not valid UTF-8: %s", sDec)
33 return cty.UnknownVal(cty.String), fmt.Errorf("the result of decoding the the provided string is not valid UTF-8")
34 }
35 return cty.StringVal(string(sDec)), nil
36 },
37})
38
39// Base64EncodeFunc constructs a function that encodes a string to a base64 sequence.
40var Base64EncodeFunc = function.New(&function.Spec{
41 Params: []function.Parameter{
42 {
43 Name: "str",
44 Type: cty.String,
45 },
46 },
47 Type: function.StaticReturnType(cty.String),
48 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
49 return cty.StringVal(base64.StdEncoding.EncodeToString([]byte(args[0].AsString()))), nil
50 },
51})
52
53// Base64GzipFunc constructs a function that compresses a string with gzip and then encodes the result in
54// Base64 encoding.
55var Base64GzipFunc = function.New(&function.Spec{
56 Params: []function.Parameter{
57 {
58 Name: "str",
59 Type: cty.String,
60 },
61 },
62 Type: function.StaticReturnType(cty.String),
63 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
64 s := args[0].AsString()
65
66 var b bytes.Buffer
67 gz := gzip.NewWriter(&b)
68 if _, err := gz.Write([]byte(s)); err != nil {
69 return cty.UnknownVal(cty.String), fmt.Errorf("failed to write gzip raw data: '%s'", s)
70 }
71 if err := gz.Flush(); err != nil {
72 return cty.UnknownVal(cty.String), fmt.Errorf("failed to flush gzip writer: '%s'", s)
73 }
74 if err := gz.Close(); err != nil {
75 return cty.UnknownVal(cty.String), fmt.Errorf("failed to close gzip writer: '%s'", s)
76 }
77 return cty.StringVal(base64.StdEncoding.EncodeToString(b.Bytes())), nil
78 },
79})
80
81// URLEncodeFunc constructs a function that applies URL encoding to a given string.
82var URLEncodeFunc = function.New(&function.Spec{
83 Params: []function.Parameter{
84 {
85 Name: "str",
86 Type: cty.String,
87 },
88 },
89 Type: function.StaticReturnType(cty.String),
90 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
91 return cty.StringVal(url.QueryEscape(args[0].AsString())), nil
92 },
93})
94
95// Base64Decode decodes a string containing a base64 sequence.
96//
97// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
98//
99// Strings in the Terraform language are sequences of unicode characters rather
100// than bytes, so this function will also interpret the resulting bytes as
101// UTF-8. If the bytes after Base64 decoding are _not_ valid UTF-8, this function
102// produces an error.
103func Base64Decode(str cty.Value) (cty.Value, error) {
104 return Base64DecodeFunc.Call([]cty.Value{str})
105}
106
107// Base64Encode applies Base64 encoding to a string.
108//
109// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
110//
111// Strings in the Terraform language are sequences of unicode characters rather
112// than bytes, so this function will first encode the characters from the string
113// as UTF-8, and then apply Base64 encoding to the result.
114func Base64Encode(str cty.Value) (cty.Value, error) {
115 return Base64EncodeFunc.Call([]cty.Value{str})
116}
117
118// Base64Gzip compresses a string with gzip and then encodes the result in
119// Base64 encoding.
120//
121// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
122//
123// Strings in the Terraform language are sequences of unicode characters rather
124// than bytes, so this function will first encode the characters from the string
125// as UTF-8, then apply gzip compression, and then finally apply Base64 encoding.
126func Base64Gzip(str cty.Value) (cty.Value, error) {
127 return Base64GzipFunc.Call([]cty.Value{str})
128}
129
130// URLEncode applies URL encoding to a given string.
131//
132// This function identifies characters in the given string that would have a
133// special meaning when included as a query string argument in a URL and
134// escapes them using RFC 3986 "percent encoding".
135//
136// If the given string contains non-ASCII characters, these are first encoded as
137// UTF-8 and then percent encoding is applied separately to each UTF-8 byte.
138func URLEncode(str cty.Value) (cty.Value, error) {
139 return URLEncodeFunc.Call([]cty.Value{str})
140}
diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/filesystem.go b/vendor/github.com/hashicorp/terraform/lang/funcs/filesystem.go
new file mode 100644
index 0000000..7dfc905
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/funcs/filesystem.go
@@ -0,0 +1,345 @@
1package funcs
2
3import (
4 "encoding/base64"
5 "fmt"
6 "io/ioutil"
7 "os"
8 "path/filepath"
9 "unicode/utf8"
10
11 "github.com/hashicorp/hcl2/hcl"
12 "github.com/hashicorp/hcl2/hcl/hclsyntax"
13 homedir "github.com/mitchellh/go-homedir"
14 "github.com/zclconf/go-cty/cty"
15 "github.com/zclconf/go-cty/cty/function"
16)
17
18// MakeFileFunc constructs a function that takes a file path and returns the
19// contents of that file, either directly as a string (where valid UTF-8 is
20// required) or as a string containing base64 bytes.
21func MakeFileFunc(baseDir string, encBase64 bool) function.Function {
22 return function.New(&function.Spec{
23 Params: []function.Parameter{
24 {
25 Name: "path",
26 Type: cty.String,
27 },
28 },
29 Type: function.StaticReturnType(cty.String),
30 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
31 path := args[0].AsString()
32 src, err := readFileBytes(baseDir, path)
33 if err != nil {
34 return cty.UnknownVal(cty.String), err
35 }
36
37 switch {
38 case encBase64:
39 enc := base64.StdEncoding.EncodeToString(src)
40 return cty.StringVal(enc), nil
41 default:
42 if !utf8.Valid(src) {
43 return cty.UnknownVal(cty.String), fmt.Errorf("contents of %s are not valid UTF-8; use the filebase64 function to obtain the Base64 encoded contents or the other file functions (e.g. filemd5, filesha256) to obtain file hashing results instead", path)
44 }
45 return cty.StringVal(string(src)), nil
46 }
47 },
48 })
49}
50
51// MakeTemplateFileFunc constructs a function that takes a file path and
52// an arbitrary object of named values and attempts to render the referenced
53// file as a template using HCL template syntax.
54//
55// The template itself may recursively call other functions so a callback
56// must be provided to get access to those functions. The template cannot,
57// however, access any variables defined in the scope: it is restricted only to
58// those variables provided in the second function argument, to ensure that all
59// dependencies on other graph nodes can be seen before executing this function.
60//
61// As a special exception, a referenced template file may not recursively call
62// the templatefile function, since that would risk the same file being
63// included into itself indefinitely.
64func MakeTemplateFileFunc(baseDir string, funcsCb func() map[string]function.Function) function.Function {
65
66 params := []function.Parameter{
67 {
68 Name: "path",
69 Type: cty.String,
70 },
71 {
72 Name: "vars",
73 Type: cty.DynamicPseudoType,
74 },
75 }
76
77 loadTmpl := func(fn string) (hcl.Expression, error) {
78 // We re-use File here to ensure the same filename interpretation
79 // as it does, along with its other safety checks.
80 tmplVal, err := File(baseDir, cty.StringVal(fn))
81 if err != nil {
82 return nil, err
83 }
84
85 expr, diags := hclsyntax.ParseTemplate([]byte(tmplVal.AsString()), fn, hcl.Pos{Line: 1, Column: 1})
86 if diags.HasErrors() {
87 return nil, diags
88 }
89
90 return expr, nil
91 }
92
93 renderTmpl := func(expr hcl.Expression, varsVal cty.Value) (cty.Value, error) {
94 if varsTy := varsVal.Type(); !(varsTy.IsMapType() || varsTy.IsObjectType()) {
95 return cty.DynamicVal, function.NewArgErrorf(1, "invalid vars value: must be a map") // or an object, but we don't strongly distinguish these most of the time
96 }
97
98 ctx := &hcl.EvalContext{
99 Variables: varsVal.AsValueMap(),
100 }
101
102 // We'll pre-check references in the template here so we can give a
103 // more specialized error message than HCL would by default, so it's
104 // clearer that this problem is coming from a templatefile call.
105 for _, traversal := range expr.Variables() {
106 root := traversal.RootName()
107 if _, ok := ctx.Variables[root]; !ok {
108 return cty.DynamicVal, function.NewArgErrorf(1, "vars map does not contain key %q, referenced at %s", root, traversal[0].SourceRange())
109 }
110 }
111
112 givenFuncs := funcsCb() // this callback indirection is to avoid chicken/egg problems
113 funcs := make(map[string]function.Function, len(givenFuncs))
114 for name, fn := range givenFuncs {
115 if name == "templatefile" {
116 // We stub this one out to prevent recursive calls.
117 funcs[name] = function.New(&function.Spec{
118 Params: params,
119 Type: func(args []cty.Value) (cty.Type, error) {
120 return cty.NilType, fmt.Errorf("cannot recursively call templatefile from inside templatefile call")
121 },
122 })
123 continue
124 }
125 funcs[name] = fn
126 }
127 ctx.Functions = funcs
128
129 val, diags := expr.Value(ctx)
130 if diags.HasErrors() {
131 return cty.DynamicVal, diags
132 }
133 return val, nil
134 }
135
136 return function.New(&function.Spec{
137 Params: params,
138 Type: func(args []cty.Value) (cty.Type, error) {
139 if !(args[0].IsKnown() && args[1].IsKnown()) {
140 return cty.DynamicPseudoType, nil
141 }
142
143 // We'll render our template now to see what result type it produces.
144 // A template consisting only of a single interpolation an potentially
145 // return any type.
146 expr, err := loadTmpl(args[0].AsString())
147 if err != nil {
148 return cty.DynamicPseudoType, err
149 }
150
151 // This is safe even if args[1] contains unknowns because the HCL
152 // template renderer itself knows how to short-circuit those.
153 val, err := renderTmpl(expr, args[1])
154 return val.Type(), err
155 },
156 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
157 expr, err := loadTmpl(args[0].AsString())
158 if err != nil {
159 return cty.DynamicVal, err
160 }
161 return renderTmpl(expr, args[1])
162 },
163 })
164
165}
166
167// MakeFileExistsFunc constructs a function that takes a path
168// and determines whether a file exists at that path
169func MakeFileExistsFunc(baseDir string) function.Function {
170 return function.New(&function.Spec{
171 Params: []function.Parameter{
172 {
173 Name: "path",
174 Type: cty.String,
175 },
176 },
177 Type: function.StaticReturnType(cty.Bool),
178 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
179 path := args[0].AsString()
180 path, err := homedir.Expand(path)
181 if err != nil {
182 return cty.UnknownVal(cty.Bool), fmt.Errorf("failed to expand ~: %s", err)
183 }
184
185 if !filepath.IsAbs(path) {
186 path = filepath.Join(baseDir, path)
187 }
188
189 // Ensure that the path is canonical for the host OS
190 path = filepath.Clean(path)
191
192 fi, err := os.Stat(path)
193 if err != nil {
194 if os.IsNotExist(err) {
195 return cty.False, nil
196 }
197 return cty.UnknownVal(cty.Bool), fmt.Errorf("failed to stat %s", path)
198 }
199
200 if fi.Mode().IsRegular() {
201 return cty.True, nil
202 }
203
204 return cty.False, fmt.Errorf("%s is not a regular file, but %q",
205 path, fi.Mode().String())
206 },
207 })
208}
209
210// BasenameFunc constructs a function that takes a string containing a filesystem path
211// and removes all except the last portion from it.
212var BasenameFunc = function.New(&function.Spec{
213 Params: []function.Parameter{
214 {
215 Name: "path",
216 Type: cty.String,
217 },
218 },
219 Type: function.StaticReturnType(cty.String),
220 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
221 return cty.StringVal(filepath.Base(args[0].AsString())), nil
222 },
223})
224
225// DirnameFunc constructs a function that takes a string containing a filesystem path
226// and removes the last portion from it.
227var DirnameFunc = function.New(&function.Spec{
228 Params: []function.Parameter{
229 {
230 Name: "path",
231 Type: cty.String,
232 },
233 },
234 Type: function.StaticReturnType(cty.String),
235 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
236 return cty.StringVal(filepath.Dir(args[0].AsString())), nil
237 },
238})
239
240// PathExpandFunc constructs a function that expands a leading ~ character to the current user's home directory.
241var PathExpandFunc = function.New(&function.Spec{
242 Params: []function.Parameter{
243 {
244 Name: "path",
245 Type: cty.String,
246 },
247 },
248 Type: function.StaticReturnType(cty.String),
249 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
250
251 homePath, err := homedir.Expand(args[0].AsString())
252 return cty.StringVal(homePath), err
253 },
254})
255
256func readFileBytes(baseDir, path string) ([]byte, error) {
257 path, err := homedir.Expand(path)
258 if err != nil {
259 return nil, fmt.Errorf("failed to expand ~: %s", err)
260 }
261
262 if !filepath.IsAbs(path) {
263 path = filepath.Join(baseDir, path)
264 }
265
266 // Ensure that the path is canonical for the host OS
267 path = filepath.Clean(path)
268
269 src, err := ioutil.ReadFile(path)
270 if err != nil {
271 // ReadFile does not return Terraform-user-friendly error
272 // messages, so we'll provide our own.
273 if os.IsNotExist(err) {
274 return nil, fmt.Errorf("no file exists at %s", path)
275 }
276 return nil, fmt.Errorf("failed to read %s", path)
277 }
278
279 return src, nil
280}
281
282// File reads the contents of the file at the given path.
283//
284// The file must contain valid UTF-8 bytes, or this function will return an error.
285//
286// The underlying function implementation works relative to a particular base
287// directory, so this wrapper takes a base directory string and uses it to
288// construct the underlying function before calling it.
289func File(baseDir string, path cty.Value) (cty.Value, error) {
290 fn := MakeFileFunc(baseDir, false)
291 return fn.Call([]cty.Value{path})
292}
293
294// FileExists determines whether a file exists at the given path.
295//
296// The underlying function implementation works relative to a particular base
297// directory, so this wrapper takes a base directory string and uses it to
298// construct the underlying function before calling it.
299func FileExists(baseDir string, path cty.Value) (cty.Value, error) {
300 fn := MakeFileExistsFunc(baseDir)
301 return fn.Call([]cty.Value{path})
302}
303
304// FileBase64 reads the contents of the file at the given path.
305//
306// The bytes from the file are encoded as base64 before returning.
307//
308// The underlying function implementation works relative to a particular base
309// directory, so this wrapper takes a base directory string and uses it to
310// construct the underlying function before calling it.
311func FileBase64(baseDir string, path cty.Value) (cty.Value, error) {
312 fn := MakeFileFunc(baseDir, true)
313 return fn.Call([]cty.Value{path})
314}
315
316// Basename takes a string containing a filesystem path and removes all except the last portion from it.
317//
318// The underlying function implementation works only with the path string and does not access the filesystem itself.
319// It is therefore unable to take into account filesystem features such as symlinks.
320//
321// If the path is empty then the result is ".", representing the current working directory.
322func Basename(path cty.Value) (cty.Value, error) {
323 return BasenameFunc.Call([]cty.Value{path})
324}
325
326// Dirname takes a string containing a filesystem path and removes the last portion from it.
327//
328// The underlying function implementation works only with the path string and does not access the filesystem itself.
329// It is therefore unable to take into account filesystem features such as symlinks.
330//
331// If the path is empty then the result is ".", representing the current working directory.
332func Dirname(path cty.Value) (cty.Value, error) {
333 return DirnameFunc.Call([]cty.Value{path})
334}
335
336// Pathexpand takes a string that might begin with a `~` segment, and if so it replaces that segment with
337// the current user's home directory path.
338//
339// The underlying function implementation works only with the path string and does not access the filesystem itself.
340// It is therefore unable to take into account filesystem features such as symlinks.
341//
342// If the leading segment in the path is not `~` then the given path is returned unmodified.
343func Pathexpand(path cty.Value) (cty.Value, error) {
344 return PathExpandFunc.Call([]cty.Value{path})
345}
diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/number.go b/vendor/github.com/hashicorp/terraform/lang/funcs/number.go
new file mode 100644
index 0000000..15cfe71
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/funcs/number.go
@@ -0,0 +1,155 @@
1package funcs
2
3import (
4 "math"
5
6 "github.com/zclconf/go-cty/cty"
7 "github.com/zclconf/go-cty/cty/function"
8 "github.com/zclconf/go-cty/cty/gocty"
9)
10
11// CeilFunc contructs a function that returns the closest whole number greater
12// than or equal to the given value.
13var CeilFunc = function.New(&function.Spec{
14 Params: []function.Parameter{
15 {
16 Name: "num",
17 Type: cty.Number,
18 },
19 },
20 Type: function.StaticReturnType(cty.Number),
21 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
22 var val float64
23 if err := gocty.FromCtyValue(args[0], &val); err != nil {
24 return cty.UnknownVal(cty.String), err
25 }
26 return cty.NumberIntVal(int64(math.Ceil(val))), nil
27 },
28})
29
30// FloorFunc contructs a function that returns the closest whole number lesser
31// than or equal to the given value.
32var FloorFunc = function.New(&function.Spec{
33 Params: []function.Parameter{
34 {
35 Name: "num",
36 Type: cty.Number,
37 },
38 },
39 Type: function.StaticReturnType(cty.Number),
40 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
41 var val float64
42 if err := gocty.FromCtyValue(args[0], &val); err != nil {
43 return cty.UnknownVal(cty.String), err
44 }
45 return cty.NumberIntVal(int64(math.Floor(val))), nil
46 },
47})
48
49// LogFunc contructs a function that returns the logarithm of a given number in a given base.
50var LogFunc = function.New(&function.Spec{
51 Params: []function.Parameter{
52 {
53 Name: "num",
54 Type: cty.Number,
55 },
56 {
57 Name: "base",
58 Type: cty.Number,
59 },
60 },
61 Type: function.StaticReturnType(cty.Number),
62 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
63 var num float64
64 if err := gocty.FromCtyValue(args[0], &num); err != nil {
65 return cty.UnknownVal(cty.String), err
66 }
67
68 var base float64
69 if err := gocty.FromCtyValue(args[1], &base); err != nil {
70 return cty.UnknownVal(cty.String), err
71 }
72
73 return cty.NumberFloatVal(math.Log(num) / math.Log(base)), nil
74 },
75})
76
77// PowFunc contructs a function that returns the logarithm of a given number in a given base.
78var PowFunc = function.New(&function.Spec{
79 Params: []function.Parameter{
80 {
81 Name: "num",
82 Type: cty.Number,
83 },
84 {
85 Name: "power",
86 Type: cty.Number,
87 },
88 },
89 Type: function.StaticReturnType(cty.Number),
90 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
91 var num float64
92 if err := gocty.FromCtyValue(args[0], &num); err != nil {
93 return cty.UnknownVal(cty.String), err
94 }
95
96 var power float64
97 if err := gocty.FromCtyValue(args[1], &power); err != nil {
98 return cty.UnknownVal(cty.String), err
99 }
100
101 return cty.NumberFloatVal(math.Pow(num, power)), nil
102 },
103})
104
105// SignumFunc contructs a function that returns the closest whole number greater
106// than or equal to the given value.
107var SignumFunc = function.New(&function.Spec{
108 Params: []function.Parameter{
109 {
110 Name: "num",
111 Type: cty.Number,
112 },
113 },
114 Type: function.StaticReturnType(cty.Number),
115 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
116 var num int
117 if err := gocty.FromCtyValue(args[0], &num); err != nil {
118 return cty.UnknownVal(cty.String), err
119 }
120 switch {
121 case num < 0:
122 return cty.NumberIntVal(-1), nil
123 case num > 0:
124 return cty.NumberIntVal(+1), nil
125 default:
126 return cty.NumberIntVal(0), nil
127 }
128 },
129})
130
131// Ceil returns the closest whole number greater than or equal to the given value.
132func Ceil(num cty.Value) (cty.Value, error) {
133 return CeilFunc.Call([]cty.Value{num})
134}
135
136// Floor returns the closest whole number lesser than or equal to the given value.
137func Floor(num cty.Value) (cty.Value, error) {
138 return FloorFunc.Call([]cty.Value{num})
139}
140
141// Log returns returns the logarithm of a given number in a given base.
142func Log(num, base cty.Value) (cty.Value, error) {
143 return LogFunc.Call([]cty.Value{num, base})
144}
145
146// Pow returns the logarithm of a given number in a given base.
147func Pow(num, power cty.Value) (cty.Value, error) {
148 return PowFunc.Call([]cty.Value{num, power})
149}
150
151// Signum determines the sign of a number, returning a number between -1 and
152// 1 to represent the sign.
153func Signum(num cty.Value) (cty.Value, error) {
154 return SignumFunc.Call([]cty.Value{num})
155}
diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/string.go b/vendor/github.com/hashicorp/terraform/lang/funcs/string.go
new file mode 100644
index 0000000..c9ddf19
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/funcs/string.go
@@ -0,0 +1,280 @@
1package funcs
2
3import (
4 "fmt"
5 "regexp"
6 "sort"
7 "strings"
8
9 "github.com/zclconf/go-cty/cty"
10 "github.com/zclconf/go-cty/cty/function"
11 "github.com/zclconf/go-cty/cty/gocty"
12)
13
14var JoinFunc = function.New(&function.Spec{
15 Params: []function.Parameter{
16 {
17 Name: "separator",
18 Type: cty.String,
19 },
20 },
21 VarParam: &function.Parameter{
22 Name: "lists",
23 Type: cty.List(cty.String),
24 },
25 Type: function.StaticReturnType(cty.String),
26 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
27 sep := args[0].AsString()
28 listVals := args[1:]
29 if len(listVals) < 1 {
30 return cty.UnknownVal(cty.String), fmt.Errorf("at least one list is required")
31 }
32
33 l := 0
34 for _, list := range listVals {
35 if !list.IsWhollyKnown() {
36 return cty.UnknownVal(cty.String), nil
37 }
38 l += list.LengthInt()
39 }
40
41 items := make([]string, 0, l)
42 for ai, list := range listVals {
43 ei := 0
44 for it := list.ElementIterator(); it.Next(); {
45 _, val := it.Element()
46 if val.IsNull() {
47 if len(listVals) > 1 {
48 return cty.UnknownVal(cty.String), function.NewArgErrorf(ai+1, "element %d of list %d is null; cannot concatenate null values", ei, ai+1)
49 }
50 return cty.UnknownVal(cty.String), function.NewArgErrorf(ai+1, "element %d is null; cannot concatenate null values", ei)
51 }
52 items = append(items, val.AsString())
53 ei++
54 }
55 }
56
57 return cty.StringVal(strings.Join(items, sep)), nil
58 },
59})
60
61var SortFunc = function.New(&function.Spec{
62 Params: []function.Parameter{
63 {
64 Name: "list",
65 Type: cty.List(cty.String),
66 },
67 },
68 Type: function.StaticReturnType(cty.List(cty.String)),
69 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
70 listVal := args[0]
71
72 if !listVal.IsWhollyKnown() {
73 // If some of the element values aren't known yet then we
74 // can't yet preduct the order of the result.
75 return cty.UnknownVal(retType), nil
76 }
77 if listVal.LengthInt() == 0 { // Easy path
78 return listVal, nil
79 }
80
81 list := make([]string, 0, listVal.LengthInt())
82 for it := listVal.ElementIterator(); it.Next(); {
83 iv, v := it.Element()
84 if v.IsNull() {
85 return cty.UnknownVal(retType), fmt.Errorf("given list element %s is null; a null string cannot be sorted", iv.AsBigFloat().String())
86 }
87 list = append(list, v.AsString())
88 }
89
90 sort.Strings(list)
91 retVals := make([]cty.Value, len(list))
92 for i, s := range list {
93 retVals[i] = cty.StringVal(s)
94 }
95 return cty.ListVal(retVals), nil
96 },
97})
98
99var SplitFunc = function.New(&function.Spec{
100 Params: []function.Parameter{
101 {
102 Name: "separator",
103 Type: cty.String,
104 },
105 {
106 Name: "str",
107 Type: cty.String,
108 },
109 },
110 Type: function.StaticReturnType(cty.List(cty.String)),
111 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
112 sep := args[0].AsString()
113 str := args[1].AsString()
114 elems := strings.Split(str, sep)
115 elemVals := make([]cty.Value, len(elems))
116 for i, s := range elems {
117 elemVals[i] = cty.StringVal(s)
118 }
119 if len(elemVals) == 0 {
120 return cty.ListValEmpty(cty.String), nil
121 }
122 return cty.ListVal(elemVals), nil
123 },
124})
125
126// ChompFunc constructions a function that removes newline characters at the end of a string.
127var ChompFunc = function.New(&function.Spec{
128 Params: []function.Parameter{
129 {
130 Name: "str",
131 Type: cty.String,
132 },
133 },
134 Type: function.StaticReturnType(cty.String),
135 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
136 newlines := regexp.MustCompile(`(?:\r\n?|\n)*\z`)
137 return cty.StringVal(newlines.ReplaceAllString(args[0].AsString(), "")), nil
138 },
139})
140
141// IndentFunc constructions a function that adds a given number of spaces to the
142// beginnings of all but the first line in a given multi-line string.
143var IndentFunc = function.New(&function.Spec{
144 Params: []function.Parameter{
145 {
146 Name: "spaces",
147 Type: cty.Number,
148 },
149 {
150 Name: "str",
151 Type: cty.String,
152 },
153 },
154 Type: function.StaticReturnType(cty.String),
155 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
156 var spaces int
157 if err := gocty.FromCtyValue(args[0], &spaces); err != nil {
158 return cty.UnknownVal(cty.String), err
159 }
160 data := args[1].AsString()
161 pad := strings.Repeat(" ", spaces)
162 return cty.StringVal(strings.Replace(data, "\n", "\n"+pad, -1)), nil
163 },
164})
165
166// ReplaceFunc constructions a function that searches a given string for another
167// given substring, and replaces each occurence with a given replacement string.
168var ReplaceFunc = function.New(&function.Spec{
169 Params: []function.Parameter{
170 {
171 Name: "str",
172 Type: cty.String,
173 },
174 {
175 Name: "substr",
176 Type: cty.String,
177 },
178 {
179 Name: "replace",
180 Type: cty.String,
181 },
182 },
183 Type: function.StaticReturnType(cty.String),
184 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
185 str := args[0].AsString()
186 substr := args[1].AsString()
187 replace := args[2].AsString()
188
189 // We search/replace using a regexp if the string is surrounded
190 // in forward slashes.
191 if len(substr) > 1 && substr[0] == '/' && substr[len(substr)-1] == '/' {
192 re, err := regexp.Compile(substr[1 : len(substr)-1])
193 if err != nil {
194 return cty.UnknownVal(cty.String), err
195 }
196
197 return cty.StringVal(re.ReplaceAllString(str, replace)), nil
198 }
199
200 return cty.StringVal(strings.Replace(str, substr, replace, -1)), nil
201 },
202})
203
204// TitleFunc constructions a function that converts the first letter of each word
205// in the given string to uppercase.
206var TitleFunc = function.New(&function.Spec{
207 Params: []function.Parameter{
208 {
209 Name: "str",
210 Type: cty.String,
211 },
212 },
213 Type: function.StaticReturnType(cty.String),
214 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
215 return cty.StringVal(strings.Title(args[0].AsString())), nil
216 },
217})
218
219// TrimSpaceFunc constructions a function that removes any space characters from
220// the start and end of the given string.
221var TrimSpaceFunc = function.New(&function.Spec{
222 Params: []function.Parameter{
223 {
224 Name: "str",
225 Type: cty.String,
226 },
227 },
228 Type: function.StaticReturnType(cty.String),
229 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
230 return cty.StringVal(strings.TrimSpace(args[0].AsString())), nil
231 },
232})
233
234// Join concatenates together the string elements of one or more lists with a
235// given separator.
236func Join(sep cty.Value, lists ...cty.Value) (cty.Value, error) {
237 args := make([]cty.Value, len(lists)+1)
238 args[0] = sep
239 copy(args[1:], lists)
240 return JoinFunc.Call(args)
241}
242
243// Sort re-orders the elements of a given list of strings so that they are
244// in ascending lexicographical order.
245func Sort(list cty.Value) (cty.Value, error) {
246 return SortFunc.Call([]cty.Value{list})
247}
248
249// Split divides a given string by a given separator, returning a list of
250// strings containing the characters between the separator sequences.
251func Split(sep, str cty.Value) (cty.Value, error) {
252 return SplitFunc.Call([]cty.Value{sep, str})
253}
254
255// Chomp removes newline characters at the end of a string.
256func Chomp(str cty.Value) (cty.Value, error) {
257 return ChompFunc.Call([]cty.Value{str})
258}
259
260// Indent adds a given number of spaces to the beginnings of all but the first
261// line in a given multi-line string.
262func Indent(spaces, str cty.Value) (cty.Value, error) {
263 return IndentFunc.Call([]cty.Value{spaces, str})
264}
265
266// Replace searches a given string for another given substring,
267// and replaces all occurences with a given replacement string.
268func Replace(str, substr, replace cty.Value) (cty.Value, error) {
269 return ReplaceFunc.Call([]cty.Value{str, substr, replace})
270}
271
272// Title converts the first letter of each word in the given string to uppercase.
273func Title(str cty.Value) (cty.Value, error) {
274 return TitleFunc.Call([]cty.Value{str})
275}
276
277// TrimSpace removes any space characters from the start and end of the given string.
278func TrimSpace(str cty.Value) (cty.Value, error) {
279 return TrimSpaceFunc.Call([]cty.Value{str})
280}
diff --git a/vendor/github.com/hashicorp/terraform/lang/functions.go b/vendor/github.com/hashicorp/terraform/lang/functions.go
new file mode 100644
index 0000000..2c7b548
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/functions.go
@@ -0,0 +1,147 @@
1package lang
2
3import (
4 "fmt"
5
6 "github.com/zclconf/go-cty/cty"
7 "github.com/zclconf/go-cty/cty/function"
8 "github.com/zclconf/go-cty/cty/function/stdlib"
9
10 "github.com/hashicorp/terraform/lang/funcs"
11)
12
13var impureFunctions = []string{
14 "bcrypt",
15 "timestamp",
16 "uuid",
17}
18
19// Functions returns the set of functions that should be used to when evaluating
20// expressions in the receiving scope.
21func (s *Scope) Functions() map[string]function.Function {
22 s.funcsLock.Lock()
23 if s.funcs == nil {
24 // Some of our functions are just directly the cty stdlib functions.
25 // Others are implemented in the subdirectory "funcs" here in this
26 // repository. New functions should generally start out their lives
27 // in the "funcs" directory and potentially graduate to cty stdlib
28 // later if the functionality seems to be something domain-agnostic
29 // that would be useful to all applications using cty functions.
30
31 s.funcs = map[string]function.Function{
32 "abs": stdlib.AbsoluteFunc,
33 "basename": funcs.BasenameFunc,
34 "base64decode": funcs.Base64DecodeFunc,
35 "base64encode": funcs.Base64EncodeFunc,
36 "base64gzip": funcs.Base64GzipFunc,
37 "base64sha256": funcs.Base64Sha256Func,
38 "base64sha512": funcs.Base64Sha512Func,
39 "bcrypt": funcs.BcryptFunc,
40 "ceil": funcs.CeilFunc,
41 "chomp": funcs.ChompFunc,
42 "cidrhost": funcs.CidrHostFunc,
43 "cidrnetmask": funcs.CidrNetmaskFunc,
44 "cidrsubnet": funcs.CidrSubnetFunc,
45 "coalesce": funcs.CoalesceFunc,
46 "coalescelist": funcs.CoalesceListFunc,
47 "compact": funcs.CompactFunc,
48 "concat": stdlib.ConcatFunc,
49 "contains": funcs.ContainsFunc,
50 "csvdecode": stdlib.CSVDecodeFunc,
51 "dirname": funcs.DirnameFunc,
52 "distinct": funcs.DistinctFunc,
53 "element": funcs.ElementFunc,
54 "chunklist": funcs.ChunklistFunc,
55 "file": funcs.MakeFileFunc(s.BaseDir, false),
56 "fileexists": funcs.MakeFileExistsFunc(s.BaseDir),
57 "filebase64": funcs.MakeFileFunc(s.BaseDir, true),
58 "filebase64sha256": funcs.MakeFileBase64Sha256Func(s.BaseDir),
59 "filebase64sha512": funcs.MakeFileBase64Sha512Func(s.BaseDir),
60 "filemd5": funcs.MakeFileMd5Func(s.BaseDir),
61 "filesha1": funcs.MakeFileSha1Func(s.BaseDir),
62 "filesha256": funcs.MakeFileSha256Func(s.BaseDir),
63 "filesha512": funcs.MakeFileSha512Func(s.BaseDir),
64 "flatten": funcs.FlattenFunc,
65 "floor": funcs.FloorFunc,
66 "format": stdlib.FormatFunc,
67 "formatdate": stdlib.FormatDateFunc,
68 "formatlist": stdlib.FormatListFunc,
69 "indent": funcs.IndentFunc,
70 "index": funcs.IndexFunc,
71 "join": funcs.JoinFunc,
72 "jsondecode": stdlib.JSONDecodeFunc,
73 "jsonencode": stdlib.JSONEncodeFunc,
74 "keys": funcs.KeysFunc,
75 "length": funcs.LengthFunc,
76 "list": funcs.ListFunc,
77 "log": funcs.LogFunc,
78 "lookup": funcs.LookupFunc,
79 "lower": stdlib.LowerFunc,
80 "map": funcs.MapFunc,
81 "matchkeys": funcs.MatchkeysFunc,
82 "max": stdlib.MaxFunc,
83 "md5": funcs.Md5Func,
84 "merge": funcs.MergeFunc,
85 "min": stdlib.MinFunc,
86 "pathexpand": funcs.PathExpandFunc,
87 "pow": funcs.PowFunc,
88 "replace": funcs.ReplaceFunc,
89 "reverse": funcs.ReverseFunc,
90 "rsadecrypt": funcs.RsaDecryptFunc,
91 "setintersection": stdlib.SetIntersectionFunc,
92 "setproduct": funcs.SetProductFunc,
93 "setunion": stdlib.SetUnionFunc,
94 "sha1": funcs.Sha1Func,
95 "sha256": funcs.Sha256Func,
96 "sha512": funcs.Sha512Func,
97 "signum": funcs.SignumFunc,
98 "slice": funcs.SliceFunc,
99 "sort": funcs.SortFunc,
100 "split": funcs.SplitFunc,
101 "strrev": stdlib.ReverseFunc,
102 "substr": stdlib.SubstrFunc,
103 "timestamp": funcs.TimestampFunc,
104 "timeadd": funcs.TimeAddFunc,
105 "title": funcs.TitleFunc,
106 "tostring": funcs.MakeToFunc(cty.String),
107 "tonumber": funcs.MakeToFunc(cty.Number),
108 "tobool": funcs.MakeToFunc(cty.Bool),
109 "toset": funcs.MakeToFunc(cty.Set(cty.DynamicPseudoType)),
110 "tolist": funcs.MakeToFunc(cty.List(cty.DynamicPseudoType)),
111 "tomap": funcs.MakeToFunc(cty.Map(cty.DynamicPseudoType)),
112 "transpose": funcs.TransposeFunc,
113 "trimspace": funcs.TrimSpaceFunc,
114 "upper": stdlib.UpperFunc,
115 "urlencode": funcs.URLEncodeFunc,
116 "uuid": funcs.UUIDFunc,
117 "values": funcs.ValuesFunc,
118 "zipmap": funcs.ZipmapFunc,
119 }
120
121 s.funcs["templatefile"] = funcs.MakeTemplateFileFunc(s.BaseDir, func() map[string]function.Function {
122 // The templatefile function prevents recursive calls to itself
123 // by copying this map and overwriting the "templatefile" entry.
124 return s.funcs
125 })
126
127 if s.PureOnly {
128 // Force our few impure functions to return unknown so that we
129 // can defer evaluating them until a later pass.
130 for _, name := range impureFunctions {
131 s.funcs[name] = function.Unpredictable(s.funcs[name])
132 }
133 }
134 }
135 s.funcsLock.Unlock()
136
137 return s.funcs
138}
139
140var unimplFunc = function.New(&function.Spec{
141 Type: func([]cty.Value) (cty.Type, error) {
142 return cty.DynamicPseudoType, fmt.Errorf("function not yet implemented")
143 },
144 Impl: func([]cty.Value, cty.Type) (cty.Value, error) {
145 return cty.DynamicVal, fmt.Errorf("function not yet implemented")
146 },
147})
diff --git a/vendor/github.com/hashicorp/terraform/lang/references.go b/vendor/github.com/hashicorp/terraform/lang/references.go
new file mode 100644
index 0000000..d688477
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/references.go
@@ -0,0 +1,81 @@
1package lang
2
3import (
4 "github.com/hashicorp/hcl2/hcl"
5 "github.com/hashicorp/terraform/addrs"
6 "github.com/hashicorp/terraform/configs/configschema"
7 "github.com/hashicorp/terraform/lang/blocktoattr"
8 "github.com/hashicorp/terraform/tfdiags"
9)
10
11// References finds all of the references in the given set of traversals,
12// returning diagnostics if any of the traversals cannot be interpreted as a
13// reference.
14//
15// This function does not do any de-duplication of references, since references
16// have source location information embedded in them and so any invalid
17// references that are duplicated should have errors reported for each
18// occurence.
19//
20// If the returned diagnostics contains errors then the result may be
21// incomplete or invalid. Otherwise, the returned slice has one reference per
22// given traversal, though it is not guaranteed that the references will
23// appear in the same order as the given traversals.
24func References(traversals []hcl.Traversal) ([]*addrs.Reference, tfdiags.Diagnostics) {
25 if len(traversals) == 0 {
26 return nil, nil
27 }
28
29 var diags tfdiags.Diagnostics
30 refs := make([]*addrs.Reference, 0, len(traversals))
31
32 for _, traversal := range traversals {
33 ref, refDiags := addrs.ParseRef(traversal)
34 diags = diags.Append(refDiags)
35 if ref == nil {
36 continue
37 }
38 refs = append(refs, ref)
39 }
40
41 return refs, diags
42}
43
44// ReferencesInBlock is a helper wrapper around References that first searches
45// the given body for traversals, before converting those traversals to
46// references.
47//
48// A block schema must be provided so that this function can determine where in
49// the body variables are expected.
50func ReferencesInBlock(body hcl.Body, schema *configschema.Block) ([]*addrs.Reference, tfdiags.Diagnostics) {
51 if body == nil {
52 return nil, nil
53 }
54
55 // We use blocktoattr.ExpandedVariables instead of hcldec.Variables or
56 // dynblock.VariablesHCLDec here because when we evaluate a block we'll
57 // first apply the dynamic block extension and _then_ the blocktoattr
58 // transform, and so blocktoattr.ExpandedVariables takes into account
59 // both of those transforms when it analyzes the body to ensure we find
60 // all of the references as if they'd already moved into their final
61 // locations, even though we can't expand dynamic blocks yet until we
62 // already know which variables are required.
63 //
64 // The set of cases we want to detect here is covered by the tests for
65 // the plan graph builder in the main 'terraform' package, since it's
66 // in a better position to test this due to having mock providers etc
67 // available.
68 traversals := blocktoattr.ExpandedVariables(body, schema)
69 return References(traversals)
70}
71
72// ReferencesInExpr is a helper wrapper around References that first searches
73// the given expression for traversals, before converting those traversals
74// to references.
75func ReferencesInExpr(expr hcl.Expression) ([]*addrs.Reference, tfdiags.Diagnostics) {
76 if expr == nil {
77 return nil, nil
78 }
79 traversals := expr.Variables()
80 return References(traversals)
81}
diff --git a/vendor/github.com/hashicorp/terraform/lang/scope.go b/vendor/github.com/hashicorp/terraform/lang/scope.go
new file mode 100644
index 0000000..98fca6b
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/lang/scope.go
@@ -0,0 +1,34 @@
1package lang
2
3import (
4 "sync"
5
6 "github.com/zclconf/go-cty/cty/function"
7
8 "github.com/hashicorp/terraform/addrs"
9)
10
11// Scope is the main type in this package, allowing dynamic evaluation of
12// blocks and expressions based on some contextual information that informs
13// which variables and functions will be available.
14type Scope struct {
15 // Data is used to resolve references in expressions.
16 Data Data
17
18 // SelfAddr is the address that the "self" object should be an alias of,
19 // or nil if the "self" object should not be available at all.
20 SelfAddr addrs.Referenceable
21
22 // BaseDir is the base directory used by any interpolation functions that
23 // accept filesystem paths as arguments.
24 BaseDir string
25
26 // PureOnly can be set to true to request that any non-pure functions
27 // produce unknown value results rather than actually executing. This is
28 // important during a plan phase to avoid generating results that could
29 // then differ during apply.
30 PureOnly bool
31
32 funcs map[string]function.Function
33 funcsLock sync.Mutex
34}