You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As the final step in stabilising the ephemeral values experiment, we can
remove the separate code path for handling input variables supplied via
-var and -var-file during apply.
The intent here is conveyed in the tests: only ephemeral variables may
be supplied via -var and -var-file during apply.
As per the TODO copied from the prototype, there is some more work to be
done here in making the handling of undeclared variables during apply as
sophisticated as that during plan, emitting helpful warnings (for
example) when input variables are supplied unnecessarily via environment
variables.
"The -var and -var-file options cannot be used when applying a saved plan file, because a saved plan includes the variable values that were set when it was created.",
284
-
))
285
-
op.ReportResult(runningOp, diags)
286
-
return
269
+
270
+
// If the var is declared as ephemeral in config, go ahead and handle it
271
+
ifdecl.Ephemeral {
272
+
// Determine whether this is an apply-time variable, i.e. an
273
+
// ephemeral variable that was set (non-null) during the
// If this is an apply-time variable, the user must supply a
306
+
// value during apply: it can't be null.
307
+
ifapplyTimeVar&&val.Value.IsNull() {
308
+
diags=diags.Append(&hcl.Diagnostic{
309
+
Severity: hcl.DiagError,
310
+
Summary: "Ephemeral variable must be set for apply",
311
+
Detail: fmt.Sprintf(
312
+
"The ephemeral input variable %q was set during the planning phase, and so must be set again during the apply phase.",
313
+
varName,
314
+
),
315
+
})
316
+
continue
317
+
}
318
+
319
+
// If we get here, we are in possession of a non-null
320
+
// ephemeral apply-time input variable, and need only pass
321
+
// its value on to the ApplyOpts.
322
+
applyTimeValues[varName] =val
323
+
} else {
324
+
// TODO: We should probably actually tolerate this if the new
325
+
// value is equal to the value that was saved in the plan, since
326
+
// that'd make it possible to, for example, reuse a .tfvars file
327
+
// containing a mixture of ephemeral and non-ephemeral definitions
328
+
// during the apply phase, rather than having to split ephemeral
329
+
// and non-ephemeral definitions into separate files. For initial
330
+
// experiment we'll keep things a little simpler, though, and
331
+
// just skip this check if we're doing a combined plan/apply where
332
+
// the apply phase will therefore always have exactly the same
333
+
// inputs as the plan phase.
334
+
335
+
diags=diags.Append(&hcl.Diagnostic{
336
+
Severity: hcl.DiagError,
337
+
Summary: "Can't set variable when applying a saved plan",
338
+
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. To declare an ephemeral variable which is not saved in the plan file, use ephemeral = true.", varName),
339
+
Subject: rng,
340
+
})
287
341
}
342
+
288
343
}
289
344
}
345
+
applyOpts=&terraform.ApplyOpts{
346
+
SetVariables: applyTimeValues,
347
+
}
348
+
ifdiags.HasErrors() {
349
+
op.ReportResult(runningOp, diags)
350
+
return
351
+
}
290
352
}
291
353
292
354
// Start the apply in a goroutine so that we can be interrupted.
// The diagnostics returned by ParseUndeclaredVariableValues are written
432
-
// to make sense for the plan phase, so we'll ignore them and produce
433
-
// our own diagnostics here.
434
-
forname, defn:=rangeundeclared {
435
-
// Something can get in here either by being not declared at all,
436
-
// by being a non-ephemeral variable which should therefore have been
437
-
// set during the planning phase, or by being an ephemeral value that
438
-
// wasn't set during planning and must therefore stay unset during
439
-
// apply. We'll distinguish those cases below.
440
-
decl, declared:=decls[name]
441
-
if!declared {
442
-
// FIXME: Ideally we should treat this situation similarly to how
443
-
// we would during planning, raising an error if defined in an
444
-
// "explicit-ish" way but a warning if set in an ambient way such
445
-
// as an environment variable. But for now we'll just ignore
446
-
// undeclared input variables in all cases for simplicity's sake.
447
-
continue
448
-
}
449
-
450
-
varrng*hcl.Range
451
-
ifdefn.HasSourceRange() {
452
-
rng=defn.SourceRange.ToHCL().Ptr()
453
-
}
454
-
455
-
ifdecl.Ephemeral {
456
-
// An ephemeral variable that appears as "undeclared" is one that
457
-
// wasn't set during planning and must therefore remain unset
458
-
// during apply.
459
-
diags=diags.Append(&hcl.Diagnostic{
460
-
Severity: hcl.DiagError,
461
-
Summary: "Ephemeral variable was not set during planning",
462
-
Detail: fmt.Sprintf(
463
-
"The ephemeral input variable %q was not set during the planning phase, and so must remain unset during the apply phase.",
464
-
name,
465
-
),
466
-
Subject: rng,
467
-
})
468
-
} else {
469
-
// TODO: We should probably actually tolerate this if the new
470
-
// value is equal to the value that was saved in the plan, since
471
-
// that'd make it possible to, for example, reuse a .tfvars file
472
-
// containing a mixture of ephemeral and non-ephemeral definitions
473
-
// during the apply phase, rather than having to split ephemeral
474
-
// and non-ephemeral definitions into separate files. For initial
475
-
// experiment we'll keep things a little simpler, though, and
476
-
// just skip this check if we're doing a combined plan/apply where
477
-
// the apply phase will therefore always have exactly the same
478
-
// inputs as the plan phase.
479
-
if!ignoreExtras {
480
-
diags=diags.Append(&hcl.Diagnostic{
481
-
Severity: hcl.DiagError,
482
-
Summary: "Cannot change value for non-ephemeral variable",
483
-
Detail: fmt.Sprintf(
484
-
"Input variable %q is non-ephemeral, so its value was decided during the planning phase and cannot be reset for the apply phase.",
485
-
name,
486
-
),
487
-
Subject: rng,
488
-
})
489
-
}
490
-
}
491
-
}
492
-
493
-
// We should now have a non-null value for each of the variables in needVars
494
-
for_, name:=rangeneedVars.Elems() {
495
-
val:=cty.NullVal(cty.DynamicPseudoType)
496
-
ifdefn, ok:=ret[name]; ok {
497
-
val=defn.Value
498
-
}
499
-
ifval.IsNull() {
500
-
diags=diags.Append(&hcl.Diagnostic{
501
-
Severity: hcl.DiagError,
502
-
Summary: "Ephemeral variable must be set for apply",
503
-
Detail: fmt.Sprintf(
504
-
"The ephemeral input variable %q was set during the planning phase, and so must be set again during the apply phase.",
505
-
name,
506
-
),
507
-
})
508
-
}
509
-
}
510
-
511
-
returnret, diags
512
-
}
513
-
514
460
conststateWriteBackedUpError=`The error shown above has prevented Terraform from writing the updated state to the configured backend. To allow for recovery, the state has been written to the file "errored.tfstate" in the current working directory.
515
461
516
462
Running "terraform apply" again at this point will create a forked state, making it harder to recover.
0 commit comments