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

backend/local: Handle interactive prompts for variables in UI layer #23040

Merged
merged 3 commits into from
Oct 10, 2019

Conversation

apparentlymart
Copy link
Contributor

@apparentlymart apparentlymart commented Oct 9, 2019

During the 0.12 work we intended to move all of the variable value collection logic into the UI layer (command package and backend packages) and present them all together as a unified data structure to Terraform Core. However, we didn't quite succeed because the interactive prompts for unset required variables were still being handled after calling into Terraform Core.

Here we complete that earlier work by moving the interactive prompts for variables out into the UI layer too, thus allowing us to handle final validation of the variables all together in one place and do so in the UI layer where we have the most context still available about where all of these values are coming from.

This allows us to fix a problem where previously disabling input with -input=false on the command line could cause Terraform Core to receive an incomplete set of variable values, and fail with a bad error message. (Fixes #21659)


This refactoring also exposed a few cases where tests were not correctly constructing a realistic invocation of Terraform but had been getting away with it before due to the missing validation and a lucky coincidence. Therefore this PR includes some fixes for tests that were valid situations incorrectly framed, and some removals of tests that were testing situations that are impossible in normal use because the caller is responsible for preventing them from arising.

There are a few new tests here but since the overall functionality is still the same as it was before, and we've just pushed the concerns about a bit here, our existing context tests, command tests, and end-to-end tests were already covering the affected behaviors here and continue to do so.

I have also manually verified the correct behavior of various commands against a local test configuration with various combinations of required and optional variables.


As a consequence of this refactoring, the scope of terraform.Context.Input is now reduced to only gathering provider configuration arguments. Ideally that too would move into the UI layer somehow in a future commit, but that's a problem for another day.

@ghost ghost added the sdkv1 [PRs only] Marks changes that may potentially need to be ported to the plugi nSDK label Oct 9, 2019
@apparentlymart apparentlymart added bug cli v0.12 Issues (primarily bugs) reported against v0.12 releases labels Oct 9, 2019
@apparentlymart apparentlymart self-assigned this Oct 9, 2019
@apparentlymart apparentlymart requested a review from a team October 9, 2019 21:39
We previously deferred this to Validate, but all of our operations require
a valid set of variables and so checking this up front makes it more
obvious when a call into Terraform Core from the CLI layer isn't
populating variables correctly, instead of having it fail downstream
somewhere.

It's the caller's responsibility to ensure that it has collected values
for all of the variables in some way before calling NewContext, because
in the main case driven by the CLI there are many different places that
variable values can be collected from and so handling the main user-facing
validation in the CLI allows us to return better error messages that take
into account which way a variable is (or is not) being set.
During the 0.12 work we intended to move all of the variable value
collection logic into the UI layer (command package and backend packages)
and present them all together as a unified data structure to Terraform
Core. However, we didn't quite succeed because the interactive prompts
for unset required variables were still being handled _after_ calling
into Terraform Core.

Here we complete that earlier work by moving the interactive prompts for
variables out into the UI layer too, thus allowing us to handle final
validation of the variables all together in one place and do so in the UI
layer where we have the most context still available about where all of
these values are coming from.

This allows us to fix a problem where previously disabling input with
-input=false on the command line could cause Terraform Core to receive an
incomplete set of variable values, and fail with a bad error message.

As a consequence of this refactoring, the scope of terraform.Context.Input
is now reduced to only gathering provider configuration arguments. Ideally
that too would move into the UI layer somehow in a future commit, but
that's a problem for another day.
Terraform Core expects all variables to be set, but for some ancillary
commands it's fine for them to just be set to placeholders because the
variable values themselves are not key to the command's functionality
as long as the terraform.Context is still self-consistent.

For such commands, rather than prompting for interactive input for
required variables we'll just stub them out as unknowns to reflect that
they are placeholders for values that a user would normally need to
provide.

This achieves a similar effect to how these commands behaved before, but
without the tendency to produce a slightly invalid terraform.Context that
would fail in strange ways when asked to run certain operations.
@apparentlymart apparentlymart marked this pull request as ready for review October 9, 2019 22:09
} else {
// If interactive input is enabled, we might gather some more variable
// values through interactive prompts.
// TODO: Need to route the operation context through into here, so that
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you make a comment (here, not necessarily in the code) about why this TODO isn't being done now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't have any grand reason for it... just wanted to keep the function signature changes to a minimum here in order to keep the diff minimal, and the context wasn't routed in before either so this hasn't made things any worse than they used to be.

Copy link
Contributor

@mildwonkey mildwonkey left a comment

Choose a reason for hiding this comment

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

This all looks great to me! I left a few questions which were mainly for my own education and not meant to block merging. 🎉


// If we get here then we're planning to prompt for at least one additional
// variable's value.
sort.Strings(needed) // prompt in lexical order
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a question, not a suggestion, despite the phrasing:

If needed wasn't sorted, what order would the variables show up in? If the variables are (at least per-fil) in the same order that they are in the configuration files it might make more sense to the user if the variables are presented in that order. I'm thinking of scenarios where one has a lot of related variables lumped together in each file and where the variables make more sense when thought of as a whole (as opposed to seeing them "split up" in lexical order).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The variable definitions and values both live in maps so the default sort here would be pseudorandom.

In principle we could try to sort them by their source locations in order to recover the order in source code that you're thinking about here. So far the only precedent we've had for sorting things in lexical order is that we sort diagnostic messages that way in the hope of them appearing in an order that allows you to work through them one by one; I expect we could adapt similar logic here but we'd probably need to generalize some logic for actually sorting by the source locations so that it can be used from multiple locations.

I'm open to doing that, but I'd rather pursue it as a separate change, since my goal here was to retain the same user-facing behavior we had before and just move the logic to a different place in the code.

func (v unparsedUnknownVariableValue) ParseVariableValue(mode configs.VariableParsingMode) (*terraform.InputValue, tfdiags.Diagnostics) {
return &terraform.InputValue{
Value: cty.UnknownVal(v.WantType),
SourceType: terraform.ValueFromInput,
Copy link
Contributor

Choose a reason for hiding this comment

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

Another just-a-question: I'm not familiar with the usage of SourceType. Is there value in a SourceType that indicates that this value was explicitly left unset vs. the given terraform.ValueFromInput?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this particular case the meaning I was trying to represent here was "this isn't known yet, but it will eventually be determined by user input" (as long as the user changes nothing else in the mean time, of course).

In practice we don't currently really make strong use of this SourceType. It's used to lightly adjust some error message phrasing so that the messages can say things like "...which you set on the command line", or "...which you set in foo.tf", etc. We could add a new source type that would mean something like "unknown source" to go with the "unknown value". Perhaps we'd use that to suppress certain error messages altogether, like we do with unknown values in HCL. 🤔

if !diags.HasErrors() {
// Error should be: The input variable "provider_var" has not been assigned a value.
Copy link
Contributor

Choose a reason for hiding this comment

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

Thank you for adding these, I'll be sure to start doing the same!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Honestly, if I were writing these tests from scratch today I'd rather make the test code itself assert the error message, but I didn't want to mess too much with existing tests here and distract from the main goal...

@apparentlymart
Copy link
Contributor Author

After pondering @mildwonkey's questions/feedback for a little while, I'm going to go ahead and merge this in the interests of addressing the main issue at hand but will keep these other good ideas in mind for future work in this area, at which time we'll hopefully have time to consider the design implications more fully, as opposed to aiming for a minimal refactor to fix the bug.

@vroy
Copy link

vroy commented Oct 23, 2019

This impacted terraform import in that all variables now have to be provided even for cases where they wouldn't have impact on the import. Would that be the intended behaviour moving forward?

@ghost
Copy link

ghost commented Nov 10, 2019

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.

@ghost ghost locked and limited conversation to collaborators Nov 10, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug cli sdkv1 [PRs only] Marks changes that may potentially need to be ported to the plugi nSDK v0.12 Issues (primarily bugs) reported against v0.12 releases
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Error: Unassigned variable
3 participants