Skip to content

Commit cd1ab16

Browse files
committed
correcly parse env variables during apply
Input variables taken from the environment have no type information, so are initially stored as strings. When we compare those for validation during apply, we have to make sure to use the same parsing logic used in the plan in order to compare them with the stored variables.
1 parent 31fdb1f commit cd1ab16

File tree

1 file changed

+16
-31
lines changed

1 file changed

+16
-31
lines changed

internal/backend/local/backend_apply.go

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -259,33 +259,26 @@ func (b *Local) opApply(
259259
// same parsing logic from the plan to generate the diagnostics.
260260
undeclaredVariables := map[string]backendrun.UnparsedVariableValue{}
261261

262-
for varName, rawV := range op.Variables {
262+
parsedVars, _ := backendrun.ParseVariableValues(op.Variables, lr.Config.Module.Variables)
263+
264+
for varName, _ := range op.Variables {
265+
parsedVar, parsed := parsedVars[varName]
266+
263267
decl, ok := lr.Config.Module.Variables[varName]
264-
if !ok {
268+
if !ok || !parsed {
265269
// We'll try to parse this and handle diagnostics for missing
266270
// variables with ParseUndeclaredVariableValues after.
267-
undeclaredVariables[varName] = rawV
268-
continue
269-
}
270-
271-
// We're "parsing" only to get the resulting value's SourceType,
272-
// so we'll use configs.VariableParseLiteral just because it's
273-
// the most liberal interpretation and so least likely to
274-
// fail with an unrelated error.
275-
v, _ := rawV.ParseVariableValue(configs.VariableParseLiteral)
276-
if v == nil {
277-
// We'll ignore any that don't parse at all, because
278-
// they'll fail elsewhere in this process anyway.
271+
undeclaredVariables[varName] = op.Variables[varName]
279272
continue
280273
}
281274

282275
var rng *hcl.Range
283-
if v.HasSourceRange() {
284-
rng = v.SourceRange.ToHCL().Ptr()
276+
if parsedVar.HasSourceRange() {
277+
rng = parsedVar.SourceRange.ToHCL().Ptr()
285278
}
286279

287280
// If the var is declared as ephemeral in config, go ahead and handle it
288-
if ok && decl.Ephemeral {
281+
if decl.Ephemeral {
289282
// Determine whether this is an apply-time variable, i.e. an
290283
// ephemeral variable that was set (non-null) during the
291284
// planning phase.
@@ -311,17 +304,9 @@ func (b *Local) opApply(
311304
continue
312305
}
313306

314-
// Get the value of the variable, because we'll need it for
315-
// the next two steps.
316-
val, valDiags := rawV.ParseVariableValue(decl.ParsingMode)
317-
diags = diags.Append(valDiags)
318-
if valDiags.HasErrors() {
319-
continue
320-
}
321-
322307
// If this is an apply-time variable, the user must supply a
323308
// value during apply: it can't be null.
324-
if applyTimeVar && val.Value.IsNull() {
309+
if applyTimeVar && parsedVar.Value.IsNull() {
325310
diags = diags.Append(&hcl.Diagnostic{
326311
Severity: hcl.DiagError,
327312
Summary: "Ephemeral variable must be set for apply",
@@ -336,17 +321,17 @@ func (b *Local) opApply(
336321
// If we get here, we are in possession of a non-null
337322
// ephemeral apply-time input variable, and need only pass
338323
// its value on to the ApplyOpts.
339-
applyTimeValues[varName] = val
324+
applyTimeValues[varName] = parsedVar
340325
} else {
341326
// If a non-ephemeral variable is set differently between plan and apply, we should emit a diagnostic.
342327
plannedVariableValue, ok := plan.VariableValues[varName]
343328
if !ok {
344329
// We'll catch this with ParseUndeclaredVariableValues after
345-
undeclaredVariables[varName] = rawV
330+
undeclaredVariables[varName] = op.Variables[varName]
346331
continue
347332
}
348333

349-
val, err := plannedVariableValue.Decode(cty.DynamicPseudoType)
334+
plannedVar, err := plannedVariableValue.Decode(cty.DynamicPseudoType)
350335
if err != nil {
351336
diags = diags.Append(&hcl.Diagnostic{
352337
Severity: hcl.DiagError,
@@ -355,11 +340,11 @@ func (b *Local) opApply(
355340
Subject: rng,
356341
})
357342
} else {
358-
if v.Value.Equals(val).False() {
343+
if parsedVar.Value.Equals(plannedVar).False() {
359344
diags = diags.Append(&hcl.Diagnostic{
360345
Severity: hcl.DiagError,
361346
Summary: "Can't change variable when applying a saved plan",
362-
Detail: fmt.Sprintf("The variable %s cannot be set using the -var and -var-file options when applying a saved plan file, because a saved plan includes the variable values that were set when it was created. The saved plan specifies %s as the value whereas during apply the value %s was %s. To declare an ephemeral variable which is not saved in the plan file, use ephemeral = true.", varName, viewsjson.CompactValueStr(v.Value), viewsjson.CompactValueStr(val), v.SourceType.DiagnosticLabel()),
347+
Detail: fmt.Sprintf("The variable %s cannot be set using the -var and -var-file options when applying a saved plan file, because a saved plan includes the variable values that were set when it was created. The saved plan specifies %s as the value whereas during apply the value %s was %s. To declare an ephemeral variable which is not saved in the plan file, use ephemeral = true.", varName, viewsjson.CompactValueStr(parsedVar.Value), viewsjson.CompactValueStr(plannedVar), parsedVar.SourceType.DiagnosticLabel()),
363348
Subject: rng,
364349
})
365350
}

0 commit comments

Comments
 (0)