diff --git a/terraform/eval_read_data.go b/terraform/eval_read_data.go new file mode 100644 index 000000000000..43376670b74e --- /dev/null +++ b/terraform/eval_read_data.go @@ -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 +} diff --git a/terraform/transform_resource.go b/terraform/transform_resource.go index d252e3ef0f5e..99cb7ac41b7c 100644 --- a/terraform/transform_resource.go +++ b/terraform/transform_resource.go @@ -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 }