-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
lang/funcs: add (console-only) TypeFunction #28501
Merged
Merged
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,7 +1,10 @@ | ||||||
package funcs | ||||||
|
||||||
import ( | ||||||
"fmt" | ||||||
"sort" | ||||||
"strconv" | ||||||
"strings" | ||||||
|
||||||
"github.com/zclconf/go-cty/cty" | ||||||
"github.com/zclconf/go-cty/cty/convert" | ||||||
|
@@ -91,3 +94,128 @@ func MakeToFunc(wantTy cty.Type) function.Function { | |||||
}, | ||||||
}) | ||||||
} | ||||||
|
||||||
var TypeFunc = function.New(&function.Spec{ | ||||||
Params: []function.Parameter{ | ||||||
{ | ||||||
Name: "value", | ||||||
Type: cty.DynamicPseudoType, | ||||||
AllowDynamicType: true, | ||||||
AllowUnknown: true, | ||||||
AllowNull: true, | ||||||
}, | ||||||
}, | ||||||
Type: function.StaticReturnType(cty.String), | ||||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | ||||||
return cty.StringVal(TypeString(args[0].Type())).Mark("raw"), nil | ||||||
}, | ||||||
}) | ||||||
|
||||||
// Modified copy of TypeString from go-cty: | ||||||
// https://github.com/zclconf/go-cty-debug/blob/master/ctydebug/type_string.go | ||||||
// | ||||||
// TypeString returns a string representation of a given type that is | ||||||
// reminiscent of Go syntax calling into the cty package but is mainly | ||||||
// intended for easy human inspection of values in tests, debug output, etc. | ||||||
// | ||||||
// The resulting string will include newlines and indentation in order to | ||||||
// increase the readability of complex structures. It always ends with a | ||||||
// newline, so you can print this result directly to your output. | ||||||
func TypeString(ty cty.Type) string { | ||||||
var b strings.Builder | ||||||
writeType(ty, &b, 0) | ||||||
return b.String() | ||||||
} | ||||||
|
||||||
func writeType(ty cty.Type, b *strings.Builder, indent int) { | ||||||
switch { | ||||||
case ty == cty.NilType: | ||||||
b.WriteString("nil") | ||||||
return | ||||||
case ty.IsObjectType(): | ||||||
atys := ty.AttributeTypes() | ||||||
if len(atys) == 0 { | ||||||
b.WriteString("object{}") | ||||||
return | ||||||
} | ||||||
attrNames := make([]string, 0, len(atys)) | ||||||
for name := range atys { | ||||||
attrNames = append(attrNames, name) | ||||||
} | ||||||
sort.Strings(attrNames) | ||||||
b.WriteString("object({\n") | ||||||
indent++ | ||||||
for _, name := range attrNames { | ||||||
aty := atys[name] | ||||||
b.WriteString(indentSpaces(indent)) | ||||||
fmt.Fprintf(b, "%s: ", name) | ||||||
writeType(aty, b, indent) | ||||||
b.WriteString(",\n") | ||||||
} | ||||||
indent-- | ||||||
b.WriteString(indentSpaces(indent)) | ||||||
b.WriteString("})") | ||||||
case ty.IsTupleType(): | ||||||
etys := ty.TupleElementTypes() | ||||||
if len(etys) == 0 { | ||||||
b.WriteString("tuple") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Same method as above, the type constructor syntax for this is: variable "foo" {
type = tuple([])
} |
||||||
return | ||||||
} | ||||||
b.WriteString("tuple([\n") | ||||||
indent++ | ||||||
for _, ety := range etys { | ||||||
b.WriteString(indentSpaces(indent)) | ||||||
writeType(ety, b, indent) | ||||||
b.WriteString(",\n") | ||||||
} | ||||||
indent-- | ||||||
b.WriteString(indentSpaces(indent)) | ||||||
b.WriteString("])") | ||||||
case ty.IsCollectionType(): | ||||||
ety := ty.ElementType() | ||||||
switch { | ||||||
case ty.IsListType(): | ||||||
b.WriteString("list(") | ||||||
case ty.IsMapType(): | ||||||
b.WriteString("map(") | ||||||
case ty.IsSetType(): | ||||||
b.WriteString("set(") | ||||||
default: | ||||||
// At the time of writing there are no other collection types, | ||||||
// but we'll be robust here and just pass through the GoString | ||||||
// of anything we don't recognize. | ||||||
b.WriteString(ty.FriendlyName()) | ||||||
return | ||||||
} | ||||||
// Because object and tuple types render split over multiple | ||||||
// lines, a collection type container around them can end up | ||||||
// being hard to see when scanning, so we'll generate some extra | ||||||
// indentation to make a collection of structural type more visually | ||||||
// distinct from the structural type alone. | ||||||
complexElem := ety.IsObjectType() || ety.IsTupleType() | ||||||
if complexElem { | ||||||
indent++ | ||||||
b.WriteString("\n") | ||||||
b.WriteString(indentSpaces(indent)) | ||||||
} | ||||||
writeType(ty.ElementType(), b, indent) | ||||||
if complexElem { | ||||||
indent-- | ||||||
b.WriteString(",\n") | ||||||
b.WriteString(indentSpaces(indent)) | ||||||
} | ||||||
b.WriteString(")") | ||||||
default: | ||||||
// For any other type we'll just use its GoString and assume it'll | ||||||
// follow the usual GoString conventions. | ||||||
b.WriteString(ty.FriendlyName()) | ||||||
} | ||||||
} | ||||||
|
||||||
func indentSpaces(level int) string { | ||||||
return strings.Repeat(" ", level) | ||||||
} | ||||||
|
||||||
func Type(input []cty.Value) (cty.Value, error) { | ||||||
return TypeFunc.Call(input) | ||||||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
--- | ||
layout: "language" | ||
page_title: "type - Functions - Configuration Language" | ||
sidebar_current: "docs-funcs-conversion-type" | ||
description: |- | ||
The type function returns the type of a given value. | ||
--- | ||
|
||
# `type` Function | ||
|
||
-> **Note:** This function is available only in Terraform 1.0 and later. | ||
|
||
`type` retuns the type of a given value. | ||
|
||
Sometimes a Terraform configuration can result in confusing errors regarding | ||
inconsistent types. This function displays terraform's evaluation of a given | ||
value's type, which is useful in understanding this error message. | ||
|
||
This is a special function which is only available in the `terraform console` command. | ||
|
||
## Examples | ||
|
||
Here we have a conditional `output` which prints either the value of `var.list` or a local named `default_list`: | ||
|
||
```hcl | ||
variable "list" { | ||
default = [] | ||
} | ||
|
||
locals { | ||
default_list = [ | ||
{ | ||
foo = "bar" | ||
map = { bleep = "bloop" } | ||
}, | ||
{ | ||
beep = "boop" | ||
}, | ||
] | ||
} | ||
|
||
output "list" { | ||
value = var.list != [] ? var.list : local.default_list | ||
} | ||
``` | ||
|
||
Applying this configuration results in the following error: | ||
|
||
``` | ||
Error: Inconsistent conditional result types | ||
|
||
on main.tf line 18, in output "list": | ||
18: value = var.list != [] ? var.list : local.default_list | ||
|---------------- | ||
| local.default_list is tuple with 2 elements | ||
| var.list is empty tuple | ||
|
||
The true and false result expressions must have consistent types. The given | ||
expressions are tuple and tuple, respectively. | ||
``` | ||
|
||
While this error message does include some type information, it can be helpful | ||
to inspect the exact type that Terraform has determined for each given input. | ||
Examining both `var.list` and `local.default_list` using the `type` function | ||
provides more context for the error message: | ||
|
||
``` | ||
> type(var.list) | ||
tuple | ||
> type(local.default_list) | ||
tuple([ | ||
object({ | ||
foo: string, | ||
map: object({ | ||
bleep: string, | ||
}), | ||
}), | ||
object({ | ||
beep: string, | ||
}), | ||
]) | ||
``` |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lines up with the configuration for an empty object type constructor:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great catch, thank you, those are both much better. I made those changes and added more test cases to go with 'em.