github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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  func (i *InstanceInfo) uniqueId() string {
    92  	prefix := i.HumanId()
    93  	if v := i.uniqueExtra; v != "" {
    94  		prefix += " " + v
    95  	}
    96  
    97  	return prefix
    98  }
    99  
   100  // ResourceConfig holds the configuration given for a resource. This is
   101  // done instead of a raw `map[string]interface{}` type so that rich
   102  // methods can be added to it to make dealing with it easier.
   103  type ResourceConfig struct {
   104  	ComputedKeys []string
   105  	Raw          map[string]interface{}
   106  	Config       map[string]interface{}
   107  
   108  	raw *config.RawConfig
   109  }
   110  
   111  // NewResourceConfig creates a new ResourceConfig from a config.RawConfig.
   112  func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
   113  	result := &ResourceConfig{raw: c}
   114  	result.interpolateForce()
   115  	return result
   116  }
   117  
   118  // DeepCopy performs a deep copy of the configuration. This makes it safe
   119  // to modify any of the structures that are part of the resource config without
   120  // affecting the original configuration.
   121  func (c *ResourceConfig) DeepCopy() *ResourceConfig {
   122  	// DeepCopying a nil should return a nil to avoid panics
   123  	if c == nil {
   124  		return nil
   125  	}
   126  
   127  	// Copy, this will copy all the exported attributes
   128  	copy, err := copystructure.Config{Lock: true}.Copy(c)
   129  	if err != nil {
   130  		panic(err)
   131  	}
   132  
   133  	// Force the type
   134  	result := copy.(*ResourceConfig)
   135  
   136  	// For the raw configuration, we can just use its own copy method
   137  	result.raw = c.raw.Copy()
   138  
   139  	return result
   140  }
   141  
   142  // Equal checks the equality of two resource configs.
   143  func (c *ResourceConfig) Equal(c2 *ResourceConfig) bool {
   144  	// If either are nil, then they're only equal if they're both nil
   145  	if c == nil || c2 == nil {
   146  		return c == c2
   147  	}
   148  
   149  	// Sort the computed keys so they're deterministic
   150  	sort.Strings(c.ComputedKeys)
   151  	sort.Strings(c2.ComputedKeys)
   152  
   153  	// Two resource configs if their exported properties are equal.
   154  	// We don't compare "raw" because it is never used again after
   155  	// initialization and for all intents and purposes they are equal
   156  	// if the exported properties are equal.
   157  	check := [][2]interface{}{
   158  		{c.ComputedKeys, c2.ComputedKeys},
   159  		{c.Raw, c2.Raw},
   160  		{c.Config, c2.Config},
   161  	}
   162  	for _, pair := range check {
   163  		if !reflect.DeepEqual(pair[0], pair[1]) {
   164  			return false
   165  		}
   166  	}
   167  
   168  	return true
   169  }
   170  
   171  // CheckSet checks that the given list of configuration keys is
   172  // properly set. If not, errors are returned for each unset key.
   173  //
   174  // This is useful to be called in the Validate method of a ResourceProvider.
   175  func (c *ResourceConfig) CheckSet(keys []string) []error {
   176  	var errs []error
   177  
   178  	for _, k := range keys {
   179  		if !c.IsSet(k) {
   180  			errs = append(errs, fmt.Errorf("%s must be set", k))
   181  		}
   182  	}
   183  
   184  	return errs
   185  }
   186  
   187  // Get looks up a configuration value by key and returns the value.
   188  //
   189  // The second return value is true if the get was successful. Get will
   190  // return the raw value if the key is computed, so you should pair this
   191  // with IsComputed.
   192  func (c *ResourceConfig) Get(k string) (interface{}, bool) {
   193  	// We aim to get a value from the configuration. If it is computed,
   194  	// then we return the pure raw value.
   195  	source := c.Config
   196  	if c.IsComputed(k) {
   197  		source = c.Raw
   198  	}
   199  
   200  	return c.get(k, source)
   201  }
   202  
   203  // GetRaw looks up a configuration value by key and returns the value,
   204  // from the raw, uninterpolated config.
   205  //
   206  // The second return value is true if the get was successful. Get will
   207  // not succeed if the value is being computed.
   208  func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) {
   209  	return c.get(k, c.Raw)
   210  }
   211  
   212  // IsComputed returns whether the given key is computed or not.
   213  func (c *ResourceConfig) IsComputed(k string) bool {
   214  	// The next thing we do is check the config if we get a computed
   215  	// value out of it.
   216  	v, ok := c.get(k, c.Config)
   217  	if !ok {
   218  		return false
   219  	}
   220  
   221  	// If value is nil, then it isn't computed
   222  	if v == nil {
   223  		return false
   224  	}
   225  
   226  	// Test if the value contains an unknown value
   227  	var w unknownCheckWalker
   228  	if err := reflectwalk.Walk(v, &w); err != nil {
   229  		panic(err)
   230  	}
   231  
   232  	return w.Unknown
   233  }
   234  
   235  // IsSet checks if the key in the configuration is set. A key is set if
   236  // it has a value or the value is being computed (is unknown currently).
   237  //
   238  // This function should be used rather than checking the keys of the
   239  // raw configuration itself, since a key may be omitted from the raw
   240  // configuration if it is being computed.
   241  func (c *ResourceConfig) IsSet(k string) bool {
   242  	if c == nil {
   243  		return false
   244  	}
   245  
   246  	if c.IsComputed(k) {
   247  		return true
   248  	}
   249  
   250  	if _, ok := c.Get(k); ok {
   251  		return true
   252  	}
   253  
   254  	return false
   255  }
   256  
   257  func (c *ResourceConfig) get(
   258  	k string, raw map[string]interface{}) (interface{}, bool) {
   259  	parts := strings.Split(k, ".")
   260  	if len(parts) == 1 && parts[0] == "" {
   261  		parts = nil
   262  	}
   263  
   264  	var current interface{} = raw
   265  	var previous interface{} = nil
   266  	for i, part := range parts {
   267  		if current == nil {
   268  			return nil, false
   269  		}
   270  
   271  		cv := reflect.ValueOf(current)
   272  		switch cv.Kind() {
   273  		case reflect.Map:
   274  			previous = current
   275  			v := cv.MapIndex(reflect.ValueOf(part))
   276  			if !v.IsValid() {
   277  				if i > 0 && i != (len(parts)-1) {
   278  					tryKey := strings.Join(parts[i:], ".")
   279  					v := cv.MapIndex(reflect.ValueOf(tryKey))
   280  					if !v.IsValid() {
   281  						return nil, false
   282  					}
   283  
   284  					return v.Interface(), true
   285  				}
   286  
   287  				return nil, false
   288  			}
   289  
   290  			current = v.Interface()
   291  		case reflect.Slice:
   292  			previous = current
   293  
   294  			if part == "#" {
   295  				// If any value in a list is computed, this whole thing
   296  				// is computed and we can't read any part of it.
   297  				for i := 0; i < cv.Len(); i++ {
   298  					if v := cv.Index(i).Interface(); v == unknownValue() {
   299  						return v, true
   300  					}
   301  				}
   302  
   303  				current = cv.Len()
   304  			} else {
   305  				i, err := strconv.ParseInt(part, 0, 0)
   306  				if err != nil {
   307  					return nil, false
   308  				}
   309  				if i >= int64(cv.Len()) {
   310  					return nil, false
   311  				}
   312  				current = cv.Index(int(i)).Interface()
   313  			}
   314  		case reflect.String:
   315  			// This happens when map keys contain "." and have a common
   316  			// prefix so were split as path components above.
   317  			actualKey := strings.Join(parts[i-1:], ".")
   318  			if prevMap, ok := previous.(map[string]interface{}); ok {
   319  				v, ok := prevMap[actualKey]
   320  				return v, ok
   321  			}
   322  
   323  			return nil, false
   324  		default:
   325  			panic(fmt.Sprintf("Unknown kind: %s", cv.Kind()))
   326  		}
   327  	}
   328  
   329  	return current, true
   330  }
   331  
   332  // interpolateForce is a temporary thing. We want to get rid of interpolate
   333  // above and likewise this, but it can only be done after the f-ast-graph
   334  // refactor is complete.
   335  func (c *ResourceConfig) interpolateForce() {
   336  	if c.raw == nil {
   337  		var err error
   338  		c.raw, err = config.NewRawConfig(make(map[string]interface{}))
   339  		if err != nil {
   340  			panic(err)
   341  		}
   342  	}
   343  
   344  	c.ComputedKeys = c.raw.UnknownKeys()
   345  	c.Raw = c.raw.RawMap()
   346  	c.Config = c.raw.Config()
   347  }
   348  
   349  // unknownCheckWalker
   350  type unknownCheckWalker struct {
   351  	Unknown bool
   352  }
   353  
   354  func (w *unknownCheckWalker) Primitive(v reflect.Value) error {
   355  	if v.Interface() == unknownValue() {
   356  		w.Unknown = true
   357  	}
   358  
   359  	return nil
   360  }