github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/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 full lock. Even calling something like WriteState can modify
   111  	// (prune) the state, so we need the full lock.
   112  	lock.Lock()
   113  	defer lock.Unlock()
   114  
   115  	// Call the hook
   116  	err := ctx.Hook(func(h Hook) (HookAction, error) {
   117  		return h.PostStateUpdate(state)
   118  	})
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	return nil, nil
   124  }
   125  
   126  // EvalWriteState is an EvalNode implementation that writes the
   127  // primary InstanceState for a specific resource into the state.
   128  type EvalWriteState struct {
   129  	Name         string
   130  	ResourceType string
   131  	Provider     string
   132  	Dependencies []string
   133  	State        **InstanceState
   134  }
   135  
   136  func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) {
   137  	return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies,
   138  		func(rs *ResourceState) error {
   139  			rs.Primary = *n.State
   140  			return nil
   141  		},
   142  	)
   143  }
   144  
   145  // EvalWriteStateDeposed is an EvalNode implementation that writes
   146  // an InstanceState out to the Deposed list of a resource in the state.
   147  type EvalWriteStateDeposed struct {
   148  	Name         string
   149  	ResourceType string
   150  	Provider     string
   151  	Dependencies []string
   152  	State        **InstanceState
   153  	// Index indicates which instance in the Deposed list to target, or -1 to append.
   154  	Index int
   155  }
   156  
   157  func (n *EvalWriteStateDeposed) Eval(ctx EvalContext) (interface{}, error) {
   158  	return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies,
   159  		func(rs *ResourceState) error {
   160  			if n.Index == -1 {
   161  				rs.Deposed = append(rs.Deposed, *n.State)
   162  			} else {
   163  				rs.Deposed[n.Index] = *n.State
   164  			}
   165  			return nil
   166  		},
   167  	)
   168  }
   169  
   170  // Pulls together the common tasks of the EvalWriteState nodes.  All the args
   171  // are passed directly down from the EvalNode along with a `writer` function
   172  // which is yielded the *ResourceState and is responsible for writing an
   173  // InstanceState to the proper field in the ResourceState.
   174  func writeInstanceToState(
   175  	ctx EvalContext,
   176  	resourceName string,
   177  	resourceType string,
   178  	provider string,
   179  	dependencies []string,
   180  	writerFn func(*ResourceState) error,
   181  ) (*InstanceState, error) {
   182  	state, lock := ctx.State()
   183  	if state == nil {
   184  		return nil, fmt.Errorf("cannot write state to nil state")
   185  	}
   186  
   187  	// Get a write lock so we can access this instance
   188  	lock.Lock()
   189  	defer lock.Unlock()
   190  
   191  	// Look for the module state. If we don't have one, create it.
   192  	mod := state.ModuleByPath(ctx.Path())
   193  	if mod == nil {
   194  		mod = state.AddModule(ctx.Path())
   195  	}
   196  
   197  	// Look for the resource state.
   198  	rs := mod.Resources[resourceName]
   199  	if rs == nil {
   200  		rs = &ResourceState{}
   201  		rs.init()
   202  		mod.Resources[resourceName] = rs
   203  	}
   204  	rs.Type = resourceType
   205  	rs.Dependencies = dependencies
   206  	rs.Provider = provider
   207  
   208  	if err := writerFn(rs); err != nil {
   209  		return nil, err
   210  	}
   211  
   212  	return nil, nil
   213  }
   214  
   215  // EvalClearPrimaryState is an EvalNode implementation that clears the primary
   216  // instance from a resource state.
   217  type EvalClearPrimaryState struct {
   218  	Name string
   219  }
   220  
   221  func (n *EvalClearPrimaryState) Eval(ctx EvalContext) (interface{}, error) {
   222  	state, lock := ctx.State()
   223  
   224  	// Get a read lock so we can access this instance
   225  	lock.RLock()
   226  	defer lock.RUnlock()
   227  
   228  	// Look for the module state. If we don't have one, then it doesn't matter.
   229  	mod := state.ModuleByPath(ctx.Path())
   230  	if mod == nil {
   231  		return nil, nil
   232  	}
   233  
   234  	// Look for the resource state. If we don't have one, then it is okay.
   235  	rs := mod.Resources[n.Name]
   236  	if rs == nil {
   237  		return nil, nil
   238  	}
   239  
   240  	// Clear primary from the resource state
   241  	rs.Primary = nil
   242  
   243  	return nil, nil
   244  }
   245  
   246  // EvalDeposeState is an EvalNode implementation that takes the primary
   247  // out of a state and makes it Deposed. This is done at the beginning of
   248  // create-before-destroy calls so that the create can create while preserving
   249  // the old state of the to-be-destroyed resource.
   250  type EvalDeposeState struct {
   251  	Name string
   252  }
   253  
   254  // TODO: test
   255  func (n *EvalDeposeState) Eval(ctx EvalContext) (interface{}, error) {
   256  	state, lock := ctx.State()
   257  
   258  	// Get a read lock so we can access this instance
   259  	lock.RLock()
   260  	defer lock.RUnlock()
   261  
   262  	// Look for the module state. If we don't have one, then it doesn't matter.
   263  	mod := state.ModuleByPath(ctx.Path())
   264  	if mod == nil {
   265  		return nil, nil
   266  	}
   267  
   268  	// Look for the resource state. If we don't have one, then it is okay.
   269  	rs := mod.Resources[n.Name]
   270  	if rs == nil {
   271  		return nil, nil
   272  	}
   273  
   274  	// If we don't have a primary, we have nothing to depose
   275  	if rs.Primary == nil {
   276  		return nil, nil
   277  	}
   278  
   279  	// Depose
   280  	rs.Deposed = append(rs.Deposed, rs.Primary)
   281  	rs.Primary = nil
   282  
   283  	return nil, nil
   284  }
   285  
   286  // EvalUndeposeState is an EvalNode implementation that reads the
   287  // InstanceState for a specific resource out of the state.
   288  type EvalUndeposeState struct {
   289  	Name  string
   290  	State **InstanceState
   291  }
   292  
   293  // TODO: test
   294  func (n *EvalUndeposeState) Eval(ctx EvalContext) (interface{}, error) {
   295  	state, lock := ctx.State()
   296  
   297  	// Get a read lock so we can access this instance
   298  	lock.RLock()
   299  	defer lock.RUnlock()
   300  
   301  	// Look for the module state. If we don't have one, then it doesn't matter.
   302  	mod := state.ModuleByPath(ctx.Path())
   303  	if mod == nil {
   304  		return nil, nil
   305  	}
   306  
   307  	// Look for the resource state. If we don't have one, then it is okay.
   308  	rs := mod.Resources[n.Name]
   309  	if rs == nil {
   310  		return nil, nil
   311  	}
   312  
   313  	// If we don't have any desposed resource, then we don't have anything to do
   314  	if len(rs.Deposed) == 0 {
   315  		return nil, nil
   316  	}
   317  
   318  	// Undepose
   319  	idx := len(rs.Deposed) - 1
   320  	rs.Primary = rs.Deposed[idx]
   321  	rs.Deposed[idx] = *n.State
   322  
   323  	return nil, nil
   324  }