Skip to content

Commit 0fdedbd

Browse files
authored
Merge pull request hashicorp#9496 from hashicorp/b-apply-data
terraform: new apply resource node supports data sources
2 parents 293e214 + 4a6cc3b commit 0fdedbd

4 files changed

Lines changed: 144 additions & 4 deletions

File tree

terraform/context_apply_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,38 @@ func TestContext2Apply_destroyComputed(t *testing.T) {
508508
}
509509
}
510510

511+
func TestContext2Apply_dataBasic(t *testing.T) {
512+
m := testModule(t, "apply-data-basic")
513+
p := testProvider("null")
514+
p.ApplyFn = testApplyFn
515+
p.DiffFn = testDiffFn
516+
p.ReadDataApplyReturn = &InstanceState{ID: "yo"}
517+
518+
ctx := testContext2(t, &ContextOpts{
519+
Module: m,
520+
Providers: map[string]ResourceProviderFactory{
521+
"null": testProviderFuncFixed(p),
522+
},
523+
})
524+
525+
if p, err := ctx.Plan(); err != nil {
526+
t.Fatalf("err: %s", err)
527+
} else {
528+
t.Logf(p.String())
529+
}
530+
531+
state, err := ctx.Apply()
532+
if err != nil {
533+
t.Fatalf("err: %s", err)
534+
}
535+
536+
actual := strings.TrimSpace(state.String())
537+
expected := strings.TrimSpace(testTerraformApplyDataBasicStr)
538+
if actual != expected {
539+
t.Fatalf("bad: \n%s", actual)
540+
}
541+
}
542+
511543
func TestContext2Apply_destroyData(t *testing.T) {
512544
m := testModule(t, "apply-destroy-data-resource")
513545
p := testProvider("null")

terraform/node_resource_apply.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package terraform
22

33
import (
44
"fmt"
5+
6+
"github.com/hashicorp/terraform/config"
57
)
68

79
// NodeApplyableResource represents a resource that is "applyable":
@@ -49,6 +51,106 @@ func (n *NodeApplyableResource) EvalTree() EvalNode {
4951
stateDeps = oldN.StateDependencies()
5052
}
5153

54+
// Eval info is different depending on what kind of resource this is
55+
switch n.Config.Mode {
56+
case config.ManagedResourceMode:
57+
return n.evalTreeManagedResource(
58+
stateId, info, resource, stateDeps,
59+
)
60+
case config.DataResourceMode:
61+
return n.evalTreeDataResource(
62+
stateId, info, resource, stateDeps)
63+
default:
64+
panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode))
65+
}
66+
}
67+
68+
func (n *NodeApplyableResource) evalTreeDataResource(
69+
stateId string, info *InstanceInfo,
70+
resource *Resource, stateDeps []string) EvalNode {
71+
var provider ResourceProvider
72+
var config *ResourceConfig
73+
var diff *InstanceDiff
74+
var state *InstanceState
75+
76+
return &EvalSequence{
77+
Nodes: []EvalNode{
78+
// Get the saved diff for apply
79+
&EvalReadDiff{
80+
Name: stateId,
81+
Diff: &diff,
82+
},
83+
84+
// Stop here if we don't actually have a diff
85+
&EvalIf{
86+
If: func(ctx EvalContext) (bool, error) {
87+
if diff == nil {
88+
return true, EvalEarlyExitError{}
89+
}
90+
91+
if diff.GetAttributesLen() == 0 {
92+
return true, EvalEarlyExitError{}
93+
}
94+
95+
return true, nil
96+
},
97+
Then: EvalNoop{},
98+
},
99+
100+
// We need to re-interpolate the config here, rather than
101+
// just using the diff's values directly, because we've
102+
// potentially learned more variable values during the
103+
// apply pass that weren't known when the diff was produced.
104+
&EvalInterpolate{
105+
Config: n.Config.RawConfig.Copy(),
106+
Resource: resource,
107+
Output: &config,
108+
},
109+
110+
&EvalGetProvider{
111+
Name: n.ProvidedBy()[0],
112+
Output: &provider,
113+
},
114+
115+
// Make a new diff with our newly-interpolated config.
116+
&EvalReadDataDiff{
117+
Info: info,
118+
Config: &config,
119+
Previous: &diff,
120+
Provider: &provider,
121+
Output: &diff,
122+
},
123+
124+
&EvalReadDataApply{
125+
Info: info,
126+
Diff: &diff,
127+
Provider: &provider,
128+
Output: &state,
129+
},
130+
131+
&EvalWriteState{
132+
Name: stateId,
133+
ResourceType: n.Config.Type,
134+
Provider: n.Config.Provider,
135+
Dependencies: stateDeps,
136+
State: &state,
137+
},
138+
139+
// Clear the diff now that we've applied it, so
140+
// later nodes won't see a diff that's now a no-op.
141+
&EvalWriteDiff{
142+
Name: stateId,
143+
Diff: nil,
144+
},
145+
146+
&EvalUpdateStateHook{},
147+
},
148+
}
149+
}
150+
151+
func (n *NodeApplyableResource) evalTreeManagedResource(
152+
stateId string, info *InstanceInfo,
153+
resource *Resource, stateDeps []string) EvalNode {
52154
// Declare a bunch of variables that are used for state during
53155
// evaluation. Most of this are written to by-address below.
54156
var provider ResourceProvider

terraform/terraform_test.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ func TestMain(m *testing.M) {
2525
// Experimental features
2626
xNewApply := flag.Bool("Xnew-apply", false, "Experiment: new apply graph")
2727

28+
// Normal features
29+
shadow := flag.Bool("shadow", true, "Enable shadow graph")
30+
2831
flag.Parse()
2932

3033
// Setup experimental features
3134
X_newApply = *xNewApply
32-
if X_newApply {
33-
println("Xnew-apply enabled")
34-
}
3535

3636
if testing.Verbose() {
3737
// if we're verbose, use the logging requested by TF_LOG
@@ -48,7 +48,7 @@ func TestMain(m *testing.M) {
4848
contextTestDeepCopyOnPlan = true
4949

5050
// Shadow the new graphs
51-
contextTestShadow = true
51+
contextTestShadow = *shadow
5252

5353
os.Exit(m.Run())
5454
}
@@ -257,6 +257,11 @@ aws_instance.foo:
257257
type = aws_instance
258258
`
259259

260+
const testTerraformApplyDataBasicStr = `
261+
data.null_data_source.testing:
262+
ID = yo
263+
`
264+
260265
const testTerraformApplyRefCountStr = `
261266
aws_instance.bar:
262267
ID = foo
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
data "null_data_source" "testing" {}

0 commit comments

Comments
 (0)