github.com/kjmkznr/terraform@v0.5.2-0.20180216194316-1d0f5fdac99e/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  // GraphNodeReferencer, overriding NodeAbstractResource
    21  func (n *NodeApplyableResource) References() []string {
    22  	result := n.NodeAbstractResource.References()
    23  
    24  	// The "apply" side of a resource generally also depends on the
    25  	// destruction of its dependencies as well. For example, if a LB
    26  	// references a set of VMs with ${vm.foo.*.id}, then we must wait for
    27  	// the destruction so we get the newly updated list of VMs.
    28  	//
    29  	// The exception here is CBD. When CBD is set, we don't do this since
    30  	// it would create a cycle. By not creating a cycle, we require two
    31  	// applies since the first apply the creation step will use the OLD
    32  	// values (pre-destroy) and the second step will update.
    33  	//
    34  	// This is how Terraform behaved with "legacy" graphs (TF <= 0.7.x).
    35  	// We mimic that behavior here now and can improve upon it in the future.
    36  	//
    37  	// This behavior is tested in graph_build_apply_test.go to test ordering.
    38  	cbd := n.Config != nil && n.Config.Lifecycle.CreateBeforeDestroy
    39  	if !cbd {
    40  		// The "apply" side of a resource always depends on the destruction
    41  		// of all its dependencies in addition to the creation.
    42  		for _, v := range result {
    43  			result = append(result, v+".destroy")
    44  		}
    45  	}
    46  
    47  	return result
    48  }
    49  
    50  // GraphNodeEvalable
    51  func (n *NodeApplyableResource) EvalTree() EvalNode {
    52  	addr := n.NodeAbstractResource.Addr
    53  
    54  	// stateId is the ID to put into the state
    55  	stateId := addr.stateId()
    56  
    57  	// Build the instance info. More of this will be populated during eval
    58  	info := &InstanceInfo{
    59  		Id:   stateId,
    60  		Type: addr.Type,
    61  	}
    62  
    63  	// Build the resource for eval
    64  	resource := &Resource{
    65  		Name:       addr.Name,
    66  		Type:       addr.Type,
    67  		CountIndex: addr.Index,
    68  	}
    69  	if resource.CountIndex < 0 {
    70  		resource.CountIndex = 0
    71  	}
    72  
    73  	// Determine the dependencies for the state.
    74  	stateDeps := n.StateReferences()
    75  
    76  	// Eval info is different depending on what kind of resource this is
    77  	switch n.Config.Mode {
    78  	case config.ManagedResourceMode:
    79  		return n.evalTreeManagedResource(
    80  			stateId, info, resource, stateDeps,
    81  		)
    82  	case config.DataResourceMode:
    83  		return n.evalTreeDataResource(
    84  			stateId, info, resource, stateDeps)
    85  	default:
    86  		panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode))
    87  	}
    88  }
    89  
    90  func (n *NodeApplyableResource) evalTreeDataResource(
    91  	stateId string, info *InstanceInfo,
    92  	resource *Resource, stateDeps []string) EvalNode {
    93  	var provider ResourceProvider
    94  	var config *ResourceConfig
    95  	var diff *InstanceDiff
    96  	var state *InstanceState
    97  
    98  	return &EvalSequence{
    99  		Nodes: []EvalNode{
   100  			// Build the instance info
   101  			&EvalInstanceInfo{
   102  				Info: info,
   103  			},
   104  
   105  			// Get the saved diff for apply
   106  			&EvalReadDiff{
   107  				Name: stateId,
   108  				Diff: &diff,
   109  			},
   110  
   111  			// Stop here if we don't actually have a diff
   112  			&EvalIf{
   113  				If: func(ctx EvalContext) (bool, error) {
   114  					if diff == nil {
   115  						return true, EvalEarlyExitError{}
   116  					}
   117  
   118  					if diff.GetAttributesLen() == 0 {
   119  						return true, EvalEarlyExitError{}
   120  					}
   121  
   122  					return true, nil
   123  				},
   124  				Then: EvalNoop{},
   125  			},
   126  
   127  			// Normally we interpolate count as a preparation step before
   128  			// a DynamicExpand, but an apply graph has pre-expanded nodes
   129  			// and so the count would otherwise never be interpolated.
   130  			//
   131  			// This is redundant when there are multiple instances created
   132  			// from the same config (count > 1) but harmless since the
   133  			// underlying structures have mutexes to make this concurrency-safe.
   134  			//
   135  			// In most cases this isn't actually needed because we dealt with
   136  			// all of the counts during the plan walk, but we do it here
   137  			// for completeness because other code assumes that the
   138  			// final count is always available during interpolation.
   139  			//
   140  			// Here we are just populating the interpolated value in-place
   141  			// inside this RawConfig object, like we would in
   142  			// NodeAbstractCountResource.
   143  			&EvalInterpolate{Config: n.Config.RawCount},
   144  
   145  			// We need to re-interpolate the config here, rather than
   146  			// just using the diff's values directly, because we've
   147  			// potentially learned more variable values during the
   148  			// apply pass that weren't known when the diff was produced.
   149  			&EvalInterpolate{
   150  				Config:   n.Config.RawConfig.Copy(),
   151  				Resource: resource,
   152  				Output:   &config,
   153  			},
   154  
   155  			&EvalGetProvider{
   156  				Name:   n.ResolvedProvider,
   157  				Output: &provider,
   158  			},
   159  
   160  			// Make a new diff with our newly-interpolated config.
   161  			&EvalReadDataDiff{
   162  				Info:     info,
   163  				Config:   &config,
   164  				Previous: &diff,
   165  				Provider: &provider,
   166  				Output:   &diff,
   167  			},
   168  
   169  			&EvalReadDataApply{
   170  				Info:     info,
   171  				Diff:     &diff,
   172  				Provider: &provider,
   173  				Output:   &state,
   174  			},
   175  
   176  			&EvalWriteState{
   177  				Name:         stateId,
   178  				ResourceType: n.Config.Type,
   179  				Provider:     n.ResolvedProvider,
   180  				Dependencies: stateDeps,
   181  				State:        &state,
   182  			},
   183  
   184  			// Clear the diff now that we've applied it, so
   185  			// later nodes won't see a diff that's now a no-op.
   186  			&EvalWriteDiff{
   187  				Name: stateId,
   188  				Diff: nil,
   189  			},
   190  
   191  			&EvalUpdateStateHook{},
   192  		},
   193  	}
   194  }
   195  
   196  func (n *NodeApplyableResource) evalTreeManagedResource(
   197  	stateId string, info *InstanceInfo,
   198  	resource *Resource, stateDeps []string) EvalNode {
   199  	// Declare a bunch of variables that are used for state during
   200  	// evaluation. Most of this are written to by-address below.
   201  	var provider ResourceProvider
   202  	var diff, diffApply *InstanceDiff
   203  	var state *InstanceState
   204  	var resourceConfig *ResourceConfig
   205  	var err error
   206  	var createNew bool
   207  	var createBeforeDestroyEnabled bool
   208  
   209  	return &EvalSequence{
   210  		Nodes: []EvalNode{
   211  			// Build the instance info
   212  			&EvalInstanceInfo{
   213  				Info: info,
   214  			},
   215  
   216  			// Get the saved diff for apply
   217  			&EvalReadDiff{
   218  				Name: stateId,
   219  				Diff: &diffApply,
   220  			},
   221  
   222  			// We don't want to do any destroys
   223  			&EvalIf{
   224  				If: func(ctx EvalContext) (bool, error) {
   225  					if diffApply == nil {
   226  						return true, EvalEarlyExitError{}
   227  					}
   228  
   229  					if diffApply.GetDestroy() && diffApply.GetAttributesLen() == 0 {
   230  						return true, EvalEarlyExitError{}
   231  					}
   232  
   233  					diffApply.SetDestroy(false)
   234  					return true, nil
   235  				},
   236  				Then: EvalNoop{},
   237  			},
   238  
   239  			&EvalIf{
   240  				If: func(ctx EvalContext) (bool, error) {
   241  					destroy := false
   242  					if diffApply != nil {
   243  						destroy = diffApply.GetDestroy() || diffApply.RequiresNew()
   244  					}
   245  
   246  					createBeforeDestroyEnabled =
   247  						n.Config.Lifecycle.CreateBeforeDestroy &&
   248  							destroy
   249  
   250  					return createBeforeDestroyEnabled, nil
   251  				},
   252  				Then: &EvalDeposeState{
   253  					Name: stateId,
   254  				},
   255  			},
   256  
   257  			// Normally we interpolate count as a preparation step before
   258  			// a DynamicExpand, but an apply graph has pre-expanded nodes
   259  			// and so the count would otherwise never be interpolated.
   260  			//
   261  			// This is redundant when there are multiple instances created
   262  			// from the same config (count > 1) but harmless since the
   263  			// underlying structures have mutexes to make this concurrency-safe.
   264  			//
   265  			// In most cases this isn't actually needed because we dealt with
   266  			// all of the counts during the plan walk, but we need to do this
   267  			// in order to support interpolation of resource counts from
   268  			// apply-time-interpolated expressions, such as those in
   269  			// "provisioner" blocks.
   270  			//
   271  			// Here we are just populating the interpolated value in-place
   272  			// inside this RawConfig object, like we would in
   273  			// NodeAbstractCountResource.
   274  			&EvalInterpolate{Config: n.Config.RawCount},
   275  
   276  			&EvalInterpolate{
   277  				Config:   n.Config.RawConfig.Copy(),
   278  				Resource: resource,
   279  				Output:   &resourceConfig,
   280  			},
   281  			&EvalGetProvider{
   282  				Name:   n.ResolvedProvider,
   283  				Output: &provider,
   284  			},
   285  			&EvalReadState{
   286  				Name:   stateId,
   287  				Output: &state,
   288  			},
   289  			// Re-run validation to catch any errors we missed, e.g. type
   290  			// mismatches on computed values.
   291  			&EvalValidateResource{
   292  				Provider:       &provider,
   293  				Config:         &resourceConfig,
   294  				ResourceName:   n.Config.Name,
   295  				ResourceType:   n.Config.Type,
   296  				ResourceMode:   n.Config.Mode,
   297  				IgnoreWarnings: true,
   298  			},
   299  			&EvalDiff{
   300  				Info:       info,
   301  				Config:     &resourceConfig,
   302  				Resource:   n.Config,
   303  				Provider:   &provider,
   304  				Diff:       &diffApply,
   305  				State:      &state,
   306  				OutputDiff: &diffApply,
   307  			},
   308  
   309  			// Get the saved diff
   310  			&EvalReadDiff{
   311  				Name: stateId,
   312  				Diff: &diff,
   313  			},
   314  
   315  			// Compare the diffs
   316  			&EvalCompareDiff{
   317  				Info: info,
   318  				One:  &diff,
   319  				Two:  &diffApply,
   320  			},
   321  
   322  			&EvalGetProvider{
   323  				Name:   n.ResolvedProvider,
   324  				Output: &provider,
   325  			},
   326  			&EvalReadState{
   327  				Name:   stateId,
   328  				Output: &state,
   329  			},
   330  			// Call pre-apply hook
   331  			&EvalApplyPre{
   332  				Info:  info,
   333  				State: &state,
   334  				Diff:  &diffApply,
   335  			},
   336  			&EvalApply{
   337  				Info:      info,
   338  				State:     &state,
   339  				Diff:      &diffApply,
   340  				Provider:  &provider,
   341  				Output:    &state,
   342  				Error:     &err,
   343  				CreateNew: &createNew,
   344  			},
   345  			&EvalWriteState{
   346  				Name:         stateId,
   347  				ResourceType: n.Config.Type,
   348  				Provider:     n.ResolvedProvider,
   349  				Dependencies: stateDeps,
   350  				State:        &state,
   351  			},
   352  			&EvalApplyProvisioners{
   353  				Info:           info,
   354  				State:          &state,
   355  				Resource:       n.Config,
   356  				InterpResource: resource,
   357  				CreateNew:      &createNew,
   358  				Error:          &err,
   359  				When:           config.ProvisionerWhenCreate,
   360  			},
   361  			&EvalIf{
   362  				If: func(ctx EvalContext) (bool, error) {
   363  					return createBeforeDestroyEnabled && err != nil, nil
   364  				},
   365  				Then: &EvalUndeposeState{
   366  					Name:  stateId,
   367  					State: &state,
   368  				},
   369  				Else: &EvalWriteState{
   370  					Name:         stateId,
   371  					ResourceType: n.Config.Type,
   372  					Provider:     n.ResolvedProvider,
   373  					Dependencies: stateDeps,
   374  					State:        &state,
   375  				},
   376  			},
   377  
   378  			// We clear the diff out here so that future nodes
   379  			// don't see a diff that is already complete. There
   380  			// is no longer a diff!
   381  			&EvalWriteDiff{
   382  				Name: stateId,
   383  				Diff: nil,
   384  			},
   385  
   386  			&EvalApplyPost{
   387  				Info:  info,
   388  				State: &state,
   389  				Error: &err,
   390  			},
   391  			&EvalUpdateStateHook{},
   392  		},
   393  	}
   394  }