Skip to content
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

Some small improvements to diagnostic reporting #31299

Merged
merged 4 commits into from
Jun 23, 2022
Merged

Conversation

apparentlymart
Copy link
Contributor

This PR upgrades to a newer version of HCL which supports annotating diagnostics with "extra information", and then uses that new mechanism as the basis for two separate improvements to our diagnostic rendering.

Function signatures for function call errors

HCL itself is now annotating diagnostics relating to problems calling a function with extra information to allow a renderer to determine which function is being called.

We can now use that information to add additional context to error messages about incorrect function calls, so that it's easier to see which of the positional parameters a particular name mentioned in an error message belongs to:

╷
│ Error: Invalid function argument
│ 
│   on invalid-func-call.tf line 2, in output "result":
│    2:   value = join(["foo"], "bar")
│     ├────────────────
│     │ while calling join(separator, lists...)
│ 
│ Invalid value for "separator" parameter: string required.
╵
╷
│ Error: Invalid function argument
│ 
│   on invalid-func-call.tf line 2, in output "result":
│    2:   value = join(["foo"], "bar")
│     ├────────────────
│     │ while calling join(separator, lists...)
│ 
│ Invalid value for "lists" parameter: list of string required.
╵

In the above example I made the classic mistake of getting the two join arguments mixed up, which causes one diagnostic for each argument but both of them include the additional statement "while calling join(separator, lists...)" so a reader can see which of the parameters is "separator" and which is "lists".

This particular change also adds a new property to the JSON representation of diagnostics in order to describe the function name and signature, so any external UIs built in terms of the JSON will need a corresponding change if they want to include the same information.

Don't mention unknown values and sensitive values when they aren't relevant

I've repeatedly noticed people asking questions in a way that betrays that they thought that Terraform saying that a value would be "known only during apply" was Terraform describing a problem they needed to fix, rather than just describing which values were in scope during evaluation. That has come up less often for sensitive values, but a similar principle applies in both cases: mentioning possibly-unfamiliar concepts that are not relevant to the error message can cause a reasonable reader to assume that they are part of the problem Terraform is describing, and it can therefore be a "red herring" leading the user off in an unproductive direction when trying to understand the problem.

To address that, I've added an ability to explicitly mark a diagnostic as being related to unknown values or related to sensitive values. The diagnostic render will then avoid using the words "known only after apply" and avoid mentioning sensitive values at all unless the diagnostic has the corresponding annotation indicating that the additional information is likely to be relevant and helpful.

I don't have a handy example to show of this one since it's a subtractive change rather than an additive one -- we'll now be showing slightly less information than we were before -- but essentially the idea is to prefer to not mention a value at all if there is no way to talk about it without mentioning that it is unknown or sensitive. As a compromise, there is a special case where if we have an unknown value of a known type we will still state the value's type:

Error: Contrived example

  on test.tf line 1:
   1: contrived example
    ├────────────────
    │ boop.beep is a string

This is indeed a contrived example.

In the above example, boop.beep was cty.UnknownVal(cty.String) and so we can at least mention that it's a string, even though we'll no longer include the subsequent ", known only after apply" that we would've included before this.

In this case the filtering is done at the JSON generation stage and so anyone consuming the JSON form of diagnostics will just see fewer elements in the array of values than before, and so any alternative UI based on the JSON output will automatically follow along and skip showing the distracting information.

HCL's diagnostic model now includes the idea of "extra information" which
works by attaching an initially-opaque interface value to each diagnostic
and then asking callers to type-assert against that value to sniff for
particular interfaces in order to discover additional machine-readable
context about a certain diagnostic message.

This commit echoes that idea into our tfdiags API, for now only for
diagnostics that are backed by an hcl.Diagnostic. All other implementations
of the diagnostic interface just always return nil, which means they never
carry any "extra information".

As is typical for our wrapping abstraction, we have here also a modified
copy of HCL's helper function for conveniently probing a diagnostic for
information of a particular type, designed to work with our diagnostic
interface instead of HCL's concrete diagnostic type.
When an error occurs in a function call, the error message text often
includes references to particular parameters in the function signature.

This commit improves that reporting by also including a summary of the
full function signature as part of the diagnostic context in that case,
so a reader can see which parameter is which given that function
arguments are always assigned positionally and so the parameter names
do not appear in the caller's source code.
By observing the sorts of questions people ask in the community, and the
ways they ask them, we've inferred that various different people have been
confused by Terraform reporting that a value won't be known until apply
or that a value is sensitive as part of an error message when that message
doesn't actually relate to the known-ness and sensitivity of any value.

Quite reasonably, someone who sees Terraform discussing an unfamiliar
concept like unknown values can assume that it must be somehow relevant to
the problem being discussed, and so in that sense Terraform's current
error messages are giving "too much information": information that isn't
actually helpful in understanding the problem being described, and in the
worst case is a distraction from understanding the problem being described.

With that in mind then, here we introduce an explicit annotation on
diagnostic objects that are directly talking about unknown values or
sensitive values, and then the diagnostic renderer will react to that to
avoid using the terminology "known only after apply" or "sensitive" in the
generated diagnostic annotations unless we're rendering a message that is
explicitly related to one of those topics.

This ends up being a bit of a cross-cutting concern because the code that
generates these diagnostics and the code that renders them are in separate
packages and are not directly aware of each other. With that in mind, the
logic for actually deciding for a particular diagnostic whether it's
flagged in one of these special ways lives inside the tfdiags package as
an intermediation point, which both the diagnostic generator (in the core
package) and the diagnostic renderer can both depend on.
@apparentlymart apparentlymart requested a review from a team June 22, 2022 19:13
@apparentlymart apparentlymart self-assigned this Jun 22, 2022
Copy link
Contributor

@alisdair alisdair left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really nice improvement!

It might be worth adding the json-output label for folks using that to catch changes to our JSON formats. We could possibly also bump the streaming JSON UI JSON_UI_VERSION and the terraform validate -json FormatVersion minor versions, although since this is purely additional that's probably not necessary.

@apparentlymart
Copy link
Contributor Author

Thanks for the reminder about the label!

Thanks also for raising the question about format version numbers. I think I'm going to leave the version numbers unchanged here because I know we've had some grief changing those version numbers in the past (even just changing the minor versions) and although all of the consumers I'm directly aware of should now be updated to deal with it I'd rather not risk problems for other consumers without there being some reason for a consumer to vary its behavior by minor version here. (I expect a system consuming this would just use the presence of the new data as the trigger to act on it, rather than using the minor version as a proxy for that.)

@apparentlymart apparentlymart merged commit 90ea7b0 into main Jun 23, 2022
@apparentlymart apparentlymart deleted the f-hcl-diags-extra branch June 23, 2022 20:52
@github-actions
Copy link
Contributor

Reminder for the merging maintainer: if this is a user-visible change, please update the changelog on the appropriate release branch.

@github-actions
Copy link
Contributor

I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 24, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants