github.com/rhenning/terraform@v0.8.0-beta2/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  			// Build the instance info
    79  			&EvalInstanceInfo{
    80  				Info: info,
    81  			},
    82  
    83  			// Get the saved diff for apply
    84  			&EvalReadDiff{
    85  				Name: stateId,
    86  				Diff: &diff,
    87  			},
    88  
    89  			// Stop here if we don't actually have a diff
    90  			&EvalIf{
    91  				If: func(ctx EvalContext) (bool, error) {
    92  					if diff == nil {
    93  						return true, EvalEarlyExitError{}
    94  					}
    95  
    96  					if diff.GetAttributesLen() == 0 {
    97  						return true, EvalEarlyExitError{}
    98  					}
    99  
   100  					return true, nil
   101  				},
   102  				Then: EvalNoop{},
   103  			},
   104  
   105  			// We need to re-interpolate the config here, rather than
   106  			// just using the diff's values directly, because we've
   107  			// potentially learned more variable values during the
   108  			// apply pass that weren't known when the diff was produced.
   109  			&EvalInterpolate{
   110  				Config:   n.Config.RawConfig.Copy(),
   111  				Resource: resource,
   112  				Output:   &config,
   113  			},
   114  
   115  			&EvalGetProvider{
   116  				Name:   n.ProvidedBy()[0],
   117  				Output: &provider,
   118  			},
   119  
   120  			// Make a new diff with our newly-interpolated config.
   121  			&EvalReadDataDiff{
   122  				Info:     info,
   123  				Config:   &config,
   124  				Previous: &diff,
   125  				Provider: &provider,
   126  				Output:   &diff,
   127  			},
   128  
   129  			&EvalReadDataApply{
   130  				Info:     info,
   131  				Diff:     &diff,
   132  				Provider: &provider,
   133  				Output:   &state,
   134  			},
   135  
   136  			&EvalWriteState{
   137  				Name:         stateId,
   138  				ResourceType: n.Config.Type,
   139  				Provider:     n.Config.Provider,
   140  				Dependencies: stateDeps,
   141  				State:        &state,
   142  			},
   143  
   144  			// Clear the diff now that we've applied it, so
   145  			// later nodes won't see a diff that's now a no-op.
   146  			&EvalWriteDiff{
   147  				Name: stateId,
   148  				Diff: nil,
   149  			},
   150  
   151  			&EvalUpdateStateHook{},
   152  		},
   153  	}
   154  }
   155  
   156  func (n *NodeApplyableResource) evalTreeManagedResource(
   157  	stateId string, info *InstanceInfo,
   158  	resource *Resource, stateDeps []string) EvalNode {
   159  	// Declare a bunch of variables that are used for state during
   160  	// evaluation. Most of this are written to by-address below.
   161  	var provider ResourceProvider
   162  	var diff, diffApply *InstanceDiff
   163  	var state *InstanceState
   164  	var resourceConfig *ResourceConfig
   165  	var err error
   166  	var createNew bool
   167  	var createBeforeDestroyEnabled bool
   168  
   169  	return &EvalSequence{
   170  		Nodes: []EvalNode{
   171  			// Build the instance info
   172  			&EvalInstanceInfo{
   173  				Info: info,
   174  			},
   175  
   176  			// Get the saved diff for apply
   177  			&EvalReadDiff{
   178  				Name: stateId,
   179  				Diff: &diffApply,
   180  			},
   181  
   182  			// We don't want to do any destroys
   183  			&EvalIf{
   184  				If: func(ctx EvalContext) (bool, error) {
   185  					if diffApply == nil {
   186  						return true, EvalEarlyExitError{}
   187  					}
   188  
   189  					if diffApply.GetDestroy() && diffApply.GetAttributesLen() == 0 {
   190  						return true, EvalEarlyExitError{}
   191  					}
   192  
   193  					diffApply.SetDestroy(false)
   194  					return true, nil
   195  				},
   196  				Then: EvalNoop{},
   197  			},
   198  
   199  			&EvalIf{
   200  				If: func(ctx EvalContext) (bool, error) {
   201  					destroy := false
   202  					if diffApply != nil {
   203  						destroy = diffApply.GetDestroy() || diffApply.RequiresNew()
   204  					}
   205  
   206  					createBeforeDestroyEnabled =
   207  						n.Config.Lifecycle.CreateBeforeDestroy &&
   208  							destroy
   209  
   210  					return createBeforeDestroyEnabled, nil
   211  				},
   212  				Then: &EvalDeposeState{
   213  					Name: stateId,
   214  				},
   215  			},
   216  
   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  			&EvalReadState{
   227  				Name:   stateId,
   228  				Output: &state,
   229  			},
   230  			// Re-run validation to catch any errors we missed, e.g. type
   231  			// mismatches on computed values.
   232  			&EvalValidateResource{
   233  				Provider:       &provider,
   234  				Config:         &resourceConfig,
   235  				ResourceName:   n.Config.Name,
   236  				ResourceType:   n.Config.Type,
   237  				ResourceMode:   n.Config.Mode,
   238  				IgnoreWarnings: true,
   239  			},
   240  			&EvalDiff{
   241  				Info:       info,
   242  				Config:     &resourceConfig,
   243  				Resource:   n.Config,
   244  				Provider:   &provider,
   245  				Diff:       &diffApply,
   246  				State:      &state,
   247  				OutputDiff: &diffApply,
   248  			},
   249  
   250  			// Get the saved diff
   251  			&EvalReadDiff{
   252  				Name: stateId,
   253  				Diff: &diff,
   254  			},
   255  
   256  			// Compare the diffs
   257  			&EvalCompareDiff{
   258  				Info: info,
   259  				One:  &diff,
   260  				Two:  &diffApply,
   261  			},
   262  
   263  			&EvalGetProvider{
   264  				Name:   n.ProvidedBy()[0],
   265  				Output: &provider,
   266  			},
   267  			&EvalReadState{
   268  				Name:   stateId,
   269  				Output: &state,
   270  			},
   271  			&EvalApply{
   272  				Info:      info,
   273  				State:     &state,
   274  				Diff:      &diffApply,
   275  				Provider:  &provider,
   276  				Output:    &state,
   277  				Error:     &err,
   278  				CreateNew: &createNew,
   279  			},
   280  			&EvalWriteState{
   281  				Name:         stateId,
   282  				ResourceType: n.Config.Type,
   283  				Provider:     n.Config.Provider,
   284  				Dependencies: stateDeps,
   285  				State:        &state,
   286  			},
   287  			&EvalApplyProvisioners{
   288  				Info:           info,
   289  				State:          &state,
   290  				Resource:       n.Config,
   291  				InterpResource: resource,
   292  				CreateNew:      &createNew,
   293  				Error:          &err,
   294  			},
   295  			&EvalIf{
   296  				If: func(ctx EvalContext) (bool, error) {
   297  					return createBeforeDestroyEnabled && err != nil, nil
   298  				},
   299  				Then: &EvalUndeposeState{
   300  					Name:  stateId,
   301  					State: &state,
   302  				},
   303  				Else: &EvalWriteState{
   304  					Name:         stateId,
   305  					ResourceType: n.Config.Type,
   306  					Provider:     n.Config.Provider,
   307  					Dependencies: stateDeps,
   308  					State:        &state,
   309  				},
   310  			},
   311  
   312  			// We clear the diff out here so that future nodes
   313  			// don't see a diff that is already complete. There
   314  			// is no longer a diff!
   315  			&EvalWriteDiff{
   316  				Name: stateId,
   317  				Diff: nil,
   318  			},
   319  
   320  			&EvalApplyPost{
   321  				Info:  info,
   322  				State: &state,
   323  				Error: &err,
   324  			},
   325  			&EvalUpdateStateHook{},
   326  		},
   327  	}
   328  }