Comments (4)
Hi @azr,
It's intentional that cty.NilVal
and cty.NilType
are not valid and will panic on any use; they represent the total absense of a value/type in the Go space, rather than a null
in cty
space, and so this is like calling a method on a nil
interface in Go, or working with a zero reflect.Value
in Go's own reflect
package.
I think the bug here is in the HCL layer instead, but I'm not entirely sure where. The hcldec
attribute spec seems to be checking for an unset attribute and returning a proper cty.NullVal
(rather than the zero value), but the stack trace shows it managing to reach the convert.Convert
call below, so it seems like this is a weird edge case where the attr.Expr.Value
method is returning cty.NilVal
.
The contract for Expression.Value
is that it should always return a valid value, even on error, to allow for this sort of error recovery behavior. I'm honestly not sure why we'd even get to that when the attribute isn't set at all, so the bug might be further upstream, but at the very least it's incorrect for that Expression.Value
call to be returning cty.NilVal
... if it's totally failing and unable to produce any sort of result then it should at least return cty.DynamicVal
to represent "I don't know".
Hopefully the above is a useful starting point! I'm not sure where exactly the issue is but I'm happy to give some more pointers if needed. Might be worth opening in issue for this over in the HCL repository so we can discuss it more over there.
from go-cty.
Hey Martin ! Thanks for the explanation.
You are right, this variable code that will return cty.NilVal, &hcl.Diagnostic{...
when a variable is not set by a user. This totally felt like a return nil, error
and felt correct. I wanted it to cause an error if a user used it.
This was handy on the unit testing side because it would be silent when a variable was unused and using the variable would error: The expression result is null. Cannot include a null value in a string template.
with a link to variable definition usage.
But a Packer run, tries to get as far as possible, which caused this panic.
I changed it to return cty.DynamicVal
, but now if a user uses the variable the error is weird and doesn't pinpoints to exactly where the problem happens:
Error: failed to read dynamic type descriptor value: invalid character ',' looking for beginning of value
[00]
[00] on ../.vscode/hcl2/crash/var_with_no_default.pkr.hcl line 18, in source "null" "null-builder":
[00] 18: source "null" "null-builder" {
I thought this was really missleading for users, so I tried something else
I added a piece of code that checks that all variables are set, but then this is also not the best because a user can define an unused variable but must set it.
Trying to look for a better option. For now my thinking is that it would be super nice if the hcl.EvalContext allowed to be more dynamic, for example:
type EvalContext interface {
VariableValue(name string) (cty.Value, hcl.Diagnostics)
Function(name string) (function.Function, hcl.Diagnostics)
Parent() *EvalContext
}
But this is more of an hcl topic :D
from go-cty.
Hi @azr,
If you want to represent the absence of a value in "userspace" (that is, representing something absent in the space of the configuration language rather than in the space of the Go program implementing it) then you could use cty.NullVal(cty.DynamicPseudoType)
or cty.NullVal(cty.String)
(depending on how much type information you have) instead.
If you do that, then the convert.Convert
call will succeed and convert the null to the requested type (null is a valid value of any type, so it will always succeed) and then either the calling Go code could handle it, or it could let that null value propagate into the HCL EvalContext
where HCL should be able to handle it and produce a suitable user-facing error saying that a null value isn't permitted.
The main thing here is that an hcl.EvalContext
should never have a cty.NilVal
in it. To represent something that exists but has no value, use cty.NullVal
instead. cty.DynamicVal
instead represents that something exists and its value/type are not yet known, which activates a different set of behaviors which I think are unnecessary in Packer because the final configuration values do not depend on external side-effects as they do in Terraform.
As some additional context, here's how Terraform deals with this problem:
By default, a Terraform variable is required and so Terraform would emit an error if it isn't set. An author can make a variable optional by providing a default value:
variable "example" {
type = string
default = "Hello"
}
In some cases there isn't any reasonable default value, and the module simply wants to detect whether or not the value is set. In that case, the author can specify the default as null
to say that the variable is optional but it has no default:
variable "example" {
type = string
default = null
}
When preparing the object var
to go in the EvalContext
, Terraform looks to see if the caller of the module explicitly provided a value for the variable, and if not it uses the default instead. In the second example above where the default is null
, evaluating that default expression produces cty.NullVal(cty.DynamicPseudoType)
and then, due to the type constraint string
, terraform calls convert.Convert(cty.NullVal(cty.DynamicPseudoType), cty.String)
and ends up with cty.NullVal(cty.String)
.
If the module elsewhere contains an expression like "${var.example}-moo"
then HCL will automatically detect that var.example
is a cty
null (as opposed to the zero value cty.NilVal
) and produce a user-friendly error saying that null isn't valid for string concatenation, rather than crashing.
from go-cty.
Howdy @apparentlymart many thanks for explaining this; I superconfused myself reading the docs over and now reading your comment it's super clear 👍 👍 👍 👍 , I think this can be closed.
from go-cty.
Related Issues (20)
- How can I get Value as interface HOT 4
- third param for `lookup` function is not optional HOT 4
- Extend FormatDateFunc to support seconds since Unix Epoch HOT 4
- Consider using "hash/maphash" for set element hashing HOT 1
- 1.11.0 causes errors with //go:generate packer-sdc mapstructure-to-hcl2 HOT 1
- hashicorp/packer-plugin-sdk incompatible with zclconf/go-cty v1.11.0 HOT 4
- TestFormatDate fails with upcoming Go 1.20 release HOT 5
- Add support for decoding into structs with a custom tag HOT 1
- Nested go struct to cty value fails HOT 1
- Proposal: JSON serialization of `cty.Path` HOT 3
- cty/json: configure (non-)HTML-escaping serialization HOT 3
- stdlib: SetProductFunc doesn't seem to handle refinements quite right HOT 2
- How to convert cty.Value of unknown type into `interface{}` HOT 2
- cty.StringVal always doubles $ in `${}` output HOT 2
- Parse string into cty type HOT 1
- cty.StringVal always doubles $ in ${} output Pt2 HOT 1
- adding jsonencode() block into a generated terraform file using golang cty HOT 2
- Encoding values with custom function (terraform provider) HOT 1
- Marshal() generate invalid JSON HOT 2
- panic with `AsString()` on result of json encoding of to_number conversion of null value HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from go-cty.