]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / node_data_refresh.go
index d5ca641a6fdf378f5dd841fd095dc249cbbf545f..ab8216341232702017e35363470acd65b77d0a92 100644 (file)
@@ -2,46 +2,71 @@ package terraform
 
 import (
        "github.com/hashicorp/terraform/dag"
+       "github.com/hashicorp/terraform/plans"
+       "github.com/hashicorp/terraform/providers"
+       "github.com/hashicorp/terraform/states"
+       "github.com/hashicorp/terraform/tfdiags"
+       "github.com/zclconf/go-cty/cty"
 )
 
-// NodeRefreshableDataResource represents a resource that is "plannable":
-// it is ready to be planned in order to create a diff.
+// NodeRefreshableDataResource represents a resource that is "refreshable".
 type NodeRefreshableDataResource struct {
-       *NodeAbstractCountResource
+       *NodeAbstractResource
 }
 
+var (
+       _ GraphNodeSubPath              = (*NodeRefreshableDataResource)(nil)
+       _ GraphNodeDynamicExpandable    = (*NodeRefreshableDataResource)(nil)
+       _ GraphNodeReferenceable        = (*NodeRefreshableDataResource)(nil)
+       _ GraphNodeReferencer           = (*NodeRefreshableDataResource)(nil)
+       _ GraphNodeResource             = (*NodeRefreshableDataResource)(nil)
+       _ GraphNodeAttachResourceConfig = (*NodeRefreshableDataResource)(nil)
+)
+
 // GraphNodeDynamicExpandable
 func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
-       // Grab the state which we read
-       state, lock := ctx.State()
-       lock.RLock()
-       defer lock.RUnlock()
-
-       // Expand the resource count which must be available by now from EvalTree
-       count, err := n.Config.Count()
-       if err != nil {
-               return nil, err
+       var diags tfdiags.Diagnostics
+
+       count, countKnown, countDiags := evaluateResourceCountExpressionKnown(n.Config.Count, ctx)
+       diags = diags.Append(countDiags)
+       if countDiags.HasErrors() {
+               return nil, diags.Err()
+       }
+       if !countKnown {
+               // If the count isn't known yet, we'll skip refreshing and try expansion
+               // again during the plan walk.
+               return nil, nil
        }
 
+       // Next we need to potentially rename an instance address in the state
+       // if we're transitioning whether "count" is set at all.
+       fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1)
+
+       // Our graph transformers require access to the full state, so we'll
+       // temporarily lock it while we work on this.
+       state := ctx.State().Lock()
+       defer ctx.State().Unlock()
+
        // The concrete resource factory we'll use
