github.com/nicgrayson/terraform@v0.4.3-0.20150415203910-c4de50829380/terraform/resource.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/hashicorp/terraform/config"
    10  )
    11  
    12  // ResourceProvisionerConfig is used to pair a provisioner
    13  // with its provided configuration. This allows us to use singleton
    14  // instances of each ResourceProvisioner and to keep the relevant
    15  // configuration instead of instantiating a new Provisioner for each
    16  // resource.
    17  type ResourceProvisionerConfig struct {
    18  	Type        string
    19  	Provisioner ResourceProvisioner
    20  	Config      *ResourceConfig
    21  	RawConfig   *config.RawConfig
    22  	ConnInfo    *config.RawConfig
    23  }
    24  
    25  // Resource encapsulates a resource, its configuration, its provider,
    26  // its current state, and potentially a desired diff from the state it
    27  // wants to reach.
    28  type Resource struct {
    29  	// These are all used by the new EvalNode stuff.
    30  	Name       string
    31  	Type       string
    32  	CountIndex int
    33  
    34  	// These aren't really used anymore anywhere, but we keep them around
    35  	// since we haven't done a proper cleanup yet.
    36  	Id           string
    37  	Info         *InstanceInfo
    38  	Config       *ResourceConfig
    39  	Dependencies []string
    40  	Diff         *InstanceDiff
    41  	Provider     ResourceProvider
    42  	State        *InstanceState
    43  	Provisioners []*ResourceProvisionerConfig
    44  	Flags        ResourceFlag
    45  	TaintedIndex int
    46  }
    47  
    48  // ResourceKind specifies what kind of instance we're working with, whether
    49  // its a primary instance, a tainted instance, or an orphan.
    50  type ResourceFlag byte
    51  
    52  const (
    53  	FlagPrimary ResourceFlag = 1 << iota
    54  	FlagTainted
    55  	FlagOrphan
    56  	FlagHasTainted
    57  	FlagReplacePrimary
    58  	FlagDeposed
    59  )
    60  
    61  // InstanceInfo is used to hold information about the instance and/or
    62  // resource being modified.
    63  type InstanceInfo struct {
    64  	// Id is a unique name to represent this instance. This is not related
    65  	// to InstanceState.ID in any way.
    66  	Id string
    67  
    68  	// ModulePath is the complete path of the module containing this
    69  	// instance.
    70  	ModulePath []string
    71  
    72  	// Type is the resource type of this instance
    73  	Type string
    74  }
    75  
    76  // HumanId is a unique Id that is human-friendly and useful for UI elements.
    77  func (i *InstanceInfo) HumanId() string {
    78  	if len(i.ModulePath) <= 1 {
    79  		return i.Id
    80  	}
    81  
    82  	return fmt.Sprintf(
    83  		"module.%s.%s",
    84  		strings.Join(i.ModulePath[1:], "."),
    85  		i.Id)
    86  }
    87  
    88  // ResourceConfig holds the configuration given for a resource. This is
    89  // done instead of a raw `map[string]interface{}` type so that rich
    90  // methods can be added to it to make dealing with it easier.
    91  type ResourceConfig struct {
    92  	ComputedKeys []string
    93  	Raw          map[string]interface{}
    94  	Config       map[string]interface{}
    95  
    96  	raw *config.RawConfig
    97  }
    98  
    99  // NewResourceConfig creates a new ResourceConfig from a config.RawConfig.
   100  func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
   101  	result := &ResourceConfig{raw: c}
   102  	result.interpolateForce()
   103  	return result
   104  }
   105  
   106  // CheckSet checks that the given list of configuration keys is
   107  // properly set. If not, errors are returned for each unset key.
   108  //
   109  // This is useful to be called in the Validate method of a ResourceProvider.
   110  func (c *ResourceConfig) CheckSet(keys []string) []error {
   111  	var errs []error
   112  
   113  	for _, k := range keys {
   114  		if !c.IsSet(k) {
   115  			errs = append(errs, fmt.Errorf("%s must be set", k))
   116  		}
   117  	}
   118  
   119  	return errs
   120  }
   121  
   122  // Get looks up a configuration value by key and returns the value.
   123  //
   124  // The second return value is true if the get was successful. Get will
   125  // not succeed if the value is being computed.
   126  func (c *ResourceConfig) Get(k string) (interface{}, bool) {
   127  	// First try to get it from c.Config since that has interpolated values
   128  	result, ok := c.get(k, c.Config)
   129  	if ok {
   130  		return result, ok
   131  	}
   132  
   133  	// Otherwise, just get it from the raw config
   134  	return c.get(k, c.Raw)
   135  }
   136  
   137  // IsComputed returns whether the given key is computed or not.
   138  func (c *ResourceConfig) IsComputed(k string) bool {
   139  	_, ok := c.get(k, c.Config)
   140  	_, okRaw := c.get(k, c.Raw)
   141  	return !ok && okRaw
   142  }
   143  
   144  // IsSet checks if the key in the configuration is set. A key is set if
   145  // it has a value or the value is being computed (is unknown currently).
   146  //
   147  // This function should be used rather than checking the keys of the
   148  // raw configuration itself, since a key may be omitted from the raw
   149  // configuration if it is being computed.
   150  func (c *ResourceConfig) IsSet(k string) bool {
   151  	if c == nil {
   152  		return false
   153  	}
   154  
   155  	for _, ck := range c.ComputedKeys {
   156  		if ck == k {
   157  			return true
   158  		}
   159  	}
   160  
   161  	if _, ok := c.Get(k); ok {
   162  		return true
   163  	}
   164  
   165  	return false
   166  }
   167  
   168  func (c *ResourceConfig) get(
   169  	k string, raw map[string]interface{}) (interface{}, bool) {
   170  	parts := strings.Split(k, ".")
   171  	if len(parts) == 1 && parts[0] == "" {
   172  		parts = nil
   173  	}
   174  
   175  	var current interface{} = raw
   176  	for _, part := range parts {
   177  		if current == nil {
   178  			return nil, false
   179  		}
   180  
   181  		cv := reflect.ValueOf(current)
   182  		switch cv.Kind() {
   183  		case reflect.Map:
   184  			v := cv.MapIndex(reflect.ValueOf(part))
   185  			if !v.IsValid() {
   186  				return nil, false
   187  			}
   188  			current = v.Interface()
   189  		case reflect.Slice:
   190  			if part == "#" {
   191  				current = cv.Len()
   192  			} else {
   193  				i, err := strconv.ParseInt(part, 0, 0)
   194  				if err != nil {
   195  					return nil, false
   196  				}
   197  				if i >= int64(cv.Len()) {
   198  					return nil, false
   199  				}
   200  				current = cv.Index(int(i)).Interface()
   201  			}
   202  		default:
   203  			panic(fmt.Sprintf("Unknown kind: %s", cv.Kind()))
   204  		}
   205  	}
   206  
   207  	return current, true
   208  }
   209  
   210  // interpolateForce is a temporary thing. We want to get rid of interpolate
   211  // above and likewise this, but it can only be done after the f-ast-graph
   212  // refactor is complete.
   213  func (c *ResourceConfig) interpolateForce() {
   214  	if c.raw == nil {
   215  		var err error
   216  		c.raw, err = config.NewRawConfig(make(map[string]interface{}))
   217  		if err != nil {
   218  			panic(err)
   219  		}
   220  	}
   221  
   222  	c.ComputedKeys = c.raw.UnknownKeys()
   223  	c.Raw = c.raw.Raw
   224  	c.Config = c.raw.Config()
   225  }