github.com/opsidian/terraform@v0.7.8-0.20161104123224-27c39cdfba5b/terraform/resource.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sort"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/hashicorp/terraform/config"
    11  	"github.com/mitchellh/copystructure"
    12  )
    13  
    14  // ResourceProvisionerConfig is used to pair a provisioner
    15  // with its provided configuration. This allows us to use singleton
    16  // instances of each ResourceProvisioner and to keep the relevant
    17  // configuration instead of instantiating a new Provisioner for each
    18  // resource.
    19  type ResourceProvisionerConfig struct {
    20  	Type        string
    21  	Provisioner ResourceProvisioner
    22  	Config      *ResourceConfig
    23  	RawConfig   *config.RawConfig
    24  	ConnInfo    *config.RawConfig
    25  }
    26  
    27  // Resource encapsulates a resource, its configuration, its provider,
    28  // its current state, and potentially a desired diff from the state it
    29  // wants to reach.
    30  type Resource struct {
    31  	// These are all used by the new EvalNode stuff.
    32  	Name       string
    33  	Type       string
    34  	CountIndex int
    35  
    36  	// These aren't really used anymore anywhere, but we keep them around
    37  	// since we haven't done a proper cleanup yet.
    38  	Id           string
    39  	Info         *InstanceInfo
    40  	Config       *ResourceConfig
    41  	Dependencies []string
    42  	Diff         *InstanceDiff
    43  	Provider     ResourceProvider
    44  	State        *InstanceState
    45  	Provisioners []*ResourceProvisionerConfig
    46  	Flags        ResourceFlag
    47  }
    48  
    49  // ResourceKind specifies what kind of instance we're working with, whether
    50  // its a primary instance, a tainted instance, or an orphan.
    51  type ResourceFlag byte
    52  
    53  // InstanceInfo is used to hold information about the instance and/or
    54  // resource being modified.
    55  type InstanceInfo struct {
    56  	// Id is a unique name to represent this instance. This is not related
    57  	// to InstanceState.ID in any way.
    58  	Id string
    59  
    60  	// ModulePath is the complete path of the module containing this
    61  	// instance.
    62  	ModulePath []string
    63  
    64  	// Type is the resource type of this instance
    65  	Type string
    66  
    67  	// uniqueExtra is an internal field that can be populated to supply
    68  	// extra metadata that is used to identify a unique instance in
    69  	// the graph walk. This will be appended to HumanID when uniqueId
    70  	// is called.
    71  	uniqueExtra string
    72  }
    73  
    74  // HumanId is a unique Id that is human-friendly and useful for UI elements.
    75  func (i *InstanceInfo) HumanId() string {
    76  	if i == nil {
    77  		return "<nil>"
    78  	}
    79  
    80  	if len(i.ModulePath) <= 1 {
    81  		return i.Id
    82  	}
    83  
    84  	return fmt.Sprintf(
    85  		"module.%s.%s",
    86  		strings.Join(i.ModulePath[1:], "."),
    87  		i.Id)
    88  }
    89  
    90  func (i *InstanceInfo) uniqueId() string {
    91  	prefix := i.HumanId()
    92  	if v := i.uniqueExtra; v != "" {
    93  		prefix += " " + v
    94  	}
    95  
    96  	return prefix
    97  }
    98  
    99  // ResourceConfig holds the configuration given for a resource. This is
   100  // done instead of a raw `map[string]interface{}` type so that rich
   101  // methods can be added to it to make dealing with it easier.
   102  type ResourceConfig struct {
   103  	ComputedKeys []string
   104  	Raw          map[string]interface{}
   105  	Config       map[string]interface{}
   106  
   107  	raw *config.RawConfig
   108  }
   109  
   110  // NewResourceConfig creates a new ResourceConfig from a config.RawConfig.
   111  func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
   112  	result := &ResourceConfig{raw: c}
   113  	result.interpolateForce()
   114  	return result
   115  }
   116  
   117  // DeepCopy performs a deep copy of the configuration. This makes it safe
   118  // to modify any of the structures that are part of the resource config without
   119  // affecting the original configuration.
   120  func (c *ResourceConfig) DeepCopy() *ResourceConfig {
   121  	// DeepCopying a nil should return a nil to avoid panics
   122  	if c == nil {
   123  		return nil
   124  	}
   125  
   126  	// Copy, this will copy all the exported attributes
   127  	copy, err := copystructure.Config{Lock: true}.Copy(c)
   128  	if err != nil {
   129  		panic(err)
   130  	}
   131  
   132  	// Force the type
   133  	result := copy.(*ResourceConfig)
   134  
   135  	// For the raw configuration, we can just use its own copy method
   136  	result.raw = c.raw.Copy()
   137  
   138  	return result
   139  }
   140  
   141  // Equal checks the equality of two resource configs.
   142  func (c *ResourceConfig) Equal(c2 *ResourceConfig) bool {
   143  	// If either are nil, then they're only equal if they're both nil
   144  	if c == nil || c2 == nil {
   145  		return c == c2
   146  	}
   147  
   148  	// Sort the computed keys so they're deterministic
   149  	sort.Strings(c.ComputedKeys)
   150  	sort.Strings(c2.ComputedKeys)
   151  
   152  	// Two resource configs if their exported properties are equal.
   153  	// We don't compare "raw" because it is never used again after
   154  	// initialization and for all intents and purposes they are equal
   155  	// if the exported properties are equal.
   156  	check := [][2]interface{}{
   157  		{c.ComputedKeys, c2.ComputedKeys},
   158  		{c.Raw, c2.Raw},
   159  		{c.Config, c2.Config},
   160  	}
   161  	for _, pair := range check {
   162  		if !reflect.DeepEqual(pair[0], pair[1]) {
   163  			return false
   164  		}
   165  	}
   166  
   167  	return true
   168  }
   169  
   170  // CheckSet checks that the given list of configuration keys is
   171  // properly set. If not, errors are returned for each unset key.
   172  //
   173  // This is useful to be called in the Validate method of a ResourceProvider.
   174  func (c *ResourceConfig) CheckSet(keys []string) []error {
   175  	var errs []error
   176  
   177  	for _, k := range keys {
   178  		if !c.IsSet(k) {
   179  			errs = append(errs, fmt.Errorf("%s must be set", k))
   180  		}
   181  	}
   182  
   183  	return errs
   184  }
   185  
   186  // Get looks up a configuration value by key and returns the value.
   187  //
   188  // The second return value is true if the get was successful. Get will
   189  // not succeed if the value is being computed.
   190  func (c *ResourceConfig) Get(k string) (interface{}, bool) {
   191  	// First try to get it from c.Config since that has interpolated values
   192  	result, ok := c.get(k, c.Config)
   193  	if ok {
   194  		return result, ok
   195  	}
   196  
   197  	// Otherwise, just get it from the raw config
   198  	return c.get(k, c.Raw)
   199  }
   200  
   201  // GetRaw looks up a configuration value by key and returns the value,
   202  // from the raw, uninterpolated config.
   203  //
   204  // The second return value is true if the get was successful. Get will
   205  // not succeed if the value is being computed.
   206  func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) {
   207  	return c.get(k, c.Raw)
   208  }
   209  
   210  // IsComputed returns whether the given key is computed or not.
   211  func (c *ResourceConfig) IsComputed(k string) bool {
   212  	_, ok := c.get(k, c.Config)
   213  	_, okRaw := c.get(k, c.Raw)
   214  	return !ok && okRaw
   215  }
   216  
   217  // IsSet checks if the key in the configuration is set. A key is set if
   218  // it has a value or the value is being computed (is unknown currently).
   219  //
   220  // This function should be used rather than checking the keys of the
   221  // raw configuration itself, since a key may be omitted from the raw
   222  // configuration if it is being computed.
   223  func (c *ResourceConfig) IsSet(k string) bool {
   224  	if c == nil {
   225  		return false
   226  	}
   227  
   228  	for _, ck := range c.ComputedKeys {
   229  		if ck == k {
   230  			return true
   231  		}
   232  	}
   233  
   234  	if _, ok := c.Get(k); ok {
   235  		return true
   236  	}
   237  
   238  	return false
   239  }
   240  
   241  func (c *ResourceConfig) get(
   242  	k string, raw map[string]interface{}) (interface{}, bool) {
   243  	parts := strings.Split(k, ".")
   244  	if len(parts) == 1 && parts[0] == "" {
   245  		parts = nil
   246  	}
   247  
   248  	var current interface{} = raw
   249  	var previous interface{} = nil
   250  	for i, part := range parts {
   251  		if current == nil {
   252  			return nil, false
   253  		}
   254  
   255  		cv := reflect.ValueOf(current)
   256  		switch cv.Kind() {
   257  		case reflect.Map:
   258  			previous = current
   259  			v := cv.MapIndex(reflect.ValueOf(part))
   260  			if !v.IsValid() {
   261  				if i > 0 && i != (len(parts)-1) {
   262  					tryKey := strings.Join(parts[i:], ".")
   263  					v := cv.MapIndex(reflect.ValueOf(tryKey))
   264  					if !v.IsValid() {
   265  						return nil, false
   266  					}
   267  					return v.Interface(), true
   268  				}
   269  
   270  				return nil, false
   271  			}
   272  			current = v.Interface()
   273  		case reflect.Slice:
   274  			previous = current
   275  			if part == "#" {
   276  				current = cv.Len()
   277  			} else {
   278  				i, err := strconv.ParseInt(part, 0, 0)
   279  				if err != nil {
   280  					return nil, false
   281  				}
   282  				if i >= int64(cv.Len()) {
   283  					return nil, false
   284  				}
   285  				current = cv.Index(int(i)).Interface()
   286  			}
   287  		case reflect.String:
   288  			// This happens when map keys contain "." and have a common
   289  			// prefix so were split as path components above.
   290  			actualKey := strings.Join(parts[i-1:], ".")
   291  			if prevMap, ok := previous.(map[string]interface{}); ok {
   292  				return prevMap[actualKey], true
   293  			}
   294  			return nil, false
   295  		default:
   296  			panic(fmt.Sprintf("Unknown kind: %s", cv.Kind()))
   297  		}
   298  	}
   299  
   300  	return current, true
   301  }
   302  
   303  // interpolateForce is a temporary thing. We want to get rid of interpolate
   304  // above and likewise this, but it can only be done after the f-ast-graph
   305  // refactor is complete.
   306  func (c *ResourceConfig) interpolateForce() {
   307  	if c.raw == nil {
   308  		var err error
   309  		c.raw, err = config.NewRawConfig(make(map[string]interface{}))
   310  		if err != nil {
   311  			panic(err)
   312  		}
   313  	}
   314  
   315  	c.ComputedKeys = c.raw.UnknownKeys()
   316  	c.Raw = c.raw.RawMap()
   317  	c.Config = c.raw.Config()
   318  }