github.com/salme4/terraform@v0.11.12-beta1/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  	"github.com/mitchellh/reflectwalk"
    13  )
    14  
    15  // ResourceProvisionerConfig is used to pair a provisioner
    16  // with its provided configuration. This allows us to use singleton
    17  // instances of each ResourceProvisioner and to keep the relevant
    18  // configuration instead of instantiating a new Provisioner for each
    19  // resource.
    20  type ResourceProvisionerConfig struct {
    21  	Type        string
    22  	Provisioner ResourceProvisioner
    23  	Config      *ResourceConfig
    24  	RawConfig   *config.RawConfig
    25  	ConnInfo    *config.RawConfig
    26  }
    27  
    28  // Resource encapsulates a resource, its configuration, its provider,
    29  // its current state, and potentially a desired diff from the state it
    30  // wants to reach.
    31  type Resource struct {
    32  	// These are all used by the new EvalNode stuff.
    33  	Name       string
    34  	Type       string
    35  	CountIndex int
    36  
    37  	// These aren't really used anymore anywhere, but we keep them around
    38  	// since we haven't done a proper cleanup yet.
    39  	Id           string
    40  	Info         *InstanceInfo
    41  	Config       *ResourceConfig
    42  	Dependencies []string
    43  	Diff         *InstanceDiff
    44  	Provider     ResourceProvider
    45  	State        *InstanceState
    46  	Provisioners []*ResourceProvisionerConfig
    47  	Flags        ResourceFlag
    48  }
    49  
    50  // ResourceKind specifies what kind of instance we're working with, whether
    51  // its a primary instance, a tainted instance, or an orphan.
    52  type ResourceFlag byte
    53  
    54  // InstanceInfo is used to hold information about the instance and/or
    55  // resource being modified.
    56  type InstanceInfo struct {
    57  	// Id is a unique name to represent this instance. This is not related
    58  	// to InstanceState.ID in any way.
    59  	Id string
    60  
    61  	// ModulePath is the complete path of the module containing this
    62  	// instance.
    63  	ModulePath []string
    64  
    65  	// Type is the resource type of this instance
    66  	Type string
    67  
    68  	// uniqueExtra is an internal field that can be populated to supply
    69  	// extra metadata that is used to identify a unique instance in
    70  	// the graph walk. This will be appended to HumanID when uniqueId
    71  	// is called.
    72  	uniqueExtra string
    73  }
    74  
    75  // HumanId is a unique Id that is human-friendly and useful for UI elements.
    76  func (i *InstanceInfo) HumanId() string {
    77  	if i == nil {
    78  		return "<nil>"
    79  	}
    80  
    81  	if len(i.ModulePath) <= 1 {
    82  		return i.Id
    83  	}
    84  
    85  	return fmt.Sprintf(
    86  		"module.%s.%s",
    87  		strings.Join(i.ModulePath[1:], "."),
    88  		i.Id)
    89  }
    90  
    91  // ResourceAddress returns the address of the resource that the receiver is describing.
    92  func (i *InstanceInfo) ResourceAddress() *ResourceAddress {
    93  	// GROSS: for tainted and deposed instances, their status gets appended
    94  	// to i.Id to create a unique id for the graph node. Historically these
    95  	// ids were displayed to the user, so it's designed to be human-readable:
    96  	//   "aws_instance.bar.0 (deposed #0)"
    97  	//
    98  	// So here we detect such suffixes and try to interpret them back to
    99  	// their original meaning so we can then produce a ResourceAddress
   100  	// with a suitable InstanceType.
   101  	id := i.Id
   102  	instanceType := TypeInvalid
   103  	if idx := strings.Index(id, " ("); idx != -1 {
   104  		remain := id[idx:]
   105  		id = id[:idx]
   106  
   107  		switch {
   108  		case strings.Contains(remain, "tainted"):
   109  			instanceType = TypeTainted
   110  		case strings.Contains(remain, "deposed"):
   111  			instanceType = TypeDeposed
   112  		}
   113  	}
   114  
   115  	addr, err := parseResourceAddressInternal(id)
   116  	if err != nil {
   117  		// should never happen, since that would indicate a bug in the
   118  		// code that constructed this InstanceInfo.
   119  		panic(fmt.Errorf("InstanceInfo has invalid Id %s", id))
   120  	}
   121  	if len(i.ModulePath) > 1 {
   122  		addr.Path = i.ModulePath[1:] // trim off "root" prefix, which is implied
   123  	}
   124  	if instanceType != TypeInvalid {
   125  		addr.InstanceTypeSet = true
   126  		addr.InstanceType = instanceType
   127  	}
   128  	return addr
   129  }
   130  
   131  func (i *InstanceInfo) uniqueId() string {
   132  	prefix := i.HumanId()
   133  	if v := i.uniqueExtra; v != "" {
   134  		prefix += " " + v
   135  	}
   136  
   137  	return prefix
   138  }
   139  
   140  // ResourceConfig holds the configuration given for a resource. This is
   141  // done instead of a raw `map[string]interface{}` type so that rich
   142  // methods can be added to it to make dealing with it easier.
   143  type ResourceConfig struct {
   144  	ComputedKeys []string
   145  	Raw          map[string]interface{}
   146  	Config       map[string]interface{}
   147  
   148  	raw *config.RawConfig
   149  }
   150  
   151  // NewResourceConfig creates a new ResourceConfig from a config.RawConfig.
   152  func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
   153  	result := &ResourceConfig{raw: c}
   154  	result.interpolateForce()
   155  	return result
   156  }
   157  
   158  // DeepCopy performs a deep copy of the configuration. This makes it safe
   159  // to modify any of the structures that are part of the resource config without
   160  // affecting the original configuration.
   161  func (c *ResourceConfig) DeepCopy() *ResourceConfig {
   162  	// DeepCopying a nil should return a nil to avoid panics
   163  	if c == nil {
   164  		return nil
   165  	}
   166  
   167  	// Copy, this will copy all the exported attributes
   168  	copy, err := copystructure.Config{Lock: true}.Copy(c)
   169  	if err != nil {
   170  		panic(err)
   171  	}
   172  
   173  	// Force the type
   174  	result := copy.(*ResourceConfig)
   175  
   176  	// For the raw configuration, we can just use its own copy method
   177  	result.raw = c.raw.Copy()
   178  
   179  	return result
   180  }
   181  
   182  // Equal checks the equality of two resource configs.
   183  func (c *ResourceConfig) Equal(c2 *ResourceConfig) bool {
   184  	// If either are nil, then they're only equal if they're both nil
   185  	if c == nil || c2 == nil {
   186  		return c == c2
   187  	}
   188  
   189  	// Sort the computed keys so they're deterministic
   190  	sort.Strings(c.ComputedKeys)
   191  	sort.Strings(c2.ComputedKeys)
   192  
   193  	// Two resource configs if their exported properties are equal.
   194  	// We don't compare "raw" because it is never used again after
   195  	// initialization and for all intents and purposes they are equal
   196  	// if the exported properties are equal.
   197  	check := [][2]interface{}{
   198  		{c.ComputedKeys, c2.ComputedKeys},
   199  		{c.Raw, c2.Raw},
   200  		{c.Config, c2.Config},
   201  	}
   202  	for _, pair := range check {
   203  		if !reflect.DeepEqual(pair[0], pair[1]) {
   204  			return false
   205  		}
   206  	}
   207  
   208  	return true
   209  }
   210  
   211  // CheckSet checks that the given list of configuration keys is
   212  // properly set. If not, errors are returned for each unset key.
   213  //
   214  // This is useful to be called in the Validate method of a ResourceProvider.
   215  func (c *ResourceConfig) CheckSet(keys []string) []error {
   216  	var errs []error
   217  
   218  	for _, k := range keys {
   219  		if !c.IsSet(k) {
   220  			errs = append(errs, fmt.Errorf("%s must be set", k))
   221  		}
   222  	}
   223  
   224  	return errs
   225  }
   226  
   227  // Get looks up a configuration value by key and returns the value.
   228  //
   229  // The second return value is true if the get was successful. Get will
   230  // return the raw value if the key is computed, so you should pair this
   231  // with IsComputed.
   232  func (c *ResourceConfig) Get(k string) (interface{}, bool) {
   233  	// We aim to get a value from the configuration. If it is computed,
   234  	// then we return the pure raw value.
   235  	source := c.Config
   236  	if c.IsComputed(k) {
   237  		source = c.Raw
   238  	}
   239  
   240  	return c.get(k, source)
   241  }
   242  
   243  // GetRaw looks up a configuration value by key and returns the value,
   244  // from the raw, uninterpolated config.
   245  //
   246  // The second return value is true if the get was successful. Get will
   247  // not succeed if the value is being computed.
   248  func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) {
   249  	return c.get(k, c.Raw)
   250  }
   251  
   252  // IsComputed returns whether the given key is computed or not.
   253  func (c *ResourceConfig) IsComputed(k string) bool {
   254  	// The next thing we do is check the config if we get a computed
   255  	// value out of it.
   256  	v, ok := c.get(k, c.Config)
   257  	if !ok {
   258  		return false
   259  	}
   260  
   261  	// If value is nil, then it isn't computed
   262  	if v == nil {
   263  		return false
   264  	}
   265  
   266  	// Test if the value contains an unknown value
   267  	var w unknownCheckWalker
   268  	if err := reflectwalk.Walk(v, &w); err != nil {
   269  		panic(err)
   270  	}
   271  
   272  	return w.Unknown
   273  }
   274  
   275  // IsSet checks if the key in the configuration is set. A key is set if
   276  // it has a value or the value is being computed (is unknown currently).
   277  //
   278  // This function should be used rather than checking the keys of the
   279  // raw configuration itself, since a key may be omitted from the raw
   280  // configuration if it is being computed.
   281  func (c *ResourceConfig) IsSet(k string) bool {
   282  	if c == nil {
   283  		return false
   284  	}
   285  
   286  	if c.IsComputed(k) {
   287  		return true
   288  	}
   289  
   290  	if _, ok := c.Get(k); ok {
   291  		return true
   292  	}
   293  
   294  	return false
   295  }
   296  
   297  func (c *ResourceConfig) get(
   298  	k string, raw map[string]interface{}) (interface{}, bool) {
   299  	parts := strings.Split(k, ".")
   300  	if len(parts) == 1 && parts[0] == "" {
   301  		parts = nil
   302  	}
   303  
   304  	var current interface{} = raw
   305  	var previous interface{} = nil
   306  	for i, part := range parts {
   307  		if current == nil {
   308  			return nil, false
   309  		}
   310  
   311  		cv := reflect.ValueOf(current)
   312  		switch cv.Kind() {
   313  		case reflect.Map:
   314  			previous = current
   315  			v := cv.MapIndex(reflect.ValueOf(part))
   316  			if !v.IsValid() {
   317  				if i > 0 && i != (len(parts)-1) {
   318  					tryKey := strings.Join(parts[i:], ".")
   319  					v := cv.MapIndex(reflect.ValueOf(tryKey))
   320  					if !v.IsValid() {
   321  						return nil, false
   322  					}
   323  
   324  					return v.Interface(), true
   325  				}
   326  
   327  				return nil, false
   328  			}
   329  
   330  			current = v.Interface()
   331  		case reflect.Slice:
   332  			previous = current
   333  
   334  			if part == "#" {
   335  				// If any value in a list is computed, this whole thing
   336  				// is computed and we can't read any part of it.
   337  				for i := 0; i < cv.Len(); i++ {
   338  					if v := cv.Index(i).Interface(); v == unknownValue() {
   339  						return v, true
   340  					}
   341  				}
   342  
   343  				current = cv.Len()
   344  			} else {
   345  				i, err := strconv.ParseInt(part, 0, 0)
   346  				if err != nil {
   347  					return nil, false
   348  				}
   349  				if int(i) < 0 || int(i) >= cv.Len() {
   350  					return nil, false
   351  				}
   352  				current = cv.Index(int(i)).Interface()
   353  			}
   354  		case reflect.String:
   355  			// This happens when map keys contain "." and have a common
   356  			// prefix so were split as path components above.
   357  			actualKey := strings.Join(parts[i-1:], ".")
   358  			if prevMap, ok := previous.(map[string]interface{}); ok {
   359  				v, ok := prevMap[actualKey]
   360  				return v, ok
   361  			}
   362  
   363  			return nil, false
   364  		default:
   365  			panic(fmt.Sprintf("Unknown kind: %s", cv.Kind()))
   366  		}
   367  	}
   368  
   369  	return current, true
   370  }
   371  
   372  // interpolateForce is a temporary thing. We want to get rid of interpolate
   373  // above and likewise this, but it can only be done after the f-ast-graph
   374  // refactor is complete.
   375  func (c *ResourceConfig) interpolateForce() {
   376  	if c.raw == nil {
   377  		var err error
   378  		c.raw, err = config.NewRawConfig(make(map[string]interface{}))
   379  		if err != nil {
   380  			panic(err)
   381  		}
   382  	}
   383  
   384  	c.ComputedKeys = c.raw.UnknownKeys()
   385  	c.Raw = c.raw.RawMap()
   386  	c.Config = c.raw.Config()
   387  }
   388  
   389  // unknownCheckWalker
   390  type unknownCheckWalker struct {
   391  	Unknown bool
   392  }
   393  
   394  func (w *unknownCheckWalker) Primitive(v reflect.Value) error {
   395  	if v.Interface() == unknownValue() {
   396  		w.Unknown = true
   397  	}
   398  
   399  	return nil
   400  }