From cbc8d1eba2ba5e97b9fa2700725e3a2d70965e52 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 17 Apr 2019 08:48:39 -0700 Subject: [PATCH] core: Input variables are always unknown during validate Earlier on in the v0.12 development cycle we made the decision that the validation walk should consider input values to always be unknown so that validation is checking validity for all possible inputs rather than for a specific set of inputs; checking for a specific set of inputs is the responsibility of the plan walk. However, we didn't implement that in the best way: we made the "terraform validate" command force all of the input variables to unknown but that was insufficient because it didn't also affect the implicit validation walk we do as part of "terraform plan" and "terraform apply", causing those to produce confusingly-different results. Instead, we'll address the problem directly in the reference resolver code, ensuring that all variable values will always be treated as an unknown (of the declared type, so type checking is still possible) during any validate walk, regardless of which command is running it. --- terraform/context_validate_test.go | 8 ++++++-- terraform/evaluate.go | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/terraform/context_validate_test.go b/terraform/context_validate_test.go index 1b6d571af..aae8da090 100644 --- a/terraform/context_validate_test.go +++ b/terraform/context_validate_test.go @@ -1030,7 +1030,7 @@ func TestContext2Validate_targetedDestroy(t *testing.T) { } } -func TestContext2Validate_varRefFilled(t *testing.T) { +func TestContext2Validate_varRefUnknown(t *testing.T) { m := testModule(t, "validate-variable-ref") p := testProvider("aws") p.GetSchemaReturn = &ProviderSchema{ @@ -1064,7 +1064,11 @@ func TestContext2Validate_varRefFilled(t *testing.T) { } c.Validate() - if !value.RawEquals(cty.StringVal("bar")) { + + // Input variables are always unknown during the validate walk, because + // we're checking for validity of all possible input values. Validity + // against specific input values is checked during the plan walk. + if !value.RawEquals(cty.UnknownVal(cty.String)) { t.Fatalf("bad: %#v", value) } } diff --git a/terraform/evaluate.go b/terraform/evaluate.go index 38fe87673..ab65d475b 100644 --- a/terraform/evaluate.go +++ b/terraform/evaluate.go @@ -215,6 +215,23 @@ func (d *evaluationStateData) GetInputVariable(addr addrs.InputVariable, rng tfd d.Evaluator.VariableValuesLock.Lock() defer d.Evaluator.VariableValuesLock.Unlock() + // During the validate walk, input variables are always unknown so + // that we are validating the configuration for all possible input values + // rather than for a specific set. Checking against a specific set of + // input values then happens during the plan walk. + // + // This is important because otherwise the validation walk will tend to be + // overly strict, requiring expressions throughout the configuration to + // be complicated to accommodate all possible inputs, whereas returning + // known here allows for simpler patterns like using input values as + // guards to broadly enable/disable resources, avoid processing things + // that are disabled, etc. Terraform's static validation leans towards + // being liberal in what it accepts because the subsequent plan walk has + // more information available and so can be more conservative. + if d.Operation == walkValidate { + return cty.UnknownVal(wantType), diags + } + moduleAddrStr := d.ModulePath.String() vals := d.Evaluator.VariableValues[moduleAddrStr] if vals == nil {