github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/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  	Dependencies []string
   158  	State        **InstanceState
   159  }
   160  
   161  func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) {
   162  	return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Dependencies,
   163  		func(rs *ResourceState) error {
   164  			rs.Primary = *n.State
   165  			return nil
   166  		},
   167  	)
   168  }
   169  
   170  // EvalWriteStateTainted is an EvalNode implementation that writes
   171  // an InstanceState out to the Tainted list of a resource in the state.
   172  type EvalWriteStateTainted struct {
   173  	Name         string
   174  	ResourceType string
   175  	Dependencies []string
   176  	State        **InstanceState
   177  	// Index indicates which instance in the Tainted list to target, or -1 to append.
   178  	Index int
   179  }
   180  
   181  // EvalWriteStateTainted is an EvalNode implementation that writes the
   182  // one of the tainted InstanceStates for a specific resource out of the state.
   183  func (n *EvalWriteStateTainted) Eval(ctx EvalContext) (interface{}, error) {
   184  	return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Dependencies,
   185  		func(rs *ResourceState) error {
   186  			if n.Index == -1 {
   187  				rs.Tainted = append(rs.Tainted, *n.State)
   188  			} else {
   189  				rs.Tainted[n.Index] = *n.State
   190  			}
   191  			return nil
   192  		},
   193  	)
   194  }
   195  
   196  // EvalWriteStateDeposed is an EvalNode implementation that writes
   197  // an InstanceState out to the Deposed list of a resource in the state.
   198  type EvalWriteStateDeposed struct {
   199  	Name         string
   200  	ResourceType string
   201  	Dependencies []string
   202  	State        **InstanceState
   203  	// Index indicates which instance in the Deposed list to target, or -1 to append.
   204  	Index int
   205  }
   206  
   207  func (n *EvalWriteStateDeposed) Eval(ctx EvalContext) (interface{}, error) {
   208  	return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Dependencies,
   209  		func(rs *ResourceState) error {
   210  			if n.Index == -1 {
   211  				rs.Deposed = append(rs.Deposed, *n.State)
   212  			} else {
   213  				rs.Deposed[n.Index] = *n.State
   214  			}
   215  			return nil
   216  		},
   217  	)
   218  }
   219  
   220  // Pulls together the common tasks of the EvalWriteState nodes.  All the args
   221  // are passed directly down from the EvalNode along with a `writer` function
   222  // which is yielded the *ResourceState and is responsible for writing an
   223  // InstanceState to the proper field in the ResourceState.
   224  func writeInstanceToState(
   225  	ctx EvalContext,
   226  	resourceName string,
   227  	resourceType string,
   228  	dependencies []string,
   229  	writerFn func(*ResourceState) error,
   230  ) (*InstanceState, error) {
   231  	state, lock := ctx.State()
   232  	if state == nil {
   233  		return nil, fmt.Errorf("cannot write state to nil state")
   234  	}
   235  
   236  	// Get a write lock so we can access this instance
   237  	lock.Lock()
   238  	defer lock.Unlock()
   239  
   240  	// Look for the module state. If we don't have one, create it.
   241  	mod := state.ModuleByPath(ctx.Path())
   242  	if mod == nil {
   243  		mod = state.AddModule(ctx.Path())
   244  	}
   245  
   246  	// Look for the resource state.
   247  	rs := mod.Resources[resourceName]
   248  	if rs == nil {
   249  		rs = &ResourceState{}
   250  		rs.init()
   251  		mod.Resources[resourceName] = rs
   252  	}
   253  	rs.Type = resourceType
   254  	rs.Dependencies = dependencies
   255  
   256  	if err := writerFn(rs); err != nil {
   257  		return nil, err
   258  	}
   259  
   260  	return nil, nil
   261  }
   262  
   263  // EvalClearPrimaryState is an EvalNode implementation that clears the primary
   264  // instance from a resource state.
   265  type EvalClearPrimaryState struct {
   266  	Name string
   267  }
   268  
   269  func (n *EvalClearPrimaryState) Eval(ctx EvalContext) (interface{}, error) {
   270  	state, lock := ctx.State()
   271  
   272  	// Get a read lock so we can access this instance
   273  	lock.RLock()
   274  	defer lock.RUnlock()
   275  
   276  	// Look for the module state. If we don't have one, then it doesn't matter.
   277  	mod := state.ModuleByPath(ctx.Path())
   278  	if mod == nil {
   279  		return nil, nil
   280  	}
   281  
   282  	// Look for the resource state. If we don't have one, then it is okay.
   283  	rs := mod.Resources[n.Name]
   284  	if rs == nil {
   285  		return nil, nil
   286  	}
   287  
   288  	// Clear primary from the resource state
   289  	rs.Primary = nil
   290  
   291  	return nil, nil
   292  }
   293  
   294  // EvalDeposeState is an EvalNode implementation that takes the primary
   295  // out of a state and makes it Deposed. This is done at the beginning of
   296  // create-before-destroy calls so that the create can create while preserving
   297  // the old state of the to-be-destroyed resource.
   298  type EvalDeposeState struct {
   299  	Name string
   300  }
   301  
   302  // TODO: test
   303  func (n *EvalDeposeState) Eval(ctx EvalContext) (interface{}, error) {
   304  	state, lock := ctx.State()
   305  
   306  	// Get a read lock so we can access this instance
   307  	lock.RLock()
   308  	defer lock.RUnlock()
   309  
   310  	// Look for the module state. If we don't have one, then it doesn't matter.
   311  	mod := state.ModuleByPath(ctx.Path())
   312  	if mod == nil {
   313  		return nil, nil
   314  	}
   315  
   316  	// Look for the resource state. If we don't have one, then it is okay.
   317  	rs := mod.Resources[n.Name]
   318  	if rs == nil {
   319  		return nil, nil
   320  	}
   321  
   322  	// If we don't have a primary, we have nothing to depose
   323  	if rs.Primary == nil {
   324  		return nil, nil
   325  	}
   326  
   327  	// Depose
   328  	rs.Deposed = append(rs.Deposed, rs.Primary)
   329  	rs.Primary = nil
   330  
   331  	return nil, nil
   332  }
   333  
   334  // EvalUndeposeState is an EvalNode implementation that reads the
   335  // InstanceState for a specific resource out of the state.
   336  type EvalUndeposeState struct {
   337  	Name string
   338  }
   339  
   340  // TODO: test
   341  func (n *EvalUndeposeState) Eval(ctx EvalContext) (interface{}, error) {
   342  	state, lock := ctx.State()
   343  
   344  	// Get a read lock so we can access this instance
   345  	lock.RLock()
   346  	defer lock.RUnlock()
   347  
   348  	// Look for the module state. If we don't have one, then it doesn't matter.
   349  	mod := state.ModuleByPath(ctx.Path())
   350  	if mod == nil {
   351  		return nil, nil
   352  	}
   353  
   354  	// Look for the resource state. If we don't have one, then it is okay.
   355  	rs := mod.Resources[n.Name]
   356  	if rs == nil {
   357  		return nil, nil
   358  	}
   359  
   360  	// If we don't have any desposed resource, then we don't have anything to do
   361  	if len(rs.Deposed) == 0 {
   362  		return nil, nil
   363  	}
   364  
   365  	// Undepose
   366  	idx := len(rs.Deposed) - 1
   367  	rs.Primary = rs.Deposed[idx]
   368  	rs.Deposed[idx] = nil
   369  
   370  	return nil, nil
   371  }