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