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