github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/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  	Diff        **InstanceDiff
    73  	State       **InstanceState
    74  	OutputDiff  **InstanceDiff
    75  	OutputState **InstanceState
    76  }
    77  
    78  // TODO: test
    79  func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
    80  	state := *n.State
    81  	config := *n.Config
    82  	provider := *n.Provider
    83  
    84  	// Call pre-diff hook
    85  	err := ctx.Hook(func(h Hook) (HookAction, error) {
    86  		return h.PreDiff(n.Info, state)
    87  	})
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  
    92  	// The state for the diff must never be nil
    93  	diffState := state
    94  	if diffState == nil {
    95  		diffState = new(InstanceState)
    96  	}
    97  	diffState.init()
    98  
    99  	// Diff!
   100  	diff, err := provider.Diff(n.Info, diffState, config)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	if diff == nil {
   105  		diff = new(InstanceDiff)
   106  	}
   107  
   108  	// Preserve the DestroyTainted flag
   109  	if n.Diff != nil {
   110  		diff.DestroyTainted = (*n.Diff).DestroyTainted
   111  	}
   112  
   113  	// Require a destroy if there is an ID and it requires new.
   114  	if diff.RequiresNew() && state != nil && state.ID != "" {
   115  		diff.Destroy = true
   116  	}
   117  
   118  	// If we're creating a new resource, compute its ID
   119  	if diff.RequiresNew() || state == nil || state.ID == "" {
   120  		var oldID string
   121  		if state != nil {
   122  			oldID = state.Attributes["id"]
   123  		}
   124  
   125  		// Add diff to compute new ID
   126  		diff.init()
   127  		diff.Attributes["id"] = &ResourceAttrDiff{
   128  			Old:         oldID,
   129  			NewComputed: true,
   130  			RequiresNew: true,
   131  			Type:        DiffAttrOutput,
   132  		}
   133  	}
   134  
   135  	// Call post-refresh hook
   136  	err = ctx.Hook(func(h Hook) (HookAction, error) {
   137  		return h.PostDiff(n.Info, diff)
   138  	})
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	// Update our output
   144  	*n.OutputDiff = diff
   145  
   146  	// Update the state if we care
   147  	if n.OutputState != nil {
   148  		*n.OutputState = state
   149  
   150  		// Merge our state so that the state is updated with our plan
   151  		if !diff.Empty() && n.OutputState != nil {
   152  			*n.OutputState = state.MergeDiff(diff)
   153  		}
   154  	}
   155  
   156  	return nil, nil
   157  }
   158  
   159  // EvalDiffDestroy is an EvalNode implementation that returns a plain
   160  // destroy diff.
   161  type EvalDiffDestroy struct {
   162  	Info   *InstanceInfo
   163  	State  **InstanceState
   164  	Output **InstanceDiff
   165  }
   166  
   167  // TODO: test
   168  func (n *EvalDiffDestroy) Eval(ctx EvalContext) (interface{}, error) {
   169  	state := *n.State
   170  
   171  	// If there is no state or we don't have an ID, we're already destroyed
   172  	if state == nil || state.ID == "" {
   173  		return nil, nil
   174  	}
   175  
   176  	// Call pre-diff hook
   177  	err := ctx.Hook(func(h Hook) (HookAction, error) {
   178  		return h.PreDiff(n.Info, state)
   179  	})
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  
   184  	// The diff
   185  	diff := &InstanceDiff{Destroy: true}
   186  
   187  	// Call post-diff hook
   188  	err = ctx.Hook(func(h Hook) (HookAction, error) {
   189  		return h.PostDiff(n.Info, diff)
   190  	})
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	// Update our output
   196  	*n.Output = diff
   197  
   198  	return nil, nil
   199  }
   200  
   201  // EvalDiffDestroyModule is an EvalNode implementation that writes the diff to
   202  // the full diff.
   203  type EvalDiffDestroyModule struct {
   204  	Path []string
   205  }
   206  
   207  // TODO: test
   208  func (n *EvalDiffDestroyModule) Eval(ctx EvalContext) (interface{}, error) {
   209  	diff, lock := ctx.Diff()
   210  
   211  	// Acquire the lock so that we can do this safely concurrently
   212  	lock.Lock()
   213  	defer lock.Unlock()
   214  
   215  	// Write the diff
   216  	modDiff := diff.ModuleByPath(n.Path)
   217  	if modDiff == nil {
   218  		modDiff = diff.AddModule(n.Path)
   219  	}
   220  	modDiff.Destroy = true
   221  
   222  	return nil, nil
   223  }
   224  
   225  // EvalFilterDiff is an EvalNode implementation that filters the diff
   226  // according to some filter.
   227  type EvalFilterDiff struct {
   228  	// Input and output
   229  	Diff   **InstanceDiff
   230  	Output **InstanceDiff
   231  
   232  	// Destroy, if true, will only include a destroy diff if it is set.
   233  	Destroy bool
   234  }
   235  
   236  func (n *EvalFilterDiff) Eval(ctx EvalContext) (interface{}, error) {
   237  	if *n.Diff == nil {
   238  		return nil, nil
   239  	}
   240  
   241  	input := *n.Diff
   242  	result := new(InstanceDiff)
   243  
   244  	if n.Destroy {
   245  		if input.Destroy || input.RequiresNew() {
   246  			result.Destroy = true
   247  		}
   248  	}
   249  
   250  	if n.Output != nil {
   251  		*n.Output = result
   252  	}
   253  
   254  	return nil, nil
   255  }
   256  
   257  // EvalReadDiff is an EvalNode implementation that writes the diff to
   258  // the full diff.
   259  type EvalReadDiff struct {
   260  	Name string
   261  	Diff **InstanceDiff
   262  }
   263  
   264  func (n *EvalReadDiff) Eval(ctx EvalContext) (interface{}, error) {
   265  	diff, lock := ctx.Diff()
   266  
   267  	// Acquire the lock so that we can do this safely concurrently
   268  	lock.Lock()
   269  	defer lock.Unlock()
   270  
   271  	// Write the diff
   272  	modDiff := diff.ModuleByPath(ctx.Path())
   273  	if modDiff == nil {
   274  		return nil, nil
   275  	}
   276  
   277  	*n.Diff = modDiff.Resources[n.Name]
   278  
   279  	return nil, nil
   280  }
   281  
   282  // EvalWriteDiff is an EvalNode implementation that writes the diff to
   283  // the full diff.
   284  type EvalWriteDiff struct {
   285  	Name string
   286  	Diff **InstanceDiff
   287  }
   288  
   289  // TODO: test
   290  func (n *EvalWriteDiff) Eval(ctx EvalContext) (interface{}, error) {
   291  	diff, lock := ctx.Diff()
   292  
   293  	// The diff to write, if its empty it should write nil
   294  	var diffVal *InstanceDiff
   295  	if n.Diff != nil {
   296  		diffVal = *n.Diff
   297  	}
   298  	if diffVal.Empty() {
   299  		diffVal = nil
   300  	}
   301  
   302  	// Acquire the lock so that we can do this safely concurrently
   303  	lock.Lock()
   304  	defer lock.Unlock()
   305  
   306  	// Write the diff
   307  	modDiff := diff.ModuleByPath(ctx.Path())
   308  	if modDiff == nil {
   309  		modDiff = diff.AddModule(ctx.Path())
   310  	}
   311  	if diffVal != nil {
   312  		modDiff.Resources[n.Name] = diffVal
   313  	} else {
   314  		delete(modDiff.Resources, n.Name)
   315  	}
   316  
   317  	return nil, nil
   318  }