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