backend/remote: Report invalid variables only remotely
The remote backend uses backend.ParseVariableValues locally only to decide if the user seems to be trying to use -var or -var-file options locally, since those are not supported for the remote backend. Other than detecting those, we don't actually have any need to use the results of backend.ParseVariableValues, and so it's better for us to ignore any errors it produces itself and prefer to just send a potentially-invalid request to the remote system and let the remote system be responsible for validating it. This then avoids issues caused by the fact that when remote operations are in use the local system does not have all of the required context: it can't see which environment variables will be set in the remote execution context nor which variables the remote system will set using its own generated -var-file based on the workspace stored variables.
This commit is contained in:
parent
71e4468e09
commit
a8d01e3940
|
@ -75,10 +75,7 @@ func (b *Remote) opApply(stopCtx, cancelCtx context.Context, op *backend.Operati
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
variables, parseDiags := b.parseVariableValues(op)
|
if b.hasExplicitVariableValues(op) {
|
||||||
diags = diags.Append(parseDiags)
|
|
||||||
|
|
||||||
if len(variables) > 0 {
|
|
||||||
diags = diags.Append(tfdiags.Sourceless(
|
diags = diags.Append(tfdiags.Sourceless(
|
||||||
tfdiags.Error,
|
tfdiags.Error,
|
||||||
"Run variables are currently not supported",
|
"Run variables are currently not supported",
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
tfe "github.com/hashicorp/go-tfe"
|
tfe "github.com/hashicorp/go-tfe"
|
||||||
"github.com/hashicorp/terraform/backend"
|
"github.com/hashicorp/terraform/backend"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/hashicorp/terraform/tfdiags"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -201,32 +200,42 @@ func (b *Remote) waitForRun(stopCtx, cancelCtx context.Context, op *backend.Oper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Remote) parseVariableValues(op *backend.Operation) (terraform.InputValues, tfdiags.Diagnostics) {
|
// hasExplicitVariableValues is a best-effort check to determine whether the
|
||||||
var diags tfdiags.Diagnostics
|
// user has provided -var or -var-file arguments to a remote operation.
|
||||||
result := make(terraform.InputValues)
|
//
|
||||||
|
// The results may be inaccurate if the configuration is invalid or if
|
||||||
|
// individual variable values are invalid. That's okay because we only use this
|
||||||
|
// result to hint the user to set variables a different way. It's always the
|
||||||
|
// remote system's responsibility to do final validation of the input.
|
||||||
|
func (b *Remote) hasExplicitVariableValues(op *backend.Operation) bool {
|
||||||
// Load the configuration using the caller-provided configuration loader.
|
// Load the configuration using the caller-provided configuration loader.
|
||||||
config, _, configDiags := op.ConfigLoader.LoadConfigWithSnapshot(op.ConfigDir)
|
config, _, configDiags := op.ConfigLoader.LoadConfigWithSnapshot(op.ConfigDir)
|
||||||
diags = diags.Append(configDiags)
|
if configDiags.HasErrors() {
|
||||||
if diags.HasErrors() {
|
// If we can't load the configuration then we'll assume no explicit
|
||||||
return nil, diags
|
// variable values just to let the remote operation start and let
|
||||||
|
// the remote system return the same set of configuration errors.
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
variables, varDiags := backend.ParseVariableValues(op.Variables, config.Module.Variables)
|
// We're intentionally ignoring the diagnostics here because validation
|
||||||
diags = diags.Append(varDiags)
|
// of the variable values is the responsibilty of the remote system. Our
|
||||||
if diags.HasErrors() {
|
// goal here is just to make a best effort count of how many variable
|
||||||
return nil, diags
|
// values are coming from -var or -var-file CLI arguments so that we can
|
||||||
}
|
// hint the user that those are not supported for remote operations.
|
||||||
|
variables, _ := backend.ParseVariableValues(op.Variables, config.Module.Variables)
|
||||||
|
|
||||||
// Save only the explicitly defined variables.
|
// Check for explicitly-defined (-var and -var-file) variables, which the
|
||||||
for k, v := range variables {
|
// remote backend does not support. All other source types are okay,
|
||||||
|
// because they are implicit from the execution context anyway and so
|
||||||
|
// their final values will come from the _remote_ execution context.
|
||||||
|
for _, v := range variables {
|
||||||
switch v.SourceType {
|
switch v.SourceType {
|
||||||
case terraform.ValueFromCLIArg, terraform.ValueFromNamedFile:
|
case terraform.ValueFromCLIArg, terraform.ValueFromNamedFile:
|
||||||
result[k] = v
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, diags
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Remote) costEstimate(stopCtx, cancelCtx context.Context, op *backend.Operation, r *tfe.Run) error {
|
func (b *Remote) costEstimate(stopCtx, cancelCtx context.Context, op *backend.Operation, r *tfe.Run) error {
|
||||||
|
|
|
@ -78,10 +78,7 @@ func (b *Remote) opPlan(stopCtx, cancelCtx context.Context, op *backend.Operatio
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
variables, parseDiags := b.parseVariableValues(op)
|
if b.hasExplicitVariableValues(op) {
|
||||||
diags = diags.Append(parseDiags)
|
|
||||||
|
|
||||||
if len(variables) > 0 {
|
|
||||||
diags = diags.Append(tfdiags.Sourceless(
|
diags = diags.Append(tfdiags.Sourceless(
|
||||||
tfdiags.Error,
|
tfdiags.Error,
|
||||||
"Run variables are currently not supported",
|
"Run variables are currently not supported",
|
||||||
|
|
|
@ -37,7 +37,10 @@ type UnparsedVariableValue interface {
|
||||||
//
|
//
|
||||||
// If this function returns without any errors in the diagnostics, the
|
// If this function returns without any errors in the diagnostics, the
|
||||||
// resulting input values map is guaranteed to be valid and ready to pass
|
// resulting input values map is guaranteed to be valid and ready to pass
|
||||||
// to terraform.NewContext.
|
// to terraform.NewContext. If the diagnostics contains errors, the returned
|
||||||
|
// InputValues may be incomplete but will include the subset of variables
|
||||||
|
// that were successfully processed, allowing for careful analysis of the
|
||||||
|
// partial result.
|
||||||
func ParseVariableValues(vv map[string]UnparsedVariableValue, decls map[string]*configs.Variable) (terraform.InputValues, tfdiags.Diagnostics) {
|
func ParseVariableValues(vv map[string]UnparsedVariableValue, decls map[string]*configs.Variable) (terraform.InputValues, tfdiags.Diagnostics) {
|
||||||
var diags tfdiags.Diagnostics
|
var diags tfdiags.Diagnostics
|
||||||
ret := make(terraform.InputValues, len(vv))
|
ret := make(terraform.InputValues, len(vv))
|
||||||
|
|
Loading…
Reference in New Issue