github.com/spirius/terraform@v0.10.0-beta2.0.20170714185654-87b2c0cf8fea/terraform/node_resource_refresh.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform/config"
     7  	"github.com/hashicorp/terraform/dag"
     8  )
     9  
    10  // NodeRefreshableManagedResource represents a resource that is expanabled into
    11  // NodeRefreshableManagedResourceInstance. Resource count orphans are also added.
    12  type NodeRefreshableManagedResource struct {
    13  	*NodeAbstractCountResource
    14  }
    15  
    16  // GraphNodeDynamicExpandable
    17  func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
    18  	// Grab the state which we read
    19  	state, lock := ctx.State()
    20  	lock.RLock()
    21  	defer lock.RUnlock()
    22  
    23  	// Expand the resource count which must be available by now from EvalTree
    24  	count, err := n.Config.Count()
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  
    29  	// The concrete resource factory we'll use
    30  	concreteResource := func(a *NodeAbstractResource) dag.Vertex {
    31  		// Add the config and state since we don't do that via transforms
    32  		a.Config = n.Config
    33  
    34  		return &NodeRefreshableManagedResourceInstance{
    35  			NodeAbstractResource: a,
    36  		}
    37  	}
    38  
    39  	// Start creating the steps
    40  	steps := []GraphTransformer{
    41  		// Expand the count.
    42  		&ResourceCountTransformer{
    43  			Concrete: concreteResource,
    44  			Count:    count,
    45  			Addr:     n.ResourceAddr(),
    46  		},
    47  
    48  		// Add the count orphans to make sure these resources are accounted for
    49  		// during a scale in.
    50  		&OrphanResourceCountTransformer{
    51  			Concrete: concreteResource,
    52  			Count:    count,
    53  			Addr:     n.ResourceAddr(),
    54  			State:    state,
    55  		},
    56  
    57  		// Attach the state
    58  		&AttachStateTransformer{State: state},
    59  
    60  		// Targeting
    61  		&TargetsTransformer{ParsedTargets: n.Targets},
    62  
    63  		// Connect references so ordering is correct
    64  		&ReferenceTransformer{},
    65  
    66  		// Make sure there is a single root
    67  		&RootTransformer{},
    68  	}
    69  
    70  	// Build the graph
    71  	b := &BasicGraphBuilder{
    72  		Steps:    steps,
    73  		Validate: true,
    74  		Name:     "NodeRefreshableManagedResource",
    75  	}
    76  
    77  	return b.Build(ctx.Path())
    78  }
    79  
    80  // NodeRefreshableManagedResourceInstance represents a resource that is "applyable":
    81  // it is ready to be applied and is represented by a diff.
    82  type NodeRefreshableManagedResourceInstance struct {
    83  	*NodeAbstractResource
    84  }
    85  
    86  // GraphNodeDestroyer
    87  func (n *NodeRefreshableManagedResourceInstance) DestroyAddr() *ResourceAddress {
    88  	return n.Addr
    89  }
    90  
    91  // GraphNodeEvalable
    92  func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode {
    93  	// Eval info is different depending on what kind of resource this is
    94  	switch mode := n.Addr.Mode; mode {
    95  	case config.ManagedResourceMode:
    96  		if n.ResourceState == nil {
    97  			return n.evalTreeManagedResourceNoState()
    98  		}
    99  		return n.evalTreeManagedResource()
   100  
   101  	case config.DataResourceMode:
   102  		// Get the data source node. If we don't have a configuration
   103  		// then it is an orphan so we destroy it (remove it from the state).
   104  		var dn GraphNodeEvalable
   105  		if n.Config != nil {
   106  			dn = &NodeRefreshableDataResourceInstance{
   107  				NodeAbstractResource: n.NodeAbstractResource,
   108  			}
   109  		} else {
   110  			dn = &NodeDestroyableDataResource{
   111  				NodeAbstractResource: n.NodeAbstractResource,
   112  			}
   113  		}
   114  
   115  		return dn.EvalTree()
   116  	default:
   117  		panic(fmt.Errorf("unsupported resource mode %s", mode))
   118  	}
   119  }
   120  
   121  func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalNode {
   122  	addr := n.NodeAbstractResource.Addr
   123  
   124  	// stateId is the ID to put into the state
   125  	stateId := addr.stateId()
   126  
   127  	// Build the instance info. More of this will be populated during eval
   128  	info := &InstanceInfo{
   129  		Id:   stateId,
   130  		Type: addr.Type,
   131  	}
   132  
   133  	// Declare a bunch of variables that are used for state during
   134  	// evaluation. Most of this are written to by-address below.
   135  	var provider ResourceProvider
   136  	var state *InstanceState
   137  
   138  	// This happened during initial development. All known cases were
   139  	// fixed and tested but as a sanity check let's assert here.
   140  	if n.ResourceState == nil {
   141  		err := fmt.Errorf(
   142  			"No resource state attached for addr: %s\n\n"+
   143  				"This is a bug. Please report this to Terraform with your configuration\n"+
   144  				"and state attached. Please be careful to scrub any sensitive information.",
   145  			addr)
   146  		return &EvalReturnError{Error: &err}
   147  	}
   148  
   149  	return &EvalSequence{
   150  		Nodes: []EvalNode{
   151  			&EvalGetProvider{
   152  				Name:   n.ProvidedBy()[0],
   153  				Output: &provider,
   154  			},
   155  			&EvalReadState{
   156  				Name:   stateId,
   157  				Output: &state,
   158  			},
   159  			&EvalRefresh{
   160  				Info:     info,
   161  				Provider: &provider,
   162  				State:    &state,
   163  				Output:   &state,
   164  			},
   165  			&EvalWriteState{
   166  				Name:         stateId,
   167  				ResourceType: n.ResourceState.Type,
   168  				Provider:     n.ResourceState.Provider,
   169  				Dependencies: n.ResourceState.Dependencies,
   170  				State:        &state,
   171  			},
   172  		},
   173  	}
   174  }
   175  
   176  // evalTreeManagedResourceNoState produces an EvalSequence for refresh resource
   177  // nodes that don't have state attached. An example of where this functionality
   178  // is useful is when a resource that already exists in state is being scaled
   179  // out, ie: has its resource count increased. In this case, the scaled out node
   180  // needs to be available to other nodes (namely data sources) that may depend
   181  // on it for proper interpolation, or confusing "index out of range" errors can
   182  // occur.
   183  //
   184  // The steps in this sequence are very similar to the steps carried out in
   185  // plan, but nothing is done with the diff after it is created - it is dropped,
   186  // and its changes are not counted in the UI.
   187  func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState() EvalNode {
   188  	// Declare a bunch of variables that are used for state during
   189  	// evaluation. Most of this are written to by-address below.
   190  	var provider ResourceProvider
   191  	var state *InstanceState
   192  	var resourceConfig *ResourceConfig
   193  
   194  	addr := n.NodeAbstractResource.Addr
   195  	stateID := addr.stateId()
   196  	info := &InstanceInfo{
   197  		Id:         stateID,
   198  		Type:       addr.Type,
   199  		ModulePath: normalizeModulePath(addr.Path),
   200  	}
   201  
   202  	// Build the resource for eval
   203  	resource := &Resource{
   204  		Name:       addr.Name,
   205  		Type:       addr.Type,
   206  		CountIndex: addr.Index,
   207  	}
   208  	if resource.CountIndex < 0 {
   209  		resource.CountIndex = 0
   210  	}
   211  
   212  	// Determine the dependencies for the state.
   213  	stateDeps := n.StateReferences()
   214  
   215  	return &EvalSequence{
   216  		Nodes: []EvalNode{
   217  			&EvalInterpolate{
   218  				Config:   n.Config.RawConfig.Copy(),
   219  				Resource: resource,
   220  				Output:   &resourceConfig,
   221  			},
   222  			&EvalGetProvider{
   223  				Name:   n.ProvidedBy()[0],
   224  				Output: &provider,
   225  			},
   226  			// Re-run validation to catch any errors we missed, e.g. type
   227  			// mismatches on computed values.
   228  			&EvalValidateResource{
   229  				Provider:       &provider,
   230  				Config:         &resourceConfig,
   231  				ResourceName:   n.Config.Name,
   232  				ResourceType:   n.Config.Type,
   233  				ResourceMode:   n.Config.Mode,
   234  				IgnoreWarnings: true,
   235  			},
   236  			&EvalReadState{
   237  				Name:   stateID,
   238  				Output: &state,
   239  			},
   240  			&EvalDiff{
   241  				Name:        stateID,
   242  				Info:        info,
   243  				Config:      &resourceConfig,
   244  				Resource:    n.Config,
   245  				Provider:    &provider,
   246  				State:       &state,
   247  				OutputState: &state,
   248  				Stub:        true,
   249  			},
   250  			&EvalWriteState{
   251  				Name:         stateID,
   252  				ResourceType: n.Config.Type,
   253  				Provider:     n.Config.Provider,
   254  				Dependencies: stateDeps,
   255  				State:        &state,
   256  			},
   257  		},
   258  	}
   259  }