Skip to content

Commit

Permalink
core: refresh steps for data resources
Browse files Browse the repository at this point in the history
This causes data resources that don't have computed arguments to be loaded
as expected, populating the state so that dependent resources can use
their attributes.

It doesn't yet work for data resources with computed arguments; there are
some TODO comments in there for what remains to complete that.
  • Loading branch information
apparentlymart committed May 2, 2016
1 parent 64c5cad commit 64b811e
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 2 deletions.
65 changes: 65 additions & 0 deletions terraform/eval_read_data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package terraform

import (
"fmt"
"log"
)

// EvalReadData is an EvalNode implementation that executes a data resource's
// ReadData method and populates its state.
type EvalReadData struct {
Provider *ResourceProvider
Output **InstanceState
Config **ResourceConfig
Info *InstanceInfo
}

func (n *EvalReadData) Eval(ctx EvalContext) (interface{}, error) {
// TODO: test
provider := *n.Provider

config := *n.Config
if config == nil {
// Should never happen
panic(fmt.Errorf("EvalDataResourceInit for %s given nil ResourceConfig", n.Info.HumanId()))
}

// We can't initialize until our config has been completely interpolated.
// If a data resource depends on a not-yet-created managed resource then
// we'll exit here during Refresh and then visit again during Apply,
// at which point the dependencies should all be ready.
if config.ComputedKeys != nil && len(config.ComputedKeys) > 0 {
log.Printf("[TRACE] %s: skipping read: config has computed attributes", n.Info.Id)
return nil, nil
}

// Call pre-refresh hook
err := ctx.Hook(func(h Hook) (HookAction, error) {
// We don't have a state yet, so we'll just give the hook an
// empty one to work with.
return h.PreRefresh(n.Info, &InstanceState{})
})
if err != nil {
return nil, err
}

// Refresh!
state, err := provider.ReadData(n.Info, config)
if err != nil {
return nil, fmt.Errorf("%s: %s", n.Info.Id, err.Error())
}

// Call post-refresh hook
err = ctx.Hook(func(h Hook) (HookAction, error) {
return h.PostRefresh(n.Info, state)
})
if err != nil {
return nil, err
}

if n.Output != nil {
*n.Output = state
}

return nil, nil
}
45 changes: 43 additions & 2 deletions terraform/transform_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -586,11 +586,52 @@ func (n *graphNodeExpandedResource) managedResourceEvalNodes(resource *Resource,

func (n *graphNodeExpandedResource) dataResourceEvalNodes(resource *Resource, info *InstanceInfo, resourceConfig *ResourceConfig) []EvalNode {
//var diff *InstanceDiff
//var provider ResourceProvider
//var state *InstanceState
var provider ResourceProvider
var state *InstanceState
var config *ResourceConfig

nodes := make([]EvalNode, 0, 5)

// Refresh the resource
nodes = append(nodes, &EvalOpFilter{
Ops: []walkOperation{walkRefresh},
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Output: &provider,
},
&EvalInterpolate{
Config: n.Resource.RawConfig.Copy(),
Resource: resource,
Output: &config,
},
&EvalReadData{
Provider: &provider,
Output: &state,
Config: &config,
Info: info,
},
&EvalWriteState{
Name: n.stateId(),
ResourceType: n.Resource.Type,
Provider: n.Resource.Provider,
Dependencies: n.StateDependencies(),
State: &state,
},
},
},
})

// TODO: Diff should check if we have a state yet, and if not
// produce a creation diff for our resource that we can then
// process during Apply.

// TODO: Apply should check whether we have a creation diff,
// and if so repeat the same steps we would do during Refresh
// so that we'll populate our state before any of our dependencies
// need access to it.

return nodes
}

Expand Down

0 comments on commit 64b811e

Please sign in to comment.