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