github.com/tomaszheflik/terraform@v0.7.3-0.20160827060421-32f990b41594/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  }
    46  
    47  // ResourceKind specifies what kind of instance we're working with, whether
    48  // its a primary instance, a tainted instance, or an orphan.
    49  type ResourceFlag byte
    50  
    51  // InstanceInfo is used to hold information about the instance and/or
    52  // resource being modified.
    53  type InstanceInfo struct {
    54  	// Id is a unique name to represent this instance. This is not related
    55  	// to InstanceState.ID in any way.
    56  	Id string
    57  
    58  	// ModulePath is the complete path of the module containing this
    59  	// instance.
    60  	ModulePath []string
    61  
    62  	// Type is the resource type of this instance
    63  	Type string
    64  }
    65  
    66  // HumanId is a unique Id that is human-friendly and useful for UI elements.
    67  func (i *InstanceInfo) HumanId() string {
    68  	if len(i.ModulePath) <= 1 {
    69  		return i.Id
    70  	}
    71  
    72  	return fmt.Sprintf(
    73  		"module.%s.%s",
    74  		strings.Join(i.ModulePath[1:], "."),
    75  		i.Id)
    76  }
    77  
    78  // ResourceConfig holds the configuration given for a resource. This is
    79  // done instead of a raw `map[string]interface{}` type so that rich
    80  // methods can be added to it to make dealing with it easier.
    81  type ResourceConfig struct {
    82  	ComputedKeys []string
    83  	Raw          map[string]interface{}
    84  	Config       map[string]interface{}
    85  
    86  	raw *config.RawConfig
    87  }
    88  
    89  // NewResourceConfig creates a new ResourceConfig from a config.RawConfig.
    90  func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
    91  	result := &ResourceConfig{raw: c}
    92  	result.interpolateForce()
    93  	return result
    94  }
    95  
    96  // CheckSet checks that the given list of configuration keys is
    97  // properly set. If not, errors are returned for each unset key.
    98  //
    99  // This is useful to be called in the Validate method of a ResourceProvider.
   100  func (c *ResourceConfig) CheckSet(keys []string) []error {
   101  	var errs []error
   102  
   103  	for _, k := range keys {
   104  		if !c.IsSet(k) {
   105  			errs = append(errs, fmt.Errorf("%s must be set", k))
   106  		}
   107  	}
   108  
   109  	return errs
   110  }
   111  
   112  // Get looks up a configuration value by key and returns the value.
   113  //
   114  // The second return value is true if the get was successful. Get will
   115  // not succeed if the value is being computed.
   116  func (c *ResourceConfig) Get(k string) (interface{}, bool) {
   117  	// First try to get it from c.Config since that has interpolated values
   118  	result, ok := c.get(k, c.Config)
   119  	if ok {
   120  		return result, ok
   121  	}
   122  
   123  	// Otherwise, just get it from the raw config
   124  	return c.get(k, c.Raw)
   125  }
   126  
   127  // GetRaw looks up a configuration value by key and returns the value,
   128  // from the raw, uninterpolated config.
   129  //
   130  // The second return value is true if the get was successful. Get will
   131  // not succeed if the value is being computed.
   132  func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) {
   133  	return c.get(k, c.Raw)
   134  }
   135  
   136  // IsComputed returns whether the given key is computed or not.
   137  func (c *ResourceConfig) IsComputed(k string) bool {
   138  	_, ok := c.get(k, c.Config)
   139  	_, okRaw := c.get(k, c.Raw)
   140  	return !ok && okRaw
   141  }
   142  
   143  // IsSet checks if the key in the configuration is set. A key is set if
   144  // it has a value or the value is being computed (is unknown currently).
   145  //
   146  // This function should be used rather than checking the keys of the
   147  // raw configuration itself, since a key may be omitted from the raw
   148  // configuration if it is being computed.
   149  func (c *ResourceConfig) IsSet(k string) bool {
   150  	if c == nil {
   151  		return false
   152  	}
   153  
   154  	for _, ck := range c.ComputedKeys {
   155  		if ck == k {
   156  			return true
   157  		}
   158  	}
   159  
   160  	if _, ok := c.Get(k); ok {
   161  		return true
   162  	}
   163  
   164  	return false
   165  }
   166  
   167  func (c *ResourceConfig) get(
   168  	k string, raw map[string]interface{}) (interface{}, bool) {
   169  	parts := strings.Split(k, ".")
   170  	if len(parts) == 1 && parts[0] == "" {
   171  		parts = nil
   172  	}
   173  
   174  	var current interface{} = raw
   175  	var previous interface{} = nil
   176  	for i, part := range parts {
   177  		if current == nil {
   178  			return nil, false
   179  		}
   180  
   181  		cv := reflect.ValueOf(current)
   182  		switch cv.Kind() {
   183  		case reflect.Map:
   184  			previous = current
   185  			v := cv.MapIndex(reflect.ValueOf(part))
   186  			if !v.IsValid() {
   187  				if i > 0 && i != (len(parts)-1) {
   188  					tryKey := strings.Join(parts[i:], ".")
   189  					v := cv.MapIndex(reflect.ValueOf(tryKey))
   190  					if !v.IsValid() {
   191  						return nil, false
   192  					}
   193  					return v.Interface(), true
   194  				}
   195  
   196  				return nil, false
   197  			}
   198  			current = v.Interface()
   199  		case reflect.Slice:
   200  			previous = current
   201  			if part == "#" {
   202  				current = cv.Len()
   203  			} else {
   204  				i, err := strconv.ParseInt(part, 0, 0)
   205  				if err != nil {
   206  					return nil, false
   207  				}
   208  				if i >= int64(cv.Len()) {
   209  					return nil, false
   210  				}
   211  				current = cv.Index(int(i)).Interface()
   212  			}
   213  		case reflect.String:
   214  			// This happens when map keys contain "." and have a common
   215  			// prefix so were split as path components above.
   216  			actualKey := strings.Join(parts[i-1:], ".")
   217  			if prevMap, ok := previous.(map[string]interface{}); ok {
   218  				return prevMap[actualKey], true
   219  			}
   220  			return nil, false
   221  		default:
   222  			panic(fmt.Sprintf("Unknown kind: %s", cv.Kind()))
   223  		}
   224  	}
   225  
   226  	return current, true
   227  }
   228  
   229  // interpolateForce is a temporary thing. We want to get rid of interpolate
   230  // above and likewise this, but it can only be done after the f-ast-graph
   231  // refactor is complete.
   232  func (c *ResourceConfig) interpolateForce() {
   233  	if c.raw == nil {
   234  		var err error
   235  		c.raw, err = config.NewRawConfig(make(map[string]interface{}))
   236  		if err != nil {
   237  			panic(err)
   238  		}
   239  	}
   240  
   241  	c.ComputedKeys = c.raw.UnknownKeys()
   242  	c.Raw = c.raw.Raw
   243  	c.Config = c.raw.Config()
   244  }