-
Notifications
You must be signed in to change notification settings - Fork 9.6k
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
helper/schema: fix validating nested objects #14784
Conversation
5bdb376
to
c154246
Compare
}, | ||
}, | ||
|
||
Vars: map[string]string{}, |
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.
This was needed to trigger interpolation. Otherwise it's skipped and map()
is never resolved since tc.Vars == nil
.
Also, to be explicit, this now fixes behavior and allows doing syntax such as: provider "google" {
region = "test"
}
# default image name to use for boot disk
variable "image" {
default = "test-image"
}
# variable for boot disk overrides
variable "boot_disk" {
type = "map"
# override the default boot disk attributes
default = {
image = "something-awesome"
size = 512
}
}
# Declare additional disk to be attached
variable "disk" {
type = "list"
# test simply adding one new disk
default = [{
image = "awesome-image"
}]
}
resource "google_compute_instance" "test" {
name = "test"
machine_type = "test"
zone = "test"
# Merge a default boot disk option, { image => var.image, size => 100 }
# with overrides coming in from `boot_disk`, then concat with the additional
# disks defined in `disk`
disk = "${concat(
list(
merge(
map(
"image", var.image,
"size", "100",
),
var.root_disk,
),
),
var.disk
)}"
} Which spits out the plan:
|
When interpreting a nested object, we were validating against the "raw" value, and not the interpolated value, causing incorrect errors. This affects structures such as: ```tf tags = "${list(map("foo", "bar"))}" ``` Prior to this, a complaint about "expected object, got string" since the raw value is obviously a string, when the interpolated value is the correct shape.
c154246
to
b9a3433
Compare
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.
Thanks for the fix!
This section predates first-class maps and lists altogether, regardless of nesting, so I can see how it was overlooked.
@@ -1373,7 +1373,7 @@ func (m schemaMap) validateObject( | |||
k string, | |||
schema map[string]*Schema, | |||
c *terraform.ResourceConfig) ([]string, []error) { | |||
raw, _ := c.GetRaw(k) | |||
raw, _ := c.Get(k) |
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.
Since Get will return the Raw value if it's computed, this should probably check for that too. I don't think that validation error helps in the unknown variable case either.
@@ -1373,7 +1373,7 @@ func (m schemaMap) validateObject( | |||
k string, | |||
schema map[string]*Schema, | |||
c *terraform.ResourceConfig) ([]string, []error) { | |||
raw, _ := c.GetRaw(k) | |||
raw, _ := c.Get(k) | |||
if _, ok := raw.(map[string]interface{}); !ok { |
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.
I think simply adding ; !ok && !c.IsComputed(k)
would suffice here
@@ -3947,6 +3947,33 @@ func TestSchemaMap_Validate(t *testing.T) { | |||
Err: false, | |||
}, | |||
|
|||
"Good sub-resource, interpolated value": { |
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.
Since we're already in here, I think a test with a computed value (that fails before the IsComputed check) would be useful too.
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.
It's a bit unclear to me what IsComputed
is actually checking for. Can you provide me with an example? Is this something like, foo.0
?
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.
I think I got it, but I'm unable to put together a failing test.
The best case I could come up with was:
"Bad sub-resource, interpolated value": {
Schema: map[string]*Schema{
"ingress": &Schema{
Type: TypeList,
Optional: true,
Elem: &Resource{
Schema: map[string]*Schema{
"from": &Schema{
Type: TypeInt,
Required: true,
},
},
},
},
},
Config: map[string]interface{}{
"ingress": []interface{}{
`${map("from", var.port)}`,
},
},
Vars: map[string]string{
"var.port": config.UnknownVariableValue,
},
Err: true,
},
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.
IsComputed basically checks if the value is known during validation, i.e. it's in the config and doesn't contain any UnknownValues. If you look at the source to Get
, it's actually going to call IsComputed
there too, and otherwise return GetRaw
.
I think the PR is still good on its own, so I can merge this and I'll send a follow up PR with the additional changes.
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.
IsComputed basically checks if the value is known during validation, i.e. it's in the config and doesn't contain any UnknownValues.
Yeah, I got that far. :) I was moreso struggling to construct a test case that proves that additional check is required.
I'll send a follow up PR with the additional changes.
Mind cc'ing me on this? I'd like to see what I'm missing here for the future.
Thanks!
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.
Sorry, started typing before I saw your update...
That looks right, and I swear I had it fail when I tested it. Let me see what's going on.
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.
I see, it's passing because Err: true,
. 😉
You'll just need to change Required
to Optional
here to pass the test, since there there is no real value here.
GH-14784 allowed nested structures to be validate, rather than relying on the raw value. However this still returns the same validation error if the structures contain a computed value, since Get will return the raw string in that case. This simply skips the validation in the IsComputed case, since there's nothing that can be checked.
GH-14784 allowed nested structures to be validate, rather than relying on the raw value. However this still returns the same validation error if the structures contain a computed value, since Get will return the raw string in that case. This simply skips the validation in the IsComputed case, since there's nothing that can be checked.
I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. |
When interpreting a nested object, we were validating against the "raw"
value, and not the interpolated value, causing incorrect errors.
This affects structures such as:
Prior to this, a complaint about "expected object, got string" since the
raw value is obviously a string, when the interpolated value is the
correct shape.