github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/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  // GetRaw looks up a configuration value by key and returns the value,
   138  // from the raw, uninterpolated config.
   139  //
   140  // The second return value is true if the get was successful. Get will
   141  // not succeed if the value is being computed.
   142  func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) {
   143  	return c.get(k, c.Raw)
   144  }
   145  
   146  // IsComputed returns whether the given key is computed or not.
   147  func (c *ResourceConfig) IsComputed(k string) bool {
   148  	_, ok := c.get(k, c.Config)
   149  	_, okRaw := c.get(k, c.Raw)
   150  	return !ok && okRaw
   151  }
   152  
   153  // IsSet checks if the key in the configuration is set. A key is set if
   154  // it has a value or the value is being computed (is unknown currently).
   155  //
   156  // This function should be used rather than checking the keys of the
   157  // raw configuration itself, since a key may be omitted from the raw
   158  // configuration if it is being computed.
   159  func (c *ResourceConfig) IsSet(k string) bool {
   160  	if c == nil {
   161  		return false
   162  	}
   163  
   164  	for _, ck := range c.ComputedKeys {
   165  		if ck == k {
   166  			return true
   167  		}
   168  	}
   169  
   170  	if _, ok := c.Get(k); ok {
   171  		return true
   172  	}
   173  
   174  	return false
   175  }
   176  
   177  func (c *ResourceConfig) get(
   178  	k string, raw map[string]interface{}) (interface{}, bool) {
   179  	parts := strings.Split(k, ".")
   180  	if len(parts) == 1 && parts[0] == "" {
   181  		parts = nil
   182  	}
   183  
   184  	var current interface{} = raw
   185  	for _, part := range parts {
   186  		if current == nil {
   187  			return nil, false
   188  		}
   189  
   190  		cv := reflect.ValueOf(current)
   191  		switch cv.Kind() {
   192  		case reflect.Map:
   193  			v := cv.MapIndex(reflect.ValueOf(part))
   194  			if !v.IsValid() {
   195  				return nil, false
   196  			}
   197  			current = v.Interface()
   198  		case reflect.Slice:
   199  			if part == "#" {
   200  				current = cv.Len()
   201  			} else {
   202  				i, err := strconv.ParseInt(part, 0, 0)
   203  				if err != nil {
   204  					return nil, false
   205  				}
   206  				if i >= int64(cv.Len()) {
   207  					return nil, false
   208  				}
   209  				current = cv.Index(int(i)).Interface()
   210  			}
   211  		default:
   212  			panic(fmt.Sprintf("Unknown kind: %s", cv.Kind()))
   213  		}
   214  	}
   215  
   216  	return current, true
   217  }
   218  
   219  // interpolateForce is a temporary thing. We want to get rid of interpolate
   220  // above and likewise this, but it can only be done after the f-ast-graph
   221  // refactor is complete.
   222  func (c *ResourceConfig) interpolateForce() {
   223  	if c.raw == nil {
   224  		var err error
   225  		c.raw, err = config.NewRawConfig(make(map[string]interface{}))
   226  		if err != nil {
   227  			panic(err)
   228  		}
   229  	}
   230  
   231  	c.ComputedKeys = c.raw.UnknownKeys()
   232  	c.Raw = c.raw.Raw
   233  	c.Config = c.raw.Config()
   234  }