github.com/icebourg/terraform@v0.6.5-0.20151015205227-263cc1b85535/terraform/eval_state.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  )
     6  
     7  // EvalReadState is an EvalNode implementation that reads the
     8  // primary InstanceState for a specific resource out of the state.
     9  type EvalReadState struct {
    10  	Name   string
    11  	Output **InstanceState
    12  }
    13  
    14  func (n *EvalReadState) Eval(ctx EvalContext) (interface{}, error) {
    15  	return readInstanceFromState(ctx, n.Name, n.Output, func(rs *ResourceState) (*InstanceState, error) {
    16  		return rs.Primary, nil
    17  	})
    18  }
    19  
    20  // EvalReadStateTainted is an EvalNode implementation that reads a
    21  // tainted InstanceState for a specific resource out of the state
    22  type EvalReadStateTainted struct {
    23  	Name   string
    24  	Output **InstanceState
    25  	// Index indicates which instance in the Tainted list to target, or -1 for
    26  	// the last item.
    27  	Index int
    28  }
    29  
    30  func (n *EvalReadStateTainted) Eval(ctx EvalContext) (interface{}, error) {
    31  	return readInstanceFromState(ctx, n.Name, n.Output, func(rs *ResourceState) (*InstanceState, error) {
    32  		// Get the index. If it is negative, then we get the last one
    33  		idx := n.Index
    34  		if idx < 0 {
    35  			idx = len(rs.Tainted) - 1
    36  		}
    37  		if idx >= 0 && idx < len(rs.Tainted) {
    38  			return rs.Tainted[idx], nil
    39  		} else {
    40  			return nil, fmt.Errorf("bad tainted index: %d, for resource: %#v", idx, rs)
    41  		}
    42  	})
    43  }
    44  
    45  // EvalReadStateDeposed is an EvalNode implementation that reads the
    46  // deposed InstanceState for a specific resource out of the state
    47  type EvalReadStateDeposed struct {
    48  	Name   string
    49  	Output **InstanceState
    50  	// Index indicates which instance in the Deposed list to target, or -1 for
    51  	// the last item.
    52  	Index int
    53  }
    54  
    55  func (n *EvalReadStateDeposed) Eval(ctx EvalContext) (interface{}, error) {
    56  	return readInstanceFromState(ctx, n.Name, n.Output, func(rs *ResourceState) (*InstanceState, error) {
    57  		// Get the index. If it is negative, then we get the last one
    58  		idx := n.Index
    59  		if idx < 0 {
    60  			idx = len(rs.Deposed) - 1
    61  		}
    62  		if idx >= 0 && idx < len(rs.Deposed) {
    63  			return rs.Deposed[idx], nil
    64  		} else {
    65  			return nil, fmt.Errorf("bad deposed index: %d, for resource: %#v", idx, rs)
    66  		}
    67  	})
    68  }
    69  
    70  // Does the bulk of the work for the various flavors of ReadState eval nodes.
    71  // Each node just provides a reader function to get from the ResourceState to the
    72  // InstanceState, and this takes care of all the plumbing.
    73  func readInstanceFromState(
    74  	ctx EvalContext,
    75  	resourceName string,
    76  	output **InstanceState,
    77  	readerFn func(*ResourceState) (*InstanceState, error),
    78  ) (*InstanceState, error) {
    79  	state, lock := ctx.State()
    80  
    81  	// Get a read lock so we can access this instance
    82  	lock.RLock()
    83  	defer lock.RUnlock()
    84  
    85  	// Look for the module state. If we don't have one, then it doesn't matter.
    86  	mod := state.ModuleByPath(ctx.Path())
    87  	if mod == nil {
    88  		return nil, nil
    89  	}
    90  
    91  	// Look for the resource state. If we don't have one, then it is okay.
    92  	rs := mod.Resources[resourceName]
    93  	if rs == nil {
    94  		return nil, nil
    95  	}
    96  
    97  	// Use the delegate function to get the instance state from the resource state
    98  	is, err := readerFn(rs)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	// Write the result to the output pointer
   104  	if output != nil {
   105  		*output = is
   106  	}
   107  
   108  	return is, nil
   109  }
   110  
   111  // EvalRequireState is an EvalNode implementation that early exits
   112  // if the state doesn't have an ID.
   113  type EvalRequireState struct {
   114  	State **InstanceState
   115  }
   116  
   117  func (n *EvalRequireState) Eval(ctx EvalContext) (interface{}, error) {
   118  	if n.State == nil {
   119  		return nil, EvalEarlyExitError{}
   120  	}
   121  
   122  	state := *n.State
   123  	if state == nil || state.ID == "" {
   124  		return nil, EvalEarlyExitError{}
   125  	}
   126  
   127  	return nil, nil
   128  }
   129  
   130  // EvalUpdateStateHook is an EvalNode implementation that calls the
   131  // PostStateUpdate hook with the current state.
   132  type EvalUpdateStateHook struct{}
   133  
   134  func (n *EvalUpdateStateHook) Eval(ctx EvalContext) (interface{}, error) {
   135  	state, lock := ctx.State()
   136  
   137  	// Get a read lock so it doesn't change while we're calling this
   138  	lock.RLock()
   139  	defer lock.RUnlock()
   140  
   141  	// Call the hook
   142  	err := ctx.Hook(func(h Hook) (HookAction, error) {
   143  		return h.PostStateUpdate(state)
   144  	})
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	return nil, nil
   150  }
   151  
   152  // EvalWriteState is an EvalNode implementation that writes the
   153  // primary InstanceState for a specific resource into the state.
   154  type EvalWriteState struct {
   155  	Name         string
   156  	ResourceType string
   157  	Provider     string
   158  	Dependencies []string
   159  	State        **InstanceState
   160  }
   161  
   162  func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) {
   163  	return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies,
   164  		func(rs *ResourceState) error {
   165  			rs.Primary = *n.State
   166  			return nil
   167  		},
   168  	)
   169  }
   170  
   171  // EvalWriteStateTainted is an EvalNode implementation that writes
   172  // an InstanceState out to the Tainted list of a resource in the state.
   173  type EvalWriteStateTainted struct {
   174  	Name         string
   175  	ResourceType string
   176  	Provider     string
   177  	Dependencies []string
   178  	State        **InstanceState
   179  	// Index indicates which instance in the Tainted list to target, or -1 to append.
   180  	Index int
   181  }
   182  
   183  // EvalWriteStateTainted is an EvalNode implementation that writes the
   184  // one of the tainted InstanceStates for a specific resource out of the state.
   185  func (n *EvalWriteStateTainted) Eval(ctx EvalContext) (interface{}, error) {
   186  	return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies,
   187  		func(rs *ResourceState) error {
   188  			if n.Index == -1 {
   189  				rs.Tainted = append(rs.Tainted, *n.State)
   190  			} else {
   191  				rs.Tainted[n.Index] = *n.State
   192  			}
   193  			return nil
   194  		},
   195  	)
   196  }
   197  
   198  // EvalWriteStateDeposed is an EvalNode implementation that writes
   199  // an InstanceState out to the Deposed list of a resource in the state.
   200  type EvalWriteStateDeposed struct {
   201  	Name         string
   202  	ResourceType string
   203  	Provider     string
   204  	Dependencies []string
   205  	State        **InstanceState
   206  	// Index indicates which instance in the Deposed list to target, or -1 to append.
   207  	Index int
   208  }
   209  
   210  func (n *EvalWriteStateDeposed) Eval(ctx EvalContext) (interface{}, error) {
   211  	return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies,
   212  		func(rs *ResourceState) error {
   213  			if n.Index == -1 {
   214  				rs.Deposed = append(rs.Deposed, *n.State)
   215  			} else {
   216  				rs.Deposed[n.Index] = *n.State
   217  			}
   218  			return nil
   219  		},
   220  	)
   221  }
   222  
   223  // Pulls together the common tasks of the EvalWriteState nodes.  All the args
   224  // are passed directly down from the EvalNode along with a `writer` function
   225  // which is yielded the *ResourceState and is responsible for writing an
   226  // InstanceState to the proper field in the ResourceState.
   227  func writeInstanceToState(
   228  	ctx EvalContext,
   229  	resourceName string,
   230  	resourceType string,
   231  	provider string,
   232  	dependencies []string,
   233  	writerFn func(*ResourceState) error,
   234  ) (*InstanceState, error) {
   235  	state, lock := ctx.State()
   236  	if state == nil {
   237  		return nil, fmt.Errorf("cannot write state to nil state")
   238  	}
   239  
   240  	// Get a write lock so we can access this instance
   241  	lock.Lock()
   242  	defer lock.Unlock()
   243  
   244  	// Look for the module state. If we don't have one, create it.
   245  	mod := state.ModuleByPath(ctx.Path())
   246  	if mod == nil {
   247  		mod = state.AddModule(ctx.Path())
   248  	}
   249  
   250  	// Look for the resource state.
   251  	rs := mod.Resources[resourceName]
   252  	if rs == nil {
   253  		rs = &ResourceState{}
   254  		rs.init()
   255  		mod.Resources[resourceName] = rs
   256  	}
   257  	rs.Type = resourceType
   258  	rs.Dependencies = dependencies
   259  	rs.Provider = provider
   260  
   261  	if err := writerFn(rs); err != nil {
   262  		return nil, err
   263  	}
   264  
   265  	return nil, nil
   266  }
   267  
   268  // EvalClearPrimaryState is an EvalNode implementation that clears the primary
   269  // instance from a resource state.
   270  type EvalClearPrimaryState struct {
   271  	Name string
   272  }
   273  
   274  func (n *EvalClearPrimaryState) Eval(ctx EvalContext) (interface{}, error) {
   275  	state, lock := ctx.State()
   276  
   277  	// Get a read lock so we can access this instance
   278  	lock.RLock()
   279  	defer lock.RUnlock()
   280  
   281  	// Look for the module state. If we don't have one, then it doesn't matter.
   282  	mod := state.ModuleByPath(ctx.Path())
   283  	if mod == nil {
   284  		return nil, nil
   285  	}
   286  
   287  	// Look for the resource state. If we don't have one, then it is okay.
   288  	rs := mod.Resources[n.Name]
   289  	if rs == nil {
   290  		return nil, nil
   291  	}
   292  
   293  	// Clear primary from the resource state
   294  	rs.Primary = nil
   295  
   296  	return nil, nil
   297  }
   298  
   299  // EvalDeposeState is an EvalNode implementation that takes the primary
   300  // out of a state and makes it Deposed. This is done at the beginning of
   301  // create-before-destroy calls so that the create can create while preserving
   302  // the old state of the to-be-destroyed resource.
   303  type EvalDeposeState struct {
   304  	Name string
   305  }
   306  
   307  // TODO: test
   308  func (n *EvalDeposeState) Eval(ctx EvalContext) (interface{}, error) {
   309  	state, lock := ctx.State()
   310  
   311  	// Get a read lock so we can access this instance
   312  	lock.RLock()
   313  	defer lock.RUnlock()
   314  
   315  	// Look for the module state. If we don't have one, then it doesn't matter.
   316  	mod := state.ModuleByPath(ctx.Path())
   317  	if mod == nil {
   318  		return nil, nil
   319  	}
   320  
   321  	// Look for the resource state. If we don't have one, then it is okay.
   322  	rs := mod.Resources[n.Name]
   323  	if rs == nil {
   324  		return nil, nil
   325  	}
   326  
   327  	// If we don't have a primary, we have nothing to depose
   328  	if rs.Primary == nil {
   329  		return nil, nil
   330  	}
   331  
   332  	// Depose
   333  	rs.Deposed = append(rs.Deposed, rs.Primary)
   334  	rs.Primary = nil
   335  
   336  	return nil, nil
   337  }
   338  
   339  // EvalUndeposeState is an EvalNode implementation that reads the
   340  // InstanceState for a specific resource out of the state.
   341  type EvalUndeposeState struct {
   342  	Name string
   343  }
   344  
   345  // TODO: test
   346  func (n *EvalUndeposeState) Eval(ctx EvalContext) (interface{}, error) {
   347  	state, lock := ctx.State()
   348  
   349  	// Get a read lock so we can access this instance
   350  	lock.RLock()
   351  	defer lock.RUnlock()
   352  
   353  	// Look for the module state. If we don't have one, then it doesn't matter.
   354  	mod := state.ModuleByPath(ctx.Path())
   355  	if mod == nil {
   356  		return nil, nil
   357  	}
   358  
   359  	// Look for the resource state. If we don't have one, then it is okay.
   360  	rs := mod.Resources[n.Name]
   361  	if rs == nil {
   362  		return nil, nil
   363  	}
   364  
   365  	// If we don't have any desposed resource, then we don't have anything to do
   366  	if len(rs.Deposed) == 0 {
   367  		return nil, nil
   368  	}
   369  
   370  	// Undepose
   371  	idx := len(rs.Deposed) - 1
   372  	rs.Primary = rs.Deposed[idx]
   373  	rs.Deposed[idx] = nil
   374  
   375  	return nil, nil
   376  }