diff --git a/terraform/eval_output.go b/terraform/eval_output.go index cf61781e5..a454efe94 100644 --- a/terraform/eval_output.go +++ b/terraform/eval_output.go @@ -41,15 +41,16 @@ type EvalWriteOutput struct { Name string Sensitive bool Value *config.RawConfig + // ContinueOnErr allows interpolation to fail during Input + ContinueOnErr bool } // TODO: test func (n *EvalWriteOutput) Eval(ctx EvalContext) (interface{}, error) { + // This has to run before we have a state lock, since interpolation also + // reads the state cfg, err := ctx.Interpolate(n.Value, nil) - if err != nil { - // Log error but continue anyway - log.Printf("[WARN] Output interpolation %q failed: %s", n.Name, err) - } + // handle the error after we have the module from the state state, lock := ctx.State() if state == nil { @@ -59,13 +60,28 @@ func (n *EvalWriteOutput) Eval(ctx EvalContext) (interface{}, error) { // Get a write lock so we can access this instance lock.Lock() defer lock.Unlock() - // Look for the module state. If we don't have one, create it. mod := state.ModuleByPath(ctx.Path()) if mod == nil { mod = state.AddModule(ctx.Path()) } + // handling the interpolation error + if err != nil { + if n.ContinueOnErr { + log.Printf("[ERROR] Output interpolation %q failed: %s", n.Name, err) + // if we're continueing, make sure the output is included, and + // marked as unknown + mod.Outputs[n.Name] = &OutputState{ + Type: "string", + Value: config.UnknownVariableValue, + } + return nil, EvalEarlyExitError{} + } + + return nil, err + } + // Get the value from the config var valueRaw interface{} = config.UnknownVariableValue if cfg != nil { diff --git a/terraform/node_output.go b/terraform/node_output.go index d3281c346..4fd246a10 100644 --- a/terraform/node_output.go +++ b/terraform/node_output.go @@ -72,8 +72,18 @@ func (n *NodeApplyableOutput) EvalTree() EvalNode { return &EvalSequence{ Nodes: []EvalNode{ &EvalOpFilter{ - Ops: []walkOperation{walkRefresh, walkPlan, walkApply, - walkInput, walkValidate}, + // Don't let interpolation errors stop Input, since it happens + // before Refresh. + Ops: []walkOperation{walkInput}, + Node: &EvalWriteOutput{ + Name: n.Config.Name, + Sensitive: n.Config.Sensitive, + Value: n.Config.RawConfig, + ContinueOnErr: true, + }, + }, + &EvalOpFilter{ + Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkValidate}, Node: &EvalWriteOutput{ Name: n.Config.Name, Sensitive: n.Config.Sensitive,