github.com/opsidian/terraform@v0.7.8-0.20161104123224-27c39cdfba5b/terraform/node_resource_apply.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform/config"
     7  )
     8  
     9  // NodeApplyableResource represents a resource that is "applyable":
    10  // it is ready to be applied and is represented by a diff.
    11  type NodeApplyableResource struct {
    12  	*NodeAbstractResource
    13  }
    14  
    15  // GraphNodeCreator
    16  func (n *NodeApplyableResource) CreateAddr() *ResourceAddress {
    17  	return n.NodeAbstractResource.Addr
    18  }
    19  
    20  // GraphNodeEvalable
    21  func (n *NodeApplyableResource) EvalTree() EvalNode {
    22  	addr := n.NodeAbstractResource.Addr
    23  
    24  	// stateId is the ID to put into the state
    25  	stateId := addr.stateId()
    26  	if addr.Index > -1 {
    27  		stateId = fmt.Sprintf("%s.%d", stateId, addr.Index)
    28  	}
    29  
    30  	// Build the instance info. More of this will be populated during eval
    31  	info := &InstanceInfo{
    32  		Id:   stateId,
    33  		Type: addr.Type,
    34  	}
    35  
    36  	// Build the resource for eval
    37  	resource := &Resource{
    38  		Name:       addr.Name,
    39  		Type:       addr.Type,
    40  		CountIndex: addr.Index,
    41  	}
    42  	if resource.CountIndex < 0 {
    43  		resource.CountIndex = 0
    44  	}
    45  
    46  	// Determine the dependencies for the state. We use some older
    47  	// code for this that we've used for a long time.
    48  	var stateDeps []string
    49  	{
    50  		oldN := &graphNodeExpandedResource{Resource: n.Config}
    51  		stateDeps = oldN.StateDependencies()
    52  	}
    53  
    54  	// Eval info is different depending on what kind of resource this is
    55  	switch n.Config.Mode {
    56  	case config.ManagedResourceMode:
    57  		return n.evalTreeManagedResource(
    58  			stateId, info, resource, stateDeps,
    59  		)
    60  	case config.DataResourceMode:
    61  		return n.evalTreeDataResource(
    62  			stateId, info, resource, stateDeps)
    63  	default:
    64  		panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode))
    65  	}
    66  }
    67  
    68  func (n *NodeApplyableResource) evalTreeDataResource(
    69  	stateId string, info *InstanceInfo,
    70  	resource *Resource, stateDeps []string) EvalNode {
    71  	var provider ResourceProvider
    72  	var config *ResourceConfig
    73  	var diff *InstanceDiff
    74  	var state *InstanceState
    75  
    76  	return &EvalSequence{
    77  		Nodes: []EvalNode{
    78  			// Get the saved diff for apply
    79  			&EvalReadDiff{
    80  				Name: stateId,
    81  				Diff: &diff,
    82  			},
    83  
    84  			// Stop here if we don't actually have a diff
    85  			&EvalIf{
    86  				If: func(ctx EvalContext) (bool, error) {
    87  					if diff == nil {
    88  						return true, EvalEarlyExitError{}
    89  					}
    90  
    91  					if diff.GetAttributesLen() == 0 {
    92  						return true, EvalEarlyExitError{}
    93  					}
    94  
    95  					return true, nil
    96  				},
    97  				Then: EvalNoop{},
    98  			},
    99  
   100  			// We need to re-interpolate the config here, rather than
   101  			// just using the diff's values directly, because we've
   102  			// potentially learned more variable values during the
   103  			// apply pass that weren't known when the diff was produced.
   104  			&EvalInterpolate{
   105  				Config:   n.Config.RawConfig.Copy(),
   106  				Resource: resource,
   107  				Output:   &config,
   108  			},
   109  
   110  			&EvalGetProvider{
   111  				Name:   n.ProvidedBy()[0],
   112  				Output: &provider,
   113  			},
   114  
   115  			// Make a new diff with our newly-interpolated config.
   116  			&EvalReadDataDiff{
   117  				Info:     info,
   118  				Config:   &config,
   119  				Previous: &diff,
   120  				Provider: &provider,
   121  				Output:   &diff,
   122  			},
   123  
   124  			&EvalReadDataApply{
   125  				Info:     info,
   126  				Diff:     &diff,
   127  				Provider: &provider,
   128  				Output:   &state,
   129  			},
   130  
   131  			&EvalWriteState{
   132  				Name:         stateId,
   133  				ResourceType: n.Config.Type,
   134  				Provider:     n.Config.Provider,
   135  				Dependencies: stateDeps,
   136  				State:        &state,
   137  			},
   138  
   139  			// Clear the diff now that we've applied it, so
   140  			// later nodes won't see a diff that's now a no-op.
   141  			&EvalWriteDiff{
   142  				Name: stateId,
   143  				Diff: nil,
   144  			},
   145  
   146  			&EvalUpdateStateHook{},
   147  		},
   148  	}
   149  }
   150  
   151  func (n *NodeApplyableResource) evalTreeManagedResource(
   152  	stateId string, info *InstanceInfo,
   153  	resource *Resource, stateDeps []string) EvalNode {
   154  	// Declare a bunch of variables that are used for state during
   155  	// evaluation. Most of this are written to by-address below.
   156  	var provider ResourceProvider
   157  	var diff, diffApply *InstanceDiff
   158  	var state *InstanceState
   159  	var resourceConfig *ResourceConfig
   160  	var err error
   161  	var createNew bool
   162  	var createBeforeDestroyEnabled bool
   163  
   164  	return &EvalSequence{
   165  		Nodes: []EvalNode{
   166  			// Build the instance info
   167  			&EvalInstanceInfo{
   168  				Info: info,
   169  			},
   170  
   171  			// Get the saved diff for apply
   172  			&EvalReadDiff{
   173  				Name: stateId,
   174  				Diff: &diffApply,
   175  			},
   176  
   177  			// We don't want to do any destroys
   178  			&EvalIf{
   179  				If: func(ctx EvalContext) (bool, error) {
   180  					if diffApply == nil {
   181  						return true, EvalEarlyExitError{}
   182  					}
   183  
   184  					if diffApply.GetDestroy() && diffApply.GetAttributesLen() == 0 {
   185  						return true, EvalEarlyExitError{}
   186  					}
   187  
   188  					diffApply.SetDestroy(false)
   189  					return true, nil
   190  				},
   191  				Then: EvalNoop{},
   192  			},
   193  
   194  			&EvalIf{
   195  				If: func(ctx EvalContext) (bool, error) {
   196  					destroy := false
   197  					if diffApply != nil {
   198  						destroy = diffApply.GetDestroy() || diffApply.RequiresNew()
   199  					}
   200  
   201  					createBeforeDestroyEnabled =
   202  						n.Config.Lifecycle.CreateBeforeDestroy &&
   203  							destroy
   204  
   205  					return createBeforeDestroyEnabled, nil
   206  				},
   207  				Then: &EvalDeposeState{
   208  					Name: stateId,
   209  				},
   210  			},
   211  
   212  			&EvalInterpolate{
   213  				Config:   n.Config.RawConfig.Copy(),
   214  				Resource: resource,
   215  				Output:   &resourceConfig,
   216  			},
   217  			&EvalGetProvider{
   218  				Name:   n.ProvidedBy()[0],
   219  				Output: &provider,
   220  			},
   221  			&EvalReadState{
   222  				Name:   stateId,
   223  				Output: &state,
   224  			},
   225  			// Re-run validation to catch any errors we missed, e.g. type
   226  			// mismatches on computed values.
   227  			&EvalValidateResource{
   228  				Provider:       &provider,
   229  				Config:         &resourceConfig,
   230  				ResourceName:   n.Config.Name,
   231  				ResourceType:   n.Config.Type,
   232  				ResourceMode:   n.Config.Mode,
   233  				IgnoreWarnings: true,
   234  			},
   235  			&EvalDiff{
   236  				Info:       info,
   237  				Config:     &resourceConfig,
   238  				Resource:   n.Config,
   239  				Provider:   &provider,
   240  				Diff:       &diffApply,
   241  				State:      &state,
   242  				OutputDiff: &diffApply,
   243  			},
   244  
   245  			// Get the saved diff
   246  			&EvalReadDiff{
   247  				Name: stateId,
   248  				Diff: &diff,
   249  			},
   250  
   251  			// Compare the diffs
   252  			&EvalCompareDiff{
   253  				Info: info,
   254  				One:  &diff,
   255  				Two:  &diffApply,
   256  			},
   257  
   258  			&EvalGetProvider{
   259  				Name:   n.ProvidedBy()[0],
   260  				Output: &provider,
   261  			},
   262  			&EvalReadState{
   263  				Name:   stateId,
   264  				Output: &state,
   265  			},
   266  			&EvalApply{
   267  				Info:      info,
   268  				State:     &state,
   269  				Diff:      &diffApply,
   270  				Provider:  &provider,
   271  				Output:    &state,
   272  				Error:     &err,
   273  				CreateNew: &createNew,
   274  			},
   275  			&EvalWriteState{
   276  				Name:         stateId,
   277  				ResourceType: n.Config.Type,
   278  				Provider:     n.Config.Provider,
   279  				Dependencies: stateDeps,
   280  				State:        &state,
   281  			},
   282  			&EvalApplyProvisioners{
   283  				Info:           info,
   284  				State:          &state,
   285  				Resource:       n.Config,
   286  				InterpResource: resource,
   287  				CreateNew:      &createNew,
   288  				Error:          &err,
   289  			},
   290  			&EvalIf{
   291  				If: func(ctx EvalContext) (bool, error) {
   292  					return createBeforeDestroyEnabled && err != nil, nil
   293  				},
   294  				Then: &EvalUndeposeState{
   295  					Name:  stateId,
   296  					State: &state,
   297  				},
   298  				Else: &EvalWriteState{
   299  					Name:         stateId,
   300  					ResourceType: n.Config.Type,
   301  					Provider:     n.Config.Provider,
   302  					Dependencies: stateDeps,
   303  					State:        &state,
   304  				},
   305  			},
   306  
   307  			// We clear the diff out here so that future nodes
   308  			// don't see a diff that is already complete. There
   309  			// is no longer a diff!
   310  			&EvalWriteDiff{
   311  				Name: stateId,
   312  				Diff: nil,
   313  			},
   314  
   315  			&EvalApplyPost{
   316  				Info:  info,
   317  				State: &state,
   318  				Error: &err,
   319  			},
   320  			&EvalUpdateStateHook{},
   321  		},
   322  	}
   323  }