diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform')
23 files changed, 553 insertions, 386 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/config.go b/vendor/github.com/hashicorp/terraform/config/config.go index 9a764ac..a157824 100644 --- a/vendor/github.com/hashicorp/terraform/config/config.go +++ b/vendor/github.com/hashicorp/terraform/config/config.go | |||
@@ -545,7 +545,7 @@ func (c *Config) Validate() error { | |||
545 | 545 | ||
546 | // Verify provisioners | 546 | // Verify provisioners |
547 | for _, p := range r.Provisioners { | 547 | for _, p := range r.Provisioners { |
548 | // This validation checks that there are now splat variables | 548 | // This validation checks that there are no splat variables |
549 | // referencing ourself. This currently is not allowed. | 549 | // referencing ourself. This currently is not allowed. |
550 | 550 | ||
551 | for _, v := range p.ConnInfo.Variables { | 551 | for _, v := range p.ConnInfo.Variables { |
@@ -705,17 +705,6 @@ func (c *Config) Validate() error { | |||
705 | } | 705 | } |
706 | } | 706 | } |
707 | 707 | ||
708 | // Check that all variables are in the proper context | ||
709 | for source, rc := range c.rawConfigs() { | ||
710 | walker := &interpolationWalker{ | ||
711 | ContextF: c.validateVarContextFn(source, &errs), | ||
712 | } | ||
713 | if err := reflectwalk.Walk(rc.Raw, walker); err != nil { | ||
714 | errs = append(errs, fmt.Errorf( | ||
715 | "%s: error reading config: %s", source, err)) | ||
716 | } | ||
717 | } | ||
718 | |||
719 | // Validate the self variable | 708 | // Validate the self variable |
720 | for source, rc := range c.rawConfigs() { | 709 | for source, rc := range c.rawConfigs() { |
721 | // Ignore provisioners. This is a pretty brittle way to do this, | 710 | // Ignore provisioners. This is a pretty brittle way to do this, |
@@ -787,57 +776,6 @@ func (c *Config) rawConfigs() map[string]*RawConfig { | |||
787 | return result | 776 | return result |
788 | } | 777 | } |
789 | 778 | ||
790 | func (c *Config) validateVarContextFn( | ||
791 | source string, errs *[]error) interpolationWalkerContextFunc { | ||
792 | return func(loc reflectwalk.Location, node ast.Node) { | ||
793 | // If we're in a slice element, then its fine, since you can do | ||
794 | // anything in there. | ||
795 | if loc == reflectwalk.SliceElem { | ||
796 | return | ||
797 | } | ||
798 | |||
799 | // Otherwise, let's check if there is a splat resource variable | ||
800 | // at the top level in here. We do this by doing a transform that | ||
801 | // replaces everything with a noop node unless its a variable | ||
802 | // access or concat. This should turn the AST into a flat tree | ||
803 | // of Concat(Noop, ...). If there are any variables left that are | ||
804 | // multi-access, then its still broken. | ||
805 | node = node.Accept(func(n ast.Node) ast.Node { | ||
806 | // If it is a concat or variable access, we allow it. | ||
807 | switch n.(type) { | ||
808 | case *ast.Output: | ||
809 | return n | ||
810 | case *ast.VariableAccess: | ||
811 | return n | ||
812 | } | ||
813 | |||
814 | // Otherwise, noop | ||
815 | return &noopNode{} | ||
816 | }) | ||
817 | |||
818 | vars, err := DetectVariables(node) | ||
819 | if err != nil { | ||
820 | // Ignore it since this will be caught during parse. This | ||
821 | // actually probably should never happen by the time this | ||
822 | // is called, but its okay. | ||
823 | return | ||
824 | } | ||
825 | |||
826 | for _, v := range vars { | ||
827 | rv, ok := v.(*ResourceVariable) | ||
828 | if !ok { | ||
829 | return | ||
830 | } | ||
831 | |||
832 | if rv.Multi && rv.Index == -1 { | ||
833 | *errs = append(*errs, fmt.Errorf( | ||
834 | "%s: use of the splat ('*') operator must be wrapped in a list declaration", | ||
835 | source)) | ||
836 | } | ||
837 | } | ||
838 | } | ||
839 | } | ||
840 | |||
841 | func (c *Config) validateDependsOn( | 779 | func (c *Config) validateDependsOn( |
842 | n string, | 780 | n string, |
843 | v []string, | 781 | v []string, |
diff --git a/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go b/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go index f1f97b0..7b7b3f2 100644 --- a/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go +++ b/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go | |||
@@ -24,6 +24,7 @@ import ( | |||
24 | "github.com/hashicorp/hil" | 24 | "github.com/hashicorp/hil" |
25 | "github.com/hashicorp/hil/ast" | 25 | "github.com/hashicorp/hil/ast" |
26 | "github.com/mitchellh/go-homedir" | 26 | "github.com/mitchellh/go-homedir" |
27 | "golang.org/x/crypto/bcrypt" | ||
27 | ) | 28 | ) |
28 | 29 | ||
29 | // stringSliceToVariableValue converts a string slice into the value | 30 | // stringSliceToVariableValue converts a string slice into the value |
@@ -59,6 +60,7 @@ func Funcs() map[string]ast.Function { | |||
59 | "base64encode": interpolationFuncBase64Encode(), | 60 | "base64encode": interpolationFuncBase64Encode(), |
60 | "base64sha256": interpolationFuncBase64Sha256(), | 61 | "base64sha256": interpolationFuncBase64Sha256(), |
61 | "base64sha512": interpolationFuncBase64Sha512(), | 62 | "base64sha512": interpolationFuncBase64Sha512(), |
63 | "bcrypt": interpolationFuncBcrypt(), | ||
62 | "ceil": interpolationFuncCeil(), | 64 | "ceil": interpolationFuncCeil(), |
63 | "chomp": interpolationFuncChomp(), | 65 | "chomp": interpolationFuncChomp(), |
64 | "cidrhost": interpolationFuncCidrHost(), | 66 | "cidrhost": interpolationFuncCidrHost(), |
@@ -89,6 +91,7 @@ func Funcs() map[string]ast.Function { | |||
89 | "merge": interpolationFuncMerge(), | 91 | "merge": interpolationFuncMerge(), |
90 | "min": interpolationFuncMin(), | 92 | "min": interpolationFuncMin(), |
91 | "pathexpand": interpolationFuncPathExpand(), | 93 | "pathexpand": interpolationFuncPathExpand(), |
94 | "pow": interpolationFuncPow(), | ||
92 | "uuid": interpolationFuncUUID(), | 95 | "uuid": interpolationFuncUUID(), |
93 | "replace": interpolationFuncReplace(), | 96 | "replace": interpolationFuncReplace(), |
94 | "sha1": interpolationFuncSha1(), | 97 | "sha1": interpolationFuncSha1(), |
@@ -394,6 +397,17 @@ func interpolationFuncConcat() ast.Function { | |||
394 | } | 397 | } |
395 | } | 398 | } |
396 | 399 | ||
400 | // interpolationFuncPow returns base x exponential of y. | ||
401 | func interpolationFuncPow() ast.Function { | ||
402 | return ast.Function{ | ||
403 | ArgTypes: []ast.Type{ast.TypeFloat, ast.TypeFloat}, | ||
404 | ReturnType: ast.TypeFloat, | ||
405 | Callback: func(args []interface{}) (interface{}, error) { | ||
406 | return math.Pow(args[0].(float64), args[1].(float64)), nil | ||
407 | }, | ||
408 | } | ||
409 | } | ||
410 | |||
397 | // interpolationFuncFile implements the "file" function that allows | 411 | // interpolationFuncFile implements the "file" function that allows |
398 | // loading contents from a file. | 412 | // loading contents from a file. |
399 | func interpolationFuncFile() ast.Function { | 413 | func interpolationFuncFile() ast.Function { |
@@ -1310,6 +1324,40 @@ func interpolationFuncBase64Sha512() ast.Function { | |||
1310 | } | 1324 | } |
1311 | } | 1325 | } |
1312 | 1326 | ||
1327 | func interpolationFuncBcrypt() ast.Function { | ||
1328 | return ast.Function{ | ||
1329 | ArgTypes: []ast.Type{ast.TypeString}, | ||
1330 | Variadic: true, | ||
1331 | VariadicType: ast.TypeString, | ||
1332 | ReturnType: ast.TypeString, | ||
1333 | Callback: func(args []interface{}) (interface{}, error) { | ||
1334 | defaultCost := 10 | ||
1335 | |||
1336 | if len(args) > 1 { | ||
1337 | costStr := args[1].(string) | ||
1338 | cost, err := strconv.Atoi(costStr) | ||
1339 | if err != nil { | ||
1340 | return "", err | ||
1341 | } | ||
1342 | |||
1343 | defaultCost = cost | ||
1344 | } | ||
1345 | |||
1346 | if len(args) > 2 { | ||
1347 | return "", fmt.Errorf("bcrypt() takes no more than two arguments") | ||
1348 | } | ||
1349 | |||
1350 | input := args[0].(string) | ||
1351 | out, err := bcrypt.GenerateFromPassword([]byte(input), defaultCost) | ||
1352 | if err != nil { | ||
1353 | return "", fmt.Errorf("error occured generating password %s", err.Error()) | ||
1354 | } | ||
1355 | |||
1356 | return string(out), nil | ||
1357 | }, | ||
1358 | } | ||
1359 | } | ||
1360 | |||
1313 | func interpolationFuncUUID() ast.Function { | 1361 | func interpolationFuncUUID() ast.Function { |
1314 | return ast.Function{ | 1362 | return ast.Function{ |
1315 | ArgTypes: []ast.Type{}, | 1363 | ArgTypes: []ast.Type{}, |
diff --git a/vendor/github.com/hashicorp/terraform/dag/set.go b/vendor/github.com/hashicorp/terraform/dag/set.go index 3929c9d..92b4215 100644 --- a/vendor/github.com/hashicorp/terraform/dag/set.go +++ b/vendor/github.com/hashicorp/terraform/dag/set.go | |||
@@ -81,6 +81,20 @@ func (s *Set) Difference(other *Set) *Set { | |||
81 | return result | 81 | return result |
82 | } | 82 | } |
83 | 83 | ||
84 | // Filter returns a set that contains the elements from the receiver | ||
85 | // where the given callback returns true. | ||
86 | func (s *Set) Filter(cb func(interface{}) bool) *Set { | ||
87 | result := new(Set) | ||
88 | |||
89 | for _, v := range s.m { | ||
90 | if cb(v) { | ||
91 | result.Add(v) | ||
92 | } | ||
93 | } | ||
94 | |||
95 | return result | ||
96 | } | ||
97 | |||
84 | // Len is the number of items in the set. | 98 | // Len is the number of items in the set. |
85 | func (s *Set) Len() int { | 99 | func (s *Set) Len() int { |
86 | if s == nil { | 100 | if s == nil { |
diff --git a/vendor/github.com/hashicorp/terraform/helper/acctest/acctest.go b/vendor/github.com/hashicorp/terraform/helper/acctest/acctest.go deleted file mode 100644 index 9d31031..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/acctest/acctest.go +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | // Package acctest contains for Terraform Acceptance Tests | ||
2 | package acctest | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/acctest/random.go b/vendor/github.com/hashicorp/terraform/helper/acctest/random.go deleted file mode 100644 index 3ddc078..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/acctest/random.go +++ /dev/null | |||
@@ -1,93 +0,0 @@ | |||
1 | package acctest | ||
2 | |||
3 | import ( | ||
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 | ||
22 | func 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 | ||
29 | func RandomWithPrefix(name string) string { | ||
30 | reseed() | ||
31 | return fmt.Sprintf("%s-%d", name, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) | ||
32 | } | ||
33 | |||
34 | func 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 | ||
43 | func 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 | ||
49 | func 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. | ||
60 | func 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 | ||
81 | func reseed() { | ||
82 | rand.Seed(time.Now().UTC().UnixNano()) | ||
83 | } | ||
84 | |||
85 | const ( | ||
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 deleted file mode 100644 index 87c60b8..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/acctest/remotetests.go +++ /dev/null | |||
@@ -1,27 +0,0 @@ | |||
1 | package acctest | ||
2 | |||
3 | import ( | ||
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. | ||
14 | const 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. | ||
19 | func 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/resource/testing.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing.go index 04367c5..ebdbde2 100644 --- a/vendor/github.com/hashicorp/terraform/helper/resource/testing.go +++ b/vendor/github.com/hashicorp/terraform/helper/resource/testing.go | |||
@@ -1,6 +1,7 @@ | |||
1 | package resource | 1 | package resource |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "flag" | ||
4 | "fmt" | 5 | "fmt" |
5 | "io" | 6 | "io" |
6 | "io/ioutil" | 7 | "io/ioutil" |
@@ -20,6 +21,153 @@ import ( | |||
20 | "github.com/hashicorp/terraform/terraform" | 21 | "github.com/hashicorp/terraform/terraform" |
21 | ) | 22 | ) |
22 | 23 | ||
24 | // flagSweep is a flag available when running tests on the command line. It | ||
25 | // contains a comma seperated list of regions to for the sweeper functions to | ||
26 | // run in. This flag bypasses the normal Test path and instead runs functions designed to | ||
27 | // clean up any leaked resources a testing environment could have created. It is | ||
28 | // a best effort attempt, and relies on Provider authors to implement "Sweeper" | ||
29 | // methods for resources. | ||
30 | |||
31 | // Adding Sweeper methods with AddTestSweepers will | ||
32 | // construct a list of sweeper funcs to be called here. We iterate through | ||
33 | // regions provided by the sweep flag, and for each region we iterate through the | ||
34 | // tests, and exit on any errors. At time of writing, sweepers are ran | ||
35 | // sequentially, however they can list dependencies to be ran first. We track | ||
36 | // the sweepers that have been ran, so as to not run a sweeper twice for a given | ||
37 | // region. | ||
38 | // | ||
39 | // WARNING: | ||
40 | // Sweepers are designed to be destructive. You should not use the -sweep flag | ||
41 | // in any environment that is not strictly a test environment. Resources will be | ||
42 | // destroyed. | ||
43 | |||
44 | var flagSweep = flag.String("sweep", "", "List of Regions to run available Sweepers") | ||
45 | var flagSweepRun = flag.String("sweep-run", "", "Comma seperated list of Sweeper Tests to run") | ||
46 | var sweeperFuncs map[string]*Sweeper | ||
47 | |||
48 | // map of sweepers that have ran, and the success/fail status based on any error | ||
49 | // raised | ||
50 | var sweeperRunList map[string]bool | ||
51 | |||
52 | // type SweeperFunc is a signature for a function that acts as a sweeper. It | ||
53 | // accepts a string for the region that the sweeper is to be ran in. This | ||
54 | // function must be able to construct a valid client for that region. | ||
55 | type SweeperFunc func(r string) error | ||
56 | |||
57 | type Sweeper struct { | ||
58 | // Name for sweeper. Must be unique to be ran by the Sweeper Runner | ||
59 | Name string | ||
60 | |||
61 | // Dependencies list the const names of other Sweeper functions that must be ran | ||
62 | // prior to running this Sweeper. This is an ordered list that will be invoked | ||
63 | // recursively at the helper/resource level | ||
64 | Dependencies []string | ||
65 | |||
66 | // Sweeper function that when invoked sweeps the Provider of specific | ||
67 | // resources | ||
68 | F SweeperFunc | ||
69 | } | ||
70 | |||
71 | func init() { | ||
72 | sweeperFuncs = make(map[string]*Sweeper) | ||
73 | } | ||
74 | |||
75 | // AddTestSweepers function adds a given name and Sweeper configuration | ||
76 | // pair to the internal sweeperFuncs map. Invoke this function to register a | ||
77 | // resource sweeper to be available for running when the -sweep flag is used | ||
78 | // with `go test`. Sweeper names must be unique to help ensure a given sweeper | ||
79 | // is only ran once per run. | ||
80 | func AddTestSweepers(name string, s *Sweeper) { | ||
81 | if _, ok := sweeperFuncs[name]; ok { | ||
82 | log.Fatalf("[ERR] Error adding (%s) to sweeperFuncs: function already exists in map", name) | ||
83 | } | ||
84 | |||
85 | sweeperFuncs[name] = s | ||
86 | } | ||
87 | |||
88 | func TestMain(m *testing.M) { | ||
89 | flag.Parse() | ||
90 | if *flagSweep != "" { | ||
91 | // parse flagSweep contents for regions to run | ||
92 | regions := strings.Split(*flagSweep, ",") | ||
93 | |||
94 | // get filtered list of sweepers to run based on sweep-run flag | ||
95 | sweepers := filterSweepers(*flagSweepRun, sweeperFuncs) | ||
96 | for _, region := range regions { | ||
97 | region = strings.TrimSpace(region) | ||
98 | // reset sweeperRunList for each region | ||
99 | sweeperRunList = map[string]bool{} | ||
100 | |||
101 | log.Printf("[DEBUG] Running Sweepers for region (%s):\n", region) | ||
102 | for _, sweeper := range sweepers { | ||
103 | if err := runSweeperWithRegion(region, sweeper); err != nil { | ||
104 | log.Fatalf("[ERR] error running (%s): %s", sweeper.Name, err) | ||
105 | } | ||
106 | } | ||
107 | |||
108 | log.Printf("Sweeper Tests ran:\n") | ||
109 | for s, _ := range sweeperRunList { | ||
110 | fmt.Printf("\t- %s\n", s) | ||
111 | } | ||
112 | } | ||
113 | } else { | ||
114 | os.Exit(m.Run()) | ||
115 | } | ||
116 | } | ||
117 | |||
118 | // filterSweepers takes a comma seperated string listing the names of sweepers | ||
119 | // to be ran, and returns a filtered set from the list of all of sweepers to | ||
120 | // run based on the names given. | ||
121 | func filterSweepers(f string, source map[string]*Sweeper) map[string]*Sweeper { | ||
122 | filterSlice := strings.Split(strings.ToLower(f), ",") | ||
123 | if len(filterSlice) == 1 && filterSlice[0] == "" { | ||
124 | // if the filter slice is a single element of "" then no sweeper list was | ||
125 | // given, so just return the full list | ||
126 | return source | ||
127 | } | ||
128 | |||
129 | sweepers := make(map[string]*Sweeper) | ||
130 | for name, sweeper := range source { | ||
131 | for _, s := range filterSlice { | ||
132 | if strings.Contains(strings.ToLower(name), s) { | ||
133 | sweepers[name] = sweeper | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | return sweepers | ||
138 | } | ||
139 | |||
140 | // runSweeperWithRegion recieves a sweeper and a region, and recursively calls | ||
141 | // itself with that region for every dependency found for that sweeper. If there | ||
142 | // are no dependencies, invoke the contained sweeper fun with the region, and | ||
143 | // add the success/fail status to the sweeperRunList. | ||
144 | func runSweeperWithRegion(region string, s *Sweeper) error { | ||
145 | for _, dep := range s.Dependencies { | ||
146 | if depSweeper, ok := sweeperFuncs[dep]; ok { | ||
147 | log.Printf("[DEBUG] Sweeper (%s) has dependency (%s), running..", s.Name, dep) | ||
148 | if err := runSweeperWithRegion(region, depSweeper); err != nil { | ||
149 | return err | ||
150 | } | ||
151 | } else { | ||
152 | log.Printf("[DEBUG] Sweeper (%s) has dependency (%s), but that sweeper was not found", s.Name, dep) | ||
153 | } | ||
154 | } | ||
155 | |||
156 | if _, ok := sweeperRunList[s.Name]; ok { | ||
157 | log.Printf("[DEBUG] Sweeper (%s) already ran in region (%s)", s.Name, region) | ||
158 | return nil | ||
159 | } | ||
160 | |||
161 | runE := s.F(region) | ||
162 | if runE == nil { | ||
163 | sweeperRunList[s.Name] = true | ||
164 | } else { | ||
165 | sweeperRunList[s.Name] = false | ||
166 | } | ||
167 | |||
168 | return runE | ||
169 | } | ||
170 | |||
23 | const TestEnvVar = "TF_ACC" | 171 | const TestEnvVar = "TF_ACC" |
24 | 172 | ||
25 | // TestProvider can be implemented by any ResourceProvider to provide custom | 173 | // TestProvider can be implemented by any ResourceProvider to provide custom |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go index c1564a2..856c675 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go | |||
@@ -41,6 +41,10 @@ type Provisioner struct { | |||
41 | // information. | 41 | // information. |
42 | ApplyFunc func(ctx context.Context) error | 42 | ApplyFunc func(ctx context.Context) error |
43 | 43 | ||
44 | // ValidateFunc is a function for extended validation. This is optional | ||
45 | // and should be used when individual field validation is not enough. | ||
46 | ValidateFunc func(*ResourceData) ([]string, []error) | ||
47 | |||
44 | stopCtx context.Context | 48 | stopCtx context.Context |
45 | stopCtxCancel context.CancelFunc | 49 | stopCtxCancel context.CancelFunc |
46 | stopOnce sync.Once | 50 | stopOnce sync.Once |
@@ -117,8 +121,30 @@ func (p *Provisioner) Stop() error { | |||
117 | return nil | 121 | return nil |
118 | } | 122 | } |
119 | 123 | ||
120 | func (p *Provisioner) Validate(c *terraform.ResourceConfig) ([]string, []error) { | 124 | func (p *Provisioner) Validate(config *terraform.ResourceConfig) ([]string, []error) { |
121 | return schemaMap(p.Schema).Validate(c) | 125 | if err := p.InternalValidate(); err != nil { |
126 | return nil, []error{fmt.Errorf( | ||
127 | "Internal validation of the provisioner failed! This is always a bug\n"+ | ||
128 | "with the provisioner itself, and not a user issue. Please report\n"+ | ||
129 | "this bug:\n\n%s", err)} | ||
130 | } | ||
131 | w := []string{} | ||
132 | e := []error{} | ||
133 | if p.Schema != nil { | ||
134 | w2, e2 := schemaMap(p.Schema).Validate(config) | ||
135 | w = append(w, w2...) | ||
136 | e = append(e, e2...) | ||
137 | } | ||
138 | if p.ValidateFunc != nil { | ||
139 | data := &ResourceData{ | ||
140 | schema: p.Schema, | ||
141 | config: config, | ||
142 | } | ||
143 | w2, e2 := p.ValidateFunc(data) | ||
144 | w = append(w, w2...) | ||
145 | e = append(e, e2...) | ||
146 | } | ||
147 | return w, e | ||
122 | } | 148 | } |
123 | 149 | ||
124 | // Apply implementation of terraform.ResourceProvisioner interface. | 150 | // Apply implementation of terraform.ResourceProvisioner interface. |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go index 32d1721..632672a 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go | |||
@@ -1373,8 +1373,8 @@ func (m schemaMap) validateObject( | |||
1373 | k string, | 1373 | k string, |
1374 | schema map[string]*Schema, | 1374 | schema map[string]*Schema, |
1375 | c *terraform.ResourceConfig) ([]string, []error) { | 1375 | c *terraform.ResourceConfig) ([]string, []error) { |
1376 | raw, _ := c.GetRaw(k) | 1376 | raw, _ := c.Get(k) |
1377 | if _, ok := raw.(map[string]interface{}); !ok { | 1377 | if _, ok := raw.(map[string]interface{}); !ok && !c.IsComputed(k) { |
1378 | return nil, []error{fmt.Errorf( | 1378 | return nil, []error{fmt.Errorf( |
1379 | "%s: expected object, got %s", | 1379 | "%s: expected object, got %s", |
1380 | k, reflect.ValueOf(raw).Kind())} | 1380 | k, reflect.ValueOf(raw).Kind())} |
diff --git a/vendor/github.com/hashicorp/terraform/helper/structure/expand_json.go b/vendor/github.com/hashicorp/terraform/helper/structure/expand_json.go deleted file mode 100644 index b3eb90f..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/structure/expand_json.go +++ /dev/null | |||
@@ -1,11 +0,0 @@ | |||
1 | package structure | ||
2 | |||
3 | import "encoding/json" | ||
4 | |||
5 | func 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 deleted file mode 100644 index 578ad2e..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/structure/flatten_json.go +++ /dev/null | |||
@@ -1,16 +0,0 @@ | |||
1 | package structure | ||
2 | |||
3 | import "encoding/json" | ||
4 | |||
5 | func 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 deleted file mode 100644 index 3256b47..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/structure/normalize_json.go +++ /dev/null | |||
@@ -1,24 +0,0 @@ | |||
1 | package structure | ||
2 | |||
3 | import "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. | ||
8 | func 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 deleted file mode 100644 index 46f794a..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/structure/suppress_json_diff.go +++ /dev/null | |||
@@ -1,21 +0,0 @@ | |||
1 | package structure | ||
2 | |||
3 | import ( | ||
4 | "reflect" | ||
5 | |||
6 | "github.com/hashicorp/terraform/helper/schema" | ||
7 | ) | ||
8 | |||
9 | func 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 deleted file mode 100644 index 7b894f5..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/validation/validation.go +++ /dev/null | |||
@@ -1,108 +0,0 @@ | |||
1 | package validation | ||
2 | |||
3 | import ( | ||
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) | ||
14 | func 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 | ||
34 | func 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) | ||
55 | func 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) | ||
71 | func 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 | |||
103 | func 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 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_refresh.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_refresh.go index 88ae338..0634f96 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_refresh.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_refresh.go | |||
@@ -1,6 +1,8 @@ | |||
1 | package terraform | 1 | package terraform |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "log" | ||
5 | |||
4 | "github.com/hashicorp/terraform/config" | 6 | "github.com/hashicorp/terraform/config" |
5 | "github.com/hashicorp/terraform/config/module" | 7 | "github.com/hashicorp/terraform/config/module" |
6 | "github.com/hashicorp/terraform/dag" | 8 | "github.com/hashicorp/terraform/dag" |
@@ -56,8 +58,16 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer { | |||
56 | } | 58 | } |
57 | } | 59 | } |
58 | 60 | ||
59 | concreteResource := func(a *NodeAbstractResource) dag.Vertex { | 61 | concreteManagedResource := func(a *NodeAbstractResource) dag.Vertex { |
60 | return &NodeRefreshableResource{ | 62 | return &NodeRefreshableManagedResource{ |
63 | NodeAbstractCountResource: &NodeAbstractCountResource{ | ||
64 | NodeAbstractResource: a, | ||
65 | }, | ||
66 | } | ||
67 | } | ||
68 | |||
69 | concreteManagedResourceInstance := func(a *NodeAbstractResource) dag.Vertex { | ||
70 | return &NodeRefreshableManagedResourceInstance{ | ||
61 | NodeAbstractResource: a, | 71 | NodeAbstractResource: a, |
62 | } | 72 | } |
63 | } | 73 | } |
@@ -71,13 +81,25 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer { | |||
71 | } | 81 | } |
72 | 82 | ||
73 | steps := []GraphTransformer{ | 83 | steps := []GraphTransformer{ |
74 | // Creates all the resources represented in the state | 84 | // Creates all the managed resources that aren't in the state, but only if |
75 | &StateTransformer{ | 85 | // we have a state already. No resources in state means there's not |
76 | Concrete: concreteResource, | 86 | // anything to refresh. |
77 | State: b.State, | 87 | func() GraphTransformer { |
78 | }, | 88 | if b.State.HasResources() { |
79 | 89 | return &ConfigTransformer{ | |
80 | // Creates all the data resources that aren't in the state | 90 | Concrete: concreteManagedResource, |
91 | Module: b.Module, | ||
92 | Unique: true, | ||
93 | ModeFilter: true, | ||
94 | Mode: config.ManagedResourceMode, | ||
95 | } | ||
96 | } | ||
97 | log.Println("[TRACE] No managed resources in state during refresh, skipping managed resource transformer") | ||
98 | return nil | ||
99 | }(), | ||
100 | |||
101 | // Creates all the data resources that aren't in the state. This will also | ||
102 | // add any orphans from scaling in as destroy nodes. | ||
81 | &ConfigTransformer{ | 103 | &ConfigTransformer{ |
82 | Concrete: concreteDataResource, | 104 | Concrete: concreteDataResource, |
83 | Module: b.Module, | 105 | Module: b.Module, |
@@ -86,6 +108,15 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer { | |||
86 | Mode: config.DataResourceMode, | 108 | Mode: config.DataResourceMode, |
87 | }, | 109 | }, |
88 | 110 | ||
111 | // Add any fully-orphaned resources from config (ones that have been | ||
112 | // removed completely, not ones that are just orphaned due to a scaled-in | ||
113 | // count. | ||
114 | &OrphanResourceTransformer{ | ||
115 | Concrete: concreteManagedResourceInstance, | ||
116 | State: b.State, | ||
117 | Module: b.Module, | ||
118 | }, | ||
119 | |||
89 | // Attach the state | 120 | // Attach the state |
90 | &AttachStateTransformer{State: b.State}, | 121 | &AttachStateTransformer{State: b.State}, |
91 | 122 | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/interpolate.go b/vendor/github.com/hashicorp/terraform/terraform/interpolate.go index 19dcf21..0def295 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/interpolate.go +++ b/vendor/github.com/hashicorp/terraform/terraform/interpolate.go | |||
@@ -731,6 +731,19 @@ func (i *Interpolater) resourceCountMax( | |||
731 | return count, nil | 731 | return count, nil |
732 | } | 732 | } |
733 | 733 | ||
734 | // If we have no module state in the apply walk, that suggests we've hit | ||
735 | // a rather awkward edge-case: the resource this variable refers to | ||
736 | // has count = 0 and is the only resource processed so far on this walk, | ||
737 | // and so we've ended up not creating any resource states yet. We don't | ||
738 | // create a module state until the first resource is written into it, | ||
739 | // so the module state doesn't exist when we get here. | ||
740 | // | ||
741 | // In this case we act as we would if we had been passed a module | ||
742 | // with an empty resource state map. | ||
743 | if ms == nil { | ||
744 | return 0, nil | ||
745 | } | ||
746 | |||
734 | // We need to determine the list of resource keys to get values from. | 747 | // We need to determine the list of resource keys to get values from. |
735 | // This needs to be sorted so the order is deterministic. We used to | 748 | // This needs to be sorted so the order is deterministic. We used to |
736 | // use "cr.Count()" but that doesn't work if the count is interpolated | 749 | // use "cr.Count()" but that doesn't work if the count is interpolated |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go b/vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go index d504c89..45129b3 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go | |||
@@ -33,6 +33,17 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er | |||
33 | } | 33 | } |
34 | } | 34 | } |
35 | 35 | ||
36 | // We also need a destroyable resource for orphans that are a result of a | ||
37 | // scaled-in count. | ||
38 | concreteResourceDestroyable := func(a *NodeAbstractResource) dag.Vertex { | ||
39 | // Add the config since we don't do that via transforms | ||
40 | a.Config = n.Config | ||
41 | |||
42 | return &NodeDestroyableDataResource{ | ||
43 | NodeAbstractResource: a, | ||
44 | } | ||
45 | } | ||
46 | |||
36 | // Start creating the steps | 47 | // Start creating the steps |
37 | steps := []GraphTransformer{ | 48 | steps := []GraphTransformer{ |
38 | // Expand the count. | 49 | // Expand the count. |
@@ -42,6 +53,15 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er | |||
42 | Addr: n.ResourceAddr(), | 53 | Addr: n.ResourceAddr(), |
43 | }, | 54 | }, |
44 | 55 | ||
56 | // Add the count orphans. As these are orphaned refresh nodes, we add them | ||
57 | // directly as NodeDestroyableDataResource. | ||
58 | &OrphanResourceCountTransformer{ | ||
59 | Concrete: concreteResourceDestroyable, | ||
60 | Count: count, | ||
61 | Addr: n.ResourceAddr(), | ||
62 | State: state, | ||
63 | }, | ||
64 | |||
45 | // Attach the state | 65 | // Attach the state |
46 | &AttachStateTransformer{State: state}, | 66 | &AttachStateTransformer{State: state}, |
47 | 67 | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_output.go b/vendor/github.com/hashicorp/terraform/terraform/node_output.go index e28e6f0..9017a63 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_output.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_output.go | |||
@@ -5,6 +5,7 @@ import ( | |||
5 | "strings" | 5 | "strings" |
6 | 6 | ||
7 | "github.com/hashicorp/terraform/config" | 7 | "github.com/hashicorp/terraform/config" |
8 | "github.com/hashicorp/terraform/dag" | ||
8 | ) | 9 | ) |
9 | 10 | ||
10 | // NodeApplyableOutput represents an output that is "applyable": | 11 | // NodeApplyableOutput represents an output that is "applyable": |
@@ -35,6 +36,14 @@ func (n *NodeApplyableOutput) RemoveIfNotTargeted() bool { | |||
35 | return true | 36 | return true |
36 | } | 37 | } |
37 | 38 | ||
39 | // GraphNodeTargetDownstream | ||
40 | func (n *NodeApplyableOutput) TargetDownstream(targetedDeps, untargetedDeps *dag.Set) bool { | ||
41 | // If any of the direct dependencies of an output are targeted then | ||
42 | // the output must always be targeted as well, so its value will always | ||
43 | // be up-to-date at the completion of an apply walk. | ||
44 | return true | ||
45 | } | ||
46 | |||
38 | // GraphNodeReferenceable | 47 | // GraphNodeReferenceable |
39 | func (n *NodeApplyableOutput) ReferenceableName() []string { | 48 | func (n *NodeApplyableOutput) ReferenceableName() []string { |
40 | name := fmt.Sprintf("output.%s", n.Config.Name) | 49 | name := fmt.Sprintf("output.%s", n.Config.Name) |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_refresh.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_refresh.go index 3a44926..6ab9df7 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_refresh.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_refresh.go | |||
@@ -4,21 +4,99 @@ import ( | |||
4 | "fmt" | 4 | "fmt" |
5 | 5 | ||
6 | "github.com/hashicorp/terraform/config" | 6 | "github.com/hashicorp/terraform/config" |
7 | "github.com/hashicorp/terraform/dag" | ||
7 | ) | 8 | ) |
8 | 9 | ||
9 | // NodeRefreshableResource represents a resource that is "applyable": | 10 | // NodeRefreshableManagedResource represents a resource that is expanabled into |
11 | // NodeRefreshableManagedResourceInstance. Resource count orphans are also added. | ||
12 | type NodeRefreshableManagedResource struct { | ||
13 | *NodeAbstractCountResource | ||
14 | } | ||
15 | |||
16 | // GraphNodeDynamicExpandable | ||
17 | func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph, error) { | ||
18 | // Grab the state which we read | ||
19 | state, lock := ctx.State() | ||
20 | lock.RLock() | ||
21 | defer lock.RUnlock() | ||
22 | |||
23 | // Expand the resource count which must be available by now from EvalTree | ||
24 | count, err := n.Config.Count() | ||
25 | if err != nil { | ||
26 | return nil, err | ||
27 | } | ||
28 | |||
29 | // The concrete resource factory we'll use | ||
30 | concreteResource := func(a *NodeAbstractResource) dag.Vertex { | ||
31 | // Add the config and state since we don't do that via transforms | ||
32 | a.Config = n.Config | ||
33 | |||
34 | return &NodeRefreshableManagedResourceInstance{ | ||
35 | NodeAbstractResource: a, | ||
36 | } | ||
37 | } | ||
38 | |||
39 | // Start creating the steps | ||
40 | steps := []GraphTransformer{ | ||
41 | // Expand the count. | ||
42 | &ResourceCountTransformer{ | ||
43 | Concrete: concreteResource, | ||
44 | Count: count, | ||
45 | Addr: n.ResourceAddr(), | ||
46 | }, | ||
47 | |||
48 | // Switch up any node missing state to a plannable resource. This helps | ||
49 | // catch cases where data sources depend on the counts from this resource | ||
50 | // during a scale out. | ||
51 | &ResourceRefreshPlannableTransformer{ | ||
52 | State: state, | ||
53 | }, | ||
54 | |||
55 | // Add the count orphans to make sure these resources are accounted for | ||
56 | // during a scale in. | ||
57 | &OrphanResourceCountTransformer{ | ||
58 | Concrete: concreteResource, | ||
59 | Count: count, | ||
60 | Addr: n.ResourceAddr(), | ||
61 | State: state, | ||
62 | }, | ||
63 | |||
64 | // Attach the state | ||
65 | &AttachStateTransformer{State: state}, | ||
66 | |||
67 | // Targeting | ||
68 | &TargetsTransformer{ParsedTargets: n.Targets}, | ||
69 | |||
70 | // Connect references so ordering is correct | ||
71 | &ReferenceTransformer{}, | ||
72 | |||
73 | // Make sure there is a single root | ||
74 | &RootTransformer{}, | ||
75 | } | ||
76 | |||
77 | // Build the graph | ||
78 | b := &BasicGraphBuilder{ | ||
79 | Steps: steps, | ||
80 | Validate: true, | ||
81 | Name: "NodeRefreshableManagedResource", | ||
82 | } | ||
83 | |||
84 | return b.Build(ctx.Path()) | ||
85 | } | ||
86 | |||
87 | // NodeRefreshableManagedResourceInstance represents a resource that is "applyable": | ||
10 | // it is ready to be applied and is represented by a diff. | 88 | // it is ready to be applied and is represented by a diff. |
11 | type NodeRefreshableResource struct { | 89 | type NodeRefreshableManagedResourceInstance struct { |
12 | *NodeAbstractResource | 90 | *NodeAbstractResource |
13 | } | 91 | } |
14 | 92 | ||
15 | // GraphNodeDestroyer | 93 | // GraphNodeDestroyer |
16 | func (n *NodeRefreshableResource) DestroyAddr() *ResourceAddress { | 94 | func (n *NodeRefreshableManagedResourceInstance) DestroyAddr() *ResourceAddress { |
17 | return n.Addr | 95 | return n.Addr |
18 | } | 96 | } |
19 | 97 | ||
20 | // GraphNodeEvalable | 98 | // GraphNodeEvalable |
21 | func (n *NodeRefreshableResource) EvalTree() EvalNode { | 99 | func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode { |
22 | // Eval info is different depending on what kind of resource this is | 100 | // Eval info is different depending on what kind of resource this is |
23 | switch mode := n.Addr.Mode; mode { | 101 | switch mode := n.Addr.Mode; mode { |
24 | case config.ManagedResourceMode: | 102 | case config.ManagedResourceMode: |
@@ -44,7 +122,7 @@ func (n *NodeRefreshableResource) EvalTree() EvalNode { | |||
44 | } | 122 | } |
45 | } | 123 | } |
46 | 124 | ||
47 | func (n *NodeRefreshableResource) evalTreeManagedResource() EvalNode { | 125 | func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalNode { |
48 | addr := n.NodeAbstractResource.Addr | 126 | addr := n.NodeAbstractResource.Addr |
49 | 127 | ||
50 | // stateId is the ID to put into the state | 128 | // stateId is the ID to put into the state |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_resource_refresh_plannable.go b/vendor/github.com/hashicorp/terraform/terraform/transform_resource_refresh_plannable.go new file mode 100644 index 0000000..35358a3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_resource_refresh_plannable.go | |||
@@ -0,0 +1,55 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "log" | ||
6 | ) | ||
7 | |||
8 | // ResourceRefreshPlannableTransformer is a GraphTransformer that replaces any | ||
9 | // nodes that don't have state yet exist in config with | ||
10 | // NodePlannableResourceInstance. | ||
11 | // | ||
12 | // This transformer is used when expanding count on managed resource nodes | ||
13 | // during the refresh phase to ensure that data sources that have | ||
14 | // interpolations that depend on resources existing in the graph can be walked | ||
15 | // properly. | ||
16 | type ResourceRefreshPlannableTransformer struct { | ||
17 | // The full global state. | ||
18 | State *State | ||
19 | } | ||
20 | |||
21 | // Transform implements GraphTransformer for | ||
22 | // ResourceRefreshPlannableTransformer. | ||
23 | func (t *ResourceRefreshPlannableTransformer) Transform(g *Graph) error { | ||
24 | nextVertex: | ||
25 | for _, v := range g.Vertices() { | ||
26 | addr := v.(*NodeRefreshableManagedResourceInstance).Addr | ||
27 | |||
28 | // Find the state for this address, if there is one | ||
29 | filter := &StateFilter{State: t.State} | ||
30 | results, err := filter.Filter(addr.String()) | ||
31 | if err != nil { | ||
32 | return err | ||
33 | } | ||
34 | |||
35 | // Check to see if we have a state for this resource. If we do, skip this | ||
36 | // node. | ||
37 | for _, result := range results { | ||
38 | if _, ok := result.Value.(*ResourceState); ok { | ||
39 | continue nextVertex | ||
40 | } | ||
41 | } | ||
42 | // If we don't, convert this resource to a NodePlannableResourceInstance node | ||
43 | // with all of the data we need to make it happen. | ||
44 | log.Printf("[TRACE] No state for %s, converting to NodePlannableResourceInstance", addr.String()) | ||
45 | new := &NodePlannableResourceInstance{ | ||
46 | NodeAbstractResource: v.(*NodeRefreshableManagedResourceInstance).NodeAbstractResource, | ||
47 | } | ||
48 | // Replace the node in the graph | ||
49 | if !g.Replace(v, new) { | ||
50 | return fmt.Errorf("ResourceRefreshPlannableTransformer: Could not replace node %#v with %#v", v, new) | ||
51 | } | ||
52 | } | ||
53 | |||
54 | return nil | ||
55 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_targets.go b/vendor/github.com/hashicorp/terraform/terraform/transform_targets.go index 225ac4b..125f9e3 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_targets.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_targets.go | |||
@@ -15,6 +15,21 @@ type GraphNodeTargetable interface { | |||
15 | SetTargets([]ResourceAddress) | 15 | SetTargets([]ResourceAddress) |
16 | } | 16 | } |
17 | 17 | ||
18 | // GraphNodeTargetDownstream is an interface for graph nodes that need to | ||
19 | // be remain present under targeting if any of their dependencies are targeted. | ||
20 | // TargetDownstream is called with the set of vertices that are direct | ||
21 | // dependencies for the node, and it should return true if the node must remain | ||
22 | // in the graph in support of those dependencies. | ||
23 | // | ||
24 | // This is used in situations where the dependency edges are representing an | ||
25 | // ordering relationship but the dependency must still be visited if its | ||
26 | // dependencies are visited. This is true for outputs, for example, since | ||
27 | // they must get updated if any of their dependent resources get updated, | ||
28 | // which would not normally be true if one of their dependencies were targeted. | ||
29 | type GraphNodeTargetDownstream interface { | ||
30 | TargetDownstream(targeted, untargeted *dag.Set) bool | ||
31 | } | ||
32 | |||
18 | // TargetsTransformer is a GraphTransformer that, when the user specifies a | 33 | // TargetsTransformer is a GraphTransformer that, when the user specifies a |
19 | // list of resources to target, limits the graph to only those resources and | 34 | // list of resources to target, limits the graph to only those resources and |
20 | // their dependencies. | 35 | // their dependencies. |
@@ -84,7 +99,10 @@ func (t *TargetsTransformer) parseTargetAddresses() ([]ResourceAddress, error) { | |||
84 | func (t *TargetsTransformer) selectTargetedNodes( | 99 | func (t *TargetsTransformer) selectTargetedNodes( |
85 | g *Graph, addrs []ResourceAddress) (*dag.Set, error) { | 100 | g *Graph, addrs []ResourceAddress) (*dag.Set, error) { |
86 | targetedNodes := new(dag.Set) | 101 | targetedNodes := new(dag.Set) |
87 | for _, v := range g.Vertices() { | 102 | |
103 | vertices := g.Vertices() | ||
104 | |||
105 | for _, v := range vertices { | ||
88 | if t.nodeIsTarget(v, addrs) { | 106 | if t.nodeIsTarget(v, addrs) { |
89 | targetedNodes.Add(v) | 107 | targetedNodes.Add(v) |
90 | 108 | ||
@@ -112,6 +130,63 @@ func (t *TargetsTransformer) selectTargetedNodes( | |||
112 | } | 130 | } |
113 | } | 131 | } |
114 | 132 | ||
133 | // Handle nodes that need to be included if their dependencies are included. | ||
134 | // This requires multiple passes since we need to catch transitive | ||
135 | // dependencies if and only if they are via other nodes that also | ||
136 | // support TargetDownstream. For example: | ||
137 | // output -> output -> targeted-resource: both outputs need to be targeted | ||
138 | // output -> non-targeted-resource -> targeted-resource: output not targeted | ||
139 | // | ||
140 | // We'll keep looping until we stop targeting more nodes. | ||
141 | queue := targetedNodes.List() | ||
142 | for len(queue) > 0 { | ||
143 | vertices := queue | ||
144 | queue = nil // ready to append for next iteration if neccessary | ||
145 | for _, v := range vertices { | ||
146 | dependers := g.UpEdges(v) | ||
147 | if dependers == nil { | ||
148 | // indicates that there are no up edges for this node, so | ||
149 | // we have nothing to do here. | ||
150 | continue | ||
151 | } | ||
152 | |||
153 | dependers = dependers.Filter(func(dv interface{}) bool { | ||
154 | // Can ignore nodes that are already targeted | ||
155 | /*if targetedNodes.Include(dv) { | ||
156 | return false | ||
157 | }*/ | ||
158 | |||
159 | _, ok := dv.(GraphNodeTargetDownstream) | ||
160 | return ok | ||
161 | }) | ||
162 | |||
163 | if dependers.Len() == 0 { | ||
164 | continue | ||
165 | } | ||
166 | |||
167 | for _, dv := range dependers.List() { | ||
168 | if targetedNodes.Include(dv) { | ||
169 | // Already present, so nothing to do | ||
170 | continue | ||
171 | } | ||
172 | |||
173 | // We'll give the node some information about what it's | ||
174 | // depending on in case that informs its decision about whether | ||
175 | // it is safe to be targeted. | ||
176 | deps := g.DownEdges(v) | ||
177 | depsTargeted := deps.Intersection(targetedNodes) | ||
178 | depsUntargeted := deps.Difference(depsTargeted) | ||
179 | |||
180 | if dv.(GraphNodeTargetDownstream).TargetDownstream(depsTargeted, depsUntargeted) { | ||
181 | targetedNodes.Add(dv) | ||
182 | // Need to visit this node on the next pass to see if it | ||
183 | // has any transitive dependers. | ||
184 | queue = append(queue, dv) | ||
185 | } | ||
186 | } | ||
187 | } | ||
188 | } | ||
189 | |||
115 | return targetedNodes, nil | 190 | return targetedNodes, nil |
116 | } | 191 | } |
117 | 192 | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/user_agent.go b/vendor/github.com/hashicorp/terraform/terraform/user_agent.go new file mode 100644 index 0000000..700be2a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/user_agent.go | |||
@@ -0,0 +1,14 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "runtime" | ||
6 | ) | ||
7 | |||
8 | // The standard Terraform User-Agent format | ||
9 | const UserAgent = "Terraform %s (%s)" | ||
10 | |||
11 | // Generate a UserAgent string | ||
12 | func UserAgentString() string { | ||
13 | return fmt.Sprintf(UserAgent, VersionString(), runtime.Version()) | ||
14 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/version.go b/vendor/github.com/hashicorp/terraform/terraform/version.go index 93fb429..cdfb8fb 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/version.go +++ b/vendor/github.com/hashicorp/terraform/terraform/version.go | |||
@@ -7,12 +7,12 @@ import ( | |||
7 | ) | 7 | ) |
8 | 8 | ||
9 | // The main version number that is being run at the moment. | 9 | // The main version number that is being run at the moment. |
10 | const Version = "0.9.5" | 10 | const Version = "0.9.8" |
11 | 11 | ||
12 | // A pre-release marker for the version. If this is "" (empty string) | 12 | // A pre-release marker for the version. If this is "" (empty string) |
13 | // then it means that it is a final release. Otherwise, this is a pre-release | 13 | // then it means that it is a final release. Otherwise, this is a pre-release |
14 | // such as "dev" (in development), "beta", "rc1", etc. | 14 | // such as "dev" (in development), "beta", "rc1", etc. |
15 | const VersionPrerelease = "" | 15 | var VersionPrerelease = "" |
16 | 16 | ||
17 | // SemVersion is an instance of version.Version. This has the secondary | 17 | // SemVersion is an instance of version.Version. This has the secondary |
18 | // benefit of verifying during tests and init time that our version is a | 18 | // benefit of verifying during tests and init time that our version is a |