]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/github.com/hashicorp/terraform/terraform/node_resource_refresh.go
update vendor and go.mod
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / node_resource_refresh.go
index 3a44926cef76d51c8d32795403b2539996ffdd65..9daeabfa69cbef77c2eb1c9d5e4db0b59affbaa6 100644 (file)
@@ -2,64 +2,179 @@ package terraform
 
 import (
        "fmt"
+       "log"
 
-       "github.com/hashicorp/terraform/config"
+       "github.com/hashicorp/terraform/plans"
+       "github.com/hashicorp/terraform/providers"
+
+       "github.com/hashicorp/terraform/states"
+
+       "github.com/hashicorp/terraform/addrs"
+       "github.com/hashicorp/terraform/dag"
+       "github.com/hashicorp/terraform/tfdiags"
 )
 
-// NodeRefreshableResource represents a resource that is "applyable":
-// it is ready to be applied and is represented by a diff.
-type NodeRefreshableResource struct {
+// NodeRefreshableManagedResource represents a resource that is expanabled into
+// NodeRefreshableManagedResourceInstance. Resource count orphans are also added.
+type NodeRefreshableManagedResource struct {
        *NodeAbstractResource
 }
 
+var (
+       _ GraphNodeSubPath              = (*NodeRefreshableManagedResource)(nil)
+       _ GraphNodeDynamicExpandable    = (*NodeRefreshableManagedResource)(nil)
+       _ GraphNodeReferenceable        = (*NodeRefreshableManagedResource)(nil)
+       _ GraphNodeReferencer           = (*NodeRefreshableManagedResource)(nil)
+       _ GraphNodeResource             = (*NodeRefreshableManagedResource)(nil)
+       _ GraphNodeAttachResourceConfig = (*NodeRefreshableManagedResource)(nil)
+)
+
+// GraphNodeDynamicExpandable
+func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
+       var diags tfdiags.Diagnostics
+
+       count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx)
+       diags = diags.Append(countDiags)
+       if countDiags.HasErrors() {
+               return nil, diags.Err()
+       }
+
+       forEachMap, forEachDiags := evaluateResourceForEachExpression(n.Config.ForEach, ctx)
+       if forEachDiags.HasErrors() {
+               return nil, diags.Err()
+       }
+
+       // 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 *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 &NodeRefreshableManagedResourceInstance{
+                       NodeAbstractResourceInstance: a,
+               }
+       }
+
+       // Start creating the steps
+       steps := []GraphTransformer{
+               // Expand the count.
+               &ResourceCountTransformer{
+                       Concrete: concreteResource,
+                       Schema:   n.Schema,
+                       Count:    count,
+                       ForEach:  forEachMap,
+                       Addr:     n.ResourceAddr(),
+               },
+
+               // Add the count orphans to make sure these resources are accounted for
+               // during a scale in.
+               &OrphanResourceCountTransformer{
+                       Concrete: concreteResource,
+                       Count:    count,
+                       ForEach:  forEachMap,
+                       Addr:     n.ResourceAddr(),
+                       State:    state,
+               },
+
+               // Attach the state
+               &AttachStateTransformer{State: state},
+
+               // Targeting
+               &TargetsTransformer{Targets: n.Targets},
+
+               // Connect references so ordering is correct
+               &ReferenceTransformer{},
+
+               // Make sure there is a single root
+               &RootTransformer{},
+       }
+
+       // Build the graph
+       b := &BasicGraphBuilder{
+               Steps:    steps,
+               Validate: true,
+               Name:     "NodeRefreshableManagedResource",
+       }
+
+       graph, diags := b.Build(ctx.Path())
+       return graph, diags.ErrWithWarnings()
+}
+
+// NodeRefreshableManagedResourceInstance represents a resource that is "applyable":
+// it is ready to be applied and is represented by a diff.
+type NodeRefreshableManagedResourceInstance struct {
+       *NodeAbstractResourceInstance
+}
+
+var (
+       _ GraphNodeSubPath              = (*NodeRefreshableManagedResourceInstance)(nil)
+       _ GraphNodeReferenceable        = (*NodeRefreshableManagedResourceInstance)(nil)
+       _ GraphNodeReferencer           = (*NodeRefreshableManagedResourceInstance)(nil)
+       _ GraphNodeDestroyer            = (*NodeRefreshableManagedResourceInstance)(nil)
+       _ GraphNodeResource             = (*NodeRefreshableManagedResourceInstance)(nil)
+       _ GraphNodeResourceInstance     = (*NodeRefreshableManagedResourceInstance)(nil)
+       _ GraphNodeAttachResourceConfig = (*NodeRefreshableManagedResourceInstance)(nil)
+       _ GraphNodeAttachResourceState  = (*NodeRefreshableManagedResourceInstance)(nil)
+       _ GraphNodeEvalable             = (*NodeRefreshableManagedResourceInstance)(nil)
+)
+
 // GraphNodeDestroyer
