aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/helper
diff options
context:
space:
mode:
authorJake Champlin <jake.champlin.27@gmail.com>2017-06-06 12:40:07 -0400
committerJake Champlin <jake.champlin.27@gmail.com>2017-06-06 12:40:07 -0400
commitbae9f6d2fd5eb5bc80929bd393932b23f14d7c93 (patch)
treeca9ab12a7d78b1fc27a8f734729081357ce6d252 /vendor/github.com/hashicorp/terraform/helper
parent254c495b6bebab3fb72a243c4bce858d79e6ee99 (diff)
downloadterraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.gz
terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.zst
terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.zip
Initial transfer of provider code
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/helper')
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/acctest/acctest.go2
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/acctest/random.go93
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/acctest/remotetests.go27
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/config/decode.go28
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/config/validator.go214
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/experiment/experiment.go154
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/experiment/id.go34
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode.go22
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/hilmapstructure/hilmapstructure.go41
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/logging/logging.go100
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/logging/transport.go53
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/error.go79
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/id.go39
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/map.go140
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/resource.go49
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/state.go259
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/testing.go790
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go160
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go141
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/wait.go84
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/README.md11
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/backend.go94
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go59
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/equal.go6
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go334
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go333
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go208
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go232
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_reader_multi.go63
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_writer.go8
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go319
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go36
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/provider.go400
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go180
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/resource.go478
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go502
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/resource_data_get_source.go17
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/resource_importer.go52
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go237
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/schema.go1537
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/serialize.go125
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/set.go209
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/testing.go30
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/valuetype.go21
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go16
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/shadow/closer.go80
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go128
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/shadow/keyed_value.go151
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/shadow/ordered_value.go66
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/shadow/value.go79
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/structure/expand_json.go11
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/structure/flatten_json.go16
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/structure/normalize_json.go24
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/structure/suppress_json_diff.go21
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/validation/validation.go108
55 files changed, 8700 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/helper/acctest/acctest.go b/vendor/github.com/hashicorp/terraform/helper/acctest/acctest.go
new file mode 100644
index 0000000..9d31031
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/acctest/acctest.go
@@ -0,0 +1,2 @@
1// Package acctest contains for Terraform Acceptance Tests
2package acctest
diff --git a/vendor/github.com/hashicorp/terraform/helper/acctest/random.go b/vendor/github.com/hashicorp/terraform/helper/acctest/random.go
new file mode 100644
index 0000000..3ddc078
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/acctest/random.go
@@ -0,0 +1,93 @@
1package acctest
2
3import (
4 "bufio"
5 "bytes"
6 crand "crypto/rand"
7 "crypto/rsa"
8 "crypto/x509"
9 "encoding/pem"
10 "fmt"
11 "math/rand"
12 "strings"
13 "time"
14
15 "golang.org/x/crypto/ssh"
16)
17
18// Helpers for generating random tidbits for use in identifiers to prevent
19// collisions in acceptance tests.
20
21// RandInt generates a random integer
22func RandInt() int {
23 reseed()
24 return rand.New(rand.NewSource(time.Now().UnixNano())).Int()
25}
26
27// RandomWithPrefix is used to generate a unique name with a prefix, for
28// randomizing names in acceptance tests
29func RandomWithPrefix(name string) string {
30 reseed()
31 return fmt.Sprintf("%s-%d", name, rand.New(rand.NewSource(time.Now().UnixNano())).Int())
32}
33
34func RandIntRange(min int, max int) int {
35 reseed()
36 source := rand.New(rand.NewSource(time.Now().UnixNano()))
37 rangeMax := max - min
38
39 return int(source.Int31n(int32(rangeMax)))
40}
41
42// RandString generates a random alphanumeric string of the length specified
43func RandString(strlen int) string {
44 return RandStringFromCharSet(strlen, CharSetAlphaNum)
45}
46
47// RandStringFromCharSet generates a random string by selecting characters from
48// the charset provided
49func RandStringFromCharSet(strlen int, charSet string) string {
50 reseed()
51 result := make([]byte, strlen)
52 for i := 0; i < strlen; i++ {
53 result[i] = charSet[rand.Intn(len(charSet))]
54 }
55 return string(result)
56}
57
58// RandSSHKeyPair generates a public and private SSH key pair. The public key is
59// returned in OpenSSH format, and the private key is PEM encoded.
60func RandSSHKeyPair(comment string) (string, string, error) {
61 privateKey, err := rsa.GenerateKey(crand.Reader, 1024)
62 if err != nil {
63 return "", "", err
64 }
65
66 var privateKeyBuffer bytes.Buffer
67 privateKeyPEM := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}
68 if err := pem.Encode(bufio.NewWriter(&privateKeyBuffer), privateKeyPEM); err != nil {
69 return "", "", err
70 }
71
72 publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey)
73 if err != nil {
74 return "", "", err
75 }
76 keyMaterial := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(publicKey)))
77 return fmt.Sprintf("%s %s", keyMaterial, comment), privateKeyBuffer.String(), nil
78}
79
80// Seeds random with current timestamp
81func reseed() {
82 rand.Seed(time.Now().UTC().UnixNano())
83}
84
85const (
86 // CharSetAlphaNum is the alphanumeric character set for use with
87 // RandStringFromCharSet
88 CharSetAlphaNum = "abcdefghijklmnopqrstuvwxyz012346789"
89
90 // CharSetAlpha is the alphabetical character set for use with
91 // RandStringFromCharSet
92 CharSetAlpha = "abcdefghijklmnopqrstuvwxyz"
93)
diff --git a/vendor/github.com/hashicorp/terraform/helper/acctest/remotetests.go b/vendor/github.com/hashicorp/terraform/helper/acctest/remotetests.go
new file mode 100644
index 0000000..87c60b8
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/acctest/remotetests.go
@@ -0,0 +1,27 @@
1package acctest
2
3import (
4 "net/http"
5 "os"
6 "testing"
7)
8
9// SkipRemoteTestsEnvVar is an environment variable that can be set by a user
10// running the tests in an environment with limited network connectivity. By
11// default, tests requiring internet connectivity make an effort to skip if no
12// internet is available, but in some cases the smoke test will pass even
13// though the test should still be skipped.
14const SkipRemoteTestsEnvVar = "TF_SKIP_REMOTE_TESTS"
15
16// RemoteTestPrecheck is meant to be run by any unit test that requires
17// outbound internet connectivity. The test will be skipped if it's
18// unavailable.
19func RemoteTestPrecheck(t *testing.T) {
20 if os.Getenv(SkipRemoteTestsEnvVar) != "" {
21 t.Skipf("skipping test, %s was set", SkipRemoteTestsEnvVar)
22 }
23
24 if _, err := http.Get("http://google.com"); err != nil {
25 t.Skipf("skipping, internet seems to not be available: %s", err)
26 }
27}
diff --git a/vendor/github.com/hashicorp/terraform/helper/config/decode.go b/vendor/github.com/hashicorp/terraform/helper/config/decode.go
new file mode 100644
index 0000000..f470c9b
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/config/decode.go
@@ -0,0 +1,28 @@
1package config
2
3import (
4 "github.com/mitchellh/mapstructure"
5)
6
7func Decode(target interface{}, raws ...interface{}) (*mapstructure.Metadata, error) {
8 var md mapstructure.Metadata
9 decoderConfig := &mapstructure.DecoderConfig{
10 Metadata: &md,
11 Result: target,
12 WeaklyTypedInput: true,
13 }
14
15 decoder, err := mapstructure.NewDecoder(decoderConfig)
16 if err != nil {
17 return nil, err
18 }
19
20 for _, raw := range raws {
21 err := decoder.Decode(raw)
22 if err != nil {
23 return nil, err
24 }
25 }
26
27 return &md, nil
28}
diff --git a/vendor/github.com/hashicorp/terraform/helper/config/validator.go b/vendor/github.com/hashicorp/terraform/helper/config/validator.go
new file mode 100644
index 0000000..1a6e023
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/config/validator.go
@@ -0,0 +1,214 @@
1package config
2
3import (
4 "fmt"
5 "strconv"
6 "strings"
7
8 "github.com/hashicorp/terraform/flatmap"
9 "github.com/hashicorp/terraform/terraform"
10)
11
12// Validator is a helper that helps you validate the configuration
13// of your resource, resource provider, etc.
14//
15// At the most basic level, set the Required and Optional lists to be
16// specifiers of keys that are required or optional. If a key shows up
17// that isn't in one of these two lists, then an error is generated.
18//
19// The "specifiers" allowed in this is a fairly rich syntax to help
20// describe the format of your configuration:
21//
22// * Basic keys are just strings. For example: "foo" will match the
23// "foo" key.
24//
25// * Nested structure keys can be matched by doing
26// "listener.*.foo". This will verify that there is at least one
27// listener element that has the "foo" key set.
28//
29// * The existence of a nested structure can be checked by simply
30// doing "listener.*" which will verify that there is at least
31// one element in the "listener" structure. This is NOT
32// validating that "listener" is an array. It is validating
33// that it is a nested structure in the configuration.
34//
35type Validator struct {
36 Required []string
37 Optional []string
38}
39
40func (v *Validator) Validate(
41 c *terraform.ResourceConfig) (ws []string, es []error) {
42 // Flatten the configuration so it is easier to reason about
43 flat := flatmap.Flatten(c.Raw)
44
45 keySet := make(map[string]validatorKey)
46 for i, vs := range [][]string{v.Required, v.Optional} {
47 req := i == 0
48 for _, k := range vs {
49 vk, err := newValidatorKey(k, req)
50 if err != nil {
51 es = append(es, err)
52 continue
53 }
54
55 keySet[k] = vk
56 }
57 }
58
59 purged := make([]string, 0)
60 for _, kv := range keySet {
61 p, w, e := kv.Validate(flat)
62 if len(w) > 0 {
63 ws = append(ws, w...)
64 }
65 if len(e) > 0 {
66 es = append(es, e...)
67 }
68
69 purged = append(purged, p...)
70 }
71
72 // Delete all the keys we processed in order to find
73 // the unknown keys.
74 for _, p := range purged {
75 delete(flat, p)
76 }
77
78 // The rest are unknown
79 for k, _ := range flat {
80 es = append(es, fmt.Errorf("Unknown configuration: %s", k))
81 }
82
83 return
84}
85
86type validatorKey interface {
87 // Validate validates the given configuration and returns viewed keys,
88 // warnings, and errors.
89 Validate(map[string]string) ([]string, []string, []error)
90}
91
92func newValidatorKey(k string, req bool) (validatorKey, error) {
93 var result validatorKey
94
95 parts := strings.Split(k, ".")
96 if len(parts) > 1 && parts[1] == "*" {
97 result = &nestedValidatorKey{
98 Parts: parts,
99 Required: req,
100 }
101 } else {
102 result = &basicValidatorKey{
103 Key: k,
104 Required: req,
105 }
106 }
107
108 return result, nil
109}
110
111// basicValidatorKey validates keys that are basic such as "foo"
112type basicValidatorKey struct {
113 Key string
114 Required bool
115}
116
117func (v *basicValidatorKey) Validate(
118 m map[string]string) ([]string, []string, []error) {
119 for k, _ := range m {
120 // If we have the exact key its a match
121 if k == v.Key {
122 return []string{k}, nil, nil
123 }
124 }
125
126 if !v.Required {
127 return nil, nil, nil
128 }
129
130 return nil, nil, []error{fmt.Errorf(
131 "Key not found: %s", v.Key)}
132}
133
134type nestedValidatorKey struct {
135 Parts []string
136 Required bool
137}
138
139func (v *nestedValidatorKey) validate(
140 m map[string]string,
141 prefix string,
142 offset int) ([]string, []string, []error) {
143 if offset >= len(v.Parts) {
144 // We're at the end. Look for a specific key.
145 v2 := &basicValidatorKey{Key: prefix, Required: v.Required}
146 return v2.Validate(m)
147 }
148
149 current := v.Parts[offset]
150
151 // If we're at offset 0, special case to start at the next one.
152 if offset == 0 {
153 return v.validate(m, current, offset+1)
154 }
155
156 // Determine if we're doing a "for all" or a specific key
157 if current != "*" {
158 // We're looking at a specific key, continue on.
159 return v.validate(m, prefix+"."+current, offset+1)
160 }
161
162 // We're doing a "for all", so we loop over.
163 countStr, ok := m[prefix+".#"]
164 if !ok {
165 if !v.Required {
166 // It wasn't required, so its no problem.
167 return nil, nil, nil
168 }
169
170 return nil, nil, []error{fmt.Errorf(
171 "Key not found: %s", prefix)}
172 }
173
174 count, err := strconv.ParseInt(countStr, 0, 0)
175 if err != nil {
176 // This shouldn't happen if flatmap works properly
177 panic("invalid flatmap array")
178 }
179
180 var e []error
181 var w []string
182 u := make([]string, 1, count+1)
183 u[0] = prefix + ".#"
184 for i := 0; i < int(count); i++ {
185 prefix := fmt.Sprintf("%s.%d", prefix, i)
186
187 // Mark that we saw this specific key
188 u = append(u, prefix)
189
190 // Mark all prefixes of this
191 for k, _ := range m {
192 if !strings.HasPrefix(k, prefix+".") {
193 continue
194 }
195 u = append(u, k)
196 }
197
198 // If we have more parts, then validate deeper
199 if offset+1 < len(v.Parts) {
200 u2, w2, e2 := v.validate(m, prefix, offset+1)
201
202 u = append(u, u2...)
203 w = append(w, w2...)
204 e = append(e, e2...)
205 }
206 }
207
208 return u, w, e
209}
210
211func (v *nestedValidatorKey) Validate(
212 m map[string]string) ([]string, []string, []error) {
213 return v.validate(m, "", 0)
214}
diff --git a/vendor/github.com/hashicorp/terraform/helper/experiment/experiment.go b/vendor/github.com/hashicorp/terraform/helper/experiment/experiment.go
new file mode 100644
index 0000000..18b8837
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/experiment/experiment.go
@@ -0,0 +1,154 @@
1// experiment package contains helper functions for tracking experimental
2// features throughout Terraform.
3//
4// This package should be used for creating, enabling, querying, and deleting
5// experimental features. By unifying all of that onto a single interface,
6// we can have the Go compiler help us by enforcing every place we touch
7// an experimental feature.
8//
9// To create a new experiment:
10//
11// 1. Add the experiment to the global vars list below, prefixed with X_
12//
13// 2. Add the experiment variable to the All listin the init() function
14//
15// 3. Use it!
16//
17// To remove an experiment:
18//
19// 1. Delete the experiment global var.
20//
21// 2. Try to compile and fix all the places where the var was referenced.
22//
23// To use an experiment:
24//
25// 1. Use Flag() if you want the experiment to be available from the CLI.
26//
27// 2. Use Enabled() to check whether it is enabled.
28//
29// As a general user:
30//
31// 1. The `-Xexperiment-name` flag
32// 2. The `TF_X_<experiment-name>` env var.
33// 3. The `TF_X_FORCE` env var can be set to force an experimental feature
34// without human verifications.
35//
36package experiment
37
38import (
39 "flag"
40 "fmt"
41 "os"
42 "strconv"
43 "strings"
44 "sync"
45)
46
47// The experiments that are available are listed below. Any package in
48// Terraform defining an experiment should define the experiments below.
49// By keeping them all within the experiment package we force a single point
50// of definition and use. This allows the compiler to enforce references
51// so it becomes easy to remove the features.
52var (
53 // Shadow graph. This is already on by default. Disabling it will be
54 // allowed for awhile in order for it to not block operations.
55 X_shadow = newBasicID("shadow", "SHADOW", false)
56)
57
58// Global variables this package uses because we are a package
59// with global state.
60var (
61 // all is the list of all experiements. Do not modify this.
62 All []ID
63
64 // enabled keeps track of what flags have been enabled
65 enabled map[string]bool
66 enabledLock sync.Mutex
67
68 // Hidden "experiment" that forces all others to be on without verification
69 x_force = newBasicID("force", "FORCE", false)
70)
71
72func init() {
73 // The list of all experiments, update this when an experiment is added.
74 All = []ID{
75 X_shadow,
76 x_force,
77 }
78
79 // Load
80 reload()
81}
82
83// reload is used by tests to reload the global state. This is called by
84// init publicly.
85func reload() {
86 // Initialize
87 enabledLock.Lock()
88 enabled = make(map[string]bool)
89 enabledLock.Unlock()
90
91 // Set defaults and check env vars
92 for _, id := range All {
93 // Get the default value
94 def := id.Default()
95
96 // If we set it in the env var, default it to true
97 key := fmt.Sprintf("TF_X_%s", strings.ToUpper(id.Env()))
98 if v := os.Getenv(key); v != "" {
99 def = v != "0"
100 }
101
102 // Set the default
103 SetEnabled(id, def)
104 }
105}
106
107// Enabled returns whether an experiment has been enabled or not.
108func Enabled(id ID) bool {
109 enabledLock.Lock()
110 defer enabledLock.Unlock()
111 return enabled[id.Flag()]
112}
113
114// SetEnabled sets an experiment to enabled/disabled. Please check with
115// the experiment docs for when calling this actually affects the experiment.
116func SetEnabled(id ID, v bool) {
117 enabledLock.Lock()
118 defer enabledLock.Unlock()
119 enabled[id.Flag()] = v
120}
121
122// Force returns true if the -Xforce of TF_X_FORCE flag is present, which
123// advises users of this package to not verify with the user that they want
124// experimental behavior and to just continue with it.
125func Force() bool {
126 return Enabled(x_force)
127}
128
129// Flag configures the given FlagSet with the flags to configure
130// all active experiments.
131func Flag(fs *flag.FlagSet) {
132 for _, id := range All {
133 desc := id.Flag()
134 key := fmt.Sprintf("X%s", id.Flag())
135 fs.Var(&idValue{X: id}, key, desc)
136 }
137}
138
139// idValue implements flag.Value for setting the enabled/disabled state
140// of an experiment from the CLI.
141type idValue struct {
142 X ID
143}
144
145func (v *idValue) IsBoolFlag() bool { return true }
146func (v *idValue) String() string { return strconv.FormatBool(Enabled(v.X)) }
147func (v *idValue) Set(raw string) error {
148 b, err := strconv.ParseBool(raw)
149 if err == nil {
150 SetEnabled(v.X, b)
151 }
152
153 return err
154}
diff --git a/vendor/github.com/hashicorp/terraform/helper/experiment/id.go b/vendor/github.com/hashicorp/terraform/helper/experiment/id.go
new file mode 100644
index 0000000..8e2f707
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/experiment/id.go
@@ -0,0 +1,34 @@
1package experiment
2
3// ID represents an experimental feature.
4//
5// The global vars defined on this package should be used as ID values.
6// This interface is purposely not implement-able outside of this package
7// so that we can rely on the Go compiler to enforce all experiment references.
8type ID interface {
9 Env() string
10 Flag() string
11 Default() bool
12
13 unexported() // So the ID can't be implemented externally.
14}
15
16// basicID implements ID.
17type basicID struct {
18 EnvValue string
19 FlagValue string
20 DefaultValue bool
21}
22
23func newBasicID(flag, env string, def bool) ID {
24 return &basicID{
25 EnvValue: env,
26 FlagValue: flag,
27 DefaultValue: def,
28 }
29}
30
31func (id *basicID) Env() string { return id.EnvValue }
32func (id *basicID) Flag() string { return id.FlagValue }
33func (id *basicID) Default() bool { return id.DefaultValue }
34func (id *basicID) unexported() {}
diff --git a/vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode.go b/vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode.go
new file mode 100644
index 0000000..64d8263
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode.go
@@ -0,0 +1,22 @@
1package hashcode
2
3import (
4 "hash/crc32"
5)
6
7// String hashes a string to a unique hashcode.
8//
9// crc32 returns a uint32, but for our use we need
10// and non negative integer. Here we cast to an integer
11// and invert it if the result is negative.
12func String(s string) int {
13 v := int(crc32.ChecksumIEEE([]byte(s)))
14 if v >= 0 {
15 return v
16 }
17 if -v >= 0 {
18 return -v
19 }
20 // v == MinInt
21 return 0
22}
diff --git a/vendor/github.com/hashicorp/terraform/helper/hilmapstructure/hilmapstructure.go b/vendor/github.com/hashicorp/terraform/helper/hilmapstructure/hilmapstructure.go
new file mode 100644
index 0000000..67be1df
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/hilmapstructure/hilmapstructure.go
@@ -0,0 +1,41 @@
1package hilmapstructure
2
3import (
4 "fmt"
5 "reflect"
6
7 "github.com/mitchellh/mapstructure"
8)
9
10var hilMapstructureDecodeHookEmptySlice []interface{}
11var hilMapstructureDecodeHookStringSlice []string
12var hilMapstructureDecodeHookEmptyMap map[string]interface{}
13
14// WeakDecode behaves in the same way as mapstructure.WeakDecode but has a
15// DecodeHook which defeats the backward compatibility mode of mapstructure
16// which WeakDecodes []interface{}{} into an empty map[string]interface{}. This
17// allows us to use WeakDecode (desirable), but not fail on empty lists.
18func WeakDecode(m interface{}, rawVal interface{}) error {
19 config := &mapstructure.DecoderConfig{
20 DecodeHook: func(source reflect.Type, target reflect.Type, val interface{}) (interface{}, error) {
21 sliceType := reflect.TypeOf(hilMapstructureDecodeHookEmptySlice)
22 stringSliceType := reflect.TypeOf(hilMapstructureDecodeHookStringSlice)
23 mapType := reflect.TypeOf(hilMapstructureDecodeHookEmptyMap)
24
25 if (source == sliceType || source == stringSliceType) && target == mapType {
26 return nil, fmt.Errorf("Cannot convert a []interface{} into a map[string]interface{}")
27 }
28
29 return val, nil
30 },
31 WeaklyTypedInput: true,
32 Result: rawVal,
33 }
34
35 decoder, err := mapstructure.NewDecoder(config)
36 if err != nil {
37 return err
38 }
39
40 return decoder.Decode(m)
41}
diff --git a/vendor/github.com/hashicorp/terraform/helper/logging/logging.go b/vendor/github.com/hashicorp/terraform/helper/logging/logging.go
new file mode 100644
index 0000000..433cd77
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/logging/logging.go
@@ -0,0 +1,100 @@
1package logging
2
3import (
4 "io"
5 "io/ioutil"
6 "log"
7 "os"
8 "strings"
9 "syscall"
10
11 "github.com/hashicorp/logutils"
12)
13
14// These are the environmental variables that determine if we log, and if
15// we log whether or not the log should go to a file.
16const (
17 EnvLog = "TF_LOG" // Set to True
18 EnvLogFile = "TF_LOG_PATH" // Set to a file
19)
20
21var validLevels = []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARN", "ERROR"}
22
23// LogOutput determines where we should send logs (if anywhere) and the log level.
24func LogOutput() (logOutput io.Writer, err error) {
25 logOutput = ioutil.Discard
26
27 logLevel := LogLevel()
28 if logLevel == "" {
29 return
30 }
31
32 logOutput = os.Stderr
33 if logPath := os.Getenv(EnvLogFile); logPath != "" {
34 var err error
35 logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666)
36 if err != nil {
37 return nil, err
38 }
39 }
40
41 // This was the default since the beginning
42 logOutput = &logutils.LevelFilter{
43 Levels: validLevels,
44 MinLevel: logutils.LogLevel(logLevel),
45 Writer: logOutput,
46 }
47
48 return
49}
50
51// SetOutput checks for a log destination with LogOutput, and calls
52// log.SetOutput with the result. If LogOutput returns nil, SetOutput uses
53// ioutil.Discard. Any error from LogOutout is fatal.
54func SetOutput() {
55 out, err := LogOutput()
56 if err != nil {
57 log.Fatal(err)
58 }
59
60 if out == nil {
61 out = ioutil.Discard
62 }
63
64 log.SetOutput(out)
65}
66
67// LogLevel returns the current log level string based the environment vars
68func LogLevel() string {
69 envLevel := os.Getenv(EnvLog)
70 if envLevel == "" {
71 return ""
72 }
73
74 logLevel := "TRACE"
75 if isValidLogLevel(envLevel) {
76 // allow following for better ux: info, Info or INFO
77 logLevel = strings.ToUpper(envLevel)
78 } else {
79 log.Printf("[WARN] Invalid log level: %q. Defaulting to level: TRACE. Valid levels are: %+v",
80 envLevel, validLevels)
81 }
82
83 return logLevel
84}
85
86// IsDebugOrHigher returns whether or not the current log level is debug or trace
87func IsDebugOrHigher() bool {
88 level := string(LogLevel())
89 return level == "DEBUG" || level == "TRACE"
90}
91
92func isValidLogLevel(level string) bool {
93 for _, l := range validLevels {
94 if strings.ToUpper(level) == string(l) {
95 return true
96 }
97 }
98
99 return false
100}
diff --git a/vendor/github.com/hashicorp/terraform/helper/logging/transport.go b/vendor/github.com/hashicorp/terraform/helper/logging/transport.go
new file mode 100644
index 0000000..4477924
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/logging/transport.go
@@ -0,0 +1,53 @@
1package logging
2
3import (
4 "log"
5 "net/http"
6 "net/http/httputil"
7)
8
9type transport struct {
10 name string
11 transport http.RoundTripper
12}
13
14func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
15 if IsDebugOrHigher() {
16 reqData, err := httputil.DumpRequestOut(req, true)
17 if err == nil {
18 log.Printf("[DEBUG] "+logReqMsg, t.name, string(reqData))
19 } else {
20 log.Printf("[ERROR] %s API Request error: %#v", t.name, err)
21 }
22 }
23
24 resp, err := t.transport.RoundTrip(req)
25 if err != nil {
26 return resp, err
27 }
28
29 if IsDebugOrHigher() {
30 respData, err := httputil.DumpResponse(resp, true)
31 if err == nil {
32 log.Printf("[DEBUG] "+logRespMsg, t.name, string(respData))
33 } else {
34 log.Printf("[ERROR] %s API Response error: %#v", t.name, err)
35 }
36 }
37
38 return resp, nil
39}
40
41func NewTransport(name string, t http.RoundTripper) *transport {
42 return &transport{name, t}
43}
44
45const logReqMsg = `%s API Request Details:
46---[ REQUEST ]---------------------------------------
47%s
48-----------------------------------------------------`
49
50const logRespMsg = `%s API Response Details:
51---[ RESPONSE ]--------------------------------------
52%s
53-----------------------------------------------------`
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/error.go b/vendor/github.com/hashicorp/terraform/helper/resource/error.go
new file mode 100644
index 0000000..7ee2161
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/error.go
@@ -0,0 +1,79 @@
1package resource
2
3import (
4 "fmt"
5 "strings"
6 "time"
7)
8
9type NotFoundError struct {
10 LastError error
11 LastRequest interface{}
12 LastResponse interface{}
13 Message string
14 Retries int
15}
16
17func (e *NotFoundError) Error() string {
18 if e.Message != "" {
19 return e.Message
20 }
21
22 if e.Retries > 0 {
23 return fmt.Sprintf("couldn't find resource (%d retries)", e.Retries)
24 }
25
26 return "couldn't find resource"
27}
28
29// UnexpectedStateError is returned when Refresh returns a state that's neither in Target nor Pending
30type UnexpectedStateError struct {
31 LastError error
32 State string
33 ExpectedState []string
34}
35
36func (e *UnexpectedStateError) Error() string {
37 return fmt.Sprintf(
38 "unexpected state '%s', wanted target '%s'. last error: %s",
39 e.State,
40 strings.Join(e.ExpectedState, ", "),
41 e.LastError,
42 )
43}
44
45// TimeoutError is returned when WaitForState times out
46type TimeoutError struct {
47 LastError error
48 LastState string
49 Timeout time.Duration
50 ExpectedState []string
51}
52
53func (e *TimeoutError) Error() string {
54 expectedState := "resource to be gone"
55 if len(e.ExpectedState) > 0 {
56 expectedState = fmt.Sprintf("state to become '%s'", strings.Join(e.ExpectedState, ", "))
57 }
58
59 extraInfo := make([]string, 0)
60 if e.LastState != "" {
61 extraInfo = append(extraInfo, fmt.Sprintf("last state: '%s'", e.LastState))
62 }
63 if e.Timeout > 0 {
64 extraInfo = append(extraInfo, fmt.Sprintf("timeout: %s", e.Timeout.String()))
65 }
66
67 suffix := ""
68 if len(extraInfo) > 0 {
69 suffix = fmt.Sprintf(" (%s)", strings.Join(extraInfo, ", "))
70 }
71
72 if e.LastError != nil {
73 return fmt.Sprintf("timeout while waiting for %s%s: %s",
74 expectedState, suffix, e.LastError)
75 }
76
77 return fmt.Sprintf("timeout while waiting for %s%s",
78 expectedState, suffix)
79}
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/id.go b/vendor/github.com/hashicorp/terraform/helper/resource/id.go
new file mode 100644
index 0000000..629582b
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/id.go
@@ -0,0 +1,39 @@
1package resource
2
3import (
4 "crypto/rand"
5 "fmt"
6 "math/big"
7 "sync"
8)
9
10const UniqueIdPrefix = `terraform-`
11
12// idCounter is a randomly seeded monotonic counter for generating ordered
13// unique ids. It uses a big.Int so we can easily increment a long numeric
14// string. The max possible hex value here with 12 random bytes is
15// "01000000000000000000000000", so there's no chance of rollover during
16// operation.
17var idMutex sync.Mutex
18var idCounter = big.NewInt(0).SetBytes(randomBytes(12))
19
20// Helper for a resource to generate a unique identifier w/ default prefix
21func UniqueId() string {
22 return PrefixedUniqueId(UniqueIdPrefix)
23}
24
25// Helper for a resource to generate a unique identifier w/ given prefix
26//
27// After the prefix, the ID consists of an incrementing 26 digit value (to match
28// previous timestamp output).
29func PrefixedUniqueId(prefix string) string {
30 idMutex.Lock()
31 defer idMutex.Unlock()
32 return fmt.Sprintf("%s%026x", prefix, idCounter.Add(idCounter, big.NewInt(1)))
33}
34
35func randomBytes(n int) []byte {
36 b := make([]byte, n)
37 rand.Read(b)
38 return b
39}
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/map.go b/vendor/github.com/hashicorp/terraform/helper/resource/map.go
new file mode 100644
index 0000000..a465136
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/map.go
@@ -0,0 +1,140 @@
1package resource
2
3import (
4 "fmt"
5 "sort"
6
7 "github.com/hashicorp/terraform/terraform"
8)
9
10// Map is a map of resources that are supported, and provides helpers for
11// more easily implementing a ResourceProvider.
12type Map struct {
13 Mapping map[string]Resource
14}
15
16func (m *Map) Validate(
17 t string, c *terraform.ResourceConfig) ([]string, []error) {
18 r, ok := m.Mapping[t]
19 if !ok {
20 return nil, []error{fmt.Errorf("Unknown resource type: %s", t)}
21 }
22
23 // If there is no validator set, then it is valid
24 if r.ConfigValidator == nil {
25 return nil, nil
26 }
27
28 return r.ConfigValidator.Validate(c)
29}
30
31// Apply performs a create or update depending on the diff, and calls
32// the proper function on the matching Resource.
33func (m *Map) Apply(
34 info *terraform.InstanceInfo,
35 s *terraform.InstanceState,
36 d *terraform.InstanceDiff,
37 meta interface{}) (*terraform.InstanceState, error) {
38 r, ok := m.Mapping[info.Type]
39 if !ok {
40 return nil, fmt.Errorf("Unknown resource type: %s", info.Type)
41 }
42
43 if d.Destroy || d.RequiresNew() {
44 if s.ID != "" {
45 // Destroy the resource if it is created
46 err := r.Destroy(s, meta)
47 if err != nil {
48 return s, err
49 }
50
51 s.ID = ""
52 }
53
54 // If we're only destroying, and not creating, then return now.
55 // Otherwise, we continue so that we can create a new resource.
56 if !d.RequiresNew() {
57 return nil, nil
58 }
59 }
60
61 var result *terraform.InstanceState
62 var err error
63 if s.ID == "" {
64 result, err = r.Create(s, d, meta)
65 } else {
66 if r.Update == nil {
67 return s, fmt.Errorf(
68 "Resource type '%s' doesn't support update",
69 info.Type)
70 }
71
72 result, err = r.Update(s, d, meta)
73 }
74 if result != nil {
75 if result.Attributes == nil {
76 result.Attributes = make(map[string]string)
77 }
78
79 result.Attributes["id"] = result.ID
80 }
81
82 return result, err
83}
84
85// Diff performs a diff on the proper resource type.
86func (m *Map) Diff(
87 info *terraform.InstanceInfo,
88 s *terraform.InstanceState,
89 c *terraform.ResourceConfig,
90 meta interface{}) (*terraform.InstanceDiff, error) {
91 r, ok := m.Mapping[info.Type]
92 if !ok {
93 return nil, fmt.Errorf("Unknown resource type: %s", info.Type)
94 }
95
96 return r.Diff(s, c, meta)
97}
98
99// Refresh performs a Refresh on the proper resource type.
100//
101// Refresh on the Resource won't be called if the state represents a
102// non-created resource (ID is blank).
103//
104// An error is returned if the resource isn't registered.
105func (m *Map) Refresh(
106 info *terraform.InstanceInfo,
107 s *terraform.InstanceState,
108 meta interface{}) (*terraform.InstanceState, error) {
109 // If the resource isn't created, don't refresh.
110 if s.ID == "" {
111 return s, nil
112 }
113
114 r, ok := m.Mapping[info.Type]
115 if !ok {
116 return nil, fmt.Errorf("Unknown resource type: %s", info.Type)
117 }
118
119 return r.Refresh(s, meta)
120}
121
122// Resources returns all the resources that are supported by this
123// resource map and can be used to satisfy the Resources method of
124// a ResourceProvider.
125func (m *Map) Resources() []terraform.ResourceType {
126 ks := make([]string, 0, len(m.Mapping))
127 for k, _ := range m.Mapping {
128 ks = append(ks, k)
129 }
130 sort.Strings(ks)
131
132 rs := make([]terraform.ResourceType, 0, len(m.Mapping))
133 for _, k := range ks {
134 rs = append(rs, terraform.ResourceType{
135 Name: k,
136 })
137 }
138
139 return rs
140}
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/resource.go b/vendor/github.com/hashicorp/terraform/helper/resource/resource.go
new file mode 100644
index 0000000..0d9c831
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/resource.go
@@ -0,0 +1,49 @@
1package resource
2
3import (
4 "github.com/hashicorp/terraform/helper/config"
5 "github.com/hashicorp/terraform/terraform"
6)
7
8type Resource struct {
9 ConfigValidator *config.Validator
10 Create CreateFunc
11 Destroy DestroyFunc
12 Diff DiffFunc
13 Refresh RefreshFunc
14 Update UpdateFunc
15}
16
17// CreateFunc is a function that creates a resource that didn't previously
18// exist.
19type CreateFunc func(
20 *terraform.InstanceState,
21 *terraform.InstanceDiff,
22 interface{}) (*terraform.InstanceState, error)
23
24// DestroyFunc is a function that destroys a resource that previously
25// exists using the state.
26type DestroyFunc func(
27 *terraform.InstanceState,
28 interface{}) error
29
30// DiffFunc is a function that performs a diff of a resource.
31type DiffFunc func(
32 *terraform.InstanceState,
33 *terraform.ResourceConfig,
34 interface{}) (*terraform.InstanceDiff, error)
35
36// RefreshFunc is a function that performs a refresh of a specific type
37// of resource.
38type RefreshFunc func(
39 *terraform.InstanceState,
40 interface{}) (*terraform.InstanceState, error)
41
42// UpdateFunc is a function that is called to update a resource that
43// previously existed. The difference between this and CreateFunc is that
44// the diff is guaranteed to only contain attributes that don't require
45// a new resource.
46type UpdateFunc func(
47 *terraform.InstanceState,
48 *terraform.InstanceDiff,
49 interface{}) (*terraform.InstanceState, error)
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/state.go b/vendor/github.com/hashicorp/terraform/helper/resource/state.go
new file mode 100644
index 0000000..37c586a
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/state.go
@@ -0,0 +1,259 @@
1package resource
2
3import (
4 "log"
5 "time"
6)
7
8var refreshGracePeriod = 30 * time.Second
9
10// StateRefreshFunc is a function type used for StateChangeConf that is
11// responsible for refreshing the item being watched for a state change.
12//
13// It returns three results. `result` is any object that will be returned
14// as the final object after waiting for state change. This allows you to
15// return the final updated object, for example an EC2 instance after refreshing
16// it.
17//
18// `state` is the latest state of that object. And `err` is any error that
19// may have happened while refreshing the state.
20type StateRefreshFunc func() (result interface{}, state string, err error)
21
22// StateChangeConf is the configuration struct used for `WaitForState`.
23type StateChangeConf struct {
24 Delay time.Duration // Wait this time before starting checks
25 Pending []string // States that are "allowed" and will continue trying
26 Refresh StateRefreshFunc // Refreshes the current state
27 Target []string // Target state
28 Timeout time.Duration // The amount of time to wait before timeout
29 MinTimeout time.Duration // Smallest time to wait before refreshes
30 PollInterval time.Duration // Override MinTimeout/backoff and only poll this often
31 NotFoundChecks int // Number of times to allow not found
32
33 // This is to work around inconsistent APIs
34 ContinuousTargetOccurence int // Number of times the Target state has to occur continuously
35}
36
37// WaitForState watches an object and waits for it to achieve the state
38// specified in the configuration using the specified Refresh() func,
39// waiting the number of seconds specified in the timeout configuration.
40//
41// If the Refresh function returns a error, exit immediately with that error.
42//
43// If the Refresh function returns a state other than the Target state or one
44// listed in Pending, return immediately with an error.
45//
46// If the Timeout is exceeded before reaching the Target state, return an
47// error.
48//
49// Otherwise, result the result of the first call to the Refresh function to
50// reach the target state.
51func (conf *StateChangeConf) WaitForState() (interface{}, error) {
52 log.Printf("[DEBUG] Waiting for state to become: %s", conf.Target)
53
54 notfoundTick := 0
55 targetOccurence := 0
56
57 // Set a default for times to check for not found
58 if conf.NotFoundChecks == 0 {
59 conf.NotFoundChecks = 20
60 }
61
62 if conf.ContinuousTargetOccurence == 0 {
63 conf.ContinuousTargetOccurence = 1
64 }
65
66 type Result struct {
67 Result interface{}
68 State string
69 Error error
70 Done bool
71 }
72
73 // Read every result from the refresh loop, waiting for a positive result.Done.
74 resCh := make(chan Result, 1)
75 // cancellation channel for the refresh loop
76 cancelCh := make(chan struct{})
77
78 result := Result{}
79
80 go func() {
81 defer close(resCh)
82
83 time.Sleep(conf.Delay)
84
85 // start with 0 delay for the first loop
86 var wait time.Duration
87
88 for {
89 // store the last result
90 resCh <- result
91
92 // wait and watch for cancellation
93 select {
94 case <-cancelCh:
95 return
96 case <-time.After(wait):
97 // first round had no wait
98 if wait == 0 {
99 wait = 100 * time.Millisecond
100 }
101 }
102
103 res, currentState, err := conf.Refresh()
104 result = Result{
105 Result: res,
106 State: currentState,
107 Error: err,
108 }
109
110 if err != nil {
111 resCh <- result
112 return
113 }
114
115 // If we're waiting for the absence of a thing, then return
116 if res == nil && len(conf.Target) == 0 {
117 targetOccurence++
118 if conf.ContinuousTargetOccurence == targetOccurence {
119 result.Done = true
120 resCh <- result
121 return
122 }
123 continue
124 }
125
126 if res == nil {
127 // If we didn't find the resource, check if we have been
128 // not finding it for awhile, and if so, report an error.
129 notfoundTick++
130 if notfoundTick > conf.NotFoundChecks {
131 result.Error = &NotFoundError{
132 LastError: err,
133 Retries: notfoundTick,
134 }
135 resCh <- result
136 return
137 }
138 } else {
139 // Reset the counter for when a resource isn't found
140 notfoundTick = 0
141 found := false
142
143 for _, allowed := range conf.Target {
144 if currentState == allowed {
145 found = true
146 targetOccurence++
147 if conf.ContinuousTargetOccurence == targetOccurence {
148 result.Done = true
149 resCh <- result
150 return
151 }
152 continue
153 }
154 }
155
156 for _, allowed := range conf.Pending {
157 if currentState == allowed {
158 found = true
159 targetOccurence = 0
160 break
161 }
162 }
163
164 if !found && len(conf.Pending) > 0 {
165 result.Error = &UnexpectedStateError{
166 LastError: err,
167 State: result.State,
168 ExpectedState: conf.Target,
169 }
170 resCh <- result
171 return
172 }
173 }
174
175 // Wait between refreshes using exponential backoff, except when
176 // waiting for the target state to reoccur.
177 if targetOccurence == 0 {
178 wait *= 2
179 }
180
181 // If a poll interval has been specified, choose that interval.
182 // Otherwise bound the default value.
183 if conf.PollInterval > 0 && conf.PollInterval < 180*time.Second {
184 wait = conf.PollInterval
185 } else {
186 if wait < conf.MinTimeout {
187 wait = conf.MinTimeout
188 } else if wait > 10*time.Second {
189 wait = 10 * time.Second
190 }
191 }
192
193 log.Printf("[TRACE] Waiting %s before next try", wait)
194 }
195 }()
196
197 // store the last value result from the refresh loop
198 lastResult := Result{}
199
200 timeout := time.After(conf.Timeout)
201 for {
202 select {
203 case r, ok := <-resCh:
204 // channel closed, so return the last result
205 if !ok {
206 return lastResult.Result, lastResult.Error
207 }
208
209 // we reached the intended state
210 if r.Done {
211 return r.Result, r.Error
212 }
213
214 // still waiting, store the last result
215 lastResult = r
216
217 case <-timeout:
218 log.Printf("[WARN] WaitForState timeout after %s", conf.Timeout)
219 log.Printf("[WARN] WaitForState starting %s refresh grace period", refreshGracePeriod)
220
221 // cancel the goroutine and start our grace period timer
222 close(cancelCh)
223 timeout := time.After(refreshGracePeriod)
224
225 // we need a for loop and a label to break on, because we may have
226 // an extra response value to read, but still want to wait for the
227 // channel to close.
228 forSelect:
229 for {
230 select {
231 case r, ok := <-resCh:
232 if r.Done {
233 // the last refresh loop reached the desired state
234 return r.Result, r.Error
235 }
236
237 if !ok {
238 // the goroutine returned
239 break forSelect
240 }
241
242 // target state not reached, save the result for the
243 // TimeoutError and wait for the channel to close
244 lastResult = r
245 case <-timeout:
246 log.Println("[ERROR] WaitForState exceeded refresh grace period")
247 break forSelect
248 }
249 }
250
251 return nil, &TimeoutError{
252 LastError: lastResult.Error,
253 LastState: lastResult.State,
254 Timeout: conf.Timeout,
255 ExpectedState: conf.Target,
256 }
257 }
258 }
259}
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing.go
new file mode 100644
index 0000000..04367c5
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/testing.go
@@ -0,0 +1,790 @@
1package resource
2
3import (
4 "fmt"
5 "io"
6 "io/ioutil"
7 "log"
8 "os"
9 "path/filepath"
10 "reflect"
11 "regexp"
12 "strings"
13 "testing"
14
15 "github.com/davecgh/go-spew/spew"
16 "github.com/hashicorp/go-getter"
17 "github.com/hashicorp/go-multierror"
18 "github.com/hashicorp/terraform/config/module"
19 "github.com/hashicorp/terraform/helper/logging"
20 "github.com/hashicorp/terraform/terraform"
21)
22
23const TestEnvVar = "TF_ACC"
24
25// TestProvider can be implemented by any ResourceProvider to provide custom
26// reset functionality at the start of an acceptance test.
27// The helper/schema Provider implements this interface.
28type TestProvider interface {
29 TestReset() error
30}
31
32// TestCheckFunc is the callback type used with acceptance tests to check
33// the state of a resource. The state passed in is the latest state known,
34// or in the case of being after a destroy, it is the last known state when
35// it was created.
36type TestCheckFunc func(*terraform.State) error
37
38// ImportStateCheckFunc is the check function for ImportState tests
39type ImportStateCheckFunc func([]*terraform.InstanceState) error
40
41// TestCase is a single acceptance test case used to test the apply/destroy
42// lifecycle of a resource in a specific configuration.
43//
44// When the destroy plan is executed, the config from the last TestStep
45// is used to plan it.
46type TestCase struct {
47 // IsUnitTest allows a test to run regardless of the TF_ACC
48 // environment variable. This should be used with care - only for
49 // fast tests on local resources (e.g. remote state with a local
50 // backend) but can be used to increase confidence in correct
51 // operation of Terraform without waiting for a full acctest run.
52 IsUnitTest bool
53
54 // PreCheck, if non-nil, will be called before any test steps are
55 // executed. It will only be executed in the case that the steps
56 // would run, so it can be used for some validation before running
57 // acceptance tests, such as verifying that keys are setup.
58 PreCheck func()
59
60 // Providers is the ResourceProvider that will be under test.
61 //
62 // Alternately, ProviderFactories can be specified for the providers
63 // that are valid. This takes priority over Providers.
64 //
65 // The end effect of each is the same: specifying the providers that
66 // are used within the tests.
67 Providers map[string]terraform.ResourceProvider
68 ProviderFactories map[string]terraform.ResourceProviderFactory
69
70 // PreventPostDestroyRefresh can be set to true for cases where data sources
71 // are tested alongside real resources
72 PreventPostDestroyRefresh bool
73
74 // CheckDestroy is called after the resource is finally destroyed
75 // to allow the tester to test that the resource is truly gone.
76 CheckDestroy TestCheckFunc
77
78 // Steps are the apply sequences done within the context of the
79 // same state. Each step can have its own check to verify correctness.
80 Steps []TestStep
81
82 // The settings below control the "ID-only refresh test." This is
83 // an enabled-by-default test that tests that a refresh can be
84 // refreshed with only an ID to result in the same attributes.
85 // This validates completeness of Refresh.
86 //
87 // IDRefreshName is the name of the resource to check. This will
88 // default to the first non-nil primary resource in the state.
89 //
90 // IDRefreshIgnore is a list of configuration keys that will be ignored.
91 IDRefreshName string
92 IDRefreshIgnore []string
93}
94
95// TestStep is a single apply sequence of a test, done within the
96// context of a state.
97//
98// Multiple TestSteps can be sequenced in a Test to allow testing
99// potentially complex update logic. In general, simply create/destroy
100// tests will only need one step.
101type TestStep struct {
102 // ResourceName should be set to the name of the resource
103 // that is being tested. Example: "aws_instance.foo". Various test
104 // modes use this to auto-detect state information.
105 //
106 // This is only required if the test mode settings below say it is
107 // for the mode you're using.
108 ResourceName string
109
110 // PreConfig is called before the Config is applied to perform any per-step
111 // setup that needs to happen. This is called regardless of "test mode"
112 // below.
113 PreConfig func()
114
115 //---------------------------------------------------------------
116 // Test modes. One of the following groups of settings must be
117 // set to determine what the test step will do. Ideally we would've
118 // used Go interfaces here but there are now hundreds of tests we don't
119 // want to re-type so instead we just determine which step logic
120 // to run based on what settings below are set.
121 //---------------------------------------------------------------
122
123 //---------------------------------------------------------------
124 // Plan, Apply testing
125 //---------------------------------------------------------------
126
127 // Config a string of the configuration to give to Terraform. If this
128 // is set, then the TestCase will execute this step with the same logic
129 // as a `terraform apply`.
130 Config string
131
132 // Check is called after the Config is applied. Use this step to
133 // make your own API calls to check the status of things, and to
134 // inspect the format of the ResourceState itself.
135 //
136 // If an error is returned, the test will fail. In this case, a
137 // destroy plan will still be attempted.
138 //
139 // If this is nil, no check is done on this step.
140 Check TestCheckFunc
141
142 // Destroy will create a destroy plan if set to true.
143 Destroy bool
144
145 // ExpectNonEmptyPlan can be set to true for specific types of tests that are
146 // looking to verify that a diff occurs
147 ExpectNonEmptyPlan bool
148
149 // ExpectError allows the construction of test cases that we expect to fail
150 // with an error. The specified regexp must match against the error for the
151 // test to pass.
152 ExpectError *regexp.Regexp
153
154 // PlanOnly can be set to only run `plan` with this configuration, and not
155 // actually apply it. This is useful for ensuring config changes result in
156 // no-op plans
157 PlanOnly bool
158
159 // PreventPostDestroyRefresh can be set to true for cases where data sources
160 // are tested alongside real resources
161 PreventPostDestroyRefresh bool
162
163 //---------------------------------------------------------------
164 // ImportState testing
165 //---------------------------------------------------------------
166
167 // ImportState, if true, will test the functionality of ImportState
168 // by importing the resource with ResourceName (must be set) and the
169 // ID of that resource.
170 ImportState bool
171
172 // ImportStateId is the ID to perform an ImportState operation with.
173 // This is optional. If it isn't set, then the resource ID is automatically
174 // determined by inspecting the state for ResourceName's ID.
175 ImportStateId string
176
177 // ImportStateIdPrefix is the prefix added in front of ImportStateId.
178 // This can be useful in complex import cases, where more than one
179 // attribute needs to be passed on as the Import ID. Mainly in cases
180 // where the ID is not known, and a known prefix needs to be added to
181 // the unset ImportStateId field.
182 ImportStateIdPrefix string
183
184 // ImportStateCheck checks the results of ImportState. It should be
185 // used to verify that the resulting value of ImportState has the
186 // proper resources, IDs, and attributes.
187 ImportStateCheck ImportStateCheckFunc
188
189 // ImportStateVerify, if true, will also check that the state values
190 // that are finally put into the state after import match for all the
191 // IDs returned by the Import.
192 //
193 // ImportStateVerifyIgnore are fields that should not be verified to
194 // be equal. These can be set to ephemeral fields or fields that can't
195 // be refreshed and don't matter.
196 ImportStateVerify bool
197 ImportStateVerifyIgnore []string
198}
199
200// Test performs an acceptance test on a resource.
201//
202// Tests are not run unless an environmental variable "TF_ACC" is
203// set to some non-empty value. This is to avoid test cases surprising
204// a user by creating real resources.
205//
206// Tests will fail unless the verbose flag (`go test -v`, or explicitly
207// the "-test.v" flag) is set. Because some acceptance tests take quite
208// long, we require the verbose flag so users are able to see progress
209// output.
210func Test(t TestT, c TestCase) {
211 // We only run acceptance tests if an env var is set because they're
212 // slow and generally require some outside configuration. You can opt out
213 // of this with OverrideEnvVar on individual TestCases.
214 if os.Getenv(TestEnvVar) == "" && !c.IsUnitTest {
215 t.Skip(fmt.Sprintf(
216 "Acceptance tests skipped unless env '%s' set",
217 TestEnvVar))
218 return
219 }
220
221 logWriter, err := logging.LogOutput()
222 if err != nil {
223 t.Error(fmt.Errorf("error setting up logging: %s", err))
224 }
225 log.SetOutput(logWriter)
226
227 // We require verbose mode so that the user knows what is going on.
228 if !testTesting && !testing.Verbose() && !c.IsUnitTest {
229 t.Fatal("Acceptance tests must be run with the -v flag on tests")
230 return
231 }
232
233 // Run the PreCheck if we have it
234 if c.PreCheck != nil {
235 c.PreCheck()
236 }
237
238 ctxProviders, err := testProviderFactories(c)
239 if err != nil {
240 t.Fatal(err)
241 }
242 opts := terraform.ContextOpts{Providers: ctxProviders}
243
244 // A single state variable to track the lifecycle, starting with no state
245 var state *terraform.State
246
247 // Go through each step and run it
248 var idRefreshCheck *terraform.ResourceState
249 idRefresh := c.IDRefreshName != ""
250 errored := false
251 for i, step := range c.Steps {
252 var err error
253 log.Printf("[WARN] Test: Executing step %d", i)
254
255 // Determine the test mode to execute
256 if step.Config != "" {
257 state, err = testStepConfig(opts, state, step)
258 } else if step.ImportState {
259 state, err = testStepImportState(opts, state, step)
260 } else {
261 err = fmt.Errorf(
262 "unknown test mode for step. Please see TestStep docs\n\n%#v",
263 step)
264 }
265
266 // If there was an error, exit
267 if err != nil {
268 // Perhaps we expected an error? Check if it matches
269 if step.ExpectError != nil {
270 if !step.ExpectError.MatchString(err.Error()) {
271 errored = true
272 t.Error(fmt.Sprintf(
273 "Step %d, expected error:\n\n%s\n\nTo match:\n\n%s\n\n",
274 i, err, step.ExpectError))
275 break
276 }
277 } else {
278 errored = true
279 t.Error(fmt.Sprintf(
280 "Step %d error: %s", i, err))
281 break
282 }
283 }
284
285 // If we've never checked an id-only refresh and our state isn't
286 // empty, find the first resource and test it.
287 if idRefresh && idRefreshCheck == nil && !state.Empty() {
288 // Find the first non-nil resource in the state
289 for _, m := range state.Modules {
290 if len(m.Resources) > 0 {
291 if v, ok := m.Resources[c.IDRefreshName]; ok {
292 idRefreshCheck = v
293 }
294
295 break
296 }
297 }
298
299 // If we have an instance to check for refreshes, do it
300 // immediately. We do it in the middle of another test
301 // because it shouldn't affect the overall state (refresh
302 // is read-only semantically) and we want to fail early if
303 // this fails. If refresh isn't read-only, then this will have
304 // caught a different bug.
305 if idRefreshCheck != nil {
306 log.Printf(
307 "[WARN] Test: Running ID-only refresh check on %s",
308 idRefreshCheck.Primary.ID)
309 if err := testIDOnlyRefresh(c, opts, step, idRefreshCheck); err != nil {
310 log.Printf("[ERROR] Test: ID-only test failed: %s", err)
311 t.Error(fmt.Sprintf(
312 "[ERROR] Test: ID-only test failed: %s", err))
313 break
314 }
315 }
316 }
317 }
318
319 // If we never checked an id-only refresh, it is a failure.
320 if idRefresh {
321 if !errored && len(c.Steps) > 0 && idRefreshCheck == nil {
322 t.Error("ID-only refresh check never ran.")
323 }
324 }
325
326 // If we have a state, then run the destroy
327 if state != nil {
328 lastStep := c.Steps[len(c.Steps)-1]
329 destroyStep := TestStep{
330 Config: lastStep.Config,
331 Check: c.CheckDestroy,
332 Destroy: true,
333 PreventPostDestroyRefresh: c.PreventPostDestroyRefresh,
334 }
335
336 log.Printf("[WARN] Test: Executing destroy step")
337 state, err := testStep(opts, state, destroyStep)
338 if err != nil {
339 t.Error(fmt.Sprintf(
340 "Error destroying resource! WARNING: Dangling resources\n"+
341 "may exist. The full state and error is shown below.\n\n"+
342 "Error: %s\n\nState: %s",
343 err,
344 state))
345 }
346 } else {
347 log.Printf("[WARN] Skipping destroy test since there is no state.")
348 }
349}
350
351// testProviderFactories is a helper to build the ResourceProviderFactory map
352// with pre instantiated ResourceProviders, so that we can reset them for the
353// test, while only calling the factory function once.
354// Any errors are stored so that they can be returned by the factory in
355// terraform to match non-test behavior.
356func testProviderFactories(c TestCase) (map[string]terraform.ResourceProviderFactory, error) {
357 ctxProviders := c.ProviderFactories // make(map[string]terraform.ResourceProviderFactory)
358 if ctxProviders == nil {
359 ctxProviders = make(map[string]terraform.ResourceProviderFactory)
360 }
361 // add any fixed providers
362 for k, p := range c.Providers {
363 ctxProviders[k] = terraform.ResourceProviderFactoryFixed(p)
364 }
365
366 // reset the providers if needed
367 for k, pf := range ctxProviders {
368 // we can ignore any errors here, if we don't have a provider to reset
369 // the error will be handled later
370 p, err := pf()
371 if err != nil {
372 return nil, err
373 }
374 if p, ok := p.(TestProvider); ok {
375 err := p.TestReset()
376 if err != nil {
377 return nil, fmt.Errorf("[ERROR] failed to reset provider %q: %s", k, err)
378 }
379 }
380 }
381
382 return ctxProviders, nil
383}
384
385// UnitTest is a helper to force the acceptance testing harness to run in the
386// normal unit test suite. This should only be used for resource that don't
387// have any external dependencies.
388func UnitTest(t TestT, c TestCase) {
389 c.IsUnitTest = true
390 Test(t, c)
391}
392
393func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r *terraform.ResourceState) error {
394 // TODO: We guard by this right now so master doesn't explode. We
395 // need to remove this eventually to make this part of the normal tests.
396 if os.Getenv("TF_ACC_IDONLY") == "" {
397 return nil
398 }
399
400 name := fmt.Sprintf("%s.foo", r.Type)
401
402 // Build the state. The state is just the resource with an ID. There
403 // are no attributes. We only set what is needed to perform a refresh.
404 state := terraform.NewState()
405 state.RootModule().Resources[name] = &terraform.ResourceState{
406 Type: r.Type,
407 Primary: &terraform.InstanceState{
408 ID: r.Primary.ID,
409 },
410 }
411
412 // Create the config module. We use the full config because Refresh
413 // doesn't have access to it and we may need things like provider
414 // configurations. The initial implementation of id-only checks used
415 // an empty config module, but that caused the aforementioned problems.
416 mod, err := testModule(opts, step)
417 if err != nil {
418 return err
419 }
420
421 // Initialize the context
422 opts.Module = mod
423 opts.State = state
424 ctx, err := terraform.NewContext(&opts)
425 if err != nil {
426 return err
427 }
428 if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 {
429 if len(es) > 0 {
430 estrs := make([]string, len(es))
431 for i, e := range es {
432 estrs[i] = e.Error()
433 }
434 return fmt.Errorf(
435 "Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v",
436 ws, estrs)
437 }
438
439 log.Printf("[WARN] Config warnings: %#v", ws)
440 }
441
442 // Refresh!
443 state, err = ctx.Refresh()
444 if err != nil {
445 return fmt.Errorf("Error refreshing: %s", err)
446 }
447
448 // Verify attribute equivalence.
449 actualR := state.RootModule().Resources[name]
450 if actualR == nil {
451 return fmt.Errorf("Resource gone!")
452 }
453 if actualR.Primary == nil {
454 return fmt.Errorf("Resource has no primary instance")
455 }
456 actual := actualR.Primary.Attributes
457 expected := r.Primary.Attributes
458 // Remove fields we're ignoring
459 for _, v := range c.IDRefreshIgnore {
460 for k, _ := range actual {
461 if strings.HasPrefix(k, v) {
462 delete(actual, k)
463 }
464 }
465 for k, _ := range expected {
466 if strings.HasPrefix(k, v) {
467 delete(expected, k)
468 }
469 }
470 }
471
472 if !reflect.DeepEqual(actual, expected) {
473 // Determine only the different attributes
474 for k, v := range expected {
475 if av, ok := actual[k]; ok && v == av {
476 delete(expected, k)
477 delete(actual, k)
478 }
479 }
480
481 spewConf := spew.NewDefaultConfig()
482 spewConf.SortKeys = true
483 return fmt.Errorf(
484 "Attributes not equivalent. Difference is shown below. Top is actual, bottom is expected."+
485 "\n\n%s\n\n%s",
486 spewConf.Sdump(actual), spewConf.Sdump(expected))
487 }
488
489 return nil
490}
491
492func testModule(
493 opts terraform.ContextOpts,
494 step TestStep) (*module.Tree, error) {
495 if step.PreConfig != nil {
496 step.PreConfig()
497 }
498
499 cfgPath, err := ioutil.TempDir("", "tf-test")
500 if err != nil {
501 return nil, fmt.Errorf(
502 "Error creating temporary directory for config: %s", err)
503 }
504 defer os.RemoveAll(cfgPath)
505
506 // Write the configuration
507 cfgF, err := os.Create(filepath.Join(cfgPath, "main.tf"))
508 if err != nil {
509 return nil, fmt.Errorf(
510 "Error creating temporary file for config: %s", err)
511 }
512
513 _, err = io.Copy(cfgF, strings.NewReader(step.Config))
514 cfgF.Close()
515 if err != nil {
516 return nil, fmt.Errorf(
517 "Error creating temporary file for config: %s", err)
518 }
519
520 // Parse the configuration
521 mod, err := module.NewTreeModule("", cfgPath)
522 if err != nil {
523 return nil, fmt.Errorf(
524 "Error loading configuration: %s", err)
525 }
526
527 // Load the modules
528 modStorage := &getter.FolderStorage{
529 StorageDir: filepath.Join(cfgPath, ".tfmodules"),
530 }
531 err = mod.Load(modStorage, module.GetModeGet)
532 if err != nil {
533 return nil, fmt.Errorf("Error downloading modules: %s", err)
534 }
535
536 return mod, nil
537}
538
539func testResource(c TestStep, state *terraform.State) (*terraform.ResourceState, error) {
540 if c.ResourceName == "" {
541 return nil, fmt.Errorf("ResourceName must be set in TestStep")
542 }
543
544 for _, m := range state.Modules {
545 if len(m.Resources) > 0 {
546 if v, ok := m.Resources[c.ResourceName]; ok {
547 return v, nil
548 }
549 }
550 }
551
552 return nil, fmt.Errorf(
553 "Resource specified by ResourceName couldn't be found: %s", c.ResourceName)
554}
555
556// ComposeTestCheckFunc lets you compose multiple TestCheckFuncs into
557// a single TestCheckFunc.
558//
559// As a user testing their provider, this lets you decompose your checks
560// into smaller pieces more easily.
561func ComposeTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc {
562 return func(s *terraform.State) error {
563 for i, f := range fs {
564 if err := f(s); err != nil {
565 return fmt.Errorf("Check %d/%d error: %s", i+1, len(fs), err)
566 }
567 }
568
569 return nil
570 }
571}
572
573// ComposeAggregateTestCheckFunc lets you compose multiple TestCheckFuncs into
574// a single TestCheckFunc.
575//
576// As a user testing their provider, this lets you decompose your checks
577// into smaller pieces more easily.
578//
579// Unlike ComposeTestCheckFunc, ComposeAggergateTestCheckFunc runs _all_ of the
580// TestCheckFuncs and aggregates failures.
581func ComposeAggregateTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc {
582 return func(s *terraform.State) error {
583 var result *multierror.Error
584
585 for i, f := range fs {
586 if err := f(s); err != nil {
587 result = multierror.Append(result, fmt.Errorf("Check %d/%d error: %s", i+1, len(fs), err))
588 }
589 }
590
591 return result.ErrorOrNil()
592 }
593}
594
595// TestCheckResourceAttrSet is a TestCheckFunc which ensures a value
596// exists in state for the given name/key combination. It is useful when
597// testing that computed values were set, when it is not possible to
598// know ahead of time what the values will be.
599func TestCheckResourceAttrSet(name, key string) TestCheckFunc {
600 return func(s *terraform.State) error {
601 is, err := primaryInstanceState(s, name)
602 if err != nil {
603 return err
604 }
605
606 if val, ok := is.Attributes[key]; ok && val != "" {
607 return nil
608 }
609
610 return fmt.Errorf("%s: Attribute '%s' expected to be set", name, key)
611 }
612}
613
614// TestCheckResourceAttr is a TestCheckFunc which validates
615// the value in state for the given name/key combination.
616func TestCheckResourceAttr(name, key, value string) TestCheckFunc {
617 return func(s *terraform.State) error {
618 is, err := primaryInstanceState(s, name)
619 if err != nil {
620 return err
621 }
622
623 if v, ok := is.Attributes[key]; !ok || v != value {
624 if !ok {
625 return fmt.Errorf("%s: Attribute '%s' not found", name, key)
626 }
627
628 return fmt.Errorf(
629 "%s: Attribute '%s' expected %#v, got %#v",
630 name,
631 key,
632 value,
633 v)
634 }
635
636 return nil
637 }
638}
639
640// TestCheckNoResourceAttr is a TestCheckFunc which ensures that
641// NO value exists in state for the given name/key combination.
642func TestCheckNoResourceAttr(name, key string) TestCheckFunc {
643 return func(s *terraform.State) error {
644 is, err := primaryInstanceState(s, name)
645 if err != nil {
646 return err
647 }
648
649 if _, ok := is.Attributes[key]; ok {
650 return fmt.Errorf("%s: Attribute '%s' found when not expected", name, key)
651 }
652
653 return nil
654 }
655}
656
657// TestMatchResourceAttr is a TestCheckFunc which checks that the value
658// in state for the given name/key combination matches the given regex.
659func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc {
660 return func(s *terraform.State) error {
661 is, err := primaryInstanceState(s, name)
662 if err != nil {
663 return err
664 }
665
666 if !r.MatchString(is.Attributes[key]) {
667 return fmt.Errorf(
668 "%s: Attribute '%s' didn't match %q, got %#v",
669 name,
670 key,
671 r.String(),
672 is.Attributes[key])
673 }
674
675 return nil
676 }
677}
678
679// TestCheckResourceAttrPtr is like TestCheckResourceAttr except the
680// value is a pointer so that it can be updated while the test is running.
681// It will only be dereferenced at the point this step is run.
682func TestCheckResourceAttrPtr(name string, key string, value *string) TestCheckFunc {
683 return func(s *terraform.State) error {
684 return TestCheckResourceAttr(name, key, *value)(s)
685 }
686}
687
688// TestCheckResourceAttrPair is a TestCheckFunc which validates that the values
689// in state for a pair of name/key combinations are equal.
690func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc {
691 return func(s *terraform.State) error {
692 isFirst, err := primaryInstanceState(s, nameFirst)
693 if err != nil {
694 return err
695 }
696 vFirst, ok := isFirst.Attributes[keyFirst]
697 if !ok {
698 return fmt.Errorf("%s: Attribute '%s' not found", nameFirst, keyFirst)
699 }
700
701 isSecond, err := primaryInstanceState(s, nameSecond)
702 if err != nil {
703 return err
704 }
705 vSecond, ok := isSecond.Attributes[keySecond]
706 if !ok {
707 return fmt.Errorf("%s: Attribute '%s' not found", nameSecond, keySecond)
708 }
709
710 if vFirst != vSecond {
711 return fmt.Errorf(
712 "%s: Attribute '%s' expected %#v, got %#v",
713 nameFirst,
714 keyFirst,
715 vSecond,
716 vFirst)
717 }
718
719 return nil
720 }
721}
722
723// TestCheckOutput checks an output in the Terraform configuration
724func TestCheckOutput(name, value string) TestCheckFunc {
725 return func(s *terraform.State) error {
726 ms := s.RootModule()
727 rs, ok := ms.Outputs[name]
728 if !ok {
729 return fmt.Errorf("Not found: %s", name)
730 }
731
732 if rs.Value != value {
733 return fmt.Errorf(
734 "Output '%s': expected %#v, got %#v",
735 name,
736 value,
737 rs)
738 }
739
740 return nil
741 }
742}
743
744func TestMatchOutput(name string, r *regexp.Regexp) TestCheckFunc {
745 return func(s *terraform.State) error {
746 ms := s.RootModule()
747 rs, ok := ms.Outputs[name]
748 if !ok {
749 return fmt.Errorf("Not found: %s", name)
750 }
751
752 if !r.MatchString(rs.Value.(string)) {
753 return fmt.Errorf(
754 "Output '%s': %#v didn't match %q",
755 name,
756 rs,
757 r.String())
758 }
759
760 return nil
761 }
762}
763
764// TestT is the interface used to handle the test lifecycle of a test.
765//
766// Users should just use a *testing.T object, which implements this.
767type TestT interface {
768 Error(args ...interface{})
769 Fatal(args ...interface{})
770 Skip(args ...interface{})
771}
772
773// This is set to true by unit tests to alter some behavior
774var testTesting = false
775
776// primaryInstanceState returns the primary instance state for the given resource name.
777func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) {
778 ms := s.RootModule()
779 rs, ok := ms.Resources[name]
780 if !ok {
781 return nil, fmt.Errorf("Not found: %s", name)
782 }
783
784 is := rs.Primary
785 if is == nil {
786 return nil, fmt.Errorf("No primary instance: %s", name)
787 }
788
789 return is, nil
790}
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go
new file mode 100644
index 0000000..537a11c
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go
@@ -0,0 +1,160 @@
1package resource
2
3import (
4 "fmt"
5 "log"
6 "strings"
7
8 "github.com/hashicorp/terraform/terraform"
9)
10
11// testStepConfig runs a config-mode test step
12func testStepConfig(
13 opts terraform.ContextOpts,
14 state *terraform.State,
15 step TestStep) (*terraform.State, error) {
16 return testStep(opts, state, step)
17}
18
19func testStep(
20 opts terraform.ContextOpts,
21 state *terraform.State,
22 step TestStep) (*terraform.State, error) {
23 mod, err := testModule(opts, step)
24 if err != nil {
25 return state, err
26 }
27
28 // Build the context
29 opts.Module = mod
30 opts.State = state
31 opts.Destroy = step.Destroy
32 ctx, err := terraform.NewContext(&opts)
33 if err != nil {
34 return state, fmt.Errorf("Error initializing context: %s", err)
35 }
36 if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 {
37 if len(es) > 0 {
38 estrs := make([]string, len(es))
39 for i, e := range es {
40 estrs[i] = e.Error()
41 }
42 return state, fmt.Errorf(
43 "Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v",
44 ws, estrs)
45 }
46 log.Printf("[WARN] Config warnings: %#v", ws)
47 }
48
49 // Refresh!
50 state, err = ctx.Refresh()
51 if err != nil {
52 return state, fmt.Errorf(
53 "Error refreshing: %s", err)
54 }
55
56 // If this step is a PlanOnly step, skip over this first Plan and subsequent
57 // Apply, and use the follow up Plan that checks for perpetual diffs
58 if !step.PlanOnly {
59 // Plan!
60 if p, err := ctx.Plan(); err != nil {
61 return state, fmt.Errorf(
62 "Error planning: %s", err)
63 } else {
64 log.Printf("[WARN] Test: Step plan: %s", p)
65 }
66
67 // We need to keep a copy of the state prior to destroying
68 // such that destroy steps can verify their behaviour in the check
69 // function
70 stateBeforeApplication := state.DeepCopy()
71
72 // Apply!
73 state, err = ctx.Apply()
74 if err != nil {
75 return state, fmt.Errorf("Error applying: %s", err)
76 }
77
78 // Check! Excitement!
79 if step.Check != nil {
80 if step.Destroy {
81 if err := step.Check(stateBeforeApplication); err != nil {
82 return state, fmt.Errorf("Check failed: %s", err)
83 }
84 } else {
85 if err := step.Check(state); err != nil {
86 return state, fmt.Errorf("Check failed: %s", err)
87 }
88 }
89 }
90 }
91
92 // Now, verify that Plan is now empty and we don't have a perpetual diff issue
93 // We do this with TWO plans. One without a refresh.
94 var p *terraform.Plan
95 if p, err = ctx.Plan(); err != nil {
96 return state, fmt.Errorf("Error on follow-up plan: %s", err)
97 }
98 if p.Diff != nil && !p.Diff.Empty() {
99 if step.ExpectNonEmptyPlan {
100 log.Printf("[INFO] Got non-empty plan, as expected:\n\n%s", p)
101 } else {
102 return state, fmt.Errorf(
103 "After applying this step, the plan was not empty:\n\n%s", p)
104 }
105 }
106
107 // And another after a Refresh.
108 if !step.Destroy || (step.Destroy && !step.PreventPostDestroyRefresh) {
109 state, err = ctx.Refresh()
110 if err != nil {
111 return state, fmt.Errorf(
112 "Error on follow-up refresh: %s", err)
113 }
114 }
115 if p, err = ctx.Plan(); err != nil {
116 return state, fmt.Errorf("Error on second follow-up plan: %s", err)
117 }
118 empty := p.Diff == nil || p.Diff.Empty()
119
120 // Data resources are tricky because they legitimately get instantiated
121 // during refresh so that they will be already populated during the
122 // plan walk. Because of this, if we have any data resources in the
123 // config we'll end up wanting to destroy them again here. This is
124 // acceptable and expected, and we'll treat it as "empty" for the
125 // sake of this testing.
126 if step.Destroy {
127 empty = true
128
129 for _, moduleDiff := range p.Diff.Modules {
130 for k, instanceDiff := range moduleDiff.Resources {
131 if !strings.HasPrefix(k, "data.") {
132 empty = false
133 break
134 }
135
136 if !instanceDiff.Destroy {
137 empty = false
138 }
139 }
140 }
141 }
142
143 if !empty {
144 if step.ExpectNonEmptyPlan {
145 log.Printf("[INFO] Got non-empty plan, as expected:\n\n%s", p)
146 } else {
147 return state, fmt.Errorf(
148 "After applying this step and refreshing, "+
149 "the plan was not empty:\n\n%s", p)
150 }
151 }
152
153 // Made it here, but expected a non-empty plan, fail!
154 if step.ExpectNonEmptyPlan && (p.Diff == nil || p.Diff.Empty()) {
155 return state, fmt.Errorf("Expected a non-empty plan, but got an empty plan!")
156 }
157
158 // Made it here? Good job test step!
159 return state, nil
160}
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go
new file mode 100644
index 0000000..28ad105
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go
@@ -0,0 +1,141 @@
1package resource
2
3import (
4 "fmt"
5 "log"
6 "reflect"
7 "strings"
8
9 "github.com/davecgh/go-spew/spew"
10 "github.com/hashicorp/terraform/terraform"
11)
12
13// testStepImportState runs an imort state test step
14func testStepImportState(
15 opts terraform.ContextOpts,
16 state *terraform.State,
17 step TestStep) (*terraform.State, error) {
18 // Determine the ID to import
19 importId := step.ImportStateId
20 if importId == "" {
21 resource, err := testResource(step, state)
22 if err != nil {
23 return state, err
24 }
25
26 importId = resource.Primary.ID
27 }
28 importPrefix := step.ImportStateIdPrefix
29 if importPrefix != "" {
30 importId = fmt.Sprintf("%s%s", importPrefix, importId)
31 }
32
33 // Setup the context. We initialize with an empty state. We use the
34 // full config for provider configurations.
35 mod, err := testModule(opts, step)
36 if err != nil {
37 return state, err
38 }
39
40 opts.Module = mod
41 opts.State = terraform.NewState()
42 ctx, err := terraform.NewContext(&opts)
43 if err != nil {
44 return state, err
45 }
46
47 // Do the import!
48 newState, err := ctx.Import(&terraform.ImportOpts{
49 // Set the module so that any provider config is loaded
50 Module: mod,
51
52 Targets: []*terraform.ImportTarget{
53 &terraform.ImportTarget{
54 Addr: step.ResourceName,
55 ID: importId,
56 },
57 },
58 })
59 if err != nil {
60 log.Printf("[ERROR] Test: ImportState failure: %s", err)
61 return state, err
62 }
63
64 // Go through the new state and verify
65 if step.ImportStateCheck != nil {
66 var states []*terraform.InstanceState
67 for _, r := range newState.RootModule().Resources {
68 if r.Primary != nil {
69 states = append(states, r.Primary)
70 }
71 }
72 if err := step.ImportStateCheck(states); err != nil {
73 return state, err
74 }
75 }
76
77 // Verify that all the states match
78 if step.ImportStateVerify {
79 new := newState.RootModule().Resources
80 old := state.RootModule().Resources
81 for _, r := range new {
82 // Find the existing resource
83 var oldR *terraform.ResourceState
84 for _, r2 := range old {
85 if r2.Primary != nil && r2.Primary.ID == r.Primary.ID && r2.Type == r.Type {
86 oldR = r2
87 break
88 }
89 }
90 if oldR == nil {
91 return state, fmt.Errorf(
92 "Failed state verification, resource with ID %s not found",
93 r.Primary.ID)
94 }
95
96 // Compare their attributes
97 actual := make(map[string]string)
98 for k, v := range r.Primary.Attributes {
99 actual[k] = v
100 }
101 expected := make(map[string]string)
102 for k, v := range oldR.Primary.Attributes {
103 expected[k] = v
104 }
105
106 // Remove fields we're ignoring
107 for _, v := range step.ImportStateVerifyIgnore {
108 for k, _ := range actual {
109 if strings.HasPrefix(k, v) {
110 delete(actual, k)
111 }
112 }
113 for k, _ := range expected {
114 if strings.HasPrefix(k, v) {
115 delete(expected, k)
116 }
117 }
118 }
119
120 if !reflect.DeepEqual(actual, expected) {
121 // Determine only the different attributes
122 for k, v := range expected {
123 if av, ok := actual[k]; ok && v == av {
124 delete(expected, k)
125 delete(actual, k)
126 }
127 }
128
129 spewConf := spew.NewDefaultConfig()
130 spewConf.SortKeys = true
131 return state, fmt.Errorf(
132 "ImportStateVerify attributes not equivalent. Difference is shown below. Top is actual, bottom is expected."+
133 "\n\n%s\n\n%s",
134 spewConf.Sdump(actual), spewConf.Sdump(expected))
135 }
136 }
137 }
138
139 // Return the old state (non-imported) so we don't change anything.
140 return state, nil
141}
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/wait.go b/vendor/github.com/hashicorp/terraform/helper/resource/wait.go
new file mode 100644
index 0000000..ca50e29
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/wait.go
@@ -0,0 +1,84 @@
1package resource
2
3import (
4 "sync"
5 "time"
6)
7
8// Retry is a basic wrapper around StateChangeConf that will just retry
9// a function until it no longer returns an error.
10func Retry(timeout time.Duration, f RetryFunc) error {
11 // These are used to pull the error out of the function; need a mutex to
12 // avoid a data race.
13 var resultErr error
14 var resultErrMu sync.Mutex
15
16 c := &StateChangeConf{
17 Pending: []string{"retryableerror"},
18 Target: []string{"success"},
19 Timeout: timeout,
20 MinTimeout: 500 * time.Millisecond,
21 Refresh: func() (interface{}, string, error) {
22 rerr := f()
23
24 resultErrMu.Lock()
25 defer resultErrMu.Unlock()
26
27 if rerr == nil {
28 resultErr = nil
29 return 42, "success", nil
30 }
31
32 resultErr = rerr.Err
33
34 if rerr.Retryable {
35 return 42, "retryableerror", nil
36 }
37 return nil, "quit", rerr.Err
38 },
39 }
40
41 _, waitErr := c.WaitForState()
42
43 // Need to acquire the lock here to be able to avoid race using resultErr as
44 // the return value
45 resultErrMu.Lock()
46 defer resultErrMu.Unlock()
47
48 // resultErr may be nil because the wait timed out and resultErr was never
49 // set; this is still an error
50 if resultErr == nil {
51 return waitErr
52 }
53 // resultErr takes precedence over waitErr if both are set because it is
54 // more likely to be useful
55 return resultErr
56}
57
58// RetryFunc is the function retried until it succeeds.
59type RetryFunc func() *RetryError
60
61// RetryError is the required return type of RetryFunc. It forces client code
62// to choose whether or not a given error is retryable.
63type RetryError struct {
64 Err error
65 Retryable bool
66}
67
68// RetryableError is a helper to create a RetryError that's retryable from a
69// given error.
70func RetryableError(err error) *RetryError {
71 if err == nil {
72 return nil
73 }
74 return &RetryError{Err: err, Retryable: true}
75}
76
77// NonRetryableError is a helper to create a RetryError that's _not)_ retryable
78// from a given error.
79func NonRetryableError(err error) *RetryError {
80 if err == nil {
81 return nil
82 }
83 return &RetryError{Err: err, Retryable: false}
84}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/README.md b/vendor/github.com/hashicorp/terraform/helper/schema/README.md
new file mode 100644
index 0000000..28c8362
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/README.md
@@ -0,0 +1,11 @@
1# Terraform Helper Lib: schema
2
3The `schema` package provides a high-level interface for writing resource
4providers for Terraform.
5
6If you're writing a resource provider, we recommend you use this package.
7
8The interface exposed by this package is much friendlier than trying to
9write to the Terraform API directly. The core Terraform API is low-level
10and built for maximum flexibility and control, whereas this library is built
11as a framework around that to more easily write common providers.
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/backend.go b/vendor/github.com/hashicorp/terraform/helper/schema/backend.go
new file mode 100644
index 0000000..a0729c0
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/backend.go
@@ -0,0 +1,94 @@
1package schema
2
3import (
4 "context"
5
6 "github.com/hashicorp/terraform/terraform"
7)
8
9// Backend represents a partial backend.Backend implementation and simplifies
10// the creation of configuration loading and validation.
11//
12// Unlike other schema structs such as Provider, this struct is meant to be
13// embedded within your actual implementation. It provides implementations
14// only for Input and Configure and gives you a method for accessing the
15// configuration in the form of a ResourceData that you're expected to call
16// from the other implementation funcs.
17type Backend struct {
18 // Schema is the schema for the configuration of this backend. If this
19 // Backend has no configuration this can be omitted.
20 Schema map[string]*Schema
21
22 // ConfigureFunc is called to configure the backend. Use the
23 // FromContext* methods to extract information from the context.
24 // This can be nil, in which case nothing will be called but the
25 // config will still be stored.
26 ConfigureFunc func(context.Context) error
27
28 config *ResourceData
29}
30
31var (
32 backendConfigKey = contextKey("backend config")
33)
34
35// FromContextBackendConfig extracts a ResourceData with the configuration
36// from the context. This should only be called by Backend functions.
37func FromContextBackendConfig(ctx context.Context) *ResourceData {
38 return ctx.Value(backendConfigKey).(*ResourceData)
39}
40
41func (b *Backend) Input(
42 input terraform.UIInput,
43 c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
44 if b == nil {
45 return c, nil
46 }
47
48 return schemaMap(b.Schema).Input(input, c)
49}
50
51func (b *Backend) Validate(c *terraform.ResourceConfig) ([]string, []error) {
52 if b == nil {
53 return nil, nil
54 }
55
56 return schemaMap(b.Schema).Validate(c)
57}
58
59func (b *Backend) Configure(c *terraform.ResourceConfig) error {
60 if b == nil {
61 return nil
62 }
63
64 sm := schemaMap(b.Schema)
65
66 // Get a ResourceData for this configuration. To do this, we actually
67 // generate an intermediary "diff" although that is never exposed.
68 diff, err := sm.Diff(nil, c)
69 if err != nil {
70 return err
71 }
72
73 data, err := sm.Data(nil, diff)
74 if err != nil {
75 return err
76 }
77 b.config = data
78
79 if b.ConfigureFunc != nil {
80 err = b.ConfigureFunc(context.WithValue(
81 context.Background(), backendConfigKey, data))
82 if err != nil {
83 return err
84 }
85 }
86
87 return nil
88}
89
90// Config returns the configuration. This is available after Configure is
91// called.
92func (b *Backend) Config() *ResourceData {
93 return b.config
94}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go b/vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go
new file mode 100644
index 0000000..5a03d2d
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go
@@ -0,0 +1,59 @@
1package schema
2
3import (
4 "fmt"
5)
6
7// DataSourceResourceShim takes a Resource instance describing a data source
8// (with a Read implementation and a Schema, at least) and returns a new
9// Resource instance with additional Create and Delete implementations that
10// allow the data source to be used as a resource.
11//
12// This is a backward-compatibility layer for data sources that were formerly
13// read-only resources before the data source concept was added. It should not
14// be used for any *new* data sources.
15//
16// The Read function for the data source *must* call d.SetId with a non-empty
17// id in order for this shim to function as expected.
18//
19// The provided Resource instance, and its schema, will be modified in-place
20// to make it suitable for use as a full resource.
21func DataSourceResourceShim(name string, dataSource *Resource) *Resource {
22 // Recursively, in-place adjust the schema so that it has ForceNew
23 // on any user-settable resource.
24 dataSourceResourceShimAdjustSchema(dataSource.Schema)
25
26 dataSource.Create = CreateFunc(dataSource.Read)
27 dataSource.Delete = func(d *ResourceData, meta interface{}) error {
28 d.SetId("")
29 return nil
30 }
31 dataSource.Update = nil // should already be nil, but let's make sure
32
33 // FIXME: Link to some further docs either on the website or in the
34 // changelog, once such a thing exists.
35 dataSource.deprecationMessage = fmt.Sprintf(
36 "using %s as a resource is deprecated; consider using the data source instead",
37 name,
38 )
39
40 return dataSource
41}
42
43func dataSourceResourceShimAdjustSchema(schema map[string]*Schema) {
44 for _, s := range schema {
45 // If the attribute is configurable then it must be ForceNew,
46 // since we have no Update implementation.
47 if s.Required || s.Optional {
48 s.ForceNew = true
49 }
50
51 // If the attribute is a nested resource, we need to recursively
52 // apply these same adjustments to it.
53 if s.Elem != nil {
54 if r, ok := s.Elem.(*Resource); ok {
55 dataSourceResourceShimAdjustSchema(r.Schema)
56 }
57 }
58 }
59}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/equal.go b/vendor/github.com/hashicorp/terraform/helper/schema/equal.go
new file mode 100644
index 0000000..d5e20e0
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/equal.go
@@ -0,0 +1,6 @@
1package schema
2
3// Equal is an interface that checks for deep equality between two objects.
4type Equal interface {
5 Equal(interface{}) bool
6}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go
new file mode 100644
index 0000000..1660a67
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go
@@ -0,0 +1,334 @@
1package schema
2
3import (
4 "fmt"
5 "strconv"
6)
7
8// FieldReaders are responsible for decoding fields out of data into
9// the proper typed representation. ResourceData uses this to query data
10// out of multiple sources: config, state, diffs, etc.
11type FieldReader interface {
12 ReadField([]string) (FieldReadResult, error)
13}
14
15// FieldReadResult encapsulates all the resulting data from reading
16// a field.
17type FieldReadResult struct {
18 // Value is the actual read value. NegValue is the _negative_ value
19 // or the items that should be removed (if they existed). NegValue
20 // doesn't make sense for primitives but is important for any
21 // container types such as maps, sets, lists.
22 Value interface{}
23 ValueProcessed interface{}
24
25 // Exists is true if the field was found in the data. False means
26 // it wasn't found if there was no error.
27 Exists bool
28
29 // Computed is true if the field was found but the value
30 // is computed.
31 Computed bool
32}
33
34// ValueOrZero returns the value of this result or the zero value of the
35// schema type, ensuring a consistent non-nil return value.
36func (r *FieldReadResult) ValueOrZero(s *Schema) interface{} {
37 if r.Value != nil {
38 return r.Value
39 }
40
41 return s.ZeroValue()
42}
43
44// addrToSchema finds the final element schema for the given address
45// and the given schema. It returns all the schemas that led to the final
46// schema. These are in order of the address (out to in).
47func addrToSchema(addr []string, schemaMap map[string]*Schema) []*Schema {
48 current := &Schema{
49 Type: typeObject,
50 Elem: schemaMap,
51 }
52
53 // If we aren't given an address, then the user is requesting the
54 // full object, so we return the special value which is the full object.
55 if len(addr) == 0 {
56 return []*Schema{current}
57 }
58
59 result := make([]*Schema, 0, len(addr))
60 for len(addr) > 0 {
61 k := addr[0]
62 addr = addr[1:]
63
64 REPEAT:
65 // We want to trim off the first "typeObject" since its not a
66 // real lookup that people do. i.e. []string{"foo"} in a structure
67 // isn't {typeObject, typeString}, its just a {typeString}.
68 if len(result) > 0 || current.Type != typeObject {
69 result = append(result, current)
70 }
71
72 switch t := current.Type; t {
73 case TypeBool, TypeInt, TypeFloat, TypeString:
74 if len(addr) > 0 {
75 return nil
76 }
77 case TypeList, TypeSet:
78 isIndex := len(addr) > 0 && addr[0] == "#"
79
80 switch v := current.Elem.(type) {
81 case *Resource:
82 current = &Schema{
83 Type: typeObject,
84 Elem: v.Schema,
85 }
86 case *Schema:
87 current = v
88 case ValueType:
89 current = &Schema{Type: v}
90 default:
91 // we may not know the Elem type and are just looking for the
92 // index
93 if isIndex {
94 break
95 }
96
97 if len(addr) == 0 {
98 // we've processed the address, so return what we've
99 // collected
100 return result
101 }
102
103 if len(addr) == 1 {
104 if _, err := strconv.Atoi(addr[0]); err == nil {
105 // we're indexing a value without a schema. This can
106 // happen if the list is nested in another schema type.
107 // Default to a TypeString like we do with a map
108 current = &Schema{Type: TypeString}
109 break
110 }
111 }
112
113 return nil
114 }
115
116 // If we only have one more thing and the next thing
117 // is a #, then we're accessing the index which is always
118 // an int.
119 if isIndex {
120 current = &Schema{Type: TypeInt}
121 break
122 }
123
124 case TypeMap:
125 if len(addr) > 0 {
126 switch v := current.Elem.(type) {
127 case ValueType:
128 current = &Schema{Type: v}
129 default:
130 // maps default to string values. This is all we can have
131 // if this is nested in another list or map.
132 current = &Schema{Type: TypeString}
133 }
134 }
135 case typeObject:
136 // If we're already in the object, then we want to handle Sets
137 // and Lists specially. Basically, their next key is the lookup
138 // key (the set value or the list element). For these scenarios,
139 // we just want to skip it and move to the next element if there
140 // is one.
141 if len(result) > 0 {
142 lastType := result[len(result)-2].Type
143 if lastType == TypeSet || lastType == TypeList {
144 if len(addr) == 0 {
145 break
146 }
147
148 k = addr[0]
149 addr = addr[1:]
150 }
151 }
152
153 m := current.Elem.(map[string]*Schema)
154 val, ok := m[k]
155 if !ok {
156 return nil
157 }
158
159 current = val
160 goto REPEAT
161 }
162 }
163
164 return result
165}
166
167// readListField is a generic method for reading a list field out of a
168// a FieldReader. It does this based on the assumption that there is a key
169// "foo.#" for a list "foo" and that the indexes are "foo.0", "foo.1", etc.
170// after that point.
171func readListField(
172 r FieldReader, addr []string, schema *Schema) (FieldReadResult, error) {
173 addrPadded := make([]string, len(addr)+1)
174 copy(addrPadded, addr)
175 addrPadded[len(addrPadded)-1] = "#"
176
177 // Get the number of elements in the list
178 countResult, err := r.ReadField(addrPadded)
179 if err != nil {
180 return FieldReadResult{}, err
181 }
182 if !countResult.Exists {
183 // No count, means we have no list
184 countResult.Value = 0
185 }
186
187 // If we have an empty list, then return an empty list
188 if countResult.Computed || countResult.Value.(int) == 0 {
189 return FieldReadResult{
190 Value: []interface{}{},
191 Exists: countResult.Exists,
192 Computed: countResult.Computed,
193 }, nil
194 }
195
196 // Go through each count, and get the item value out of it
197 result := make([]interface{}, countResult.Value.(int))
198 for i, _ := range result {
199 is := strconv.FormatInt(int64(i), 10)
200 addrPadded[len(addrPadded)-1] = is
201 rawResult, err := r.ReadField(addrPadded)
202 if err != nil {
203 return FieldReadResult{}, err
204 }
205 if !rawResult.Exists {
206 // This should never happen, because by the time the data
207 // gets to the FieldReaders, all the defaults should be set by
208 // Schema.
209 rawResult.Value = nil
210 }
211
212 result[i] = rawResult.Value
213 }
214
215 return FieldReadResult{
216 Value: result,
217 Exists: true,
218 }, nil
219}
220
221// readObjectField is a generic method for reading objects out of FieldReaders
222// based on the assumption that building an address of []string{k, FIELD}
223// will result in the proper field data.
224func readObjectField(
225 r FieldReader,
226 addr []string,
227 schema map[string]*Schema) (FieldReadResult, error) {
228 result := make(map[string]interface{})
229 exists := false
230 for field, s := range schema {
231 addrRead := make([]string, len(addr), len(addr)+1)
232 copy(addrRead, addr)
233 addrRead = append(addrRead, field)
234 rawResult, err := r.ReadField(addrRead)
235 if err != nil {
236 return FieldReadResult{}, err
237 }
238 if rawResult.Exists {
239 exists = true
240 }
241
242 result[field] = rawResult.ValueOrZero(s)
243 }
244
245 return FieldReadResult{
246 Value: result,
247 Exists: exists,
248 }, nil
249}
250
251// convert map values to the proper primitive type based on schema.Elem
252func mapValuesToPrimitive(m map[string]interface{}, schema *Schema) error {
253
254 elemType := TypeString
255 if et, ok := schema.Elem.(ValueType); ok {
256 elemType = et
257 }
258
259 switch elemType {
260 case TypeInt, TypeFloat, TypeBool:
261 for k, v := range m {
262 vs, ok := v.(string)
263 if !ok {
264 continue
265 }
266
267 v, err := stringToPrimitive(vs, false, &Schema{Type: elemType})
268 if err != nil {
269 return err
270 }
271
272 m[k] = v
273 }
274 }
275 return nil
276}
277
278func stringToPrimitive(
279 value string, computed bool, schema *Schema) (interface{}, error) {
280 var returnVal interface{}
281 switch schema.Type {
282 case TypeBool:
283 if value == "" {
284 returnVal = false
285 break
286 }
287 if computed {
288 break
289 }
290
291 v, err := strconv.ParseBool(value)
292 if err != nil {
293 return nil, err
294 }
295
296 returnVal = v
297 case TypeFloat:
298 if value == "" {
299 returnVal = 0.0
300 break
301 }
302 if computed {
303 break
304 }
305
306 v, err := strconv.ParseFloat(value, 64)
307 if err != nil {
308 return nil, err
309 }
310
311 returnVal = v
312 case TypeInt:
313 if value == "" {
314 returnVal = 0
315 break
316 }
317 if computed {
318 break
319 }
320
321 v, err := strconv.ParseInt(value, 0, 0)
322 if err != nil {
323 return nil, err
324 }
325
326 returnVal = int(v)
327 case TypeString:
328 returnVal = value
329 default:
330 panic(fmt.Sprintf("Unknown type: %s", schema.Type))
331 }
332
333 return returnVal, nil
334}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go
new file mode 100644
index 0000000..f958bbc
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go
@@ -0,0 +1,333 @@
1package schema
2
3import (
4 "fmt"
5 "strconv"
6 "strings"
7 "sync"
8
9 "github.com/hashicorp/terraform/terraform"
10 "github.com/mitchellh/mapstructure"
11)
12
13// ConfigFieldReader reads fields out of an untyped map[string]string to the
14// best of its ability. It also applies defaults from the Schema. (The other
15// field readers do not need default handling because they source fully
16// populated data structures.)
17type ConfigFieldReader struct {
18 Config *terraform.ResourceConfig
19 Schema map[string]*Schema
20
21 indexMaps map[string]map[string]int
22 once sync.Once
23}
24
25func (r *ConfigFieldReader) ReadField(address []string) (FieldReadResult, error) {
26 r.once.Do(func() { r.indexMaps = make(map[string]map[string]int) })
27 return r.readField(address, false)
28}
29
30func (r *ConfigFieldReader) readField(
31 address []string, nested bool) (FieldReadResult, error) {
32 schemaList := addrToSchema(address, r.Schema)
33 if len(schemaList) == 0 {
34 return FieldReadResult{}, nil
35 }
36
37 if !nested {
38 // If we have a set anywhere in the address, then we need to
39 // read that set out in order and actually replace that part of
40 // the address with the real list index. i.e. set.50 might actually
41 // map to set.12 in the config, since it is in list order in the
42 // config, not indexed by set value.
43 for i, v := range schemaList {
44 // Sets are the only thing that cause this issue.
45 if v.Type != TypeSet {
46 continue
47 }
48
49 // If we're at the end of the list, then we don't have to worry
50 // about this because we're just requesting the whole set.
51 if i == len(schemaList)-1 {
52 continue
53 }
54
55 // If we're looking for the count, then ignore...
56 if address[i+1] == "#" {
57 continue
58 }
59
60 indexMap, ok := r.indexMaps[strings.Join(address[:i+1], ".")]
61 if !ok {
62 // Get the set so we can get the index map that tells us the
63 // mapping of the hash code to the list index
64 _, err := r.readSet(address[:i+1], v)
65 if err != nil {
66 return FieldReadResult{}, err
67 }
68 indexMap = r.indexMaps[strings.Join(address[:i+1], ".")]
69 }
70
71 index, ok := indexMap[address[i+1]]
72 if !ok {
73 return FieldReadResult{}, nil
74 }
75
76 address[i+1] = strconv.FormatInt(int64(index), 10)
77 }
78 }
79
80 k := strings.Join(address, ".")
81 schema := schemaList[len(schemaList)-1]
82
83 // If we're getting the single element of a promoted list, then
84 // check to see if we have a single element we need to promote.
85 if address[len(address)-1] == "0" && len(schemaList) > 1 {
86 lastSchema := schemaList[len(schemaList)-2]
87 if lastSchema.Type == TypeList && lastSchema.PromoteSingle {
88 k := strings.Join(address[:len(address)-1], ".")
89 result, err := r.readPrimitive(k, schema)
90 if err == nil {
91 return result, nil
92 }
93 }
94 }
95
96 switch schema.Type {
97 case TypeBool, TypeFloat, TypeInt, TypeString:
98 return r.readPrimitive(k, schema)
99 case TypeList:
100 // If we support promotion then we first check if we have a lone
101 // value that we must promote.
102 // a value that is alone.
103 if schema.PromoteSingle {
104 result, err := r.readPrimitive(k, schema.Elem.(*Schema))
105 if err == nil && result.Exists {
106 result.Value = []interface{}{result.Value}
107 return result, nil
108 }
109 }
110
111 return readListField(&nestedConfigFieldReader{r}, address, schema)
112 case TypeMap:
113 return r.readMap(k, schema)
114 case TypeSet:
115 return r.readSet(address, schema)
116 case typeObject:
117 return readObjectField(
118 &nestedConfigFieldReader{r},
119 address, schema.Elem.(map[string]*Schema))
120 default:
121 panic(fmt.Sprintf("Unknown type: %s", schema.Type))
122 }
123}
124
125func (r *ConfigFieldReader) readMap(k string, schema *Schema) (FieldReadResult, error) {
126 // We want both the raw value and the interpolated. We use the interpolated
127 // to store actual values and we use the raw one to check for
128 // computed keys. Actual values are obtained in the switch, depending on
129 // the type of the raw value.
130 mraw, ok := r.Config.GetRaw(k)
131 if !ok {
132 // check if this is from an interpolated field by seeing if it exists
133 // in the config
134 _, ok := r.Config.Get(k)
135 if !ok {
136 // this really doesn't exist
137 return FieldReadResult{}, nil
138 }
139
140 // We couldn't fetch the value from a nested data structure, so treat the
141 // raw value as an interpolation string. The mraw value is only used
142 // for the type switch below.
143 mraw = "${INTERPOLATED}"
144 }
145
146 result := make(map[string]interface{})
147 computed := false
148 switch m := mraw.(type) {
149 case string:
150 // This is a map which has come out of an interpolated variable, so we
151 // can just get the value directly from config. Values cannot be computed
152 // currently.
153 v, _ := r.Config.Get(k)
154
155 // If this isn't a map[string]interface, it must be computed.
156 mapV, ok := v.(map[string]interface{})
157 if !ok {
158 return FieldReadResult{
159 Exists: true,
160 Computed: true,
161 }, nil
162 }
163
164 // Otherwise we can proceed as usual.
165 for i, iv := range mapV {
166 result[i] = iv
167 }
168 case []interface{}:
169 for i, innerRaw := range m {
170 for ik := range innerRaw.(map[string]interface{}) {
171 key := fmt.Sprintf("%s.%d.%s", k, i, ik)
172 if r.Config.IsComputed(key) {
173 computed = true
174 break
175 }
176
177 v, _ := r.Config.Get(key)
178 result[ik] = v
179 }
180 }
181 case []map[string]interface{}:
182 for i, innerRaw := range m {
183 for ik := range innerRaw {
184 key := fmt.Sprintf("%s.%d.%s", k, i, ik)
185 if r.Config.IsComputed(key) {
186 computed = true
187 break
188 }
189
190 v, _ := r.Config.Get(key)
191 result[ik] = v
192 }
193 }
194 case map[string]interface{}:
195 for ik := range m {
196 key := fmt.Sprintf("%s.%s", k, ik)
197 if r.Config.IsComputed(key) {
198 computed = true
199 break
200 }
201
202 v, _ := r.Config.Get(key)
203 result[ik] = v
204 }
205 default:
206 panic(fmt.Sprintf("unknown type: %#v", mraw))
207 }
208
209 err := mapValuesToPrimitive(result, schema)
210 if err != nil {
211 return FieldReadResult{}, nil
212 }
213
214 var value interface{}
215 if !computed {
216 value = result
217 }
218
219 return FieldReadResult{
220 Value: value,
221 Exists: true,
222 Computed: computed,
223 }, nil
224}
225
226func (r *ConfigFieldReader) readPrimitive(
227 k string, schema *Schema) (FieldReadResult, error) {
228 raw, ok := r.Config.Get(k)
229 if !ok {
230 // Nothing in config, but we might still have a default from the schema
231 var err error
232 raw, err = schema.DefaultValue()
233 if err != nil {
234 return FieldReadResult{}, fmt.Errorf("%s, error loading default: %s", k, err)
235 }
236
237 if raw == nil {
238 return FieldReadResult{}, nil
239 }
240 }
241
242 var result string
243 if err := mapstructure.WeakDecode(raw, &result); err != nil {
244 return FieldReadResult{}, err
245 }
246
247 computed := r.Config.IsComputed(k)
248 returnVal, err := stringToPrimitive(result, computed, schema)
249 if err != nil {
250 return FieldReadResult{}, err
251 }
252
253 return FieldReadResult{
254 Value: returnVal,
255 Exists: true,
256 Computed: computed,
257 }, nil
258}
259
260func (r *ConfigFieldReader) readSet(
261 address []string, schema *Schema) (FieldReadResult, error) {
262 indexMap := make(map[string]int)
263 // Create the set that will be our result
264 set := schema.ZeroValue().(*Set)
265
266 raw, err := readListField(&nestedConfigFieldReader{r}, address, schema)
267 if err != nil {
268 return FieldReadResult{}, err
269 }
270 if !raw.Exists {
271 return FieldReadResult{Value: set}, nil
272 }
273
274 // If the list is computed, the set is necessarilly computed
275 if raw.Computed {
276 return FieldReadResult{
277 Value: set,
278 Exists: true,
279 Computed: raw.Computed,
280 }, nil
281 }
282
283 // Build up the set from the list elements
284 for i, v := range raw.Value.([]interface{}) {
285 // Check if any of the keys in this item are computed
286 computed := r.hasComputedSubKeys(
287 fmt.Sprintf("%s.%d", strings.Join(address, "."), i), schema)
288
289 code := set.add(v, computed)
290 indexMap[code] = i
291 }
292
293 r.indexMaps[strings.Join(address, ".")] = indexMap
294
295 return FieldReadResult{
296 Value: set,
297 Exists: true,
298 }, nil
299}
300
301// hasComputedSubKeys walks through a schema and returns whether or not the
302// given key contains any subkeys that are computed.
303func (r *ConfigFieldReader) hasComputedSubKeys(key string, schema *Schema) bool {
304 prefix := key + "."
305
306 switch t := schema.Elem.(type) {
307 case *Resource:
308 for k, schema := range t.Schema {
309 if r.Config.IsComputed(prefix + k) {
310 return true
311 }
312
313 if r.hasComputedSubKeys(prefix+k, schema) {
314 return true
315 }
316 }
317 }
318
319 return false
320}
321
322// nestedConfigFieldReader is a funny little thing that just wraps a
323// ConfigFieldReader to call readField when ReadField is called so that
324// we don't recalculate the set rewrites in the address, which leads to
325// an infinite loop.
326type nestedConfigFieldReader struct {
327 Reader *ConfigFieldReader
328}
329
330func (r *nestedConfigFieldReader) ReadField(
331 address []string) (FieldReadResult, error) {
332 return r.Reader.readField(address, true)
333}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go
new file mode 100644
index 0000000..16bbae2
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go
@@ -0,0 +1,208 @@
1package schema
2
3import (
4 "fmt"
5 "strings"
6
7 "github.com/hashicorp/terraform/terraform"
8 "github.com/mitchellh/mapstructure"
9)
10
11// DiffFieldReader reads fields out of a diff structures.
12//
13// It also requires access to a Reader that reads fields from the structure
14// that the diff was derived from. This is usually the state. This is required
15// because a diff on its own doesn't have complete data about full objects
16// such as maps.
17//
18// The Source MUST be the data that the diff was derived from. If it isn't,
19// the behavior of this struct is undefined.
20//
21// Reading fields from a DiffFieldReader is identical to reading from
22// Source except the diff will be applied to the end result.
23//
24// The "Exists" field on the result will be set to true if the complete
25// field exists whether its from the source, diff, or a combination of both.
26// It cannot be determined whether a retrieved value is composed of
27// diff elements.
28type DiffFieldReader struct {
29 Diff *terraform.InstanceDiff
30 Source FieldReader
31 Schema map[string]*Schema
32}
33
34func (r *DiffFieldReader) ReadField(address []string) (FieldReadResult, error) {
35 schemaList := addrToSchema(address, r.Schema)
36 if len(schemaList) == 0 {
37 return FieldReadResult{}, nil
38 }
39
40 schema := schemaList[len(schemaList)-1]
41 switch schema.Type {
42 case TypeBool, TypeInt, TypeFloat, TypeString:
43 return r.readPrimitive(address, schema)
44 case TypeList:
45 return readListField(r, address, schema)
46 case TypeMap:
47 return r.readMap(address, schema)
48 case TypeSet:
49 return r.readSet(address, schema)
50 case typeObject:
51 return readObjectField(r, address, schema.Elem.(map[string]*Schema))
52 default:
53 panic(fmt.Sprintf("Unknown type: %#v", schema.Type))
54 }
55}
56
57func (r *DiffFieldReader) readMap(
58 address []string, schema *Schema) (FieldReadResult, error) {
59 result := make(map[string]interface{})
60 resultSet := false
61
62 // First read the map from the underlying source
63 source, err := r.Source.ReadField(address)
64 if err != nil {
65 return FieldReadResult{}, err
66 }
67 if source.Exists {
68 result = source.Value.(map[string]interface{})
69 resultSet = true
70 }
71
72 // Next, read all the elements we have in our diff, and apply
73 // the diff to our result.
74 prefix := strings.Join(address, ".") + "."
75 for k, v := range r.Diff.Attributes {
76 if !strings.HasPrefix(k, prefix) {
77 continue
78 }
79 if strings.HasPrefix(k, prefix+"%") {
80 // Ignore the count field
81 continue
82 }
83
84 resultSet = true
85
86 k = k[len(prefix):]
87 if v.NewRemoved {
88 delete(result, k)
89 continue
90 }
91
92 result[k] = v.New
93 }
94
95 err = mapValuesToPrimitive(result, schema)
96 if err != nil {
97 return FieldReadResult{}, nil
98 }
99
100 var resultVal interface{}
101 if resultSet {
102 resultVal = result
103 }
104
105 return FieldReadResult{
106 Value: resultVal,
107 Exists: resultSet,
108 }, nil
109}
110
111func (r *DiffFieldReader) readPrimitive(
112 address []string, schema *Schema) (FieldReadResult, error) {
113 result, err := r.Source.ReadField(address)
114 if err != nil {
115 return FieldReadResult{}, err
116 }
117
118 attrD, ok := r.Diff.Attributes[strings.Join(address, ".")]
119 if !ok {
120 return result, nil
121 }
122
123 var resultVal string
124 if !attrD.NewComputed {
125 resultVal = attrD.New
126 if attrD.NewExtra != nil {
127 result.ValueProcessed = resultVal
128 if err := mapstructure.WeakDecode(attrD.NewExtra, &resultVal); err != nil {
129 return FieldReadResult{}, err
130 }
131 }
132 }
133
134 result.Computed = attrD.NewComputed
135 result.Exists = true
136 result.Value, err = stringToPrimitive(resultVal, false, schema)
137 if err != nil {
138 return FieldReadResult{}, err
139 }
140
141 return result, nil
142}
143
144func (r *DiffFieldReader) readSet(
145 address []string, schema *Schema) (FieldReadResult, error) {
146 prefix := strings.Join(address, ".") + "."
147
148 // Create the set that will be our result
149 set := schema.ZeroValue().(*Set)
150
151 // Go through the map and find all the set items
152 for k, d := range r.Diff.Attributes {
153 if d.NewRemoved {
154 // If the field is removed, we always ignore it
155 continue
156 }
157 if !strings.HasPrefix(k, prefix) {
158 continue
159 }
160 if strings.HasSuffix(k, "#") {
161 // Ignore any count field
162 continue
163 }
164
165 // Split the key, since it might be a sub-object like "idx.field"
166 parts := strings.Split(k[len(prefix):], ".")
167 idx := parts[0]
168
169 raw, err := r.ReadField(append(address, idx))
170 if err != nil {
171 return FieldReadResult{}, err
172 }
173 if !raw.Exists {
174 // This shouldn't happen because we just verified it does exist
175 panic("missing field in set: " + k + "." + idx)
176 }
177
178 set.Add(raw.Value)
179 }
180
181 // Determine if the set "exists". It exists if there are items or if
182 // the diff explicitly wanted it empty.
183 exists := set.Len() > 0
184 if !exists {
185 // We could check if the diff value is "0" here but I think the
186 // existence of "#" on its own is enough to show it existed. This
187 // protects us in the future from the zero value changing from
188 // "0" to "" breaking us (if that were to happen).
189 if _, ok := r.Diff.Attributes[prefix+"#"]; ok {
190 exists = true
191 }
192 }
193
194 if !exists {
195 result, err := r.Source.ReadField(address)
196 if err != nil {
197 return FieldReadResult{}, err
198 }
199 if result.Exists {
200 return result, nil
201 }
202 }
203
204 return FieldReadResult{
205 Value: set,
206 Exists: exists,
207 }, nil
208}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go
new file mode 100644
index 0000000..9533981
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go
@@ -0,0 +1,232 @@
1package schema
2
3import (
4 "fmt"
5 "strings"
6)
7
8// MapFieldReader reads fields out of an untyped map[string]string to
9// the best of its ability.
10type MapFieldReader struct {
11 Map MapReader
12 Schema map[string]*Schema
13}
14
15func (r *MapFieldReader) ReadField(address []string) (FieldReadResult, error) {
16 k := strings.Join(address, ".")
17 schemaList := addrToSchema(address, r.Schema)
18 if len(schemaList) == 0 {
19 return FieldReadResult{}, nil
20 }
21
22 schema := schemaList[len(schemaList)-1]
23 switch schema.Type {
24 case TypeBool, TypeInt, TypeFloat, TypeString:
25 return r.readPrimitive(address, schema)
26 case TypeList:
27 return readListField(r, address, schema)
28 case TypeMap:
29 return r.readMap(k, schema)
30 case TypeSet:
31 return r.readSet(address, schema)
32 case typeObject:
33 return readObjectField(r, address, schema.Elem.(map[string]*Schema))
34 default:
35 panic(fmt.Sprintf("Unknown type: %s", schema.Type))
36 }
37}
38
39func (r *MapFieldReader) readMap(k string, schema *Schema) (FieldReadResult, error) {
40 result := make(map[string]interface{})
41 resultSet := false
42
43 // If the name of the map field is directly in the map with an
44 // empty string, it means that the map is being deleted, so mark
45 // that is is set.
46 if v, ok := r.Map.Access(k); ok && v == "" {
47 resultSet = true
48 }
49
50 prefix := k + "."
51 r.Map.Range(func(k, v string) bool {
52 if strings.HasPrefix(k, prefix) {
53 resultSet = true
54
55 key := k[len(prefix):]
56 if key != "%" && key != "#" {
57 result[key] = v
58 }
59 }
60
61 return true
62 })
63
64 err := mapValuesToPrimitive(result, schema)
65 if err != nil {
66 return FieldReadResult{}, nil
67 }
68
69 var resultVal interface{}
70 if resultSet {
71 resultVal = result
72 }
73
74 return FieldReadResult{
75 Value: resultVal,
76 Exists: resultSet,
77 }, nil
78}
79
80func (r *MapFieldReader) readPrimitive(
81 address []string, schema *Schema) (FieldReadResult, error) {
82 k := strings.Join(address, ".")
83 result, ok := r.Map.Access(k)
84 if !ok {
85 return FieldReadResult{}, nil
86 }
87
88 returnVal, err := stringToPrimitive(result, false, schema)
89 if err != nil {
90 return FieldReadResult{}, err
91 }
92
93 return FieldReadResult{
94 Value: returnVal,
95 Exists: true,
96 }, nil
97}
98
99func (r *MapFieldReader) readSet(
100 address []string, schema *Schema) (FieldReadResult, error) {
101 // Get the number of elements in the list
102 countRaw, err := r.readPrimitive(
103 append(address, "#"), &Schema{Type: TypeInt})
104 if err != nil {
105 return FieldReadResult{}, err
106 }
107 if !countRaw.Exists {
108 // No count, means we have no list
109 countRaw.Value = 0
110 }
111
112 // Create the set that will be our result
113 set := schema.ZeroValue().(*Set)
114
115 // If we have an empty list, then return an empty list
116 if countRaw.Computed || countRaw.Value.(int) == 0 {
117 return FieldReadResult{
118 Value: set,
119 Exists: countRaw.Exists,
120 Computed: countRaw.Computed,
121 }, nil
122 }
123
124 // Go through the map and find all the set items
125 prefix := strings.Join(address, ".") + "."
126 countExpected := countRaw.Value.(int)
127 countActual := make(map[string]struct{})
128 completed := r.Map.Range(func(k, _ string) bool {
129 if !strings.HasPrefix(k, prefix) {
130 return true
131 }
132 if strings.HasPrefix(k, prefix+"#") {
133 // Ignore the count field
134 return true
135 }
136
137 // Split the key, since it might be a sub-object like "idx.field"
138 parts := strings.Split(k[len(prefix):], ".")
139 idx := parts[0]
140
141 var raw FieldReadResult
142 raw, err = r.ReadField(append(address, idx))
143 if err != nil {
144 return false
145 }
146 if !raw.Exists {
147 // This shouldn't happen because we just verified it does exist
148 panic("missing field in set: " + k + "." + idx)
149 }
150
151 set.Add(raw.Value)
152
153 // Due to the way multimap readers work, if we've seen the number
154 // of fields we expect, then exit so that we don't read later values.
155 // For example: the "set" map might have "ports.#", "ports.0", and
156 // "ports.1", but the "state" map might have those plus "ports.2".
157 // We don't want "ports.2"
158 countActual[idx] = struct{}{}
159 if len(countActual) >= countExpected {
160 return false
161 }
162
163 return true
164 })
165 if !completed && err != nil {
166 return FieldReadResult{}, err
167 }
168
169 return FieldReadResult{
170 Value: set,
171 Exists: true,
172 }, nil
173}
174
175// MapReader is an interface that is given to MapFieldReader for accessing
176// a "map". This can be used to have alternate implementations. For a basic
177// map[string]string, use BasicMapReader.
178type MapReader interface {
179 Access(string) (string, bool)
180 Range(func(string, string) bool) bool
181}
182
183// BasicMapReader implements MapReader for a single map.
184type BasicMapReader map[string]string
185
186func (r BasicMapReader) Access(k string) (string, bool) {
187 v, ok := r[k]
188 return v, ok
189}
190
191func (r BasicMapReader) Range(f func(string, string) bool) bool {
192 for k, v := range r {
193 if cont := f(k, v); !cont {
194 return false
195 }
196 }
197
198 return true
199}
200
201// MultiMapReader reads over multiple maps, preferring keys that are
202// founder earlier (lower number index) vs. later (higher number index)
203type MultiMapReader []map[string]string
204
205func (r MultiMapReader) Access(k string) (string, bool) {
206 for _, m := range r {
207 if v, ok := m[k]; ok {
208 return v, ok
209 }
210 }
211
212 return "", false
213}
214
215func (r MultiMapReader) Range(f func(string, string) bool) bool {
216 done := make(map[string]struct{})
217 for _, m := range r {
218 for k, v := range m {
219 if _, ok := done[k]; ok {
220 continue
221 }
222
223 if cont := f(k, v); !cont {
224 return false
225 }
226
227 done[k] = struct{}{}
228 }
229 }
230
231 return true
232}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_multi.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_multi.go
new file mode 100644
index 0000000..89ad3a8
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_multi.go
@@ -0,0 +1,63 @@
1package schema
2
3import (
4 "fmt"
5)
6
7// MultiLevelFieldReader reads from other field readers,
8// merging their results along the way in a specific order. You can specify
9// "levels" and name them in order to read only an exact level or up to
10// a specific level.
11//
12// This is useful for saying things such as "read the field from the state
13// and config and merge them" or "read the latest value of the field".
14type MultiLevelFieldReader struct {
15 Readers map[string]FieldReader
16 Levels []string
17}
18
19func (r *MultiLevelFieldReader) ReadField(address []string) (FieldReadResult, error) {
20 return r.ReadFieldMerge(address, r.Levels[len(r.Levels)-1])
21}
22
23func (r *MultiLevelFieldReader) ReadFieldExact(
24 address []string, level string) (FieldReadResult, error) {
25 reader, ok := r.Readers[level]
26 if !ok {
27 return FieldReadResult{}, fmt.Errorf(
28 "Unknown reader level: %s", level)
29 }
30
31 result, err := reader.ReadField(address)
32 if err != nil {
33 return FieldReadResult{}, fmt.Errorf(
34 "Error reading level %s: %s", level, err)
35 }
36
37 return result, nil
38}
39
40func (r *MultiLevelFieldReader) ReadFieldMerge(
41 address []string, level string) (FieldReadResult, error) {
42 var result FieldReadResult
43 for _, l := range r.Levels {
44 if r, ok := r.Readers[l]; ok {
45 out, err := r.ReadField(address)
46 if err != nil {
47 return FieldReadResult{}, fmt.Errorf(
48 "Error reading level %s: %s", l, err)
49 }
50
51 // TODO: computed
52 if out.Exists {
53 result = out
54 }
55 }
56
57 if l == level {
58 break
59 }
60 }
61
62 return result, nil
63}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_writer.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_writer.go
new file mode 100644
index 0000000..9abc41b
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_writer.go
@@ -0,0 +1,8 @@
1package schema
2
3// FieldWriters are responsible for writing fields by address into
4// a proper typed representation. ResourceData uses this to write new data
5// into existing sources.
6type FieldWriter interface {
7 WriteField([]string, interface{}) error
8}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go
new file mode 100644
index 0000000..689ed8d
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go
@@ -0,0 +1,319 @@
1package schema
2
3import (
4 "fmt"
5 "reflect"
6 "strconv"
7 "strings"
8 "sync"
9
10 "github.com/mitchellh/mapstructure"
11)
12
13// MapFieldWriter writes data into a single map[string]string structure.
14type MapFieldWriter struct {
15 Schema map[string]*Schema
16
17 lock sync.Mutex
18 result map[string]string
19}
20
21// Map returns the underlying map that is being written to.
22func (w *MapFieldWriter) Map() map[string]string {
23 w.lock.Lock()
24 defer w.lock.Unlock()
25 if w.result == nil {
26 w.result = make(map[string]string)
27 }
28
29 return w.result
30}
31
32func (w *MapFieldWriter) unsafeWriteField(addr string, value string) {
33 w.lock.Lock()
34 defer w.lock.Unlock()
35 if w.result == nil {
36 w.result = make(map[string]string)
37 }
38
39 w.result[addr] = value
40}
41
42func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error {
43 w.lock.Lock()
44 defer w.lock.Unlock()
45 if w.result == nil {
46 w.result = make(map[string]string)
47 }
48
49 schemaList := addrToSchema(addr, w.Schema)
50 if len(schemaList) == 0 {
51 return fmt.Errorf("Invalid address to set: %#v", addr)
52 }
53
54 // If we're setting anything other than a list root or set root,
55 // then disallow it.
56 for _, schema := range schemaList[:len(schemaList)-1] {
57 if schema.Type == TypeList {
58 return fmt.Errorf(
59 "%s: can only set full list",
60 strings.Join(addr, "."))
61 }
62
63 if schema.Type == TypeMap {
64 return fmt.Errorf(
65 "%s: can only set full map",
66 strings.Join(addr, "."))
67 }
68
69 if schema.Type == TypeSet {
70 return fmt.Errorf(
71 "%s: can only set full set",
72 strings.Join(addr, "."))
73 }
74 }
75
76 return w.set(addr, value)
77}
78
79func (w *MapFieldWriter) set(addr []string, value interface{}) error {
80 schemaList := addrToSchema(addr, w.Schema)
81 if len(schemaList) == 0 {
82 return fmt.Errorf("Invalid address to set: %#v", addr)
83 }
84
85 schema := schemaList[len(schemaList)-1]
86 switch schema.Type {
87 case TypeBool, TypeInt, TypeFloat, TypeString:
88 return w.setPrimitive(addr, value, schema)
89 case TypeList:
90 return w.setList(addr, value, schema)
91 case TypeMap:
92 return w.setMap(addr, value, schema)
93 case TypeSet:
94 return w.setSet(addr, value, schema)
95 case typeObject:
96 return w.setObject(addr, value, schema)
97 default:
98 panic(fmt.Sprintf("Unknown type: %#v", schema.Type))
99 }
100}
101
102func (w *MapFieldWriter) setList(
103 addr []string,
104 v interface{},
105 schema *Schema) error {
106 k := strings.Join(addr, ".")
107 setElement := func(idx string, value interface{}) error {
108 addrCopy := make([]string, len(addr), len(addr)+1)
109 copy(addrCopy, addr)
110 return w.set(append(addrCopy, idx), value)
111 }
112
113 var vs []interface{}
114 if err := mapstructure.Decode(v, &vs); err != nil {
115 return fmt.Errorf("%s: %s", k, err)
116 }
117
118 // Set the entire list.
119 var err error
120 for i, elem := range vs {
121 is := strconv.FormatInt(int64(i), 10)
122 err = setElement(is, elem)
123 if err != nil {
124 break
125 }
126 }
127 if err != nil {
128 for i, _ := range vs {
129 is := strconv.FormatInt(int64(i), 10)
130 setElement(is, nil)
131 }
132
133 return err
134 }
135
136 w.result[k+".#"] = strconv.FormatInt(int64(len(vs)), 10)
137 return nil
138}
139
140func (w *MapFieldWriter) setMap(
141 addr []string,
142 value interface{},
143 schema *Schema) error {
144 k := strings.Join(addr, ".")
145 v := reflect.ValueOf(value)
146 vs := make(map[string]interface{})
147
148 if value == nil {
149 // The empty string here means the map is removed.
150 w.result[k] = ""
151 return nil
152 }
153
154 if v.Kind() != reflect.Map {
155 return fmt.Errorf("%s: must be a map", k)
156 }
157 if v.Type().Key().Kind() != reflect.String {
158 return fmt.Errorf("%s: keys must strings", k)
159 }
160 for _, mk := range v.MapKeys() {
161 mv := v.MapIndex(mk)
162 vs[mk.String()] = mv.Interface()
163 }
164
165 // Remove the pure key since we're setting the full map value
166 delete(w.result, k)
167
168 // Set each subkey
169 addrCopy := make([]string, len(addr), len(addr)+1)
170 copy(addrCopy, addr)
171 for subKey, v := range vs {
172 if err := w.set(append(addrCopy, subKey), v); err != nil {
173 return err
174 }
175 }
176
177 // Set the count
178 w.result[k+".%"] = strconv.Itoa(len(vs))
179
180 return nil
181}
182
183func (w *MapFieldWriter) setObject(
184 addr []string,
185 value interface{},
186 schema *Schema) error {
187 // Set the entire object. First decode into a proper structure
188 var v map[string]interface{}
189 if err := mapstructure.Decode(value, &v); err != nil {
190 return fmt.Errorf("%s: %s", strings.Join(addr, "."), err)
191 }
192
193 // Make space for additional elements in the address
194 addrCopy := make([]string, len(addr), len(addr)+1)
195 copy(addrCopy, addr)
196
197 // Set each element in turn
198 var err error
199 for k1, v1 := range v {
200 if err = w.set(append(addrCopy, k1), v1); err != nil {
201 break
202 }
203 }
204 if err != nil {
205 for k1, _ := range v {
206 w.set(append(addrCopy, k1), nil)
207 }
208 }
209
210 return err
211}
212
213func (w *MapFieldWriter) setPrimitive(
214 addr []string,
215 v interface{},
216 schema *Schema) error {
217 k := strings.Join(addr, ".")
218
219 if v == nil {
220 // The empty string here means the value is removed.
221 w.result[k] = ""
222 return nil
223 }
224
225 var set string
226 switch schema.Type {
227 case TypeBool:
228 var b bool
229 if err := mapstructure.Decode(v, &b); err != nil {
230 return fmt.Errorf("%s: %s", k, err)
231 }
232
233 set = strconv.FormatBool(b)
234 case TypeString:
235 if err := mapstructure.Decode(v, &set); err != nil {
236 return fmt.Errorf("%s: %s", k, err)
237 }
238 case TypeInt:
239 var n int
240 if err := mapstructure.Decode(v, &n); err != nil {
241 return fmt.Errorf("%s: %s", k, err)
242 }
243 set = strconv.FormatInt(int64(n), 10)
244 case TypeFloat:
245 var n float64
246 if err := mapstructure.Decode(v, &n); err != nil {
247 return fmt.Errorf("%s: %s", k, err)
248 }
249 set = strconv.FormatFloat(float64(n), 'G', -1, 64)
250 default:
251 return fmt.Errorf("Unknown type: %#v", schema.Type)
252 }
253
254 w.result[k] = set
255 return nil
256}
257
258func (w *MapFieldWriter) setSet(
259 addr []string,
260 value interface{},
261 schema *Schema) error {
262 addrCopy := make([]string, len(addr), len(addr)+1)
263 copy(addrCopy, addr)
264 k := strings.Join(addr, ".")
265
266 if value == nil {
267 w.result[k+".#"] = "0"
268 return nil
269 }
270
271 // If it is a slice, then we have to turn it into a *Set so that
272 // we get the proper order back based on the hash code.
273 if v := reflect.ValueOf(value); v.Kind() == reflect.Slice {
274 // Build a temp *ResourceData to use for the conversion
275 tempSchema := *schema
276 tempSchema.Type = TypeList
277 tempSchemaMap := map[string]*Schema{addr[0]: &tempSchema}
278 tempW := &MapFieldWriter{Schema: tempSchemaMap}
279
280 // Set the entire list, this lets us get sane values out of it
281 if err := tempW.WriteField(addr, value); err != nil {
282 return err
283 }
284
285 // Build the set by going over the list items in order and
286 // hashing them into the set. The reason we go over the list and
287 // not the `value` directly is because this forces all types
288 // to become []interface{} (generic) instead of []string, which
289 // most hash functions are expecting.
290 s := schema.ZeroValue().(*Set)
291 tempR := &MapFieldReader{
292 Map: BasicMapReader(tempW.Map()),
293 Schema: tempSchemaMap,
294 }
295 for i := 0; i < v.Len(); i++ {
296 is := strconv.FormatInt(int64(i), 10)
297 result, err := tempR.ReadField(append(addrCopy, is))
298 if err != nil {
299 return err
300 }
301 if !result.Exists {
302 panic("set item just set doesn't exist")
303 }
304
305 s.Add(result.Value)
306 }
307
308 value = s
309 }
310
311 for code, elem := range value.(*Set).m {
312 if err := w.set(append(addrCopy, code), elem); err != nil {
313 return err
314 }
315 }
316
317 w.result[k+".#"] = strconv.Itoa(value.(*Set).Len())
318 return nil
319}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go b/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go
new file mode 100644
index 0000000..3a97629
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go
@@ -0,0 +1,36 @@
1// Code generated by "stringer -type=getSource resource_data_get_source.go"; DO NOT EDIT.
2
3package schema
4
5import "fmt"
6
7const (
8 _getSource_name_0 = "getSourceStategetSourceConfig"
9 _getSource_name_1 = "getSourceDiff"
10 _getSource_name_2 = "getSourceSet"
11 _getSource_name_3 = "getSourceLevelMaskgetSourceExact"
12)
13
14var (
15 _getSource_index_0 = [...]uint8{0, 14, 29}
16 _getSource_index_1 = [...]uint8{0, 13}
17 _getSource_index_2 = [...]uint8{0, 12}
18 _getSource_index_3 = [...]uint8{0, 18, 32}
19)
20
21func (i getSource) String() string {
22 switch {
23 case 1 <= i && i <= 2:
24 i -= 1
25 return _getSource_name_0[_getSource_index_0[i]:_getSource_index_0[i+1]]
26 case i == 4:
27 return _getSource_name_1
28 case i == 8:
29 return _getSource_name_2
30 case 15 <= i && i <= 16:
31 i -= 15
32 return _getSource_name_3[_getSource_index_3[i]:_getSource_index_3[i+1]]
33 default:
34 return fmt.Sprintf("getSource(%d)", i)
35 }
36}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provider.go b/vendor/github.com/hashicorp/terraform/helper/schema/provider.go
new file mode 100644
index 0000000..d52d2f5
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/provider.go
@@ -0,0 +1,400 @@
1package schema
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "sort"
8 "sync"
9
10 "github.com/hashicorp/go-multierror"
11 "github.com/hashicorp/terraform/terraform"
12)
13
14// Provider represents a resource provider in Terraform, and properly
15// implements all of the ResourceProvider API.
16//
17// By defining a schema for the configuration of the provider, the
18// map of supporting resources, and a configuration function, the schema
19// framework takes over and handles all the provider operations for you.
20//
21// After defining the provider structure, it is unlikely that you'll require any
22// of the methods on Provider itself.
23type Provider struct {
24 // Schema is the schema for the configuration of this provider. If this
25 // provider has no configuration, this can be omitted.
26 //
27 // The keys of this map are the configuration keys, and the value is
28 // the schema describing the value of the configuration.
29 Schema map[string]*Schema
30
31 // ResourcesMap is the list of available resources that this provider
32 // can manage, along with their Resource structure defining their
33 // own schemas and CRUD operations.
34 //
35 // Provider automatically handles routing operations such as Apply,
36 // Diff, etc. to the proper resource.
37 ResourcesMap map[string]*Resource
38
39 // DataSourcesMap is the collection of available data sources that
40 // this provider implements, with a Resource instance defining
41 // the schema and Read operation of each.
42 //
43 // Resource instances for data sources must have a Read function
44 // and must *not* implement Create, Update or Delete.
45 DataSourcesMap map[string]*Resource
46
47 // ConfigureFunc is a function for configuring the provider. If the
48 // provider doesn't need to be configured, this can be omitted.
49 //
50 // See the ConfigureFunc documentation for more information.
51 ConfigureFunc ConfigureFunc
52
53 // MetaReset is called by TestReset to reset any state stored in the meta
54 // interface. This is especially important if the StopContext is stored by
55 // the provider.
56 MetaReset func() error
57
58 meta interface{}
59
60 // a mutex is required because TestReset can directly repalce the stopCtx
61 stopMu sync.Mutex
62 stopCtx context.Context
63 stopCtxCancel context.CancelFunc
64 stopOnce sync.Once
65}
66
67// ConfigureFunc is the function used to configure a Provider.
68//
69// The interface{} value returned by this function is stored and passed into
70// the subsequent resources as the meta parameter. This return value is
71// usually used to pass along a configured API client, a configuration
72// structure, etc.
73type ConfigureFunc func(*ResourceData) (interface{}, error)
74
75// InternalValidate should be called to validate the structure
76// of the provider.
77//
78// This should be called in a unit test for any provider to verify
79// before release that a provider is properly configured for use with
80// this library.
81func (p *Provider) InternalValidate() error {
82 if p == nil {
83 return errors.New("provider is nil")
84 }
85
86 var validationErrors error
87 sm := schemaMap(p.Schema)
88 if err := sm.InternalValidate(sm); err != nil {
89 validationErrors = multierror.Append(validationErrors, err)
90 }
91
92 for k, r := range p.ResourcesMap {
93 if err := r.InternalValidate(nil, true); err != nil {
94 validationErrors = multierror.Append(validationErrors, fmt.Errorf("resource %s: %s", k, err))
95 }
96 }
97
98 for k, r := range p.DataSourcesMap {
99 if err := r.InternalValidate(nil, false); err != nil {
100 validationErrors = multierror.Append(validationErrors, fmt.Errorf("data source %s: %s", k, err))
101 }
102 }
103
104 return validationErrors
105}
106
107// Meta returns the metadata associated with this provider that was
108// returned by the Configure call. It will be nil until Configure is called.
109func (p *Provider) Meta() interface{} {
110 return p.meta
111}
112
113// SetMeta can be used to forcefully set the Meta object of the provider.
114// Note that if Configure is called the return value will override anything
115// set here.
116func (p *Provider) SetMeta(v interface{}) {
117 p.meta = v
118}
119
120// Stopped reports whether the provider has been stopped or not.
121func (p *Provider) Stopped() bool {
122 ctx := p.StopContext()
123 select {
124 case <-ctx.Done():
125 return true
126 default:
127 return false
128 }
129}
130
131// StopCh returns a channel that is closed once the provider is stopped.
132func (p *Provider) StopContext() context.Context {
133 p.stopOnce.Do(p.stopInit)
134
135 p.stopMu.Lock()
136 defer p.stopMu.Unlock()
137
138 return p.stopCtx
139}
140
141func (p *Provider) stopInit() {
142 p.stopMu.Lock()
143 defer p.stopMu.Unlock()
144
145 p.stopCtx, p.stopCtxCancel = context.WithCancel(context.Background())
146}
147
148// Stop implementation of terraform.ResourceProvider interface.
149func (p *Provider) Stop() error {
150 p.stopOnce.Do(p.stopInit)
151
152 p.stopMu.Lock()
153 defer p.stopMu.Unlock()
154
155 p.stopCtxCancel()
156 return nil
157}
158
159// TestReset resets any state stored in the Provider, and will call TestReset
160// on Meta if it implements the TestProvider interface.
161// This may be used to reset the schema.Provider at the start of a test, and is
162// automatically called by resource.Test.
163func (p *Provider) TestReset() error {
164 p.stopInit()
165 if p.MetaReset != nil {
166 return p.MetaReset()
167 }
168 return nil
169}
170
171// Input implementation of terraform.ResourceProvider interface.
172func (p *Provider) Input(
173 input terraform.UIInput,
174 c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
175 return schemaMap(p.Schema).Input(input, c)
176}
177
178// Validate implementation of terraform.ResourceProvider interface.
179func (p *Provider) Validate(c *terraform.ResourceConfig) ([]string, []error) {
180 if err := p.InternalValidate(); err != nil {
181 return nil, []error{fmt.Errorf(
182 "Internal validation of the provider failed! This is always a bug\n"+
183 "with the provider itself, and not a user issue. Please report\n"+
184 "this bug:\n\n%s", err)}
185 }
186
187 return schemaMap(p.Schema).Validate(c)
188}
189
190// ValidateResource implementation of terraform.ResourceProvider interface.
191func (p *Provider) ValidateResource(
192 t string, c *terraform.ResourceConfig) ([]string, []error) {
193 r, ok := p.ResourcesMap[t]
194 if !ok {
195 return nil, []error{fmt.Errorf(
196 "Provider doesn't support resource: %s", t)}
197 }
198
199 return r.Validate(c)
200}
201
202// Configure implementation of terraform.ResourceProvider interface.
203func (p *Provider) Configure(c *terraform.ResourceConfig) error {
204 // No configuration
205 if p.ConfigureFunc == nil {
206 return nil
207 }
208
209 sm := schemaMap(p.Schema)
210
211 // Get a ResourceData for this configuration. To do this, we actually
212 // generate an intermediary "diff" although that is never exposed.
213 diff, err := sm.Diff(nil, c)
214 if err != nil {
215 return err
216 }
217
218 data, err := sm.Data(nil, diff)
219 if err != nil {
220 return err
221 }
222
223 meta, err := p.ConfigureFunc(data)
224 if err != nil {
225 return err
226 }
227
228 p.meta = meta
229 return nil
230}
231
232// Apply implementation of terraform.ResourceProvider interface.
233func (p *Provider) Apply(
234 info *terraform.InstanceInfo,
235 s *terraform.InstanceState,
236 d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
237 r, ok := p.ResourcesMap[info.Type]
238 if !ok {
239 return nil, fmt.Errorf("unknown resource type: %s", info.Type)
240 }
241
242 return r.Apply(s, d, p.meta)
243}
244
245// Diff implementation of terraform.ResourceProvider interface.
246func (p *Provider) Diff(
247 info *terraform.InstanceInfo,
248 s *terraform.InstanceState,
249 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
250 r, ok := p.ResourcesMap[info.Type]
251 if !ok {
252 return nil, fmt.Errorf("unknown resource type: %s", info.Type)
253 }
254
255 return r.Diff(s, c)
256}
257
258// Refresh implementation of terraform.ResourceProvider interface.
259func (p *Provider) Refresh(
260 info *terraform.InstanceInfo,
261 s *terraform.InstanceState) (*terraform.InstanceState, error) {
262 r, ok := p.ResourcesMap[info.Type]
263 if !ok {
264 return nil, fmt.Errorf("unknown resource type: %s", info.Type)
265 }
266
267 return r.Refresh(s, p.meta)
268}
269
270// Resources implementation of terraform.ResourceProvider interface.
271func (p *Provider) Resources() []terraform.ResourceType {
272 keys := make([]string, 0, len(p.ResourcesMap))
273 for k, _ := range p.ResourcesMap {
274 keys = append(keys, k)
275 }
276 sort.Strings(keys)
277
278 result := make([]terraform.ResourceType, 0, len(keys))
279 for _, k := range keys {
280 resource := p.ResourcesMap[k]
281
282 // This isn't really possible (it'd fail InternalValidate), but
283 // we do it anyways to avoid a panic.
284 if resource == nil {
285 resource = &Resource{}
286 }
287
288 result = append(result, terraform.ResourceType{
289 Name: k,
290 Importable: resource.Importer != nil,
291 })
292 }
293
294 return result
295}
296
297func (p *Provider) ImportState(
298 info *terraform.InstanceInfo,
299 id string) ([]*terraform.InstanceState, error) {
300 // Find the resource
301 r, ok := p.ResourcesMap[info.Type]
302 if !ok {
303 return nil, fmt.Errorf("unknown resource type: %s", info.Type)
304 }
305
306 // If it doesn't support import, error
307 if r.Importer == nil {
308 return nil, fmt.Errorf("resource %s doesn't support import", info.Type)
309 }
310
311 // Create the data
312 data := r.Data(nil)
313 data.SetId(id)
314 data.SetType(info.Type)
315
316 // Call the import function
317 results := []*ResourceData{data}
318 if r.Importer.State != nil {
319 var err error
320 results, err = r.Importer.State(data, p.meta)
321 if err != nil {
322 return nil, err
323 }
324 }
325
326 // Convert the results to InstanceState values and return it
327 states := make([]*terraform.InstanceState, len(results))
328 for i, r := range results {
329 states[i] = r.State()
330 }
331
332 // Verify that all are non-nil. If there are any nil the error
333 // isn't obvious so we circumvent that with a friendlier error.
334 for _, s := range states {
335 if s == nil {
336 return nil, fmt.Errorf(
337 "nil entry in ImportState results. This is always a bug with\n" +
338 "the resource that is being imported. Please report this as\n" +
339 "a bug to Terraform.")
340 }
341 }
342
343 return states, nil
344}
345
346// ValidateDataSource implementation of terraform.ResourceProvider interface.
347func (p *Provider) ValidateDataSource(
348 t string, c *terraform.ResourceConfig) ([]string, []error) {
349 r, ok := p.DataSourcesMap[t]
350 if !ok {
351 return nil, []error{fmt.Errorf(
352 "Provider doesn't support data source: %s", t)}
353 }
354
355 return r.Validate(c)
356}
357
358// ReadDataDiff implementation of terraform.ResourceProvider interface.
359func (p *Provider) ReadDataDiff(
360 info *terraform.InstanceInfo,
361 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
362
363 r, ok := p.DataSourcesMap[info.Type]
364 if !ok {
365 return nil, fmt.Errorf("unknown data source: %s", info.Type)
366 }
367
368 return r.Diff(nil, c)
369}
370
371// RefreshData implementation of terraform.ResourceProvider interface.
372func (p *Provider) ReadDataApply(
373 info *terraform.InstanceInfo,
374 d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
375
376 r, ok := p.DataSourcesMap[info.Type]
377 if !ok {
378 return nil, fmt.Errorf("unknown data source: %s", info.Type)
379 }
380
381 return r.ReadDataApply(d, p.meta)
382}
383
384// DataSources implementation of terraform.ResourceProvider interface.
385func (p *Provider) DataSources() []terraform.DataSource {
386 keys := make([]string, 0, len(p.DataSourcesMap))
387 for k, _ := range p.DataSourcesMap {
388 keys = append(keys, k)
389 }
390 sort.Strings(keys)
391
392 result := make([]terraform.DataSource, 0, len(keys))
393 for _, k := range keys {
394 result = append(result, terraform.DataSource{
395 Name: k,
396 })
397 }
398
399 return result
400}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go
new file mode 100644
index 0000000..c1564a2
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go
@@ -0,0 +1,180 @@
1package schema
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "sync"
8
9 "github.com/hashicorp/go-multierror"
10 "github.com/hashicorp/terraform/config"
11 "github.com/hashicorp/terraform/terraform"
12)
13
14// Provisioner represents a resource provisioner in Terraform and properly
15// implements all of the ResourceProvisioner API.
16//
17// This higher level structure makes it much easier to implement a new or
18// custom provisioner for Terraform.
19//
20// The function callbacks for this structure are all passed a context object.
21// This context object has a number of pre-defined values that can be accessed
22// via the global functions defined in context.go.
23type Provisioner struct {
24 // ConnSchema is the schema for the connection settings for this
25 // provisioner.
26 //
27 // The keys of this map are the configuration keys, and the value is
28 // the schema describing the value of the configuration.
29 //
30 // NOTE: The value of connection keys can only be strings for now.
31 ConnSchema map[string]*Schema
32
33 // Schema is the schema for the usage of this provisioner.
34 //
35 // The keys of this map are the configuration keys, and the value is
36 // the schema describing the value of the configuration.
37 Schema map[string]*Schema
38
39 // ApplyFunc is the function for executing the provisioner. This is required.
40 // It is given a context. See the Provisioner struct docs for more
41 // information.
42 ApplyFunc func(ctx context.Context) error
43
44 stopCtx context.Context
45 stopCtxCancel context.CancelFunc
46 stopOnce sync.Once
47}
48
49// Keys that can be used to access data in the context parameters for
50// Provisioners.
51var (
52 connDataInvalid = contextKey("data invalid")
53
54 // This returns a *ResourceData for the connection information.
55 // Guaranteed to never be nil.
56 ProvConnDataKey = contextKey("provider conn data")
57
58 // This returns a *ResourceData for the config information.
59 // Guaranteed to never be nil.
60 ProvConfigDataKey = contextKey("provider config data")
61
62 // This returns a terraform.UIOutput. Guaranteed to never be nil.
63 ProvOutputKey = contextKey("provider output")
64
65 // This returns the raw InstanceState passed to Apply. Guaranteed to
66 // be set, but may be nil.
67 ProvRawStateKey = contextKey("provider raw state")
68)
69
70// InternalValidate should be called to validate the structure
71// of the provisioner.
72//
73// This should be called in a unit test to verify before release that this
74// structure is properly configured for use.
75func (p *Provisioner) InternalValidate() error {
76 if p == nil {
77 return errors.New("provisioner is nil")
78 }
79
80 var validationErrors error
81 {
82 sm := schemaMap(p.ConnSchema)
83 if err := sm.InternalValidate(sm); err != nil {
84 validationErrors = multierror.Append(validationErrors, err)
85 }
86 }
87
88 {
89 sm := schemaMap(p.Schema)
90 if err := sm.InternalValidate(sm); err != nil {
91 validationErrors = multierror.Append(validationErrors, err)
92 }
93 }
94
95 if p.ApplyFunc == nil {
96 validationErrors = multierror.Append(validationErrors, fmt.Errorf(
97 "ApplyFunc must not be nil"))
98 }
99
100 return validationErrors
101}
102
103// StopContext returns a context that checks whether a provisioner is stopped.
104func (p *Provisioner) StopContext() context.Context {
105 p.stopOnce.Do(p.stopInit)
106 return p.stopCtx
107}
108
109func (p *Provisioner) stopInit() {
110 p.stopCtx, p.stopCtxCancel = context.WithCancel(context.Background())
111}
112
113// Stop implementation of terraform.ResourceProvisioner interface.
114func (p *Provisioner) Stop() error {
115 p.stopOnce.Do(p.stopInit)
116 p.stopCtxCancel()
117 return nil
118}
119
120func (p *Provisioner) Validate(c *terraform.ResourceConfig) ([]string, []error) {
121 return schemaMap(p.Schema).Validate(c)
122}
123
124// Apply implementation of terraform.ResourceProvisioner interface.
125func (p *Provisioner) Apply(
126 o terraform.UIOutput,
127 s *terraform.InstanceState,
128 c *terraform.ResourceConfig) error {
129 var connData, configData *ResourceData
130
131 {
132 // We first need to turn the connection information into a
133 // terraform.ResourceConfig so that we can use that type to more
134 // easily build a ResourceData structure. We do this by simply treating
135 // the conn info as configuration input.
136 raw := make(map[string]interface{})
137 if s != nil {
138 for k, v := range s.Ephemeral.ConnInfo {
139 raw[k] = v
140 }
141 }
142
143 c, err := config.NewRawConfig(raw)
144 if err != nil {
145 return err
146 }
147
148 sm := schemaMap(p.ConnSchema)
149 diff, err := sm.Diff(nil, terraform.NewResourceConfig(c))
150 if err != nil {
151 return err
152 }
153 connData, err = sm.Data(nil, diff)
154 if err != nil {
155 return err
156 }
157 }
158
159 {
160 // Build the configuration data. Doing this requires making a "diff"
161 // even though that's never used. We use that just to get the correct types.
162 configMap := schemaMap(p.Schema)
163 diff, err := configMap.Diff(nil, c)
164 if err != nil {
165 return err
166 }
167 configData, err = configMap.Data(nil, diff)
168 if err != nil {
169 return err
170 }
171 }
172
173 // Build the context and call the function
174 ctx := p.StopContext()
175 ctx = context.WithValue(ctx, ProvConnDataKey, connData)
176 ctx = context.WithValue(ctx, ProvConfigDataKey, configData)
177 ctx = context.WithValue(ctx, ProvOutputKey, o)
178 ctx = context.WithValue(ctx, ProvRawStateKey, s)
179 return p.ApplyFunc(ctx)
180}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go
new file mode 100644
index 0000000..c810558
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go
@@ -0,0 +1,478 @@
1package schema
2
3import (
4 "errors"
5 "fmt"
6 "log"
7 "strconv"
8
9 "github.com/hashicorp/terraform/terraform"
10)
11
12// Resource represents a thing in Terraform that has a set of configurable
13// attributes and a lifecycle (create, read, update, delete).
14//
15// The Resource schema is an abstraction that allows provider writers to
16// worry only about CRUD operations while off-loading validation, diff
17// generation, etc. to this higher level library.
18//
19// In spite of the name, this struct is not used only for terraform resources,
20// but also for data sources. In the case of data sources, the Create,
21// Update and Delete functions must not be provided.
22type Resource struct {
23 // Schema is the schema for the configuration of this resource.
24 //
25 // The keys of this map are the configuration keys, and the values
26 // describe the schema of the configuration value.
27 //
28 // The schema is used to represent both configurable data as well
29 // as data that might be computed in the process of creating this
30 // resource.
31 Schema map[string]*Schema
32
33 // SchemaVersion is the version number for this resource's Schema
34 // definition. The current SchemaVersion stored in the state for each
35 // resource. Provider authors can increment this version number
36 // when Schema semantics change. If the State's SchemaVersion is less than
37 // the current SchemaVersion, the InstanceState is yielded to the
38 // MigrateState callback, where the provider can make whatever changes it
39 // needs to update the state to be compatible to the latest version of the
40 // Schema.
41 //
42 // When unset, SchemaVersion defaults to 0, so provider authors can start
43 // their Versioning at any integer >= 1
44 SchemaVersion int
45
46 // MigrateState is responsible for updating an InstanceState with an old
47 // version to the format expected by the current version of the Schema.
48 //
49 // It is called during Refresh if the State's stored SchemaVersion is less
50 // than the current SchemaVersion of the Resource.
51 //
52 // The function is yielded the state's stored SchemaVersion and a pointer to
53 // the InstanceState that needs updating, as well as the configured
54 // provider's configured meta interface{}, in case the migration process
55 // needs to make any remote API calls.
56 MigrateState StateMigrateFunc
57
58 // The functions below are the CRUD operations for this resource.
59 //
60 // The only optional operation is Update. If Update is not implemented,
61 // then updates will not be supported for this resource.
62 //
63 // The ResourceData parameter in the functions below are used to
64 // query configuration and changes for the resource as well as to set
65 // the ID, computed data, etc.
66 //
67 // The interface{} parameter is the result of the ConfigureFunc in
68 // the provider for this resource. If the provider does not define
69 // a ConfigureFunc, this will be nil. This parameter should be used
70 // to store API clients, configuration structures, etc.
71 //
72 // If any errors occur during each of the operation, an error should be
73 // returned. If a resource was partially updated, be careful to enable
74 // partial state mode for ResourceData and use it accordingly.
75 //
76 // Exists is a function that is called to check if a resource still
77 // exists. If this returns false, then this will affect the diff
78 // accordingly. If this function isn't set, it will not be called. It
79 // is highly recommended to set it. The *ResourceData passed to Exists
80 // should _not_ be modified.
81 Create CreateFunc
82 Read ReadFunc
83 Update UpdateFunc
84 Delete DeleteFunc
85 Exists ExistsFunc
86
87 // Importer is the ResourceImporter implementation for this resource.
88 // If this is nil, then this resource does not support importing. If
89 // this is non-nil, then it supports importing and ResourceImporter
90 // must be validated. The validity of ResourceImporter is verified
91 // by InternalValidate on Resource.
92 Importer *ResourceImporter
93
94 // If non-empty, this string is emitted as a warning during Validate.
95 // This is a private interface for now, for use by DataSourceResourceShim,
96 // and not for general use. (But maybe later...)
97 deprecationMessage string
98
99 // Timeouts allow users to specify specific time durations in which an
100 // operation should time out, to allow them to extend an action to suit their
101 // usage. For example, a user may specify a large Creation timeout for their
102 // AWS RDS Instance due to it's size, or restoring from a snapshot.
103 // Resource implementors must enable Timeout support by adding the allowed
104 // actions (Create, Read, Update, Delete, Default) to the Resource struct, and
105 // accessing them in the matching methods.
106 Timeouts *ResourceTimeout
107}
108
109// See Resource documentation.
110type CreateFunc func(*ResourceData, interface{}) error
111
112// See Resource documentation.
113type ReadFunc func(*ResourceData, interface{}) error
114
115// See Resource documentation.
116type UpdateFunc func(*ResourceData, interface{}) error
117
118// See Resource documentation.
119type DeleteFunc func(*ResourceData, interface{}) error
120
121// See Resource documentation.
122type ExistsFunc func(*ResourceData, interface{}) (bool, error)
123
124// See Resource documentation.
125type StateMigrateFunc func(
126 int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error)
127
128// Apply creates, updates, and/or deletes a resource.
129func (r *Resource) Apply(
130 s *terraform.InstanceState,
131 d *terraform.InstanceDiff,
132 meta interface{}) (*terraform.InstanceState, error) {
133 data, err := schemaMap(r.Schema).Data(s, d)
134 if err != nil {
135 return s, err
136 }
137
138 // Instance Diff shoould have the timeout info, need to copy it over to the
139 // ResourceData meta
140 rt := ResourceTimeout{}
141 if _, ok := d.Meta[TimeoutKey]; ok {
142 if err := rt.DiffDecode(d); err != nil {
143 log.Printf("[ERR] Error decoding ResourceTimeout: %s", err)
144 }
145 } else {
146 log.Printf("[DEBUG] No meta timeoutkey found in Apply()")
147 }
148 data.timeouts = &rt
149
150 if s == nil {
151 // The Terraform API dictates that this should never happen, but
152 // it doesn't hurt to be safe in this case.
153 s = new(terraform.InstanceState)
154 }
155
156 if d.Destroy || d.RequiresNew() {
157 if s.ID != "" {
158 // Destroy the resource since it is created
159 if err := r.Delete(data, meta); err != nil {
160 return r.recordCurrentSchemaVersion(data.State()), err
161 }
162
163 // Make sure the ID is gone.
164 data.SetId("")
165 }
166
167 // If we're only destroying, and not creating, then return
168 // now since we're done!
169 if !d.RequiresNew() {
170 return nil, nil
171 }
172
173 // Reset the data to be stateless since we just destroyed
174 data, err = schemaMap(r.Schema).Data(nil, d)
175 // data was reset, need to re-apply the parsed timeouts
176 data.timeouts = &rt
177 if err != nil {
178 return nil, err
179 }
180 }
181
182 err = nil
183 if data.Id() == "" {
184 // We're creating, it is a new resource.
185 data.MarkNewResource()
186 err = r.Create(data, meta)
187 } else {
188 if r.Update == nil {
189 return s, fmt.Errorf("doesn't support update")
190 }
191
192 err = r.Update(data, meta)
193 }
194
195 return r.recordCurrentSchemaVersion(data.State()), err
196}
197
198// Diff returns a diff of this resource and is API compatible with the
199// ResourceProvider interface.
200func (r *Resource) Diff(
201 s *terraform.InstanceState,
202 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
203
204 t := &ResourceTimeout{}
205 err := t.ConfigDecode(r, c)
206
207 if err != nil {
208 return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err)
209 }
210
211 instanceDiff, err := schemaMap(r.Schema).Diff(s, c)
212 if err != nil {
213 return instanceDiff, err
214 }
215
216 if instanceDiff != nil {
217 if err := t.DiffEncode(instanceDiff); err != nil {
218 log.Printf("[ERR] Error encoding timeout to instance diff: %s", err)
219 }
220 } else {
221 log.Printf("[DEBUG] Instance Diff is nil in Diff()")
222 }
223
224 return instanceDiff, err
225}
226
227// Validate validates the resource configuration against the schema.
228func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) {
229 warns, errs := schemaMap(r.Schema).Validate(c)
230
231 if r.deprecationMessage != "" {
232 warns = append(warns, r.deprecationMessage)
233 }
234
235 return warns, errs
236}
237
238// ReadDataApply loads the data for a data source, given a diff that
239// describes the configuration arguments and desired computed attributes.
240func (r *Resource) ReadDataApply(
241 d *terraform.InstanceDiff,
242 meta interface{},
243) (*terraform.InstanceState, error) {
244
245 // Data sources are always built completely from scratch
246 // on each read, so the source state is always nil.
247 data, err := schemaMap(r.Schema).Data(nil, d)
248 if err != nil {
249 return nil, err
250 }
251
252 err = r.Read(data, meta)
253 state := data.State()
254 if state != nil && state.ID == "" {
255 // Data sources can set an ID if they want, but they aren't
256 // required to; we'll provide a placeholder if they don't,
257 // to preserve the invariant that all resources have non-empty
258 // ids.
259 state.ID = "-"
260 }
261
262 return r.recordCurrentSchemaVersion(state), err
263}
264
265// Refresh refreshes the state of the resource.
266func (r *Resource) Refresh(
267 s *terraform.InstanceState,
268 meta interface{}) (*terraform.InstanceState, error) {
269 // If the ID is already somehow blank, it doesn't exist
270 if s.ID == "" {
271 return nil, nil
272 }
273
274 rt := ResourceTimeout{}
275 if _, ok := s.Meta[TimeoutKey]; ok {
276 if err := rt.StateDecode(s); err != nil {
277 log.Printf("[ERR] Error decoding ResourceTimeout: %s", err)
278 }
279 }
280
281 if r.Exists != nil {
282 // Make a copy of data so that if it is modified it doesn't
283 // affect our Read later.
284 data, err := schemaMap(r.Schema).Data(s, nil)
285 data.timeouts = &rt
286
287 if err != nil {
288 return s, err
289 }
290
291 exists, err := r.Exists(data, meta)
292 if err != nil {
293 return s, err
294 }
295 if !exists {
296 return nil, nil
297 }
298 }
299
300 needsMigration, stateSchemaVersion := r.checkSchemaVersion(s)
301 if needsMigration && r.MigrateState != nil {
302 s, err := r.MigrateState(stateSchemaVersion, s, meta)
303 if err != nil {
304 return s, err
305 }
306 }
307
308 data, err := schemaMap(r.Schema).Data(s, nil)
309 data.timeouts = &rt
310 if err != nil {
311 return s, err
312 }
313
314 err = r.Read(data, meta)
315 state := data.State()
316 if state != nil && state.ID == "" {
317 state = nil
318 }
319
320 return r.recordCurrentSchemaVersion(state), err
321}
322
323// InternalValidate should be called to validate the structure
324// of the resource.
325//
326// This should be called in a unit test for any resource to verify
327// before release that a resource is properly configured for use with
328// this library.
329//
330// Provider.InternalValidate() will automatically call this for all of
331// the resources it manages, so you don't need to call this manually if it
332// is part of a Provider.
333func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error {
334 if r == nil {
335 return errors.New("resource is nil")
336 }
337
338 if !writable {
339 if r.Create != nil || r.Update != nil || r.Delete != nil {
340 return fmt.Errorf("must not implement Create, Update or Delete")
341 }
342 }
343
344 tsm := topSchemaMap
345
346 if r.isTopLevel() && writable {
347 // All non-Computed attributes must be ForceNew if Update is not defined
348 if r.Update == nil {
349 nonForceNewAttrs := make([]string, 0)
350 for k, v := range r.Schema {
351 if !v.ForceNew && !v.Computed {
352 nonForceNewAttrs = append(nonForceNewAttrs, k)
353 }
354 }
355 if len(nonForceNewAttrs) > 0 {
356 return fmt.Errorf(
357 "No Update defined, must set ForceNew on: %#v", nonForceNewAttrs)
358 }
359 } else {
360 nonUpdateableAttrs := make([]string, 0)
361 for k, v := range r.Schema {
362 if v.ForceNew || v.Computed && !v.Optional {
363 nonUpdateableAttrs = append(nonUpdateableAttrs, k)
364 }
365 }
366 updateableAttrs := len(r.Schema) - len(nonUpdateableAttrs)
367 if updateableAttrs == 0 {
368 return fmt.Errorf(
369 "All fields are ForceNew or Computed w/out Optional, Update is superfluous")
370 }
371 }
372
373 tsm = schemaMap(r.Schema)
374
375 // Destroy, and Read are required
376 if r.Read == nil {
377 return fmt.Errorf("Read must be implemented")
378 }
379 if r.Delete == nil {
380 return fmt.Errorf("Delete must be implemented")
381 }
382
383 // If we have an importer, we need to verify the importer.
384 if r.Importer != nil {
385 if err := r.Importer.InternalValidate(); err != nil {
386 return err
387 }
388 }
389 }
390
391 return schemaMap(r.Schema).InternalValidate(tsm)
392}
393
394// Data returns a ResourceData struct for this Resource. Each return value
395// is a separate copy and can be safely modified differently.
396//
397// The data returned from this function has no actual affect on the Resource
398// itself (including the state given to this function).
399//
400// This function is useful for unit tests and ResourceImporter functions.
401func (r *Resource) Data(s *terraform.InstanceState) *ResourceData {
402 result, err := schemaMap(r.Schema).Data(s, nil)
403 if err != nil {
404 // At the time of writing, this isn't possible (Data never returns
405 // non-nil errors). We panic to find this in the future if we have to.
406 // I don't see a reason for Data to ever return an error.
407 panic(err)
408 }
409
410 // Set the schema version to latest by default
411 result.meta = map[string]interface{}{
412 "schema_version": strconv.Itoa(r.SchemaVersion),
413 }
414
415 return result
416}
417
418// TestResourceData Yields a ResourceData filled with this resource's schema for use in unit testing
419//
420// TODO: May be able to be removed with the above ResourceData function.
421func (r *Resource) TestResourceData() *ResourceData {
422 return &ResourceData{
423 schema: r.Schema,
424 }
425}
426
427// Returns true if the resource is "top level" i.e. not a sub-resource.
428func (r *Resource) isTopLevel() bool {
429 // TODO: This is a heuristic; replace with a definitive attribute?
430 return r.Create != nil
431}
432
433// Determines if a given InstanceState needs to be migrated by checking the
434// stored version number with the current SchemaVersion
435func (r *Resource) checkSchemaVersion(is *terraform.InstanceState) (bool, int) {
436 // Get the raw interface{} value for the schema version. If it doesn't
437 // exist or is nil then set it to zero.
438 raw := is.Meta["schema_version"]
439 if raw == nil {
440 raw = "0"
441 }
442
443 // Try to convert it to a string. If it isn't a string then we pretend
444 // that it isn't set at all. It should never not be a string unless it
445 // was manually tampered with.
446 rawString, ok := raw.(string)
447 if !ok {
448 rawString = "0"
449 }
450
451 stateSchemaVersion, _ := strconv.Atoi(rawString)
452 return stateSchemaVersion < r.SchemaVersion, stateSchemaVersion
453}
454
455func (r *Resource) recordCurrentSchemaVersion(
456 state *terraform.InstanceState) *terraform.InstanceState {
457 if state != nil && r.SchemaVersion > 0 {
458 if state.Meta == nil {
459 state.Meta = make(map[string]interface{})
460 }
461 state.Meta["schema_version"] = strconv.Itoa(r.SchemaVersion)
462 }
463 return state
464}
465
466// Noop is a convenience implementation of resource function which takes
467// no action and returns no error.
468func Noop(*ResourceData, interface{}) error {
469 return nil
470}
471
472// RemoveFromState is a convenience implementation of a resource function
473// which sets the resource ID to empty string (to remove it from state)
474// and returns no error.
475func RemoveFromState(d *ResourceData, _ interface{}) error {
476 d.SetId("")
477 return nil
478}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go
new file mode 100644
index 0000000..b2bc8f6
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go
@@ -0,0 +1,502 @@
1package schema
2
3import (
4 "log"
5 "reflect"
6 "strings"
7 "sync"
8 "time"
9
10 "github.com/hashicorp/terraform/terraform"
11)
12
13// ResourceData is used to query and set the attributes of a resource.
14//
15// ResourceData is the primary argument received for CRUD operations on
16// a resource as well as configuration of a provider. It is a powerful
17// structure that can be used to not only query data, but check for changes,
18// define partial state updates, etc.
19//
20// The most relevant methods to take a look at are Get, Set, and Partial.
21type ResourceData struct {
22 // Settable (internally)
23 schema map[string]*Schema
24 config *terraform.ResourceConfig
25 state *terraform.InstanceState
26 diff *terraform.InstanceDiff
27 meta map[string]interface{}
28 timeouts *ResourceTimeout
29
30 // Don't set
31 multiReader *MultiLevelFieldReader
32 setWriter *MapFieldWriter
33 newState *terraform.InstanceState
34 partial bool
35 partialMap map[string]struct{}
36 once sync.Once
37 isNew bool
38}
39
40// getResult is the internal structure that is generated when a Get
41// is called that contains some extra data that might be used.
42type getResult struct {
43 Value interface{}
44 ValueProcessed interface{}
45 Computed bool
46 Exists bool
47 Schema *Schema
48}
49
50// UnsafeSetFieldRaw allows setting arbitrary values in state to arbitrary
51// values, bypassing schema. This MUST NOT be used in normal circumstances -
52// it exists only to support the remote_state data source.
53func (d *ResourceData) UnsafeSetFieldRaw(key string, value string) {
54 d.once.Do(d.init)
55
56 d.setWriter.unsafeWriteField(key, value)
57}
58
59// Get returns the data for the given key, or nil if the key doesn't exist
60// in the schema.
61//
62// If the key does exist in the schema but doesn't exist in the configuration,
63// then the default value for that type will be returned. For strings, this is
64// "", for numbers it is 0, etc.
65//
66// If you want to test if something is set at all in the configuration,
67// use GetOk.
68func (d *ResourceData) Get(key string) interface{} {
69 v, _ := d.GetOk(key)
70 return v
71}
72
73// GetChange returns the old and new value for a given key.
74//
75// HasChange should be used to check if a change exists. It is possible
76// that both the old and new value are the same if the old value was not
77// set and the new value is. This is common, for example, for boolean
78// fields which have a zero value of false.
79func (d *ResourceData) GetChange(key string) (interface{}, interface{}) {
80 o, n := d.getChange(key, getSourceState, getSourceDiff)
81 return o.Value, n.Value
82}
83
84// GetOk returns the data for the given key and whether or not the key
85// has been set to a non-zero value at some point.
86//
87// The first result will not necessarilly be nil if the value doesn't exist.
88// The second result should be checked to determine this information.
89func (d *ResourceData) GetOk(key string) (interface{}, bool) {
90 r := d.getRaw(key, getSourceSet)
91 exists := r.Exists && !r.Computed
92 if exists {
93 // If it exists, we also want to verify it is not the zero-value.
94 value := r.Value
95 zero := r.Schema.Type.Zero()
96
97 if eq, ok := value.(Equal); ok {
98 exists = !eq.Equal(zero)
99 } else {
100 exists = !reflect.DeepEqual(value, zero)
101 }
102 }
103
104 return r.Value, exists
105}
106
107func (d *ResourceData) getRaw(key string, level getSource) getResult {
108 var parts []string
109 if key != "" {
110 parts = strings.Split(key, ".")
111 }
112
113 return d.get(parts, level)
114}
115
116// HasChange returns whether or not the given key has been changed.
117func (d *ResourceData) HasChange(key string) bool {
118 o, n := d.GetChange(key)
119
120 // If the type implements the Equal interface, then call that
121 // instead of just doing a reflect.DeepEqual. An example where this is
122 // needed is *Set
123 if eq, ok := o.(Equal); ok {
124 return !eq.Equal(n)
125 }
126
127 return !reflect.DeepEqual(o, n)
128}
129
130// Partial turns partial state mode on/off.
131//
132// When partial state mode is enabled, then only key prefixes specified
133// by SetPartial will be in the final state. This allows providers to return
134// partial states for partially applied resources (when errors occur).
135func (d *ResourceData) Partial(on bool) {
136 d.partial = on
137 if on {
138 if d.partialMap == nil {
139 d.partialMap = make(map[string]struct{})
140 }
141 } else {
142 d.partialMap = nil
143 }
144}
145
146// Set sets the value for the given key.
147//
148// If the key is invalid or the value is not a correct type, an error
149// will be returned.
150func (d *ResourceData) Set(key string, value interface{}) error {
151 d.once.Do(d.init)
152
153 // If the value is a pointer to a non-struct, get its value and
154 // use that. This allows Set to take a pointer to primitives to
155 // simplify the interface.
156 reflectVal := reflect.ValueOf(value)
157 if reflectVal.Kind() == reflect.Ptr {
158 if reflectVal.IsNil() {
159 // If the pointer is nil, then the value is just nil
160 value = nil
161 } else {
162 // Otherwise, we dereference the pointer as long as its not
163 // a pointer to a struct, since struct pointers are allowed.
164 reflectVal = reflect.Indirect(reflectVal)
165 if reflectVal.Kind() != reflect.Struct {
166 value = reflectVal.Interface()
167 }
168 }
169 }
170
171 return d.setWriter.WriteField(strings.Split(key, "."), value)
172}
173
174// SetPartial adds the key to the final state output while
175// in partial state mode. The key must be a root key in the schema (i.e.
176// it cannot be "list.0").
177//
178// If partial state mode is disabled, then this has no effect. Additionally,
179// whenever partial state mode is toggled, the partial data is cleared.
180func (d *ResourceData) SetPartial(k string) {
181 if d.partial {
182 d.partialMap[k] = struct{}{}
183 }
184}
185
186func (d *ResourceData) MarkNewResource() {
187 d.isNew = true
188}
189
190func (d *ResourceData) IsNewResource() bool {
191 return d.isNew
192}
193
194// Id returns the ID of the resource.
195func (d *ResourceData) Id() string {
196 var result string
197
198 if d.state != nil {
199 result = d.state.ID
200 }
201
202 if d.newState != nil {
203 result = d.newState.ID
204 }
205
206 return result
207}
208
209// ConnInfo returns the connection info for this resource.
210func (d *ResourceData) ConnInfo() map[string]string {
211 if d.newState != nil {
212 return d.newState.Ephemeral.ConnInfo
213 }
214
215 if d.state != nil {
216 return d.state.Ephemeral.ConnInfo
217 }
218
219 return nil
220}
221
222// SetId sets the ID of the resource. If the value is blank, then the
223// resource is destroyed.
224func (d *ResourceData) SetId(v string) {
225 d.once.Do(d.init)
226 d.newState.ID = v
227}
228
229// SetConnInfo sets the connection info for a resource.
230func (d *ResourceData) SetConnInfo(v map[string]string) {
231 d.once.Do(d.init)
232 d.newState.Ephemeral.ConnInfo = v
233}
234
235// SetType sets the ephemeral type for the data. This is only required
236// for importing.
237func (d *ResourceData) SetType(t string) {
238 d.once.Do(d.init)
239 d.newState.Ephemeral.Type = t
240}
241
242// State returns the new InstanceState after the diff and any Set
243// calls.
244func (d *ResourceData) State() *terraform.InstanceState {
245 var result terraform.InstanceState
246 result.ID = d.Id()
247 result.Meta = d.meta
248
249 // If we have no ID, then this resource doesn't exist and we just
250 // return nil.
251 if result.ID == "" {
252 return nil
253 }
254
255 if d.timeouts != nil {
256 if err := d.timeouts.StateEncode(&result); err != nil {
257 log.Printf("[ERR] Error encoding Timeout meta to Instance State: %s", err)
258 }
259 }
260
261 // Look for a magic key in the schema that determines we skip the
262 // integrity check of fields existing in the schema, allowing dynamic
263 // keys to be created.
264 hasDynamicAttributes := false
265 for k, _ := range d.schema {
266 if k == "__has_dynamic_attributes" {
267 hasDynamicAttributes = true
268 log.Printf("[INFO] Resource %s has dynamic attributes", result.ID)
269 }
270 }
271
272 // In order to build the final state attributes, we read the full
273 // attribute set as a map[string]interface{}, write it to a MapFieldWriter,
274 // and then use that map.
275 rawMap := make(map[string]interface{})
276 for k := range d.schema {
277 source := getSourceSet
278 if d.partial {
279 source = getSourceState
280 if _, ok := d.partialMap[k]; ok {
281 source = getSourceSet
282 }
283 }
284
285 raw := d.get([]string{k}, source)
286 if raw.Exists && !raw.Computed {
287 rawMap[k] = raw.Value
288 if raw.ValueProcessed != nil {
289 rawMap[k] = raw.ValueProcessed
290 }
291 }
292 }
293
294 mapW := &MapFieldWriter{Schema: d.schema}
295 if err := mapW.WriteField(nil, rawMap); err != nil {
296 return nil
297 }
298
299 result.Attributes = mapW.Map()
300
301 if hasDynamicAttributes {
302 // If we have dynamic attributes, just copy the attributes map
303 // one for one into the result attributes.
304 for k, v := range d.setWriter.Map() {
305 // Don't clobber schema values. This limits usage of dynamic
306 // attributes to names which _do not_ conflict with schema
307 // keys!
308 if _, ok := result.Attributes[k]; !ok {
309 result.Attributes[k] = v
310 }
311 }
312 }
313
314 if d.newState != nil {
315 result.Ephemeral = d.newState.Ephemeral
316 }
317
318 // TODO: This is hacky and we can remove this when we have a proper
319 // state writer. We should instead have a proper StateFieldWriter
320 // and use that.
321 for k, schema := range d.schema {
322 if schema.Type != TypeMap {
323 continue
324 }
325
326 if result.Attributes[k] == "" {
327 delete(result.Attributes, k)
328 }
329 }
330
331 if v := d.Id(); v != "" {
332 result.Attributes["id"] = d.Id()
333 }
334
335 if d.state != nil {
336 result.Tainted = d.state.Tainted
337 }
338
339 return &result
340}
341
342// Timeout returns the data for the given timeout key
343// Returns a duration of 20 minutes for any key not found, or not found and no default.
344func (d *ResourceData) Timeout(key string) time.Duration {
345 key = strings.ToLower(key)
346
347 var timeout *time.Duration
348 switch key {
349 case TimeoutCreate:
350 timeout = d.timeouts.Create
351 case TimeoutRead:
352 timeout = d.timeouts.Read
353 case TimeoutUpdate:
354 timeout = d.timeouts.Update
355 case TimeoutDelete:
356 timeout = d.timeouts.Delete
357 }
358
359 if timeout != nil {
360 return *timeout
361 }
362
363 if d.timeouts.Default != nil {
364 return *d.timeouts.Default
365 }
366
367 // Return system default of 20 minutes
368 return 20 * time.Minute
369}
370
371func (d *ResourceData) init() {
372 // Initialize the field that will store our new state
373 var copyState terraform.InstanceState
374 if d.state != nil {
375 copyState = *d.state.DeepCopy()
376 }
377 d.newState = &copyState
378
379 // Initialize the map for storing set data
380 d.setWriter = &MapFieldWriter{Schema: d.schema}
381
382 // Initialize the reader for getting data from the
383 // underlying sources (config, diff, etc.)
384 readers := make(map[string]FieldReader)
385 var stateAttributes map[string]string
386 if d.state != nil {
387 stateAttributes = d.state.Attributes
388 readers["state"] = &MapFieldReader{
389 Schema: d.schema,
390 Map: BasicMapReader(stateAttributes),
391 }
392 }
393 if d.config != nil {
394 readers["config"] = &ConfigFieldReader{
395 Schema: d.schema,
396 Config: d.config,
397 }
398 }
399 if d.diff != nil {
400 readers["diff"] = &DiffFieldReader{
401 Schema: d.schema,
402 Diff: d.diff,
403 Source: &MultiLevelFieldReader{
404 Levels: []string{"state", "config"},
405 Readers: readers,
406 },
407 }
408 }
409 readers["set"] = &MapFieldReader{
410 Schema: d.schema,
411 Map: BasicMapReader(d.setWriter.Map()),
412 }
413 d.multiReader = &MultiLevelFieldReader{
414 Levels: []string{
415 "state",
416 "config",
417 "diff",
418 "set",
419 },
420
421 Readers: readers,
422 }
423}
424
425func (d *ResourceData) diffChange(
426 k string) (interface{}, interface{}, bool, bool) {
427 // Get the change between the state and the config.
428 o, n := d.getChange(k, getSourceState, getSourceConfig|getSourceExact)
429 if !o.Exists {
430 o.Value = nil
431 }
432 if !n.Exists {
433 n.Value = nil
434 }
435
436 // Return the old, new, and whether there is a change
437 return o.Value, n.Value, !reflect.DeepEqual(o.Value, n.Value), n.Computed
438}
439
440func (d *ResourceData) getChange(
441 k string,
442 oldLevel getSource,
443 newLevel getSource) (getResult, getResult) {
444 var parts, parts2 []string
445 if k != "" {
446 parts = strings.Split(k, ".")
447 parts2 = strings.Split(k, ".")
448 }
449
450 o := d.get(parts, oldLevel)
451 n := d.get(parts2, newLevel)
452 return o, n
453}
454
455func (d *ResourceData) get(addr []string, source getSource) getResult {
456 d.once.Do(d.init)
457
458 level := "set"
459 flags := source & ^getSourceLevelMask
460 exact := flags&getSourceExact != 0
461 source = source & getSourceLevelMask
462 if source >= getSourceSet {
463 level = "set"
464 } else if source >= getSourceDiff {
465 level = "diff"
466 } else if source >= getSourceConfig {
467 level = "config"
468 } else {
469 level = "state"
470 }
471
472 var result FieldReadResult
473 var err error
474 if exact {
475 result, err = d.multiReader.ReadFieldExact(addr, level)
476 } else {
477 result, err = d.multiReader.ReadFieldMerge(addr, level)
478 }
479 if err != nil {
480 panic(err)
481 }
482
483 // If the result doesn't exist, then we set the value to the zero value
484 var schema *Schema
485 if schemaL := addrToSchema(addr, d.schema); len(schemaL) > 0 {
486 schema = schemaL[len(schemaL)-1]
487 }
488
489 if result.Value == nil && schema != nil {
490 result.Value = result.ValueOrZero(schema)
491 }
492
493 // Transform the FieldReadResult into a getResult. It might be worth
494 // merging these two structures one day.
495 return getResult{
496 Value: result.Value,
497 ValueProcessed: result.ValueProcessed,
498 Computed: result.Computed,
499 Exists: result.Exists,
500 Schema: schema,
501 }
502}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data_get_source.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_data_get_source.go
new file mode 100644
index 0000000..7dd655d
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_data_get_source.go
@@ -0,0 +1,17 @@
1package schema
2
3//go:generate stringer -type=getSource resource_data_get_source.go
4
5// getSource represents the level we want to get for a value (internally).
6// Any source less than or equal to the level will be loaded (whichever
7// has a value first).
8type getSource byte
9
10const (
11 getSourceState getSource = 1 << iota
12 getSourceConfig
13 getSourceDiff
14 getSourceSet
15 getSourceExact // Only get from the _exact_ level
16 getSourceLevelMask getSource = getSourceState | getSourceConfig | getSourceDiff | getSourceSet
17)
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_importer.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_importer.go
new file mode 100644
index 0000000..5dada3c
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_importer.go
@@ -0,0 +1,52 @@
1package schema
2
3// ResourceImporter defines how a resource is imported in Terraform. This
4// can be set onto a Resource struct to make it Importable. Not all resources
5// have to be importable; if a Resource doesn't have a ResourceImporter then
6// it won't be importable.
7//
8// "Importing" in Terraform is the process of taking an already-created
9// resource and bringing it under Terraform management. This can include
10// updating Terraform state, generating Terraform configuration, etc.
11type ResourceImporter struct {
12 // The functions below must all be implemented for importing to work.
13
14 // State is called to convert an ID to one or more InstanceState to
15 // insert into the Terraform state. If this isn't specified, then
16 // the ID is passed straight through.
17 State StateFunc
18}
19
20// StateFunc is the function called to import a resource into the
21// Terraform state. It is given a ResourceData with only ID set. This
22// ID is going to be an arbitrary value given by the user and may not map
23// directly to the ID format that the resource expects, so that should
24// be validated.
25//
26// This should return a slice of ResourceData that turn into the state
27// that was imported. This might be as simple as returning only the argument
28// that was given to the function. In other cases (such as AWS security groups),
29// an import may fan out to multiple resources and this will have to return
30// multiple.
31//
32// To create the ResourceData structures for other resource types (if
33// you have to), instantiate your resource and call the Data function.
34type StateFunc func(*ResourceData, interface{}) ([]*ResourceData, error)
35
36// InternalValidate should be called to validate the structure of this
37// importer. This should be called in a unit test.
38//
39// Resource.InternalValidate() will automatically call this, so this doesn't
40// need to be called manually. Further, Resource.InternalValidate() is
41// automatically called by Provider.InternalValidate(), so you only need
42// to internal validate the provider.
43func (r *ResourceImporter) InternalValidate() error {
44 return nil
45}
46
47// ImportStatePassthrough is an implementation of StateFunc that can be
48// used to simply pass the ID directly through. This should be used only
49// in the case that an ID-only refresh is possible.
50func ImportStatePassthrough(d *ResourceData, m interface{}) ([]*ResourceData, error) {
51 return []*ResourceData{d}, nil
52}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go
new file mode 100644
index 0000000..445819f
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go
@@ -0,0 +1,237 @@
1package schema
2
3import (
4 "fmt"
5 "log"
6 "time"
7
8 "github.com/hashicorp/terraform/terraform"
9 "github.com/mitchellh/copystructure"
10)
11
12const TimeoutKey = "e2bfb730-ecaa-11e6-8f88-34363bc7c4c0"
13const TimeoutsConfigKey = "timeouts"
14
15const (
16 TimeoutCreate = "create"
17 TimeoutRead = "read"
18 TimeoutUpdate = "update"
19 TimeoutDelete = "delete"
20 TimeoutDefault = "default"
21)
22
23func timeoutKeys() []string {
24 return []string{
25 TimeoutCreate,
26 TimeoutRead,
27 TimeoutUpdate,
28 TimeoutDelete,
29 TimeoutDefault,
30 }
31}
32
33// could be time.Duration, int64 or float64
34func DefaultTimeout(tx interface{}) *time.Duration {
35 var td time.Duration
36 switch raw := tx.(type) {
37 case time.Duration:
38 return &raw
39 case int64:
40 td = time.Duration(raw)
41 case float64:
42 td = time.Duration(int64(raw))
43 default:
44 log.Printf("[WARN] Unknown type in DefaultTimeout: %#v", tx)
45 }
46 return &td
47}
48
49type ResourceTimeout struct {
50 Create, Read, Update, Delete, Default *time.Duration
51}
52
53// ConfigDecode takes a schema and the configuration (available in Diff) and
54// validates, parses the timeouts into `t`
55func (t *ResourceTimeout) ConfigDecode(s *Resource, c *terraform.ResourceConfig) error {
56 if s.Timeouts != nil {
57 raw, err := copystructure.Copy(s.Timeouts)
58 if err != nil {
59 log.Printf("[DEBUG] Error with deep copy: %s", err)
60 }
61 *t = *raw.(*ResourceTimeout)
62 }
63
64 if raw, ok := c.Config[TimeoutsConfigKey]; ok {
65 if configTimeouts, ok := raw.([]map[string]interface{}); ok {
66 for _, timeoutValues := range configTimeouts {
67 // loop through each Timeout given in the configuration and validate they
68 // the Timeout defined in the resource
69 for timeKey, timeValue := range timeoutValues {
70 // validate that we're dealing with the normal CRUD actions
71 var found bool
72 for _, key := range timeoutKeys() {
73 if timeKey == key {
74 found = true
75 break
76 }
77 }
78
79 if !found {
80 return fmt.Errorf("Unsupported Timeout configuration key found (%s)", timeKey)
81 }
82
83 // Get timeout
84 rt, err := time.ParseDuration(timeValue.(string))
85 if err != nil {
86 return fmt.Errorf("Error parsing Timeout for (%s): %s", timeKey, err)
87 }
88
89 var timeout *time.Duration
90 switch timeKey {
91 case TimeoutCreate:
92 timeout = t.Create
93 case TimeoutUpdate:
94 timeout = t.Update
95 case TimeoutRead:
96 timeout = t.Read
97 case TimeoutDelete:
98 timeout = t.Delete
99 case TimeoutDefault:
100 timeout = t.Default
101 }
102
103 // If the resource has not delcared this in the definition, then error
104 // with an unsupported message
105 if timeout == nil {
106 return unsupportedTimeoutKeyError(timeKey)
107 }
108
109 *timeout = rt
110 }
111 }
112 } else {
113 log.Printf("[WARN] Invalid Timeout structure found, skipping timeouts")
114 }
115 }
116
117 return nil
118}
119
120func unsupportedTimeoutKeyError(key string) error {
121 return fmt.Errorf("Timeout Key (%s) is not supported", key)
122}
123
124// DiffEncode, StateEncode, and MetaDecode are analogous to the Go stdlib JSONEncoder
125// interface: they encode/decode a timeouts struct from an instance diff, which is
126// where the timeout data is stored after a diff to pass into Apply.
127//
128// StateEncode encodes the timeout into the ResourceData's InstanceState for
129// saving to state
130//
131func (t *ResourceTimeout) DiffEncode(id *terraform.InstanceDiff) error {
132 return t.metaEncode(id)
133}
134
135func (t *ResourceTimeout) StateEncode(is *terraform.InstanceState) error {
136 return t.metaEncode(is)
137}
138
139// metaEncode encodes the ResourceTimeout into a map[string]interface{} format
140// and stores it in the Meta field of the interface it's given.
141// Assumes the interface is either *terraform.InstanceState or
142// *terraform.InstanceDiff, returns an error otherwise
143func (t *ResourceTimeout) metaEncode(ids interface{}) error {
144 m := make(map[string]interface{})
145
146 if t.Create != nil {
147 m[TimeoutCreate] = t.Create.Nanoseconds()
148 }
149 if t.Read != nil {
150 m[TimeoutRead] = t.Read.Nanoseconds()
151 }
152 if t.Update != nil {
153 m[TimeoutUpdate] = t.Update.Nanoseconds()
154 }
155 if t.Delete != nil {
156 m[TimeoutDelete] = t.Delete.Nanoseconds()
157 }
158 if t.Default != nil {
159 m[TimeoutDefault] = t.Default.Nanoseconds()
160 // for any key above that is nil, if default is specified, we need to
161 // populate it with the default
162 for _, k := range timeoutKeys() {
163 if _, ok := m[k]; !ok {
164 m[k] = t.Default.Nanoseconds()
165 }
166 }
167 }
168
169 // only add the Timeout to the Meta if we have values
170 if len(m) > 0 {
171 switch instance := ids.(type) {
172 case *terraform.InstanceDiff:
173 if instance.Meta == nil {
174 instance.Meta = make(map[string]interface{})
175 }
176 instance.Meta[TimeoutKey] = m
177 case *terraform.InstanceState:
178 if instance.Meta == nil {
179 instance.Meta = make(map[string]interface{})
180 }
181 instance.Meta[TimeoutKey] = m
182 default:
183 return fmt.Errorf("Error matching type for Diff Encode")
184 }
185 }
186
187 return nil
188}
189
190func (t *ResourceTimeout) StateDecode(id *terraform.InstanceState) error {
191 return t.metaDecode(id)
192}
193func (t *ResourceTimeout) DiffDecode(is *terraform.InstanceDiff) error {
194 return t.metaDecode(is)
195}
196
197func (t *ResourceTimeout) metaDecode(ids interface{}) error {
198 var rawMeta interface{}
199 var ok bool
200 switch rawInstance := ids.(type) {
201 case *terraform.InstanceDiff:
202 rawMeta, ok = rawInstance.Meta[TimeoutKey]
203 if !ok {
204 return nil
205 }
206 case *terraform.InstanceState:
207 rawMeta, ok = rawInstance.Meta[TimeoutKey]
208 if !ok {
209 return nil
210 }
211 default:
212 return fmt.Errorf("Unknown or unsupported type in metaDecode: %#v", ids)
213 }
214
215 times := rawMeta.(map[string]interface{})
216 if len(times) == 0 {
217 return nil
218 }
219
220 if v, ok := times[TimeoutCreate]; ok {
221 t.Create = DefaultTimeout(v)
222 }
223 if v, ok := times[TimeoutRead]; ok {
224 t.Read = DefaultTimeout(v)
225 }
226 if v, ok := times[TimeoutUpdate]; ok {
227 t.Update = DefaultTimeout(v)
228 }
229 if v, ok := times[TimeoutDelete]; ok {
230 t.Delete = DefaultTimeout(v)
231 }
232 if v, ok := times[TimeoutDefault]; ok {
233 t.Default = DefaultTimeout(v)
234 }
235
236 return nil
237}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go
new file mode 100644
index 0000000..32d1721
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go
@@ -0,0 +1,1537 @@
1// schema is a high-level framework for easily writing new providers
2// for Terraform. Usage of schema is recommended over attempting to write
3// to the low-level plugin interfaces manually.
4//
5// schema breaks down provider creation into simple CRUD operations for
6// resources. The logic of diffing, destroying before creating, updating
7// or creating, etc. is all handled by the framework. The plugin author
8// only needs to implement a configuration schema and the CRUD operations and
9// everything else is meant to just work.
10//
11// A good starting point is to view the Provider structure.
12package schema
13
14import (
15 "fmt"
16 "os"
17 "reflect"
18 "sort"
19 "strconv"
20 "strings"
21
22 "github.com/hashicorp/terraform/terraform"
23 "github.com/mitchellh/mapstructure"
24)
25
26// type used for schema package context keys
27type contextKey string
28
29// Schema is used to describe the structure of a value.
30//
31// Read the documentation of the struct elements for important details.
32type Schema struct {
33 // Type is the type of the value and must be one of the ValueType values.
34 //
35 // This type not only determines what type is expected/valid in configuring
36 // this value, but also what type is returned when ResourceData.Get is
37 // called. The types returned by Get are:
38 //
39 // TypeBool - bool
40 // TypeInt - int
41 // TypeFloat - float64
42 // TypeString - string
43 // TypeList - []interface{}
44 // TypeMap - map[string]interface{}
45 // TypeSet - *schema.Set
46 //
47 Type ValueType
48
49 // If one of these is set, then this item can come from the configuration.
50 // Both cannot be set. If Optional is set, the value is optional. If
51 // Required is set, the value is required.
52 //
53 // One of these must be set if the value is not computed. That is:
54 // value either comes from the config, is computed, or is both.
55 Optional bool
56 Required bool
57
58 // If this is non-nil, the provided function will be used during diff
59 // of this field. If this is nil, a default diff for the type of the
60 // schema will be used.
61 //
62 // This allows comparison based on something other than primitive, list
63 // or map equality - for example SSH public keys may be considered
64 // equivalent regardless of trailing whitespace.
65 DiffSuppressFunc SchemaDiffSuppressFunc
66
67 // If this is non-nil, then this will be a default value that is used
68 // when this item is not set in the configuration.
69 //
70 // DefaultFunc can be specified to compute a dynamic default.
71 // Only one of Default or DefaultFunc can be set. If DefaultFunc is
72 // used then its return value should be stable to avoid generating
73 // confusing/perpetual diffs.
74 //
75 // Changing either Default or the return value of DefaultFunc can be
76 // a breaking change, especially if the attribute in question has
77 // ForceNew set. If a default needs to change to align with changing
78 // assumptions in an upstream API then it may be necessary to also use
79 // the MigrateState function on the resource to change the state to match,
80 // or have the Read function adjust the state value to align with the
81 // new default.
82 //
83 // If Required is true above, then Default cannot be set. DefaultFunc
84 // can be set with Required. If the DefaultFunc returns nil, then there
85 // will be no default and the user will be asked to fill it in.
86 //
87 // If either of these is set, then the user won't be asked for input
88 // for this key if the default is not nil.
89 Default interface{}
90 DefaultFunc SchemaDefaultFunc
91
92 // Description is used as the description for docs or asking for user
93 // input. It should be relatively short (a few sentences max) and should
94 // be formatted to fit a CLI.
95 Description string
96
97 // InputDefault is the default value to use for when inputs are requested.
98 // This differs from Default in that if Default is set, no input is
99 // asked for. If Input is asked, this will be the default value offered.
100 InputDefault string
101
102 // The fields below relate to diffs.
103 //
104 // If Computed is true, then the result of this value is computed
105 // (unless specified by config) on creation.
106 //
107 // If ForceNew is true, then a change in this resource necessitates
108 // the creation of a new resource.
109 //
110 // StateFunc is a function called to change the value of this before
111 // storing it in the state (and likewise before comparing for diffs).
112 // The use for this is for example with large strings, you may want
113 // to simply store the hash of it.
114 Computed bool
115 ForceNew bool
116 StateFunc SchemaStateFunc
117
118 // The following fields are only set for a TypeList or TypeSet Type.
119 //
120 // Elem must be either a *Schema or a *Resource only if the Type is
121 // TypeList, and represents what the element type is. If it is *Schema,
122 // the element type is just a simple value. If it is *Resource, the
123 // element type is a complex structure, potentially with its own lifecycle.
124 //
125 // MaxItems defines a maximum amount of items that can exist within a
126 // TypeSet or TypeList. Specific use cases would be if a TypeSet is being
127 // used to wrap a complex structure, however more than one instance would
128 // cause instability.
129 //
130 // MinItems defines a minimum amount of items that can exist within a
131 // TypeSet or TypeList. Specific use cases would be if a TypeSet is being
132 // used to wrap a complex structure, however less than one instance would
133 // cause instability.
134 //
135 // PromoteSingle, if true, will allow single elements to be standalone
136 // and promote them to a list. For example "foo" would be promoted to
137 // ["foo"] automatically. This is primarily for legacy reasons and the
138 // ambiguity is not recommended for new usage. Promotion is only allowed
139 // for primitive element types.
140 Elem interface{}
141 MaxItems int
142 MinItems int
143 PromoteSingle bool
144
145 // The following fields are only valid for a TypeSet type.
146 //
147 // Set defines a function to determine the unique ID of an item so that
148 // a proper set can be built.
149 Set SchemaSetFunc
150
151 // ComputedWhen is a set of queries on the configuration. Whenever any
152 // of these things is changed, it will require a recompute (this requires
153 // that Computed is set to true).
154 //
155 // NOTE: This currently does not work.
156 ComputedWhen []string
157
158 // ConflictsWith is a set of schema keys that conflict with this schema.
159 // This will only check that they're set in the _config_. This will not
160 // raise an error for a malfunctioning resource that sets a conflicting
161 // key.
162 ConflictsWith []string
163
164 // When Deprecated is set, this attribute is deprecated.
165 //
166 // A deprecated field still works, but will probably stop working in near
167 // future. This string is the message shown to the user with instructions on
168 // how to address the deprecation.
169 Deprecated string
170
171 // When Removed is set, this attribute has been removed from the schema
172 //
173 // Removed attributes can be left in the Schema to generate informative error
174 // messages for the user when they show up in resource configurations.
175 // This string is the message shown to the user with instructions on
176 // what do to about the removed attribute.
177 Removed string
178
179 // ValidateFunc allows individual fields to define arbitrary validation
180 // logic. It is yielded the provided config value as an interface{} that is
181 // guaranteed to be of the proper Schema type, and it can yield warnings or
182 // errors based on inspection of that value.
183 //
184 // ValidateFunc currently only works for primitive types.
185 ValidateFunc SchemaValidateFunc
186
187 // Sensitive ensures that the attribute's value does not get displayed in
188 // logs or regular output. It should be used for passwords or other
189 // secret fields. Future versions of Terraform may encrypt these
190 // values.
191 Sensitive bool
192}
193
194// SchemaDiffSuppresFunc is a function which can be used to determine
195// whether a detected diff on a schema element is "valid" or not, and
196// suppress it from the plan if necessary.
197//
198// Return true if the diff should be suppressed, false to retain it.
199type SchemaDiffSuppressFunc func(k, old, new string, d *ResourceData) bool
200
201// SchemaDefaultFunc is a function called to return a default value for
202// a field.
203type SchemaDefaultFunc func() (interface{}, error)
204
205// EnvDefaultFunc is a helper function that returns the value of the
206// given environment variable, if one exists, or the default value
207// otherwise.
208func EnvDefaultFunc(k string, dv interface{}) SchemaDefaultFunc {
209 return func() (interface{}, error) {
210 if v := os.Getenv(k); v != "" {
211 return v, nil
212 }
213
214 return dv, nil
215 }
216}
217
218// MultiEnvDefaultFunc is a helper function that returns the value of the first
219// environment variable in the given list that returns a non-empty value. If
220// none of the environment variables return a value, the default value is
221// returned.
222func MultiEnvDefaultFunc(ks []string, dv interface{}) SchemaDefaultFunc {
223 return func() (interface{}, error) {
224 for _, k := range ks {
225 if v := os.Getenv(k); v != "" {
226 return v, nil
227 }
228 }
229 return dv, nil
230 }
231}
232
233// SchemaSetFunc is a function that must return a unique ID for the given
234// element. This unique ID is used to store the element in a hash.
235type SchemaSetFunc func(interface{}) int
236
237// SchemaStateFunc is a function used to convert some type to a string
238// to be stored in the state.
239type SchemaStateFunc func(interface{}) string
240
241// SchemaValidateFunc is a function used to validate a single field in the
242// schema.
243type SchemaValidateFunc func(interface{}, string) ([]string, []error)
244
245func (s *Schema) GoString() string {
246 return fmt.Sprintf("*%#v", *s)
247}
248
249// Returns a default value for this schema by either reading Default or
250// evaluating DefaultFunc. If neither of these are defined, returns nil.
251func (s *Schema) DefaultValue() (interface{}, error) {
252 if s.Default != nil {
253 return s.Default, nil
254 }
255
256 if s.DefaultFunc != nil {
257 defaultValue, err := s.DefaultFunc()
258 if err != nil {
259 return nil, fmt.Errorf("error loading default: %s", err)
260 }
261 return defaultValue, nil
262 }
263
264 return nil, nil
265}
266
267// Returns a zero value for the schema.
268func (s *Schema) ZeroValue() interface{} {
269 // If it's a set then we'll do a bit of extra work to provide the
270 // right hashing function in our empty value.
271 if s.Type == TypeSet {
272 setFunc := s.Set
273 if setFunc == nil {
274 // Default set function uses the schema to hash the whole value
275 elem := s.Elem
276 switch t := elem.(type) {
277 case *Schema:
278 setFunc = HashSchema(t)
279 case *Resource:
280 setFunc = HashResource(t)
281 default:
282 panic("invalid set element type")
283 }
284 }
285 return &Set{F: setFunc}
286 } else {
287 return s.Type.Zero()
288 }
289}
290
291func (s *Schema) finalizeDiff(
292 d *terraform.ResourceAttrDiff) *terraform.ResourceAttrDiff {
293 if d == nil {
294 return d
295 }
296
297 if s.Type == TypeBool {
298 normalizeBoolString := func(s string) string {
299 switch s {
300 case "0":
301 return "false"
302 case "1":
303 return "true"
304 }
305 return s
306 }
307 d.Old = normalizeBoolString(d.Old)
308 d.New = normalizeBoolString(d.New)
309 }
310
311 if s.Computed && !d.NewRemoved && d.New == "" {
312 // Computed attribute without a new value set
313 d.NewComputed = true
314 }
315
316 if s.ForceNew {
317 // ForceNew, mark that this field is requiring new under the
318 // following conditions, explained below:
319 //
320 // * Old != New - There is a change in value. This field
321 // is therefore causing a new resource.
322 //
323 // * NewComputed - This field is being computed, hence a
324 // potential change in value, mark as causing a new resource.
325 d.RequiresNew = d.Old != d.New || d.NewComputed
326 }
327
328 if d.NewRemoved {
329 return d
330 }
331
332 if s.Computed {
333 if d.Old != "" && d.New == "" {
334 // This is a computed value with an old value set already,
335 // just let it go.
336 return nil
337 }
338
339 if d.New == "" {
340 // Computed attribute without a new value set
341 d.NewComputed = true
342 }
343 }
344
345 if s.Sensitive {
346 // Set the Sensitive flag so output is hidden in the UI
347 d.Sensitive = true
348 }
349
350 return d
351}
352
353// schemaMap is a wrapper that adds nice functions on top of schemas.
354type schemaMap map[string]*Schema
355
356// Data returns a ResourceData for the given schema, state, and diff.
357//
358// The diff is optional.
359func (m schemaMap) Data(
360 s *terraform.InstanceState,
361 d *terraform.InstanceDiff) (*ResourceData, error) {
362 return &ResourceData{
363 schema: m,
364 state: s,
365 diff: d,
366 }, nil
367}
368
369// Diff returns the diff for a resource given the schema map,
370// state, and configuration.
371func (m schemaMap) Diff(
372 s *terraform.InstanceState,
373 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
374 result := new(terraform.InstanceDiff)
375 result.Attributes = make(map[string]*terraform.ResourceAttrDiff)
376
377 // Make sure to mark if the resource is tainted
378 if s != nil {
379 result.DestroyTainted = s.Tainted
380 }
381
382 d := &ResourceData{
383 schema: m,
384 state: s,
385 config: c,
386 }
387
388 for k, schema := range m {
389 err := m.diff(k, schema, result, d, false)
390 if err != nil {
391 return nil, err
392 }
393 }
394
395 // If the diff requires a new resource, then we recompute the diff
396 // so we have the complete new resource diff, and preserve the
397 // RequiresNew fields where necessary so the user knows exactly what
398 // caused that.
399 if result.RequiresNew() {
400 // Create the new diff
401 result2 := new(terraform.InstanceDiff)
402 result2.Attributes = make(map[string]*terraform.ResourceAttrDiff)
403
404 // Preserve the DestroyTainted flag
405 result2.DestroyTainted = result.DestroyTainted
406
407 // Reset the data to not contain state. We have to call init()
408 // again in order to reset the FieldReaders.
409 d.state = nil
410 d.init()
411
412 // Perform the diff again
413 for k, schema := range m {
414 err := m.diff(k, schema, result2, d, false)
415 if err != nil {
416 return nil, err
417 }
418 }
419
420 // Force all the fields to not force a new since we know what we
421 // want to force new.
422 for k, attr := range result2.Attributes {
423 if attr == nil {
424 continue
425 }
426
427 if attr.RequiresNew {
428 attr.RequiresNew = false
429 }
430
431 if s != nil {
432 attr.Old = s.Attributes[k]
433 }
434 }
435
436 // Now copy in all the requires new diffs...
437 for k, attr := range result.Attributes {
438 if attr == nil {
439 continue
440 }
441
442 newAttr, ok := result2.Attributes[k]
443 if !ok {
444 newAttr = attr
445 }
446
447 if attr.RequiresNew {
448 newAttr.RequiresNew = true
449 }
450
451 result2.Attributes[k] = newAttr
452 }
453
454 // And set the diff!
455 result = result2
456 }
457
458 // Remove any nil diffs just to keep things clean
459 for k, v := range result.Attributes {
460 if v == nil {
461 delete(result.Attributes, k)
462 }
463 }
464
465 // Go through and detect all of the ComputedWhens now that we've
466 // finished the diff.
467 // TODO
468
469 if result.Empty() {
470 // If we don't have any diff elements, just return nil
471 return nil, nil
472 }
473
474 return result, nil
475}
476
477// Input implements the terraform.ResourceProvider method by asking
478// for input for required configuration keys that don't have a value.
479func (m schemaMap) Input(
480 input terraform.UIInput,
481 c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
482 keys := make([]string, 0, len(m))
483 for k, _ := range m {
484 keys = append(keys, k)
485 }
486 sort.Strings(keys)
487
488 for _, k := range keys {
489 v := m[k]
490
491 // Skip things that don't require config, if that is even valid
492 // for a provider schema.
493 // Required XOR Optional must always be true to validate, so we only
494 // need to check one.
495 if v.Optional {
496 continue
497 }
498
499 // Deprecated fields should never prompt
500 if v.Deprecated != "" {
501 continue
502 }
503
504 // Skip things that have a value of some sort already
505 if _, ok := c.Raw[k]; ok {
506 continue
507 }
508
509 // Skip if it has a default value
510 defaultValue, err := v.DefaultValue()
511 if err != nil {
512 return nil, fmt.Errorf("%s: error loading default: %s", k, err)
513 }
514 if defaultValue != nil {
515 continue
516 }
517
518 var value interface{}
519 switch v.Type {
520 case TypeBool, TypeInt, TypeFloat, TypeSet, TypeList:
521 continue
522 case TypeString:
523 value, err = m.inputString(input, k, v)
524 default:
525 panic(fmt.Sprintf("Unknown type for input: %#v", v.Type))
526 }
527
528 if err != nil {
529 return nil, fmt.Errorf(
530 "%s: %s", k, err)
531 }
532
533 c.Config[k] = value
534 }
535
536 return c, nil
537}
538
539// Validate validates the configuration against this schema mapping.
540func (m schemaMap) Validate(c *terraform.ResourceConfig) ([]string, []error) {
541 return m.validateObject("", m, c)
542}
543
544// InternalValidate validates the format of this schema. This should be called
545// from a unit test (and not in user-path code) to verify that a schema
546// is properly built.
547func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error {
548 if topSchemaMap == nil {
549 topSchemaMap = m
550 }
551 for k, v := range m {
552 if v.Type == TypeInvalid {
553 return fmt.Errorf("%s: Type must be specified", k)
554 }
555
556 if v.Optional && v.Required {
557 return fmt.Errorf("%s: Optional or Required must be set, not both", k)
558 }
559
560 if v.Required && v.Computed {
561 return fmt.Errorf("%s: Cannot be both Required and Computed", k)
562 }
563
564 if !v.Required && !v.Optional && !v.Computed {
565 return fmt.Errorf("%s: One of optional, required, or computed must be set", k)
566 }
567
568 if v.Computed && v.Default != nil {
569 return fmt.Errorf("%s: Default must be nil if computed", k)
570 }
571
572 if v.Required && v.Default != nil {
573 return fmt.Errorf("%s: Default cannot be set with Required", k)
574 }
575
576 if len(v.ComputedWhen) > 0 && !v.Computed {
577 return fmt.Errorf("%s: ComputedWhen can only be set with Computed", k)
578 }
579
580 if len(v.ConflictsWith) > 0 && v.Required {
581 return fmt.Errorf("%s: ConflictsWith cannot be set with Required", k)
582 }
583
584 if len(v.ConflictsWith) > 0 {
585 for _, key := range v.ConflictsWith {
586 parts := strings.Split(key, ".")
587 sm := topSchemaMap
588 var target *Schema
589 for _, part := range parts {
590 // Skip index fields
591 if _, err := strconv.Atoi(part); err == nil {
592 continue
593 }
594
595 var ok bool
596 if target, ok = sm[part]; !ok {
597 return fmt.Errorf("%s: ConflictsWith references unknown attribute (%s)", k, key)
598 }
599
600 if subResource, ok := target.Elem.(*Resource); ok {
601 sm = schemaMap(subResource.Schema)
602 }
603 }
604 if target == nil {
605 return fmt.Errorf("%s: ConflictsWith cannot find target attribute (%s), sm: %#v", k, key, sm)
606 }
607 if target.Required {
608 return fmt.Errorf("%s: ConflictsWith cannot contain Required attribute (%s)", k, key)
609 }
610
611 if len(target.ComputedWhen) > 0 {
612 return fmt.Errorf("%s: ConflictsWith cannot contain Computed(When) attribute (%s)", k, key)
613 }
614 }
615 }
616
617 if v.Type == TypeList || v.Type == TypeSet {
618 if v.Elem == nil {
619 return fmt.Errorf("%s: Elem must be set for lists", k)
620 }
621
622 if v.Default != nil {
623 return fmt.Errorf("%s: Default is not valid for lists or sets", k)
624 }
625
626 if v.Type != TypeSet && v.Set != nil {
627 return fmt.Errorf("%s: Set can only be set for TypeSet", k)
628 }
629
630 switch t := v.Elem.(type) {
631 case *Resource:
632 if err := t.InternalValidate(topSchemaMap, true); err != nil {
633 return err
634 }
635 case *Schema:
636 bad := t.Computed || t.Optional || t.Required
637 if bad {
638 return fmt.Errorf(
639 "%s: Elem must have only Type set", k)
640 }
641 }
642 } else {
643 if v.MaxItems > 0 || v.MinItems > 0 {
644 return fmt.Errorf("%s: MaxItems and MinItems are only supported on lists or sets", k)
645 }
646 }
647
648 // Computed-only field
649 if v.Computed && !v.Optional {
650 if v.ValidateFunc != nil {
651 return fmt.Errorf("%s: ValidateFunc is for validating user input, "+
652 "there's nothing to validate on computed-only field", k)
653 }
654 if v.DiffSuppressFunc != nil {
655 return fmt.Errorf("%s: DiffSuppressFunc is for suppressing differences"+
656 " between config and state representation. "+
657 "There is no config for computed-only field, nothing to compare.", k)
658 }
659 }
660
661 if v.ValidateFunc != nil {
662 switch v.Type {
663 case TypeList, TypeSet:
664 return fmt.Errorf("ValidateFunc is not yet supported on lists or sets.")
665 }
666 }
667 }
668
669 return nil
670}
671
672func (m schemaMap) diff(
673 k string,
674 schema *Schema,
675 diff *terraform.InstanceDiff,
676 d *ResourceData,
677 all bool) error {
678
679 unsupressedDiff := new(terraform.InstanceDiff)
680 unsupressedDiff.Attributes = make(map[string]*terraform.ResourceAttrDiff)
681
682 var err error
683 switch schema.Type {
684 case TypeBool, TypeInt, TypeFloat, TypeString:
685 err = m.diffString(k, schema, unsupressedDiff, d, all)
686 case TypeList:
687 err = m.diffList(k, schema, unsupressedDiff, d, all)
688 case TypeMap:
689 err = m.diffMap(k, schema, unsupressedDiff, d, all)
690 case TypeSet:
691 err = m.diffSet(k, schema, unsupressedDiff, d, all)
692 default:
693 err = fmt.Errorf("%s: unknown type %#v", k, schema.Type)
694 }
695
696 for attrK, attrV := range unsupressedDiff.Attributes {
697 if schema.DiffSuppressFunc != nil &&
698 attrV != nil &&
699 schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, d) {
700 continue
701 }
702
703 diff.Attributes[attrK] = attrV
704 }
705
706 return err
707}
708
709func (m schemaMap) diffList(
710 k string,
711 schema *Schema,
712 diff *terraform.InstanceDiff,
713 d *ResourceData,
714 all bool) error {
715 o, n, _, computedList := d.diffChange(k)
716 if computedList {
717 n = nil
718 }
719 nSet := n != nil
720
721 // If we have an old value and no new value is set or will be
722 // computed once all variables can be interpolated and we're
723 // computed, then nothing has changed.
724 if o != nil && n == nil && !computedList && schema.Computed {
725 return nil
726 }
727
728 if o == nil {
729 o = []interface{}{}
730 }
731 if n == nil {
732 n = []interface{}{}
733 }
734 if s, ok := o.(*Set); ok {
735 o = s.List()
736 }
737 if s, ok := n.(*Set); ok {
738 n = s.List()
739 }
740 os := o.([]interface{})
741 vs := n.([]interface{})
742
743 // If the new value was set, and the two are equal, then we're done.
744 // We have to do this check here because sets might be NOT
745 // reflect.DeepEqual so we need to wait until we get the []interface{}
746 if !all && nSet && reflect.DeepEqual(os, vs) {
747 return nil
748 }
749
750 // Get the counts
751 oldLen := len(os)
752 newLen := len(vs)
753 oldStr := strconv.FormatInt(int64(oldLen), 10)
754
755 // If the whole list is computed, then say that the # is computed
756 if computedList {
757 diff.Attributes[k+".#"] = &terraform.ResourceAttrDiff{
758 Old: oldStr,
759 NewComputed: true,
760 RequiresNew: schema.ForceNew,
761 }
762 return nil
763 }
764
765 // If the counts are not the same, then record that diff
766 changed := oldLen != newLen
767 computed := oldLen == 0 && newLen == 0 && schema.Computed
768 if changed || computed || all {
769 countSchema := &Schema{
770 Type: TypeInt,
771 Computed: schema.Computed,
772 ForceNew: schema.ForceNew,
773 }
774
775 newStr := ""
776 if !computed {
777 newStr = strconv.FormatInt(int64(newLen), 10)
778 } else {
779 oldStr = ""
780 }
781
782 diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{
783 Old: oldStr,
784 New: newStr,
785 })
786 }
787
788 // Figure out the maximum
789 maxLen := oldLen
790 if newLen > maxLen {
791 maxLen = newLen
792 }
793
794 switch t := schema.Elem.(type) {
795 case *Resource:
796 // This is a complex resource
797 for i := 0; i < maxLen; i++ {
798 for k2, schema := range t.Schema {
799 subK := fmt.Sprintf("%s.%d.%s", k, i, k2)
800 err := m.diff(subK, schema, diff, d, all)
801 if err != nil {
802 return err
803 }
804 }
805 }
806 case *Schema:
807 // Copy the schema so that we can set Computed/ForceNew from
808 // the parent schema (the TypeList).
809 t2 := *t
810 t2.ForceNew = schema.ForceNew
811
812 // This is just a primitive element, so go through each and
813 // just diff each.
814 for i := 0; i < maxLen; i++ {
815 subK := fmt.Sprintf("%s.%d", k, i)
816 err := m.diff(subK, &t2, diff, d, all)
817 if err != nil {
818 return err
819 }
820 }
821 default:
822 return fmt.Errorf("%s: unknown element type (internal)", k)
823 }
824
825 return nil
826}
827
828func (m schemaMap) diffMap(
829 k string,
830 schema *Schema,
831 diff *terraform.InstanceDiff,
832 d *ResourceData,
833 all bool) error {
834 prefix := k + "."
835
836 // First get all the values from the state
837 var stateMap, configMap map[string]string
838 o, n, _, nComputed := d.diffChange(k)
839 if err := mapstructure.WeakDecode(o, &stateMap); err != nil {
840 return fmt.Errorf("%s: %s", k, err)
841 }
842 if err := mapstructure.WeakDecode(n, &configMap); err != nil {
843 return fmt.Errorf("%s: %s", k, err)
844 }
845
846 // Keep track of whether the state _exists_ at all prior to clearing it
847 stateExists := o != nil
848
849 // Delete any count values, since we don't use those
850 delete(configMap, "%")
851 delete(stateMap, "%")
852
853 // Check if the number of elements has changed.
854 oldLen, newLen := len(stateMap), len(configMap)
855 changed := oldLen != newLen
856 if oldLen != 0 && newLen == 0 && schema.Computed {
857 changed = false
858 }
859
860 // It is computed if we have no old value, no new value, the schema
861 // says it is computed, and it didn't exist in the state before. The
862 // last point means: if it existed in the state, even empty, then it
863 // has already been computed.
864 computed := oldLen == 0 && newLen == 0 && schema.Computed && !stateExists
865
866 // If the count has changed or we're computed, then add a diff for the
867 // count. "nComputed" means that the new value _contains_ a value that
868 // is computed. We don't do granular diffs for this yet, so we mark the
869 // whole map as computed.
870 if changed || computed || nComputed {
871 countSchema := &Schema{
872 Type: TypeInt,
873 Computed: schema.Computed || nComputed,
874 ForceNew: schema.ForceNew,
875 }
876
877 oldStr := strconv.FormatInt(int64(oldLen), 10)
878 newStr := ""
879 if !computed && !nComputed {
880 newStr = strconv.FormatInt(int64(newLen), 10)
881 } else {
882 oldStr = ""
883 }
884
885 diff.Attributes[k+".%"] = countSchema.finalizeDiff(
886 &terraform.ResourceAttrDiff{
887 Old: oldStr,
888 New: newStr,
889 },
890 )
891 }
892
893 // If the new map is nil and we're computed, then ignore it.
894 if n == nil && schema.Computed {
895 return nil
896 }
897
898 // Now we compare, preferring values from the config map
899 for k, v := range configMap {
900 old, ok := stateMap[k]
901 delete(stateMap, k)
902
903 if old == v && ok && !all {
904 continue
905 }
906
907 diff.Attributes[prefix+k] = schema.finalizeDiff(&terraform.ResourceAttrDiff{
908 Old: old,
909 New: v,
910 })
911 }
912 for k, v := range stateMap {
913 diff.Attributes[prefix+k] = schema.finalizeDiff(&terraform.ResourceAttrDiff{
914 Old: v,
915 NewRemoved: true,
916 })
917 }
918
919 return nil
920}
921
922func (m schemaMap) diffSet(
923 k string,
924 schema *Schema,
925 diff *terraform.InstanceDiff,
926 d *ResourceData,
927 all bool) error {
928
929 o, n, _, computedSet := d.diffChange(k)
930 if computedSet {
931 n = nil
932 }
933 nSet := n != nil
934
935 // If we have an old value and no new value is set or will be
936 // computed once all variables can be interpolated and we're
937 // computed, then nothing has changed.
938 if o != nil && n == nil && !computedSet && schema.Computed {
939 return nil
940 }
941
942 if o == nil {
943 o = schema.ZeroValue().(*Set)
944 }
945 if n == nil {
946 n = schema.ZeroValue().(*Set)
947 }
948 os := o.(*Set)
949 ns := n.(*Set)
950
951 // If the new value was set, compare the listCode's to determine if
952 // the two are equal. Comparing listCode's instead of the actual values
953 // is needed because there could be computed values in the set which
954 // would result in false positives while comparing.
955 if !all && nSet && reflect.DeepEqual(os.listCode(), ns.listCode()) {
956 return nil
957 }
958
959 // Get the counts
960 oldLen := os.Len()
961 newLen := ns.Len()
962 oldStr := strconv.Itoa(oldLen)
963 newStr := strconv.Itoa(newLen)
964
965 // Build a schema for our count
966 countSchema := &Schema{
967 Type: TypeInt,
968 Computed: schema.Computed,
969 ForceNew: schema.ForceNew,
970 }
971
972 // If the set computed then say that the # is computed
973 if computedSet || schema.Computed && !nSet {
974 // If # already exists, equals 0 and no new set is supplied, there
975 // is nothing to record in the diff
976 count, ok := d.GetOk(k + ".#")
977 if ok && count.(int) == 0 && !nSet && !computedSet {
978 return nil
979 }
980
981 // Set the count but make sure that if # does not exist, we don't
982 // use the zeroed value
983 countStr := strconv.Itoa(count.(int))
984 if !ok {
985 countStr = ""
986 }
987
988 diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{
989 Old: countStr,
990 NewComputed: true,
991 })
992 return nil
993 }
994
995 // If the counts are not the same, then record that diff
996 changed := oldLen != newLen
997 if changed || all {
998 diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{
999 Old: oldStr,
1000 New: newStr,
1001 })
1002 }
1003
1004 // Build the list of codes that will make up our set. This is the
1005 // removed codes as well as all the codes in the new codes.
1006 codes := make([][]string, 2)
1007 codes[0] = os.Difference(ns).listCode()
1008 codes[1] = ns.listCode()
1009 for _, list := range codes {
1010 for _, code := range list {
1011 switch t := schema.Elem.(type) {
1012 case *Resource:
1013 // This is a complex resource
1014 for k2, schema := range t.Schema {
1015 subK := fmt.Sprintf("%s.%s.%s", k, code, k2)
1016 err := m.diff(subK, schema, diff, d, true)
1017 if err != nil {
1018 return err
1019 }
1020 }
1021 case *Schema:
1022 // Copy the schema so that we can set Computed/ForceNew from
1023 // the parent schema (the TypeSet).
1024 t2 := *t
1025 t2.ForceNew = schema.ForceNew
1026
1027 // This is just a primitive element, so go through each and
1028 // just diff each.
1029 subK := fmt.Sprintf("%s.%s", k, code)
1030 err := m.diff(subK, &t2, diff, d, true)
1031 if err != nil {
1032 return err
1033 }
1034 default:
1035 return fmt.Errorf("%s: unknown element type (internal)", k)
1036 }
1037 }
1038 }
1039
1040 return nil
1041}
1042
1043func (m schemaMap) diffString(
1044 k string,
1045 schema *Schema,
1046 diff *terraform.InstanceDiff,
1047 d *ResourceData,
1048 all bool) error {
1049 var originalN interface{}
1050 var os, ns string
1051 o, n, _, computed := d.diffChange(k)
1052 if schema.StateFunc != nil && n != nil {
1053 originalN = n
1054 n = schema.StateFunc(n)
1055 }
1056 nraw := n
1057 if nraw == nil && o != nil {
1058 nraw = schema.Type.Zero()
1059 }
1060 if err := mapstructure.WeakDecode(o, &os); err != nil {
1061 return fmt.Errorf("%s: %s", k, err)
1062 }
1063 if err := mapstructure.WeakDecode(nraw, &ns); err != nil {
1064 return fmt.Errorf("%s: %s", k, err)
1065 }
1066
1067 if os == ns && !all {
1068 // They're the same value. If there old value is not blank or we
1069 // have an ID, then return right away since we're already setup.
1070 if os != "" || d.Id() != "" {
1071 return nil
1072 }
1073
1074 // Otherwise, only continue if we're computed
1075 if !schema.Computed && !computed {
1076 return nil
1077 }
1078 }
1079
1080 removed := false
1081 if o != nil && n == nil {
1082 removed = true
1083 }
1084 if removed && schema.Computed {
1085 return nil
1086 }
1087
1088 diff.Attributes[k] = schema.finalizeDiff(&terraform.ResourceAttrDiff{
1089 Old: os,
1090 New: ns,
1091 NewExtra: originalN,
1092 NewRemoved: removed,
1093 NewComputed: computed,
1094 })
1095
1096 return nil
1097}
1098
1099func (m schemaMap) inputString(
1100 input terraform.UIInput,
1101 k string,
1102 schema *Schema) (interface{}, error) {
1103 result, err := input.Input(&terraform.InputOpts{
1104 Id: k,
1105 Query: k,
1106 Description: schema.Description,
1107 Default: schema.InputDefault,
1108 })
1109
1110 return result, err
1111}
1112
1113func (m schemaMap) validate(
1114 k string,
1115 schema *Schema,
1116 c *terraform.ResourceConfig) ([]string, []error) {
1117 raw, ok := c.Get(k)
1118 if !ok && schema.DefaultFunc != nil {
1119 // We have a dynamic default. Check if we have a value.
1120 var err error
1121 raw, err = schema.DefaultFunc()
1122 if err != nil {
1123 return nil, []error{fmt.Errorf(
1124 "%q, error loading default: %s", k, err)}
1125 }
1126
1127 // We're okay as long as we had a value set
1128 ok = raw != nil
1129 }
1130 if !ok {
1131 if schema.Required {
1132 return nil, []error{fmt.Errorf(
1133 "%q: required field is not set", k)}
1134 }
1135
1136 return nil, nil
1137 }
1138
1139 if !schema.Required && !schema.Optional {
1140 // This is a computed-only field
1141 return nil, []error{fmt.Errorf(
1142 "%q: this field cannot be set", k)}
1143 }
1144
1145 err := m.validateConflictingAttributes(k, schema, c)
1146 if err != nil {
1147 return nil, []error{err}
1148 }
1149
1150 return m.validateType(k, raw, schema, c)
1151}
1152
1153func (m schemaMap) validateConflictingAttributes(
1154 k string,
1155 schema *Schema,
1156 c *terraform.ResourceConfig) error {
1157
1158 if len(schema.ConflictsWith) == 0 {
1159 return nil
1160 }
1161
1162 for _, conflicting_key := range schema.ConflictsWith {
1163 if value, ok := c.Get(conflicting_key); ok {
1164 return fmt.Errorf(
1165 "%q: conflicts with %s (%#v)", k, conflicting_key, value)
1166 }
1167 }
1168
1169 return nil
1170}
1171
1172func (m schemaMap) validateList(
1173 k string,
1174 raw interface{},
1175 schema *Schema,
1176 c *terraform.ResourceConfig) ([]string, []error) {
1177 // We use reflection to verify the slice because you can't
1178 // case to []interface{} unless the slice is exactly that type.
1179 rawV := reflect.ValueOf(raw)
1180
1181 // If we support promotion and the raw value isn't a slice, wrap
1182 // it in []interface{} and check again.
1183 if schema.PromoteSingle && rawV.Kind() != reflect.Slice {
1184 raw = []interface{}{raw}
1185 rawV = reflect.ValueOf(raw)
1186 }
1187
1188 if rawV.Kind() != reflect.Slice {
1189 return nil, []error{fmt.Errorf(
1190 "%s: should be a list", k)}
1191 }
1192
1193 // Validate length
1194 if schema.MaxItems > 0 && rawV.Len() > schema.MaxItems {
1195 return nil, []error{fmt.Errorf(
1196 "%s: attribute supports %d item maximum, config has %d declared", k, schema.MaxItems, rawV.Len())}
1197 }
1198
1199 if schema.MinItems > 0 && rawV.Len() < schema.MinItems {
1200 return nil, []error{fmt.Errorf(
1201 "%s: attribute supports %d item as a minimum, config has %d declared", k, schema.MinItems, rawV.Len())}
1202 }
1203
1204 // Now build the []interface{}
1205 raws := make([]interface{}, rawV.Len())
1206 for i, _ := range raws {
1207 raws[i] = rawV.Index(i).Interface()
1208 }
1209
1210 var ws []string
1211 var es []error
1212 for i, raw := range raws {
1213 key := fmt.Sprintf("%s.%d", k, i)
1214
1215 // Reify the key value from the ResourceConfig.
1216 // If the list was computed we have all raw values, but some of these
1217 // may be known in the config, and aren't individually marked as Computed.
1218 if r, ok := c.Get(key); ok {
1219 raw = r
1220 }
1221
1222 var ws2 []string
1223 var es2 []error
1224 switch t := schema.Elem.(type) {
1225 case *Resource:
1226 // This is a sub-resource
1227 ws2, es2 = m.validateObject(key, t.Schema, c)
1228 case *Schema:
1229 ws2, es2 = m.validateType(key, raw, t, c)
1230 }
1231
1232 if len(ws2) > 0 {
1233 ws = append(ws, ws2...)
1234 }
1235 if len(es2) > 0 {
1236 es = append(es, es2...)
1237 }
1238 }
1239
1240 return ws, es
1241}
1242
1243func (m schemaMap) validateMap(
1244 k string,
1245 raw interface{},
1246 schema *Schema,
1247 c *terraform.ResourceConfig) ([]string, []error) {
1248 // We use reflection to verify the slice because you can't
1249 // case to []interface{} unless the slice is exactly that type.
1250 rawV := reflect.ValueOf(raw)
1251 switch rawV.Kind() {
1252 case reflect.String:
1253 // If raw and reified are equal, this is a string and should
1254 // be rejected.
1255 reified, reifiedOk := c.Get(k)
1256 if reifiedOk && raw == reified && !c.IsComputed(k) {
1257 return nil, []error{fmt.Errorf("%s: should be a map", k)}
1258 }
1259 // Otherwise it's likely raw is an interpolation.
1260 return nil, nil
1261 case reflect.Map:
1262 case reflect.Slice:
1263 default:
1264 return nil, []error{fmt.Errorf("%s: should be a map", k)}
1265 }
1266
1267 // If it is not a slice, validate directly
1268 if rawV.Kind() != reflect.Slice {
1269 mapIface := rawV.Interface()
1270 if _, errs := validateMapValues(k, mapIface.(map[string]interface{}), schema); len(errs) > 0 {
1271 return nil, errs
1272 }
1273 if schema.ValidateFunc != nil {
1274 return schema.ValidateFunc(mapIface, k)
1275 }
1276 return nil, nil
1277 }
1278
1279 // It is a slice, verify that all the elements are maps
1280 raws := make([]interface{}, rawV.Len())
1281 for i, _ := range raws {
1282 raws[i] = rawV.Index(i).Interface()
1283 }
1284
1285 for _, raw := range raws {
1286 v := reflect.ValueOf(raw)
1287 if v.Kind() != reflect.Map {
1288 return nil, []error{fmt.Errorf(
1289 "%s: should be a map", k)}
1290 }
1291 mapIface := v.Interface()
1292 if _, errs := validateMapValues(k, mapIface.(map[string]interface{}), schema); len(errs) > 0 {
1293 return nil, errs
1294 }
1295 }
1296
1297 if schema.ValidateFunc != nil {
1298 validatableMap := make(map[string]interface{})
1299 for _, raw := range raws {
1300 for k, v := range raw.(map[string]interface{}) {
1301 validatableMap[k] = v
1302 }
1303 }
1304
1305 return schema.ValidateFunc(validatableMap, k)
1306 }
1307
1308 return nil, nil
1309}
1310
1311func validateMapValues(k string, m map[string]interface{}, schema *Schema) ([]string, []error) {
1312 for key, raw := range m {
1313 valueType, err := getValueType(k, schema)
1314 if err != nil {
1315 return nil, []error{err}
1316 }
1317
1318 switch valueType {
1319 case TypeBool:
1320 var n bool
1321 if err := mapstructure.WeakDecode(raw, &n); err != nil {
1322 return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)}
1323 }
1324 case TypeInt:
1325 var n int
1326 if err := mapstructure.WeakDecode(raw, &n); err != nil {
1327 return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)}
1328 }
1329 case TypeFloat:
1330 var n float64
1331 if err := mapstructure.WeakDecode(raw, &n); err != nil {
1332 return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)}
1333 }
1334 case TypeString:
1335 var n string
1336 if err := mapstructure.WeakDecode(raw, &n); err != nil {
1337 return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)}
1338 }
1339 default:
1340 panic(fmt.Sprintf("Unknown validation type: %#v", schema.Type))
1341 }
1342 }
1343 return nil, nil
1344}
1345
1346func getValueType(k string, schema *Schema) (ValueType, error) {
1347 if schema.Elem == nil {
1348 return TypeString, nil
1349 }
1350 if vt, ok := schema.Elem.(ValueType); ok {
1351 return vt, nil
1352 }
1353
1354 if s, ok := schema.Elem.(*Schema); ok {
1355 if s.Elem == nil {
1356 return TypeString, nil
1357 }
1358 if vt, ok := s.Elem.(ValueType); ok {
1359 return vt, nil
1360 }
1361 }
1362
1363 if _, ok := schema.Elem.(*Resource); ok {
1364 // TODO: We don't actually support this (yet)
1365 // but silently pass the validation, until we decide
1366 // how to handle nested structures in maps
1367 return TypeString, nil
1368 }
1369 return 0, fmt.Errorf("%s: unexpected map value type: %#v", k, schema.Elem)
1370}
1371
1372func (m schemaMap) validateObject(
1373 k string,
1374 schema map[string]*Schema,
1375 c *terraform.ResourceConfig) ([]string, []error) {
1376 raw, _ := c.GetRaw(k)
1377 if _, ok := raw.(map[string]interface{}); !ok {
1378 return nil, []error{fmt.Errorf(
1379 "%s: expected object, got %s",
1380 k, reflect.ValueOf(raw).Kind())}
1381 }
1382
1383 var ws []string
1384 var es []error
1385 for subK, s := range schema {
1386 key := subK
1387 if k != "" {
1388 key = fmt.Sprintf("%s.%s", k, subK)
1389 }
1390
1391 ws2, es2 := m.validate(key, s, c)
1392 if len(ws2) > 0 {
1393 ws = append(ws, ws2...)
1394 }
1395 if len(es2) > 0 {
1396 es = append(es, es2...)
1397 }
1398 }
1399
1400 // Detect any extra/unknown keys and report those as errors.
1401 if m, ok := raw.(map[string]interface{}); ok {
1402 for subk, _ := range m {
1403 if _, ok := schema[subk]; !ok {
1404 if subk == TimeoutsConfigKey {
1405 continue
1406 }
1407 es = append(es, fmt.Errorf(
1408 "%s: invalid or unknown key: %s", k, subk))
1409 }
1410 }
1411 }
1412
1413 return ws, es
1414}
1415
1416func (m schemaMap) validatePrimitive(
1417 k string,
1418 raw interface{},
1419 schema *Schema,
1420 c *terraform.ResourceConfig) ([]string, []error) {
1421
1422 // Catch if the user gave a complex type where a primitive was
1423 // expected, so we can return a friendly error message that
1424 // doesn't contain Go type system terminology.
1425 switch reflect.ValueOf(raw).Type().Kind() {
1426 case reflect.Slice:
1427 return nil, []error{
1428 fmt.Errorf("%s must be a single value, not a list", k),
1429 }
1430 case reflect.Map:
1431 return nil, []error{
1432 fmt.Errorf("%s must be a single value, not a map", k),
1433 }
1434 default: // ok
1435 }
1436
1437 if c.IsComputed(k) {
1438 // If the key is being computed, then it is not an error as
1439 // long as it's not a slice or map.
1440 return nil, nil
1441 }
1442
1443 var decoded interface{}
1444 switch schema.Type {
1445 case TypeBool:
1446 // Verify that we can parse this as the correct type
1447 var n bool
1448 if err := mapstructure.WeakDecode(raw, &n); err != nil {
1449 return nil, []error{fmt.Errorf("%s: %s", k, err)}
1450 }
1451 decoded = n
1452 case TypeInt:
1453 // Verify that we can parse this as an int
1454 var n int
1455 if err := mapstructure.WeakDecode(raw, &n); err != nil {
1456 return nil, []error{fmt.Errorf("%s: %s", k, err)}
1457 }
1458 decoded = n
1459 case TypeFloat:
1460 // Verify that we can parse this as an int
1461 var n float64
1462 if err := mapstructure.WeakDecode(raw, &n); err != nil {
1463 return nil, []error{fmt.Errorf("%s: %s", k, err)}
1464 }
1465 decoded = n
1466 case TypeString:
1467 // Verify that we can parse this as a string
1468 var n string
1469 if err := mapstructure.WeakDecode(raw, &n); err != nil {
1470 return nil, []error{fmt.Errorf("%s: %s", k, err)}
1471 }
1472 decoded = n
1473 default:
1474 panic(fmt.Sprintf("Unknown validation type: %#v", schema.Type))
1475 }
1476
1477 if schema.ValidateFunc != nil {
1478 return schema.ValidateFunc(decoded, k)
1479 }
1480
1481 return nil, nil
1482}
1483
1484func (m schemaMap) validateType(
1485 k string,
1486 raw interface{},
1487 schema *Schema,
1488 c *terraform.ResourceConfig) ([]string, []error) {
1489 var ws []string
1490 var es []error
1491 switch schema.Type {
1492 case TypeSet, TypeList:
1493 ws, es = m.validateList(k, raw, schema, c)
1494 case TypeMap:
1495 ws, es = m.validateMap(k, raw, schema, c)
1496 default:
1497 ws, es = m.validatePrimitive(k, raw, schema, c)
1498 }
1499
1500 if schema.Deprecated != "" {
1501 ws = append(ws, fmt.Sprintf(
1502 "%q: [DEPRECATED] %s", k, schema.Deprecated))
1503 }
1504
1505 if schema.Removed != "" {
1506 es = append(es, fmt.Errorf(
1507 "%q: [REMOVED] %s", k, schema.Removed))
1508 }
1509
1510 return ws, es
1511}
1512
1513// Zero returns the zero value for a type.
1514func (t ValueType) Zero() interface{} {
1515 switch t {
1516 case TypeInvalid:
1517 return nil
1518 case TypeBool:
1519 return false
1520 case TypeInt:
1521 return 0
1522 case TypeFloat:
1523 return 0.0
1524 case TypeString:
1525 return ""
1526 case TypeList:
1527 return []interface{}{}
1528 case TypeMap:
1529 return map[string]interface{}{}
1530 case TypeSet:
1531 return new(Set)
1532 case typeObject:
1533 return map[string]interface{}{}
1534 default:
1535 panic(fmt.Sprintf("unknown type %s", t))
1536 }
1537}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/serialize.go b/vendor/github.com/hashicorp/terraform/helper/schema/serialize.go
new file mode 100644
index 0000000..fe6d750
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/serialize.go
@@ -0,0 +1,125 @@
1package schema
2
3import (
4 "bytes"
5 "fmt"
6 "sort"
7 "strconv"
8)
9
10func SerializeValueForHash(buf *bytes.Buffer, val interface{}, schema *Schema) {
11 if val == nil {
12 buf.WriteRune(';')
13 return
14 }
15
16 switch schema.Type {
17 case TypeBool:
18 if val.(bool) {
19 buf.WriteRune('1')
20 } else {
21 buf.WriteRune('0')
22 }
23 case TypeInt:
24 buf.WriteString(strconv.Itoa(val.(int)))
25 case TypeFloat:
26 buf.WriteString(strconv.FormatFloat(val.(float64), 'g', -1, 64))
27 case TypeString:
28 buf.WriteString(val.(string))
29 case TypeList:
30 buf.WriteRune('(')
31 l := val.([]interface{})
32 for _, innerVal := range l {
33 serializeCollectionMemberForHash(buf, innerVal, schema.Elem)
34 }
35 buf.WriteRune(')')
36 case TypeMap:
37
38 m := val.(map[string]interface{})
39 var keys []string
40 for k := range m {
41 keys = append(keys, k)
42 }
43 sort.Strings(keys)
44 buf.WriteRune('[')
45 for _, k := range keys {
46 innerVal := m[k]
47 if innerVal == nil {
48 continue
49 }
50 buf.WriteString(k)
51 buf.WriteRune(':')
52
53 switch innerVal := innerVal.(type) {
54 case int:
55 buf.WriteString(strconv.Itoa(innerVal))
56 case float64:
57 buf.WriteString(strconv.FormatFloat(innerVal, 'g', -1, 64))
58 case string:
59 buf.WriteString(innerVal)
60 default:
61 panic(fmt.Sprintf("unknown value type in TypeMap %T", innerVal))
62 }
63
64 buf.WriteRune(';')
65 }
66 buf.WriteRune(']')
67 case TypeSet:
68 buf.WriteRune('{')
69 s := val.(*Set)
70 for _, innerVal := range s.List() {
71 serializeCollectionMemberForHash(buf, innerVal, schema.Elem)
72 }
73 buf.WriteRune('}')
74 default:
75 panic("unknown schema type to serialize")
76 }
77 buf.WriteRune(';')
78}
79
80// SerializeValueForHash appends a serialization of the given resource config
81// to the given buffer, guaranteeing deterministic results given the same value
82// and schema.
83//
84// Its primary purpose is as input into a hashing function in order
85// to hash complex substructures when used in sets, and so the serialization
86// is not reversible.
87func SerializeResourceForHash(buf *bytes.Buffer, val interface{}, resource *Resource) {
88 if val == nil {
89 return
90 }
91 sm := resource.Schema
92 m := val.(map[string]interface{})
93 var keys []string
94 for k := range sm {
95 keys = append(keys, k)
96 }
97 sort.Strings(keys)
98 for _, k := range keys {
99 innerSchema := sm[k]
100 // Skip attributes that are not user-provided. Computed attributes
101 // do not contribute to the hash since their ultimate value cannot
102 // be known at plan/diff time.
103 if !(innerSchema.Required || innerSchema.Optional) {
104 continue
105 }
106
107 buf.WriteString(k)
108 buf.WriteRune(':')
109 innerVal := m[k]
110 SerializeValueForHash(buf, innerVal, innerSchema)
111 }
112}
113
114func serializeCollectionMemberForHash(buf *bytes.Buffer, val interface{}, elem interface{}) {
115 switch tElem := elem.(type) {
116 case *Schema:
117 SerializeValueForHash(buf, val, tElem)
118 case *Resource:
119 buf.WriteRune('<')
120 SerializeResourceForHash(buf, val, tElem)
121 buf.WriteString(">;")
122 default:
123 panic(fmt.Sprintf("invalid element type: %T", tElem))
124 }
125}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/set.go b/vendor/github.com/hashicorp/terraform/helper/schema/set.go
new file mode 100644
index 0000000..de05f40
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/set.go
@@ -0,0 +1,209 @@
1package schema
2
3import (
4 "bytes"
5 "fmt"
6 "reflect"
7 "sort"
8 "strconv"
9 "sync"
10
11 "github.com/hashicorp/terraform/helper/hashcode"
12)
13
14// HashString hashes strings. If you want a Set of strings, this is the
15// SchemaSetFunc you want.
16func HashString(v interface{}) int {
17 return hashcode.String(v.(string))
18}
19
20// HashResource hashes complex structures that are described using
21// a *Resource. This is the default set implementation used when a set's
22// element type is a full resource.
23func HashResource(resource *Resource) SchemaSetFunc {
24 return func(v interface{}) int {
25 var buf bytes.Buffer
26 SerializeResourceForHash(&buf, v, resource)
27 return hashcode.String(buf.String())
28 }
29}
30
31// HashSchema hashes values that are described using a *Schema. This is the
32// default set implementation used when a set's element type is a single
33// schema.
34func HashSchema(schema *Schema) SchemaSetFunc {
35 return func(v interface{}) int {
36 var buf bytes.Buffer
37 SerializeValueForHash(&buf, v, schema)
38 return hashcode.String(buf.String())
39 }
40}
41
42// Set is a set data structure that is returned for elements of type
43// TypeSet.
44type Set struct {
45 F SchemaSetFunc
46
47 m map[string]interface{}
48 once sync.Once
49}
50
51// NewSet is a convenience method for creating a new set with the given
52// items.
53func NewSet(f SchemaSetFunc, items []interface{}) *Set {
54 s := &Set{F: f}
55 for _, i := range items {
56 s.Add(i)
57 }
58
59 return s
60}
61
62// CopySet returns a copy of another set.
63func CopySet(otherSet *Set) *Set {
64 return NewSet(otherSet.F, otherSet.List())
65}
66
67// Add adds an item to the set if it isn't already in the set.
68func (s *Set) Add(item interface{}) {
69 s.add(item, false)
70}
71
72// Remove removes an item if it's already in the set. Idempotent.
73func (s *Set) Remove(item interface{}) {
74 s.remove(item)
75}
76
77// Contains checks if the set has the given item.
78func (s *Set) Contains(item interface{}) bool {
79 _, ok := s.m[s.hash(item)]
80 return ok
81}
82
83// Len returns the amount of items in the set.
84func (s *Set) Len() int {
85 return len(s.m)
86}
87
88// List returns the elements of this set in slice format.
89//
90// The order of the returned elements is deterministic. Given the same
91// set, the order of this will always be the same.
92func (s *Set) List() []interface{} {
93 result := make([]interface{}, len(s.m))
94 for i, k := range s.listCode() {
95 result[i] = s.m[k]
96 }
97
98 return result
99}
100
101// Difference performs a set difference of the two sets, returning
102// a new third set that has only the elements unique to this set.
103func (s *Set) Difference(other *Set) *Set {
104 result := &Set{F: s.F}
105 result.once.Do(result.init)
106
107 for k, v := range s.m {
108 if _, ok := other.m[k]; !ok {
109 result.m[k] = v
110 }
111 }
112
113 return result
114}
115
116// Intersection performs the set intersection of the two sets
117// and returns a new third set.
118func (s *Set) Intersection(other *Set) *Set {
119 result := &Set{F: s.F}
120 result.once.Do(result.init)
121
122 for k, v := range s.m {
123 if _, ok := other.m[k]; ok {
124 result.m[k] = v
125 }
126 }
127
128 return result
129}
130
131// Union performs the set union of the two sets and returns a new third
132// set.
133func (s *Set) Union(other *Set) *Set {
134 result := &Set{F: s.F}
135 result.once.Do(result.init)
136
137 for k, v := range s.m {
138 result.m[k] = v
139 }
140 for k, v := range other.m {
141 result.m[k] = v
142 }
143
144 return result
145}
146
147func (s *Set) Equal(raw interface{}) bool {
148 other, ok := raw.(*Set)
149 if !ok {
150 return false
151 }
152
153 return reflect.DeepEqual(s.m, other.m)
154}
155
156func (s *Set) GoString() string {
157 return fmt.Sprintf("*Set(%#v)", s.m)
158}
159
160func (s *Set) init() {
161 s.m = make(map[string]interface{})
162}
163
164func (s *Set) add(item interface{}, computed bool) string {
165 s.once.Do(s.init)
166
167 code := s.hash(item)
168 if computed {
169 code = "~" + code
170 }
171
172 if _, ok := s.m[code]; !ok {
173 s.m[code] = item
174 }
175
176 return code
177}
178
179func (s *Set) hash(item interface{}) string {
180 code := s.F(item)
181 // Always return a nonnegative hashcode.
182 if code < 0 {
183 code = -code
184 }
185 return strconv.Itoa(code)
186}
187
188func (s *Set) remove(item interface{}) string {
189 s.once.Do(s.init)
190
191 code := s.hash(item)
192 delete(s.m, code)
193
194 return code
195}
196
197func (s *Set) index(item interface{}) int {
198 return sort.SearchStrings(s.listCode(), s.hash(item))
199}
200
201func (s *Set) listCode() []string {
202 // Sort the hash codes so the order of the list is deterministic
203 keys := make([]string, 0, len(s.m))
204 for k := range s.m {
205 keys = append(keys, k)
206 }
207 sort.Sort(sort.StringSlice(keys))
208 return keys
209}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/testing.go b/vendor/github.com/hashicorp/terraform/helper/schema/testing.go
new file mode 100644
index 0000000..9765bdb
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/testing.go
@@ -0,0 +1,30 @@
1package schema
2
3import (
4 "testing"
5
6 "github.com/hashicorp/terraform/config"
7 "github.com/hashicorp/terraform/terraform"
8)
9
10// TestResourceDataRaw creates a ResourceData from a raw configuration map.
11func TestResourceDataRaw(
12 t *testing.T, schema map[string]*Schema, raw map[string]interface{}) *ResourceData {
13 c, err := config.NewRawConfig(raw)
14 if err != nil {
15 t.Fatalf("err: %s", err)
16 }
17
18 sm := schemaMap(schema)
19 diff, err := sm.Diff(nil, terraform.NewResourceConfig(c))
20 if err != nil {
21 t.Fatalf("err: %s", err)
22 }
23
24 result, err := sm.Data(nil, diff)
25 if err != nil {
26 t.Fatalf("err: %s", err)
27 }
28
29 return result
30}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/valuetype.go b/vendor/github.com/hashicorp/terraform/helper/schema/valuetype.go
new file mode 100644
index 0000000..9286987
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/valuetype.go
@@ -0,0 +1,21 @@
1package schema
2
3//go:generate stringer -type=ValueType valuetype.go
4
5// ValueType is an enum of the type that can be represented by a schema.
6type ValueType int
7
8const (
9 TypeInvalid ValueType = iota
10 TypeBool
11 TypeInt
12 TypeFloat
13 TypeString
14 TypeList
15 TypeMap
16 TypeSet
17 typeObject
18)
19
20// NOTE: ValueType has more functions defined on it in schema.go. We can't
21// put them here because we reference other files.
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go b/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go
new file mode 100644
index 0000000..1610cec
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go
@@ -0,0 +1,16 @@
1// Code generated by "stringer -type=ValueType valuetype.go"; DO NOT EDIT.
2
3package schema
4
5import "fmt"
6
7const _ValueType_name = "TypeInvalidTypeBoolTypeIntTypeFloatTypeStringTypeListTypeMapTypeSettypeObject"
8
9var _ValueType_index = [...]uint8{0, 11, 19, 26, 35, 45, 53, 60, 67, 77}
10
11func (i ValueType) String() string {
12 if i < 0 || i >= ValueType(len(_ValueType_index)-1) {
13 return fmt.Sprintf("ValueType(%d)", i)
14 }
15 return _ValueType_name[_ValueType_index[i]:_ValueType_index[i+1]]
16}
diff --git a/vendor/github.com/hashicorp/terraform/helper/shadow/closer.go b/vendor/github.com/hashicorp/terraform/helper/shadow/closer.go
new file mode 100644
index 0000000..7edd5e7
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/shadow/closer.go
@@ -0,0 +1,80 @@
1package shadow
2
3import (
4 "fmt"
5 "io"
6 "reflect"
7
8 "github.com/hashicorp/go-multierror"
9 "github.com/mitchellh/reflectwalk"
10)
11
12// Close will close all shadow values within the given structure.
13//
14// This uses reflection to walk the structure, find all shadow elements,
15// and close them. Currently this will only find struct fields that are
16// shadow values, and not slice elements, etc.
17func Close(v interface{}) error {
18 // We require a pointer so we can address the internal fields
19 val := reflect.ValueOf(v)
20 if val.Kind() != reflect.Ptr {
21 return fmt.Errorf("value must be a pointer")
22 }
23
24 // Walk and close
25 var w closeWalker
26 if err := reflectwalk.Walk(v, &w); err != nil {
27 return err
28 }
29
30 return w.Err
31}
32
33type closeWalker struct {
34 Err error
35}
36
37func (w *closeWalker) Struct(reflect.Value) error {
38 // Do nothing. We implement this for reflectwalk.StructWalker
39 return nil
40}
41
42func (w *closeWalker) StructField(f reflect.StructField, v reflect.Value) error {
43 // Not sure why this would be but lets avoid some panics
44 if !v.IsValid() {
45 return nil
46 }
47
48 // Empty for exported, so don't check unexported fields
49 if f.PkgPath != "" {
50 return nil
51 }
52
53 // Verify the io.Closer is in this package
54 typ := v.Type()
55 if typ.PkgPath() != "github.com/hashicorp/terraform/helper/shadow" {
56 return nil
57 }
58
59 // We're looking for an io.Closer
60 raw := v.Interface()
61 if raw == nil {
62 return nil
63 }
64
65 closer, ok := raw.(io.Closer)
66 if !ok && v.CanAddr() {
67 closer, ok = v.Addr().Interface().(io.Closer)
68 }
69 if !ok {
70 return reflectwalk.SkipEntry
71 }
72
73 // Close it
74 if err := closer.Close(); err != nil {
75 w.Err = multierror.Append(w.Err, err)
76 }
77
78 // Don't go into the struct field
79 return reflectwalk.SkipEntry
80}
diff --git a/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go b/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go
new file mode 100644
index 0000000..4223e92
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go
@@ -0,0 +1,128 @@
1package shadow
2
3import (
4 "sync"
5)
6
7// ComparedValue is a struct that finds a value by comparing some key
8// to the list of stored values. This is useful when there is no easy
9// uniquely identifying key that works in a map (for that, use KeyedValue).
10//
11// ComparedValue is very expensive, relative to other Value types. Try to
12// limit the number of values stored in a ComparedValue by potentially
13// nesting it within a KeyedValue (a keyed value points to a compared value,
14// for example).
15type ComparedValue struct {
16 // Func is a function that is given the lookup key and a single
17 // stored value. If it matches, it returns true.
18 Func func(k, v interface{}) bool
19
20 lock sync.Mutex
21 once sync.Once
22 closed bool
23 values []interface{}
24 waiters map[interface{}]*Value
25}
26
27// Close closes the value. This can never fail. For a definition of
28// "close" see the ErrClosed docs.
29func (w *ComparedValue) Close() error {
30 w.lock.Lock()
31 defer w.lock.Unlock()
32
33 // Set closed to true always
34 w.closed = true
35
36 // For all waiters, complete with ErrClosed
37 for k, val := range w.waiters {
38 val.SetValue(ErrClosed)
39 delete(w.waiters, k)
40 }
41
42 return nil
43}
44
45// Value returns the value that was set for the given key, or blocks
46// until one is available.
47func (w *ComparedValue) Value(k interface{}) interface{} {
48 v, val := w.valueWaiter(k)
49 if val == nil {
50 return v
51 }
52
53 return val.Value()
54}
55
56// ValueOk gets the value for the given key, returning immediately if the
57// value doesn't exist. The second return argument is true if the value exists.
58func (w *ComparedValue) ValueOk(k interface{}) (interface{}, bool) {
59 v, val := w.valueWaiter(k)
60 return v, val == nil
61}
62
63func (w *ComparedValue) SetValue(v interface{}) {
64 w.lock.Lock()
65 defer w.lock.Unlock()
66 w.once.Do(w.init)
67
68 // Check if we already have this exact value (by simply comparing
69 // with == directly). If we do, then we don't insert it again.
70 found := false
71 for _, v2 := range w.values {
72 if v == v2 {
73 found = true
74 break
75 }
76 }
77
78 if !found {
79 // Set the value, always
80 w.values = append(w.values, v)
81 }
82
83 // Go through the waiters
84 for k, val := range w.waiters {
85 if w.Func(k, v) {
86 val.SetValue(v)
87 delete(w.waiters, k)
88 }
89 }
90}
91
92func (w *ComparedValue) valueWaiter(k interface{}) (interface{}, *Value) {
93 w.lock.Lock()
94 w.once.Do(w.init)
95
96 // Look for a pre-existing value
97 for _, v := range w.values {
98 if w.Func(k, v) {
99 w.lock.Unlock()
100 return v, nil
101 }
102 }
103
104 // If we're closed, return that
105 if w.closed {
106 w.lock.Unlock()
107 return ErrClosed, nil
108 }
109
110 // Pre-existing value doesn't exist, create a waiter
111 val := w.waiters[k]
112 if val == nil {
113 val = new(Value)
114 w.waiters[k] = val
115 }
116 w.lock.Unlock()
117
118 // Return the waiter
119 return nil, val
120}
121
122// Must be called with w.lock held.
123func (w *ComparedValue) init() {
124 w.waiters = make(map[interface{}]*Value)
125 if w.Func == nil {
126 w.Func = func(k, v interface{}) bool { return k == v }
127 }
128}
diff --git a/vendor/github.com/hashicorp/terraform/helper/shadow/keyed_value.go b/vendor/github.com/hashicorp/terraform/helper/shadow/keyed_value.go
new file mode 100644
index 0000000..432b036
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/shadow/keyed_value.go
@@ -0,0 +1,151 @@
1package shadow
2
3import (
4 "sync"
5)
6
7// KeyedValue is a struct that coordinates a value by key. If a value is
8// not available for a give key, it'll block until it is available.
9type KeyedValue struct {
10 lock sync.Mutex
11 once sync.Once
12 values map[string]interface{}
13 waiters map[string]*Value
14 closed bool
15}
16
17// Close closes the value. This can never fail. For a definition of
18// "close" see the ErrClosed docs.
19func (w *KeyedValue) Close() error {
20 w.lock.Lock()
21 defer w.lock.Unlock()
22
23 // Set closed to true always
24 w.closed = true
25
26 // For all waiters, complete with ErrClosed
27 for k, val := range w.waiters {
28 val.SetValue(ErrClosed)
29 delete(w.waiters, k)
30 }
31
32 return nil
33}
34
35// Value returns the value that was set for the given key, or blocks
36// until one is available.
37func (w *KeyedValue) Value(k string) interface{} {
38 w.lock.Lock()
39 v, val := w.valueWaiter(k)
40 w.lock.Unlock()
41
42 // If we have no waiter, then return the value
43 if val == nil {
44 return v
45 }
46
47 // We have a waiter, so wait
48 return val.Value()
49}
50
51// WaitForChange waits for the value with the given key to be set again.
52// If the key isn't set, it'll wait for an initial value. Note that while
53// it is called "WaitForChange", the value isn't guaranteed to _change_;
54// this will return when a SetValue is called for the given k.
55func (w *KeyedValue) WaitForChange(k string) interface{} {
56 w.lock.Lock()
57 w.once.Do(w.init)
58
59 // If we're closed, we're closed
60 if w.closed {
61 w.lock.Unlock()
62 return ErrClosed
63 }
64
65 // Check for an active waiter. If there isn't one, make it
66 val := w.waiters[k]
67 if val == nil {
68 val = new(Value)
69 w.waiters[k] = val
70 }
71 w.lock.Unlock()
72
73 // And wait
74 return val.Value()
75}
76
77// ValueOk gets the value for the given key, returning immediately if the
78// value doesn't exist. The second return argument is true if the value exists.
79func (w *KeyedValue) ValueOk(k string) (interface{}, bool) {
80 w.lock.Lock()
81 defer w.lock.Unlock()
82
83 v, val := w.valueWaiter(k)
84 return v, val == nil
85}
86
87func (w *KeyedValue) SetValue(k string, v interface{}) {
88 w.lock.Lock()
89 defer w.lock.Unlock()
90 w.setValue(k, v)
91}
92
93// Init will initialize the key to a given value only if the key has
94// not been set before. This is safe to call multiple times and in parallel.
95func (w *KeyedValue) Init(k string, v interface{}) {
96 w.lock.Lock()
97 defer w.lock.Unlock()
98
99 // If we have a waiter, set the value.
100 _, val := w.valueWaiter(k)
101 if val != nil {
102 w.setValue(k, v)
103 }
104}
105
106// Must be called with w.lock held.
107func (w *KeyedValue) init() {
108 w.values = make(map[string]interface{})
109 w.waiters = make(map[string]*Value)
110}
111
112// setValue is like SetValue but assumes the lock is held.
113func (w *KeyedValue) setValue(k string, v interface{}) {
114 w.once.Do(w.init)
115
116 // Set the value, always
117 w.values[k] = v
118
119 // If we have a waiter, set it
120 if val, ok := w.waiters[k]; ok {
121 val.SetValue(v)
122 delete(w.waiters, k)
123 }
124}
125
126// valueWaiter gets the value or the Value waiter for a given key.
127//
128// This must be called with lock held.
129func (w *KeyedValue) valueWaiter(k string) (interface{}, *Value) {
130 w.once.Do(w.init)
131
132 // If we have this value already, return it
133 if v, ok := w.values[k]; ok {
134 return v, nil
135 }
136
137 // If we're closed, return that
138 if w.closed {
139 return ErrClosed, nil
140 }
141
142 // No pending value, check for a waiter
143 val := w.waiters[k]
144 if val == nil {
145 val = new(Value)
146 w.waiters[k] = val
147 }
148
149 // Return the waiter
150 return nil, val
151}
diff --git a/vendor/github.com/hashicorp/terraform/helper/shadow/ordered_value.go b/vendor/github.com/hashicorp/terraform/helper/shadow/ordered_value.go
new file mode 100644
index 0000000..0a43d4d
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/shadow/ordered_value.go
@@ -0,0 +1,66 @@
1package shadow
2
3import (
4 "container/list"
5 "sync"
6)
7
8// OrderedValue is a struct that keeps track of a value in the order
9// it is set. Each time Value() is called, it will return the most recent
10// calls value then discard it.
11//
12// This is unlike Value that returns the same value once it is set.
13type OrderedValue struct {
14 lock sync.Mutex
15 values *list.List
16 waiters *list.List
17}
18
19// Value returns the last value that was set, or blocks until one
20// is received.
21func (w *OrderedValue) Value() interface{} {
22 w.lock.Lock()
23
24 // If we have a pending value already, use it
25 if w.values != nil && w.values.Len() > 0 {
26 front := w.values.Front()
27 w.values.Remove(front)
28 w.lock.Unlock()
29 return front.Value
30 }
31
32 // No pending value, create a waiter
33 if w.waiters == nil {
34 w.waiters = list.New()
35 }
36
37 var val Value
38 w.waiters.PushBack(&val)
39 w.lock.Unlock()
40
41 // Return the value once we have it
42 return val.Value()
43}
44
45// SetValue sets the latest value.
46func (w *OrderedValue) SetValue(v interface{}) {
47 w.lock.Lock()
48 defer w.lock.Unlock()
49
50 // If we have a waiter, notify it
51 if w.waiters != nil && w.waiters.Len() > 0 {
52 front := w.waiters.Front()
53 w.waiters.Remove(front)
54
55 val := front.Value.(*Value)
56 val.SetValue(v)
57 return
58 }
59
60 // Add it to the list of values
61 if w.values == nil {
62 w.values = list.New()
63 }
64
65 w.values.PushBack(v)
66}
diff --git a/vendor/github.com/hashicorp/terraform/helper/shadow/value.go b/vendor/github.com/hashicorp/terraform/helper/shadow/value.go
new file mode 100644
index 0000000..2413335
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/shadow/value.go
@@ -0,0 +1,79 @@
1package shadow
2
3import (
4 "errors"
5 "sync"
6)
7
8// ErrClosed is returned by any closed values.
9//
10// A "closed value" is when the shadow has been notified that the real
11// side is complete and any blocking values will _never_ be satisfied
12// in the future. In this case, this error is returned. If a value is already
13// available, that is still returned.
14var ErrClosed = errors.New("shadow closed")
15
16// Value is a struct that coordinates a value between two
17// parallel routines. It is similar to atomic.Value except that when
18// Value is called if it isn't set it will wait for it.
19//
20// The Value can be closed with Close, which will cause any future
21// blocking operations to return immediately with ErrClosed.
22type Value struct {
23 lock sync.Mutex
24 cond *sync.Cond
25 value interface{}
26 valueSet bool
27}
28
29// Close closes the value. This can never fail. For a definition of
30// "close" see the struct docs.
31func (w *Value) Close() error {
32 w.lock.Lock()
33 set := w.valueSet
34 w.lock.Unlock()
35
36 // If we haven't set the value, set it
37 if !set {
38 w.SetValue(ErrClosed)
39 }
40
41 // Done
42 return nil
43}
44
45// Value returns the value that was set.
46func (w *Value) Value() interface{} {
47 w.lock.Lock()
48 defer w.lock.Unlock()
49
50 // If we already have a value just return
51 for !w.valueSet {
52 // No value, setup the condition variable if we have to
53 if w.cond == nil {
54 w.cond = sync.NewCond(&w.lock)
55 }
56
57 // Wait on it
58 w.cond.Wait()
59 }
60
61 // Return the value
62 return w.value
63}
64
65// SetValue sets the value.
66func (w *Value) SetValue(v interface{}) {
67 w.lock.Lock()
68 defer w.lock.Unlock()
69
70 // Set the value
71 w.valueSet = true
72 w.value = v
73
74 // If we have a condition, clear it
75 if w.cond != nil {
76 w.cond.Broadcast()
77 w.cond = nil
78 }
79}
diff --git a/vendor/github.com/hashicorp/terraform/helper/structure/expand_json.go b/vendor/github.com/hashicorp/terraform/helper/structure/expand_json.go
new file mode 100644
index 0000000..b3eb90f
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/structure/expand_json.go
@@ -0,0 +1,11 @@
1package structure
2
3import "encoding/json"
4
5func ExpandJsonFromString(jsonString string) (map[string]interface{}, error) {
6 var result map[string]interface{}
7
8 err := json.Unmarshal([]byte(jsonString), &result)
9
10 return result, err
11}
diff --git a/vendor/github.com/hashicorp/terraform/helper/structure/flatten_json.go b/vendor/github.com/hashicorp/terraform/helper/structure/flatten_json.go
new file mode 100644
index 0000000..578ad2e
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/structure/flatten_json.go
@@ -0,0 +1,16 @@
1package structure
2
3import "encoding/json"
4
5func FlattenJsonToString(input map[string]interface{}) (string, error) {
6 if len(input) == 0 {
7 return "", nil
8 }
9
10 result, err := json.Marshal(input)
11 if err != nil {
12 return "", err
13 }
14
15 return string(result), nil
16}
diff --git a/vendor/github.com/hashicorp/terraform/helper/structure/normalize_json.go b/vendor/github.com/hashicorp/terraform/helper/structure/normalize_json.go
new file mode 100644
index 0000000..3256b47
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/structure/normalize_json.go
@@ -0,0 +1,24 @@
1package structure
2
3import "encoding/json"
4
5// Takes a value containing JSON string and passes it through
6// the JSON parser to normalize it, returns either a parsing
7// error or normalized JSON string.
8func NormalizeJsonString(jsonString interface{}) (string, error) {
9 var j interface{}
10
11 if jsonString == nil || jsonString.(string) == "" {
12 return "", nil
13 }
14
15 s := jsonString.(string)
16
17 err := json.Unmarshal([]byte(s), &j)
18 if err != nil {
19 return s, err
20 }
21
22 bytes, _ := json.Marshal(j)
23 return string(bytes[:]), nil
24}
diff --git a/vendor/github.com/hashicorp/terraform/helper/structure/suppress_json_diff.go b/vendor/github.com/hashicorp/terraform/helper/structure/suppress_json_diff.go
new file mode 100644
index 0000000..46f794a
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/structure/suppress_json_diff.go
@@ -0,0 +1,21 @@
1package structure
2
3import (
4 "reflect"
5
6 "github.com/hashicorp/terraform/helper/schema"
7)
8
9func SuppressJsonDiff(k, old, new string, d *schema.ResourceData) bool {
10 oldMap, err := ExpandJsonFromString(old)
11 if err != nil {
12 return false
13 }
14
15 newMap, err := ExpandJsonFromString(new)
16 if err != nil {
17 return false
18 }
19
20 return reflect.DeepEqual(oldMap, newMap)
21}
diff --git a/vendor/github.com/hashicorp/terraform/helper/validation/validation.go b/vendor/github.com/hashicorp/terraform/helper/validation/validation.go
new file mode 100644
index 0000000..7b894f5
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/validation/validation.go
@@ -0,0 +1,108 @@
1package validation
2
3import (
4 "fmt"
5 "net"
6 "strings"
7
8 "github.com/hashicorp/terraform/helper/schema"
9 "github.com/hashicorp/terraform/helper/structure"
10)
11
12// IntBetween returns a SchemaValidateFunc which tests if the provided value
13// is of type int and is between min and max (inclusive)
14func IntBetween(min, max int) schema.SchemaValidateFunc {
15 return func(i interface{}, k string) (s []string, es []error) {
16 v, ok := i.(int)
17 if !ok {
18 es = append(es, fmt.Errorf("expected type of %s to be int", k))
19 return
20 }
21
22 if v < min || v > max {
23 es = append(es, fmt.Errorf("expected %s to be in the range (%d - %d), got %d", k, min, max, v))
24 return
25 }
26
27 return
28 }
29}
30
31// StringInSlice returns a SchemaValidateFunc which tests if the provided value
32// is of type string and matches the value of an element in the valid slice
33// will test with in lower case if ignoreCase is true
34func StringInSlice(valid []string, ignoreCase bool) schema.SchemaValidateFunc {
35 return func(i interface{}, k string) (s []string, es []error) {
36 v, ok := i.(string)
37 if !ok {
38 es = append(es, fmt.Errorf("expected type of %s to be string", k))
39 return
40 }
41
42 for _, str := range valid {
43 if v == str || (ignoreCase && strings.ToLower(v) == strings.ToLower(str)) {
44 return
45 }
46 }
47
48 es = append(es, fmt.Errorf("expected %s to be one of %v, got %s", k, valid, v))
49 return
50 }
51}
52
53// StringLenBetween returns a SchemaValidateFunc which tests if the provided value
54// is of type string and has length between min and max (inclusive)
55func StringLenBetween(min, max int) schema.SchemaValidateFunc {
56 return func(i interface{}, k string) (s []string, es []error) {
57 v, ok := i.(string)
58 if !ok {
59 es = append(es, fmt.Errorf("expected type of %s to be string", k))
60 return
61 }
62 if len(v) < min || len(v) > max {
63 es = append(es, fmt.Errorf("expected length of %s to be in the range (%d - %d), got %s", k, min, max, v))
64 }
65 return
66 }
67}
68
69// CIDRNetwork returns a SchemaValidateFunc which tests if the provided value
70// is of type string, is in valid CIDR network notation, and has significant bits between min and max (inclusive)
71func CIDRNetwork(min, max int) schema.SchemaValidateFunc {
72 return func(i interface{}, k string) (s []string, es []error) {
73 v, ok := i.(string)
74 if !ok {
75 es = append(es, fmt.Errorf("expected type of %s to be string", k))
76 return
77 }
78
79 _, ipnet, err := net.ParseCIDR(v)
80 if err != nil {
81 es = append(es, fmt.Errorf(
82 "expected %s to contain a valid CIDR, got: %s with err: %s", k, v, err))
83 return
84 }
85
86 if ipnet == nil || v != ipnet.String() {
87 es = append(es, fmt.Errorf(
88 "expected %s to contain a valid network CIDR, expected %s, got %s",
89 k, ipnet, v))
90 }
91
92 sigbits, _ := ipnet.Mask.Size()
93 if sigbits < min || sigbits > max {
94 es = append(es, fmt.Errorf(
95 "expected %q to contain a network CIDR with between %d and %d significant bits, got: %d",
96 k, min, max, sigbits))
97 }
98
99 return
100 }
101}
102
103func ValidateJsonString(v interface{}, k string) (ws []string, errors []error) {
104 if _, err := structure.NormalizeJsonString(v); err != nil {
105 errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err))
106 }
107 return
108}