]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/eval_output.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / eval_output.go
1 package terraform
2
3 import (
4 "fmt"
5 "log"
6
7 "github.com/hashicorp/hcl2/hcl"
8 "github.com/zclconf/go-cty/cty"
9
10 "github.com/hashicorp/terraform/addrs"
11 "github.com/hashicorp/terraform/plans"
12 "github.com/hashicorp/terraform/states"
13 )
14
15 // EvalDeleteOutput is an EvalNode implementation that deletes an output
16 // from the state.
17 type EvalDeleteOutput struct {
18 Addr addrs.OutputValue
19 }
20
21 // TODO: test
22 func (n *EvalDeleteOutput) Eval(ctx EvalContext) (interface{}, error) {
23 state := ctx.State()
24 if state == nil {
25 return nil, nil
26 }
27
28 state.RemoveOutputValue(n.Addr.Absolute(ctx.Path()))
29 return nil, nil
30 }
31
32 // EvalWriteOutput is an EvalNode implementation that writes the output
33 // for the given name to the current state.
34 type EvalWriteOutput struct {
35 Addr addrs.OutputValue
36 Sensitive bool
37 Expr hcl.Expression
38 // ContinueOnErr allows interpolation to fail during Input
39 ContinueOnErr bool
40 }
41
42 // TODO: test
43 func (n *EvalWriteOutput) Eval(ctx EvalContext) (interface{}, error) {
44 addr := n.Addr.Absolute(ctx.Path())
45
46 // This has to run before we have a state lock, since evaluation also
47 // reads the state
48 val, diags := ctx.EvaluateExpr(n.Expr, cty.DynamicPseudoType, nil)
49 // We'll handle errors below, after we have loaded the module.
50
51 state := ctx.State()
52 if state == nil {
53 return nil, nil
54 }
55
56 changes := ctx.Changes() // may be nil, if we're not working on a changeset
57
58 // handling the interpolation error
59 if diags.HasErrors() {
60 if n.ContinueOnErr || flagWarnOutputErrors {
61 log.Printf("[ERROR] Output interpolation %q failed: %s", n.Addr.Name, diags.Err())
62 // if we're continuing, make sure the output is included, and
63 // marked as unknown. If the evaluator was able to find a type
64 // for the value in spite of the error then we'll use it.
65 n.setValue(addr, state, changes, cty.UnknownVal(val.Type()))
66 return nil, EvalEarlyExitError{}
67 }
68 return nil, diags.Err()
69 }
70
71 n.setValue(addr, state, changes, val)
72
73 return nil, nil
74 }
75
76 func (n *EvalWriteOutput) setValue(addr addrs.AbsOutputValue, state *states.SyncState, changes *plans.ChangesSync, val cty.Value) {
77 if val.IsKnown() && !val.IsNull() {
78 // The state itself doesn't represent unknown values, so we null them
79 // out here and then we'll save the real unknown value in the planned
80 // changeset below, if we have one on this graph walk.
81 log.Printf("[TRACE] EvalWriteOutput: Saving value for %s in state", addr)
82 stateVal := cty.UnknownAsNull(val)
83 state.SetOutputValue(addr, stateVal, n.Sensitive)
84 } else {
85 log.Printf("[TRACE] EvalWriteOutput: Removing %s from state (it is now null)", addr)
86 state.RemoveOutputValue(addr)
87 }
88
89 // If we also have an active changeset then we'll replicate the value in
90 // there. This is used in preference to the state where present, since it
91 // *is* able to represent unknowns, while the state cannot.
92 if changes != nil {
93 // For the moment we are not properly tracking changes to output
94 // values, and just marking them always as "Create" or "Destroy"
95 // actions. A future release will rework the output lifecycle so we
96 // can track their changes properly, in a similar way to how we work
97 // with resource instances.
98
99 var change *plans.OutputChange
100 if !val.IsNull() {
101 change = &plans.OutputChange{
102 Addr: addr,
103 Sensitive: n.Sensitive,
104 Change: plans.Change{
105 Action: plans.Create,
106 Before: cty.NullVal(cty.DynamicPseudoType),
107 After: val,
108 },
109 }
110 } else {
111 change = &plans.OutputChange{
112 Addr: addr,
113 Sensitive: n.Sensitive,
114 Change: plans.Change{
115 // This is just a weird placeholder delete action since
116 // we don't have an actual prior value to indicate.
117 // FIXME: Generate real planned changes for output values
118 // that include the old values.
119 Action: plans.Delete,
120 Before: cty.NullVal(cty.DynamicPseudoType),
121 After: cty.NullVal(cty.DynamicPseudoType),
122 },
123 }
124 }
125
126 cs, err := change.Encode()
127 if err != nil {
128 // Should never happen, since we just constructed this right above
129 panic(fmt.Sprintf("planned change for %s could not be encoded: %s", addr, err))
130 }
131 log.Printf("[TRACE] EvalWriteOutput: Saving %s change for %s in changeset", change.Action, addr)
132 changes.RemoveOutputChange(addr) // remove any existing planned change, if present
133 changes.AppendOutputChange(cs) // add the new planned change
134 }
135 }