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