-       concreteResource := func(a *NodeAbstractResource) dag.Vertex {
+       concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex {
                // Add the config and state since we don't do that via transforms
                a.Config = n.Config
                a.ResolvedProvider = n.ResolvedProvider
 
                return &NodeRefreshableDataResourceInstance{
-                       NodeAbstractResource: a,
+                       NodeAbstractResourceInstance: a,
                }
        }
 
        // We also need a destroyable resource for orphans that are a result of a
        // scaled-in count.
-       concreteResourceDestroyable := func(a *NodeAbstractResource) dag.Vertex {
-               // Add the config since we don't do that via transforms
+       concreteResourceDestroyable := func(a *NodeAbstractResourceInstance) dag.Vertex {
+               // Add the config and provider since we don't do that via transforms
                a.Config = n.Config
+               a.ResolvedProvider = n.ResolvedProvider
 
-               return &NodeDestroyableDataResource{
-                       NodeAbstractResource: a,
+               return &NodeDestroyableDataResourceInstance{
+                       NodeAbstractResourceInstance: a,
                }
        }
 
@@ -50,6 +75,7 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er
                // Expand the count.
                &ResourceCountTransformer{
                        Concrete: concreteResource,
+                       Schema:   n.Schema,
                        Count:    count,
                        Addr:     n.ResourceAddr(),
                },
@@ -67,7 +93,7 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er
                &AttachStateTransformer{State: state},
 
                // Targeting
-               &TargetsTransformer{ParsedTargets: n.Targets},
+               &TargetsTransformer{Targets: n.Targets},
 
                // Connect references so ordering is correct
                &ReferenceTransformer{},
@@ -83,139 +109,118 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er
                Name:     "NodeRefreshableDataResource",
        }
 
-       return b.Build(ctx.Path())
+       graph, diags := b.Build(ctx.Path())
+       return graph, diags.ErrWithWarnings()
 }
 
-// NodeRefreshableDataResourceInstance represents a _single_ resource instance
+// NodeRefreshableDataResourceInstance represents a single resource instance
 // that is refreshable.
 type NodeRefreshableDataResourceInstance struct {
-       *NodeAbstractResource
+       *NodeAbstractResourceInstance
 }
 
 // GraphNodeEvalable
 func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
-       addr := n.NodeAbstractResource.Addr
-
-       // stateId is the ID to put into the state
-       stateId := addr.stateId()
-
-       // Build the instance info. More of this will be populated during eval
-       info := &InstanceInfo{
-               Id:   stateId,
-               Type: addr.Type,
-       }
-
-       // Get the state if we have it, if not we build it
-       rs := n.ResourceState
-       if rs == nil {
-               rs = &ResourceState{
-                       Provider: n.ResolvedProvider,
-               }
-       }
+       addr := n.ResourceInstanceAddr()
 
-       // If the config isn't empty we update the state
-       if n.Config != nil {
-               rs = &ResourceState{
-                       Type:         n.Config.Type,
-                       Provider:     n.Config.Provider,
-                       Dependencies: n.StateReferences(),
-               }
-       }
-
-       // Build the resource for eval
-       resource := &Resource{
-               Name:       addr.Name,
-               Type:       addr.Type,
-               CountIndex: addr.Index,
-       }
-       if resource.CountIndex < 0 {
-               resource.CountIndex = 0
-       }
-
-       // Declare a bunch of variables that are used for state during
-       // evaluation. Most of this are written to by-address below.
-       var config *ResourceConfig
-       var diff *InstanceDiff
-       var provider ResourceProvider
-       var state *InstanceState
+       // These variables are the state for the eval sequence below, and are
+       // updated through pointers.
+       var provider providers.Interface
+       var providerSchema *ProviderSchema
+       var change *plans.ResourceInstanceChange
+       var state *states.ResourceInstanceObject
+       var configVal cty.Value
 
        return &EvalSequence{
                Nodes: []EvalNode{
+                       &EvalGetProvider{
+                               Addr:   n.ResolvedProvider,
+                               Output: &provider,
+                               Schema: &providerSchema,
+                       },
+
                        // Always destroy the existing state first, since we must
                        // make sure that values from a previous read will not
                        // get interpolated if we end up needing to defer our
                        // loading until apply time.
                        &EvalWriteState{
-                               Name:         stateId,
-                               ResourceType: rs.Type,
-                               Provider:     n.ResolvedProvider,
-                               Dependencies: rs.Dependencies,
-                               State:        &state, // state is nil here
+                               Addr:           addr.Resource,
+                               ProviderAddr:   n.ResolvedProvider,
+                               State:          &state, // a pointer to nil, here
+                               ProviderSchema: &providerSchema,
                        },
 
-                       &EvalInterpolate{
-                               Config:   n.Config.RawConfig.Copy(),
-                               Resource: resource,
-                               Output:   &config,
-                       },
-
-                       // The rest of this pass can proceed only if there are no
-                       // computed values in our config.
-                       // (If there are, we'll deal with this during the plan and
-                       // apply phases.)
                        &EvalIf{
                                If: func(ctx EvalContext) (bool, error) {
-                                       if config.ComputedKeys != nil && len(config.ComputedKeys) > 0 {
-                                               return true, EvalEarlyExitError{}
-                                       }
-
                                        // If the config explicitly has a depends_on for this
                                        // data source, assume the intention is to prevent
-                                       // refreshing ahead of that dependency.
+                                       // refreshing ahead of that dependency, and therefore
+                                       // we need to deal with this resource during the apply
+                                       // phase..
                                        if len(n.Config.DependsOn) > 0 {
                                                return true, EvalEarlyExitError{}
                                        }
 
                                        return true, nil
                                },
-
                                Then: EvalNoop{},
                        },
 
-                       // The remainder of this pass is the same as running
-                       // a "plan" pass immediately followed by an "apply" pass,
-                       // populating the state early so it'll be available to
-                       // provider configurations that need this data during
-                       // refresh/plan.
-                       &EvalGetProvider{
-                               Name:   n.ResolvedProvider,
-                               Output: &provider,
-                       },
-
-                       &EvalReadDataDiff{
-                               Info:        info,
-                               Config:      &config,
-                               Provider:    &provider,
-                               Output:      &diff,
-                               OutputState: &state,
-                       },
-
-                       &EvalReadDataApply{
-                               Info:     info,
-                               Diff:     &diff,
-                               Provider: &provider,
-                               Output:   &state,
+                       // EvalReadData will _attempt_ to read the data source, but may
+                       // generate an incomplete planned object if the configuration
+                       // includes values that won't be known until apply.
+                       &EvalReadData{
+                               Addr:              addr.Resource,
+                               Config:            n.Config,
+                               Dependencies:      n.StateReferences(),
+                               Provider:          &provider,
+                               ProviderAddr:      n.ResolvedProvider,
+                               ProviderSchema:    &providerSchema,
+                               OutputChange:      &change,
+                               OutputConfigValue: &configVal,
+                               OutputState:       &state,
                        },
 
-                       &EvalWriteState{
-                               Name:         stateId,
-                               ResourceType: rs.Type,
-                               Provider:     n.ResolvedProvider,
-                               Dependencies: rs.Dependencies,
-                               State:        &state,
+                       &EvalIf{
+                               If: func(ctx EvalContext) (bool, error) {
+                                       return (*state).Status != states.ObjectPlanned, nil
+                               },
+                               Then: &EvalSequence{
+                                       Nodes: []EvalNode{
+                                               &EvalWriteState{
+                                                       Addr:           addr.Resource,
+                                                       ProviderAddr:   n.ResolvedProvider,
+                                                       State:          &state,
+                                                       ProviderSchema: &providerSchema,
+                                               },
+                                               &EvalUpdateStateHook{},
+                                       },
+                               },
+                               Else: &EvalSequence{
+                                       // We can't deal with this yet, so we'll repeat this step
+                                       // during the plan walk to produce a planned change to read
+                                       // this during the apply walk. However, we do still need to
+                                       // save the generated change and partial state so that
+                                       // results from it can be included in other data resources
+                                       // or provider configurations during the refresh walk.
+                                       // (The planned object we save in the state here will be
+                                       // pruned out at the end of the refresh walk, returning
+                                       // it back to being unset again for subsequent walks.)
+                                       Nodes: []EvalNode{
+                                               &EvalWriteDiff{
+                                                       Addr:           addr.Resource,
+                                                       Change:         &change,
+                                                       ProviderSchema: &providerSchema,
+                                               },
+                                               &EvalWriteState{
+                                                       Addr:           addr.Resource,
+                                                       ProviderAddr:   n.ResolvedProvider,
+                                                       State:          &state,
+                                                       ProviderSchema: &providerSchema,
+                                               },
+                                       },
+                               },
                        },
-
-                       &EvalUpdateStateHook{},
                },
        }
 }