github.com/ns1/terraform@v0.7.10-0.20161109153551-8949419bef40/terraform/eval_state.go (about)

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