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