-func (n *NodeRefreshableResource) DestroyAddr() *ResourceAddress {
-       return n.Addr
+func (n *NodeRefreshableManagedResourceInstance) DestroyAddr() *addrs.AbsResourceInstance {
+       addr := n.ResourceInstanceAddr()
+       return &addr
 }
 
 // GraphNodeEvalable
-func (n *NodeRefreshableResource) EvalTree() EvalNode {
+func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode {
+       addr := n.ResourceInstanceAddr()
+
        // Eval info is different depending on what kind of resource this is
-       switch mode := n.Addr.Mode; mode {
-       case config.ManagedResourceMode:
+       switch addr.Resource.Resource.Mode {
+       case addrs.ManagedResourceMode:
+               if n.ResourceState == nil {
+                       log.Printf("[TRACE] NodeRefreshableManagedResourceInstance: %s has no existing state to refresh", addr)
+                       return n.evalTreeManagedResourceNoState()
+               }
+               log.Printf("[TRACE] NodeRefreshableManagedResourceInstance: %s will be refreshed", addr)
                return n.evalTreeManagedResource()
 
-       case config.DataResourceMode:
+       case addrs.DataResourceMode:
                // Get the data source node. If we don't have a configuration
                // then it is an orphan so we destroy it (remove it from the state).
                var dn GraphNodeEvalable
                if n.Config != nil {
                        dn = &NodeRefreshableDataResourceInstance{
-                               NodeAbstractResource: n.NodeAbstractResource,
+                               NodeAbstractResourceInstance: n.NodeAbstractResourceInstance,
                        }
                } else {
-                       dn = &NodeDestroyableDataResource{
-                               NodeAbstractResource: n.NodeAbstractResource,
+                       dn = &NodeDestroyableDataResourceInstance{
+                               NodeAbstractResourceInstance: n.NodeAbstractResourceInstance,
                        }
                }
 
                return dn.EvalTree()
        default:
-               panic(fmt.Errorf("unsupported resource mode %s", mode))
+               panic(fmt.Errorf("unsupported resource mode %s", addr.Resource.Resource.Mode))
        }
 }
 
-func (n *NodeRefreshableResource) evalTreeManagedResource() 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,
-       }
+func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalNode {
+       addr := n.ResourceInstanceAddr()
 
        // Declare a bunch of variables that are used for state during
        // evaluation. Most of this are written to by-address below.
-       var provider ResourceProvider
-       var state *InstanceState
+       var provider providers.Interface
+       var providerSchema *ProviderSchema
+       var state *states.ResourceInstanceObject
 
        // This happened during initial development. All known cases were
        // fixed and tested but as a sanity check let's assert here.
@@ -75,25 +190,106 @@ func (n *NodeRefreshableResource) evalTreeManagedResource() EvalNode {
        return &EvalSequence{
                Nodes: []EvalNode{
                        &EvalGetProvider{
-                               Name:   n.ProvidedBy()[0],
+                               Addr:   n.ResolvedProvider,
                                Output: &provider,
+                               Schema: &providerSchema,
                        },
+
                        &EvalReadState{
-                               Name:   stateId,
+                               Addr:           addr.Resource,
+                               Provider:       &provider,
+                               ProviderSchema: &providerSchema,
+
                                Output: &state,
                        },
+
                        &EvalRefresh{
-                               Info:     info,
-                               Provider: &provider,
-                               State:    &state,
-                               Output:   &state,
+                               Addr:           addr.Resource,
+                               ProviderAddr:   n.ResolvedProvider,
+                               Provider:       &provider,
+                               ProviderSchema: &providerSchema,
+                               State:          &state,
+                               Output:         &state,
+                       },
+
+                       &EvalWriteState{
+                               Addr:           addr.Resource,
+                               ProviderAddr:   n.ResolvedProvider,
+                               ProviderSchema: &providerSchema,
+                               State:          &state,
+                       },
+               },
+       }
+}
+
+// evalTreeManagedResourceNoState produces an EvalSequence for refresh resource
+// nodes that don't have state attached. An example of where this functionality
+// is useful is when a resource that already exists in state is being scaled
+// out, ie: has its resource count increased. In this case, the scaled out node
+// needs to be available to other nodes (namely data sources) that may depend
+// on it for proper interpolation, or confusing "index out of range" errors can
+// occur.
+//
+// The steps in this sequence are very similar to the steps carried out in
+// plan, but nothing is done with the diff after it is created - it is dropped,
+// and its changes are not counted in the UI.
+func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState() EvalNode {
+       addr := n.ResourceInstanceAddr()
+
+       // Declare a bunch of variables that are used for state during
+       // evaluation. Most of this are written to by-address below.
+       var provider providers.Interface
+       var providerSchema *ProviderSchema
+       var change *plans.ResourceInstanceChange
+       var state *states.ResourceInstanceObject
+
+       return &EvalSequence{
+               Nodes: []EvalNode{
+                       &EvalGetProvider{
+                               Addr:   n.ResolvedProvider,
+                               Output: &provider,
+                               Schema: &providerSchema,
                        },
+
+                       &EvalReadState{
+                               Addr:           addr.Resource,
+                               Provider:       &provider,
+                               ProviderSchema: &providerSchema,
+
+                               Output: &state,
+                       },
+
+                       &EvalDiff{
+                               Addr:           addr.Resource,
+                               Config:         n.Config,
+                               Provider:       &provider,
+                               ProviderAddr:   n.ResolvedProvider,
+                               ProviderSchema: &providerSchema,
+                               State:          &state,
+                               OutputChange:   &change,
+                               OutputState:    &state,
+                               Stub:           true,
+                       },
+
                        &EvalWriteState{
-                               Name:         stateId,
-                               ResourceType: n.ResourceState.Type,
-                               Provider:     n.ResourceState.Provider,
-                               Dependencies: n.ResourceState.Dependencies,
-                               State:        &state,
+                               Addr:           addr.Resource,
+                               ProviderAddr:   n.ResolvedProvider,
+                               ProviderSchema: &providerSchema,
+                               State:          &state,
+                       },
+
+                       // We must also save the planned change, so that expressions in
+                       // other nodes, such as provider configurations and data resources,
+                       // can work with the planned new value.
+                       //
+                       // This depends on the fact that Context.Refresh creates a
+                       // temporary new empty changeset for the duration of its graph
+                       // walk, and so this recorded change will be discarded immediately
+                       // after the refresh walk completes.
+                       &EvalWriteDiff{
+                               Addr:           addr.Resource,
+                               Change:         &change,
+                               ProviderSchema: &providerSchema,
                        },
                },
        }