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