github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/terraform/eval_diff.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  )
     7  
     8  // EvalCompareDiff is an EvalNode implementation that compares two diffs
     9  // and errors if the diffs are not equal.
    10  type EvalCompareDiff struct {
    11  	Info     *InstanceInfo
    12  	One, Two **InstanceDiff
    13  }
    14  
    15  // TODO: test
    16  func (n *EvalCompareDiff) Eval(ctx EvalContext) (interface{}, error) {
    17  	one, two := *n.One, *n.Two
    18  
    19  	// If either are nil, let them be empty
    20  	if one == nil {
    21  		one = new(InstanceDiff)
    22  		one.init()
    23  	}
    24  	if two == nil {
    25  		two = new(InstanceDiff)
    26  		two.init()
    27  	}
    28  	oneId := one.Attributes["id"]
    29  	twoId := two.Attributes["id"]
    30  	delete(one.Attributes, "id")
    31  	delete(two.Attributes, "id")
    32  	defer func() {
    33  		if oneId != nil {
    34  			one.Attributes["id"] = oneId
    35  		}
    36  		if twoId != nil {
    37  			two.Attributes["id"] = twoId
    38  		}
    39  	}()
    40  
    41  	if same, reason := one.Same(two); !same {
    42  		log.Printf("[ERROR] %s: diffs didn't match", n.Info.Id)
    43  		log.Printf("[ERROR] %s: reason: %s", n.Info.Id, reason)
    44  		log.Printf("[ERROR] %s: diff one: %#v", n.Info.Id, one)
    45  		log.Printf("[ERROR] %s: diff two: %#v", n.Info.Id, two)
    46  		return nil, fmt.Errorf(
    47  			"%s: diffs didn't match during apply. This is a bug with "+
    48  				"Terraform and should be reported.", n.Info.Id)
    49  	}
    50  
    51  	return nil, nil
    52  }
    53  
    54  // EvalDiff is an EvalNode implementation that does a refresh for
    55  // a resource.
    56  type EvalDiff struct {
    57  	Info        *InstanceInfo
    58  	Config      **ResourceConfig
    59  	Provider    *ResourceProvider
    60  	State       **InstanceState
    61  	Output      **InstanceDiff
    62  	OutputState **InstanceState
    63  }
    64  
    65  // TODO: test
    66  func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
    67  	state := *n.State
    68  	config := *n.Config
    69  	provider := *n.Provider
    70  
    71  	// Call pre-diff hook
    72  	err := ctx.Hook(func(h Hook) (HookAction, error) {
    73  		return h.PreDiff(n.Info, state)
    74  	})
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	// The state for the diff must never be nil
    80  	diffState := state
    81  	if diffState == nil {
    82  		diffState = new(InstanceState)
    83  	}
    84  	diffState.init()
    85  
    86  	// Diff!
    87  	diff, err := provider.Diff(n.Info, diffState, config)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	if diff == nil {
    92  		diff = new(InstanceDiff)
    93  	}
    94  
    95  	// Require a destroy if there is no ID and it requires new.
    96  	if diff.RequiresNew() && state != nil && state.ID != "" {
    97  		diff.Destroy = true
    98  	}
    99  
   100  	// If we're creating a new resource, compute its ID
   101  	if diff.RequiresNew() || state == nil || state.ID == "" {
   102  		var oldID string
   103  		if state != nil {
   104  			oldID = state.Attributes["id"]
   105  		}
   106  
   107  		// Add diff to compute new ID
   108  		diff.init()
   109  		diff.Attributes["id"] = &ResourceAttrDiff{
   110  			Old:         oldID,
   111  			NewComputed: true,
   112  			RequiresNew: true,
   113  			Type:        DiffAttrOutput,
   114  		}
   115  	}
   116  
   117  	// Call post-refresh hook
   118  	err = ctx.Hook(func(h Hook) (HookAction, error) {
   119  		return h.PostDiff(n.Info, diff)
   120  	})
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	// Update our output
   126  	*n.Output = diff
   127  
   128  	// Update the state if we care
   129  	if n.OutputState != nil {
   130  		*n.OutputState = state
   131  
   132  		// Merge our state so that the state is updated with our plan
   133  		if !diff.Empty() && n.OutputState != nil {
   134  			*n.OutputState = state.MergeDiff(diff)
   135  		}
   136  	}
   137  
   138  	return nil, nil
   139  }
   140  
   141  // EvalDiffDestroy is an EvalNode implementation that returns a plain
   142  // destroy diff.
   143  type EvalDiffDestroy struct {
   144  	Info   *InstanceInfo
   145  	State  **InstanceState
   146  	Output **InstanceDiff
   147  }
   148  
   149  // TODO: test
   150  func (n *EvalDiffDestroy) Eval(ctx EvalContext) (interface{}, error) {
   151  	state := *n.State
   152  
   153  	// If there is no state or we don't have an ID, we're already destroyed
   154  	if state == nil || state.ID == "" {
   155  		return nil, nil
   156  	}
   157  
   158  	// Call pre-diff hook
   159  	err := ctx.Hook(func(h Hook) (HookAction, error) {
   160  		return h.PreDiff(n.Info, state)
   161  	})
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	// The diff
   167  	diff := &InstanceDiff{Destroy: true}
   168  
   169  	// Call post-diff hook
   170  	err = ctx.Hook(func(h Hook) (HookAction, error) {
   171  		return h.PostDiff(n.Info, diff)
   172  	})
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	// Update our output
   178  	*n.Output = diff
   179  
   180  	return nil, nil
   181  }
   182  
   183  // EvalDiffDestroyModule is an EvalNode implementation that writes the diff to
   184  // the full diff.
   185  type EvalDiffDestroyModule struct {
   186  	Path []string
   187  }
   188  
   189  // TODO: test
   190  func (n *EvalDiffDestroyModule) Eval(ctx EvalContext) (interface{}, error) {
   191  	diff, lock := ctx.Diff()
   192  
   193  	// Acquire the lock so that we can do this safely concurrently
   194  	lock.Lock()
   195  	defer lock.Unlock()
   196  
   197  	// Write the diff
   198  	modDiff := diff.ModuleByPath(n.Path)
   199  	if modDiff == nil {
   200  		modDiff = diff.AddModule(n.Path)
   201  	}
   202  	modDiff.Destroy = true
   203  
   204  	return nil, nil
   205  }
   206  
   207  // EvalDiffTainted is an EvalNode implementation that writes the diff to
   208  // the full diff.
   209  type EvalDiffTainted struct {
   210  	Name string
   211  	Diff **InstanceDiff
   212  }
   213  
   214  // TODO: test
   215  func (n *EvalDiffTainted) Eval(ctx EvalContext) (interface{}, error) {
   216  	state, lock := ctx.State()
   217  
   218  	// Get a read lock so we can access this instance
   219  	lock.RLock()
   220  	defer lock.RUnlock()
   221  
   222  	// Look for the module state. If we don't have one, then it doesn't matter.
   223  	mod := state.ModuleByPath(ctx.Path())
   224  	if mod == nil {
   225  		return nil, nil
   226  	}
   227  
   228  	// Look for the resource state. If we don't have one, then it is okay.
   229  	rs := mod.Resources[n.Name]
   230  	if rs == nil {
   231  		return nil, nil
   232  	}
   233  
   234  	// If we have tainted, then mark it on the diff
   235  	if len(rs.Tainted) > 0 {
   236  		(*n.Diff).DestroyTainted = true
   237  	}
   238  
   239  	return nil, nil
   240  }
   241  
   242  // EvalFilterDiff is an EvalNode implementation that filters the diff
   243  // according to some filter.
   244  type EvalFilterDiff struct {
   245  	// Input and output
   246  	Diff   **InstanceDiff
   247  	Output **InstanceDiff
   248  
   249  	// Destroy, if true, will only include a destroy diff if it is set.
   250  	Destroy bool
   251  }
   252  
   253  func (n *EvalFilterDiff) Eval(ctx EvalContext) (interface{}, error) {
   254  	if *n.Diff == nil {
   255  		return nil, nil
   256  	}
   257  
   258  	input := *n.Diff
   259  	result := new(InstanceDiff)
   260  
   261  	if n.Destroy {
   262  		if input.Destroy || input.RequiresNew() {
   263  			result.Destroy = true
   264  		}
   265  	}
   266  
   267  	if n.Output != nil {
   268  		*n.Output = result
   269  	}
   270  
   271  	return nil, nil
   272  }
   273  
   274  // EvalReadDiff is an EvalNode implementation that writes the diff to
   275  // the full diff.
   276  type EvalReadDiff struct {
   277  	Name string
   278  	Diff **InstanceDiff
   279  }
   280  
   281  func (n *EvalReadDiff) Eval(ctx EvalContext) (interface{}, error) {
   282  	diff, lock := ctx.Diff()
   283  
   284  	// Acquire the lock so that we can do this safely concurrently
   285  	lock.Lock()
   286  	defer lock.Unlock()
   287  
   288  	// Write the diff
   289  	modDiff := diff.ModuleByPath(ctx.Path())
   290  	if modDiff == nil {
   291  		return nil, nil
   292  	}
   293  
   294  	*n.Diff = modDiff.Resources[n.Name]
   295  
   296  	return nil, nil
   297  }
   298  
   299  // EvalWriteDiff is an EvalNode implementation that writes the diff to
   300  // the full diff.
   301  type EvalWriteDiff struct {
   302  	Name string
   303  	Diff **InstanceDiff
   304  }
   305  
   306  // TODO: test
   307  func (n *EvalWriteDiff) Eval(ctx EvalContext) (interface{}, error) {
   308  	diff, lock := ctx.Diff()
   309  
   310  	// The diff to write, if its empty it should write nil
   311  	var diffVal *InstanceDiff
   312  	if n.Diff != nil {
   313  		diffVal = *n.Diff
   314  	}
   315  	if diffVal.Empty() {
   316  		diffVal = nil
   317  	}
   318  
   319  	// Acquire the lock so that we can do this safely concurrently
   320  	lock.Lock()
   321  	defer lock.Unlock()
   322  
   323  	// Write the diff
   324  	modDiff := diff.ModuleByPath(ctx.Path())
   325  	if modDiff == nil {
   326  		modDiff = diff.AddModule(ctx.Path())
   327  	}
   328  	if diffVal != nil {
   329  		modDiff.Resources[n.Name] = diffVal
   330  	} else {
   331  		delete(modDiff.Resources, n.Name)
   332  	}
   333  
   334  	return nil, nil
   335  }