2014-06-19 00:35:03 +02:00
|
|
|
package terraform
|
|
|
|
|
2014-07-01 04:29:07 +02:00
|
|
|
import (
|
|
|
|
"fmt"
|
2014-07-08 01:56:23 +02:00
|
|
|
"reflect"
|
2016-10-22 21:00:05 +02:00
|
|
|
"sort"
|
2014-07-08 01:56:23 +02:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/config"
|
2016-09-28 01:09:32 +02:00
|
|
|
"github.com/mitchellh/copystructure"
|
2014-07-01 04:29:07 +02:00
|
|
|
)
|
|
|
|
|
2014-07-08 00:58:06 +02:00
|
|
|
// ResourceProvisionerConfig is used to pair a provisioner
|
2014-08-07 09:19:56 +02:00
|
|
|
// with its provided configuration. This allows us to use singleton
|
2014-07-08 00:58:06 +02:00
|
|
|
// instances of each ResourceProvisioner and to keep the relevant
|
|
|
|
// configuration instead of instantiating a new Provisioner for each
|
|
|
|
// resource.
|
|
|
|
type ResourceProvisionerConfig struct {
|
2014-07-27 18:00:34 +02:00
|
|
|
Type string
|
2014-07-08 00:58:06 +02:00
|
|
|
Provisioner ResourceProvisioner
|
|
|
|
Config *ResourceConfig
|
2014-07-08 01:21:43 +02:00
|
|
|
RawConfig *config.RawConfig
|
2014-07-11 02:15:16 +02:00
|
|
|
ConnInfo *config.RawConfig
|
2014-07-08 00:58:06 +02:00
|
|
|
}
|
|
|
|
|
2014-06-19 00:35:03 +02:00
|
|
|
// Resource encapsulates a resource, its configuration, its provider,
|
|
|
|
// its current state, and potentially a desired diff from the state it
|
|
|
|
// wants to reach.
|
|
|
|
type Resource struct {
|
2015-02-23 23:56:02 +01:00
|
|
|
// These are all used by the new EvalNode stuff.
|
|
|
|
Name string
|
|
|
|
Type string
|
|
|
|
CountIndex int
|
|
|
|
|
|
|
|
// These aren't really used anymore anywhere, but we keep them around
|
|
|
|
// since we haven't done a proper cleanup yet.
|
2014-07-08 00:24:20 +02:00
|
|
|
Id string
|
2014-09-20 07:01:51 +02:00
|
|
|
Info *InstanceInfo
|
2014-07-08 00:24:20 +02:00
|
|
|
Config *ResourceConfig
|
2014-09-23 23:20:26 +02:00
|
|
|
Dependencies []string
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff *InstanceDiff
|
2014-07-08 00:24:20 +02:00
|
|
|
Provider ResourceProvider
|
2014-09-22 07:08:21 +02:00
|
|
|
State *InstanceState
|
2014-07-08 00:58:06 +02:00
|
|
|
Provisioners []*ResourceProvisionerConfig
|
2014-09-21 02:02:31 +02:00
|
|
|
Flags ResourceFlag
|
2014-06-19 00:35:03 +02:00
|
|
|
}
|
2014-07-01 04:29:07 +02:00
|
|
|
|
2014-09-21 02:02:31 +02:00
|
|
|
// ResourceKind specifies what kind of instance we're working with, whether
|
|
|
|
// its a primary instance, a tainted instance, or an orphan.
|
|
|
|
type ResourceFlag byte
|
|
|
|
|
2014-09-25 19:40:44 +02:00
|
|
|
// InstanceInfo is used to hold information about the instance and/or
|
|
|
|
// resource being modified.
|
|
|
|
type InstanceInfo struct {
|
|
|
|
// Id is a unique name to represent this instance. This is not related
|
|
|
|
// to InstanceState.ID in any way.
|
|
|
|
Id string
|
|
|
|
|
|
|
|
// ModulePath is the complete path of the module containing this
|
|
|
|
// instance.
|
|
|
|
ModulePath []string
|
|
|
|
|
|
|
|
// Type is the resource type of this instance
|
|
|
|
Type string
|
2016-10-08 11:33:49 +02:00
|
|
|
|
|
|
|
// uniqueExtra is an internal field that can be populated to supply
|
|
|
|
// extra metadata that is used to identify a unique instance in
|
|
|
|
// the graph walk. This will be appended to HumanID when uniqueId
|
|
|
|
// is called.
|
|
|
|
uniqueExtra string
|
2014-09-25 19:40:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// HumanId is a unique Id that is human-friendly and useful for UI elements.
|
|
|
|
func (i *InstanceInfo) HumanId() string {
|
2016-09-13 23:34:15 +02:00
|
|
|
if i == nil {
|
|
|
|
return "<nil>"
|
|
|
|
}
|
|
|
|
|
2014-09-25 19:40:44 +02:00
|
|
|
if len(i.ModulePath) <= 1 {
|
|
|
|
return i.Id
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf(
|
|
|
|
"module.%s.%s",
|
|
|
|
strings.Join(i.ModulePath[1:], "."),
|
|
|
|
i.Id)
|
|
|
|
}
|
|
|
|
|
2016-10-08 11:33:49 +02:00
|
|
|
func (i *InstanceInfo) uniqueId() string {
|
|
|
|
prefix := i.HumanId()
|
|
|
|
if v := i.uniqueExtra; v != "" {
|
|
|
|
prefix += " " + v
|
|
|
|
}
|
|
|
|
|
|
|
|
return prefix
|
|
|
|
}
|
|
|
|
|
2014-07-08 01:56:23 +02:00
|
|
|
// ResourceConfig holds the configuration given for a resource. This is
|
|
|
|
// done instead of a raw `map[string]interface{}` type so that rich
|
|
|
|
// methods can be added to it to make dealing with it easier.
|
|
|
|
type ResourceConfig struct {
|
|
|
|
ComputedKeys []string
|
|
|
|
Raw map[string]interface{}
|
|
|
|
Config map[string]interface{}
|
|
|
|
|
|
|
|
raw *config.RawConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewResourceConfig creates a new ResourceConfig from a config.RawConfig.
|
|
|
|
func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
|
|
|
|
result := &ResourceConfig{raw: c}
|
2015-02-14 03:09:45 +01:00
|
|
|
result.interpolateForce()
|
2014-07-08 01:56:23 +02:00
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2016-09-28 01:09:32 +02:00
|
|
|
// DeepCopy performs a deep copy of the configuration. This makes it safe
|
|
|
|
// to modify any of the structures that are part of the resource config without
|
|
|
|
// affecting the original configuration.
|
|
|
|
func (c *ResourceConfig) DeepCopy() *ResourceConfig {
|
2016-09-30 20:29:59 +02:00
|
|
|
// DeepCopying a nil should return a nil to avoid panics
|
|
|
|
if c == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-09-28 01:09:32 +02:00
|
|
|
// Copy, this will copy all the exported attributes
|
|
|
|
copy, err := copystructure.Config{Lock: true}.Copy(c)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Force the type
|
|
|
|
result := copy.(*ResourceConfig)
|
|
|
|
|
|
|
|
// For the raw configuration, we can just use its own copy method
|
|
|
|
result.raw = c.raw.Copy()
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2016-09-28 03:52:32 +02:00
|
|
|
// Equal checks the equality of two resource configs.
|
|
|
|
func (c *ResourceConfig) Equal(c2 *ResourceConfig) bool {
|
2016-09-29 01:47:58 +02:00
|
|
|
// If either are nil, then they're only equal if they're both nil
|
|
|
|
if c == nil || c2 == nil {
|
|
|
|
return c == c2
|
|
|
|
}
|
|
|
|
|
2016-10-22 21:00:05 +02:00
|
|
|
// Sort the computed keys so they're deterministic
|
|
|
|
sort.Strings(c.ComputedKeys)
|
|
|
|
sort.Strings(c2.ComputedKeys)
|
|
|
|
|
2016-09-28 03:52:32 +02:00
|
|
|
// Two resource configs if their exported properties are equal.
|
|
|
|
// We don't compare "raw" because it is never used again after
|
|
|
|
// initialization and for all intents and purposes they are equal
|
|
|
|
// if the exported properties are equal.
|
|
|
|
check := [][2]interface{}{
|
|
|
|
{c.ComputedKeys, c2.ComputedKeys},
|
|
|
|
{c.Raw, c2.Raw},
|
|
|
|
{c.Config, c2.Config},
|
|
|
|
}
|
|
|
|
for _, pair := range check {
|
|
|
|
if !reflect.DeepEqual(pair[0], pair[1]) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2014-07-08 01:56:23 +02:00
|
|
|
// CheckSet checks that the given list of configuration keys is
|
|
|
|
// properly set. If not, errors are returned for each unset key.
|
|
|
|
//
|
|
|
|
// This is useful to be called in the Validate method of a ResourceProvider.
|
|
|
|
func (c *ResourceConfig) CheckSet(keys []string) []error {
|
|
|
|
var errs []error
|
|
|
|
|
|
|
|
for _, k := range keys {
|
|
|
|
if !c.IsSet(k) {
|
|
|
|
errs = append(errs, fmt.Errorf("%s must be set", k))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get looks up a configuration value by key and returns the value.
|
|
|
|
//
|
|
|
|
// The second return value is true if the get was successful. Get will
|
|
|
|
// not succeed if the value is being computed.
|
|
|
|
func (c *ResourceConfig) Get(k string) (interface{}, bool) {
|
2014-08-19 18:05:50 +02:00
|
|
|
// First try to get it from c.Config since that has interpolated values
|
2014-08-19 18:05:19 +02:00
|
|
|
result, ok := c.get(k, c.Config)
|
|
|
|
if ok {
|
|
|
|
return result, ok
|
|
|
|
}
|
|
|
|
|
2014-08-19 18:05:50 +02:00
|
|
|
// Otherwise, just get it from the raw config
|
2014-08-19 18:05:19 +02:00
|
|
|
return c.get(k, c.Raw)
|
|
|
|
}
|
|
|
|
|
2015-04-21 22:07:52 +02:00
|
|
|
// GetRaw looks up a configuration value by key and returns the value,
|
|
|
|
// from the raw, uninterpolated config.
|
|
|
|
//
|
|
|
|
// The second return value is true if the get was successful. Get will
|
|
|
|
// not succeed if the value is being computed.
|
|
|
|
func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) {
|
|
|
|
return c.get(k, c.Raw)
|
|
|
|
}
|
|
|
|
|
2014-10-10 04:09:06 +02:00
|
|
|
// IsComputed returns whether the given key is computed or not.
|
|
|
|
func (c *ResourceConfig) IsComputed(k string) bool {
|
2016-11-09 07:49:56 +01:00
|
|
|
// First, check for pure computed key equality since that is fast
|
|
|
|
for _, ck := range c.ComputedKeys {
|
|
|
|
if ck == k {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The next thing we do is check the config if we get a computed
|
|
|
|
// value out of it.
|
2016-10-27 06:00:24 +02:00
|
|
|
v, ok := c.get(k, c.Config)
|
2014-10-10 04:09:06 +02:00
|
|
|
_, okRaw := c.get(k, c.Raw)
|
2016-10-27 06:00:24 +02:00
|
|
|
|
|
|
|
// Both tests probably aren't needed anymore since we don't remove
|
|
|
|
// values any longer. The latter is probably good enough since we
|
|
|
|
// thread through that value now.
|
|
|
|
return (!ok && okRaw) || v == config.UnknownVariableValue
|
2014-10-10 04:09:06 +02:00
|
|
|
}
|
|
|
|
|
2014-08-19 18:05:19 +02:00
|
|
|
// IsSet checks if the key in the configuration is set. A key is set if
|
|
|
|
// it has a value or the value is being computed (is unknown currently).
|
|
|
|
//
|
|
|
|
// This function should be used rather than checking the keys of the
|
|
|
|
// raw configuration itself, since a key may be omitted from the raw
|
|
|
|
// configuration if it is being computed.
|
|
|
|
func (c *ResourceConfig) IsSet(k string) bool {
|
|
|
|
if c == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, ck := range c.ComputedKeys {
|
|
|
|
if ck == k {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := c.Get(k); ok {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ResourceConfig) get(
|
|
|
|
k string, raw map[string]interface{}) (interface{}, bool) {
|
2014-07-08 01:56:23 +02:00
|
|
|
parts := strings.Split(k, ".")
|
2015-02-18 18:41:55 +01:00
|
|
|
if len(parts) == 1 && parts[0] == "" {
|
|
|
|
parts = nil
|
|
|
|
}
|
2014-07-08 01:56:23 +02:00
|
|
|
|
2014-08-19 18:05:19 +02:00
|
|
|
var current interface{} = raw
|
2016-07-14 20:01:45 +02:00
|
|
|
var previous interface{} = nil
|
|
|
|
for i, part := range parts {
|
2016-11-09 07:49:56 +01:00
|
|
|
println(fmt.Sprintf("%#v: %#v %T", part, current, current))
|
2014-07-08 01:56:23 +02:00
|
|
|
if current == nil {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
cv := reflect.ValueOf(current)
|
|
|
|
switch cv.Kind() {
|
|
|
|
case reflect.Map:
|
2016-07-14 20:01:45 +02:00
|
|
|
previous = current
|
2014-07-08 01:56:23 +02:00
|
|
|
v := cv.MapIndex(reflect.ValueOf(part))
|
|
|
|
if !v.IsValid() {
|
2016-07-14 20:01:45 +02:00
|
|
|
if i > 0 && i != (len(parts)-1) {
|
|
|
|
tryKey := strings.Join(parts[i:], ".")
|
|
|
|
v := cv.MapIndex(reflect.ValueOf(tryKey))
|
|
|
|
if !v.IsValid() {
|
|
|
|
return nil, false
|
|
|
|
}
|
2016-11-09 07:49:56 +01:00
|
|
|
|
2016-07-14 20:01:45 +02:00
|
|
|
return v.Interface(), true
|
|
|
|
}
|
|
|
|
|
2014-07-08 01:56:23 +02:00
|
|
|
return nil, false
|
|
|
|
}
|
2016-11-09 04:33:52 +01:00
|
|
|
|
2014-07-08 01:56:23 +02:00
|
|
|
current = v.Interface()
|
|
|
|
case reflect.Slice:
|
2016-07-14 20:01:45 +02:00
|
|
|
previous = current
|
2016-11-09 03:01:06 +01:00
|
|
|
|
|
|
|
// If any value in a list is computed, this whole thing
|
|
|
|
// is computed and we can't read any part of it.
|
|
|
|
for i := 0; i < cv.Len(); i++ {
|
|
|
|
if v := cv.Index(i).Interface(); v == unknownValue() {
|
|
|
|
return v, false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-15 08:17:53 +02:00
|
|
|
if part == "#" {
|
|
|
|
current = cv.Len()
|
|
|
|
} else {
|
|
|
|
i, err := strconv.ParseInt(part, 0, 0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, false
|
|
|
|
}
|
2014-08-21 20:37:14 +02:00
|
|
|
if i >= int64(cv.Len()) {
|
|
|
|
return nil, false
|
|
|
|
}
|
2014-08-15 08:17:53 +02:00
|
|
|
current = cv.Index(int(i)).Interface()
|
2014-07-08 01:56:23 +02:00
|
|
|
}
|
2016-07-14 20:01:45 +02:00
|
|
|
case reflect.String:
|
2016-11-09 07:49:56 +01:00
|
|
|
// If we get the unknown value, return that
|
|
|
|
if current == unknownValue() {
|
|
|
|
return current, false
|
|
|
|
}
|
|
|
|
|
2016-07-14 20:01:45 +02:00
|
|
|
// This happens when map keys contain "." and have a common
|
|
|
|
// prefix so were split as path components above.
|
|
|
|
actualKey := strings.Join(parts[i-1:], ".")
|
|
|
|
if prevMap, ok := previous.(map[string]interface{}); ok {
|
|
|
|
return prevMap[actualKey], true
|
|
|
|
}
|
2016-11-09 07:49:56 +01:00
|
|
|
|
2016-07-14 20:01:45 +02:00
|
|
|
return nil, false
|
2014-07-08 01:56:23 +02:00
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("Unknown kind: %s", cv.Kind()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-09 07:49:56 +01:00
|
|
|
switch cv := reflect.ValueOf(current); cv.Kind() {
|
|
|
|
case reflect.Slice:
|
|
|
|
// If any value in a list is computed, this whole thing
|
|
|
|
// is computed and we can't read any part of it.
|
|
|
|
for i := 0; i < cv.Len(); i++ {
|
|
|
|
if v := cv.Index(i).Interface(); v == unknownValue() {
|
|
|
|
return v, false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
// If the value is just the unknown value, then we don't
|
|
|
|
// know anything beyond here.
|
|
|
|
if current == unknownValue() {
|
|
|
|
return current, false
|
|
|
|
}
|
2016-11-09 04:33:52 +01:00
|
|
|
}
|
|
|
|
|
2014-07-08 01:56:23 +02:00
|
|
|
return current, true
|
|
|
|
}
|
|
|
|
|
2015-02-14 03:09:45 +01:00
|
|
|
// interpolateForce is a temporary thing. We want to get rid of interpolate
|
|
|
|
// above and likewise this, but it can only be done after the f-ast-graph
|
|
|
|
// refactor is complete.
|
|
|
|
func (c *ResourceConfig) interpolateForce() {
|
2014-09-29 18:13:15 +02:00
|
|
|
if c.raw == nil {
|
|
|
|
var err error
|
|
|
|
c.raw, err = config.NewRawConfig(make(map[string]interface{}))
|
|
|
|
if err != nil {
|
2015-02-14 03:09:45 +01:00
|
|
|
panic(err)
|
2014-09-29 18:13:15 +02:00
|
|
|
}
|
2014-07-09 01:16:55 +02:00
|
|
|
}
|
|
|
|
|
2014-09-29 18:13:15 +02:00
|
|
|
c.ComputedKeys = c.raw.UnknownKeys()
|
2016-08-30 20:16:37 +02:00
|
|
|
c.Raw = c.raw.RawMap()
|
2014-09-29 18:13:15 +02:00
|
|
|
c.Config = c.raw.Config()
|
2015-02-04 17:30:53 +01:00
|
|
|
}
|