aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform
diff options
context:
space:
mode:
authorAlex Pilon <apilon@hashicorp.com>2019-02-22 18:24:37 -0500
committerAlex Pilon <apilon@hashicorp.com>2019-02-22 18:24:37 -0500
commit15c0b25d011f37e7c20aeca9eaf461f78285b8d9 (patch)
tree255c250a5c9d4801c74092d33b7337d8c14438ff /vendor/github.com/hashicorp/terraform
parent07971ca38143c5faf951d152fba370ddcbe26ad5 (diff)
downloadterraform-provider-statuscake-15c0b25d011f37e7c20aeca9eaf461f78285b8d9.tar.gz
terraform-provider-statuscake-15c0b25d011f37e7c20aeca9eaf461f78285b8d9.tar.zst
terraform-provider-statuscake-15c0b25d011f37e7c20aeca9eaf461f78285b8d9.zip
deps: github.com/hashicorp/terraform@sdk-v0.11-with-go-modules
Updated via: go get github.com/hashicorp/terraform@sdk-v0.11-with-go-modules and go mod tidy
Diffstat (limited to 'vendor/github.com/hashicorp/terraform')
-rw-r--r--vendor/github.com/hashicorp/terraform/config/append.go6
-rw-r--r--vendor/github.com/hashicorp/terraform/config/config.go396
-rw-r--r--vendor/github.com/hashicorp/terraform/config/config_string.go40
-rw-r--r--vendor/github.com/hashicorp/terraform/config/configschema/decoder_spec.go97
-rw-r--r--vendor/github.com/hashicorp/terraform/config/configschema/doc.go14
-rw-r--r--vendor/github.com/hashicorp/terraform/config/configschema/implied_type.go21
-rw-r--r--vendor/github.com/hashicorp/terraform/config/configschema/internal_validate.go92
-rw-r--r--vendor/github.com/hashicorp/terraform/config/configschema/nestingmode_string.go16
-rw-r--r--vendor/github.com/hashicorp/terraform/config/configschema/schema.go107
-rw-r--r--vendor/github.com/hashicorp/terraform/config/hcl2_shim_util.go134
-rw-r--r--vendor/github.com/hashicorp/terraform/config/hcl2shim/single_attr_body.go85
-rw-r--r--vendor/github.com/hashicorp/terraform/config/hcl2shim/values.go246
-rw-r--r--vendor/github.com/hashicorp/terraform/config/import_tree.go54
-rw-r--r--vendor/github.com/hashicorp/terraform/config/interpolate.go55
-rw-r--r--vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go429
-rw-r--r--vendor/github.com/hashicorp/terraform/config/interpolate_walk.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/config/loader.go5
-rw-r--r--vendor/github.com/hashicorp/terraform/config/loader_hcl.go126
-rw-r--r--vendor/github.com/hashicorp/terraform/config/loader_hcl2.go473
-rw-r--r--vendor/github.com/hashicorp/terraform/config/merge.go11
-rw-r--r--vendor/github.com/hashicorp/terraform/config/module/get.go20
-rw-r--r--vendor/github.com/hashicorp/terraform/config/module/inode.go2
-rw-r--r--vendor/github.com/hashicorp/terraform/config/module/module.go6
-rw-r--r--vendor/github.com/hashicorp/terraform/config/module/storage.go365
-rw-r--r--vendor/github.com/hashicorp/terraform/config/module/testing.go6
-rw-r--r--vendor/github.com/hashicorp/terraform/config/module/tree.go314
-rw-r--r--vendor/github.com/hashicorp/terraform/config/module/validate_provider_alias.go2
-rw-r--r--vendor/github.com/hashicorp/terraform/config/module/versions.go95
-rw-r--r--vendor/github.com/hashicorp/terraform/config/raw_config.go125
-rw-r--r--vendor/github.com/hashicorp/terraform/config/resource_mode_string.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/config/testing.go2
-rw-r--r--vendor/github.com/hashicorp/terraform/dag/dag.go21
-rw-r--r--vendor/github.com/hashicorp/terraform/dag/marshal.go12
-rw-r--r--vendor/github.com/hashicorp/terraform/dag/walk.go16
-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.go13
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/logging/logging.go8
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/logging/transport.go21
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/id.go5
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/state.go2
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/testing.go348
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go41
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go15
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/wait.go2
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/backend.go2
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go155
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go2
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go11
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go2
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go43
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go2
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go32
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go6
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/provider.go40
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/resource.go98
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go39
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go559
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/schema.go252
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/set.go31
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/testing.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/shadow/closer.go83
-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.go87
-rw-r--r--vendor/github.com/hashicorp/terraform/httpclient/client.go18
-rw-r--r--vendor/github.com/hashicorp/terraform/httpclient/useragent.go40
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/client.go9
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/find.go27
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/get.go134
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/get_cache.go48
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/resource_provider.go39
-rw-r--r--vendor/github.com/hashicorp/terraform/registry/client.go227
-rw-r--r--vendor/github.com/hashicorp/terraform/registry/errors.go23
-rw-r--r--vendor/github.com/hashicorp/terraform/registry/regsrc/friendly_host.go140
-rw-r--r--vendor/github.com/hashicorp/terraform/registry/regsrc/module.go205
-rw-r--r--vendor/github.com/hashicorp/terraform/registry/regsrc/regsrc.go8
-rw-r--r--vendor/github.com/hashicorp/terraform/registry/response/module.go93
-rw-r--r--vendor/github.com/hashicorp/terraform/registry/response/module_list.go7
-rw-r--r--vendor/github.com/hashicorp/terraform/registry/response/module_provider.go14
-rw-r--r--vendor/github.com/hashicorp/terraform/registry/response/module_versions.go32
-rw-r--r--vendor/github.com/hashicorp/terraform/registry/response/pagination.go65
-rw-r--r--vendor/github.com/hashicorp/terraform/registry/response/redirect.go6
-rw-r--r--vendor/github.com/hashicorp/terraform/svchost/auth/cache.go45
-rw-r--r--vendor/github.com/hashicorp/terraform/svchost/auth/credentials.go63
-rw-r--r--vendor/github.com/hashicorp/terraform/svchost/auth/from_map.go18
-rw-r--r--vendor/github.com/hashicorp/terraform/svchost/auth/helper_program.go80
-rw-r--r--vendor/github.com/hashicorp/terraform/svchost/auth/static.go28
-rw-r--r--vendor/github.com/hashicorp/terraform/svchost/auth/token_credentials.go25
-rw-r--r--vendor/github.com/hashicorp/terraform/svchost/disco/disco.go259
-rw-r--r--vendor/github.com/hashicorp/terraform/svchost/disco/host.go264
-rw-r--r--vendor/github.com/hashicorp/terraform/svchost/label_iter.go69
-rw-r--r--vendor/github.com/hashicorp/terraform/svchost/svchost.go207
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/context.go189
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/context_import.go2
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/diff.go15
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/eval.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/eval_apply.go27
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/eval_context.go13
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/eval_context_builtin.go102
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/eval_context_mock.go38
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/eval_diff.go26
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/eval_interpolate.go40
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/eval_local.go86
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/eval_output.go25
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/eval_provider.go59
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/eval_state.go35
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/eval_validate.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/evaltree_provider.go41
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/features.go7
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/graph.go8
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply.go29
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/graph_builder_import.go7
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan.go23
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/graph_builder_refresh.go10
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/graph_walk_context.go3
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/graphtype_string.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/instancetype_string.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/interpolate.go84
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/module_dependencies.go1
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go11
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_local.go66
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_module_destroy.go29
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_module_removed.go77
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_module_variable.go19
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_output.go80
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_output_orphan.go5
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_provider.go2
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_provider_abstract.go24
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_provider_disabled.go6
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go15
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go55
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy.go13
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_resource_plan.go2
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go8
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_resource_refresh.go17
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/node_resource_validate.go3
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/path.go18
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/plan.go52
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/resource.go42
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/resource_address.go37
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/resource_provider.go23
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock.go20
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/schemas.go34
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/shadow.go28
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/shadow_components.go273
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/shadow_context.go158
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provider.go815
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provisioner.go282
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/state.go108
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/test_failure9
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform.go5
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_attach_config_provider.go62
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_deposed.go38
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go10
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go45
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_local.go40
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_orphan_output.go45
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_output.go46
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_provider.go574
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_provider_disable.go50
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_reference.go100
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_removed_modules.go32
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_resource_count.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_targets.go56
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/ui_output_mock.go5
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/user_agent.go11
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/version.go27
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/version_required.go12
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/walkoperation_string.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go26
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go181
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/doc.go16
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/error.go23
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/hcl.go77
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/rpc_friendly.go53
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/severity_string.go21
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/simple_warning.go25
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/source_range.go35
-rw-r--r--vendor/github.com/hashicorp/terraform/version/version.go36
183 files changed, 9142 insertions, 3772 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/append.go b/vendor/github.com/hashicorp/terraform/config/append.go
index 5f4e89e..9d80c42 100644
--- a/vendor/github.com/hashicorp/terraform/config/append.go
+++ b/vendor/github.com/hashicorp/terraform/config/append.go
@@ -82,5 +82,11 @@ func Append(c1, c2 *Config) (*Config, error) {
82 c.Variables = append(c.Variables, c2.Variables...) 82 c.Variables = append(c.Variables, c2.Variables...)
83 } 83 }
84 84
85 if len(c1.Locals) > 0 || len(c2.Locals) > 0 {
86 c.Locals = make([]*Local, 0, len(c1.Locals)+len(c2.Locals))
87 c.Locals = append(c.Locals, c1.Locals...)
88 c.Locals = append(c.Locals, c2.Locals...)
89 }
90
85 return c, nil 91 return c, nil
86} 92}
diff --git a/vendor/github.com/hashicorp/terraform/config/config.go b/vendor/github.com/hashicorp/terraform/config/config.go
index 3f756dc..1772fd7 100644
--- a/vendor/github.com/hashicorp/terraform/config/config.go
+++ b/vendor/github.com/hashicorp/terraform/config/config.go
@@ -8,11 +8,11 @@ import (
8 "strconv" 8 "strconv"
9 "strings" 9 "strings"
10 10
11 "github.com/hashicorp/go-multierror" 11 hcl2 "github.com/hashicorp/hcl2/hcl"
12 "github.com/hashicorp/hil"
13 "github.com/hashicorp/hil/ast" 12 "github.com/hashicorp/hil/ast"
14 "github.com/hashicorp/terraform/helper/hilmapstructure" 13 "github.com/hashicorp/terraform/helper/hilmapstructure"
15 "github.com/hashicorp/terraform/plugin/discovery" 14 "github.com/hashicorp/terraform/plugin/discovery"
15 "github.com/hashicorp/terraform/tfdiags"
16 "github.com/mitchellh/reflectwalk" 16 "github.com/mitchellh/reflectwalk"
17) 17)
18 18
@@ -34,6 +34,7 @@ type Config struct {
34 ProviderConfigs []*ProviderConfig 34 ProviderConfigs []*ProviderConfig
35 Resources []*Resource 35 Resources []*Resource
36 Variables []*Variable 36 Variables []*Variable
37 Locals []*Local
37 Outputs []*Output 38 Outputs []*Output
38 39
39 // The fields below can be filled in by loaders for validation 40 // The fields below can be filled in by loaders for validation
@@ -55,6 +56,8 @@ type AtlasConfig struct {
55type Module struct { 56type Module struct {
56 Name string 57 Name string
57 Source string 58 Source string
59 Version string
60 Providers map[string]string
58 RawConfig *RawConfig 61 RawConfig *RawConfig
59} 62}
60 63
@@ -147,7 +150,7 @@ func (p *Provisioner) Copy() *Provisioner {
147 } 150 }
148} 151}
149 152
150// Variable is a variable defined within the configuration. 153// Variable is a module argument defined within the configuration.
151type Variable struct { 154type Variable struct {
152 Name string 155 Name string
153 DeclaredType string `mapstructure:"type"` 156 DeclaredType string `mapstructure:"type"`
@@ -155,6 +158,12 @@ type Variable struct {
155 Description string 158 Description string
156} 159}
157 160
161// Local is a local value defined within the configuration.
162type Local struct {
163 Name string
164 RawConfig *RawConfig
165}
166
158// Output is an output defined within the configuration. An output is 167// Output is an output defined within the configuration. An output is
159// resulting data that is highlighted by Terraform when finished. An 168// resulting data that is highlighted by Terraform when finished. An
160// output marked Sensitive will be output in a masked form following 169// output marked Sensitive will be output in a masked form following
@@ -222,7 +231,10 @@ func (r *Resource) Count() (int, error) {
222 231
223 v, err := strconv.ParseInt(count, 0, 0) 232 v, err := strconv.ParseInt(count, 0, 0)
224 if err != nil { 233 if err != nil {
225 return 0, err 234 return 0, fmt.Errorf(
235 "cannot parse %q as an integer",
236 count,
237 )
226 } 238 }
227 239
228 return int(v), nil 240 return int(v), nil
@@ -253,7 +265,9 @@ func (r *Resource) ProviderFullName() string {
253// the provider name is inferred from the resource type name. 265// the provider name is inferred from the resource type name.
254func ResourceProviderFullName(resourceType, explicitProvider string) string { 266func ResourceProviderFullName(resourceType, explicitProvider string) string {
255 if explicitProvider != "" { 267 if explicitProvider != "" {
256 return explicitProvider 268 // check for an explicit provider name, or return the original
269 parts := strings.SplitAfter(explicitProvider, "provider.")
270 return parts[len(parts)-1]
257 } 271 }
258 272
259 idx := strings.IndexRune(resourceType, '_') 273 idx := strings.IndexRune(resourceType, '_')
@@ -268,30 +282,35 @@ func ResourceProviderFullName(resourceType, explicitProvider string) string {
268} 282}
269 283
270// Validate does some basic semantic checking of the configuration. 284// Validate does some basic semantic checking of the configuration.
271func (c *Config) Validate() error { 285func (c *Config) Validate() tfdiags.Diagnostics {
272 if c == nil { 286 if c == nil {
273 return nil 287 return nil
274 } 288 }
275 289
276 var errs []error 290 var diags tfdiags.Diagnostics
277 291
278 for _, k := range c.unknownKeys { 292 for _, k := range c.unknownKeys {
279 errs = append(errs, fmt.Errorf( 293 diags = diags.Append(
280 "Unknown root level key: %s", k)) 294 fmt.Errorf("Unknown root level key: %s", k),
295 )
281 } 296 }
282 297
283 // Validate the Terraform config 298 // Validate the Terraform config
284 if tf := c.Terraform; tf != nil { 299 if tf := c.Terraform; tf != nil {
285 errs = append(errs, c.Terraform.Validate()...) 300 errs := c.Terraform.Validate()
301 for _, err := range errs {
302 diags = diags.Append(err)
303 }
286 } 304 }
287 305
288 vars := c.InterpolatedVariables() 306 vars := c.InterpolatedVariables()
289 varMap := make(map[string]*Variable) 307 varMap := make(map[string]*Variable)
290 for _, v := range c.Variables { 308 for _, v := range c.Variables {
291 if _, ok := varMap[v.Name]; ok { 309 if _, ok := varMap[v.Name]; ok {
292 errs = append(errs, fmt.Errorf( 310 diags = diags.Append(fmt.Errorf(
293 "Variable '%s': duplicate found. Variable names must be unique.", 311 "Variable '%s': duplicate found. Variable names must be unique.",
294 v.Name)) 312 v.Name,
313 ))
295 } 314 }
296 315
297 varMap[v.Name] = v 316 varMap[v.Name] = v
@@ -299,17 +318,19 @@ func (c *Config) Validate() error {
299 318
300 for k, _ := range varMap { 319 for k, _ := range varMap {
301 if !NameRegexp.MatchString(k) { 320 if !NameRegexp.MatchString(k) {
302 errs = append(errs, fmt.Errorf( 321 diags = diags.Append(fmt.Errorf(
303 "variable %q: variable name must match regular expresion %s", 322 "variable %q: variable name must match regular expression %s",
304 k, NameRegexp)) 323 k, NameRegexp,
324 ))
305 } 325 }
306 } 326 }
307 327
308 for _, v := range c.Variables { 328 for _, v := range c.Variables {
309 if v.Type() == VariableTypeUnknown { 329 if v.Type() == VariableTypeUnknown {
310 errs = append(errs, fmt.Errorf( 330 diags = diags.Append(fmt.Errorf(
311 "Variable '%s': must be a string or a map", 331 "Variable '%s': must be a string or a map",
312 v.Name)) 332 v.Name,
333 ))
313 continue 334 continue
314 } 335 }
315 336
@@ -330,9 +351,10 @@ func (c *Config) Validate() error {
330 if v.Default != nil { 351 if v.Default != nil {
331 if err := reflectwalk.Walk(v.Default, w); err == nil { 352 if err := reflectwalk.Walk(v.Default, w); err == nil {
332 if interp { 353 if interp {
333 errs = append(errs, fmt.Errorf( 354 diags = diags.Append(fmt.Errorf(
334 "Variable '%s': cannot contain interpolations", 355 "variable %q: default may not contain interpolations",
335 v.Name)) 356 v.Name,
357 ))
336 } 358 }
337 } 359 }
338 } 360 }
@@ -348,10 +370,11 @@ func (c *Config) Validate() error {
348 } 370 }
349 371
350 if _, ok := varMap[uv.Name]; !ok { 372 if _, ok := varMap[uv.Name]; !ok {
351 errs = append(errs, fmt.Errorf( 373 diags = diags.Append(fmt.Errorf(
352 "%s: unknown variable referenced: '%s'. define it with 'variable' blocks", 374 "%s: unknown variable referenced: '%s'; define it with a 'variable' block",
353 source, 375 source,
354 uv.Name)) 376 uv.Name,
377 ))
355 } 378 }
356 } 379 }
357 } 380 }
@@ -362,17 +385,19 @@ func (c *Config) Validate() error {
362 switch v := rawV.(type) { 385 switch v := rawV.(type) {
363 case *CountVariable: 386 case *CountVariable:
364 if v.Type == CountValueInvalid { 387 if v.Type == CountValueInvalid {
365 errs = append(errs, fmt.Errorf( 388 diags = diags.Append(fmt.Errorf(
366 "%s: invalid count variable: %s", 389 "%s: invalid count variable: %s",
367 source, 390 source,
368 v.FullKey())) 391 v.FullKey(),
392 ))
369 } 393 }
370 case *PathVariable: 394 case *PathVariable:
371 if v.Type == PathValueInvalid { 395 if v.Type == PathValueInvalid {
372 errs = append(errs, fmt.Errorf( 396 diags = diags.Append(fmt.Errorf(
373 "%s: invalid path variable: %s", 397 "%s: invalid path variable: %s",
374 source, 398 source,
375 v.FullKey())) 399 v.FullKey(),
400 ))
376 } 401 }
377 } 402 }
378 } 403 }
@@ -380,27 +405,35 @@ func (c *Config) Validate() error {
380 405
381 // Check that providers aren't declared multiple times and that their 406 // Check that providers aren't declared multiple times and that their
382 // version constraints, where present, are syntactically valid. 407 // version constraints, where present, are syntactically valid.
383 providerSet := make(map[string]struct{}) 408 providerSet := make(map[string]bool)
384 for _, p := range c.ProviderConfigs { 409 for _, p := range c.ProviderConfigs {
385 name := p.FullName() 410 name := p.FullName()
386 if _, ok := providerSet[name]; ok { 411 if _, ok := providerSet[name]; ok {
387 errs = append(errs, fmt.Errorf( 412 diags = diags.Append(fmt.Errorf(
388 "provider.%s: declared multiple times, you can only declare a provider once", 413 "provider.%s: multiple configurations present; only one configuration is allowed per provider",
389 name)) 414 name,
415 ))
390 continue 416 continue
391 } 417 }
392 418
393 if p.Version != "" { 419 if p.Version != "" {
394 _, err := discovery.ConstraintStr(p.Version).Parse() 420 _, err := discovery.ConstraintStr(p.Version).Parse()
395 if err != nil { 421 if err != nil {
396 errs = append(errs, fmt.Errorf( 422 diags = diags.Append(&hcl2.Diagnostic{
397 "provider.%s: invalid version constraint %q: %s", 423 Severity: hcl2.DiagError,
398 name, p.Version, err, 424 Summary: "Invalid provider version constraint",
399 )) 425 Detail: fmt.Sprintf(
426 "The value %q given for provider.%s is not a valid version constraint.",
427 p.Version, name,
428 ),
429 // TODO: include a "Subject" source reference in here,
430 // once the config loader is able to retain source
431 // location information.
432 })
400 } 433 }
401 } 434 }
402 435
403 providerSet[name] = struct{}{} 436 providerSet[name] = true
404 } 437 }
405 438
406 // Check that all references to modules are valid 439 // Check that all references to modules are valid
@@ -412,9 +445,10 @@ func (c *Config) Validate() error {
412 if _, ok := dupped[m.Id()]; !ok { 445 if _, ok := dupped[m.Id()]; !ok {
413 dupped[m.Id()] = struct{}{} 446 dupped[m.Id()] = struct{}{}
414 447
415 errs = append(errs, fmt.Errorf( 448 diags = diags.Append(fmt.Errorf(
416 "%s: module repeated multiple times", 449 "module %q: module repeated multiple times",
417 m.Id())) 450 m.Id(),
451 ))
418 } 452 }
419 453
420 // Already seen this module, just skip it 454 // Already seen this module, just skip it
@@ -428,21 +462,23 @@ func (c *Config) Validate() error {
428 "root": m.Source, 462 "root": m.Source,
429 }) 463 })
430 if err != nil { 464 if err != nil {
431 errs = append(errs, fmt.Errorf( 465 diags = diags.Append(fmt.Errorf(
432 "%s: module source error: %s", 466 "module %q: module source error: %s",
433 m.Id(), err)) 467 m.Id(), err,
468 ))
434 } else if len(rc.Interpolations) > 0 { 469 } else if len(rc.Interpolations) > 0 {
435 errs = append(errs, fmt.Errorf( 470 diags = diags.Append(fmt.Errorf(
436 "%s: module source cannot contain interpolations", 471 "module %q: module source cannot contain interpolations",
437 m.Id())) 472 m.Id(),
473 ))
438 } 474 }
439 475
440 // Check that the name matches our regexp 476 // Check that the name matches our regexp
441 if !NameRegexp.Match([]byte(m.Name)) { 477 if !NameRegexp.Match([]byte(m.Name)) {
442 errs = append(errs, fmt.Errorf( 478 diags = diags.Append(fmt.Errorf(
443 "%s: module name can only contain letters, numbers, "+ 479 "module %q: module name must be a letter or underscore followed by only letters, numbers, dashes, and underscores",
444 "dashes, and underscores", 480 m.Id(),
445 m.Id())) 481 ))
446 } 482 }
447 483
448 // Check that the configuration can all be strings, lists or maps 484 // Check that the configuration can all be strings, lists or maps
@@ -466,30 +502,47 @@ func (c *Config) Validate() error {
466 continue 502 continue
467 } 503 }
468 504
469 errs = append(errs, fmt.Errorf( 505 diags = diags.Append(fmt.Errorf(
470 "%s: variable %s must be a string, list or map value", 506 "module %q: argument %s must have a string, list, or map value",
471 m.Id(), k)) 507 m.Id(), k,
508 ))
472 } 509 }
473 510
474 // Check for invalid count variables 511 // Check for invalid count variables
475 for _, v := range m.RawConfig.Variables { 512 for _, v := range m.RawConfig.Variables {
476 switch v.(type) { 513 switch v.(type) {
477 case *CountVariable: 514 case *CountVariable:
478 errs = append(errs, fmt.Errorf( 515 diags = diags.Append(fmt.Errorf(
479 "%s: count variables are only valid within resources", m.Name)) 516 "module %q: count variables are only valid within resources",
517 m.Name,
518 ))
480 case *SelfVariable: 519 case *SelfVariable:
481 errs = append(errs, fmt.Errorf( 520 diags = diags.Append(fmt.Errorf(
482 "%s: self variables are only valid within resources", m.Name)) 521 "module %q: self variables are only valid within resources",
522 m.Name,
523 ))
483 } 524 }
484 } 525 }
485 526
486 // Update the raw configuration to only contain the string values 527 // Update the raw configuration to only contain the string values
487 m.RawConfig, err = NewRawConfig(raw) 528 m.RawConfig, err = NewRawConfig(raw)
488 if err != nil { 529 if err != nil {
489 errs = append(errs, fmt.Errorf( 530 diags = diags.Append(fmt.Errorf(
490 "%s: can't initialize configuration: %s", 531 "%s: can't initialize configuration: %s",
491 m.Id(), err)) 532 m.Id(), err,
533 ))
492 } 534 }
535
536 // check that all named providers actually exist
537 for _, p := range m.Providers {
538 if !providerSet[p] {
539 diags = diags.Append(fmt.Errorf(
540 "module %q: cannot pass non-existent provider %q",
541 m.Name, p,
542 ))
543 }
544 }
545
493 } 546 }
494 dupped = nil 547 dupped = nil
495 548
@@ -503,10 +556,10 @@ func (c *Config) Validate() error {
503 } 556 }
504 557
505 if _, ok := modules[mv.Name]; !ok { 558 if _, ok := modules[mv.Name]; !ok {
506 errs = append(errs, fmt.Errorf( 559 diags = diags.Append(fmt.Errorf(
507 "%s: unknown module referenced: %s", 560 "%s: unknown module referenced: %s",
508 source, 561 source, mv.Name,
509 mv.Name)) 562 ))
510 } 563 }
511 } 564 }
512 } 565 }
@@ -519,9 +572,10 @@ func (c *Config) Validate() error {
519 if _, ok := dupped[r.Id()]; !ok { 572 if _, ok := dupped[r.Id()]; !ok {
520 dupped[r.Id()] = struct{}{} 573 dupped[r.Id()] = struct{}{}
521 574
522 errs = append(errs, fmt.Errorf( 575 diags = diags.Append(fmt.Errorf(
523 "%s: resource repeated multiple times", 576 "%s: resource repeated multiple times",
524 r.Id())) 577 r.Id(),
578 ))
525 } 579 }
526 } 580 }
527 581
@@ -535,53 +589,42 @@ func (c *Config) Validate() error {
535 for _, v := range r.RawCount.Variables { 589 for _, v := range r.RawCount.Variables {
536 switch v.(type) { 590 switch v.(type) {
537 case *CountVariable: 591 case *CountVariable:
538 errs = append(errs, fmt.Errorf( 592 diags = diags.Append(fmt.Errorf(
539 "%s: resource count can't reference count variable: %s", 593 "%s: resource count can't reference count variable: %s",
540 n, 594 n, v.FullKey(),
541 v.FullKey())) 595 ))
542 case *SimpleVariable: 596 case *SimpleVariable:
543 errs = append(errs, fmt.Errorf( 597 diags = diags.Append(fmt.Errorf(
544 "%s: resource count can't reference variable: %s", 598 "%s: resource count can't reference variable: %s",
545 n, 599 n, v.FullKey(),
546 v.FullKey())) 600 ))
547 601
548 // Good 602 // Good
549 case *ModuleVariable: 603 case *ModuleVariable:
550 case *ResourceVariable: 604 case *ResourceVariable:
551 case *TerraformVariable: 605 case *TerraformVariable:
552 case *UserVariable: 606 case *UserVariable:
607 case *LocalVariable:
553 608
554 default: 609 default:
555 errs = append(errs, fmt.Errorf( 610 diags = diags.Append(fmt.Errorf(
556 "Internal error. Unknown type in count var in %s: %T", 611 "Internal error. Unknown type in count var in %s: %T",
557 n, v)) 612 n, v,
613 ))
558 } 614 }
559 } 615 }
560 616
561 // Interpolate with a fixed number to verify that its a number. 617 if !r.RawCount.couldBeInteger() {
562 r.RawCount.interpolate(func(root ast.Node) (interface{}, error) { 618 diags = diags.Append(fmt.Errorf(
563 // Execute the node but transform the AST so that it returns 619 "%s: resource count must be an integer", n,
564 // a fixed value of "5" for all interpolations. 620 ))
565 result, err := hil.Eval(
566 hil.FixedValueTransform(
567 root, &ast.LiteralNode{Value: "5", Typex: ast.TypeString}),
568 nil)
569 if err != nil {
570 return "", err
571 }
572
573 return result.Value, nil
574 })
575 _, err := strconv.ParseInt(r.RawCount.Value().(string), 0, 0)
576 if err != nil {
577 errs = append(errs, fmt.Errorf(
578 "%s: resource count must be an integer",
579 n))
580 } 621 }
581 r.RawCount.init() 622 r.RawCount.init()
582 623
583 // Validate DependsOn 624 // Validate DependsOn
584 errs = append(errs, c.validateDependsOn(n, r.DependsOn, resources, modules)...) 625 for _, err := range c.validateDependsOn(n, r.DependsOn, resources, modules) {
626 diags = diags.Append(err)
627 }
585 628
586 // Verify provisioners 629 // Verify provisioners
587 for _, p := range r.Provisioners { 630 for _, p := range r.Provisioners {
@@ -595,9 +638,10 @@ func (c *Config) Validate() error {
595 } 638 }
596 639
597 if rv.Multi && rv.Index == -1 && rv.Type == r.Type && rv.Name == r.Name { 640 if rv.Multi && rv.Index == -1 && rv.Type == r.Type && rv.Name == r.Name {
598 errs = append(errs, fmt.Errorf( 641 diags = diags.Append(fmt.Errorf(
599 "%s: connection info cannot contain splat variable "+ 642 "%s: connection info cannot contain splat variable referencing itself",
600 "referencing itself", n)) 643 n,
644 ))
601 break 645 break
602 } 646 }
603 } 647 }
@@ -609,9 +653,10 @@ func (c *Config) Validate() error {
609 } 653 }
610 654
611 if rv.Multi && rv.Index == -1 && rv.Type == r.Type && rv.Name == r.Name { 655 if rv.Multi && rv.Index == -1 && rv.Type == r.Type && rv.Name == r.Name {
612 errs = append(errs, fmt.Errorf( 656 diags = diags.Append(fmt.Errorf(
613 "%s: connection info cannot contain splat variable "+ 657 "%s: connection info cannot contain splat variable referencing itself",
614 "referencing itself", n)) 658 n,
659 ))
615 break 660 break
616 } 661 }
617 } 662 }
@@ -619,21 +664,24 @@ func (c *Config) Validate() error {
619 // Check for invalid when/onFailure values, though this should be 664 // Check for invalid when/onFailure values, though this should be
620 // picked up by the loader we check here just in case. 665 // picked up by the loader we check here just in case.
621 if p.When == ProvisionerWhenInvalid { 666 if p.When == ProvisionerWhenInvalid {
622 errs = append(errs, fmt.Errorf( 667 diags = diags.Append(fmt.Errorf(
623 "%s: provisioner 'when' value is invalid", n)) 668 "%s: provisioner 'when' value is invalid", n,
669 ))
624 } 670 }
625 if p.OnFailure == ProvisionerOnFailureInvalid { 671 if p.OnFailure == ProvisionerOnFailureInvalid {
626 errs = append(errs, fmt.Errorf( 672 diags = diags.Append(fmt.Errorf(
627 "%s: provisioner 'on_failure' value is invalid", n)) 673 "%s: provisioner 'on_failure' value is invalid", n,
674 ))
628 } 675 }
629 } 676 }
630 677
631 // Verify ignore_changes contains valid entries 678 // Verify ignore_changes contains valid entries
632 for _, v := range r.Lifecycle.IgnoreChanges { 679 for _, v := range r.Lifecycle.IgnoreChanges {
633 if strings.Contains(v, "*") && v != "*" { 680 if strings.Contains(v, "*") && v != "*" {
634 errs = append(errs, fmt.Errorf( 681 diags = diags.Append(fmt.Errorf(
635 "%s: ignore_changes does not support using a partial string "+ 682 "%s: ignore_changes does not support using a partial string together with a wildcard: %s",
636 "together with a wildcard: %s", n, v)) 683 n, v,
684 ))
637 } 685 }
638 } 686 }
639 687
@@ -642,21 +690,24 @@ func (c *Config) Validate() error {
642 "root": r.Lifecycle.IgnoreChanges, 690 "root": r.Lifecycle.IgnoreChanges,
643 }) 691 })
644 if err != nil { 692 if err != nil {
645 errs = append(errs, fmt.Errorf( 693 diags = diags.Append(fmt.Errorf(
646 "%s: lifecycle ignore_changes error: %s", 694 "%s: lifecycle ignore_changes error: %s",
647 n, err)) 695 n, err,
696 ))
648 } else if len(rc.Interpolations) > 0 { 697 } else if len(rc.Interpolations) > 0 {
649 errs = append(errs, fmt.Errorf( 698 diags = diags.Append(fmt.Errorf(
650 "%s: lifecycle ignore_changes cannot contain interpolations", 699 "%s: lifecycle ignore_changes cannot contain interpolations",
651 n)) 700 n,
701 ))
652 } 702 }
653 703
654 // If it is a data source then it can't have provisioners 704 // If it is a data source then it can't have provisioners
655 if r.Mode == DataResourceMode { 705 if r.Mode == DataResourceMode {
656 if _, ok := r.RawConfig.Raw["provisioner"]; ok { 706 if _, ok := r.RawConfig.Raw["provisioner"]; ok {
657 errs = append(errs, fmt.Errorf( 707 diags = diags.Append(fmt.Errorf(
658 "%s: data sources cannot have provisioners", 708 "%s: data sources cannot have provisioners",
659 n)) 709 n,
710 ))
660 } 711 }
661 } 712 }
662 } 713 }
@@ -670,25 +721,50 @@ func (c *Config) Validate() error {
670 721
671 id := rv.ResourceId() 722 id := rv.ResourceId()
672 if _, ok := resources[id]; !ok { 723 if _, ok := resources[id]; !ok {
673 errs = append(errs, fmt.Errorf( 724 diags = diags.Append(fmt.Errorf(
674 "%s: unknown resource '%s' referenced in variable %s", 725 "%s: unknown resource '%s' referenced in variable %s",
675 source, 726 source,
676 id, 727 id,
677 rv.FullKey())) 728 rv.FullKey(),
729 ))
678 continue 730 continue
679 } 731 }
680 } 732 }
681 } 733 }
682 734
735 // Check that all locals are valid
736 {
737 found := make(map[string]struct{})
738 for _, l := range c.Locals {
739 if _, ok := found[l.Name]; ok {
740 diags = diags.Append(fmt.Errorf(
741 "%s: duplicate local. local value names must be unique",
742 l.Name,
743 ))
744 continue
745 }
746 found[l.Name] = struct{}{}
747
748 for _, v := range l.RawConfig.Variables {
749 if _, ok := v.(*CountVariable); ok {
750 diags = diags.Append(fmt.Errorf(
751 "local %s: count variables are only valid within resources", l.Name,
752 ))
753 }
754 }
755 }
756 }
757
683 // Check that all outputs are valid 758 // Check that all outputs are valid
684 { 759 {
685 found := make(map[string]struct{}) 760 found := make(map[string]struct{})
686 for _, o := range c.Outputs { 761 for _, o := range c.Outputs {
687 // Verify the output is new 762 // Verify the output is new
688 if _, ok := found[o.Name]; ok { 763 if _, ok := found[o.Name]; ok {
689 errs = append(errs, fmt.Errorf( 764 diags = diags.Append(fmt.Errorf(
690 "%s: duplicate output. output names must be unique.", 765 "output %q: an output of this name was already defined",
691 o.Name)) 766 o.Name,
767 ))
692 continue 768 continue
693 } 769 }
694 found[o.Name] = struct{}{} 770 found[o.Name] = struct{}{}
@@ -708,9 +784,10 @@ func (c *Config) Validate() error {
708 continue 784 continue
709 } 785 }
710 786
711 errs = append(errs, fmt.Errorf( 787 diags = diags.Append(fmt.Errorf(
712 "%s: value for 'sensitive' must be boolean", 788 "output %q: value for 'sensitive' must be boolean",
713 o.Name)) 789 o.Name,
790 ))
714 continue 791 continue
715 } 792 }
716 if k == "description" { 793 if k == "description" {
@@ -719,27 +796,78 @@ func (c *Config) Validate() error {
719 continue 796 continue
720 } 797 }
721 798
722 errs = append(errs, fmt.Errorf( 799 diags = diags.Append(fmt.Errorf(
723 "%s: value for 'description' must be string", 800 "output %q: value for 'description' must be string",
724 o.Name)) 801 o.Name,
802 ))
725 continue 803 continue
726 } 804 }
727 invalidKeys = append(invalidKeys, k) 805 invalidKeys = append(invalidKeys, k)
728 } 806 }
729 if len(invalidKeys) > 0 { 807 if len(invalidKeys) > 0 {
730 errs = append(errs, fmt.Errorf( 808 diags = diags.Append(fmt.Errorf(
731 "%s: output has invalid keys: %s", 809 "output %q: invalid keys: %s",
732 o.Name, strings.Join(invalidKeys, ", "))) 810 o.Name, strings.Join(invalidKeys, ", "),
811 ))
733 } 812 }
734 if !valueKeyFound { 813 if !valueKeyFound {
735 errs = append(errs, fmt.Errorf( 814 diags = diags.Append(fmt.Errorf(
736 "%s: output is missing required 'value' key", o.Name)) 815 "output %q: missing required 'value' argument", o.Name,
816 ))
737 } 817 }
738 818
739 for _, v := range o.RawConfig.Variables { 819 for _, v := range o.RawConfig.Variables {
740 if _, ok := v.(*CountVariable); ok { 820 if _, ok := v.(*CountVariable); ok {
741 errs = append(errs, fmt.Errorf( 821 diags = diags.Append(fmt.Errorf(
742 "%s: count variables are only valid within resources", o.Name)) 822 "output %q: count variables are only valid within resources",
823 o.Name,
824 ))
825 }
826 }
827
828 // Detect a common mistake of using a "count"ed resource in
829 // an output value without using the splat or index form.
830 // Prior to 0.11 this error was silently ignored, but outputs
831 // now have their errors checked like all other contexts.
832 //
833 // TODO: Remove this in 0.12.
834 for _, v := range o.RawConfig.Variables {
835 rv, ok := v.(*ResourceVariable)
836 if !ok {
837 continue
838 }
839
840 // If the variable seems to be treating the referenced
841 // resource as a singleton (no count specified) then
842 // we'll check to make sure it is indeed a singleton.
843 // It's a warning if not.
844
845 if rv.Multi || rv.Index != 0 {
846 // This reference is treating the resource as a
847 // multi-resource, so the warning doesn't apply.
848 continue
849 }
850
851 for _, r := range c.Resources {
852 if r.Id() != rv.ResourceId() {
853 continue
854 }
855
856 // We test specifically for the raw string "1" here
857 // because we _do_ want to generate this warning if
858 // the user has provided an expression that happens
859 // to return 1 right now, to catch situations where
860 // a count might dynamically be set to something
861 // other than 1 and thus splat syntax is still needed
862 // to be safe.
863 if r.RawCount != nil && r.RawCount.Raw != nil && r.RawCount.Raw["count"] != "1" && rv.Field != "count" {
864 diags = diags.Append(tfdiags.SimpleWarning(fmt.Sprintf(
865 "output %q: must use splat syntax to access %s attribute %q, because it has \"count\" set; use %s.*.%s to obtain a list of the attributes across all instances",
866 o.Name,
867 r.Id(), rv.Field,
868 r.Id(), rv.Field,
869 )))
870 }
743 } 871 }
744 } 872 }
745 } 873 }
@@ -755,17 +883,15 @@ func (c *Config) Validate() error {
755 883
756 for _, v := range rc.Variables { 884 for _, v := range rc.Variables {
757 if _, ok := v.(*SelfVariable); ok { 885 if _, ok := v.(*SelfVariable); ok {
758 errs = append(errs, fmt.Errorf( 886 diags = diags.Append(fmt.Errorf(
759 "%s: cannot contain self-reference %s", source, v.FullKey())) 887 "%s: cannot contain self-reference %s",
888 source, v.FullKey(),
889 ))
760 } 890 }
761 } 891 }
762 } 892 }
763 893
764 if len(errs) > 0 { 894 return diags
765 return &multierror.Error{Errors: errs}
766 }
767
768 return nil
769} 895}
770 896
771// InterpolatedVariables is a helper that returns a mapping of all the interpolated 897// InterpolatedVariables is a helper that returns a mapping of all the interpolated
diff --git a/vendor/github.com/hashicorp/terraform/config/config_string.go b/vendor/github.com/hashicorp/terraform/config/config_string.go
index 0b3abbc..a6933c2 100644
--- a/vendor/github.com/hashicorp/terraform/config/config_string.go
+++ b/vendor/github.com/hashicorp/terraform/config/config_string.go
@@ -143,6 +143,46 @@ func outputsStr(os []*Output) string {
143 result += fmt.Sprintf(" %s: %s\n", kind, str) 143 result += fmt.Sprintf(" %s: %s\n", kind, str)
144 } 144 }
145 } 145 }
146
147 if o.Description != "" {
148 result += fmt.Sprintf(" description\n %s\n", o.Description)
149 }
150 }
151
152 return strings.TrimSpace(result)
153}
154
155func localsStr(ls []*Local) string {
156 ns := make([]string, 0, len(ls))
157 m := make(map[string]*Local)
158 for _, l := range ls {
159 ns = append(ns, l.Name)
160 m[l.Name] = l
161 }
162 sort.Strings(ns)
163
164 result := ""
165 for _, n := range ns {
166 l := m[n]
167
168 result += fmt.Sprintf("%s\n", n)
169
170 if len(l.RawConfig.Variables) > 0 {
171 result += fmt.Sprintf(" vars\n")
172 for _, rawV := range l.RawConfig.Variables {
173 kind := "unknown"
174 str := rawV.FullKey()
175
176 switch rawV.(type) {
177 case *ResourceVariable:
178 kind = "resource"
179 case *UserVariable:
180 kind = "user"
181 }
182
183 result += fmt.Sprintf(" %s: %s\n", kind, str)
184 }
185 }
146 } 186 }
147 187
148 return strings.TrimSpace(result) 188 return strings.TrimSpace(result)
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/decoder_spec.go b/vendor/github.com/hashicorp/terraform/config/configschema/decoder_spec.go
new file mode 100644
index 0000000..2b1b0ca
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/configschema/decoder_spec.go
@@ -0,0 +1,97 @@
1package configschema
2
3import (
4 "github.com/hashicorp/hcl2/hcldec"
5 "github.com/zclconf/go-cty/cty"
6)
7
8var mapLabelNames = []string{"key"}
9
10// DecoderSpec returns a hcldec.Spec that can be used to decode a HCL Body
11// using the facilities in the hcldec package.
12//
13// The returned specification is guaranteed to return a value of the same type
14// returned by method ImpliedType, but it may contain null or unknown values if
15// any of the block attributes are defined as optional and/or computed
16// respectively.
17func (b *Block) DecoderSpec() hcldec.Spec {
18 ret := hcldec.ObjectSpec{}
19 if b == nil {
20 return ret
21 }
22
23 for name, attrS := range b.Attributes {
24 switch {
25 case attrS.Computed && attrS.Optional:
26 // In this special case we use an unknown value as a default
27 // to get the intended behavior that the result is computed
28 // unless it has been explicitly set in config.
29 ret[name] = &hcldec.DefaultSpec{
30 Primary: &hcldec.AttrSpec{
31 Name: name,
32 Type: attrS.Type,
33 },
34 Default: &hcldec.LiteralSpec{
35 Value: cty.UnknownVal(attrS.Type),
36 },
37 }
38 case attrS.Computed:
39 ret[name] = &hcldec.LiteralSpec{
40 Value: cty.UnknownVal(attrS.Type),
41 }
42 default:
43 ret[name] = &hcldec.AttrSpec{
44 Name: name,
45 Type: attrS.Type,
46 Required: attrS.Required,
47 }
48 }
49 }
50
51 for name, blockS := range b.BlockTypes {
52 if _, exists := ret[name]; exists {
53 // This indicates an invalid schema, since it's not valid to
54 // define both an attribute and a block type of the same name.
55 // However, we don't raise this here since it's checked by
56 // InternalValidate.
57 continue
58 }
59
60 childSpec := blockS.Block.DecoderSpec()
61
62 switch blockS.Nesting {
63 case NestingSingle:
64 ret[name] = &hcldec.BlockSpec{
65 TypeName: name,
66 Nested: childSpec,
67 Required: blockS.MinItems == 1 && blockS.MaxItems >= 1,
68 }
69 case NestingList:
70 ret[name] = &hcldec.BlockListSpec{
71 TypeName: name,
72 Nested: childSpec,
73 MinItems: blockS.MinItems,
74 MaxItems: blockS.MaxItems,
75 }
76 case NestingSet:
77 ret[name] = &hcldec.BlockSetSpec{
78 TypeName: name,
79 Nested: childSpec,
80 MinItems: blockS.MinItems,
81 MaxItems: blockS.MaxItems,
82 }
83 case NestingMap:
84 ret[name] = &hcldec.BlockMapSpec{
85 TypeName: name,
86 Nested: childSpec,
87 LabelNames: mapLabelNames,
88 }
89 default:
90 // Invalid nesting type is just ignored. It's checked by
91 // InternalValidate.
92 continue
93 }
94 }
95
96 return ret
97}
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/doc.go b/vendor/github.com/hashicorp/terraform/config/configschema/doc.go
new file mode 100644
index 0000000..caf8d73
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/configschema/doc.go
@@ -0,0 +1,14 @@
1// Package configschema contains types for describing the expected structure
2// of a configuration block whose shape is not known until runtime.
3//
4// For example, this is used to describe the expected contents of a resource
5// configuration block, which is defined by the corresponding provider plugin
6// and thus not compiled into Terraform core.
7//
8// A configschema primarily describes the shape of configuration, but it is
9// also suitable for use with other structures derived from the configuration,
10// such as the cached state of a resource or a resource diff.
11//
12// This package should not be confused with the package helper/schema, which
13// is the higher-level helper library used to implement providers themselves.
14package configschema
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/implied_type.go b/vendor/github.com/hashicorp/terraform/config/configschema/implied_type.go
new file mode 100644
index 0000000..67324eb
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/configschema/implied_type.go
@@ -0,0 +1,21 @@
1package configschema
2
3import (
4 "github.com/hashicorp/hcl2/hcldec"
5 "github.com/zclconf/go-cty/cty"
6)
7
8// ImpliedType returns the cty.Type that would result from decoding a
9// configuration block using the receiving block schema.
10//
11// ImpliedType always returns a result, even if the given schema is
12// inconsistent. Code that creates configschema.Block objects should be
13// tested using the InternalValidate method to detect any inconsistencies
14// that would cause this method to fall back on defaults and assumptions.
15func (b *Block) ImpliedType() cty.Type {
16 if b == nil {
17 return cty.EmptyObject
18 }
19
20 return hcldec.ImpliedType(b.DecoderSpec())
21}
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/internal_validate.go b/vendor/github.com/hashicorp/terraform/config/configschema/internal_validate.go
new file mode 100644
index 0000000..33cbe88
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/configschema/internal_validate.go
@@ -0,0 +1,92 @@
1package configschema
2
3import (
4 "fmt"
5 "regexp"
6
7 "github.com/zclconf/go-cty/cty"
8
9 multierror "github.com/hashicorp/go-multierror"
10)
11
12var validName = regexp.MustCompile(`^[a-z0-9_]+$`)
13
14// InternalValidate returns an error if the receiving block and its child
15// schema definitions have any consistencies with the documented rules for
16// valid schema.
17//
18// This is intended to be used within unit tests to detect when a given
19// schema is invalid.
20func (b *Block) InternalValidate() error {
21 if b == nil {
22 return fmt.Errorf("top-level block schema is nil")
23 }
24 return b.internalValidate("", nil)
25
26}
27
28func (b *Block) internalValidate(prefix string, err error) error {
29 for name, attrS := range b.Attributes {
30 if attrS == nil {
31 err = multierror.Append(err, fmt.Errorf("%s%s: attribute schema is nil", prefix, name))
32 continue
33 }
34 if !validName.MatchString(name) {
35 err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name))
36 }
37 if attrS.Optional == false && attrS.Required == false && attrS.Computed == false {
38 err = multierror.Append(err, fmt.Errorf("%s%s: must set Optional, Required or Computed", prefix, name))
39 }
40 if attrS.Optional && attrS.Required {
41 err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Optional and Required", prefix, name))
42 }
43 if attrS.Computed && attrS.Required {
44 err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Computed and Required", prefix, name))
45 }
46 if attrS.Type == cty.NilType {
47 err = multierror.Append(err, fmt.Errorf("%s%s: Type must be set to something other than cty.NilType", prefix, name))
48 }
49 }
50
51 for name, blockS := range b.BlockTypes {
52 if blockS == nil {
53 err = multierror.Append(err, fmt.Errorf("%s%s: block schema is nil", prefix, name))
54 continue
55 }
56
57 if _, isAttr := b.Attributes[name]; isAttr {
58 err = multierror.Append(err, fmt.Errorf("%s%s: name defined as both attribute and child block type", prefix, name))
59 } else if !validName.MatchString(name) {
60 err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name))
61 }
62
63 if blockS.MinItems < 0 || blockS.MaxItems < 0 {
64 err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must both be greater than zero", prefix, name))
65 }
66
67 switch blockS.Nesting {
68 case NestingSingle:
69 switch {
70 case blockS.MinItems != blockS.MaxItems:
71 err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must match in NestingSingle mode", prefix, name))
72 case blockS.MinItems < 0 || blockS.MinItems > 1:
73 err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must be set to either 0 or 1 in NestingSingle mode", prefix, name))
74 }
75 case NestingList, NestingSet:
76 if blockS.MinItems > blockS.MaxItems && blockS.MaxItems != 0 {
77 err = multierror.Append(err, fmt.Errorf("%s%s: MinItems must be less than or equal to MaxItems in %s mode", prefix, name, blockS.Nesting))
78 }
79 case NestingMap:
80 if blockS.MinItems != 0 || blockS.MaxItems != 0 {
81 err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must both be 0 in NestingMap mode", prefix, name))
82 }
83 default:
84 err = multierror.Append(err, fmt.Errorf("%s%s: invalid nesting mode %s", prefix, name, blockS.Nesting))
85 }
86
87 subPrefix := prefix + name + "."
88 err = blockS.Block.internalValidate(subPrefix, err)
89 }
90
91 return err
92}
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/nestingmode_string.go b/vendor/github.com/hashicorp/terraform/config/configschema/nestingmode_string.go
new file mode 100644
index 0000000..6cb9313
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/configschema/nestingmode_string.go
@@ -0,0 +1,16 @@
1// Code generated by "stringer -type=NestingMode"; DO NOT EDIT.
2
3package configschema
4
5import "strconv"
6
7const _NestingMode_name = "nestingModeInvalidNestingSingleNestingListNestingSetNestingMap"
8
9var _NestingMode_index = [...]uint8{0, 18, 31, 42, 52, 62}
10
11func (i NestingMode) String() string {
12 if i < 0 || i >= NestingMode(len(_NestingMode_index)-1) {
13 return "NestingMode(" + strconv.FormatInt(int64(i), 10) + ")"
14 }
15 return _NestingMode_name[_NestingMode_index[i]:_NestingMode_index[i+1]]
16}
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/schema.go b/vendor/github.com/hashicorp/terraform/config/configschema/schema.go
new file mode 100644
index 0000000..9a8ee55
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/configschema/schema.go
@@ -0,0 +1,107 @@
1package configschema
2
3import (
4 "github.com/zclconf/go-cty/cty"
5)
6
7// Block represents a configuration block.
8//
9// "Block" here is a logical grouping construct, though it happens to map
10// directly onto the physical block syntax of Terraform's native configuration
11// syntax. It may be a more a matter of convention in other syntaxes, such as
12// JSON.
13//
14// When converted to a value, a Block always becomes an instance of an object
15// type derived from its defined attributes and nested blocks
16type Block struct {
17 // Attributes describes any attributes that may appear directly inside
18 // the block.
19 Attributes map[string]*Attribute
20
21 // BlockTypes describes any nested block types that may appear directly
22 // inside the block.
23 BlockTypes map[string]*NestedBlock
24}
25
26// Attribute represents a configuration attribute, within a block.
27type Attribute struct {
28 // Type is a type specification that the attribute's value must conform to.
29 Type cty.Type
30
31 // Required, if set to true, specifies that an omitted or null value is
32 // not permitted.
33 Required bool
34
35 // Optional, if set to true, specifies that an omitted or null value is
36 // permitted. This field conflicts with Required.
37 Optional bool
38
39 // Computed, if set to true, specifies that the value comes from the
40 // provider rather than from configuration. If combined with Optional,
41 // then the config may optionally provide an overridden value.
42 Computed bool
43
44 // Sensitive, if set to true, indicates that an attribute may contain
45 // sensitive information.
46 //
47 // At present nothing is done with this information, but callers are
48 // encouraged to set it where appropriate so that it may be used in the
49 // future to help Terraform mask sensitive information. (Terraform
50 // currently achieves this in a limited sense via other mechanisms.)
51 Sensitive bool
52}
53
54// NestedBlock represents the embedding of one block within another.
55type NestedBlock struct {
56 // Block is the description of the block that's nested.
57 Block
58
59 // Nesting provides the nesting mode for the child block, which determines
60 // how many instances of the block are allowed, how many labels it expects,
61 // and how the resulting data will be converted into a data structure.
62 Nesting NestingMode
63
64 // MinItems and MaxItems set, for the NestingList and NestingSet nesting
65 // modes, lower and upper limits on the number of child blocks allowed
66 // of the given type. If both are left at zero, no limit is applied.
67 //
68 // As a special case, both values can be set to 1 for NestingSingle in
69 // order to indicate that a particular single block is required.
70 //
71 // These fields are ignored for other nesting modes and must both be left
72 // at zero.
73 MinItems, MaxItems int
74}
75
76// NestingMode is an enumeration of modes for nesting blocks inside other
77// blocks.
78type NestingMode int
79
80//go:generate stringer -type=NestingMode
81
82const (
83 nestingModeInvalid NestingMode = iota
84
85 // NestingSingle indicates that only a single instance of a given
86 // block type is permitted, with no labels, and its content should be
87 // provided directly as an object value.
88 NestingSingle
89
90 // NestingList indicates that multiple blocks of the given type are
91 // permitted, with no labels, and that their corresponding objects should
92 // be provided in a list.
93 NestingList
94
95 // NestingSet indicates that multiple blocks of the given type are
96 // permitted, with no labels, and that their corresponding objects should
97 // be provided in a set.
98 NestingSet
99
100 // NestingMap indicates that multiple blocks of the given type are
101 // permitted, each with a single label, and that their corresponding
102 // objects should be provided in a map whose keys are the labels.
103 //
104 // It's an error, therefore, to use the same label value on multiple
105 // blocks.
106 NestingMap
107)
diff --git a/vendor/github.com/hashicorp/terraform/config/hcl2_shim_util.go b/vendor/github.com/hashicorp/terraform/config/hcl2_shim_util.go
new file mode 100644
index 0000000..207d105
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/hcl2_shim_util.go
@@ -0,0 +1,134 @@
1package config
2
3import (
4 "fmt"
5
6 "github.com/zclconf/go-cty/cty/function/stdlib"
7
8 "github.com/hashicorp/hil/ast"
9 "github.com/hashicorp/terraform/config/hcl2shim"
10
11 hcl2 "github.com/hashicorp/hcl2/hcl"
12 "github.com/zclconf/go-cty/cty"
13 "github.com/zclconf/go-cty/cty/convert"
14 "github.com/zclconf/go-cty/cty/function"
15)
16
17// ---------------------------------------------------------------------------
18// This file contains some helper functions that are used to shim between
19// HCL2 concepts and HCL/HIL concepts, to help us mostly preserve the existing
20// public API that was built around HCL/HIL-oriented approaches.
21// ---------------------------------------------------------------------------
22
23func hcl2InterpolationFuncs() map[string]function.Function {
24 hcl2Funcs := map[string]function.Function{}
25
26 for name, hilFunc := range Funcs() {
27 hcl2Funcs[name] = hcl2InterpolationFuncShim(hilFunc)
28 }
29
30 // Some functions in the old world are dealt with inside langEvalConfig
31 // due to their legacy reliance on direct access to the symbol table.
32 // Since 0.7 they don't actually need it anymore and just ignore it,
33 // so we're cheating a bit here and exploiting that detail by passing nil.
34 hcl2Funcs["lookup"] = hcl2InterpolationFuncShim(interpolationFuncLookup(nil))
35 hcl2Funcs["keys"] = hcl2InterpolationFuncShim(interpolationFuncKeys(nil))
36 hcl2Funcs["values"] = hcl2InterpolationFuncShim(interpolationFuncValues(nil))
37
38 // As a bonus, we'll provide the JSON-handling functions from the cty
39 // function library since its "jsonencode" is more complete (doesn't force
40 // weird type conversions) and HIL's type system can't represent
41 // "jsondecode" at all. The result of jsondecode will eventually be forced
42 // to conform to the HIL type system on exit into the rest of Terraform due
43 // to our shimming right now, but it should be usable for decoding _within_
44 // an expression.
45 hcl2Funcs["jsonencode"] = stdlib.JSONEncodeFunc
46 hcl2Funcs["jsondecode"] = stdlib.JSONDecodeFunc
47
48 return hcl2Funcs
49}
50
51func hcl2InterpolationFuncShim(hilFunc ast.Function) function.Function {
52 spec := &function.Spec{}
53
54 for i, hilArgType := range hilFunc.ArgTypes {
55 spec.Params = append(spec.Params, function.Parameter{
56 Type: hcl2shim.HCL2TypeForHILType(hilArgType),
57 Name: fmt.Sprintf("arg%d", i+1), // HIL args don't have names, so we'll fudge it
58 })
59 }
60
61 if hilFunc.Variadic {
62 spec.VarParam = &function.Parameter{
63 Type: hcl2shim.HCL2TypeForHILType(hilFunc.VariadicType),
64 Name: "varargs", // HIL args don't have names, so we'll fudge it
65 }
66 }
67
68 spec.Type = func(args []cty.Value) (cty.Type, error) {
69 return hcl2shim.HCL2TypeForHILType(hilFunc.ReturnType), nil
70 }
71 spec.Impl = func(args []cty.Value, retType cty.Type) (cty.Value, error) {
72 hilArgs := make([]interface{}, len(args))
73 for i, arg := range args {
74 hilV := hcl2shim.HILVariableFromHCL2Value(arg)
75
76 // Although the cty function system does automatic type conversions
77 // to match the argument types, cty doesn't distinguish int and
78 // float and so we may need to adjust here to ensure that the
79 // wrapped function gets exactly the Go type it was expecting.
80 var wantType ast.Type
81 if i < len(hilFunc.ArgTypes) {
82 wantType = hilFunc.ArgTypes[i]
83 } else {
84 wantType = hilFunc.VariadicType
85 }
86 switch {
87 case hilV.Type == ast.TypeInt && wantType == ast.TypeFloat:
88 hilV.Type = wantType
89 hilV.Value = float64(hilV.Value.(int))
90 case hilV.Type == ast.TypeFloat && wantType == ast.TypeInt:
91 hilV.Type = wantType
92 hilV.Value = int(hilV.Value.(float64))
93 }
94
95 // HIL functions actually expect to have the outermost variable
96 // "peeled" but any nested values (in lists or maps) will
97 // still have their ast.Variable wrapping.
98 hilArgs[i] = hilV.Value
99 }
100
101 hilResult, err := hilFunc.Callback(hilArgs)
102 if err != nil {
103 return cty.DynamicVal, err
104 }
105
106 // Just as on the way in, we get back a partially-peeled ast.Variable
107 // which we need to re-wrap in order to convert it back into what
108 // we're calling a "config value".
109 rv := hcl2shim.HCL2ValueFromHILVariable(ast.Variable{
110 Type: hilFunc.ReturnType,
111 Value: hilResult,
112 })
113
114 return convert.Convert(rv, retType) // if result is unknown we'll force the correct type here
115 }
116 return function.New(spec)
117}
118
119func hcl2EvalWithUnknownVars(expr hcl2.Expression) (cty.Value, hcl2.Diagnostics) {
120 trs := expr.Variables()
121 vars := map[string]cty.Value{}
122 val := cty.DynamicVal
123
124 for _, tr := range trs {
125 name := tr.RootName()
126 vars[name] = val
127 }
128
129 ctx := &hcl2.EvalContext{
130 Variables: vars,
131 Functions: hcl2InterpolationFuncs(),
132 }
133 return expr.Value(ctx)
134}
diff --git a/vendor/github.com/hashicorp/terraform/config/hcl2shim/single_attr_body.go b/vendor/github.com/hashicorp/terraform/config/hcl2shim/single_attr_body.go
new file mode 100644
index 0000000..19651c8
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/hcl2shim/single_attr_body.go
@@ -0,0 +1,85 @@
1package hcl2shim
2
3import (
4 "fmt"
5
6 hcl2 "github.com/hashicorp/hcl2/hcl"
7)
8
9// SingleAttrBody is a weird implementation of hcl2.Body that acts as if
10// it has a single attribute whose value is the given expression.
11//
12// This is used to shim Resource.RawCount and Output.RawConfig to behave
13// more like they do in the old HCL loader.
14type SingleAttrBody struct {
15 Name string
16 Expr hcl2.Expression
17}
18
19var _ hcl2.Body = SingleAttrBody{}
20
21func (b SingleAttrBody) Content(schema *hcl2.BodySchema) (*hcl2.BodyContent, hcl2.Diagnostics) {
22 content, all, diags := b.content(schema)
23 if !all {
24 // This should never happen because this body implementation should only
25 // be used by code that is aware that it's using a single-attr body.
26 diags = append(diags, &hcl2.Diagnostic{
27 Severity: hcl2.DiagError,
28 Summary: "Invalid attribute",
29 Detail: fmt.Sprintf("The correct attribute name is %q.", b.Name),
30 Subject: b.Expr.Range().Ptr(),
31 })
32 }
33 return content, diags
34}
35
36func (b SingleAttrBody) PartialContent(schema *hcl2.BodySchema) (*hcl2.BodyContent, hcl2.Body, hcl2.Diagnostics) {
37 content, all, diags := b.content(schema)
38 var remain hcl2.Body
39 if all {
40 // If the request matched the one attribute we represent, then the
41 // remaining body is empty.
42 remain = hcl2.EmptyBody()
43 } else {
44 remain = b
45 }
46 return content, remain, diags
47}
48
49func (b SingleAttrBody) content(schema *hcl2.BodySchema) (*hcl2.BodyContent, bool, hcl2.Diagnostics) {
50 ret := &hcl2.BodyContent{}
51 all := false
52 var diags hcl2.Diagnostics
53
54 for _, attrS := range schema.Attributes {
55 if attrS.Name == b.Name {
56 attrs, _ := b.JustAttributes()
57 ret.Attributes = attrs
58 all = true
59 } else if attrS.Required {
60 diags = append(diags, &hcl2.Diagnostic{
61 Severity: hcl2.DiagError,
62 Summary: "Missing attribute",
63 Detail: fmt.Sprintf("The attribute %q is required.", attrS.Name),
64 Subject: b.Expr.Range().Ptr(),
65 })
66 }
67 }
68
69 return ret, all, diags
70}
71
72func (b SingleAttrBody) JustAttributes() (hcl2.Attributes, hcl2.Diagnostics) {
73 return hcl2.Attributes{
74 b.Name: {
75 Expr: b.Expr,
76 Name: b.Name,
77 NameRange: b.Expr.Range(),
78 Range: b.Expr.Range(),
79 },
80 }, nil
81}
82
83func (b SingleAttrBody) MissingItemRange() hcl2.Range {
84 return b.Expr.Range()
85}
diff --git a/vendor/github.com/hashicorp/terraform/config/hcl2shim/values.go b/vendor/github.com/hashicorp/terraform/config/hcl2shim/values.go
new file mode 100644
index 0000000..0b697a5
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/hcl2shim/values.go
@@ -0,0 +1,246 @@
1package hcl2shim
2
3import (
4 "fmt"
5 "math/big"
6
7 "github.com/hashicorp/hil/ast"
8 "github.com/zclconf/go-cty/cty"
9)
10
11// UnknownVariableValue is a sentinel value that can be used
12// to denote that the value of a variable is unknown at this time.
13// RawConfig uses this information to build up data about
14// unknown keys.
15const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66"
16
17// ConfigValueFromHCL2 converts a value from HCL2 (really, from the cty dynamic
18// types library that HCL2 uses) to a value type that matches what would've
19// been produced from the HCL-based interpolator for an equivalent structure.
20//
21// This function will transform a cty null value into a Go nil value, which
22// isn't a possible outcome of the HCL/HIL-based decoder and so callers may
23// need to detect and reject any null values.
24func ConfigValueFromHCL2(v cty.Value) interface{} {
25 if !v.IsKnown() {
26 return UnknownVariableValue
27 }
28 if v.IsNull() {
29 return nil
30 }
31
32 switch v.Type() {
33 case cty.Bool:
34 return v.True() // like HCL.BOOL
35 case cty.String:
36 return v.AsString() // like HCL token.STRING or token.HEREDOC
37 case cty.Number:
38 // We can't match HCL _exactly_ here because it distinguishes between
39 // int and float values, but we'll get as close as we can by using
40 // an int if the number is exactly representable, and a float if not.
41 // The conversion to float will force precision to that of a float64,
42 // which is potentially losing information from the specific number
43 // given, but no worse than what HCL would've done in its own conversion
44 // to float.
45
46 f := v.AsBigFloat()
47 if i, acc := f.Int64(); acc == big.Exact {
48 // if we're on a 32-bit system and the number is too big for 32-bit
49 // int then we'll fall through here and use a float64.
50 const MaxInt = int(^uint(0) >> 1)
51 const MinInt = -MaxInt - 1
52 if i <= int64(MaxInt) && i >= int64(MinInt) {
53 return int(i) // Like HCL token.NUMBER
54 }
55 }
56
57 f64, _ := f.Float64()
58 return f64 // like HCL token.FLOAT
59 }
60
61 if v.Type().IsListType() || v.Type().IsSetType() || v.Type().IsTupleType() {
62 l := make([]interface{}, 0, v.LengthInt())
63 it := v.ElementIterator()
64 for it.Next() {
65 _, ev := it.Element()
66 l = append(l, ConfigValueFromHCL2(ev))
67 }
68 return l
69 }
70
71 if v.Type().IsMapType() || v.Type().IsObjectType() {
72 l := make(map[string]interface{})
73 it := v.ElementIterator()
74 for it.Next() {
75 ek, ev := it.Element()
76 l[ek.AsString()] = ConfigValueFromHCL2(ev)
77 }
78 return l
79 }
80
81 // If we fall out here then we have some weird type that we haven't
82 // accounted for. This should never happen unless the caller is using
83 // capsule types, and we don't currently have any such types defined.
84 panic(fmt.Errorf("can't convert %#v to config value", v))
85}
86
87// HCL2ValueFromConfigValue is the opposite of configValueFromHCL2: it takes
88// a value as would be returned from the old interpolator and turns it into
89// a cty.Value so it can be used within, for example, an HCL2 EvalContext.
90func HCL2ValueFromConfigValue(v interface{}) cty.Value {
91 if v == nil {
92 return cty.NullVal(cty.DynamicPseudoType)
93 }
94 if v == UnknownVariableValue {
95 return cty.DynamicVal
96 }
97
98 switch tv := v.(type) {
99 case bool:
100 return cty.BoolVal(tv)
101 case string:
102 return cty.StringVal(tv)
103 case int:
104 return cty.NumberIntVal(int64(tv))
105 case float64:
106 return cty.NumberFloatVal(tv)
107 case []interface{}:
108 vals := make([]cty.Value, len(tv))
109 for i, ev := range tv {
110 vals[i] = HCL2ValueFromConfigValue(ev)
111 }
112 return cty.TupleVal(vals)
113 case map[string]interface{}:
114 vals := map[string]cty.Value{}
115 for k, ev := range tv {
116 vals[k] = HCL2ValueFromConfigValue(ev)
117 }
118 return cty.ObjectVal(vals)
119 default:
120 // HCL/HIL should never generate anything that isn't caught by
121 // the above, so if we get here something has gone very wrong.
122 panic(fmt.Errorf("can't convert %#v to cty.Value", v))
123 }
124}
125
126func HILVariableFromHCL2Value(v cty.Value) ast.Variable {
127 if v.IsNull() {
128 // Caller should guarantee/check this before calling
129 panic("Null values cannot be represented in HIL")
130 }
131 if !v.IsKnown() {
132 return ast.Variable{
133 Type: ast.TypeUnknown,
134 Value: UnknownVariableValue,
135 }
136 }
137
138 switch v.Type() {
139 case cty.Bool:
140 return ast.Variable{
141 Type: ast.TypeBool,
142 Value: v.True(),
143 }
144 case cty.Number:
145 v := ConfigValueFromHCL2(v)
146 switch tv := v.(type) {
147 case int:
148 return ast.Variable{
149 Type: ast.TypeInt,
150 Value: tv,
151 }
152 case float64:
153 return ast.Variable{
154 Type: ast.TypeFloat,
155 Value: tv,
156 }
157 default:
158 // should never happen
159 panic("invalid return value for configValueFromHCL2")
160 }
161 case cty.String:
162 return ast.Variable{
163 Type: ast.TypeString,
164 Value: v.AsString(),
165 }
166 }
167
168 if v.Type().IsListType() || v.Type().IsSetType() || v.Type().IsTupleType() {
169 l := make([]ast.Variable, 0, v.LengthInt())
170 it := v.ElementIterator()
171 for it.Next() {
172 _, ev := it.Element()
173 l = append(l, HILVariableFromHCL2Value(ev))
174 }
175 // If we were given a tuple then this could actually produce an invalid
176 // list with non-homogenous types, which we expect to be caught inside
177 // HIL just like a user-supplied non-homogenous list would be.
178 return ast.Variable{
179 Type: ast.TypeList,
180 Value: l,
181 }
182 }
183
184 if v.Type().IsMapType() || v.Type().IsObjectType() {
185 l := make(map[string]ast.Variable)
186 it := v.ElementIterator()
187 for it.Next() {
188 ek, ev := it.Element()
189 l[ek.AsString()] = HILVariableFromHCL2Value(ev)
190 }
191 // If we were given an object then this could actually produce an invalid
192 // map with non-homogenous types, which we expect to be caught inside
193 // HIL just like a user-supplied non-homogenous map would be.
194 return ast.Variable{
195 Type: ast.TypeMap,
196 Value: l,
197 }
198 }
199
200 // If we fall out here then we have some weird type that we haven't
201 // accounted for. This should never happen unless the caller is using
202 // capsule types, and we don't currently have any such types defined.
203 panic(fmt.Errorf("can't convert %#v to HIL variable", v))
204}
205
206func HCL2ValueFromHILVariable(v ast.Variable) cty.Value {
207 switch v.Type {
208 case ast.TypeList:
209 vals := make([]cty.Value, len(v.Value.([]ast.Variable)))
210 for i, ev := range v.Value.([]ast.Variable) {
211 vals[i] = HCL2ValueFromHILVariable(ev)
212 }
213 return cty.TupleVal(vals)
214 case ast.TypeMap:
215 vals := make(map[string]cty.Value, len(v.Value.(map[string]ast.Variable)))
216 for k, ev := range v.Value.(map[string]ast.Variable) {
217 vals[k] = HCL2ValueFromHILVariable(ev)
218 }
219 return cty.ObjectVal(vals)
220 default:
221 return HCL2ValueFromConfigValue(v.Value)
222 }
223}
224
225func HCL2TypeForHILType(hilType ast.Type) cty.Type {
226 switch hilType {
227 case ast.TypeAny:
228 return cty.DynamicPseudoType
229 case ast.TypeUnknown:
230 return cty.DynamicPseudoType
231 case ast.TypeBool:
232 return cty.Bool
233 case ast.TypeInt:
234 return cty.Number
235 case ast.TypeFloat:
236 return cty.Number
237 case ast.TypeString:
238 return cty.String
239 case ast.TypeList:
240 return cty.List(cty.DynamicPseudoType)
241 case ast.TypeMap:
242 return cty.Map(cty.DynamicPseudoType)
243 default:
244 return cty.NilType // equilvalent to ast.TypeInvalid
245 }
246}
diff --git a/vendor/github.com/hashicorp/terraform/config/import_tree.go b/vendor/github.com/hashicorp/terraform/config/import_tree.go
index 37ec11a..08cbc77 100644
--- a/vendor/github.com/hashicorp/terraform/config/import_tree.go
+++ b/vendor/github.com/hashicorp/terraform/config/import_tree.go
@@ -1,8 +1,12 @@
1package config 1package config
2 2
3import ( 3import (
4 "bufio"
4 "fmt" 5 "fmt"
5 "io" 6 "io"
7 "os"
8
9 "github.com/hashicorp/errwrap"
6) 10)
7 11
8// configurable is an interface that must be implemented by any configuration 12// configurable is an interface that must be implemented by any configuration
@@ -27,15 +31,52 @@ type importTree struct {
27// imports. 31// imports.
28type fileLoaderFunc func(path string) (configurable, []string, error) 32type fileLoaderFunc func(path string) (configurable, []string, error)
29 33
34// Set this to a non-empty value at link time to enable the HCL2 experiment.
35// This is not currently enabled for release builds.
36//
37// For example:
38// go install -ldflags="-X github.com/hashicorp/terraform/config.enableHCL2Experiment=true" github.com/hashicorp/terraform
39var enableHCL2Experiment = ""
40
30// loadTree takes a single file and loads the entire importTree for that 41// loadTree takes a single file and loads the entire importTree for that
31// file. This function detects what kind of configuration file it is an 42// file. This function detects what kind of configuration file it is an
32// executes the proper fileLoaderFunc. 43// executes the proper fileLoaderFunc.
33func loadTree(root string) (*importTree, error) { 44func loadTree(root string) (*importTree, error) {
34 var f fileLoaderFunc 45 var f fileLoaderFunc
35 switch ext(root) { 46
36 case ".tf", ".tf.json": 47 // HCL2 experiment is currently activated at build time via the linker.
37 f = loadFileHcl 48 // See the comment on this variable for more information.
38 default: 49 if enableHCL2Experiment == "" {
50 // Main-line behavior: always use the original HCL parser
51 switch ext(root) {
52 case ".tf", ".tf.json":
53 f = loadFileHcl
54 default:
55 }
56 } else {
57 // Experimental behavior: use the HCL2 parser if the opt-in comment
58 // is present.
59 switch ext(root) {
60 case ".tf":
61 // We need to sniff the file for the opt-in comment line to decide
62 // if the file is participating in the HCL2 experiment.
63 cf, err := os.Open(root)
64 if err != nil {
65 return nil, err
66 }
67 sc := bufio.NewScanner(cf)
68 for sc.Scan() {
69 if sc.Text() == "#terraform:hcl2" {
70 f = globalHCL2Loader.loadFile
71 }
72 }
73 if f == nil {
74 f = loadFileHcl
75 }
76 case ".tf.json":
77 f = loadFileHcl
78 default:
79 }
39 } 80 }
40 81
41 if f == nil { 82 if f == nil {
@@ -86,10 +127,7 @@ func (t *importTree) Close() error {
86func (t *importTree) ConfigTree() (*configTree, error) { 127func (t *importTree) ConfigTree() (*configTree, error) {
87 config, err := t.Raw.Config() 128 config, err := t.Raw.Config()
88 if err != nil { 129 if err != nil {
89 return nil, fmt.Errorf( 130 return nil, errwrap.Wrapf(fmt.Sprintf("Error loading %s: {{err}}", t.Path), err)
90 "Error loading %s: %s",
91 t.Path,
92 err)
93 } 131 }
94 132
95 // Build our result 133 // Build our result
diff --git a/vendor/github.com/hashicorp/terraform/config/interpolate.go b/vendor/github.com/hashicorp/terraform/config/interpolate.go
index bbb3555..599e5ec 100644
--- a/vendor/github.com/hashicorp/terraform/config/interpolate.go
+++ b/vendor/github.com/hashicorp/terraform/config/interpolate.go
@@ -5,6 +5,8 @@ import (
5 "strconv" 5 "strconv"
6 "strings" 6 "strings"
7 7
8 "github.com/hashicorp/terraform/tfdiags"
9
8 "github.com/hashicorp/hil/ast" 10 "github.com/hashicorp/hil/ast"
9) 11)
10 12
@@ -14,6 +16,21 @@ import (
14// variables can come from: user variables, resources, etc. 16// variables can come from: user variables, resources, etc.
15type InterpolatedVariable interface { 17type InterpolatedVariable interface {
16 FullKey() string 18 FullKey() string
19 SourceRange() tfdiags.SourceRange
20}
21
22// varRange can be embedded into an InterpolatedVariable implementation to
23// implement the SourceRange method.
24type varRange struct {
25 rng tfdiags.SourceRange
26}
27
28func (r varRange) SourceRange() tfdiags.SourceRange {
29 return r.rng
30}
31
32func makeVarRange(rng tfdiags.SourceRange) varRange {
33 return varRange{rng}
17} 34}
18 35
19// CountVariable is a variable for referencing information about 36// CountVariable is a variable for referencing information about
@@ -21,6 +38,7 @@ type InterpolatedVariable interface {
21type CountVariable struct { 38type CountVariable struct {
22 Type CountValueType 39 Type CountValueType
23 key string 40 key string
41 varRange
24} 42}
25 43
26// CountValueType is the type of the count variable that is referenced. 44// CountValueType is the type of the count variable that is referenced.
@@ -37,6 +55,7 @@ type ModuleVariable struct {
37 Name string 55 Name string
38 Field string 56 Field string
39 key string 57 key string
58 varRange
40} 59}
41 60
42// A PathVariable is a variable that references path information about the 61// A PathVariable is a variable that references path information about the
@@ -44,6 +63,7 @@ type ModuleVariable struct {
44type PathVariable struct { 63type PathVariable struct {
45 Type PathValueType 64 Type PathValueType
46 key string 65 key string
66 varRange
47} 67}
48 68
49type PathValueType byte 69type PathValueType byte
@@ -67,6 +87,7 @@ type ResourceVariable struct {
67 Index int // Index for multi-variable: aws_instance.foo.1.id == 1 87 Index int // Index for multi-variable: aws_instance.foo.1.id == 1
68 88
69 key string 89 key string
90 varRange
70} 91}
71 92
72// SelfVariable is a variable that is referencing the same resource 93// SelfVariable is a variable that is referencing the same resource
@@ -75,6 +96,7 @@ type SelfVariable struct {
75 Field string 96 Field string
76 97
77 key string 98 key string
99 varRange
78} 100}
79 101
80// SimpleVariable is an unprefixed variable, which can show up when users have 102// SimpleVariable is an unprefixed variable, which can show up when users have
@@ -82,6 +104,7 @@ type SelfVariable struct {
82// internally. The template_file resource is an example of this. 104// internally. The template_file resource is an example of this.
83type SimpleVariable struct { 105type SimpleVariable struct {
84 Key string 106 Key string
107 varRange
85} 108}
86 109
87// TerraformVariable is a "terraform."-prefixed variable used to access 110// TerraformVariable is a "terraform."-prefixed variable used to access
@@ -89,6 +112,7 @@ type SimpleVariable struct {
89type TerraformVariable struct { 112type TerraformVariable struct {
90 Field string 113 Field string
91 key string 114 key string
115 varRange
92} 116}
93 117
94// A UserVariable is a variable that is referencing a user variable 118// A UserVariable is a variable that is referencing a user variable
@@ -99,6 +123,14 @@ type UserVariable struct {
99 Elem string 123 Elem string
100 124
101 key string 125 key string
126 varRange
127}
128
129// A LocalVariable is a variable that references a local value defined within
130// the current module, via a "locals" block. This looks like "${local.foo}".
131type LocalVariable struct {
132 Name string
133 varRange
102} 134}
103 135
104func NewInterpolatedVariable(v string) (InterpolatedVariable, error) { 136func NewInterpolatedVariable(v string) (InterpolatedVariable, error) {
@@ -112,6 +144,8 @@ func NewInterpolatedVariable(v string) (InterpolatedVariable, error) {
112 return NewTerraformVariable(v) 144 return NewTerraformVariable(v)
113 } else if strings.HasPrefix(v, "var.") { 145 } else if strings.HasPrefix(v, "var.") {
114 return NewUserVariable(v) 146 return NewUserVariable(v)
147 } else if strings.HasPrefix(v, "local.") {
148 return NewLocalVariable(v)
115 } else if strings.HasPrefix(v, "module.") { 149 } else if strings.HasPrefix(v, "module.") {
116 return NewModuleVariable(v) 150 return NewModuleVariable(v)
117 } else if !strings.ContainsRune(v, '.') { 151 } else if !strings.ContainsRune(v, '.') {
@@ -276,7 +310,7 @@ func (v *SelfVariable) GoString() string {
276} 310}
277 311
278func NewSimpleVariable(key string) (*SimpleVariable, error) { 312func NewSimpleVariable(key string) (*SimpleVariable, error) {
279 return &SimpleVariable{key}, nil 313 return &SimpleVariable{Key: key}, nil
280} 314}
281 315
282func (v *SimpleVariable) FullKey() string { 316func (v *SimpleVariable) FullKey() string {
@@ -331,6 +365,25 @@ func (v *UserVariable) GoString() string {
331 return fmt.Sprintf("*%#v", *v) 365 return fmt.Sprintf("*%#v", *v)
332} 366}
333 367
368func NewLocalVariable(key string) (*LocalVariable, error) {
369 name := key[len("local."):]
370 if idx := strings.Index(name, "."); idx > -1 {
371 return nil, fmt.Errorf("Can't use dot (.) attribute access in local.%s; use square bracket indexing", name)
372 }
373
374 return &LocalVariable{
375 Name: name,
376 }, nil
377}
378
379func (v *LocalVariable) FullKey() string {
380 return fmt.Sprintf("local.%s", v.Name)
381}
382
383func (v *LocalVariable) GoString() string {
384 return fmt.Sprintf("*%#v", *v)
385}
386
334// DetectVariables takes an AST root and returns all the interpolated 387// DetectVariables takes an AST root and returns all the interpolated
335// variables that are detected in the AST tree. 388// variables that are detected in the AST tree.
336func DetectVariables(root ast.Node) ([]InterpolatedVariable, error) { 389func DetectVariables(root ast.Node) ([]InterpolatedVariable, error) {
diff --git a/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go b/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go
index a298cf2..421edb0 100644
--- a/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go
+++ b/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go
@@ -1,17 +1,23 @@
1package config 1package config
2 2
3import ( 3import (
4 "bytes"
5 "compress/gzip"
4 "crypto/md5" 6 "crypto/md5"
7 "crypto/rsa"
5 "crypto/sha1" 8 "crypto/sha1"
6 "crypto/sha256" 9 "crypto/sha256"
7 "crypto/sha512" 10 "crypto/sha512"
11 "crypto/x509"
8 "encoding/base64" 12 "encoding/base64"
9 "encoding/hex" 13 "encoding/hex"
10 "encoding/json" 14 "encoding/json"
15 "encoding/pem"
11 "fmt" 16 "fmt"
12 "io/ioutil" 17 "io/ioutil"
13 "math" 18 "math"
14 "net" 19 "net"
20 "net/url"
15 "path/filepath" 21 "path/filepath"
16 "regexp" 22 "regexp"
17 "sort" 23 "sort"
@@ -55,59 +61,74 @@ func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) {
55// Funcs is the mapping of built-in functions for configuration. 61// Funcs is the mapping of built-in functions for configuration.
56func Funcs() map[string]ast.Function { 62func Funcs() map[string]ast.Function {
57 return map[string]ast.Function{ 63 return map[string]ast.Function{
58 "basename": interpolationFuncBasename(), 64 "abs": interpolationFuncAbs(),
59 "base64decode": interpolationFuncBase64Decode(), 65 "basename": interpolationFuncBasename(),
60 "base64encode": interpolationFuncBase64Encode(), 66 "base64decode": interpolationFuncBase64Decode(),
61 "base64sha256": interpolationFuncBase64Sha256(), 67 "base64encode": interpolationFuncBase64Encode(),
62 "base64sha512": interpolationFuncBase64Sha512(), 68 "base64gzip": interpolationFuncBase64Gzip(),
63 "bcrypt": interpolationFuncBcrypt(), 69 "base64sha256": interpolationFuncBase64Sha256(),
64 "ceil": interpolationFuncCeil(), 70 "base64sha512": interpolationFuncBase64Sha512(),
65 "chomp": interpolationFuncChomp(), 71 "bcrypt": interpolationFuncBcrypt(),
66 "cidrhost": interpolationFuncCidrHost(), 72 "ceil": interpolationFuncCeil(),
67 "cidrnetmask": interpolationFuncCidrNetmask(), 73 "chomp": interpolationFuncChomp(),
68 "cidrsubnet": interpolationFuncCidrSubnet(), 74 "cidrhost": interpolationFuncCidrHost(),
69 "coalesce": interpolationFuncCoalesce(), 75 "cidrnetmask": interpolationFuncCidrNetmask(),
70 "coalescelist": interpolationFuncCoalesceList(), 76 "cidrsubnet": interpolationFuncCidrSubnet(),
71 "compact": interpolationFuncCompact(), 77 "coalesce": interpolationFuncCoalesce(),
72 "concat": interpolationFuncConcat(), 78 "coalescelist": interpolationFuncCoalesceList(),
73 "contains": interpolationFuncContains(), 79 "compact": interpolationFuncCompact(),
74 "dirname": interpolationFuncDirname(), 80 "concat": interpolationFuncConcat(),
75 "distinct": interpolationFuncDistinct(), 81 "contains": interpolationFuncContains(),
76 "element": interpolationFuncElement(), 82 "dirname": interpolationFuncDirname(),
77 "file": interpolationFuncFile(), 83 "distinct": interpolationFuncDistinct(),
78 "matchkeys": interpolationFuncMatchKeys(), 84 "element": interpolationFuncElement(),
79 "floor": interpolationFuncFloor(), 85 "chunklist": interpolationFuncChunklist(),
80 "format": interpolationFuncFormat(), 86 "file": interpolationFuncFile(),
81 "formatlist": interpolationFuncFormatList(), 87 "filebase64sha256": interpolationFuncMakeFileHash(interpolationFuncBase64Sha256()),
82 "index": interpolationFuncIndex(), 88 "filebase64sha512": interpolationFuncMakeFileHash(interpolationFuncBase64Sha512()),
83 "join": interpolationFuncJoin(), 89 "filemd5": interpolationFuncMakeFileHash(interpolationFuncMd5()),
84 "jsonencode": interpolationFuncJSONEncode(), 90 "filesha1": interpolationFuncMakeFileHash(interpolationFuncSha1()),
85 "length": interpolationFuncLength(), 91 "filesha256": interpolationFuncMakeFileHash(interpolationFuncSha256()),
86 "list": interpolationFuncList(), 92 "filesha512": interpolationFuncMakeFileHash(interpolationFuncSha512()),
87 "log": interpolationFuncLog(), 93 "matchkeys": interpolationFuncMatchKeys(),
88 "lower": interpolationFuncLower(), 94 "flatten": interpolationFuncFlatten(),
89 "map": interpolationFuncMap(), 95 "floor": interpolationFuncFloor(),
90 "max": interpolationFuncMax(), 96 "format": interpolationFuncFormat(),
91 "md5": interpolationFuncMd5(), 97 "formatlist": interpolationFuncFormatList(),
92 "merge": interpolationFuncMerge(), 98 "indent": interpolationFuncIndent(),
93 "min": interpolationFuncMin(), 99 "index": interpolationFuncIndex(),
94 "pathexpand": interpolationFuncPathExpand(), 100 "join": interpolationFuncJoin(),
95 "pow": interpolationFuncPow(), 101 "jsonencode": interpolationFuncJSONEncode(),
96 "uuid": interpolationFuncUUID(), 102 "length": interpolationFuncLength(),
97 "replace": interpolationFuncReplace(), 103 "list": interpolationFuncList(),
98 "sha1": interpolationFuncSha1(), 104 "log": interpolationFuncLog(),
99 "sha256": interpolationFuncSha256(), 105 "lower": interpolationFuncLower(),
100 "sha512": interpolationFuncSha512(), 106 "map": interpolationFuncMap(),
101 "signum": interpolationFuncSignum(), 107 "max": interpolationFuncMax(),
102 "slice": interpolationFuncSlice(), 108 "md5": interpolationFuncMd5(),
103 "sort": interpolationFuncSort(), 109 "merge": interpolationFuncMerge(),
104 "split": interpolationFuncSplit(), 110 "min": interpolationFuncMin(),
105 "substr": interpolationFuncSubstr(), 111 "pathexpand": interpolationFuncPathExpand(),
106 "timestamp": interpolationFuncTimestamp(), 112 "pow": interpolationFuncPow(),
107 "title": interpolationFuncTitle(), 113 "uuid": interpolationFuncUUID(),
108 "trimspace": interpolationFuncTrimSpace(), 114 "replace": interpolationFuncReplace(),
109 "upper": interpolationFuncUpper(), 115 "rsadecrypt": interpolationFuncRsaDecrypt(),
110 "zipmap": interpolationFuncZipMap(), 116 "sha1": interpolationFuncSha1(),
117 "sha256": interpolationFuncSha256(),
118 "sha512": interpolationFuncSha512(),
119 "signum": interpolationFuncSignum(),
120 "slice": interpolationFuncSlice(),
121 "sort": interpolationFuncSort(),
122 "split": interpolationFuncSplit(),
123 "substr": interpolationFuncSubstr(),
124 "timestamp": interpolationFuncTimestamp(),
125 "timeadd": interpolationFuncTimeAdd(),
126 "title": interpolationFuncTitle(),
127 "transpose": interpolationFuncTranspose(),
128 "trimspace": interpolationFuncTrimSpace(),
129 "upper": interpolationFuncUpper(),
130 "urlencode": interpolationFuncURLEncode(),
131 "zipmap": interpolationFuncZipMap(),
111 } 132 }
112} 133}
113 134
@@ -669,6 +690,21 @@ func interpolationFuncFormatList() ast.Function {
669 } 690 }
670} 691}
671 692
693// interpolationFuncIndent indents a multi-line string with the
694// specified number of spaces
695func interpolationFuncIndent() ast.Function {
696 return ast.Function{
697 ArgTypes: []ast.Type{ast.TypeInt, ast.TypeString},
698 ReturnType: ast.TypeString,
699 Callback: func(args []interface{}) (interface{}, error) {
700 spaces := args[0].(int)
701 data := args[1].(string)
702 pad := strings.Repeat(" ", spaces)
703 return strings.Replace(data, "\n", "\n"+pad, -1), nil
704 },
705 }
706}
707
672// interpolationFuncIndex implements the "index" function that allows one to 708// interpolationFuncIndex implements the "index" function that allows one to
673// find the index of a specific element in a list 709// find the index of a specific element in a list
674func interpolationFuncIndex() ast.Function { 710func interpolationFuncIndex() ast.Function {
@@ -823,8 +859,7 @@ func interpolationFuncJoin() ast.Function {
823} 859}
824 860
825// interpolationFuncJSONEncode implements the "jsonencode" function that encodes 861// interpolationFuncJSONEncode implements the "jsonencode" function that encodes
826// a string, list, or map as its JSON representation. For now, values in the 862// a string, list, or map as its JSON representation.
827// list or map may only be strings.
828func interpolationFuncJSONEncode() ast.Function { 863func interpolationFuncJSONEncode() ast.Function {
829 return ast.Function{ 864 return ast.Function{
830 ArgTypes: []ast.Type{ast.TypeAny}, 865 ArgTypes: []ast.Type{ast.TypeAny},
@@ -837,28 +872,36 @@ func interpolationFuncJSONEncode() ast.Function {
837 toEncode = typedArg 872 toEncode = typedArg
838 873
839 case []ast.Variable: 874 case []ast.Variable:
840 // We preallocate the list here. Note that it's important that in
841 // the length 0 case, we have an empty list rather than nil, as
842 // they encode differently.
843 // XXX It would be nice to support arbitrarily nested data here. Is
844 // there an inverse of hil.InterfaceToVariable?
845 strings := make([]string, len(typedArg)) 875 strings := make([]string, len(typedArg))
846 876
847 for i, v := range typedArg { 877 for i, v := range typedArg {
848 if v.Type != ast.TypeString { 878 if v.Type != ast.TypeString {
849 return "", fmt.Errorf("list elements must be strings") 879 variable, _ := hil.InterfaceToVariable(typedArg)
880 toEncode, _ = hil.VariableToInterface(variable)
881
882 jEnc, err := json.Marshal(toEncode)
883 if err != nil {
884 return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode)
885 }
886 return string(jEnc), nil
887
850 } 888 }
851 strings[i] = v.Value.(string) 889 strings[i] = v.Value.(string)
852 } 890 }
853 toEncode = strings 891 toEncode = strings
854 892
855 case map[string]ast.Variable: 893 case map[string]ast.Variable:
856 // XXX It would be nice to support arbitrarily nested data here. Is
857 // there an inverse of hil.InterfaceToVariable?
858 stringMap := make(map[string]string) 894 stringMap := make(map[string]string)
859 for k, v := range typedArg { 895 for k, v := range typedArg {
860 if v.Type != ast.TypeString { 896 if v.Type != ast.TypeString {
861 return "", fmt.Errorf("map values must be strings") 897 variable, _ := hil.InterfaceToVariable(typedArg)
898 toEncode, _ = hil.VariableToInterface(variable)
899
900 jEnc, err := json.Marshal(toEncode)
901 if err != nil {
902 return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode)
903 }
904 return string(jEnc), nil
862 } 905 }
863 stringMap[k] = v.Value.(string) 906 stringMap[k] = v.Value.(string)
864 } 907 }
@@ -1098,6 +1141,56 @@ func interpolationFuncElement() ast.Function {
1098 } 1141 }
1099} 1142}
1100 1143
1144// returns the `list` items chunked by `size`.
1145func interpolationFuncChunklist() ast.Function {
1146 return ast.Function{
1147 ArgTypes: []ast.Type{
1148 ast.TypeList, // inputList
1149 ast.TypeInt, // size
1150 },
1151 ReturnType: ast.TypeList,
1152 Callback: func(args []interface{}) (interface{}, error) {
1153 output := make([]ast.Variable, 0)
1154
1155 values, _ := args[0].([]ast.Variable)
1156 size, _ := args[1].(int)
1157
1158 // errors if size is negative
1159 if size < 0 {
1160 return nil, fmt.Errorf("The size argument must be positive")
1161 }
1162
1163 // if size is 0, returns a list made of the initial list
1164 if size == 0 {
1165 output = append(output, ast.Variable{
1166 Type: ast.TypeList,
1167 Value: values,
1168 })
1169 return output, nil
1170 }
1171
1172 variables := make([]ast.Variable, 0)
1173 chunk := ast.Variable{
1174 Type: ast.TypeList,
1175 Value: variables,
1176 }
1177 l := len(values)
1178 for i, v := range values {
1179 variables = append(variables, v)
1180
1181 // Chunk when index isn't 0, or when reaching the values's length
1182 if (i+1)%size == 0 || (i+1) == l {
1183 chunk.Value = variables
1184 output = append(output, chunk)
1185 variables = make([]ast.Variable, 0)
1186 }
1187 }
1188
1189 return output, nil
1190 },
1191 }
1192}
1193
1101// interpolationFuncKeys implements the "keys" function that yields a list of 1194// interpolationFuncKeys implements the "keys" function that yields a list of
1102// keys of map types within a Terraform configuration. 1195// keys of map types within a Terraform configuration.
1103func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function { 1196func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function {
@@ -1197,6 +1290,32 @@ func interpolationFuncBase64Decode() ast.Function {
1197 } 1290 }
1198} 1291}
1199 1292
1293// interpolationFuncBase64Gzip implements the "gzip" function that allows gzip
1294// compression encoding the result using base64
1295func interpolationFuncBase64Gzip() ast.Function {
1296 return ast.Function{
1297 ArgTypes: []ast.Type{ast.TypeString},
1298 ReturnType: ast.TypeString,
1299 Callback: func(args []interface{}) (interface{}, error) {
1300 s := args[0].(string)
1301
1302 var b bytes.Buffer
1303 gz := gzip.NewWriter(&b)
1304 if _, err := gz.Write([]byte(s)); err != nil {
1305 return "", fmt.Errorf("failed to write gzip raw data: '%s'", s)
1306 }
1307 if err := gz.Flush(); err != nil {
1308 return "", fmt.Errorf("failed to flush gzip writer: '%s'", s)
1309 }
1310 if err := gz.Close(); err != nil {
1311 return "", fmt.Errorf("failed to close gzip writer: '%s'", s)
1312 }
1313
1314 return base64.StdEncoding.EncodeToString(b.Bytes()), nil
1315 },
1316 }
1317}
1318
1200// interpolationFuncLower implements the "lower" function that does 1319// interpolationFuncLower implements the "lower" function that does
1201// string lower casing. 1320// string lower casing.
1202func interpolationFuncLower() ast.Function { 1321func interpolationFuncLower() ast.Function {
@@ -1396,6 +1515,29 @@ func interpolationFuncTimestamp() ast.Function {
1396 } 1515 }
1397} 1516}
1398 1517
1518func interpolationFuncTimeAdd() ast.Function {
1519 return ast.Function{
1520 ArgTypes: []ast.Type{
1521 ast.TypeString, // input timestamp string in RFC3339 format
1522 ast.TypeString, // duration to add to input timestamp that should be parsable by time.ParseDuration
1523 },
1524 ReturnType: ast.TypeString,
1525 Callback: func(args []interface{}) (interface{}, error) {
1526
1527 ts, err := time.Parse(time.RFC3339, args[0].(string))
1528 if err != nil {
1529 return nil, err
1530 }
1531 duration, err := time.ParseDuration(args[1].(string))
1532 if err != nil {
1533 return nil, err
1534 }
1535
1536 return ts.Add(duration).Format(time.RFC3339), nil
1537 },
1538 }
1539}
1540
1399// interpolationFuncTitle implements the "title" function that returns a copy of the 1541// interpolationFuncTitle implements the "title" function that returns a copy of the
1400// string in which first characters of all the words are capitalized. 1542// string in which first characters of all the words are capitalized.
1401func interpolationFuncTitle() ast.Function { 1543func interpolationFuncTitle() ast.Function {
@@ -1441,7 +1583,7 @@ func interpolationFuncSubstr() ast.Function {
1441 return nil, fmt.Errorf("length should be a non-negative integer") 1583 return nil, fmt.Errorf("length should be a non-negative integer")
1442 } 1584 }
1443 1585
1444 if offset > len(str) { 1586 if offset > len(str) || offset < 0 {
1445 return nil, fmt.Errorf("offset cannot be larger than the length of the string") 1587 return nil, fmt.Errorf("offset cannot be larger than the length of the string")
1446 } 1588 }
1447 1589
@@ -1453,3 +1595,160 @@ func interpolationFuncSubstr() ast.Function {
1453 }, 1595 },
1454 } 1596 }
1455} 1597}
1598
1599// Flatten until it's not ast.TypeList
1600func flattener(finalList []ast.Variable, flattenList []ast.Variable) []ast.Variable {
1601 for _, val := range flattenList {
1602 if val.Type == ast.TypeList {
1603 finalList = flattener(finalList, val.Value.([]ast.Variable))
1604 } else {
1605 finalList = append(finalList, val)
1606 }
1607 }
1608 return finalList
1609}
1610
1611// Flatten to single list
1612func interpolationFuncFlatten() ast.Function {
1613 return ast.Function{
1614 ArgTypes: []ast.Type{ast.TypeList},
1615 ReturnType: ast.TypeList,
1616 Variadic: false,
1617 Callback: func(args []interface{}) (interface{}, error) {
1618 inputList := args[0].([]ast.Variable)
1619
1620 var outputList []ast.Variable
1621 return flattener(outputList, inputList), nil
1622 },
1623 }
1624}
1625
1626func interpolationFuncURLEncode() ast.Function {
1627 return ast.Function{
1628 ArgTypes: []ast.Type{ast.TypeString},
1629 ReturnType: ast.TypeString,
1630 Callback: func(args []interface{}) (interface{}, error) {
1631 s := args[0].(string)
1632 return url.QueryEscape(s), nil
1633 },
1634 }
1635}
1636
1637// interpolationFuncTranspose implements the "transpose" function
1638// that converts a map (string,list) to a map (string,list) where
1639// the unique values of the original lists become the keys of the
1640// new map and the keys of the original map become values for the
1641// corresponding new keys.
1642func interpolationFuncTranspose() ast.Function {
1643 return ast.Function{
1644 ArgTypes: []ast.Type{ast.TypeMap},
1645 ReturnType: ast.TypeMap,
1646 Callback: func(args []interface{}) (interface{}, error) {
1647
1648 inputMap := args[0].(map[string]ast.Variable)
1649 outputMap := make(map[string]ast.Variable)
1650 tmpMap := make(map[string][]string)
1651
1652 for inKey, inVal := range inputMap {
1653 if inVal.Type != ast.TypeList {
1654 return nil, fmt.Errorf("transpose requires a map of lists of strings")
1655 }
1656 values := inVal.Value.([]ast.Variable)
1657 for _, listVal := range values {
1658 if listVal.Type != ast.TypeString {
1659 return nil, fmt.Errorf("transpose requires the given map values to be lists of strings")
1660 }
1661 outKey := listVal.Value.(string)
1662 if _, ok := tmpMap[outKey]; !ok {
1663 tmpMap[outKey] = make([]string, 0)
1664 }
1665 outVal := tmpMap[outKey]
1666 outVal = append(outVal, inKey)
1667 sort.Strings(outVal)
1668 tmpMap[outKey] = outVal
1669 }
1670 }
1671
1672 for outKey, outVal := range tmpMap {
1673 values := make([]ast.Variable, 0)
1674 for _, v := range outVal {
1675 values = append(values, ast.Variable{Type: ast.TypeString, Value: v})
1676 }
1677 outputMap[outKey] = ast.Variable{Type: ast.TypeList, Value: values}
1678 }
1679 return outputMap, nil
1680 },
1681 }
1682}
1683
1684// interpolationFuncAbs returns the absolute value of a given float.
1685func interpolationFuncAbs() ast.Function {
1686 return ast.Function{
1687 ArgTypes: []ast.Type{ast.TypeFloat},
1688 ReturnType: ast.TypeFloat,
1689 Callback: func(args []interface{}) (interface{}, error) {
1690 return math.Abs(args[0].(float64)), nil
1691 },
1692 }
1693}
1694
1695// interpolationFuncRsaDecrypt implements the "rsadecrypt" function that does
1696// RSA decryption.
1697func interpolationFuncRsaDecrypt() ast.Function {
1698 return ast.Function{
1699 ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
1700 ReturnType: ast.TypeString,
1701 Callback: func(args []interface{}) (interface{}, error) {
1702 s := args[0].(string)
1703 key := args[1].(string)
1704
1705 b, err := base64.StdEncoding.DecodeString(s)
1706 if err != nil {
1707 return "", fmt.Errorf("Failed to decode input %q: cipher text must be base64-encoded", s)
1708 }
1709
1710 block, _ := pem.Decode([]byte(key))
1711 if block == nil {
1712 return "", fmt.Errorf("Failed to read key %q: no key found", key)
1713 }
1714 if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
1715 return "", fmt.Errorf(
1716 "Failed to read key %q: password protected keys are\n"+
1717 "not supported. Please decrypt the key prior to use.", key)
1718 }
1719
1720 x509Key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
1721 if err != nil {
1722 return "", err
1723 }
1724
1725 out, err := rsa.DecryptPKCS1v15(nil, x509Key, b)
1726 if err != nil {
1727 return "", err
1728 }
1729
1730 return string(out), nil
1731 },
1732 }
1733}
1734
1735// interpolationFuncMakeFileHash constructs a function that hashes the contents
1736// of a file by combining the implementations of the file(...) function and
1737// a given other function that is assumed to take a single string argument and
1738// return a hash value.
1739func interpolationFuncMakeFileHash(hashFunc ast.Function) ast.Function {
1740 fileFunc := interpolationFuncFile()
1741
1742 return ast.Function{
1743 ArgTypes: []ast.Type{ast.TypeString},
1744 ReturnType: ast.TypeString,
1745 Callback: func(args []interface{}) (interface{}, error) {
1746 filename := args[0].(string)
1747 contents, err := fileFunc.Callback([]interface{}{filename})
1748 if err != nil {
1749 return nil, err
1750 }
1751 return hashFunc.Callback([]interface{}{contents})
1752 },
1753 }
1754}
diff --git a/vendor/github.com/hashicorp/terraform/config/interpolate_walk.go b/vendor/github.com/hashicorp/terraform/config/interpolate_walk.go
index ead3d10..66a677d 100644
--- a/vendor/github.com/hashicorp/terraform/config/interpolate_walk.go
+++ b/vendor/github.com/hashicorp/terraform/config/interpolate_walk.go
@@ -271,9 +271,7 @@ func (w *interpolationWalker) splitSlice() {
271 result = append(result, val.Value) 271 result = append(result, val.Value)
272 } 272 }
273 case []interface{}: 273 case []interface{}:
274 for _, element := range val { 274 result = append(result, val...)
275 result = append(result, element)
276 }
277 default: 275 default:
278 result = append(result, v) 276 result = append(result, v)
279 } 277 }
diff --git a/vendor/github.com/hashicorp/terraform/config/loader.go b/vendor/github.com/hashicorp/terraform/config/loader.go
index 5dd7d46..6e34781 100644
--- a/vendor/github.com/hashicorp/terraform/config/loader.go
+++ b/vendor/github.com/hashicorp/terraform/config/loader.go
@@ -80,7 +80,7 @@ func LoadDir(root string) (*Config, error) {
80 if err != nil { 80 if err != nil {
81 return nil, err 81 return nil, err
82 } 82 }
83 if len(files) == 0 { 83 if len(files) == 0 && len(overrides) == 0 {
84 return nil, &ErrNoConfigsFound{Dir: root} 84 return nil, &ErrNoConfigsFound{Dir: root}
85 } 85 }
86 86
@@ -112,6 +112,9 @@ func LoadDir(root string) (*Config, error) {
112 result = c 112 result = c
113 } 113 }
114 } 114 }
115 if len(files) == 0 {
116 result = &Config{}
117 }
115 118
116 // Load all the overrides, and merge them into the config 119 // Load all the overrides, and merge them into the config
117 for _, f := range overrides { 120 for _, f := range overrides {
diff --git a/vendor/github.com/hashicorp/terraform/config/loader_hcl.go b/vendor/github.com/hashicorp/terraform/config/loader_hcl.go
index e85e493..68cffe2 100644
--- a/vendor/github.com/hashicorp/terraform/config/loader_hcl.go
+++ b/vendor/github.com/hashicorp/terraform/config/loader_hcl.go
@@ -17,10 +17,20 @@ type hclConfigurable struct {
17 Root *ast.File 17 Root *ast.File
18} 18}
19 19
20var ReservedDataSourceFields = []string{
21 "connection",
22 "count",
23 "depends_on",
24 "lifecycle",
25 "provider",
26 "provisioner",
27}
28
20var ReservedResourceFields = []string{ 29var ReservedResourceFields = []string{
21 "connection", 30 "connection",
22 "count", 31 "count",
23 "depends_on", 32 "depends_on",
33 "id",
24 "lifecycle", 34 "lifecycle",
25 "provider", 35 "provider",
26 "provisioner", 36 "provisioner",
@@ -35,6 +45,7 @@ func (t *hclConfigurable) Config() (*Config, error) {
35 validKeys := map[string]struct{}{ 45 validKeys := map[string]struct{}{
36 "atlas": struct{}{}, 46 "atlas": struct{}{},
37 "data": struct{}{}, 47 "data": struct{}{},
48 "locals": struct{}{},
38 "module": struct{}{}, 49 "module": struct{}{},
39 "output": struct{}{}, 50 "output": struct{}{},
40 "provider": struct{}{}, 51 "provider": struct{}{},
@@ -70,6 +81,15 @@ func (t *hclConfigurable) Config() (*Config, error) {
70 } 81 }
71 } 82 }
72 83
84 // Build local values
85 if locals := list.Filter("locals"); len(locals.Items) > 0 {
86 var err error
87 config.Locals, err = loadLocalsHcl(locals)
88 if err != nil {
89 return nil, err
90 }
91 }
92
73 // Get Atlas configuration 93 // Get Atlas configuration
74 if atlas := list.Filter("atlas"); len(atlas.Items) > 0 { 94 if atlas := list.Filter("atlas"); len(atlas.Items) > 0 {
75 var err error 95 var err error
@@ -373,9 +393,6 @@ func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) {
373 err) 393 err)
374 } 394 }
375 395
376 // Remove the fields we handle specially
377 delete(config, "source")
378
379 rawConfig, err := NewRawConfig(config) 396 rawConfig, err := NewRawConfig(config)
380 if err != nil { 397 if err != nil {
381 return nil, fmt.Errorf( 398 return nil, fmt.Errorf(
@@ -384,7 +401,11 @@ func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) {
384 err) 401 err)
385 } 402 }
386 403
387 // If we have a count, then figure it out 404 // Remove the fields we handle specially
405 delete(config, "source")
406 delete(config, "version")
407 delete(config, "providers")
408
388 var source string 409 var source string
389 if o := listVal.Filter("source"); len(o.Items) > 0 { 410 if o := listVal.Filter("source"); len(o.Items) > 0 {
390 err = hcl.DecodeObject(&source, o.Items[0].Val) 411 err = hcl.DecodeObject(&source, o.Items[0].Val)
@@ -396,9 +417,33 @@ func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) {
396 } 417 }
397 } 418 }
398 419
420 var version string
421 if o := listVal.Filter("version"); len(o.Items) > 0 {
422 err = hcl.DecodeObject(&version, o.Items[0].Val)
423 if err != nil {
424 return nil, fmt.Errorf(
425 "Error parsing version for %s: %s",
426 k,
427 err)
428 }
429 }
430
431 var providers map[string]string
432 if o := listVal.Filter("providers"); len(o.Items) > 0 {
433 err = hcl.DecodeObject(&providers, o.Items[0].Val)
434 if err != nil {
435 return nil, fmt.Errorf(
436 "Error parsing providers for %s: %s",
437 k,
438 err)
439 }
440 }
441
399 result = append(result, &Module{ 442 result = append(result, &Module{
400 Name: k, 443 Name: k,
401 Source: source, 444 Source: source,
445 Version: version,
446 Providers: providers,
402 RawConfig: rawConfig, 447 RawConfig: rawConfig,
403 }) 448 })
404 } 449 }
@@ -406,6 +451,59 @@ func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) {
406 return result, nil 451 return result, nil
407} 452}
408 453
454// loadLocalsHcl recurses into the given HCL object turns it into
455// a list of locals.
456func loadLocalsHcl(list *ast.ObjectList) ([]*Local, error) {
457
458 result := make([]*Local, 0, len(list.Items))
459
460 for _, block := range list.Items {
461 if len(block.Keys) > 0 {
462 return nil, fmt.Errorf(
463 "locals block at %s should not have label %q",
464 block.Pos(), block.Keys[0].Token.Value(),
465 )
466 }
467
468 blockObj, ok := block.Val.(*ast.ObjectType)
469 if !ok {
470 return nil, fmt.Errorf("locals value at %s should be a block", block.Val.Pos())
471 }
472
473 // blockObj now contains directly our local decls
474 for _, item := range blockObj.List.Items {
475 if len(item.Keys) != 1 {
476 return nil, fmt.Errorf("local declaration at %s may not be a block", item.Val.Pos())
477 }
478
479 // By the time we get here there can only be one item left, but
480 // we'll decode into a map anyway because it's a convenient way
481 // to extract both the key and the value robustly.
482 kv := map[string]interface{}{}
483 hcl.DecodeObject(&kv, item)
484 for k, v := range kv {
485 rawConfig, err := NewRawConfig(map[string]interface{}{
486 "value": v,
487 })
488
489 if err != nil {
490 return nil, fmt.Errorf(
491 "error parsing local value %q at %s: %s",
492 k, item.Val.Pos(), err,
493 )
494 }
495
496 result = append(result, &Local{
497 Name: k,
498 RawConfig: rawConfig,
499 })
500 }
501 }
502 }
503
504 return result, nil
505}
506
409// LoadOutputsHcl recurses into the given HCL object and turns 507// LoadOutputsHcl recurses into the given HCL object and turns
410// it into a mapping of outputs. 508// it into a mapping of outputs.
411func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) { 509func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) {
@@ -434,6 +532,7 @@ func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) {
434 532
435 // Delete special keys 533 // Delete special keys
436 delete(config, "depends_on") 534 delete(config, "depends_on")
535 delete(config, "description")
437 536
438 rawConfig, err := NewRawConfig(config) 537 rawConfig, err := NewRawConfig(config)
439 if err != nil { 538 if err != nil {
@@ -455,10 +554,23 @@ func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) {
455 } 554 }
456 } 555 }
457 556
557 // If we have a description field, then filter that
558 var description string
559 if o := listVal.Filter("description"); len(o.Items) > 0 {
560 err := hcl.DecodeObject(&description, o.Items[0].Val)
561 if err != nil {
562 return nil, fmt.Errorf(
563 "Error reading description for output %q: %s",
564 n,
565 err)
566 }
567 }
568
458 result = append(result, &Output{ 569 result = append(result, &Output{
459 Name: n, 570 Name: n,
460 RawConfig: rawConfig, 571 RawConfig: rawConfig,
461 DependsOn: dependsOn, 572 DependsOn: dependsOn,
573 Description: description,
462 }) 574 })
463 } 575 }
464 576
diff --git a/vendor/github.com/hashicorp/terraform/config/loader_hcl2.go b/vendor/github.com/hashicorp/terraform/config/loader_hcl2.go
new file mode 100644
index 0000000..4f9f129
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/loader_hcl2.go
@@ -0,0 +1,473 @@
1package config
2
3import (
4 "fmt"
5 "sort"
6 "strings"
7
8 gohcl2 "github.com/hashicorp/hcl2/gohcl"
9 hcl2 "github.com/hashicorp/hcl2/hcl"
10 hcl2parse "github.com/hashicorp/hcl2/hclparse"
11 "github.com/hashicorp/terraform/config/hcl2shim"
12 "github.com/zclconf/go-cty/cty"
13)
14
15// hcl2Configurable is an implementation of configurable that knows
16// how to turn a HCL Body into a *Config object.
17type hcl2Configurable struct {
18 SourceFilename string
19 Body hcl2.Body
20}
21
22// hcl2Loader is a wrapper around a HCL parser that provides a fileLoaderFunc.
23type hcl2Loader struct {
24 Parser *hcl2parse.Parser
25}
26
27// For the moment we'll just have a global loader since we don't have anywhere
28// better to stash this.
29// TODO: refactor the loader API so that it uses some sort of object we can
30// stash the parser inside.
31var globalHCL2Loader = newHCL2Loader()
32
33// newHCL2Loader creates a new hcl2Loader containing a new HCL Parser.
34//
35// HCL parsers retain information about files that are loaded to aid in
36// producing diagnostic messages, so all files within a single configuration
37// should be loaded with the same parser to ensure the availability of
38// full diagnostic information.
39func newHCL2Loader() hcl2Loader {
40 return hcl2Loader{
41 Parser: hcl2parse.NewParser(),
42 }
43}
44
45// loadFile is a fileLoaderFunc that knows how to read a HCL2 file and turn it
46// into a hcl2Configurable.
47func (l hcl2Loader) loadFile(filename string) (configurable, []string, error) {
48 var f *hcl2.File
49 var diags hcl2.Diagnostics
50 if strings.HasSuffix(filename, ".json") {
51 f, diags = l.Parser.ParseJSONFile(filename)
52 } else {
53 f, diags = l.Parser.ParseHCLFile(filename)
54 }
55 if diags.HasErrors() {
56 // Return diagnostics as an error; callers may type-assert this to
57 // recover the original diagnostics, if it doesn't end up wrapped
58 // in another error.
59 return nil, nil, diags
60 }
61
62 return &hcl2Configurable{
63 SourceFilename: filename,
64 Body: f.Body,
65 }, nil, nil
66}
67
68func (t *hcl2Configurable) Config() (*Config, error) {
69 config := &Config{}
70
71 // these structs are used only for the initial shallow decoding; we'll
72 // expand this into the main, public-facing config structs afterwards.
73 type atlas struct {
74 Name string `hcl:"name"`
75 Include *[]string `hcl:"include"`
76 Exclude *[]string `hcl:"exclude"`
77 }
78 type provider struct {
79 Name string `hcl:"name,label"`
80 Alias *string `hcl:"alias,attr"`
81 Version *string `hcl:"version,attr"`
82 Config hcl2.Body `hcl:",remain"`
83 }
84 type module struct {
85 Name string `hcl:"name,label"`
86 Source string `hcl:"source,attr"`
87 Version *string `hcl:"version,attr"`
88 Providers *map[string]string `hcl:"providers,attr"`
89 Config hcl2.Body `hcl:",remain"`
90 }
91 type resourceLifecycle struct {
92 CreateBeforeDestroy *bool `hcl:"create_before_destroy,attr"`
93 PreventDestroy *bool `hcl:"prevent_destroy,attr"`
94 IgnoreChanges *[]string `hcl:"ignore_changes,attr"`
95 }
96 type connection struct {
97 Config hcl2.Body `hcl:",remain"`
98 }
99 type provisioner struct {
100 Type string `hcl:"type,label"`
101
102 When *string `hcl:"when,attr"`
103 OnFailure *string `hcl:"on_failure,attr"`
104
105 Connection *connection `hcl:"connection,block"`
106 Config hcl2.Body `hcl:",remain"`
107 }
108 type managedResource struct {
109 Type string `hcl:"type,label"`
110 Name string `hcl:"name,label"`
111
112 CountExpr hcl2.Expression `hcl:"count,attr"`
113 Provider *string `hcl:"provider,attr"`
114 DependsOn *[]string `hcl:"depends_on,attr"`
115
116 Lifecycle *resourceLifecycle `hcl:"lifecycle,block"`
117 Provisioners []provisioner `hcl:"provisioner,block"`
118 Connection *connection `hcl:"connection,block"`
119
120 Config hcl2.Body `hcl:",remain"`
121 }
122 type dataResource struct {
123 Type string `hcl:"type,label"`
124 Name string `hcl:"name,label"`
125
126 CountExpr hcl2.Expression `hcl:"count,attr"`
127 Provider *string `hcl:"provider,attr"`
128 DependsOn *[]string `hcl:"depends_on,attr"`
129
130 Config hcl2.Body `hcl:",remain"`
131 }
132 type variable struct {
133 Name string `hcl:"name,label"`
134
135 DeclaredType *string `hcl:"type,attr"`
136 Default *cty.Value `hcl:"default,attr"`
137 Description *string `hcl:"description,attr"`
138 Sensitive *bool `hcl:"sensitive,attr"`
139 }
140 type output struct {
141 Name string `hcl:"name,label"`
142
143 ValueExpr hcl2.Expression `hcl:"value,attr"`
144 DependsOn *[]string `hcl:"depends_on,attr"`
145 Description *string `hcl:"description,attr"`
146 Sensitive *bool `hcl:"sensitive,attr"`
147 }
148 type locals struct {
149 Definitions hcl2.Attributes `hcl:",remain"`
150 }
151 type backend struct {
152 Type string `hcl:"type,label"`
153 Config hcl2.Body `hcl:",remain"`
154 }
155 type terraform struct {
156 RequiredVersion *string `hcl:"required_version,attr"`
157 Backend *backend `hcl:"backend,block"`
158 }
159 type topLevel struct {
160 Atlas *atlas `hcl:"atlas,block"`
161 Datas []dataResource `hcl:"data,block"`
162 Modules []module `hcl:"module,block"`
163 Outputs []output `hcl:"output,block"`
164 Providers []provider `hcl:"provider,block"`
165 Resources []managedResource `hcl:"resource,block"`
166 Terraform *terraform `hcl:"terraform,block"`
167 Variables []variable `hcl:"variable,block"`
168 Locals []*locals `hcl:"locals,block"`
169 }
170
171 var raw topLevel
172 diags := gohcl2.DecodeBody(t.Body, nil, &raw)
173 if diags.HasErrors() {
174 // Do some minimal decoding to see if we can at least get the
175 // required Terraform version, which might help explain why we
176 // couldn't parse the rest.
177 if raw.Terraform != nil && raw.Terraform.RequiredVersion != nil {
178 config.Terraform = &Terraform{
179 RequiredVersion: *raw.Terraform.RequiredVersion,
180 }
181 }
182
183 // We return the diags as an implementation of error, which the
184 // caller than then type-assert if desired to recover the individual
185 // diagnostics.
186 // FIXME: The current API gives us no way to return warnings in the
187 // absense of any errors.
188 return config, diags
189 }
190
191 if raw.Terraform != nil {
192 var reqdVersion string
193 var backend *Backend
194
195 if raw.Terraform.RequiredVersion != nil {
196 reqdVersion = *raw.Terraform.RequiredVersion
197 }
198 if raw.Terraform.Backend != nil {
199 backend = new(Backend)
200 backend.Type = raw.Terraform.Backend.Type
201
202 // We don't permit interpolations or nested blocks inside the
203 // backend config, so we can decode the config early here and
204 // get direct access to the values, which is important for the
205 // config hashing to work as expected.
206 var config map[string]string
207 configDiags := gohcl2.DecodeBody(raw.Terraform.Backend.Config, nil, &config)
208 diags = append(diags, configDiags...)
209
210 raw := make(map[string]interface{}, len(config))
211 for k, v := range config {
212 raw[k] = v
213 }
214
215 var err error
216 backend.RawConfig, err = NewRawConfig(raw)
217 if err != nil {
218 diags = append(diags, &hcl2.Diagnostic{
219 Severity: hcl2.DiagError,
220 Summary: "Invalid backend configuration",
221 Detail: fmt.Sprintf("Error in backend configuration: %s", err),
222 })
223 }
224 }
225
226 config.Terraform = &Terraform{
227 RequiredVersion: reqdVersion,
228 Backend: backend,
229 }
230 }
231
232 if raw.Atlas != nil {
233 var include, exclude []string
234 if raw.Atlas.Include != nil {
235 include = *raw.Atlas.Include
236 }
237 if raw.Atlas.Exclude != nil {
238 exclude = *raw.Atlas.Exclude
239 }
240 config.Atlas = &AtlasConfig{
241 Name: raw.Atlas.Name,
242 Include: include,
243 Exclude: exclude,
244 }
245 }
246
247 for _, rawM := range raw.Modules {
248 m := &Module{
249 Name: rawM.Name,
250 Source: rawM.Source,
251 RawConfig: NewRawConfigHCL2(rawM.Config),
252 }
253
254 if rawM.Version != nil {
255 m.Version = *rawM.Version
256 }
257
258 if rawM.Providers != nil {
259 m.Providers = *rawM.Providers
260 }
261
262 config.Modules = append(config.Modules, m)
263 }
264
265 for _, rawV := range raw.Variables {
266 v := &Variable{
267 Name: rawV.Name,
268 }
269 if rawV.DeclaredType != nil {
270 v.DeclaredType = *rawV.DeclaredType
271 }
272 if rawV.Default != nil {
273 v.Default = hcl2shim.ConfigValueFromHCL2(*rawV.Default)
274 }
275 if rawV.Description != nil {
276 v.Description = *rawV.Description
277 }
278
279 config.Variables = append(config.Variables, v)
280 }
281
282 for _, rawO := range raw.Outputs {
283 o := &Output{
284 Name: rawO.Name,
285 }
286
287 if rawO.Description != nil {
288 o.Description = *rawO.Description
289 }
290 if rawO.DependsOn != nil {
291 o.DependsOn = *rawO.DependsOn
292 }
293 if rawO.Sensitive != nil {
294 o.Sensitive = *rawO.Sensitive
295 }
296
297 // The result is expected to be a map like map[string]interface{}{"value": something},
298 // so we'll fake that with our hcl2shim.SingleAttrBody shim.
299 o.RawConfig = NewRawConfigHCL2(hcl2shim.SingleAttrBody{
300 Name: "value",
301 Expr: rawO.ValueExpr,
302 })
303
304 config.Outputs = append(config.Outputs, o)
305 }
306
307 for _, rawR := range raw.Resources {
308 r := &Resource{
309 Mode: ManagedResourceMode,
310 Type: rawR.Type,
311 Name: rawR.Name,
312 }
313 if rawR.Lifecycle != nil {
314 var l ResourceLifecycle
315 if rawR.Lifecycle.CreateBeforeDestroy != nil {
316 l.CreateBeforeDestroy = *rawR.Lifecycle.CreateBeforeDestroy
317 }
318 if rawR.Lifecycle.PreventDestroy != nil {
319 l.PreventDestroy = *rawR.Lifecycle.PreventDestroy
320 }
321 if rawR.Lifecycle.IgnoreChanges != nil {
322 l.IgnoreChanges = *rawR.Lifecycle.IgnoreChanges
323 }
324 r.Lifecycle = l
325 }
326 if rawR.Provider != nil {
327 r.Provider = *rawR.Provider
328 }
329 if rawR.DependsOn != nil {
330 r.DependsOn = *rawR.DependsOn
331 }
332
333 var defaultConnInfo *RawConfig
334 if rawR.Connection != nil {
335 defaultConnInfo = NewRawConfigHCL2(rawR.Connection.Config)
336 }
337
338 for _, rawP := range rawR.Provisioners {
339 p := &Provisioner{
340 Type: rawP.Type,
341 }
342
343 switch {
344 case rawP.When == nil:
345 p.When = ProvisionerWhenCreate
346 case *rawP.When == "create":
347 p.When = ProvisionerWhenCreate
348 case *rawP.When == "destroy":
349 p.When = ProvisionerWhenDestroy
350 default:
351 p.When = ProvisionerWhenInvalid
352 }
353
354 switch {
355 case rawP.OnFailure == nil:
356 p.OnFailure = ProvisionerOnFailureFail
357 case *rawP.When == "fail":
358 p.OnFailure = ProvisionerOnFailureFail
359 case *rawP.When == "continue":
360 p.OnFailure = ProvisionerOnFailureContinue
361 default:
362 p.OnFailure = ProvisionerOnFailureInvalid
363 }
364
365 if rawP.Connection != nil {
366 p.ConnInfo = NewRawConfigHCL2(rawP.Connection.Config)
367 } else {
368 p.ConnInfo = defaultConnInfo
369 }
370
371 p.RawConfig = NewRawConfigHCL2(rawP.Config)
372
373 r.Provisioners = append(r.Provisioners, p)
374 }
375
376 // The old loader records the count expression as a weird RawConfig with
377 // a single-element map inside. Since the rest of the world is assuming
378 // that, we'll mimic it here.
379 {
380 countBody := hcl2shim.SingleAttrBody{
381 Name: "count",
382 Expr: rawR.CountExpr,
383 }
384
385 r.RawCount = NewRawConfigHCL2(countBody)
386 r.RawCount.Key = "count"
387 }
388
389 r.RawConfig = NewRawConfigHCL2(rawR.Config)
390
391 config.Resources = append(config.Resources, r)
392
393 }
394
395 for _, rawR := range raw.Datas {
396 r := &Resource{
397 Mode: DataResourceMode,
398 Type: rawR.Type,
399 Name: rawR.Name,
400 }
401
402 if rawR.Provider != nil {
403 r.Provider = *rawR.Provider
404 }
405 if rawR.DependsOn != nil {
406 r.DependsOn = *rawR.DependsOn
407 }
408
409 // The old loader records the count expression as a weird RawConfig with
410 // a single-element map inside. Since the rest of the world is assuming
411 // that, we'll mimic it here.
412 {
413 countBody := hcl2shim.SingleAttrBody{
414 Name: "count",
415 Expr: rawR.CountExpr,
416 }
417
418 r.RawCount = NewRawConfigHCL2(countBody)
419 r.RawCount.Key = "count"
420 }
421
422 r.RawConfig = NewRawConfigHCL2(rawR.Config)
423
424 config.Resources = append(config.Resources, r)
425 }
426
427 for _, rawP := range raw.Providers {
428 p := &ProviderConfig{
429 Name: rawP.Name,
430 }
431
432 if rawP.Alias != nil {
433 p.Alias = *rawP.Alias
434 }
435 if rawP.Version != nil {
436 p.Version = *rawP.Version
437 }
438
439 // The result is expected to be a map like map[string]interface{}{"value": something},
440 // so we'll fake that with our hcl2shim.SingleAttrBody shim.
441 p.RawConfig = NewRawConfigHCL2(rawP.Config)
442
443 config.ProviderConfigs = append(config.ProviderConfigs, p)
444 }
445
446 for _, rawL := range raw.Locals {
447 names := make([]string, 0, len(rawL.Definitions))
448 for n := range rawL.Definitions {
449 names = append(names, n)
450 }
451 sort.Strings(names)
452 for _, n := range names {
453 attr := rawL.Definitions[n]
454 l := &Local{
455 Name: n,
456 RawConfig: NewRawConfigHCL2(hcl2shim.SingleAttrBody{
457 Name: "value",
458 Expr: attr.Expr,
459 }),
460 }
461 config.Locals = append(config.Locals, l)
462 }
463 }
464
465 // FIXME: The current API gives us no way to return warnings in the
466 // absense of any errors.
467 var err error
468 if diags.HasErrors() {
469 err = diags
470 }
471
472 return config, err
473}
diff --git a/vendor/github.com/hashicorp/terraform/config/merge.go b/vendor/github.com/hashicorp/terraform/config/merge.go
index db214be..55fc864 100644
--- a/vendor/github.com/hashicorp/terraform/config/merge.go
+++ b/vendor/github.com/hashicorp/terraform/config/merge.go
@@ -137,6 +137,17 @@ func Merge(c1, c2 *Config) (*Config, error) {
137 } 137 }
138 } 138 }
139 139
140 // Local Values
141 // These are simpler than the other config elements because they are just
142 // flat values and so no deep merging is required.
143 if localsCount := len(c1.Locals) + len(c2.Locals); localsCount != 0 {
144 // Explicit length check above because we want c.Locals to remain
145 // nil if the result would be empty.
146 c.Locals = make([]*Local, 0, len(c1.Locals)+len(c2.Locals))
147 c.Locals = append(c.Locals, c1.Locals...)
148 c.Locals = append(c.Locals, c2.Locals...)
149 }
150
140 return c, nil 151 return c, nil
141} 152}
142 153
diff --git a/vendor/github.com/hashicorp/terraform/config/module/get.go b/vendor/github.com/hashicorp/terraform/config/module/get.go
index 96b4a63..5073d0d 100644
--- a/vendor/github.com/hashicorp/terraform/config/module/get.go
+++ b/vendor/github.com/hashicorp/terraform/config/module/get.go
@@ -3,6 +3,7 @@ package module
3import ( 3import (
4 "io/ioutil" 4 "io/ioutil"
5 "os" 5 "os"
6 "path/filepath"
6 7
7 "github.com/hashicorp/go-getter" 8 "github.com/hashicorp/go-getter"
8) 9)
@@ -37,13 +38,10 @@ func GetCopy(dst, src string) error {
37 if err != nil { 38 if err != nil {
38 return err 39 return err
39 } 40 }
40 // FIXME: This isn't completely safe. Creating and removing our temp path
41 // exposes where to race to inject files.
42 if err := os.RemoveAll(tmpDir); err != nil {
43 return err
44 }
45 defer os.RemoveAll(tmpDir) 41 defer os.RemoveAll(tmpDir)
46 42
43 tmpDir = filepath.Join(tmpDir, "module")
44
47 // Get to that temporary dir 45 // Get to that temporary dir
48 if err := getter.Get(tmpDir, src); err != nil { 46 if err := getter.Get(tmpDir, src); err != nil {
49 return err 47 return err
@@ -57,15 +55,3 @@ func GetCopy(dst, src string) error {
57 // Copy to the final location 55 // Copy to the final location
58 return copyDir(dst, tmpDir) 56 return copyDir(dst, tmpDir)
59} 57}
60
61func getStorage(s getter.Storage, key string, src string, mode GetMode) (string, bool, error) {
62 // Get the module with the level specified if we were told to.
63 if mode > GetModeNone {
64 if err := s.Get(key, src, mode == GetModeUpdate); err != nil {
65 return "", false, err
66 }
67 }
68
69 // Get the directory where the module is.
70 return s.Dir(key)
71}
diff --git a/vendor/github.com/hashicorp/terraform/config/module/inode.go b/vendor/github.com/hashicorp/terraform/config/module/inode.go
index 8603ee2..da520ab 100644
--- a/vendor/github.com/hashicorp/terraform/config/module/inode.go
+++ b/vendor/github.com/hashicorp/terraform/config/module/inode.go
@@ -1,4 +1,4 @@
1// +build linux darwin openbsd netbsd solaris 1// +build linux darwin openbsd netbsd solaris dragonfly
2 2
3package module 3package module
4 4
diff --git a/vendor/github.com/hashicorp/terraform/config/module/module.go b/vendor/github.com/hashicorp/terraform/config/module/module.go
index f8649f6..7dc8fcc 100644
--- a/vendor/github.com/hashicorp/terraform/config/module/module.go
+++ b/vendor/github.com/hashicorp/terraform/config/module/module.go
@@ -2,6 +2,8 @@ package module
2 2
3// Module represents the metadata for a single module. 3// Module represents the metadata for a single module.
4type Module struct { 4type Module struct {
5 Name string 5 Name string
6 Source string 6 Source string
7 Version string
8 Providers map[string]string
7} 9}
diff --git a/vendor/github.com/hashicorp/terraform/config/module/storage.go b/vendor/github.com/hashicorp/terraform/config/module/storage.go
new file mode 100644
index 0000000..58e3a10
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/module/storage.go
@@ -0,0 +1,365 @@
1package module
2
3import (
4 "encoding/json"
5 "fmt"
6 "io/ioutil"
7 "log"
8 "os"
9 "path/filepath"
10 "strings"
11
12 getter "github.com/hashicorp/go-getter"
13 "github.com/hashicorp/terraform/registry"
14 "github.com/hashicorp/terraform/registry/regsrc"
15 "github.com/hashicorp/terraform/svchost/disco"
16 "github.com/mitchellh/cli"
17)
18
19const manifestName = "modules.json"
20
21// moduleManifest is the serialization structure used to record the stored
22// module's metadata.
23type moduleManifest struct {
24 Modules []moduleRecord
25}
26
27// moduleRecords represents the stored module's metadata.
28// This is compared for equality using '==', so all fields needs to remain
29// comparable.
30type moduleRecord struct {
31 // Source is the module source string from the config, minus any
32 // subdirectory.
33 Source string
34
35 // Key is the locally unique identifier for this module.
36 Key string
37
38 // Version is the exact version string for the stored module.
39 Version string
40
41 // Dir is the directory name returned by the FileStorage. This is what
42 // allows us to correlate a particular module version with the location on
43 // disk.
44 Dir string
45
46 // Root is the root directory containing the module. If the module is
47 // unpacked from an archive, and not located in the root directory, this is
48 // used to direct the loader to the correct subdirectory. This is
49 // independent from any subdirectory in the original source string, which
50 // may traverse further into the module tree.
51 Root string
52
53 // url is the location of the module source
54 url string
55
56 // Registry is true if this module is sourced from a registry
57 registry bool
58}
59
60// Storage implements methods to manage the storage of modules.
61// This is used by Tree.Load to query registries, authenticate requests, and
62// store modules locally.
63type Storage struct {
64 // StorageDir is the full path to the directory where all modules will be
65 // stored.
66 StorageDir string
67
68 // Ui is an optional cli.Ui for user output
69 Ui cli.Ui
70
71 // Mode is the GetMode that will be used for various operations.
72 Mode GetMode
73
74 registry *registry.Client
75}
76
77// NewStorage returns a new initialized Storage object.
78func NewStorage(dir string, services *disco.Disco) *Storage {
79 regClient := registry.NewClient(services, nil)
80
81 return &Storage{
82 StorageDir: dir,
83 registry: regClient,
84 }
85}
86
87// loadManifest returns the moduleManifest file from the parent directory.
88func (s Storage) loadManifest() (moduleManifest, error) {
89 manifest := moduleManifest{}
90
91 manifestPath := filepath.Join(s.StorageDir, manifestName)
92 data, err := ioutil.ReadFile(manifestPath)
93 if err != nil && !os.IsNotExist(err) {
94 return manifest, err
95 }
96
97 if len(data) == 0 {
98 return manifest, nil
99 }
100
101 if err := json.Unmarshal(data, &manifest); err != nil {
102 return manifest, err
103 }
104
105 for i, rec := range manifest.Modules {
106 // If the path was recorded before we changed to always using a
107 // slash as separator, we delete the record from the manifest so
108 // it can be discovered again and will be recorded using a slash.
109 if strings.Contains(rec.Dir, "\\") {
110 manifest.Modules[i] = manifest.Modules[len(manifest.Modules)-1]
111 manifest.Modules = manifest.Modules[:len(manifest.Modules)-1]
112 continue
113 }
114
115 // Make sure we use the correct path separator.
116 rec.Dir = filepath.FromSlash(rec.Dir)
117 }
118
119 return manifest, nil
120}
121
122// Store the location of the module, along with the version used and the module
123// root directory. The storage method loads the entire file and rewrites it
124// each time. This is only done a few times during init, so efficiency is
125// not a concern.
126func (s Storage) recordModule(rec moduleRecord) error {
127 manifest, err := s.loadManifest()
128 if err != nil {
129 // if there was a problem with the file, we will attempt to write a new
130 // one. Any non-data related error should surface there.
131 log.Printf("[WARN] error reading module manifest: %s", err)
132 }
133
134 // do nothing if we already have the exact module
135 for i, stored := range manifest.Modules {
136 if rec == stored {
137 return nil
138 }
139
140 // they are not equal, but if the storage path is the same we need to
141 // remove this rec to be replaced.
142 if rec.Dir == stored.Dir {
143 manifest.Modules[i] = manifest.Modules[len(manifest.Modules)-1]
144 manifest.Modules = manifest.Modules[:len(manifest.Modules)-1]
145 break
146 }
147 }
148
149 // Make sure we always use a slash separator.
150 rec.Dir = filepath.ToSlash(rec.Dir)
151
152 manifest.Modules = append(manifest.Modules, rec)
153
154 js, err := json.Marshal(manifest)
155 if err != nil {
156 panic(err)
157 }
158
159 manifestPath := filepath.Join(s.StorageDir, manifestName)
160 return ioutil.WriteFile(manifestPath, js, 0644)
161}
162
163// load the manifest from dir, and return all module versions matching the
164// provided source. Records with no version info will be skipped, as they need
165// to be uniquely identified by other means.
166func (s Storage) moduleVersions(source string) ([]moduleRecord, error) {
167 manifest, err := s.loadManifest()
168 if err != nil {
169 return manifest.Modules, err
170 }
171
172 var matching []moduleRecord
173
174 for _, m := range manifest.Modules {
175 if m.Source == source && m.Version != "" {
176 log.Printf("[DEBUG] found local version %q for module %s", m.Version, m.Source)
177 matching = append(matching, m)
178 }
179 }
180
181 return matching, nil
182}
183
184func (s Storage) moduleDir(key string) (string, error) {
185 manifest, err := s.loadManifest()
186 if err != nil {
187 return "", err
188 }
189
190 for _, m := range manifest.Modules {
191 if m.Key == key {
192 return m.Dir, nil
193 }
194 }
195
196 return "", nil
197}
198
199// return only the root directory of the module stored in dir.
200func (s Storage) getModuleRoot(dir string) (string, error) {
201 manifest, err := s.loadManifest()
202 if err != nil {
203 return "", err
204 }
205
206 for _, mod := range manifest.Modules {
207 if mod.Dir == dir {
208 return mod.Root, nil
209 }
210 }
211 return "", nil
212}
213
214// record only the Root directory for the module stored at dir.
215func (s Storage) recordModuleRoot(dir, root string) error {
216 rec := moduleRecord{
217 Dir: dir,
218 Root: root,
219 }
220
221 return s.recordModule(rec)
222}
223
224func (s Storage) output(msg string) {
225 if s.Ui == nil || s.Mode == GetModeNone {
226 return
227 }
228 s.Ui.Output(msg)
229}
230
231func (s Storage) getStorage(key string, src string) (string, bool, error) {
232 storage := &getter.FolderStorage{
233 StorageDir: s.StorageDir,
234 }
235
236 log.Printf("[DEBUG] fetching module from %s", src)
237
238 // Get the module with the level specified if we were told to.
239 if s.Mode > GetModeNone {
240 log.Printf("[DEBUG] fetching %q with key %q", src, key)
241 if err := storage.Get(key, src, s.Mode == GetModeUpdate); err != nil {
242 return "", false, err
243 }
244 }
245
246 // Get the directory where the module is.
247 dir, found, err := storage.Dir(key)
248 log.Printf("[DEBUG] found %q in %q: %t", src, dir, found)
249 return dir, found, err
250}
251
252// find a stored module that's not from a registry
253func (s Storage) findModule(key string) (string, error) {
254 if s.Mode == GetModeUpdate {
255 return "", nil
256 }
257
258 return s.moduleDir(key)
259}
260
261// GetModule fetches a module source into the specified directory. This is used
262// as a convenience function by the CLI to initialize a configuration.
263func (s Storage) GetModule(dst, src string) error {
264 // reset this in case the caller was going to re-use it
265 mode := s.Mode
266 s.Mode = GetModeUpdate
267 defer func() {
268 s.Mode = mode
269 }()
270
271 rec, err := s.findRegistryModule(src, anyVersion)
272 if err != nil {
273 return err
274 }
275
276 pwd, err := os.Getwd()
277 if err != nil {
278 return err
279 }
280
281 source := rec.url
282 if source == "" {
283 source, err = getter.Detect(src, pwd, getter.Detectors)
284 if err != nil {
285 return fmt.Errorf("module %s: %s", src, err)
286 }
287 }
288
289 if source == "" {
290 return fmt.Errorf("module %q not found", src)
291 }
292
293 return GetCopy(dst, source)
294}
295
296// find a registry module
297func (s Storage) findRegistryModule(mSource, constraint string) (moduleRecord, error) {
298 rec := moduleRecord{
299 Source: mSource,
300 }
301 // detect if we have a registry source
302 mod, err := regsrc.ParseModuleSource(mSource)
303 switch err {
304 case nil:
305 //ok
306 case regsrc.ErrInvalidModuleSource:
307 return rec, nil
308 default:
309 return rec, err
310 }
311 rec.registry = true
312
313 log.Printf("[TRACE] %q is a registry module", mod.Display())
314
315 versions, err := s.moduleVersions(mod.String())
316 if err != nil {
317 log.Printf("[ERROR] error looking up versions for %q: %s", mod.Display(), err)
318 return rec, err
319 }
320
321 match, err := newestRecord(versions, constraint)
322 if err != nil {
323 log.Printf("[INFO] no matching version for %q<%s>, %s", mod.Display(), constraint, err)
324 }
325 log.Printf("[DEBUG] matched %q version %s for %s", mod, match.Version, constraint)
326
327 rec.Dir = match.Dir
328 rec.Version = match.Version
329 found := rec.Dir != ""
330
331 // we need to lookup available versions
332 // Only on Get if it's not found, on unconditionally on Update
333 if (s.Mode == GetModeGet && !found) || (s.Mode == GetModeUpdate) {
334 resp, err := s.registry.Versions(mod)
335 if err != nil {
336 return rec, err
337 }
338
339 if len(resp.Modules) == 0 {
340 return rec, fmt.Errorf("module %q not found in registry", mod.Display())
341 }
342
343 match, err := newestVersion(resp.Modules[0].Versions, constraint)
344 if err != nil {
345 return rec, err
346 }
347
348 if match == nil {
349 return rec, fmt.Errorf("no versions for %q found matching %q", mod.Display(), constraint)
350 }
351
352 rec.Version = match.Version
353
354 rec.url, err = s.registry.Location(mod, rec.Version)
355 if err != nil {
356 return rec, err
357 }
358
359 // we've already validated this by now
360 host, _ := mod.SvcHost()
361 s.output(fmt.Sprintf(" Found version %s of %s on %s", rec.Version, mod.Module(), host.ForDisplay()))
362
363 }
364 return rec, nil
365}
diff --git a/vendor/github.com/hashicorp/terraform/config/module/testing.go b/vendor/github.com/hashicorp/terraform/config/module/testing.go
index fc9e733..6f1ff05 100644
--- a/vendor/github.com/hashicorp/terraform/config/module/testing.go
+++ b/vendor/github.com/hashicorp/terraform/config/module/testing.go
@@ -4,8 +4,6 @@ import (
4 "io/ioutil" 4 "io/ioutil"
5 "os" 5 "os"
6 "testing" 6 "testing"
7
8 "github.com/hashicorp/go-getter"
9) 7)
10 8
11// TestTree loads a module at the given path and returns the tree as well 9// TestTree loads a module at the given path and returns the tree as well
@@ -26,8 +24,8 @@ func TestTree(t *testing.T, path string) (*Tree, func()) {
26 } 24 }
27 25
28 // Get the child modules 26 // Get the child modules
29 s := &getter.FolderStorage{StorageDir: dir} 27 s := &Storage{StorageDir: dir, Mode: GetModeGet}
30 if err := mod.Load(s, GetModeGet); err != nil { 28 if err := mod.Load(s); err != nil {
31 t.Fatalf("err: %s", err) 29 t.Fatalf("err: %s", err)
32 return nil, nil 30 return nil, nil
33 } 31 }
diff --git a/vendor/github.com/hashicorp/terraform/config/module/tree.go b/vendor/github.com/hashicorp/terraform/config/module/tree.go
index 4b0b153..f56d69b 100644
--- a/vendor/github.com/hashicorp/terraform/config/module/tree.go
+++ b/vendor/github.com/hashicorp/terraform/config/module/tree.go
@@ -4,11 +4,14 @@ import (
4 "bufio" 4 "bufio"
5 "bytes" 5 "bytes"
6 "fmt" 6 "fmt"
7 "log"
7 "path/filepath" 8 "path/filepath"
8 "strings" 9 "strings"
9 "sync" 10 "sync"
10 11
11 "github.com/hashicorp/go-getter" 12 "github.com/hashicorp/terraform/tfdiags"
13
14 getter "github.com/hashicorp/go-getter"
12 "github.com/hashicorp/terraform/config" 15 "github.com/hashicorp/terraform/config"
13) 16)
14 17
@@ -26,6 +29,17 @@ type Tree struct {
26 children map[string]*Tree 29 children map[string]*Tree
27 path []string 30 path []string
28 lock sync.RWMutex 31 lock sync.RWMutex
32
33 // version is the final version of the config loaded for the Tree's module
34 version string
35 // source is the "source" string used to load this module. It's possible
36 // for a module source to change, but the path remains the same, preventing
37 // it from being reloaded.
38 source string
39 // parent allows us to walk back up the tree and determine if there are any
40 // versioned ancestor modules which may effect the stored location of
41 // submodules
42 parent *Tree
29} 43}
30 44
31// NewTree returns a new Tree for the given config structure. 45// NewTree returns a new Tree for the given config structure.
@@ -40,7 +54,7 @@ func NewEmptyTree() *Tree {
40 // We do this dummy load so that the tree is marked as "loaded". It 54 // We do this dummy load so that the tree is marked as "loaded". It
41 // should never fail because this is just about a no-op. If it does fail 55 // should never fail because this is just about a no-op. If it does fail
42 // we panic so we can know its a bug. 56 // we panic so we can know its a bug.
43 if err := t.Load(nil, GetModeGet); err != nil { 57 if err := t.Load(&Storage{Mode: GetModeGet}); err != nil {
44 panic(err) 58 panic(err)
45 } 59 }
46 60
@@ -126,8 +140,10 @@ func (t *Tree) Modules() []*Module {
126 result := make([]*Module, len(t.config.Modules)) 140 result := make([]*Module, len(t.config.Modules))
127 for i, m := range t.config.Modules { 141 for i, m := range t.config.Modules {
128 result[i] = &Module{ 142 result[i] = &Module{
129 Name: m.Name, 143 Name: m.Name,
130 Source: m.Source, 144 Version: m.Version,
145 Source: m.Source,
146 Providers: m.Providers,
131 } 147 }
132 } 148 }
133 149
@@ -155,81 +171,178 @@ func (t *Tree) Name() string {
155// module trees inherently require the configuration to be in a reasonably 171// module trees inherently require the configuration to be in a reasonably
156// sane state: no circular dependencies, proper module sources, etc. A full 172// sane state: no circular dependencies, proper module sources, etc. A full
157// suite of validations can be done by running Validate (after loading). 173// suite of validations can be done by running Validate (after loading).
158func (t *Tree) Load(s getter.Storage, mode GetMode) error { 174func (t *Tree) Load(s *Storage) error {
159 t.lock.Lock() 175 t.lock.Lock()
160 defer t.lock.Unlock() 176 defer t.lock.Unlock()
161 177
162 // Reset the children if we have any 178 children, err := t.getChildren(s)
163 t.children = nil 179 if err != nil {
180 return err
181 }
182
183 // Go through all the children and load them.
184 for _, c := range children {
185 if err := c.Load(s); err != nil {
186 return err
187 }
188 }
189
190 // Set our tree up
191 t.children = children
164 192
165 modules := t.Modules() 193 return nil
194}
195
196func (t *Tree) getChildren(s *Storage) (map[string]*Tree, error) {
166 children := make(map[string]*Tree) 197 children := make(map[string]*Tree)
167 198
168 // Go through all the modules and get the directory for them. 199 // Go through all the modules and get the directory for them.
169 for _, m := range modules { 200 for _, m := range t.Modules() {
170 if _, ok := children[m.Name]; ok { 201 if _, ok := children[m.Name]; ok {
171 return fmt.Errorf( 202 return nil, fmt.Errorf(
172 "module %s: duplicated. module names must be unique", m.Name) 203 "module %s: duplicated. module names must be unique", m.Name)
173 } 204 }
174 205
175 // Determine the path to this child 206 // Determine the path to this child
176 path := make([]string, len(t.path), len(t.path)+1) 207 modPath := make([]string, len(t.path), len(t.path)+1)
177 copy(path, t.path) 208 copy(modPath, t.path)
178 path = append(path, m.Name) 209 modPath = append(modPath, m.Name)
179 210
180 // Split out the subdir if we have one 211 log.Printf("[TRACE] module source: %q", m.Source)
181 source, subDir := getter.SourceDirSubdir(m.Source)
182 212
183 source, err := getter.Detect(source, t.config.Dir, getter.Detectors) 213 // add the module path to help indicate where modules with relative
214 // paths are being loaded from
215 s.output(fmt.Sprintf("- module.%s", strings.Join(modPath, ".")))
216
217 // Lookup the local location of the module.
218 // dir is the local directory where the module is stored
219 mod, err := s.findRegistryModule(m.Source, m.Version)
184 if err != nil { 220 if err != nil {
185 return fmt.Errorf("module %s: %s", m.Name, err) 221 return nil, err
186 } 222 }
187 223
224 // The key is the string that will be used to uniquely id the Source in
225 // the local storage. The prefix digit can be incremented to
226 // invalidate the local module storage.
227 key := "1." + t.versionedPathKey(m)
228 if mod.Version != "" {
229 key += "." + mod.Version
230 }
231
232 // Check for the exact key if it's not a registry module
233 if !mod.registry {
234 mod.Dir, err = s.findModule(key)
235 if err != nil {
236 return nil, err
237 }
238 }
239
240 if mod.Dir != "" && s.Mode != GetModeUpdate {
241 // We found it locally, but in order to load the Tree we need to
242 // find out if there was another subDir stored from detection.
243 subDir, err := s.getModuleRoot(mod.Dir)
244 if err != nil {
245 // If there's a problem with the subdir record, we'll let the
246 // recordSubdir method fix it up. Any other filesystem errors
247 // will turn up again below.
248 log.Println("[WARN] error reading subdir record:", err)
249 }
250
251 fullDir := filepath.Join(mod.Dir, subDir)
252
253 child, err := NewTreeModule(m.Name, fullDir)
254 if err != nil {
255 return nil, fmt.Errorf("module %s: %s", m.Name, err)
256 }
257 child.path = modPath
258 child.parent = t
259 child.version = mod.Version
260 child.source = m.Source
261 children[m.Name] = child
262 continue
263 }
264
265 // Split out the subdir if we have one.
266 // Terraform keeps the entire requested tree, so that modules can
267 // reference sibling modules from the same archive or repo.
268 rawSource, subDir := getter.SourceDirSubdir(m.Source)
269
270 // we haven't found a source, so fallback to the go-getter detectors
271 source := mod.url
272 if source == "" {
273 source, err = getter.Detect(rawSource, t.config.Dir, getter.Detectors)
274 if err != nil {
275 return nil, fmt.Errorf("module %s: %s", m.Name, err)
276 }
277 }
278
279 log.Printf("[TRACE] detected module source %q", source)
280
188 // Check if the detector introduced something new. 281 // Check if the detector introduced something new.
189 source, subDir2 := getter.SourceDirSubdir(source) 282 // For example, the registry always adds a subdir of `//*`,
190 if subDir2 != "" { 283 // indicating that we need to strip off the first component from the
191 subDir = filepath.Join(subDir2, subDir) 284 // tar archive, though we may not yet know what it is called.
285 source, detectedSubDir := getter.SourceDirSubdir(source)
286 if detectedSubDir != "" {
287 subDir = filepath.Join(detectedSubDir, subDir)
288 }
289
290 output := ""
291 switch s.Mode {
292 case GetModeUpdate:
293 output = fmt.Sprintf(" Updating source %q", m.Source)
294 default:
295 output = fmt.Sprintf(" Getting source %q", m.Source)
192 } 296 }
297 s.output(output)
193 298
194 // Get the directory where this module is so we can load it 299 dir, ok, err := s.getStorage(key, source)
195 key := strings.Join(path, ".")
196 key = fmt.Sprintf("root.%s-%s", key, m.Source)
197 dir, ok, err := getStorage(s, key, source, mode)
198 if err != nil { 300 if err != nil {
199 return err 301 return nil, err
200 } 302 }
201 if !ok { 303 if !ok {
202 return fmt.Errorf( 304 return nil, fmt.Errorf("module %s: not found, may need to run 'terraform init'", m.Name)
203 "module %s: not found, may need to be downloaded using 'terraform get'", m.Name)
204 } 305 }
205 306
206 // If we have a subdirectory, then merge that in 307 log.Printf("[TRACE] %q stored in %q", source, dir)
308
309 // expand and record the subDir for later
310 fullDir := dir
207 if subDir != "" { 311 if subDir != "" {
208 dir = filepath.Join(dir, subDir) 312 fullDir, err = getter.SubdirGlob(dir, subDir)
209 } 313 if err != nil {
314 return nil, err
315 }
210 316
211 // Load the configurations.Dir(source) 317 // +1 to account for the pathsep
212 children[m.Name], err = NewTreeModule(m.Name, dir) 318 if len(dir)+1 > len(fullDir) {
213 if err != nil { 319 return nil, fmt.Errorf("invalid module storage path %q", fullDir)
214 return fmt.Errorf( 320 }
215 "module %s: %s", m.Name, err) 321 subDir = fullDir[len(dir)+1:]
216 } 322 }
217 323
218 // Set the path of this child 324 // add new info to the module record
219 children[m.Name].path = path 325 mod.Key = key
220 } 326 mod.Dir = dir
327 mod.Root = subDir
221 328
222 // Go through all the children and load them. 329 // record the module in our manifest
223 for _, c := range children { 330 if err := s.recordModule(mod); err != nil {
224 if err := c.Load(s, mode); err != nil { 331 return nil, err
225 return err
226 } 332 }
227 }
228 333
229 // Set our tree up 334 child, err := NewTreeModule(m.Name, fullDir)
230 t.children = children 335 if err != nil {
336 return nil, fmt.Errorf("module %s: %s", m.Name, err)
337 }
338 child.path = modPath
339 child.parent = t
340 child.version = mod.Version
341 child.source = m.Source
342 children[m.Name] = child
343 }
231 344
232 return nil 345 return children, nil
233} 346}
234 347
235// Path is the full path to this tree. 348// Path is the full path to this tree.
@@ -272,32 +385,35 @@ func (t *Tree) String() string {
272// as verifying things such as parameters/outputs between the various modules. 385// as verifying things such as parameters/outputs between the various modules.
273// 386//
274// Load must be called prior to calling Validate or an error will be returned. 387// Load must be called prior to calling Validate or an error will be returned.
275func (t *Tree) Validate() error { 388func (t *Tree) Validate() tfdiags.Diagnostics {
389 var diags tfdiags.Diagnostics
390
276 if !t.Loaded() { 391 if !t.Loaded() {
277 return fmt.Errorf("tree must be loaded before calling Validate") 392 diags = diags.Append(fmt.Errorf(
393 "tree must be loaded before calling Validate",
394 ))
395 return diags
278 } 396 }
279 397
280 // If something goes wrong, here is our error template
281 newErr := &treeError{Name: []string{t.Name()}}
282
283 // Terraform core does not handle root module children named "root". 398 // Terraform core does not handle root module children named "root".
284 // We plan to fix this in the future but this bug was brought up in 399 // We plan to fix this in the future but this bug was brought up in
285 // the middle of a release and we don't want to introduce wide-sweeping 400 // the middle of a release and we don't want to introduce wide-sweeping
286 // changes at that time. 401 // changes at that time.
287 if len(t.path) == 1 && t.name == "root" { 402 if len(t.path) == 1 && t.name == "root" {
288 return fmt.Errorf("root module cannot contain module named 'root'") 403 diags = diags.Append(fmt.Errorf(
404 "root module cannot contain module named 'root'",
405 ))
406 return diags
289 } 407 }
290 408
291 // Validate our configuration first. 409 // Validate our configuration first.
292 if err := t.config.Validate(); err != nil { 410 diags = diags.Append(t.config.Validate())
293 newErr.Add(err)
294 }
295 411
296 // If we're the root, we do extra validation. This validation usually 412 // If we're the root, we do extra validation. This validation usually
297 // requires the entire tree (since children don't have parent pointers). 413 // requires the entire tree (since children don't have parent pointers).
298 if len(t.path) == 0 { 414 if len(t.path) == 0 {
299 if err := t.validateProviderAlias(); err != nil { 415 if err := t.validateProviderAlias(); err != nil {
300 newErr.Add(err) 416 diags = diags.Append(err)
301 } 417 }
302 } 418 }
303 419
@@ -306,20 +422,11 @@ func (t *Tree) Validate() error {
306 422
307 // Validate all our children 423 // Validate all our children
308 for _, c := range children { 424 for _, c := range children {
309 err := c.Validate() 425 childDiags := c.Validate()
310 if err == nil { 426 diags = diags.Append(childDiags)
427 if diags.HasErrors() {
311 continue 428 continue
312 } 429 }
313
314 verr, ok := err.(*treeError)
315 if !ok {
316 // Unknown error, just return...
317 return err
318 }
319
320 // Append ourselves to the error and then return
321 verr.Name = append(verr.Name, t.Name())
322 newErr.AddChild(verr)
323 } 430 }
324 431
325 // Go over all the modules and verify that any parameters are valid 432 // Go over all the modules and verify that any parameters are valid
@@ -345,9 +452,10 @@ func (t *Tree) Validate() error {
345 // Compare to the keys in our raw config for the module 452 // Compare to the keys in our raw config for the module
346 for k, _ := range m.RawConfig.Raw { 453 for k, _ := range m.RawConfig.Raw {
347 if _, ok := varMap[k]; !ok { 454 if _, ok := varMap[k]; !ok {
348 newErr.Add(fmt.Errorf( 455 diags = diags.Append(fmt.Errorf(
349 "module %s: %s is not a valid parameter", 456 "module %q: %q is not a valid argument",
350 m.Name, k)) 457 m.Name, k,
458 ))
351 } 459 }
352 460
353 // Remove the required 461 // Remove the required
@@ -356,9 +464,10 @@ func (t *Tree) Validate() error {
356 464
357 // If we have any required left over, they aren't set. 465 // If we have any required left over, they aren't set.
358 for k, _ := range requiredMap { 466 for k, _ := range requiredMap {
359 newErr.Add(fmt.Errorf( 467 diags = diags.Append(fmt.Errorf(
360 "module %s: required variable %q not set", 468 "module %q: missing required argument %q",
361 m.Name, k)) 469 m.Name, k,
470 ))
362 } 471 }
363 } 472 }
364 473
@@ -373,9 +482,10 @@ func (t *Tree) Validate() error {
373 482
374 tree, ok := children[mv.Name] 483 tree, ok := children[mv.Name]
375 if !ok { 484 if !ok {
376 newErr.Add(fmt.Errorf( 485 diags = diags.Append(fmt.Errorf(
377 "%s: undefined module referenced %s", 486 "%s: reference to undefined module %q",
378 source, mv.Name)) 487 source, mv.Name,
488 ))
379 continue 489 continue
380 } 490 }
381 491
@@ -387,14 +497,56 @@ func (t *Tree) Validate() error {
387 } 497 }
388 } 498 }
389 if !found { 499 if !found {
390 newErr.Add(fmt.Errorf( 500 diags = diags.Append(fmt.Errorf(
391 "%s: %s is not a valid output for module %s", 501 "%s: %q is not a valid output for module %q",
392 source, mv.Field, mv.Name)) 502 source, mv.Field, mv.Name,
503 ))
393 } 504 }
394 } 505 }
395 } 506 }
396 507
397 return newErr.ErrOrNil() 508 return diags
509}
510
511// versionedPathKey returns a path string with every levels full name, version
512// and source encoded. This is to provide a unique key for our module storage,
513// since submodules need to know which versions of their ancestor modules they
514// are loaded from.
515// For example, if module A has a subdirectory B, if module A's source or
516// version is updated B's storage key must reflect this change in order for the
517// correct version of B's source to be loaded.
518func (t *Tree) versionedPathKey(m *Module) string {
519 path := make([]string, len(t.path)+1)
520 path[len(path)-1] = m.Name + ";" + m.Source
521 // We're going to load these in order for easier reading and debugging, but
522 // in practice they only need to be unique and consistent.
523
524 p := t
525 i := len(path) - 2
526 for ; i >= 0; i-- {
527 if p == nil {
528 break
529 }
530 // we may have been loaded under a blank Tree, so always check for a name
531 // too.
532 if p.name == "" {
533 break
534 }
535 seg := p.name
536 if p.version != "" {
537 seg += "#" + p.version
538 }
539
540 if p.source != "" {
541 seg += ";" + p.source
542 }
543
544 path[i] = seg
545 p = p.parent
546 }
547
548 key := strings.Join(path, "|")
549 return key
398} 550}
399 551
400// treeError is an error use by Tree.Validate to accumulates all 552// treeError is an error use by Tree.Validate to accumulates all
diff --git a/vendor/github.com/hashicorp/terraform/config/module/validate_provider_alias.go b/vendor/github.com/hashicorp/terraform/config/module/validate_provider_alias.go
index 090d4f7..f203556 100644
--- a/vendor/github.com/hashicorp/terraform/config/module/validate_provider_alias.go
+++ b/vendor/github.com/hashicorp/terraform/config/module/validate_provider_alias.go
@@ -67,7 +67,7 @@ func (t *Tree) validateProviderAlias() error {
67 67
68 // We didn't find the alias, error! 68 // We didn't find the alias, error!
69 err = multierror.Append(err, fmt.Errorf( 69 err = multierror.Append(err, fmt.Errorf(
70 "module %s: provider alias must be defined by the module or a parent: %s", 70 "module %s: provider alias must be defined by the module: %s",
71 strings.Join(pv.Path, "."), k)) 71 strings.Join(pv.Path, "."), k))
72 } 72 }
73 } 73 }
diff --git a/vendor/github.com/hashicorp/terraform/config/module/versions.go b/vendor/github.com/hashicorp/terraform/config/module/versions.go
new file mode 100644
index 0000000..8348d4b
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/module/versions.go
@@ -0,0 +1,95 @@
1package module
2
3import (
4 "errors"
5 "fmt"
6 "sort"
7
8 version "github.com/hashicorp/go-version"
9 "github.com/hashicorp/terraform/registry/response"
10)
11
12const anyVersion = ">=0.0.0"
13
14// return the newest version that satisfies the provided constraint
15func newest(versions []string, constraint string) (string, error) {
16 if constraint == "" {
17 constraint = anyVersion
18 }
19 cs, err := version.NewConstraint(constraint)
20 if err != nil {
21 return "", err
22 }
23
24 switch len(versions) {
25 case 0:
26 return "", errors.New("no versions found")
27 case 1:
28 v, err := version.NewVersion(versions[0])
29 if err != nil {
30 return "", err
31 }
32
33 if !cs.Check(v) {
34 return "", fmt.Errorf("no version found matching constraint %q", constraint)
35 }
36 return versions[0], nil
37 }
38
39 sort.Slice(versions, func(i, j int) bool {
40 // versions should have already been validated
41 // sort invalid version strings to the end
42 iv, err := version.NewVersion(versions[i])
43 if err != nil {
44 return true
45 }
46 jv, err := version.NewVersion(versions[j])
47 if err != nil {
48 return true
49 }
50 return iv.GreaterThan(jv)
51 })
52
53 // versions are now in order, so just find the first which satisfies the
54 // constraint
55 for i := range versions {
56 v, err := version.NewVersion(versions[i])
57 if err != nil {
58 continue
59 }
60 if cs.Check(v) {
61 return versions[i], nil
62 }
63 }
64
65 return "", nil
66}
67
68// return the newest *moduleVersion that matches the given constraint
69// TODO: reconcile these two types and newest* functions
70func newestVersion(moduleVersions []*response.ModuleVersion, constraint string) (*response.ModuleVersion, error) {
71 var versions []string
72 modules := make(map[string]*response.ModuleVersion)
73
74 for _, m := range moduleVersions {
75 versions = append(versions, m.Version)
76 modules[m.Version] = m
77 }
78
79 match, err := newest(versions, constraint)
80 return modules[match], err
81}
82
83// return the newest moduleRecord that matches the given constraint
84func newestRecord(moduleVersions []moduleRecord, constraint string) (moduleRecord, error) {
85 var versions []string
86 modules := make(map[string]moduleRecord)
87
88 for _, m := range moduleVersions {
89 versions = append(versions, m.Version)
90 modules[m.Version] = m
91 }
92
93 match, err := newest(versions, constraint)
94 return modules[match], err
95}
diff --git a/vendor/github.com/hashicorp/terraform/config/raw_config.go b/vendor/github.com/hashicorp/terraform/config/raw_config.go
index f8498d8..1854a8b 100644
--- a/vendor/github.com/hashicorp/terraform/config/raw_config.go
+++ b/vendor/github.com/hashicorp/terraform/config/raw_config.go
@@ -3,8 +3,14 @@ package config
3import ( 3import (
4 "bytes" 4 "bytes"
5 "encoding/gob" 5 "encoding/gob"
6 "errors"
7 "strconv"
6 "sync" 8 "sync"
7 9
10 "github.com/zclconf/go-cty/cty"
11 "github.com/zclconf/go-cty/cty/convert"
12
13 hcl2 "github.com/hashicorp/hcl2/hcl"
8 "github.com/hashicorp/hil" 14 "github.com/hashicorp/hil"
9 "github.com/hashicorp/hil/ast" 15 "github.com/hashicorp/hil/ast"
10 "github.com/mitchellh/copystructure" 16 "github.com/mitchellh/copystructure"
@@ -27,8 +33,24 @@ const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66"
27// RawConfig supports a query-like interface to request 33// RawConfig supports a query-like interface to request
28// information from deep within the structure. 34// information from deep within the structure.
29type RawConfig struct { 35type RawConfig struct {
30 Key string 36 Key string
31 Raw map[string]interface{} 37
38 // Only _one_ of Raw and Body may be populated at a time.
39 //
40 // In the normal case, Raw is populated and Body is nil.
41 //
42 // When the experimental HCL2 parsing mode is enabled, "Body"
43 // is populated and RawConfig serves only to transport the hcl2.Body
44 // through the rest of Terraform core so we can ultimately decode it
45 // once its schema is known.
46 //
47 // Once we transition to HCL2 as the primary representation, RawConfig
48 // should be removed altogether and the hcl2.Body should be passed
49 // around directly.
50
51 Raw map[string]interface{}
52 Body hcl2.Body
53
32 Interpolations []ast.Node 54 Interpolations []ast.Node
33 Variables map[string]InterpolatedVariable 55 Variables map[string]InterpolatedVariable
34 56
@@ -48,6 +70,26 @@ func NewRawConfig(raw map[string]interface{}) (*RawConfig, error) {
48 return result, nil 70 return result, nil
49} 71}
50 72
73// NewRawConfigHCL2 creates a new RawConfig that is serving as a capsule
74// to transport a hcl2.Body. In this mode, the publicly-readable struct
75// fields are not populated since all operations should instead be diverted
76// to the HCL2 body.
77//
78// For a RawConfig object constructed with this function, the only valid use
79// is to later retrieve the Body value and call its own methods. Callers
80// may choose to set and then later handle the Key field, in a manner
81// consistent with how it is handled by the Value method, but the Value
82// method itself must not be used.
83//
84// This is an experimental codepath to be used only by the HCL2 config loader.
85// Non-experimental parsing should _always_ use NewRawConfig to produce a
86// fully-functional RawConfig object.
87func NewRawConfigHCL2(body hcl2.Body) *RawConfig {
88 return &RawConfig{
89 Body: body,
90 }
91}
92
51// RawMap returns a copy of the RawConfig.Raw map. 93// RawMap returns a copy of the RawConfig.Raw map.
52func (r *RawConfig) RawMap() map[string]interface{} { 94func (r *RawConfig) RawMap() map[string]interface{} {
53 r.lock.Lock() 95 r.lock.Lock()
@@ -69,6 +111,10 @@ func (r *RawConfig) Copy() *RawConfig {
69 r.lock.Lock() 111 r.lock.Lock()
70 defer r.lock.Unlock() 112 defer r.lock.Unlock()
71 113
114 if r.Body != nil {
115 return NewRawConfigHCL2(r.Body)
116 }
117
72 newRaw := make(map[string]interface{}) 118 newRaw := make(map[string]interface{})
73 for k, v := range r.Raw { 119 for k, v := range r.Raw {
74 newRaw[k] = v 120 newRaw[k] = v
@@ -223,6 +269,13 @@ func (r *RawConfig) init() error {
223} 269}
224 270
225func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error { 271func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error {
272 if r.Body != nil {
273 // For RawConfigs created for the HCL2 experiement, callers must
274 // use the HCL2 Body API directly rather than interpolating via
275 // the RawConfig.
276 return errors.New("this feature is not yet supported under the HCL2 experiment")
277 }
278
226 config, err := copystructure.Copy(r.Raw) 279 config, err := copystructure.Copy(r.Raw)
227 if err != nil { 280 if err != nil {
228 return err 281 return err
@@ -268,6 +321,74 @@ func (r *RawConfig) merge(r2 *RawConfig) *RawConfig {
268 return result 321 return result
269} 322}
270 323
324// couldBeInteger is a helper that determines if the represented value could
325// result in an integer.
326//
327// This function only works for RawConfigs that have "Key" set, meaning that
328// a single result can be produced. Calling this function will overwrite
329// the Config and Value results to be a test value.
330//
331// This function is conservative. If there is some doubt about whether the
332// result could be an integer -- for example, if it depends on a variable
333// whose type we don't know yet -- it will still return true.
334func (r *RawConfig) couldBeInteger() bool {
335 if r.Key == "" {
336 // un-keyed RawConfigs can never produce numbers
337 return false
338 }
339 if r.Body == nil {
340 // Normal path: using the interpolator in this package
341 // Interpolate with a fixed number to verify that its a number.
342 r.interpolate(func(root ast.Node) (interface{}, error) {
343 // Execute the node but transform the AST so that it returns
344 // a fixed value of "5" for all interpolations.
345 result, err := hil.Eval(
346 hil.FixedValueTransform(
347 root, &ast.LiteralNode{Value: "5", Typex: ast.TypeString}),
348 nil)
349 if err != nil {
350 return "", err
351 }
352
353 return result.Value, nil
354 })
355 _, err := strconv.ParseInt(r.Value().(string), 0, 0)
356 return err == nil
357 } else {
358 // HCL2 experiment path: using the HCL2 API via shims
359 //
360 // This path catches fewer situations because we have to assume all
361 // variables are entirely unknown in HCL2, rather than the assumption
362 // above that all variables can be numbers because names like "var.foo"
363 // are considered a single variable rather than an attribute access.
364 // This is fine in practice, because we get a definitive answer
365 // during the graph walk when we have real values to work with.
366 attrs, diags := r.Body.JustAttributes()
367 if diags.HasErrors() {
368 // This body is not just a single attribute with a value, so
369 // this can't be a number.
370 return false
371 }
372 attr, hasAttr := attrs[r.Key]
373 if !hasAttr {
374 return false
375 }
376 result, diags := hcl2EvalWithUnknownVars(attr.Expr)
377 if diags.HasErrors() {
378 // We'll conservatively assume that this error is a result of
379 // us not being ready to fully-populate the scope, and catch
380 // any further problems during the main graph walk.
381 return true
382 }
383
384 // If the result is convertable to number then we'll allow it.
385 // We do this because an unknown string is optimistically convertable
386 // to number (might be "5") but a _known_ string "hello" is not.
387 _, err := convert.Convert(result, cty.Number)
388 return err == nil
389 }
390}
391
271// UnknownKeys returns the keys of the configuration that are unknown 392// UnknownKeys returns the keys of the configuration that are unknown
272// because they had interpolated variables that must be computed. 393// because they had interpolated variables that must be computed.
273func (r *RawConfig) UnknownKeys() []string { 394func (r *RawConfig) UnknownKeys() []string {
diff --git a/vendor/github.com/hashicorp/terraform/config/resource_mode_string.go b/vendor/github.com/hashicorp/terraform/config/resource_mode_string.go
index ea68b4f..8a55e06 100644
--- a/vendor/github.com/hashicorp/terraform/config/resource_mode_string.go
+++ b/vendor/github.com/hashicorp/terraform/config/resource_mode_string.go
@@ -2,7 +2,7 @@
2 2
3package config 3package config
4 4
5import "fmt" 5import "strconv"
6 6
7const _ResourceMode_name = "ManagedResourceModeDataResourceMode" 7const _ResourceMode_name = "ManagedResourceModeDataResourceMode"
8 8
@@ -10,7 +10,7 @@ var _ResourceMode_index = [...]uint8{0, 19, 35}
10 10
11func (i ResourceMode) String() string { 11func (i ResourceMode) String() string {
12 if i < 0 || i >= ResourceMode(len(_ResourceMode_index)-1) { 12 if i < 0 || i >= ResourceMode(len(_ResourceMode_index)-1) {
13 return fmt.Sprintf("ResourceMode(%d)", i) 13 return "ResourceMode(" + strconv.FormatInt(int64(i), 10) + ")"
14 } 14 }
15 return _ResourceMode_name[_ResourceMode_index[i]:_ResourceMode_index[i+1]] 15 return _ResourceMode_name[_ResourceMode_index[i]:_ResourceMode_index[i+1]]
16} 16}
diff --git a/vendor/github.com/hashicorp/terraform/config/testing.go b/vendor/github.com/hashicorp/terraform/config/testing.go
index f7bfadd..831fc77 100644
--- a/vendor/github.com/hashicorp/terraform/config/testing.go
+++ b/vendor/github.com/hashicorp/terraform/config/testing.go
@@ -6,6 +6,8 @@ import (
6 6
7// TestRawConfig is used to create a RawConfig for testing. 7// TestRawConfig is used to create a RawConfig for testing.
8func TestRawConfig(t *testing.T, c map[string]interface{}) *RawConfig { 8func TestRawConfig(t *testing.T, c map[string]interface{}) *RawConfig {
9 t.Helper()
10
9 cfg, err := NewRawConfig(c) 11 cfg, err := NewRawConfig(c)
10 if err != nil { 12 if err != nil {
11 t.Fatalf("err: %s", err) 13 t.Fatalf("err: %s", err)
diff --git a/vendor/github.com/hashicorp/terraform/dag/dag.go b/vendor/github.com/hashicorp/terraform/dag/dag.go
index f8776bc..b7eb10c 100644
--- a/vendor/github.com/hashicorp/terraform/dag/dag.go
+++ b/vendor/github.com/hashicorp/terraform/dag/dag.go
@@ -106,7 +106,7 @@ func (g *AcyclicGraph) TransitiveReduction() {
106 uTargets := g.DownEdges(u) 106 uTargets := g.DownEdges(u)
107 vs := AsVertexList(g.DownEdges(u)) 107 vs := AsVertexList(g.DownEdges(u))
108 108
109 g.DepthFirstWalk(vs, func(v Vertex, d int) error { 109 g.depthFirstWalk(vs, false, func(v Vertex, d int) error {
110 shared := uTargets.Intersection(g.DownEdges(v)) 110 shared := uTargets.Intersection(g.DownEdges(v))
111 for _, vPrime := range AsVertexList(shared) { 111 for _, vPrime := range AsVertexList(shared) {
112 g.RemoveEdge(BasicEdge(u, vPrime)) 112 g.RemoveEdge(BasicEdge(u, vPrime))
@@ -187,9 +187,18 @@ type vertexAtDepth struct {
187} 187}
188 188
189// depthFirstWalk does a depth-first walk of the graph starting from 189// depthFirstWalk does a depth-first walk of the graph starting from
190// the vertices in start. This is not exported now but it would make sense 190// the vertices in start.
191// to export this publicly at some point.
192func (g *AcyclicGraph) DepthFirstWalk(start []Vertex, f DepthWalkFunc) error { 191func (g *AcyclicGraph) DepthFirstWalk(start []Vertex, f DepthWalkFunc) error {
192 return g.depthFirstWalk(start, true, f)
193}
194
195// This internal method provides the option of not sorting the vertices during
196// the walk, which we use for the Transitive reduction.
197// Some configurations can lead to fully-connected subgraphs, which makes our
198// transitive reduction algorithm O(n^3). This is still passable for the size
199// of our graphs, but the additional n^2 sort operations would make this
200// uncomputable in a reasonable amount of time.
201func (g *AcyclicGraph) depthFirstWalk(start []Vertex, sorted bool, f DepthWalkFunc) error {
193 defer g.debug.BeginOperation(typeDepthFirstWalk, "").End("") 202 defer g.debug.BeginOperation(typeDepthFirstWalk, "").End("")
194 203
195 seen := make(map[Vertex]struct{}) 204 seen := make(map[Vertex]struct{})
@@ -219,7 +228,11 @@ func (g *AcyclicGraph) DepthFirstWalk(start []Vertex, f DepthWalkFunc) error {
219 228
220 // Visit targets of this in a consistent order. 229 // Visit targets of this in a consistent order.
221 targets := AsVertexList(g.DownEdges(current.Vertex)) 230 targets := AsVertexList(g.DownEdges(current.Vertex))
222 sort.Sort(byVertexName(targets)) 231
232 if sorted {
233 sort.Sort(byVertexName(targets))
234 }
235
223 for _, t := range targets { 236 for _, t := range targets {
224 frontier = append(frontier, &vertexAtDepth{ 237 frontier = append(frontier, &vertexAtDepth{
225 Vertex: t, 238 Vertex: t,
diff --git a/vendor/github.com/hashicorp/terraform/dag/marshal.go b/vendor/github.com/hashicorp/terraform/dag/marshal.go
index 16d5dd6..c567d27 100644
--- a/vendor/github.com/hashicorp/terraform/dag/marshal.go
+++ b/vendor/github.com/hashicorp/terraform/dag/marshal.go
@@ -273,6 +273,9 @@ func (e *encoder) Encode(i interface{}) {
273} 273}
274 274
275func (e *encoder) Add(v Vertex) { 275func (e *encoder) Add(v Vertex) {
276 if e == nil {
277 return
278 }
276 e.Encode(marshalTransform{ 279 e.Encode(marshalTransform{
277 Type: typeTransform, 280 Type: typeTransform,
278 AddVertex: newMarshalVertex(v), 281 AddVertex: newMarshalVertex(v),
@@ -281,6 +284,9 @@ func (e *encoder) Add(v Vertex) {
281 284
282// Remove records the removal of Vertex v. 285// Remove records the removal of Vertex v.
283func (e *encoder) Remove(v Vertex) { 286func (e *encoder) Remove(v Vertex) {
287 if e == nil {
288 return
289 }
284 e.Encode(marshalTransform{ 290 e.Encode(marshalTransform{
285 Type: typeTransform, 291 Type: typeTransform,
286 RemoveVertex: newMarshalVertex(v), 292 RemoveVertex: newMarshalVertex(v),
@@ -288,6 +294,9 @@ func (e *encoder) Remove(v Vertex) {
288} 294}
289 295
290func (e *encoder) Connect(edge Edge) { 296func (e *encoder) Connect(edge Edge) {
297 if e == nil {
298 return
299 }
291 e.Encode(marshalTransform{ 300 e.Encode(marshalTransform{
292 Type: typeTransform, 301 Type: typeTransform,
293 AddEdge: newMarshalEdge(edge), 302 AddEdge: newMarshalEdge(edge),
@@ -295,6 +304,9 @@ func (e *encoder) Connect(edge Edge) {
295} 304}
296 305
297func (e *encoder) RemoveEdge(edge Edge) { 306func (e *encoder) RemoveEdge(edge Edge) {
307 if e == nil {
308 return
309 }
298 e.Encode(marshalTransform{ 310 e.Encode(marshalTransform{
299 Type: typeTransform, 311 Type: typeTransform,
300 RemoveEdge: newMarshalEdge(edge), 312 RemoveEdge: newMarshalEdge(edge),
diff --git a/vendor/github.com/hashicorp/terraform/dag/walk.go b/vendor/github.com/hashicorp/terraform/dag/walk.go
index 23c87ad..f03b100 100644
--- a/vendor/github.com/hashicorp/terraform/dag/walk.go
+++ b/vendor/github.com/hashicorp/terraform/dag/walk.go
@@ -166,7 +166,7 @@ func (w *Walker) Update(g *AcyclicGraph) {
166 w.wait.Add(1) 166 w.wait.Add(1)
167 167
168 // Add to our own set so we know about it already 168 // Add to our own set so we know about it already
169 log.Printf("[DEBUG] dag/walk: added new vertex: %q", VertexName(v)) 169 log.Printf("[TRACE] dag/walk: added new vertex: %q", VertexName(v))
170 w.vertices.Add(raw) 170 w.vertices.Add(raw)
171 171
172 // Initialize the vertex info 172 // Initialize the vertex info
@@ -198,7 +198,7 @@ func (w *Walker) Update(g *AcyclicGraph) {
198 // Delete it out of the map 198 // Delete it out of the map
199 delete(w.vertexMap, v) 199 delete(w.vertexMap, v)
200 200
201 log.Printf("[DEBUG] dag/walk: removed vertex: %q", VertexName(v)) 201 log.Printf("[TRACE] dag/walk: removed vertex: %q", VertexName(v))
202 w.vertices.Delete(raw) 202 w.vertices.Delete(raw)
203 } 203 }
204 204
@@ -229,7 +229,7 @@ func (w *Walker) Update(g *AcyclicGraph) {
229 changedDeps.Add(waiter) 229 changedDeps.Add(waiter)
230 230
231 log.Printf( 231 log.Printf(
232 "[DEBUG] dag/walk: added edge: %q waiting on %q", 232 "[TRACE] dag/walk: added edge: %q waiting on %q",
233 VertexName(waiter), VertexName(dep)) 233 VertexName(waiter), VertexName(dep))
234 w.edges.Add(raw) 234 w.edges.Add(raw)
235 } 235 }
@@ -253,7 +253,7 @@ func (w *Walker) Update(g *AcyclicGraph) {
253 changedDeps.Add(waiter) 253 changedDeps.Add(waiter)
254 254
255 log.Printf( 255 log.Printf(
256 "[DEBUG] dag/walk: removed edge: %q waiting on %q", 256 "[TRACE] dag/walk: removed edge: %q waiting on %q",
257 VertexName(waiter), VertexName(dep)) 257 VertexName(waiter), VertexName(dep))
258 w.edges.Delete(raw) 258 w.edges.Delete(raw)
259 } 259 }
@@ -296,7 +296,7 @@ func (w *Walker) Update(g *AcyclicGraph) {
296 info.depsCancelCh = cancelCh 296 info.depsCancelCh = cancelCh
297 297
298 log.Printf( 298 log.Printf(
299 "[DEBUG] dag/walk: dependencies changed for %q, sending new deps", 299 "[TRACE] dag/walk: dependencies changed for %q, sending new deps",
300 VertexName(v)) 300 VertexName(v))
301 301
302 // Start the waiter 302 // Start the waiter
@@ -383,10 +383,10 @@ func (w *Walker) walkVertex(v Vertex, info *walkerVertex) {
383 // Run our callback or note that our upstream failed 383 // Run our callback or note that our upstream failed
384 var err error 384 var err error
385 if depsSuccess { 385 if depsSuccess {
386 log.Printf("[DEBUG] dag/walk: walking %q", VertexName(v)) 386 log.Printf("[TRACE] dag/walk: walking %q", VertexName(v))
387 err = w.Callback(v) 387 err = w.Callback(v)
388 } else { 388 } else {
389 log.Printf("[DEBUG] dag/walk: upstream errored, not walking %q", VertexName(v)) 389 log.Printf("[TRACE] dag/walk: upstream errored, not walking %q", VertexName(v))
390 err = errWalkUpstream 390 err = errWalkUpstream
391 } 391 }
392 392
@@ -423,7 +423,7 @@ func (w *Walker) waitDeps(
423 return 423 return
424 424
425 case <-time.After(time.Second * 5): 425 case <-time.After(time.Second * 5):
426 log.Printf("[DEBUG] dag/walk: vertex %q, waiting for: %q", 426 log.Printf("[TRACE] dag/walk: vertex %q, waiting for: %q",
427 VertexName(v), VertexName(dep)) 427 VertexName(v), VertexName(dep))
428 } 428 }
429 } 429 }
diff --git a/vendor/github.com/hashicorp/terraform/helper/experiment/experiment.go b/vendor/github.com/hashicorp/terraform/helper/experiment/experiment.go
deleted file mode 100644
index 18b8837..0000000
--- a/vendor/github.com/hashicorp/terraform/helper/experiment/experiment.go
+++ /dev/null
@@ -1,154 +0,0 @@
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
deleted file mode 100644
index 8e2f707..0000000
--- a/vendor/github.com/hashicorp/terraform/helper/experiment/id.go
+++ /dev/null
@@ -1,34 +0,0 @@
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
index 64d8263..6ccc523 100644
--- a/vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode.go
+++ b/vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode.go
@@ -1,6 +1,8 @@
1package hashcode 1package hashcode
2 2
3import ( 3import (
4 "bytes"
5 "fmt"
4 "hash/crc32" 6 "hash/crc32"
5) 7)
6 8
@@ -20,3 +22,14 @@ func String(s string) int {
20 // v == MinInt 22 // v == MinInt
21 return 0 23 return 0
22} 24}
25
26// Strings hashes a list of strings to a unique hashcode.
27func Strings(strings []string) string {
28 var buf bytes.Buffer
29
30 for _, s := range strings {
31 buf.WriteString(fmt.Sprintf("%s-", s))
32 }
33
34 return fmt.Sprintf("%d", String(buf.String()))
35}
diff --git a/vendor/github.com/hashicorp/terraform/helper/logging/logging.go b/vendor/github.com/hashicorp/terraform/helper/logging/logging.go
index 433cd77..6bd92f7 100644
--- a/vendor/github.com/hashicorp/terraform/helper/logging/logging.go
+++ b/vendor/github.com/hashicorp/terraform/helper/logging/logging.go
@@ -18,7 +18,7 @@ const (
18 EnvLogFile = "TF_LOG_PATH" // Set to a file 18 EnvLogFile = "TF_LOG_PATH" // Set to a file
19) 19)
20 20
21var validLevels = []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARN", "ERROR"} 21var ValidLevels = []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARN", "ERROR"}
22 22
23// LogOutput determines where we should send logs (if anywhere) and the log level. 23// LogOutput determines where we should send logs (if anywhere) and the log level.
24func LogOutput() (logOutput io.Writer, err error) { 24func LogOutput() (logOutput io.Writer, err error) {
@@ -40,7 +40,7 @@ func LogOutput() (logOutput io.Writer, err error) {
40 40
41 // This was the default since the beginning 41 // This was the default since the beginning
42 logOutput = &logutils.LevelFilter{ 42 logOutput = &logutils.LevelFilter{
43 Levels: validLevels, 43 Levels: ValidLevels,
44 MinLevel: logutils.LogLevel(logLevel), 44 MinLevel: logutils.LogLevel(logLevel),
45 Writer: logOutput, 45 Writer: logOutput,
46 } 46 }
@@ -77,7 +77,7 @@ func LogLevel() string {
77 logLevel = strings.ToUpper(envLevel) 77 logLevel = strings.ToUpper(envLevel)
78 } else { 78 } else {
79 log.Printf("[WARN] Invalid log level: %q. Defaulting to level: TRACE. Valid levels are: %+v", 79 log.Printf("[WARN] Invalid log level: %q. Defaulting to level: TRACE. Valid levels are: %+v",
80 envLevel, validLevels) 80 envLevel, ValidLevels)
81 } 81 }
82 82
83 return logLevel 83 return logLevel
@@ -90,7 +90,7 @@ func IsDebugOrHigher() bool {
90} 90}
91 91
92func isValidLogLevel(level string) bool { 92func isValidLogLevel(level string) bool {
93 for _, l := range validLevels { 93 for _, l := range ValidLevels {
94 if strings.ToUpper(level) == string(l) { 94 if strings.ToUpper(level) == string(l) {
95 return true 95 return true
96 } 96 }
diff --git a/vendor/github.com/hashicorp/terraform/helper/logging/transport.go b/vendor/github.com/hashicorp/terraform/helper/logging/transport.go
index 4477924..bddabe6 100644
--- a/vendor/github.com/hashicorp/terraform/helper/logging/transport.go
+++ b/vendor/github.com/hashicorp/terraform/helper/logging/transport.go
@@ -1,9 +1,12 @@
1package logging 1package logging
2 2
3import ( 3import (
4 "bytes"
5 "encoding/json"
4 "log" 6 "log"
5 "net/http" 7 "net/http"
6 "net/http/httputil" 8 "net/http/httputil"
9 "strings"
7) 10)
8 11
9type transport struct { 12type transport struct {
@@ -15,7 +18,7 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
15 if IsDebugOrHigher() { 18 if IsDebugOrHigher() {
16 reqData, err := httputil.DumpRequestOut(req, true) 19 reqData, err := httputil.DumpRequestOut(req, true)
17 if err == nil { 20 if err == nil {
18 log.Printf("[DEBUG] "+logReqMsg, t.name, string(reqData)) 21 log.Printf("[DEBUG] "+logReqMsg, t.name, prettyPrintJsonLines(reqData))
19 } else { 22 } else {
20 log.Printf("[ERROR] %s API Request error: %#v", t.name, err) 23 log.Printf("[ERROR] %s API Request error: %#v", t.name, err)
21 } 24 }
@@ -29,7 +32,7 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
29 if IsDebugOrHigher() { 32 if IsDebugOrHigher() {
30 respData, err := httputil.DumpResponse(resp, true) 33 respData, err := httputil.DumpResponse(resp, true)
31 if err == nil { 34 if err == nil {
32 log.Printf("[DEBUG] "+logRespMsg, t.name, string(respData)) 35 log.Printf("[DEBUG] "+logRespMsg, t.name, prettyPrintJsonLines(respData))
33 } else { 36 } else {
34 log.Printf("[ERROR] %s API Response error: %#v", t.name, err) 37 log.Printf("[ERROR] %s API Response error: %#v", t.name, err)
35 } 38 }
@@ -42,6 +45,20 @@ func NewTransport(name string, t http.RoundTripper) *transport {
42 return &transport{name, t} 45 return &transport{name, t}
43} 46}
44 47
48// prettyPrintJsonLines iterates through a []byte line-by-line,
49// transforming any lines that are complete json into pretty-printed json.
50func prettyPrintJsonLines(b []byte) string {
51 parts := strings.Split(string(b), "\n")
52 for i, p := range parts {
53 if b := []byte(p); json.Valid(b) {
54 var out bytes.Buffer
55 json.Indent(&out, b, "", " ")
56 parts[i] = out.String()
57 }
58 }
59 return strings.Join(parts, "\n")
60}
61
45const logReqMsg = `%s API Request Details: 62const logReqMsg = `%s API Request Details:
46---[ REQUEST ]--------------------------------------- 63---[ REQUEST ]---------------------------------------
47%s 64%s
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/id.go b/vendor/github.com/hashicorp/terraform/helper/resource/id.go
index 1cde67c..4494955 100644
--- a/vendor/github.com/hashicorp/terraform/helper/resource/id.go
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/id.go
@@ -18,6 +18,11 @@ func UniqueId() string {
18 return PrefixedUniqueId(UniqueIdPrefix) 18 return PrefixedUniqueId(UniqueIdPrefix)
19} 19}
20 20
21// UniqueIDSuffixLength is the string length of the suffix generated by
22// PrefixedUniqueId. This can be used by length validation functions to
23// ensure prefixes are the correct length for the target field.
24const UniqueIDSuffixLength = 26
25
21// Helper for a resource to generate a unique identifier w/ given prefix 26// Helper for a resource to generate a unique identifier w/ given prefix
22// 27//
23// After the prefix, the ID consists of an incrementing 26 digit value (to match 28// After the prefix, the ID consists of an incrementing 26 digit value (to match
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/state.go b/vendor/github.com/hashicorp/terraform/helper/resource/state.go
index 37c586a..c34e21b 100644
--- a/vendor/github.com/hashicorp/terraform/helper/resource/state.go
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/state.go
@@ -46,7 +46,7 @@ type StateChangeConf struct {
46// If the Timeout is exceeded before reaching the Target state, return an 46// If the Timeout is exceeded before reaching the Target state, return an
47// error. 47// error.
48// 48//
49// Otherwise, result the result of the first call to the Refresh function to 49// Otherwise, the result is the result of the first call to the Refresh function to
50// reach the target state. 50// reach the target state.
51func (conf *StateChangeConf) WaitForState() (interface{}, error) { 51func (conf *StateChangeConf) WaitForState() (interface{}, error) {
52 log.Printf("[DEBUG] Waiting for state to become: %s", conf.Target) 52 log.Printf("[DEBUG] Waiting for state to become: %s", conf.Target)
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing.go
index d7de1a0..b97673f 100644
--- a/vendor/github.com/hashicorp/terraform/helper/resource/testing.go
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/testing.go
@@ -11,11 +11,13 @@ import (
11 "reflect" 11 "reflect"
12 "regexp" 12 "regexp"
13 "strings" 13 "strings"
14 "syscall"
14 "testing" 15 "testing"
15 16
16 "github.com/davecgh/go-spew/spew" 17 "github.com/davecgh/go-spew/spew"
17 "github.com/hashicorp/go-getter" 18 "github.com/hashicorp/errwrap"
18 "github.com/hashicorp/go-multierror" 19 "github.com/hashicorp/go-multierror"
20 "github.com/hashicorp/logutils"
19 "github.com/hashicorp/terraform/config/module" 21 "github.com/hashicorp/terraform/config/module"
20 "github.com/hashicorp/terraform/helper/logging" 22 "github.com/hashicorp/terraform/helper/logging"
21 "github.com/hashicorp/terraform/terraform" 23 "github.com/hashicorp/terraform/terraform"
@@ -186,6 +188,10 @@ type TestCheckFunc func(*terraform.State) error
186// ImportStateCheckFunc is the check function for ImportState tests 188// ImportStateCheckFunc is the check function for ImportState tests
187type ImportStateCheckFunc func([]*terraform.InstanceState) error 189type ImportStateCheckFunc func([]*terraform.InstanceState) error
188 190
191// ImportStateIdFunc is an ID generation function to help with complex ID
192// generation for ImportState tests.
193type ImportStateIdFunc func(*terraform.State) (string, error)
194
189// TestCase is a single acceptance test case used to test the apply/destroy 195// TestCase is a single acceptance test case used to test the apply/destroy
190// lifecycle of a resource in a specific configuration. 196// lifecycle of a resource in a specific configuration.
191// 197//
@@ -260,6 +266,15 @@ type TestStep struct {
260 // below. 266 // below.
261 PreConfig func() 267 PreConfig func()
262 268
269 // Taint is a list of resource addresses to taint prior to the execution of
270 // the step. Be sure to only include this at a step where the referenced
271 // address will be present in state, as it will fail the test if the resource
272 // is missing.
273 //
274 // This option is ignored on ImportState tests, and currently only works for
275 // resources in the root module path.
276 Taint []string
277
263 //--------------------------------------------------------------- 278 //---------------------------------------------------------------
264 // Test modes. One of the following groups of settings must be 279 // Test modes. One of the following groups of settings must be
265 // set to determine what the test step will do. Ideally we would've 280 // set to determine what the test step will do. Ideally we would've
@@ -304,10 +319,19 @@ type TestStep struct {
304 // no-op plans 319 // no-op plans
305 PlanOnly bool 320 PlanOnly bool
306 321
322 // PreventDiskCleanup can be set to true for testing terraform modules which
323 // require access to disk at runtime. Note that this will leave files in the
324 // temp folder
325 PreventDiskCleanup bool
326
307 // PreventPostDestroyRefresh can be set to true for cases where data sources 327 // PreventPostDestroyRefresh can be set to true for cases where data sources
308 // are tested alongside real resources 328 // are tested alongside real resources
309 PreventPostDestroyRefresh bool 329 PreventPostDestroyRefresh bool
310 330
331 // SkipFunc is called before applying config, but after PreConfig
332 // This is useful for defining test steps with platform-dependent checks
333 SkipFunc func() (bool, error)
334
311 //--------------------------------------------------------------- 335 //---------------------------------------------------------------
312 // ImportState testing 336 // ImportState testing
313 //--------------------------------------------------------------- 337 //---------------------------------------------------------------
@@ -329,6 +353,12 @@ type TestStep struct {
329 // the unset ImportStateId field. 353 // the unset ImportStateId field.
330 ImportStateIdPrefix string 354 ImportStateIdPrefix string
331 355
356 // ImportStateIdFunc is a function that can be used to dynamically generate
357 // the ID for the ImportState tests. It is sent the state, which can be
358 // checked to derive the attributes necessary and generate the string in the
359 // desired format.
360 ImportStateIdFunc ImportStateIdFunc
361
332 // ImportStateCheck checks the results of ImportState. It should be 362 // ImportStateCheck checks the results of ImportState. It should be
333 // used to verify that the resulting value of ImportState has the 363 // used to verify that the resulting value of ImportState has the
334 // proper resources, IDs, and attributes. 364 // proper resources, IDs, and attributes.
@@ -345,6 +375,60 @@ type TestStep struct {
345 ImportStateVerifyIgnore []string 375 ImportStateVerifyIgnore []string
346} 376}
347 377
378// Set to a file mask in sprintf format where %s is test name
379const EnvLogPathMask = "TF_LOG_PATH_MASK"
380
381func LogOutput(t TestT) (logOutput io.Writer, err error) {
382 logOutput = ioutil.Discard
383
384 logLevel := logging.LogLevel()
385 if logLevel == "" {
386 return
387 }
388
389 logOutput = os.Stderr
390
391 if logPath := os.Getenv(logging.EnvLogFile); logPath != "" {
392 var err error
393 logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666)
394 if err != nil {
395 return nil, err
396 }
397 }
398
399 if logPathMask := os.Getenv(EnvLogPathMask); logPathMask != "" {
400 // Escape special characters which may appear if we have subtests
401 testName := strings.Replace(t.Name(), "/", "__", -1)
402
403 logPath := fmt.Sprintf(logPathMask, testName)
404 var err error
405 logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666)
406 if err != nil {
407 return nil, err
408 }
409 }
410
411 // This was the default since the beginning
412 logOutput = &logutils.LevelFilter{
413 Levels: logging.ValidLevels,
414 MinLevel: logutils.LogLevel(logLevel),
415 Writer: logOutput,
416 }
417
418 return
419}
420
421// ParallelTest performs an acceptance test on a resource, allowing concurrency
422// with other ParallelTest.
423//
424// Tests will fail if they do not properly handle conditions to allow multiple
425// tests to occur against the same resource or service (e.g. random naming).
426// All other requirements of the Test function also apply to this function.
427func ParallelTest(t TestT, c TestCase) {
428 t.Parallel()
429 Test(t, c)
430}
431
348// Test performs an acceptance test on a resource. 432// Test performs an acceptance test on a resource.
349// 433//
350// Tests are not run unless an environmental variable "TF_ACC" is 434// Tests are not run unless an environmental variable "TF_ACC" is
@@ -366,7 +450,7 @@ func Test(t TestT, c TestCase) {
366 return 450 return
367 } 451 }
368 452
369 logWriter, err := logging.LogOutput() 453 logWriter, err := LogOutput(t)
370 if err != nil { 454 if err != nil {
371 t.Error(fmt.Errorf("error setting up logging: %s", err)) 455 t.Error(fmt.Errorf("error setting up logging: %s", err))
372 } 456 }
@@ -398,7 +482,18 @@ func Test(t TestT, c TestCase) {
398 errored := false 482 errored := false
399 for i, step := range c.Steps { 483 for i, step := range c.Steps {
400 var err error 484 var err error
401 log.Printf("[WARN] Test: Executing step %d", i) 485 log.Printf("[DEBUG] Test: Executing step %d", i)
486
487 if step.SkipFunc != nil {
488 skip, err := step.SkipFunc()
489 if err != nil {
490 t.Fatal(err)
491 }
492 if skip {
493 log.Printf("[WARN] Skipping step %d", i)
494 continue
495 }
496 }
402 497
403 if step.Config == "" && !step.ImportState { 498 if step.Config == "" && !step.ImportState {
404 err = fmt.Errorf( 499 err = fmt.Errorf(
@@ -418,6 +513,15 @@ func Test(t TestT, c TestCase) {
418 } 513 }
419 } 514 }
420 515
516 // If we expected an error, but did not get one, fail
517 if err == nil && step.ExpectError != nil {
518 errored = true
519 t.Error(fmt.Sprintf(
520 "Step %d, no error received, but expected a match to:\n\n%s\n\n",
521 i, step.ExpectError))
522 break
523 }
524
421 // If there was an error, exit 525 // If there was an error, exit
422 if err != nil { 526 if err != nil {
423 // Perhaps we expected an error? Check if it matches 527 // Perhaps we expected an error? Check if it matches
@@ -485,6 +589,7 @@ func Test(t TestT, c TestCase) {
485 Config: lastStep.Config, 589 Config: lastStep.Config,
486 Check: c.CheckDestroy, 590 Check: c.CheckDestroy,
487 Destroy: true, 591 Destroy: true,
592 PreventDiskCleanup: lastStep.PreventDiskCleanup,
488 PreventPostDestroyRefresh: c.PreventPostDestroyRefresh, 593 PreventPostDestroyRefresh: c.PreventPostDestroyRefresh,
489 } 594 }
490 595
@@ -593,18 +698,12 @@ func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r
593 if err != nil { 698 if err != nil {
594 return err 699 return err
595 } 700 }
596 if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 { 701 if diags := ctx.Validate(); len(diags) > 0 {
597 if len(es) > 0 { 702 if diags.HasErrors() {
598 estrs := make([]string, len(es)) 703 return errwrap.Wrapf("config is invalid: {{err}}", diags.Err())
599 for i, e := range es {
600 estrs[i] = e.Error()
601 }
602 return fmt.Errorf(
603 "Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v",
604 ws, estrs)
605 } 704 }
606 705
607 log.Printf("[WARN] Config warnings: %#v", ws) 706 log.Printf("[WARN] Config warnings:\n%s", diags.Err().Error())
608 } 707 }
609 708
610 // Refresh! 709 // Refresh!
@@ -657,9 +756,7 @@ func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r
657 return nil 756 return nil
658} 757}
659 758
660func testModule( 759func testModule(opts terraform.ContextOpts, step TestStep) (*module.Tree, error) {
661 opts terraform.ContextOpts,
662 step TestStep) (*module.Tree, error) {
663 if step.PreConfig != nil { 760 if step.PreConfig != nil {
664 step.PreConfig() 761 step.PreConfig()
665 } 762 }
@@ -669,7 +766,12 @@ func testModule(
669 return nil, fmt.Errorf( 766 return nil, fmt.Errorf(
670 "Error creating temporary directory for config: %s", err) 767 "Error creating temporary directory for config: %s", err)
671 } 768 }
672 defer os.RemoveAll(cfgPath) 769
770 if step.PreventDiskCleanup {
771 log.Printf("[INFO] Skipping defer os.RemoveAll call")
772 } else {
773 defer os.RemoveAll(cfgPath)
774 }
673 775
674 // Write the configuration 776 // Write the configuration
675 cfgF, err := os.Create(filepath.Join(cfgPath, "main.tf")) 777 cfgF, err := os.Create(filepath.Join(cfgPath, "main.tf"))
@@ -693,10 +795,11 @@ func testModule(
693 } 795 }
694 796
695 // Load the modules 797 // Load the modules
696 modStorage := &getter.FolderStorage{ 798 modStorage := &module.Storage{
697 StorageDir: filepath.Join(cfgPath, ".tfmodules"), 799 StorageDir: filepath.Join(cfgPath, ".tfmodules"),
800 Mode: module.GetModeGet,
698 } 801 }
699 err = mod.Load(modStorage, module.GetModeGet) 802 err = mod.Load(modStorage)
700 if err != nil { 803 if err != nil {
701 return nil, fmt.Errorf("Error downloading modules: %s", err) 804 return nil, fmt.Errorf("Error downloading modules: %s", err)
702 } 805 }
@@ -771,12 +874,29 @@ func TestCheckResourceAttrSet(name, key string) TestCheckFunc {
771 return err 874 return err
772 } 875 }
773 876
774 if val, ok := is.Attributes[key]; ok && val != "" { 877 return testCheckResourceAttrSet(is, name, key)
775 return nil 878 }
879}
880
881// TestCheckModuleResourceAttrSet - as per TestCheckResourceAttrSet but with
882// support for non-root modules
883func TestCheckModuleResourceAttrSet(mp []string, name string, key string) TestCheckFunc {
884 return func(s *terraform.State) error {
885 is, err := modulePathPrimaryInstanceState(s, mp, name)
886 if err != nil {
887 return err
776 } 888 }
777 889
890 return testCheckResourceAttrSet(is, name, key)
891 }
892}
893
894func testCheckResourceAttrSet(is *terraform.InstanceState, name string, key string) error {
895 if val, ok := is.Attributes[key]; !ok || val == "" {
778 return fmt.Errorf("%s: Attribute '%s' expected to be set", name, key) 896 return fmt.Errorf("%s: Attribute '%s' expected to be set", name, key)
779 } 897 }
898
899 return nil
780} 900}
781 901
782// TestCheckResourceAttr is a TestCheckFunc which validates 902// TestCheckResourceAttr is a TestCheckFunc which validates
@@ -788,21 +908,37 @@ func TestCheckResourceAttr(name, key, value string) TestCheckFunc {
788 return err 908 return err
789 } 909 }
790 910
791 if v, ok := is.Attributes[key]; !ok || v != value { 911 return testCheckResourceAttr(is, name, key, value)
792 if !ok { 912 }
793 return fmt.Errorf("%s: Attribute '%s' not found", name, key) 913}
794 }
795 914
796 return fmt.Errorf( 915// TestCheckModuleResourceAttr - as per TestCheckResourceAttr but with
797 "%s: Attribute '%s' expected %#v, got %#v", 916// support for non-root modules
798 name, 917func TestCheckModuleResourceAttr(mp []string, name string, key string, value string) TestCheckFunc {
799 key, 918 return func(s *terraform.State) error {
800 value, 919 is, err := modulePathPrimaryInstanceState(s, mp, name)
801 v) 920 if err != nil {
921 return err
802 } 922 }
803 923
804 return nil 924 return testCheckResourceAttr(is, name, key, value)
925 }
926}
927
928func testCheckResourceAttr(is *terraform.InstanceState, name string, key string, value string) error {
929 if v, ok := is.Attributes[key]; !ok || v != value {
930 if !ok {
931 return fmt.Errorf("%s: Attribute '%s' not found", name, key)
932 }
933
934 return fmt.Errorf(
935 "%s: Attribute '%s' expected %#v, got %#v",
936 name,
937 key,
938 value,
939 v)
805 } 940 }
941 return nil
806} 942}
807 943
808// TestCheckNoResourceAttr is a TestCheckFunc which ensures that 944// TestCheckNoResourceAttr is a TestCheckFunc which ensures that
@@ -814,14 +950,31 @@ func TestCheckNoResourceAttr(name, key string) TestCheckFunc {
814 return err 950 return err
815 } 951 }
816 952
817 if _, ok := is.Attributes[key]; ok { 953 return testCheckNoResourceAttr(is, name, key)
818 return fmt.Errorf("%s: Attribute '%s' found when not expected", name, key) 954 }
955}
956
957// TestCheckModuleNoResourceAttr - as per TestCheckNoResourceAttr but with
958// support for non-root modules
959func TestCheckModuleNoResourceAttr(mp []string, name string, key string) TestCheckFunc {
960 return func(s *terraform.State) error {
961 is, err := modulePathPrimaryInstanceState(s, mp, name)
962 if err != nil {
963 return err
819 } 964 }
820 965
821 return nil 966 return testCheckNoResourceAttr(is, name, key)
822 } 967 }
823} 968}
824 969
970func testCheckNoResourceAttr(is *terraform.InstanceState, name string, key string) error {
971 if _, ok := is.Attributes[key]; ok {
972 return fmt.Errorf("%s: Attribute '%s' found when not expected", name, key)
973 }
974
975 return nil
976}
977
825// TestMatchResourceAttr is a TestCheckFunc which checks that the value 978// TestMatchResourceAttr is a TestCheckFunc which checks that the value
826// in state for the given name/key combination matches the given regex. 979// in state for the given name/key combination matches the given regex.
827func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc { 980func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc {
@@ -831,17 +984,34 @@ func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc {
831 return err 984 return err
832 } 985 }
833 986
834 if !r.MatchString(is.Attributes[key]) { 987 return testMatchResourceAttr(is, name, key, r)
835 return fmt.Errorf( 988 }
836 "%s: Attribute '%s' didn't match %q, got %#v", 989}
837 name, 990
838 key, 991// TestModuleMatchResourceAttr - as per TestMatchResourceAttr but with
839 r.String(), 992// support for non-root modules
840 is.Attributes[key]) 993func TestModuleMatchResourceAttr(mp []string, name string, key string, r *regexp.Regexp) TestCheckFunc {
994 return func(s *terraform.State) error {
995 is, err := modulePathPrimaryInstanceState(s, mp, name)
996 if err != nil {
997 return err
841 } 998 }
842 999
843 return nil 1000 return testMatchResourceAttr(is, name, key, r)
1001 }
1002}
1003
1004func testMatchResourceAttr(is *terraform.InstanceState, name string, key string, r *regexp.Regexp) error {
1005 if !r.MatchString(is.Attributes[key]) {
1006 return fmt.Errorf(
1007 "%s: Attribute '%s' didn't match %q, got %#v",
1008 name,
1009 key,
1010 r.String(),
1011 is.Attributes[key])
844 } 1012 }
1013
1014 return nil
845} 1015}
846 1016
847// TestCheckResourceAttrPtr is like TestCheckResourceAttr except the 1017// TestCheckResourceAttrPtr is like TestCheckResourceAttr except the
@@ -853,6 +1023,14 @@ func TestCheckResourceAttrPtr(name string, key string, value *string) TestCheckF
853 } 1023 }
854} 1024}
855 1025
1026// TestCheckModuleResourceAttrPtr - as per TestCheckResourceAttrPtr but with
1027// support for non-root modules
1028func TestCheckModuleResourceAttrPtr(mp []string, name string, key string, value *string) TestCheckFunc {
1029 return func(s *terraform.State) error {
1030 return TestCheckModuleResourceAttr(mp, name, key, *value)(s)
1031 }
1032}
1033
856// TestCheckResourceAttrPair is a TestCheckFunc which validates that the values 1034// TestCheckResourceAttrPair is a TestCheckFunc which validates that the values
857// in state for a pair of name/key combinations are equal. 1035// in state for a pair of name/key combinations are equal.
858func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc { 1036func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc {
@@ -861,33 +1039,57 @@ func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string
861 if err != nil { 1039 if err != nil {
862 return err 1040 return err
863 } 1041 }
864 vFirst, ok := isFirst.Attributes[keyFirst]
865 if !ok {
866 return fmt.Errorf("%s: Attribute '%s' not found", nameFirst, keyFirst)
867 }
868 1042
869 isSecond, err := primaryInstanceState(s, nameSecond) 1043 isSecond, err := primaryInstanceState(s, nameSecond)
870 if err != nil { 1044 if err != nil {
871 return err 1045 return err
872 } 1046 }
873 vSecond, ok := isSecond.Attributes[keySecond] 1047
874 if !ok { 1048 return testCheckResourceAttrPair(isFirst, nameFirst, keyFirst, isSecond, nameSecond, keySecond)
875 return fmt.Errorf("%s: Attribute '%s' not found", nameSecond, keySecond) 1049 }
1050}
1051
1052// TestCheckModuleResourceAttrPair - as per TestCheckResourceAttrPair but with
1053// support for non-root modules
1054func TestCheckModuleResourceAttrPair(mpFirst []string, nameFirst string, keyFirst string, mpSecond []string, nameSecond string, keySecond string) TestCheckFunc {
1055 return func(s *terraform.State) error {
1056 isFirst, err := modulePathPrimaryInstanceState(s, mpFirst, nameFirst)
1057 if err != nil {
1058 return err
876 } 1059 }
877 1060
878 if vFirst != vSecond { 1061 isSecond, err := modulePathPrimaryInstanceState(s, mpSecond, nameSecond)
879 return fmt.Errorf( 1062 if err != nil {
880 "%s: Attribute '%s' expected %#v, got %#v", 1063 return err
881 nameFirst,
882 keyFirst,
883 vSecond,
884 vFirst)
885 } 1064 }
886 1065
887 return nil 1066 return testCheckResourceAttrPair(isFirst, nameFirst, keyFirst, isSecond, nameSecond, keySecond)
888 } 1067 }
889} 1068}
890 1069
1070func testCheckResourceAttrPair(isFirst *terraform.InstanceState, nameFirst string, keyFirst string, isSecond *terraform.InstanceState, nameSecond string, keySecond string) error {
1071 vFirst, ok := isFirst.Attributes[keyFirst]
1072 if !ok {
1073 return fmt.Errorf("%s: Attribute '%s' not found", nameFirst, keyFirst)
1074 }
1075
1076 vSecond, ok := isSecond.Attributes[keySecond]
1077 if !ok {
1078 return fmt.Errorf("%s: Attribute '%s' not found", nameSecond, keySecond)
1079 }
1080
1081 if vFirst != vSecond {
1082 return fmt.Errorf(
1083 "%s: Attribute '%s' expected %#v, got %#v",
1084 nameFirst,
1085 keyFirst,
1086 vSecond,
1087 vFirst)
1088 }
1089
1090 return nil
1091}
1092
891// TestCheckOutput checks an output in the Terraform configuration 1093// TestCheckOutput checks an output in the Terraform configuration
892func TestCheckOutput(name, value string) TestCheckFunc { 1094func TestCheckOutput(name, value string) TestCheckFunc {
893 return func(s *terraform.State) error { 1095 return func(s *terraform.State) error {
@@ -936,23 +1138,43 @@ type TestT interface {
936 Error(args ...interface{}) 1138 Error(args ...interface{})
937 Fatal(args ...interface{}) 1139 Fatal(args ...interface{})
938 Skip(args ...interface{}) 1140 Skip(args ...interface{})
1141 Name() string
1142 Parallel()
939} 1143}
940 1144
941// This is set to true by unit tests to alter some behavior 1145// This is set to true by unit tests to alter some behavior
942var testTesting = false 1146var testTesting = false
943 1147
944// primaryInstanceState returns the primary instance state for the given resource name. 1148// modulePrimaryInstanceState returns the instance state for the given resource
945func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) { 1149// name in a ModuleState
946 ms := s.RootModule() 1150func modulePrimaryInstanceState(s *terraform.State, ms *terraform.ModuleState, name string) (*terraform.InstanceState, error) {
947 rs, ok := ms.Resources[name] 1151 rs, ok := ms.Resources[name]
948 if !ok { 1152 if !ok {
949 return nil, fmt.Errorf("Not found: %s", name) 1153 return nil, fmt.Errorf("Not found: %s in %s", name, ms.Path)
950 } 1154 }
951 1155
952 is := rs.Primary 1156 is := rs.Primary
953 if is == nil { 1157 if is == nil {
954 return nil, fmt.Errorf("No primary instance: %s", name) 1158 return nil, fmt.Errorf("No primary instance: %s in %s", name, ms.Path)
955 } 1159 }
956 1160
957 return is, nil 1161 return is, nil
958} 1162}
1163
1164// modulePathPrimaryInstanceState returns the primary instance state for the
1165// given resource name in a given module path.
1166func modulePathPrimaryInstanceState(s *terraform.State, mp []string, name string) (*terraform.InstanceState, error) {
1167 ms := s.ModuleByPath(mp)
1168 if ms == nil {
1169 return nil, fmt.Errorf("No module found at: %s", mp)
1170 }
1171
1172 return modulePrimaryInstanceState(s, ms, name)
1173}
1174
1175// primaryInstanceState returns the primary instance state for the given
1176// resource name in the root module.
1177func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) {
1178 ms := s.RootModule()
1179 return modulePrimaryInstanceState(s, ms, name)
1180}
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go
index 537a11c..033f126 100644
--- a/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go
@@ -1,10 +1,12 @@
1package resource 1package resource
2 2
3import ( 3import (
4 "errors"
4 "fmt" 5 "fmt"
5 "log" 6 "log"
6 "strings" 7 "strings"
7 8
9 "github.com/hashicorp/errwrap"
8 "github.com/hashicorp/terraform/terraform" 10 "github.com/hashicorp/terraform/terraform"
9) 11)
10 12
@@ -20,6 +22,14 @@ func testStep(
20 opts terraform.ContextOpts, 22 opts terraform.ContextOpts,
21 state *terraform.State, 23 state *terraform.State,
22 step TestStep) (*terraform.State, error) { 24 step TestStep) (*terraform.State, error) {
25 // Pre-taint any resources that have been defined in Taint, as long as this
26 // is not a destroy step.
27 if !step.Destroy {
28 if err := testStepTaint(state, step); err != nil {
29 return state, err
30 }
31 }
32
23 mod, err := testModule(opts, step) 33 mod, err := testModule(opts, step)
24 if err != nil { 34 if err != nil {
25 return state, err 35 return state, err
@@ -33,17 +43,12 @@ func testStep(
33 if err != nil { 43 if err != nil {
34 return state, fmt.Errorf("Error initializing context: %s", err) 44 return state, fmt.Errorf("Error initializing context: %s", err)
35 } 45 }
36 if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 { 46 if diags := ctx.Validate(); len(diags) > 0 {
37 if len(es) > 0 { 47 if diags.HasErrors() {
38 estrs := make([]string, len(es)) 48 return nil, errwrap.Wrapf("config is invalid: {{err}}", diags.Err())
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 } 49 }
46 log.Printf("[WARN] Config warnings: %#v", ws) 50
51 log.Printf("[WARN] Config warnings:\n%s", diags)
47 } 52 }
48 53
49 // Refresh! 54 // Refresh!
@@ -158,3 +163,19 @@ func testStep(
158 // Made it here? Good job test step! 163 // Made it here? Good job test step!
159 return state, nil 164 return state, nil
160} 165}
166
167func testStepTaint(state *terraform.State, step TestStep) error {
168 for _, p := range step.Taint {
169 m := state.RootModule()
170 if m == nil {
171 return errors.New("no state")
172 }
173 rs, ok := m.Resources[p]
174 if !ok {
175 return fmt.Errorf("resource %q not found in state", p)
176 }
177 log.Printf("[WARN] Test: Explicitly tainting resource %q", p)
178 rs.Taint()
179 }
180 return nil
181}
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
index 28ad105..94fef3c 100644
--- a/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go
@@ -16,15 +16,24 @@ func testStepImportState(
16 state *terraform.State, 16 state *terraform.State,
17 step TestStep) (*terraform.State, error) { 17 step TestStep) (*terraform.State, error) {
18 // Determine the ID to import 18 // Determine the ID to import
19 importId := step.ImportStateId 19 var importId string
20 if importId == "" { 20 switch {
21 case step.ImportStateIdFunc != nil:
22 var err error
23 importId, err = step.ImportStateIdFunc(state)
24 if err != nil {
25 return state, err
26 }
27 case step.ImportStateId != "":
28 importId = step.ImportStateId
29 default:
21 resource, err := testResource(step, state) 30 resource, err := testResource(step, state)
22 if err != nil { 31 if err != nil {
23 return state, err 32 return state, err
24 } 33 }
25
26 importId = resource.Primary.ID 34 importId = resource.Primary.ID
27 } 35 }
36
28 importPrefix := step.ImportStateIdPrefix 37 importPrefix := step.ImportStateIdPrefix
29 if importPrefix != "" { 38 if importPrefix != "" {
30 importId = fmt.Sprintf("%s%s", importPrefix, importId) 39 importId = fmt.Sprintf("%s%s", importPrefix, importId)
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/wait.go b/vendor/github.com/hashicorp/terraform/helper/resource/wait.go
index ca50e29..e56a515 100644
--- a/vendor/github.com/hashicorp/terraform/helper/resource/wait.go
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/wait.go
@@ -74,7 +74,7 @@ func RetryableError(err error) *RetryError {
74 return &RetryError{Err: err, Retryable: true} 74 return &RetryError{Err: err, Retryable: true}
75} 75}
76 76
77// NonRetryableError is a helper to create a RetryError that's _not)_ retryable 77// NonRetryableError is a helper to create a RetryError that's _not_ retryable
78// from a given error. 78// from a given error.
79func NonRetryableError(err error) *RetryError { 79func NonRetryableError(err error) *RetryError {
80 if err == nil { 80 if err == nil {
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/backend.go b/vendor/github.com/hashicorp/terraform/helper/schema/backend.go
index a0729c0..57fbba7 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/backend.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/backend.go
@@ -65,7 +65,7 @@ func (b *Backend) Configure(c *terraform.ResourceConfig) error {
65 65
66 // Get a ResourceData for this configuration. To do this, we actually 66 // Get a ResourceData for this configuration. To do this, we actually
67 // generate an intermediary "diff" although that is never exposed. 67 // generate an intermediary "diff" although that is never exposed.
68 diff, err := sm.Diff(nil, c) 68 diff, err := sm.Diff(nil, c, nil, nil)
69 if err != nil { 69 if err != nil {
70 return err 70 return err
71 } 71 }
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go b/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go
new file mode 100644
index 0000000..bf952f6
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go
@@ -0,0 +1,155 @@
1package schema
2
3import (
4 "fmt"
5
6 "github.com/hashicorp/terraform/config/configschema"
7 "github.com/zclconf/go-cty/cty"
8)
9
10// The functions and methods in this file are concerned with the conversion
11// of this package's schema model into the slightly-lower-level schema model
12// used by Terraform core for configuration parsing.
13
14// CoreConfigSchema lowers the receiver to the schema model expected by
15// Terraform core.
16//
17// This lower-level model has fewer features than the schema in this package,
18// describing only the basic structure of configuration and state values we
19// expect. The full schemaMap from this package is still required for full
20// validation, handling of default values, etc.
21//
22// This method presumes a schema that passes InternalValidate, and so may
23// panic or produce an invalid result if given an invalid schemaMap.
24func (m schemaMap) CoreConfigSchema() *configschema.Block {
25 if len(m) == 0 {
26 // We return an actual (empty) object here, rather than a nil,
27 // because a nil result would mean that we don't have a schema at
28 // all, rather than that we have an empty one.
29 return &configschema.Block{}
30 }
31
32 ret := &configschema.Block{
33 Attributes: map[string]*configschema.Attribute{},
34 BlockTypes: map[string]*configschema.NestedBlock{},
35 }
36
37 for name, schema := range m {
38 if schema.Elem == nil {
39 ret.Attributes[name] = schema.coreConfigSchemaAttribute()
40 continue
41 }
42 switch schema.Elem.(type) {
43 case *Schema:
44 ret.Attributes[name] = schema.coreConfigSchemaAttribute()
45 case *Resource:
46 ret.BlockTypes[name] = schema.coreConfigSchemaBlock()
47 default:
48 // Should never happen for a valid schema
49 panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", schema.Elem))
50 }
51 }
52
53 return ret
54}
55
56// coreConfigSchemaAttribute prepares a configschema.Attribute representation
57// of a schema. This is appropriate only for primitives or collections whose
58// Elem is an instance of Schema. Use coreConfigSchemaBlock for collections
59// whose elem is a whole resource.
60func (s *Schema) coreConfigSchemaAttribute() *configschema.Attribute {
61 return &configschema.Attribute{
62 Type: s.coreConfigSchemaType(),
63 Optional: s.Optional,
64 Required: s.Required,
65 Computed: s.Computed,
66 Sensitive: s.Sensitive,
67 }
68}
69
70// coreConfigSchemaBlock prepares a configschema.NestedBlock representation of
71// a schema. This is appropriate only for collections whose Elem is an instance
72// of Resource, and will panic otherwise.
73func (s *Schema) coreConfigSchemaBlock() *configschema.NestedBlock {
74 ret := &configschema.NestedBlock{}
75 if nested := s.Elem.(*Resource).CoreConfigSchema(); nested != nil {
76 ret.Block = *nested
77 }
78 switch s.Type {
79 case TypeList:
80 ret.Nesting = configschema.NestingList
81 case TypeSet:
82 ret.Nesting = configschema.NestingSet
83 case TypeMap:
84 ret.Nesting = configschema.NestingMap
85 default:
86 // Should never happen for a valid schema
87 panic(fmt.Errorf("invalid s.Type %s for s.Elem being resource", s.Type))
88 }
89
90 ret.MinItems = s.MinItems
91 ret.MaxItems = s.MaxItems
92
93 if s.Required && s.MinItems == 0 {
94 // configschema doesn't have a "required" representation for nested
95 // blocks, but we can fake it by requiring at least one item.
96 ret.MinItems = 1
97 }
98
99 return ret
100}
101
102// coreConfigSchemaType determines the core config schema type that corresponds
103// to a particular schema's type.
104func (s *Schema) coreConfigSchemaType() cty.Type {
105 switch s.Type {
106 case TypeString:
107 return cty.String
108 case TypeBool:
109 return cty.Bool
110 case TypeInt, TypeFloat:
111 // configschema doesn't distinguish int and float, so helper/schema
112 // will deal with this as an additional validation step after
113 // configuration has been parsed and decoded.
114 return cty.Number
115 case TypeList, TypeSet, TypeMap:
116 var elemType cty.Type
117 switch set := s.Elem.(type) {
118 case *Schema:
119 elemType = set.coreConfigSchemaType()
120 case *Resource:
121 // In practice we don't actually use this for normal schema
122 // construction because we construct a NestedBlock in that
123 // case instead. See schemaMap.CoreConfigSchema.
124 elemType = set.CoreConfigSchema().ImpliedType()
125 default:
126 if set != nil {
127 // Should never happen for a valid schema
128 panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", s.Elem))
129 }
130 // Some pre-existing schemas assume string as default, so we need
131 // to be compatible with them.
132 elemType = cty.String
133 }
134 switch s.Type {
135 case TypeList:
136 return cty.List(elemType)
137 case TypeSet:
138 return cty.Set(elemType)
139 case TypeMap:
140 return cty.Map(elemType)
141 default:
142 // can never get here in practice, due to the case we're inside
143 panic("invalid collection type")
144 }
145 default:
146 // should never happen for a valid schema
147 panic(fmt.Errorf("invalid Schema.Type %s", s.Type))
148 }
149}
150
151// CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema
152// on the resource's schema.
153func (r *Resource) CoreConfigSchema() *configschema.Block {
154 return schemaMap(r.Schema).CoreConfigSchema()
155}
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
index 5a03d2d..8d93750 100644
--- 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
@@ -32,7 +32,7 @@ func DataSourceResourceShim(name string, dataSource *Resource) *Resource {
32 32
33 // FIXME: Link to some further docs either on the website or in the 33 // FIXME: Link to some further docs either on the website or in the
34 // changelog, once such a thing exists. 34 // changelog, once such a thing exists.
35 dataSource.deprecationMessage = fmt.Sprintf( 35 dataSource.DeprecationMessage = fmt.Sprintf(
36 "using %s as a resource is deprecated; consider using the data source instead", 36 "using %s as a resource is deprecated; consider using the data source instead",
37 name, 37 name,
38 ) 38 )
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go
index 1660a67..b80b223 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go
@@ -126,6 +126,8 @@ func addrToSchema(addr []string, schemaMap map[string]*Schema) []*Schema {
126 switch v := current.Elem.(type) { 126 switch v := current.Elem.(type) {
127 case ValueType: 127 case ValueType:
128 current = &Schema{Type: v} 128 current = &Schema{Type: v}
129 case *Schema:
130 current, _ = current.Elem.(*Schema)
129 default: 131 default:
130 // maps default to string values. This is all we can have 132 // maps default to string values. This is all we can have
131 // if this is nested in another list or map. 133 // if this is nested in another list or map.
@@ -249,11 +251,10 @@ func readObjectField(
249} 251}
250 252
251// convert map values to the proper primitive type based on schema.Elem 253// convert map values to the proper primitive type based on schema.Elem
252func mapValuesToPrimitive(m map[string]interface{}, schema *Schema) error { 254func mapValuesToPrimitive(k string, m map[string]interface{}, schema *Schema) error {
253 255 elemType, err := getValueType(k, schema)
254 elemType := TypeString 256 if err != nil {
255 if et, ok := schema.Elem.(ValueType); ok { 257 return err
256 elemType = et
257 } 258 }
258 259
259 switch elemType { 260 switch elemType {
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
index f958bbc..55a301d 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go
@@ -206,7 +206,7 @@ func (r *ConfigFieldReader) readMap(k string, schema *Schema) (FieldReadResult,
206 panic(fmt.Sprintf("unknown type: %#v", mraw)) 206 panic(fmt.Sprintf("unknown type: %#v", mraw))
207 } 207 }
208 208
209 err := mapValuesToPrimitive(result, schema) 209 err := mapValuesToPrimitive(k, result, schema)
210 if err != nil { 210 if err != nil {
211 return FieldReadResult{}, nil 211 return FieldReadResult{}, nil
212 } 212 }
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
index 16bbae2..d558a5b 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go
@@ -29,29 +29,59 @@ type DiffFieldReader struct {
29 Diff *terraform.InstanceDiff 29 Diff *terraform.InstanceDiff
30 Source FieldReader 30 Source FieldReader
31 Schema map[string]*Schema 31 Schema map[string]*Schema
32
33 // cache for memoizing ReadField calls.
34 cache map[string]cachedFieldReadResult
35}
36
37type cachedFieldReadResult struct {
38 val FieldReadResult
39 err error
32} 40}
33 41
34func (r *DiffFieldReader) ReadField(address []string) (FieldReadResult, error) { 42func (r *DiffFieldReader) ReadField(address []string) (FieldReadResult, error) {
43 if r.cache == nil {
44 r.cache = make(map[string]cachedFieldReadResult)
45 }
46
47 // Create the cache key by joining around a value that isn't a valid part
48 // of an address. This assumes that the Source and Schema are not changed
49 // for the life of this DiffFieldReader.
50 cacheKey := strings.Join(address, "|")
51 if cached, ok := r.cache[cacheKey]; ok {
52 return cached.val, cached.err
53 }
54
35 schemaList := addrToSchema(address, r.Schema) 55 schemaList := addrToSchema(address, r.Schema)
36 if len(schemaList) == 0 { 56 if len(schemaList) == 0 {
57 r.cache[cacheKey] = cachedFieldReadResult{}
37 return FieldReadResult{}, nil 58 return FieldReadResult{}, nil
38 } 59 }
39 60
61 var res FieldReadResult
62 var err error
63
40 schema := schemaList[len(schemaList)-1] 64 schema := schemaList[len(schemaList)-1]
41 switch schema.Type { 65 switch schema.Type {
42 case TypeBool, TypeInt, TypeFloat, TypeString: 66 case TypeBool, TypeInt, TypeFloat, TypeString:
43 return r.readPrimitive(address, schema) 67 res, err = r.readPrimitive(address, schema)
44 case TypeList: 68 case TypeList:
45 return readListField(r, address, schema) 69 res, err = readListField(r, address, schema)
46 case TypeMap: 70 case TypeMap:
47 return r.readMap(address, schema) 71 res, err = r.readMap(address, schema)
48 case TypeSet: 72 case TypeSet:
49 return r.readSet(address, schema) 73 res, err = r.readSet(address, schema)
50 case typeObject: 74 case typeObject:
51 return readObjectField(r, address, schema.Elem.(map[string]*Schema)) 75 res, err = readObjectField(r, address, schema.Elem.(map[string]*Schema))
52 default: 76 default:
53 panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) 77 panic(fmt.Sprintf("Unknown type: %#v", schema.Type))
54 } 78 }
79
80 r.cache[cacheKey] = cachedFieldReadResult{
81 val: res,
82 err: err,
83 }
84 return res, err
55} 85}
56 86
57func (r *DiffFieldReader) readMap( 87func (r *DiffFieldReader) readMap(
@@ -92,7 +122,8 @@ func (r *DiffFieldReader) readMap(
92 result[k] = v.New 122 result[k] = v.New
93 } 123 }
94 124
95 err = mapValuesToPrimitive(result, schema) 125 key := address[len(address)-1]
126 err = mapValuesToPrimitive(key, result, schema)
96 if err != nil { 127 if err != nil {
97 return FieldReadResult{}, nil 128 return FieldReadResult{}, nil
98 } 129 }
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
index 9533981..054efe0 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go
@@ -61,7 +61,7 @@ func (r *MapFieldReader) readMap(k string, schema *Schema) (FieldReadResult, err
61 return true 61 return true
62 }) 62 })
63 63
64 err := mapValuesToPrimitive(result, schema) 64 err := mapValuesToPrimitive(k, result, schema)
65 if err != nil { 65 if err != nil {
66 return FieldReadResult{}, nil 66 return FieldReadResult{}, nil
67 } 67 }
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
index 689ed8d..814c7ba 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go
@@ -39,6 +39,19 @@ func (w *MapFieldWriter) unsafeWriteField(addr string, value string) {
39 w.result[addr] = value 39 w.result[addr] = value
40} 40}
41 41
42// clearTree clears a field and any sub-fields of the given address out of the
43// map. This should be used to reset some kind of complex structures (namely
44// sets) before writing to make sure that any conflicting data is removed (for
45// example, if the set was previously written to the writer's layer).
46func (w *MapFieldWriter) clearTree(addr []string) {
47 prefix := strings.Join(addr, ".") + "."
48 for k := range w.result {
49 if strings.HasPrefix(k, prefix) {
50 delete(w.result, k)
51 }
52 }
53}
54
42func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error { 55func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error {
43 w.lock.Lock() 56 w.lock.Lock()
44 defer w.lock.Unlock() 57 defer w.lock.Unlock()
@@ -115,6 +128,14 @@ func (w *MapFieldWriter) setList(
115 return fmt.Errorf("%s: %s", k, err) 128 return fmt.Errorf("%s: %s", k, err)
116 } 129 }
117 130
131 // Wipe the set from the current writer prior to writing if it exists.
132 // Multiple writes to the same layer is a lot safer for lists than sets due
133 // to the fact that indexes are always deterministic and the length will
134 // always be updated with the current length on the last write, but making
135 // sure we have a clean namespace removes any chance for edge cases to pop up
136 // and ensures that the last write to the set is the correct value.
137 w.clearTree(addr)
138
118 // Set the entire list. 139 // Set the entire list.
119 var err error 140 var err error
120 for i, elem := range vs { 141 for i, elem := range vs {
@@ -162,6 +183,10 @@ func (w *MapFieldWriter) setMap(
162 vs[mk.String()] = mv.Interface() 183 vs[mk.String()] = mv.Interface()
163 } 184 }
164 185
186 // Wipe this address tree. The contents of the map should always reflect the
187 // last write made to it.
188 w.clearTree(addr)
189
165 // Remove the pure key since we're setting the full map value 190 // Remove the pure key since we're setting the full map value
166 delete(w.result, k) 191 delete(w.result, k)
167 192
@@ -308,6 +333,13 @@ func (w *MapFieldWriter) setSet(
308 value = s 333 value = s
309 } 334 }
310 335
336 // Clear any keys that match the set address first. This is necessary because
337 // it's always possible and sometimes may be necessary to write to a certain
338 // writer layer more than once with different set data each time, which will
339 // lead to different keys being inserted, which can lead to determinism
340 // problems when the old data isn't wiped first.
341 w.clearTree(addr)
342
311 for code, elem := range value.(*Set).m { 343 for code, elem := range value.(*Set).m {
312 if err := w.set(append(addrCopy, code), elem); err != nil { 344 if err := w.set(append(addrCopy, code), elem); err != nil {
313 return err 345 return err
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go b/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go
index 3a97629..38cd8c7 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go
@@ -2,7 +2,7 @@
2 2
3package schema 3package schema
4 4
5import "fmt" 5import "strconv"
6 6
7const ( 7const (
8 _getSource_name_0 = "getSourceStategetSourceConfig" 8 _getSource_name_0 = "getSourceStategetSourceConfig"
@@ -13,8 +13,6 @@ const (
13 13
14var ( 14var (
15 _getSource_index_0 = [...]uint8{0, 14, 29} 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} 16 _getSource_index_3 = [...]uint8{0, 18, 32}
19) 17)
20 18
@@ -31,6 +29,6 @@ func (i getSource) String() string {
31 i -= 15 29 i -= 15
32 return _getSource_name_3[_getSource_index_3[i]:_getSource_index_3[i+1]] 30 return _getSource_name_3[_getSource_index_3[i]:_getSource_index_3[i+1]]
33 default: 31 default:
34 return fmt.Sprintf("getSource(%d)", i) 32 return "getSource(" + strconv.FormatInt(int64(i), 10) + ")"
35 } 33 }
36} 34}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provider.go b/vendor/github.com/hashicorp/terraform/helper/schema/provider.go
index fb28b41..6cd325d 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/provider.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/provider.go
@@ -9,6 +9,7 @@ import (
9 9
10 "github.com/hashicorp/go-multierror" 10 "github.com/hashicorp/go-multierror"
11 "github.com/hashicorp/terraform/config" 11 "github.com/hashicorp/terraform/config"
12 "github.com/hashicorp/terraform/config/configschema"
12 "github.com/hashicorp/terraform/terraform" 13 "github.com/hashicorp/terraform/terraform"
13) 14)
14 15
@@ -58,7 +59,7 @@ type Provider struct {
58 59
59 meta interface{} 60 meta interface{}
60 61
61 // a mutex is required because TestReset can directly repalce the stopCtx 62 // a mutex is required because TestReset can directly replace the stopCtx
62 stopMu sync.Mutex 63 stopMu sync.Mutex
63 stopCtx context.Context 64 stopCtx context.Context
64 stopCtxCancel context.CancelFunc 65 stopCtxCancel context.CancelFunc
@@ -185,6 +186,29 @@ func (p *Provider) TestReset() error {
185 return nil 186 return nil
186} 187}
187 188
189// GetSchema implementation of terraform.ResourceProvider interface
190func (p *Provider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.ProviderSchema, error) {
191 resourceTypes := map[string]*configschema.Block{}
192 dataSources := map[string]*configschema.Block{}
193
194 for _, name := range req.ResourceTypes {
195 if r, exists := p.ResourcesMap[name]; exists {
196 resourceTypes[name] = r.CoreConfigSchema()
197 }
198 }
199 for _, name := range req.DataSources {
200 if r, exists := p.DataSourcesMap[name]; exists {
201 dataSources[name] = r.CoreConfigSchema()
202 }
203 }
204
205 return &terraform.ProviderSchema{
206 Provider: schemaMap(p.Schema).CoreConfigSchema(),
207 ResourceTypes: resourceTypes,
208 DataSources: dataSources,
209 }, nil
210}
211
188// Input implementation of terraform.ResourceProvider interface. 212// Input implementation of terraform.ResourceProvider interface.
189func (p *Provider) Input( 213func (p *Provider) Input(
190 input terraform.UIInput, 214 input terraform.UIInput,
@@ -227,7 +251,7 @@ func (p *Provider) Configure(c *terraform.ResourceConfig) error {
227 251
228 // Get a ResourceData for this configuration. To do this, we actually 252 // Get a ResourceData for this configuration. To do this, we actually
229 // generate an intermediary "diff" although that is never exposed. 253 // generate an intermediary "diff" although that is never exposed.
230 diff, err := sm.Diff(nil, c) 254 diff, err := sm.Diff(nil, c, nil, p.meta)
231 if err != nil { 255 if err != nil {
232 return err 256 return err
233 } 257 }
@@ -269,7 +293,7 @@ func (p *Provider) Diff(
269 return nil, fmt.Errorf("unknown resource type: %s", info.Type) 293 return nil, fmt.Errorf("unknown resource type: %s", info.Type)
270 } 294 }
271 295
272 return r.Diff(s, c) 296 return r.Diff(s, c, p.meta)
273} 297}
274 298
275// Refresh implementation of terraform.ResourceProvider interface. 299// Refresh implementation of terraform.ResourceProvider interface.
@@ -305,6 +329,10 @@ func (p *Provider) Resources() []terraform.ResourceType {
305 result = append(result, terraform.ResourceType{ 329 result = append(result, terraform.ResourceType{
306 Name: k, 330 Name: k,
307 Importable: resource.Importer != nil, 331 Importable: resource.Importer != nil,
332
333 // Indicates that a provider is compiled against a new enough
334 // version of core to support the GetSchema method.
335 SchemaAvailable: true,
308 }) 336 })
309 } 337 }
310 338
@@ -382,7 +410,7 @@ func (p *Provider) ReadDataDiff(
382 return nil, fmt.Errorf("unknown data source: %s", info.Type) 410 return nil, fmt.Errorf("unknown data source: %s", info.Type)
383 } 411 }
384 412
385 return r.Diff(nil, c) 413 return r.Diff(nil, c, p.meta)
386} 414}
387 415
388// RefreshData implementation of terraform.ResourceProvider interface. 416// RefreshData implementation of terraform.ResourceProvider interface.
@@ -410,6 +438,10 @@ func (p *Provider) DataSources() []terraform.DataSource {
410 for _, k := range keys { 438 for _, k := range keys {
411 result = append(result, terraform.DataSource{ 439 result = append(result, terraform.DataSource{
412 Name: k, 440 Name: k,
441
442 // Indicates that a provider is compiled against a new enough
443 // version of core to support the GetSchema method.
444 SchemaAvailable: true,
413 }) 445 })
414 } 446 }
415 447
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go
index 476192e..a8d42db 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go
@@ -146,7 +146,7 @@ func (p *Provisioner) Apply(
146 } 146 }
147 147
148 sm := schemaMap(p.ConnSchema) 148 sm := schemaMap(p.ConnSchema)
149 diff, err := sm.Diff(nil, terraform.NewResourceConfig(c)) 149 diff, err := sm.Diff(nil, terraform.NewResourceConfig(c), nil, nil)
150 if err != nil { 150 if err != nil {
151 return err 151 return err
152 } 152 }
@@ -160,7 +160,7 @@ func (p *Provisioner) Apply(
160 // Build the configuration data. Doing this requires making a "diff" 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. 161 // even though that's never used. We use that just to get the correct types.
162 configMap := schemaMap(p.Schema) 162 configMap := schemaMap(p.Schema)
163 diff, err := configMap.Diff(nil, c) 163 diff, err := configMap.Diff(nil, c, nil, nil)
164 if err != nil { 164 if err != nil {
165 return err 165 return err
166 } 166 }
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go
index ddba109..d3be2d6 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go
@@ -85,6 +85,37 @@ type Resource struct {
85 Delete DeleteFunc 85 Delete DeleteFunc
86 Exists ExistsFunc 86 Exists ExistsFunc
87 87
88 // CustomizeDiff is a custom function for working with the diff that
89 // Terraform has created for this resource - it can be used to customize the
90 // diff that has been created, diff values not controlled by configuration,
91 // or even veto the diff altogether and abort the plan. It is passed a
92 // *ResourceDiff, a structure similar to ResourceData but lacking most write
93 // functions like Set, while introducing new functions that work with the
94 // diff such as SetNew, SetNewComputed, and ForceNew.
95 //
96 // The phases Terraform runs this in, and the state available via functions
97 // like Get and GetChange, are as follows:
98 //
99 // * New resource: One run with no state
100 // * Existing resource: One run with state
101 // * Existing resource, forced new: One run with state (before ForceNew),
102 // then one run without state (as if new resource)
103 // * Tainted resource: No runs (custom diff logic is skipped)
104 // * Destroy: No runs (standard diff logic is skipped on destroy diffs)
105 //
106 // This function needs to be resilient to support all scenarios.
107 //
108 // If this function needs to access external API resources, remember to flag
109 // the RequiresRefresh attribute mentioned below to ensure that
110 // -refresh=false is blocked when running plan or apply, as this means that
111 // this resource requires refresh-like behaviour to work effectively.
112 //
113 // For the most part, only computed fields can be customized by this
114 // function.
115 //
116 // This function is only allowed on regular resources (not data sources).
117 CustomizeDiff CustomizeDiffFunc
118
88 // Importer is the ResourceImporter implementation for this resource. 119 // Importer is the ResourceImporter implementation for this resource.
89 // If this is nil, then this resource does not support importing. If 120 // If this is nil, then this resource does not support importing. If
90 // this is non-nil, then it supports importing and ResourceImporter 121 // this is non-nil, then it supports importing and ResourceImporter
@@ -93,9 +124,7 @@ type Resource struct {
93 Importer *ResourceImporter 124 Importer *ResourceImporter
94 125
95 // If non-empty, this string is emitted as a warning during Validate. 126 // If non-empty, this string is emitted as a warning during Validate.
96 // This is a private interface for now, for use by DataSourceResourceShim, 127 DeprecationMessage string
97 // and not for general use. (But maybe later...)
98 deprecationMessage string
99 128
100 // Timeouts allow users to specify specific time durations in which an 129 // Timeouts allow users to specify specific time durations in which an
101 // operation should time out, to allow them to extend an action to suit their 130 // operation should time out, to allow them to extend an action to suit their
@@ -126,6 +155,9 @@ type ExistsFunc func(*ResourceData, interface{}) (bool, error)
126type StateMigrateFunc func( 155type StateMigrateFunc func(
127 int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error) 156 int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error)
128 157
158// See Resource documentation.
159type CustomizeDiffFunc func(*ResourceDiff, interface{}) error
160
129// Apply creates, updates, and/or deletes a resource. 161// Apply creates, updates, and/or deletes a resource.
130func (r *Resource) Apply( 162func (r *Resource) Apply(
131 s *terraform.InstanceState, 163 s *terraform.InstanceState,
@@ -202,11 +234,11 @@ func (r *Resource) Apply(
202 return r.recordCurrentSchemaVersion(data.State()), err 234 return r.recordCurrentSchemaVersion(data.State()), err
203} 235}
204 236
205// Diff returns a diff of this resource and is API compatible with the 237// Diff returns a diff of this resource.
206// ResourceProvider interface.
207func (r *Resource) Diff( 238func (r *Resource) Diff(
208 s *terraform.InstanceState, 239 s *terraform.InstanceState,
209 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 240 c *terraform.ResourceConfig,
241 meta interface{}) (*terraform.InstanceDiff, error) {
210 242
211 t := &ResourceTimeout{} 243 t := &ResourceTimeout{}
212 err := t.ConfigDecode(r, c) 244 err := t.ConfigDecode(r, c)
@@ -215,7 +247,7 @@ func (r *Resource) Diff(
215 return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err) 247 return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err)
216 } 248 }
217 249
218 instanceDiff, err := schemaMap(r.Schema).Diff(s, c) 250 instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta)
219 if err != nil { 251 if err != nil {
220 return instanceDiff, err 252 return instanceDiff, err
221 } 253 }
@@ -235,8 +267,8 @@ func (r *Resource) Diff(
235func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) { 267func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) {
236 warns, errs := schemaMap(r.Schema).Validate(c) 268 warns, errs := schemaMap(r.Schema).Validate(c)
237 269
238 if r.deprecationMessage != "" { 270 if r.DeprecationMessage != "" {
239 warns = append(warns, r.deprecationMessage) 271 warns = append(warns, r.DeprecationMessage)
240 } 272 }
241 273
242 return warns, errs 274 return warns, errs
@@ -248,7 +280,6 @@ func (r *Resource) ReadDataApply(
248 d *terraform.InstanceDiff, 280 d *terraform.InstanceDiff,
249 meta interface{}, 281 meta interface{},
250) (*terraform.InstanceState, error) { 282) (*terraform.InstanceState, error) {
251
252 // Data sources are always built completely from scratch 283 // Data sources are always built completely from scratch
253 // on each read, so the source state is always nil. 284 // on each read, so the source state is always nil.
254 data, err := schemaMap(r.Schema).Data(nil, d) 285 data, err := schemaMap(r.Schema).Data(nil, d)
@@ -346,6 +377,11 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error
346 if r.Create != nil || r.Update != nil || r.Delete != nil { 377 if r.Create != nil || r.Update != nil || r.Delete != nil {
347 return fmt.Errorf("must not implement Create, Update or Delete") 378 return fmt.Errorf("must not implement Create, Update or Delete")
348 } 379 }
380
381 // CustomizeDiff cannot be defined for read-only resources
382 if r.CustomizeDiff != nil {
383 return fmt.Errorf("cannot implement CustomizeDiff")
384 }
349 } 385 }
350 386
351 tsm := topSchemaMap 387 tsm := topSchemaMap
@@ -393,19 +429,43 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error
393 return err 429 return err
394 } 430 }
395 } 431 }
432
433 for k, f := range tsm {
434 if isReservedResourceFieldName(k, f) {
435 return fmt.Errorf("%s is a reserved field name", k)
436 }
437 }
396 } 438 }
397 439
398 // Resource-specific checks 440 // Data source
399 for k, _ := range tsm { 441 if r.isTopLevel() && !writable {
400 if isReservedResourceFieldName(k) { 442 tsm = schemaMap(r.Schema)
401 return fmt.Errorf("%s is a reserved field name for a resource", k) 443 for k, _ := range tsm {
444 if isReservedDataSourceFieldName(k) {
445 return fmt.Errorf("%s is a reserved field name", k)
446 }
402 } 447 }
403 } 448 }
404 449
405 return schemaMap(r.Schema).InternalValidate(tsm) 450 return schemaMap(r.Schema).InternalValidate(tsm)
406} 451}
407 452
408func isReservedResourceFieldName(name string) bool { 453func isReservedDataSourceFieldName(name string) bool {
454 for _, reservedName := range config.ReservedDataSourceFields {
455 if name == reservedName {
456 return true
457 }
458 }
459 return false
460}
461
462func isReservedResourceFieldName(name string, s *Schema) bool {
463 // Allow phasing out "id"
464 // See https://github.com/terraform-providers/terraform-provider-aws/pull/1626#issuecomment-328881415
465 if name == "id" && (s.Deprecated != "" || s.Removed != "") {
466 return false
467 }
468
409 for _, reservedName := range config.ReservedResourceFields { 469 for _, reservedName := range config.ReservedResourceFields {
410 if name == reservedName { 470 if name == reservedName {
411 return true 471 return true
@@ -430,6 +490,12 @@ func (r *Resource) Data(s *terraform.InstanceState) *ResourceData {
430 panic(err) 490 panic(err)
431 } 491 }
432 492
493 // load the Resource timeouts
494 result.timeouts = r.Timeouts
495 if result.timeouts == nil {
496 result.timeouts = &ResourceTimeout{}
497 }
498
433 // Set the schema version to latest by default 499 // Set the schema version to latest by default
434 result.meta = map[string]interface{}{ 500 result.meta = map[string]interface{}{
435 "schema_version": strconv.Itoa(r.SchemaVersion), 501 "schema_version": strconv.Itoa(r.SchemaVersion),
@@ -450,7 +516,7 @@ func (r *Resource) TestResourceData() *ResourceData {
450// Returns true if the resource is "top level" i.e. not a sub-resource. 516// Returns true if the resource is "top level" i.e. not a sub-resource.
451func (r *Resource) isTopLevel() bool { 517func (r *Resource) isTopLevel() bool {
452 // TODO: This is a heuristic; replace with a definitive attribute? 518 // TODO: This is a heuristic; replace with a definitive attribute?
453 return r.Create != nil 519 return (r.Create != nil || r.Read != nil)
454} 520}
455 521
456// Determines if a given InstanceState needs to be migrated by checking the 522// Determines if a given InstanceState needs to be migrated by checking the
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go
index b2bc8f6..6cc01ee 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go
@@ -35,6 +35,8 @@ type ResourceData struct {
35 partialMap map[string]struct{} 35 partialMap map[string]struct{}
36 once sync.Once 36 once sync.Once
37 isNew bool 37 isNew bool
38
39 panicOnError bool
38} 40}
39 41
40// getResult is the internal structure that is generated when a Get 42// getResult is the internal structure that is generated when a Get
@@ -104,6 +106,22 @@ func (d *ResourceData) GetOk(key string) (interface{}, bool) {
104 return r.Value, exists 106 return r.Value, exists
105} 107}
106 108
109// GetOkExists returns the data for a given key and whether or not the key
110// has been set to a non-zero value. This is only useful for determining
111// if boolean attributes have been set, if they are Optional but do not
112// have a Default value.
113//
114// This is nearly the same function as GetOk, yet it does not check
115// for the zero value of the attribute's type. This allows for attributes
116// without a default, to fully check for a literal assignment, regardless
117// of the zero-value for that type.
118// This should only be used if absolutely required/needed.
119func (d *ResourceData) GetOkExists(key string) (interface{}, bool) {
120 r := d.getRaw(key, getSourceSet)
121 exists := r.Exists && !r.Computed
122 return r.Value, exists
123}
124
107func (d *ResourceData) getRaw(key string, level getSource) getResult { 125func (d *ResourceData) getRaw(key string, level getSource) getResult {
108 var parts []string 126 var parts []string
109 if key != "" { 127 if key != "" {
@@ -168,7 +186,11 @@ func (d *ResourceData) Set(key string, value interface{}) error {
168 } 186 }
169 } 187 }
170 188
171 return d.setWriter.WriteField(strings.Split(key, "."), value) 189 err := d.setWriter.WriteField(strings.Split(key, "."), value)
190 if err != nil && d.panicOnError {
191 panic(err)
192 }
193 return err
172} 194}
173 195
174// SetPartial adds the key to the final state output while 196// SetPartial adds the key to the final state output while
@@ -293,6 +315,7 @@ func (d *ResourceData) State() *terraform.InstanceState {
293 315
294 mapW := &MapFieldWriter{Schema: d.schema} 316 mapW := &MapFieldWriter{Schema: d.schema}
295 if err := mapW.WriteField(nil, rawMap); err != nil { 317 if err := mapW.WriteField(nil, rawMap); err != nil {
318 log.Printf("[ERR] Error writing fields: %s", err)
296 return nil 319 return nil
297 } 320 }
298 321
@@ -344,6 +367,13 @@ func (d *ResourceData) State() *terraform.InstanceState {
344func (d *ResourceData) Timeout(key string) time.Duration { 367func (d *ResourceData) Timeout(key string) time.Duration {
345 key = strings.ToLower(key) 368 key = strings.ToLower(key)
346 369
370 // System default of 20 minutes
371 defaultTimeout := 20 * time.Minute
372
373 if d.timeouts == nil {
374 return defaultTimeout
375 }
376
347 var timeout *time.Duration 377 var timeout *time.Duration
348 switch key { 378 switch key {
349 case TimeoutCreate: 379 case TimeoutCreate:
@@ -364,8 +394,7 @@ func (d *ResourceData) Timeout(key string) time.Duration {
364 return *d.timeouts.Default 394 return *d.timeouts.Default
365 } 395 }
366 396
367 // Return system default of 20 minutes 397 return defaultTimeout
368 return 20 * time.Minute
369} 398}
370 399
371func (d *ResourceData) init() { 400func (d *ResourceData) init() {
@@ -423,7 +452,7 @@ func (d *ResourceData) init() {
423} 452}
424 453
425func (d *ResourceData) diffChange( 454func (d *ResourceData) diffChange(
426 k string) (interface{}, interface{}, bool, bool) { 455 k string) (interface{}, interface{}, bool, bool, bool) {
427 // Get the change between the state and the config. 456 // Get the change between the state and the config.
428 o, n := d.getChange(k, getSourceState, getSourceConfig|getSourceExact) 457 o, n := d.getChange(k, getSourceState, getSourceConfig|getSourceExact)
429 if !o.Exists { 458 if !o.Exists {
@@ -434,7 +463,7 @@ func (d *ResourceData) diffChange(
434 } 463 }
435 464
436 // Return the old, new, and whether there is a change 465 // Return the old, new, and whether there is a change
437 return o.Value, n.Value, !reflect.DeepEqual(o.Value, n.Value), n.Computed 466 return o.Value, n.Value, !reflect.DeepEqual(o.Value, n.Value), n.Computed, false
438} 467}
439 468
440func (d *ResourceData) getChange( 469func (d *ResourceData) getChange(
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go
new file mode 100644
index 0000000..7db3dec
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go
@@ -0,0 +1,559 @@
1package schema
2
3import (
4 "errors"
5 "fmt"
6 "reflect"
7 "strings"
8 "sync"
9
10 "github.com/hashicorp/terraform/terraform"
11)
12
13// newValueWriter is a minor re-implementation of MapFieldWriter to include
14// keys that should be marked as computed, to represent the new part of a
15// pseudo-diff.
16type newValueWriter struct {
17 *MapFieldWriter
18
19 // A list of keys that should be marked as computed.
20 computedKeys map[string]bool
21
22 // A lock to prevent races on writes. The underlying writer will have one as
23 // well - this is for computed keys.
24 lock sync.Mutex
25
26 // To be used with init.
27 once sync.Once
28}
29
30// init performs any initialization tasks for the newValueWriter.
31func (w *newValueWriter) init() {
32 if w.computedKeys == nil {
33 w.computedKeys = make(map[string]bool)
34 }
35}
36
37// WriteField overrides MapValueWriter's WriteField, adding the ability to flag
38// the address as computed.
39func (w *newValueWriter) WriteField(address []string, value interface{}, computed bool) error {
40 // Fail the write if we have a non-nil value and computed is true.
41 // NewComputed values should not have a value when written.
42 if value != nil && computed {
43 return errors.New("Non-nil value with computed set")
44 }
45
46 if err := w.MapFieldWriter.WriteField(address, value); err != nil {
47 return err
48 }
49
50 w.once.Do(w.init)
51
52 w.lock.Lock()
53 defer w.lock.Unlock()
54 if computed {
55 w.computedKeys[strings.Join(address, ".")] = true
56 }
57 return nil
58}
59
60// ComputedKeysMap returns the underlying computed keys map.
61func (w *newValueWriter) ComputedKeysMap() map[string]bool {
62 w.once.Do(w.init)
63 return w.computedKeys
64}
65
66// newValueReader is a minor re-implementation of MapFieldReader and is the
67// read counterpart to MapValueWriter, allowing the read of keys flagged as
68// computed to accommodate the diff override logic in ResourceDiff.
69type newValueReader struct {
70 *MapFieldReader
71
72 // The list of computed keys from a newValueWriter.
73 computedKeys map[string]bool
74}
75
76// ReadField reads the values from the underlying writer, returning the
77// computed value if it is found as well.
78func (r *newValueReader) ReadField(address []string) (FieldReadResult, error) {
79 addrKey := strings.Join(address, ".")
80 v, err := r.MapFieldReader.ReadField(address)
81 if err != nil {
82 return FieldReadResult{}, err
83 }
84 for computedKey := range r.computedKeys {
85 if childAddrOf(addrKey, computedKey) {
86 if strings.HasSuffix(addrKey, ".#") {
87 // This is a count value for a list or set that has been marked as
88 // computed, or a sub-list/sub-set of a complex resource that has
89 // been marked as computed. We need to pass through to other readers
90 // so that an accurate previous count can be fetched for the diff.
91 v.Exists = false
92 }
93 v.Computed = true
94 }
95 }
96
97 return v, nil
98}
99
100// ResourceDiff is used to query and make custom changes to an in-flight diff.
101// It can be used to veto particular changes in the diff, customize the diff
102// that has been created, or diff values not controlled by config.
103//
104// The object functions similar to ResourceData, however most notably lacks
105// Set, SetPartial, and Partial, as it should be used to change diff values
106// only. Most other first-class ResourceData functions exist, namely Get,
107// GetOk, HasChange, and GetChange exist.
108//
109// All functions in ResourceDiff, save for ForceNew, can only be used on
110// computed fields.
111type ResourceDiff struct {
112 // The schema for the resource being worked on.
113 schema map[string]*Schema
114
115 // The current config for this resource.
116 config *terraform.ResourceConfig
117
118 // The state for this resource as it exists post-refresh, after the initial
119 // diff.
120 state *terraform.InstanceState
121
122 // The diff created by Terraform. This diff is used, along with state,
123 // config, and custom-set diff data, to provide a multi-level reader
124 // experience similar to ResourceData.
125 diff *terraform.InstanceDiff
126
127 // The internal reader structure that contains the state, config, the default
128 // diff, and the new diff.
129 multiReader *MultiLevelFieldReader
130
131 // A writer that writes overridden new fields.
132 newWriter *newValueWriter
133
134 // Tracks which keys have been updated by ResourceDiff to ensure that the
135 // diff does not get re-run on keys that were not touched, or diffs that were
136 // just removed (re-running on the latter would just roll back the removal).
137 updatedKeys map[string]bool
138
139 // Tracks which keys were flagged as forceNew. These keys are not saved in
140 // newWriter, but we need to track them so that they can be re-diffed later.
141 forcedNewKeys map[string]bool
142}
143
144// newResourceDiff creates a new ResourceDiff instance.
145func newResourceDiff(schema map[string]*Schema, config *terraform.ResourceConfig, state *terraform.InstanceState, diff *terraform.InstanceDiff) *ResourceDiff {
146 d := &ResourceDiff{
147 config: config,
148 state: state,
149 diff: diff,
150 schema: schema,
151 }
152
153 d.newWriter = &newValueWriter{
154 MapFieldWriter: &MapFieldWriter{Schema: d.schema},
155 }
156 readers := make(map[string]FieldReader)
157 var stateAttributes map[string]string
158 if d.state != nil {
159 stateAttributes = d.state.Attributes
160 readers["state"] = &MapFieldReader{
161 Schema: d.schema,
162 Map: BasicMapReader(stateAttributes),
163 }
164 }
165 if d.config != nil {
166 readers["config"] = &ConfigFieldReader{
167 Schema: d.schema,
168 Config: d.config,
169 }
170 }
171 if d.diff != nil {
172 readers["diff"] = &DiffFieldReader{
173 Schema: d.schema,
174 Diff: d.diff,
175 Source: &MultiLevelFieldReader{
176 Levels: []string{"state", "config"},
177 Readers: readers,
178 },
179 }
180 }
181 readers["newDiff"] = &newValueReader{
182 MapFieldReader: &MapFieldReader{
183 Schema: d.schema,
184 Map: BasicMapReader(d.newWriter.Map()),
185 },
186 computedKeys: d.newWriter.ComputedKeysMap(),
187 }
188 d.multiReader = &MultiLevelFieldReader{
189 Levels: []string{
190 "state",
191 "config",
192 "diff",
193 "newDiff",
194 },
195
196 Readers: readers,
197 }
198
199 d.updatedKeys = make(map[string]bool)
200 d.forcedNewKeys = make(map[string]bool)
201
202 return d
203}
204
205// UpdatedKeys returns the keys that were updated by this ResourceDiff run.
206// These are the only keys that a diff should be re-calculated for.
207//
208// This is the combined result of both keys for which diff values were updated
209// for or cleared, and also keys that were flagged to be re-diffed as a result
210// of ForceNew.
211func (d *ResourceDiff) UpdatedKeys() []string {
212 var s []string
213 for k := range d.updatedKeys {
214 s = append(s, k)
215 }
216 for k := range d.forcedNewKeys {
217 for _, l := range s {
218 if k == l {
219 break
220 }
221 }
222 s = append(s, k)
223 }
224 return s
225}
226
227// Clear wipes the diff for a particular key. It is called by ResourceDiff's
228// functionality to remove any possibility of conflicts, but can be called on
229// its own to just remove a specific key from the diff completely.
230//
231// Note that this does not wipe an override. This function is only allowed on
232// computed keys.
233func (d *ResourceDiff) Clear(key string) error {
234 if err := d.checkKey(key, "Clear", true); err != nil {
235 return err
236 }
237
238 return d.clear(key)
239}
240
241func (d *ResourceDiff) clear(key string) error {
242 // Check the schema to make sure that this key exists first.
243 schemaL := addrToSchema(strings.Split(key, "."), d.schema)
244 if len(schemaL) == 0 {
245 return fmt.Errorf("%s is not a valid key", key)
246 }
247
248 for k := range d.diff.Attributes {
249 if strings.HasPrefix(k, key) {
250 delete(d.diff.Attributes, k)
251 }
252 }
253 return nil
254}
255
256// GetChangedKeysPrefix helps to implement Resource.CustomizeDiff
257// where we need to act on all nested fields
258// without calling out each one separately
259func (d *ResourceDiff) GetChangedKeysPrefix(prefix string) []string {
260 keys := make([]string, 0)
261 for k := range d.diff.Attributes {
262 if strings.HasPrefix(k, prefix) {
263 keys = append(keys, k)
264 }
265 }
266 return keys
267}
268
269// diffChange helps to implement resourceDiffer and derives its change values
270// from ResourceDiff's own change data, in addition to existing diff, config, and state.
271func (d *ResourceDiff) diffChange(key string) (interface{}, interface{}, bool, bool, bool) {
272 old, new, customized := d.getChange(key)
273
274 if !old.Exists {
275 old.Value = nil
276 }
277 if !new.Exists || d.removed(key) {
278 new.Value = nil
279 }
280
281 return old.Value, new.Value, !reflect.DeepEqual(old.Value, new.Value), new.Computed, customized
282}
283
284// SetNew is used to set a new diff value for the mentioned key. The value must
285// be correct for the attribute's schema (mostly relevant for maps, lists, and
286// sets). The original value from the state is used as the old value.
287//
288// This function is only allowed on computed attributes.
289func (d *ResourceDiff) SetNew(key string, value interface{}) error {
290 if err := d.checkKey(key, "SetNew", false); err != nil {
291 return err
292 }
293
294 return d.setDiff(key, value, false)
295}
296
297// SetNewComputed functions like SetNew, except that it blanks out a new value
298// and marks it as computed.
299//
300// This function is only allowed on computed attributes.
301func (d *ResourceDiff) SetNewComputed(key string) error {
302 if err := d.checkKey(key, "SetNewComputed", false); err != nil {
303 return err
304 }
305
306 return d.setDiff(key, nil, true)
307}
308
309// setDiff performs common diff setting behaviour.
310func (d *ResourceDiff) setDiff(key string, new interface{}, computed bool) error {
311 if err := d.clear(key); err != nil {
312 return err
313 }
314
315 if err := d.newWriter.WriteField(strings.Split(key, "."), new, computed); err != nil {
316 return fmt.Errorf("Cannot set new diff value for key %s: %s", key, err)
317 }
318
319 d.updatedKeys[key] = true
320
321 return nil
322}
323
324// ForceNew force-flags ForceNew in the schema for a specific key, and
325// re-calculates its diff, effectively causing this attribute to force a new
326// resource.
327//
328// Keep in mind that forcing a new resource will force a second run of the
329// resource's CustomizeDiff function (with a new ResourceDiff) once the current
330// one has completed. This second run is performed without state. This behavior
331// will be the same as if a new resource is being created and is performed to
332// ensure that the diff looks like the diff for a new resource as much as
333// possible. CustomizeDiff should expect such a scenario and act correctly.
334//
335// This function is a no-op/error if there is no diff.
336//
337// Note that the change to schema is permanent for the lifecycle of this
338// specific ResourceDiff instance.
339func (d *ResourceDiff) ForceNew(key string) error {
340 if !d.HasChange(key) {
341 return fmt.Errorf("ForceNew: No changes for %s", key)
342 }
343
344 keyParts := strings.Split(key, ".")
345 var schema *Schema
346 schemaL := addrToSchema(keyParts, d.schema)
347 if len(schemaL) > 0 {
348 schema = schemaL[len(schemaL)-1]
349 } else {
350 return fmt.Errorf("ForceNew: %s is not a valid key", key)
351 }
352
353 schema.ForceNew = true
354
355 // Flag this for a re-diff. Don't save any values to guarantee that existing
356 // diffs aren't messed with, as this gets messy when dealing with complex
357 // structures, zero values, etc.
358 d.forcedNewKeys[keyParts[0]] = true
359
360 return nil
361}
362
363// Get hands off to ResourceData.Get.
364func (d *ResourceDiff) Get(key string) interface{} {
365 r, _ := d.GetOk(key)
366 return r
367}
368
369// GetChange gets the change between the state and diff, checking first to see
370// if a overridden diff exists.
371//
372// This implementation differs from ResourceData's in the way that we first get
373// results from the exact levels for the new diff, then from state and diff as
374// per normal.
375func (d *ResourceDiff) GetChange(key string) (interface{}, interface{}) {
376 old, new, _ := d.getChange(key)
377 return old.Value, new.Value
378}
379
380// GetOk functions the same way as ResourceData.GetOk, but it also checks the
381// new diff levels to provide data consistent with the current state of the
382// customized diff.
383func (d *ResourceDiff) GetOk(key string) (interface{}, bool) {
384 r := d.get(strings.Split(key, "."), "newDiff")
385 exists := r.Exists && !r.Computed
386 if exists {
387 // If it exists, we also want to verify it is not the zero-value.
388 value := r.Value
389 zero := r.Schema.Type.Zero()
390
391 if eq, ok := value.(Equal); ok {
392 exists = !eq.Equal(zero)
393 } else {
394 exists = !reflect.DeepEqual(value, zero)
395 }
396 }
397
398 return r.Value, exists
399}
400
401// GetOkExists functions the same way as GetOkExists within ResourceData, but
402// it also checks the new diff levels to provide data consistent with the
403// current state of the customized diff.
404//
405// This is nearly the same function as GetOk, yet it does not check
406// for the zero value of the attribute's type. This allows for attributes
407// without a default, to fully check for a literal assignment, regardless
408// of the zero-value for that type.
409func (d *ResourceDiff) GetOkExists(key string) (interface{}, bool) {
410 r := d.get(strings.Split(key, "."), "newDiff")
411 exists := r.Exists && !r.Computed
412 return r.Value, exists
413}
414
415// NewValueKnown returns true if the new value for the given key is available
416// as its final value at diff time. If the return value is false, this means
417// either the value is based of interpolation that was unavailable at diff
418// time, or that the value was explicitly marked as computed by SetNewComputed.
419func (d *ResourceDiff) NewValueKnown(key string) bool {
420 r := d.get(strings.Split(key, "."), "newDiff")
421 return !r.Computed
422}
423
424// HasChange checks to see if there is a change between state and the diff, or
425// in the overridden diff.
426func (d *ResourceDiff) HasChange(key string) bool {
427 old, new := d.GetChange(key)
428
429 // If the type implements the Equal interface, then call that
430 // instead of just doing a reflect.DeepEqual. An example where this is
431 // needed is *Set
432 if eq, ok := old.(Equal); ok {
433 return !eq.Equal(new)
434 }
435
436 return !reflect.DeepEqual(old, new)
437}
438
439// Id returns the ID of this resource.
440//
441// Note that technically, ID does not change during diffs (it either has
442// already changed in the refresh, or will change on update), hence we do not
443// support updating the ID or fetching it from anything else other than state.
444func (d *ResourceDiff) Id() string {
445 var result string
446
447 if d.state != nil {
448 result = d.state.ID
449 }
450 return result
451}
452
453// getChange gets values from two different levels, designed for use in
454// diffChange, HasChange, and GetChange.
455//
456// This implementation differs from ResourceData's in the way that we first get
457// results from the exact levels for the new diff, then from state and diff as
458// per normal.
459func (d *ResourceDiff) getChange(key string) (getResult, getResult, bool) {
460 old := d.get(strings.Split(key, "."), "state")
461 var new getResult
462 for p := range d.updatedKeys {
463 if childAddrOf(key, p) {
464 new = d.getExact(strings.Split(key, "."), "newDiff")
465 return old, new, true
466 }
467 }
468 new = d.get(strings.Split(key, "."), "newDiff")
469 return old, new, false
470}
471
472// removed checks to see if the key is present in the existing, pre-customized
473// diff and if it was marked as NewRemoved.
474func (d *ResourceDiff) removed(k string) bool {
475 diff, ok := d.diff.Attributes[k]
476 if !ok {
477 return false
478 }
479 return diff.NewRemoved
480}
481
482// get performs the appropriate multi-level reader logic for ResourceDiff,
483// starting at source. Refer to newResourceDiff for the level order.
484func (d *ResourceDiff) get(addr []string, source string) getResult {
485 result, err := d.multiReader.ReadFieldMerge(addr, source)
486 if err != nil {
487 panic(err)
488 }
489
490 return d.finalizeResult(addr, result)
491}
492
493// getExact gets an attribute from the exact level referenced by source.
494func (d *ResourceDiff) getExact(addr []string, source string) getResult {
495 result, err := d.multiReader.ReadFieldExact(addr, source)
496 if err != nil {
497 panic(err)
498 }
499
500 return d.finalizeResult(addr, result)
501}
502
503// finalizeResult does some post-processing of the result produced by get and getExact.
504func (d *ResourceDiff) finalizeResult(addr []string, result FieldReadResult) getResult {
505 // If the result doesn't exist, then we set the value to the zero value
506 var schema *Schema
507 if schemaL := addrToSchema(addr, d.schema); len(schemaL) > 0 {
508 schema = schemaL[len(schemaL)-1]
509 }
510
511 if result.Value == nil && schema != nil {
512 result.Value = result.ValueOrZero(schema)
513 }
514
515 // Transform the FieldReadResult into a getResult. It might be worth
516 // merging these two structures one day.
517 return getResult{
518 Value: result.Value,
519 ValueProcessed: result.ValueProcessed,
520 Computed: result.Computed,
521 Exists: result.Exists,
522 Schema: schema,
523 }
524}
525
526// childAddrOf does a comparison of two addresses to see if one is the child of
527// the other.
528func childAddrOf(child, parent string) bool {
529 cs := strings.Split(child, ".")
530 ps := strings.Split(parent, ".")
531 if len(ps) > len(cs) {
532 return false
533 }
534 return reflect.DeepEqual(ps, cs[:len(ps)])
535}
536
537// checkKey checks the key to make sure it exists and is computed.
538func (d *ResourceDiff) checkKey(key, caller string, nested bool) error {
539 var schema *Schema
540 if nested {
541 keyParts := strings.Split(key, ".")
542 schemaL := addrToSchema(keyParts, d.schema)
543 if len(schemaL) > 0 {
544 schema = schemaL[len(schemaL)-1]
545 }
546 } else {
547 s, ok := d.schema[key]
548 if ok {
549 schema = s
550 }
551 }
552 if schema == nil {
553 return fmt.Errorf("%s: invalid key: %s", caller, key)
554 }
555 if !schema.Computed {
556 return fmt.Errorf("%s only operates on computed keys - %s is not one", caller, key)
557 }
558 return nil
559}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go
index acb5618..0ea5aad 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go
@@ -21,9 +21,13 @@ import (
21 "strings" 21 "strings"
22 22
23 "github.com/hashicorp/terraform/terraform" 23 "github.com/hashicorp/terraform/terraform"
24 "github.com/mitchellh/copystructure"
24 "github.com/mitchellh/mapstructure" 25 "github.com/mitchellh/mapstructure"
25) 26)
26 27
28// Name of ENV variable which (if not empty) prefers panic over error
29const PanicOnErr = "TF_SCHEMA_PANIC_ON_ERROR"
30
27// type used for schema package context keys 31// type used for schema package context keys
28type contextKey string 32type contextKey string
29 33
@@ -116,12 +120,16 @@ type Schema struct {
116 ForceNew bool 120 ForceNew bool
117 StateFunc SchemaStateFunc 121 StateFunc SchemaStateFunc
118 122
119 // The following fields are only set for a TypeList or TypeSet Type. 123 // The following fields are only set for a TypeList, TypeSet, or TypeMap.
120 // 124 //
121 // Elem must be either a *Schema or a *Resource only if the Type is 125 // Elem represents the element type. For a TypeMap, it must be a *Schema
122 // TypeList, and represents what the element type is. If it is *Schema, 126 // with a Type of TypeString, otherwise it may be either a *Schema or a
123 // the element type is just a simple value. If it is *Resource, the 127 // *Resource. If it is *Schema, the element type is just a simple value.
124 // element type is a complex structure, potentially with its own lifecycle. 128 // If it is *Resource, the element type is a complex structure,
129 // potentially with its own lifecycle.
130 Elem interface{}
131
132 // The following fields are only set for a TypeList or TypeSet.
125 // 133 //
126 // MaxItems defines a maximum amount of items that can exist within a 134 // MaxItems defines a maximum amount of items that can exist within a
127 // TypeSet or TypeList. Specific use cases would be if a TypeSet is being 135 // TypeSet or TypeList. Specific use cases would be if a TypeSet is being
@@ -138,7 +146,6 @@ type Schema struct {
138 // ["foo"] automatically. This is primarily for legacy reasons and the 146 // ["foo"] automatically. This is primarily for legacy reasons and the
139 // ambiguity is not recommended for new usage. Promotion is only allowed 147 // ambiguity is not recommended for new usage. Promotion is only allowed
140 // for primitive element types. 148 // for primitive element types.
141 Elem interface{}
142 MaxItems int 149 MaxItems int
143 MinItems int 150 MinItems int
144 PromoteSingle bool 151 PromoteSingle bool
@@ -192,7 +199,7 @@ type Schema struct {
192 Sensitive bool 199 Sensitive bool
193} 200}
194 201
195// SchemaDiffSuppresFunc is a function which can be used to determine 202// SchemaDiffSuppressFunc is a function which can be used to determine
196// whether a detected diff on a schema element is "valid" or not, and 203// whether a detected diff on a schema element is "valid" or not, and
197// suppress it from the plan if necessary. 204// suppress it from the plan if necessary.
198// 205//
@@ -289,8 +296,7 @@ func (s *Schema) ZeroValue() interface{} {
289 } 296 }
290} 297}
291 298
292func (s *Schema) finalizeDiff( 299func (s *Schema) finalizeDiff(d *terraform.ResourceAttrDiff, customized bool) *terraform.ResourceAttrDiff {
293 d *terraform.ResourceAttrDiff) *terraform.ResourceAttrDiff {
294 if d == nil { 300 if d == nil {
295 return d 301 return d
296 } 302 }
@@ -331,13 +337,20 @@ func (s *Schema) finalizeDiff(
331 } 337 }
332 338
333 if s.Computed { 339 if s.Computed {
334 if d.Old != "" && d.New == "" { 340 // FIXME: This is where the customized bool from getChange finally
335 // This is a computed value with an old value set already, 341 // comes into play. It allows the previously incorrect behavior
336 // just let it go. 342 // of an empty string being used as "unset" when the value is
337 return nil 343 // computed. This should be removed once we can properly
344 // represent an unset/nil value from the configuration.
345 if !customized {
346 if d.Old != "" && d.New == "" {
347 // This is a computed value with an old value set already,
348 // just let it go.
349 return nil
350 }
338 } 351 }
339 352
340 if d.New == "" { 353 if d.New == "" && !d.NewComputed {
341 // Computed attribute without a new value set 354 // Computed attribute without a new value set
342 d.NewComputed = true 355 d.NewComputed = true
343 } 356 }
@@ -354,6 +367,13 @@ func (s *Schema) finalizeDiff(
354// schemaMap is a wrapper that adds nice functions on top of schemas. 367// schemaMap is a wrapper that adds nice functions on top of schemas.
355type schemaMap map[string]*Schema 368type schemaMap map[string]*Schema
356 369
370func (m schemaMap) panicOnError() bool {
371 if os.Getenv(PanicOnErr) != "" {
372 return true
373 }
374 return false
375}
376
357// Data returns a ResourceData for the given schema, state, and diff. 377// Data returns a ResourceData for the given schema, state, and diff.
358// 378//
359// The diff is optional. 379// The diff is optional.
@@ -361,17 +381,30 @@ func (m schemaMap) Data(
361 s *terraform.InstanceState, 381 s *terraform.InstanceState,
362 d *terraform.InstanceDiff) (*ResourceData, error) { 382 d *terraform.InstanceDiff) (*ResourceData, error) {
363 return &ResourceData{ 383 return &ResourceData{
364 schema: m, 384 schema: m,
365 state: s, 385 state: s,
366 diff: d, 386 diff: d,
387 panicOnError: m.panicOnError(),
367 }, nil 388 }, nil
368} 389}
369 390
391// DeepCopy returns a copy of this schemaMap. The copy can be safely modified
392// without affecting the original.
393func (m *schemaMap) DeepCopy() schemaMap {
394 copy, err := copystructure.Config{Lock: true}.Copy(m)
395 if err != nil {
396 panic(err)
397 }
398 return *copy.(*schemaMap)
399}
400
370// Diff returns the diff for a resource given the schema map, 401// Diff returns the diff for a resource given the schema map,
371// state, and configuration. 402// state, and configuration.
372func (m schemaMap) Diff( 403func (m schemaMap) Diff(
373 s *terraform.InstanceState, 404 s *terraform.InstanceState,
374 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 405 c *terraform.ResourceConfig,
406 customizeDiff CustomizeDiffFunc,
407 meta interface{}) (*terraform.InstanceDiff, error) {
375 result := new(terraform.InstanceDiff) 408 result := new(terraform.InstanceDiff)
376 result.Attributes = make(map[string]*terraform.ResourceAttrDiff) 409 result.Attributes = make(map[string]*terraform.ResourceAttrDiff)
377 410
@@ -381,9 +414,10 @@ func (m schemaMap) Diff(
381 } 414 }
382 415
383 d := &ResourceData{ 416 d := &ResourceData{
384 schema: m, 417 schema: m,
385 state: s, 418 state: s,
386 config: c, 419 config: c,
420 panicOnError: m.panicOnError(),
387 } 421 }
388 422
389 for k, schema := range m { 423 for k, schema := range m {
@@ -393,6 +427,29 @@ func (m schemaMap) Diff(
393 } 427 }
394 } 428 }
395 429
430 // Remove any nil diffs just to keep things clean
431 for k, v := range result.Attributes {
432 if v == nil {
433 delete(result.Attributes, k)
434 }
435 }
436
437 // If this is a non-destroy diff, call any custom diff logic that has been
438 // defined.
439 if !result.DestroyTainted && customizeDiff != nil {
440 mc := m.DeepCopy()
441 rd := newResourceDiff(mc, c, s, result)
442 if err := customizeDiff(rd, meta); err != nil {
443 return nil, err
444 }
445 for _, k := range rd.UpdatedKeys() {
446 err := m.diff(k, mc[k], result, rd, false)
447 if err != nil {
448 return nil, err
449 }
450 }
451 }
452
396 // If the diff requires a new resource, then we recompute the diff 453 // If the diff requires a new resource, then we recompute the diff
397 // so we have the complete new resource diff, and preserve the 454 // so we have the complete new resource diff, and preserve the
398 // RequiresNew fields where necessary so the user knows exactly what 455 // RequiresNew fields where necessary so the user knows exactly what
@@ -418,6 +475,21 @@ func (m schemaMap) Diff(
418 } 475 }
419 } 476 }
420 477
478 // Re-run customization
479 if !result2.DestroyTainted && customizeDiff != nil {
480 mc := m.DeepCopy()
481 rd := newResourceDiff(mc, c, d.state, result2)
482 if err := customizeDiff(rd, meta); err != nil {
483 return nil, err
484 }
485 for _, k := range rd.UpdatedKeys() {
486 err := m.diff(k, mc[k], result2, rd, false)
487 if err != nil {
488 return nil, err
489 }
490 }
491 }
492
421 // Force all the fields to not force a new since we know what we 493 // Force all the fields to not force a new since we know what we
422 // want to force new. 494 // want to force new.
423 for k, attr := range result2.Attributes { 495 for k, attr := range result2.Attributes {
@@ -456,13 +528,6 @@ func (m schemaMap) Diff(
456 result = result2 528 result = result2
457 } 529 }
458 530
459 // Remove any nil diffs just to keep things clean
460 for k, v := range result.Attributes {
461 if v == nil {
462 delete(result.Attributes, k)
463 }
464 }
465
466 // Go through and detect all of the ComputedWhens now that we've 531 // Go through and detect all of the ComputedWhens now that we've
467 // finished the diff. 532 // finished the diff.
468 // TODO 533 // TODO
@@ -681,11 +746,23 @@ func isValidFieldName(name string) bool {
681 return re.MatchString(name) 746 return re.MatchString(name)
682} 747}
683 748
749// resourceDiffer is an interface that is used by the private diff functions.
750// This helps facilitate diff logic for both ResourceData and ResoureDiff with
751// minimal divergence in code.
752type resourceDiffer interface {
753 diffChange(string) (interface{}, interface{}, bool, bool, bool)
754 Get(string) interface{}
755 GetChange(string) (interface{}, interface{})
756 GetOk(string) (interface{}, bool)
757 HasChange(string) bool
758 Id() string
759}
760
684func (m schemaMap) diff( 761func (m schemaMap) diff(
685 k string, 762 k string,
686 schema *Schema, 763 schema *Schema,
687 diff *terraform.InstanceDiff, 764 diff *terraform.InstanceDiff,
688 d *ResourceData, 765 d resourceDiffer,
689 all bool) error { 766 all bool) error {
690 767
691 unsupressedDiff := new(terraform.InstanceDiff) 768 unsupressedDiff := new(terraform.InstanceDiff)
@@ -706,12 +783,14 @@ func (m schemaMap) diff(
706 } 783 }
707 784
708 for attrK, attrV := range unsupressedDiff.Attributes { 785 for attrK, attrV := range unsupressedDiff.Attributes {
709 if schema.DiffSuppressFunc != nil && 786 switch rd := d.(type) {
710 attrV != nil && 787 case *ResourceData:
711 schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, d) { 788 if schema.DiffSuppressFunc != nil &&
712 continue 789 attrV != nil &&
790 schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, rd) {
791 continue
792 }
713 } 793 }
714
715 diff.Attributes[attrK] = attrV 794 diff.Attributes[attrK] = attrV
716 } 795 }
717 796
@@ -722,9 +801,9 @@ func (m schemaMap) diffList(
722 k string, 801 k string,
723 schema *Schema, 802 schema *Schema,
724 diff *terraform.InstanceDiff, 803 diff *terraform.InstanceDiff,
725 d *ResourceData, 804 d resourceDiffer,
726 all bool) error { 805 all bool) error {
727 o, n, _, computedList := d.diffChange(k) 806 o, n, _, computedList, customized := d.diffChange(k)
728 if computedList { 807 if computedList {
729 n = nil 808 n = nil
730 } 809 }
@@ -791,10 +870,13 @@ func (m schemaMap) diffList(
791 oldStr = "" 870 oldStr = ""
792 } 871 }
793 872
794 diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{ 873 diff.Attributes[k+".#"] = countSchema.finalizeDiff(
795 Old: oldStr, 874 &terraform.ResourceAttrDiff{
796 New: newStr, 875 Old: oldStr,
797 }) 876 New: newStr,
877 },
878 customized,
879 )
798 } 880 }
799 881
800 // Figure out the maximum 882 // Figure out the maximum
@@ -841,13 +923,13 @@ func (m schemaMap) diffMap(
841 k string, 923 k string,
842 schema *Schema, 924 schema *Schema,
843 diff *terraform.InstanceDiff, 925 diff *terraform.InstanceDiff,
844 d *ResourceData, 926 d resourceDiffer,
845 all bool) error { 927 all bool) error {
846 prefix := k + "." 928 prefix := k + "."
847 929
848 // First get all the values from the state 930 // First get all the values from the state
849 var stateMap, configMap map[string]string 931 var stateMap, configMap map[string]string
850 o, n, _, nComputed := d.diffChange(k) 932 o, n, _, nComputed, customized := d.diffChange(k)
851 if err := mapstructure.WeakDecode(o, &stateMap); err != nil { 933 if err := mapstructure.WeakDecode(o, &stateMap); err != nil {
852 return fmt.Errorf("%s: %s", k, err) 934 return fmt.Errorf("%s: %s", k, err)
853 } 935 }
@@ -899,6 +981,7 @@ func (m schemaMap) diffMap(
899 Old: oldStr, 981 Old: oldStr,
900 New: newStr, 982 New: newStr,
901 }, 983 },
984 customized,
902 ) 985 )
903 } 986 }
904 987
@@ -916,16 +999,22 @@ func (m schemaMap) diffMap(
916 continue 999 continue
917 } 1000 }
918 1001
919 diff.Attributes[prefix+k] = schema.finalizeDiff(&terraform.ResourceAttrDiff{ 1002 diff.Attributes[prefix+k] = schema.finalizeDiff(
920 Old: old, 1003 &terraform.ResourceAttrDiff{
921 New: v, 1004 Old: old,
922 }) 1005 New: v,
1006 },
1007 customized,
1008 )
923 } 1009 }
924 for k, v := range stateMap { 1010 for k, v := range stateMap {
925 diff.Attributes[prefix+k] = schema.finalizeDiff(&terraform.ResourceAttrDiff{ 1011 diff.Attributes[prefix+k] = schema.finalizeDiff(
926 Old: v, 1012 &terraform.ResourceAttrDiff{
927 NewRemoved: true, 1013 Old: v,
928 }) 1014 NewRemoved: true,
1015 },
1016 customized,
1017 )
929 } 1018 }
930 1019
931 return nil 1020 return nil
@@ -935,10 +1024,10 @@ func (m schemaMap) diffSet(
935 k string, 1024 k string,
936 schema *Schema, 1025 schema *Schema,
937 diff *terraform.InstanceDiff, 1026 diff *terraform.InstanceDiff,
938 d *ResourceData, 1027 d resourceDiffer,
939 all bool) error { 1028 all bool) error {
940 1029
941 o, n, _, computedSet := d.diffChange(k) 1030 o, n, _, computedSet, customized := d.diffChange(k)
942 if computedSet { 1031 if computedSet {
943 n = nil 1032 n = nil
944 } 1033 }
@@ -997,20 +1086,26 @@ func (m schemaMap) diffSet(
997 countStr = "" 1086 countStr = ""
998 } 1087 }
999 1088
1000 diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{ 1089 diff.Attributes[k+".#"] = countSchema.finalizeDiff(
1001 Old: countStr, 1090 &terraform.ResourceAttrDiff{
1002 NewComputed: true, 1091 Old: countStr,
1003 }) 1092 NewComputed: true,
1093 },
1094 customized,
1095 )
1004 return nil 1096 return nil
1005 } 1097 }
1006 1098
1007 // If the counts are not the same, then record that diff 1099 // If the counts are not the same, then record that diff
1008 changed := oldLen != newLen 1100 changed := oldLen != newLen
1009 if changed || all { 1101 if changed || all {
1010 diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{ 1102 diff.Attributes[k+".#"] = countSchema.finalizeDiff(
1011 Old: oldStr, 1103 &terraform.ResourceAttrDiff{
1012 New: newStr, 1104 Old: oldStr,
1013 }) 1105 New: newStr,
1106 },
1107 customized,
1108 )
1014 } 1109 }
1015 1110
1016 // Build the list of codes that will make up our set. This is the 1111 // Build the list of codes that will make up our set. This is the
@@ -1056,11 +1151,11 @@ func (m schemaMap) diffString(
1056 k string, 1151 k string,
1057 schema *Schema, 1152 schema *Schema,
1058 diff *terraform.InstanceDiff, 1153 diff *terraform.InstanceDiff,
1059 d *ResourceData, 1154 d resourceDiffer,
1060 all bool) error { 1155 all bool) error {
1061 var originalN interface{} 1156 var originalN interface{}
1062 var os, ns string 1157 var os, ns string
1063 o, n, _, computed := d.diffChange(k) 1158 o, n, _, computed, customized := d.diffChange(k)
1064 if schema.StateFunc != nil && n != nil { 1159 if schema.StateFunc != nil && n != nil {
1065 originalN = n 1160 originalN = n
1066 n = schema.StateFunc(n) 1161 n = schema.StateFunc(n)
@@ -1090,20 +1185,23 @@ func (m schemaMap) diffString(
1090 } 1185 }
1091 1186
1092 removed := false 1187 removed := false
1093 if o != nil && n == nil { 1188 if o != nil && n == nil && !computed {
1094 removed = true 1189 removed = true
1095 } 1190 }
1096 if removed && schema.Computed { 1191 if removed && schema.Computed {
1097 return nil 1192 return nil
1098 } 1193 }
1099 1194
1100 diff.Attributes[k] = schema.finalizeDiff(&terraform.ResourceAttrDiff{ 1195 diff.Attributes[k] = schema.finalizeDiff(
1101 Old: os, 1196 &terraform.ResourceAttrDiff{
1102 New: ns, 1197 Old: os,
1103 NewExtra: originalN, 1198 New: ns,
1104 NewRemoved: removed, 1199 NewExtra: originalN,
1105 NewComputed: computed, 1200 NewRemoved: removed,
1106 }) 1201 NewComputed: computed,
1202 },
1203 customized,
1204 )
1107 1205
1108 return nil 1206 return nil
1109} 1207}
@@ -1172,9 +1270,9 @@ func (m schemaMap) validateConflictingAttributes(
1172 } 1270 }
1173 1271
1174 for _, conflicting_key := range schema.ConflictsWith { 1272 for _, conflicting_key := range schema.ConflictsWith {
1175 if value, ok := c.Get(conflicting_key); ok { 1273 if _, ok := c.Get(conflicting_key); ok {
1176 return fmt.Errorf( 1274 return fmt.Errorf(
1177 "%q: conflicts with %s (%#v)", k, conflicting_key, value) 1275 "%q: conflicts with %s", k, conflicting_key)
1178 } 1276 }
1179 } 1277 }
1180 1278
@@ -1363,13 +1461,10 @@ func getValueType(k string, schema *Schema) (ValueType, error) {
1363 return vt, nil 1461 return vt, nil
1364 } 1462 }
1365 1463
1464 // If a Schema is provided to a Map, we use the Type of that schema
1465 // as the type for each element in the Map.
1366 if s, ok := schema.Elem.(*Schema); ok { 1466 if s, ok := schema.Elem.(*Schema); ok {
1367 if s.Elem == nil { 1467 return s.Type, nil
1368 return TypeString, nil
1369 }
1370 if vt, ok := s.Elem.(ValueType); ok {
1371 return vt, nil
1372 }
1373 } 1468 }
1374 1469
1375 if _, ok := schema.Elem.(*Resource); ok { 1470 if _, ok := schema.Elem.(*Resource); ok {
@@ -1430,7 +1525,6 @@ func (m schemaMap) validatePrimitive(
1430 raw interface{}, 1525 raw interface{},
1431 schema *Schema, 1526 schema *Schema,
1432 c *terraform.ResourceConfig) ([]string, []error) { 1527 c *terraform.ResourceConfig) ([]string, []error) {
1433
1434 // Catch if the user gave a complex type where a primitive was 1528 // Catch if the user gave a complex type where a primitive was
1435 // expected, so we can return a friendly error message that 1529 // expected, so we can return a friendly error message that
1436 // doesn't contain Go type system terminology. 1530 // doesn't contain Go type system terminology.
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/set.go b/vendor/github.com/hashicorp/terraform/helper/schema/set.go
index de05f40..cba2890 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/set.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/set.go
@@ -17,6 +17,12 @@ func HashString(v interface{}) int {
17 return hashcode.String(v.(string)) 17 return hashcode.String(v.(string))
18} 18}
19 19
20// HashInt hashes integers. If you want a Set of integers, this is the
21// SchemaSetFunc you want.
22func HashInt(v interface{}) int {
23 return hashcode.String(strconv.Itoa(v.(int)))
24}
25
20// HashResource hashes complex structures that are described using 26// HashResource hashes complex structures that are described using
21// a *Resource. This is the default set implementation used when a set's 27// a *Resource. This is the default set implementation used when a set's
22// element type is a full resource. 28// element type is a full resource.
@@ -153,6 +159,31 @@ func (s *Set) Equal(raw interface{}) bool {
153 return reflect.DeepEqual(s.m, other.m) 159 return reflect.DeepEqual(s.m, other.m)
154} 160}
155 161
162// HashEqual simply checks to the keys the top-level map to the keys in the
163// other set's top-level map to see if they are equal. This obviously assumes
164// you have a properly working hash function - use HashResource if in doubt.
165func (s *Set) HashEqual(raw interface{}) bool {
166 other, ok := raw.(*Set)
167 if !ok {
168 return false
169 }
170
171 ks1 := make([]string, 0)
172 ks2 := make([]string, 0)
173
174 for k := range s.m {
175 ks1 = append(ks1, k)
176 }
177 for k := range other.m {
178 ks2 = append(ks2, k)
179 }
180
181 sort.Strings(ks1)
182 sort.Strings(ks2)
183
184 return reflect.DeepEqual(ks1, ks2)
185}
186
156func (s *Set) GoString() string { 187func (s *Set) GoString() string {
157 return fmt.Sprintf("*Set(%#v)", s.m) 188 return fmt.Sprintf("*Set(%#v)", s.m)
158} 189}
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/testing.go b/vendor/github.com/hashicorp/terraform/helper/schema/testing.go
index 9765bdb..da754ac 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/testing.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/testing.go
@@ -10,13 +10,15 @@ import (
10// TestResourceDataRaw creates a ResourceData from a raw configuration map. 10// TestResourceDataRaw creates a ResourceData from a raw configuration map.
11func TestResourceDataRaw( 11func TestResourceDataRaw(
12 t *testing.T, schema map[string]*Schema, raw map[string]interface{}) *ResourceData { 12 t *testing.T, schema map[string]*Schema, raw map[string]interface{}) *ResourceData {
13 t.Helper()
14
13 c, err := config.NewRawConfig(raw) 15 c, err := config.NewRawConfig(raw)
14 if err != nil { 16 if err != nil {
15 t.Fatalf("err: %s", err) 17 t.Fatalf("err: %s", err)
16 } 18 }
17 19
18 sm := schemaMap(schema) 20 sm := schemaMap(schema)
19 diff, err := sm.Diff(nil, terraform.NewResourceConfig(c)) 21 diff, err := sm.Diff(nil, terraform.NewResourceConfig(c), nil, nil)
20 if err != nil { 22 if err != nil {
21 t.Fatalf("err: %s", err) 23 t.Fatalf("err: %s", err)
22 } 24 }
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go b/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go
index 1610cec..3bc3ac4 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go
@@ -2,7 +2,7 @@
2 2
3package schema 3package schema
4 4
5import "fmt" 5import "strconv"
6 6
7const _ValueType_name = "TypeInvalidTypeBoolTypeIntTypeFloatTypeStringTypeListTypeMapTypeSettypeObject" 7const _ValueType_name = "TypeInvalidTypeBoolTypeIntTypeFloatTypeStringTypeListTypeMapTypeSettypeObject"
8 8
@@ -10,7 +10,7 @@ var _ValueType_index = [...]uint8{0, 11, 19, 26, 35, 45, 53, 60, 67, 77}
10 10
11func (i ValueType) String() string { 11func (i ValueType) String() string {
12 if i < 0 || i >= ValueType(len(_ValueType_index)-1) { 12 if i < 0 || i >= ValueType(len(_ValueType_index)-1) {
13 return fmt.Sprintf("ValueType(%d)", i) 13 return "ValueType(" + strconv.FormatInt(int64(i), 10) + ")"
14 } 14 }
15 return _ValueType_name[_ValueType_index[i]:_ValueType_index[i+1]] 15 return _ValueType_name[_ValueType_index[i]:_ValueType_index[i+1]]
16} 16}
diff --git a/vendor/github.com/hashicorp/terraform/helper/shadow/closer.go b/vendor/github.com/hashicorp/terraform/helper/shadow/closer.go
deleted file mode 100644
index edc1e2a..0000000
--- a/vendor/github.com/hashicorp/terraform/helper/shadow/closer.go
+++ /dev/null
@@ -1,83 +0,0 @@
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
42var closerType = reflect.TypeOf((*io.Closer)(nil)).Elem()
43
44func (w *closeWalker) StructField(f reflect.StructField, v reflect.Value) error {
45 // Not sure why this would be but lets avoid some panics
46 if !v.IsValid() {
47 return nil
48 }
49
50 // Empty for exported, so don't check unexported fields
51 if f.PkgPath != "" {
52 return nil
53 }
54
55 // Verify the io.Closer is in this package
56 typ := v.Type()
57 if typ.PkgPath() != "github.com/hashicorp/terraform/helper/shadow" {
58 return nil
59 }
60
61 var closer io.Closer
62 if v.Type().Implements(closerType) {
63 closer = v.Interface().(io.Closer)
64 } else if v.CanAddr() {
65 // The Close method may require a pointer receiver, but we only have a value.
66 v := v.Addr()
67 if v.Type().Implements(closerType) {
68 closer = v.Interface().(io.Closer)
69 }
70 }
71
72 if closer == nil {
73 return reflectwalk.SkipEntry
74 }
75
76 // Close it
77 if err := closer.Close(); err != nil {
78 w.Err = multierror.Append(w.Err, err)
79 }
80
81 // Don't go into the struct field
82 return reflectwalk.SkipEntry
83}
diff --git a/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go b/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go
deleted file mode 100644
index 4223e92..0000000
--- a/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go
+++ /dev/null
@@ -1,128 +0,0 @@
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
deleted file mode 100644
index 432b036..0000000
--- a/vendor/github.com/hashicorp/terraform/helper/shadow/keyed_value.go
+++ /dev/null
@@ -1,151 +0,0 @@
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
deleted file mode 100644
index 0a43d4d..0000000
--- a/vendor/github.com/hashicorp/terraform/helper/shadow/ordered_value.go
+++ /dev/null
@@ -1,66 +0,0 @@
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
deleted file mode 100644
index 178b7e7..0000000
--- a/vendor/github.com/hashicorp/terraform/helper/shadow/value.go
+++ /dev/null
@@ -1,87 +0,0 @@
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
29func (v *Value) Lock() {
30 v.lock.Lock()
31}
32
33func (v *Value) Unlock() {
34 v.lock.Unlock()
35}
36
37// Close closes the value. This can never fail. For a definition of
38// "close" see the struct docs.
39func (w *Value) Close() error {
40 w.lock.Lock()
41 set := w.valueSet
42 w.lock.Unlock()
43
44 // If we haven't set the value, set it
45 if !set {
46 w.SetValue(ErrClosed)
47 }
48
49 // Done
50 return nil
51}
52
53// Value returns the value that was set.
54func (w *Value) Value() interface{} {
55 w.lock.Lock()
56 defer w.lock.Unlock()
57
58 // If we already have a value just return
59 for !w.valueSet {
60 // No value, setup the condition variable if we have to
61 if w.cond == nil {
62 w.cond = sync.NewCond(&w.lock)
63 }
64
65 // Wait on it
66 w.cond.Wait()
67 }
68
69 // Return the value
70 return w.value
71}
72
73// SetValue sets the value.
74func (w *Value) SetValue(v interface{}) {
75 w.lock.Lock()
76 defer w.lock.Unlock()
77
78 // Set the value
79 w.valueSet = true
80 w.value = v
81
82 // If we have a condition, clear it
83 if w.cond != nil {
84 w.cond.Broadcast()
85 w.cond = nil
86 }
87}
diff --git a/vendor/github.com/hashicorp/terraform/httpclient/client.go b/vendor/github.com/hashicorp/terraform/httpclient/client.go
new file mode 100644
index 0000000..bb06beb
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/httpclient/client.go
@@ -0,0 +1,18 @@
1package httpclient
2
3import (
4 "net/http"
5
6 cleanhttp "github.com/hashicorp/go-cleanhttp"
7)
8
9// New returns the DefaultPooledClient from the cleanhttp
10// package that will also send a Terraform User-Agent string.
11func New() *http.Client {
12 cli := cleanhttp.DefaultPooledClient()
13 cli.Transport = &userAgentRoundTripper{
14 userAgent: UserAgentString(),
15 inner: cli.Transport,
16 }
17 return cli
18}
diff --git a/vendor/github.com/hashicorp/terraform/httpclient/useragent.go b/vendor/github.com/hashicorp/terraform/httpclient/useragent.go
new file mode 100644
index 0000000..5e28017
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/httpclient/useragent.go
@@ -0,0 +1,40 @@
1package httpclient
2
3import (
4 "fmt"
5 "log"
6 "net/http"
7 "os"
8 "strings"
9
10 "github.com/hashicorp/terraform/version"
11)
12
13const userAgentFormat = "Terraform/%s"
14const uaEnvVar = "TF_APPEND_USER_AGENT"
15
16func UserAgentString() string {
17 ua := fmt.Sprintf(userAgentFormat, version.Version)
18
19 if add := os.Getenv(uaEnvVar); add != "" {
20 add = strings.TrimSpace(add)
21 if len(add) > 0 {
22 ua += " " + add
23 log.Printf("[DEBUG] Using modified User-Agent: %s", ua)
24 }
25 }
26
27 return ua
28}
29
30type userAgentRoundTripper struct {
31 inner http.RoundTripper
32 userAgent string
33}
34
35func (rt *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
36 if _, ok := req.Header["User-Agent"]; !ok {
37 req.Header.Set("User-Agent", rt.userAgent)
38 }
39 return rt.inner.RoundTrip(req)
40}
diff --git a/vendor/github.com/hashicorp/terraform/plugin/client.go b/vendor/github.com/hashicorp/terraform/plugin/client.go
index 3a5cb7a..7e2f4fe 100644
--- a/vendor/github.com/hashicorp/terraform/plugin/client.go
+++ b/vendor/github.com/hashicorp/terraform/plugin/client.go
@@ -1,8 +1,10 @@
1package plugin 1package plugin
2 2
3import ( 3import (
4 "os"
4 "os/exec" 5 "os/exec"
5 6
7 hclog "github.com/hashicorp/go-hclog"
6 plugin "github.com/hashicorp/go-plugin" 8 plugin "github.com/hashicorp/go-plugin"
7 "github.com/hashicorp/terraform/plugin/discovery" 9 "github.com/hashicorp/terraform/plugin/discovery"
8) 10)
@@ -10,11 +12,18 @@ import (
10// ClientConfig returns a configuration object that can be used to instantiate 12// ClientConfig returns a configuration object that can be used to instantiate
11// a client for the plugin described by the given metadata. 13// a client for the plugin described by the given metadata.
12func ClientConfig(m discovery.PluginMeta) *plugin.ClientConfig { 14func ClientConfig(m discovery.PluginMeta) *plugin.ClientConfig {
15 logger := hclog.New(&hclog.LoggerOptions{
16 Name: "plugin",
17 Level: hclog.Trace,
18 Output: os.Stderr,
19 })
20
13 return &plugin.ClientConfig{ 21 return &plugin.ClientConfig{
14 Cmd: exec.Command(m.Path), 22 Cmd: exec.Command(m.Path),
15 HandshakeConfig: Handshake, 23 HandshakeConfig: Handshake,
16 Managed: true, 24 Managed: true,
17 Plugins: PluginMap, 25 Plugins: PluginMap,
26 Logger: logger,
18 } 27 }
19} 28}
20 29
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go
index f5bc4c1..f053312 100644
--- a/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go
@@ -3,6 +3,7 @@ package discovery
3import ( 3import (
4 "io/ioutil" 4 "io/ioutil"
5 "log" 5 "log"
6 "os"
6 "path/filepath" 7 "path/filepath"
7 "strings" 8 "strings"
8) 9)
@@ -59,7 +60,6 @@ func findPluginPaths(kind string, dirs []string) []string {
59 fullName := item.Name() 60 fullName := item.Name()
60 61
61 if !strings.HasPrefix(fullName, prefix) { 62 if !strings.HasPrefix(fullName, prefix) {
62 log.Printf("[DEBUG] skipping %q, not a %s", fullName, kind)
63 continue 63 continue
64 } 64 }
65 65
@@ -71,6 +71,12 @@ func findPluginPaths(kind string, dirs []string) []string {
71 continue 71 continue
72 } 72 }
73 73
74 // Check that the file we found is usable
75 if !pathIsFile(absPath) {
76 log.Printf("[ERROR] ignoring non-file %s", absPath)
77 continue
78 }
79
74 log.Printf("[DEBUG] found %s %q", kind, fullName) 80 log.Printf("[DEBUG] found %s %q", kind, fullName)
75 ret = append(ret, filepath.Clean(absPath)) 81 ret = append(ret, filepath.Clean(absPath))
76 continue 82 continue
@@ -83,7 +89,13 @@ func findPluginPaths(kind string, dirs []string) []string {
83 continue 89 continue
84 } 90 }
85 91
86 log.Printf("[WARNING] found legacy %s %q", kind, fullName) 92 // Check that the file we found is usable
93 if !pathIsFile(absPath) {
94 log.Printf("[ERROR] ignoring non-file %s", absPath)
95 continue
96 }
97
98 log.Printf("[WARN] found legacy %s %q", kind, fullName)
87 99
88 ret = append(ret, filepath.Clean(absPath)) 100 ret = append(ret, filepath.Clean(absPath))
89 } 101 }
@@ -92,6 +104,17 @@ func findPluginPaths(kind string, dirs []string) []string {
92 return ret 104 return ret
93} 105}
94 106
107// Returns true if and only if the given path refers to a file or a symlink
108// to a file.
109func pathIsFile(path string) bool {
110 info, err := os.Stat(path)
111 if err != nil {
112 return false
113 }
114
115 return !info.IsDir()
116}
117
95// ResolvePluginPaths takes a list of paths to plugin executables (as returned 118// ResolvePluginPaths takes a list of paths to plugin executables (as returned
96// by e.g. FindPluginPaths) and produces a PluginMetaSet describing the 119// by e.g. FindPluginPaths) and produces a PluginMetaSet describing the
97// referenced plugins. 120// referenced plugins.
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go
index 241b5cb..815640f 100644
--- a/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go
@@ -3,19 +3,22 @@ package discovery
3import ( 3import (
4 "errors" 4 "errors"
5 "fmt" 5 "fmt"
6 "io"
6 "io/ioutil" 7 "io/ioutil"
7 "log" 8 "log"
8 "net/http" 9 "net/http"
9 "os" 10 "os"
11 "path/filepath"
10 "runtime" 12 "runtime"
11 "strconv" 13 "strconv"
12 "strings" 14 "strings"
13 15
14 "golang.org/x/net/html" 16 "golang.org/x/net/html"
15 17
16 cleanhttp "github.com/hashicorp/go-cleanhttp"
17 getter "github.com/hashicorp/go-getter" 18 getter "github.com/hashicorp/go-getter"
18 multierror "github.com/hashicorp/go-multierror" 19 multierror "github.com/hashicorp/go-multierror"
20 "github.com/hashicorp/terraform/httpclient"
21 "github.com/mitchellh/cli"
19) 22)
20 23
21// Releases are located by parsing the html listing from releases.hashicorp.com. 24// Releases are located by parsing the html listing from releases.hashicorp.com.
@@ -30,7 +33,19 @@ const protocolVersionHeader = "x-terraform-protocol-version"
30 33
31var releaseHost = "https://releases.hashicorp.com" 34var releaseHost = "https://releases.hashicorp.com"
32 35
33var httpClient = cleanhttp.DefaultClient() 36var httpClient *http.Client
37
38func init() {
39 httpClient = httpclient.New()
40
41 httpGetter := &getter.HttpGetter{
42 Client: httpClient,
43 Netrc: true,
44 }
45
46 getter.Getters["http"] = httpGetter
47 getter.Getters["https"] = httpGetter
48}
34 49
35// An Installer maintains a local cache of plugins by downloading plugins 50// An Installer maintains a local cache of plugins by downloading plugins
36// from an online repository. 51// from an online repository.
@@ -47,6 +62,10 @@ type Installer interface {
47type ProviderInstaller struct { 62type ProviderInstaller struct {
48 Dir string 63 Dir string
49 64
65 // Cache is used to access and update a local cache of plugins if non-nil.
66 // Can be nil to disable caching.
67 Cache PluginCache
68
50 PluginProtocolVersion uint 69 PluginProtocolVersion uint
51 70
52 // OS and Arch specify the OS and architecture that should be used when 71 // OS and Arch specify the OS and architecture that should be used when
@@ -58,6 +77,8 @@ type ProviderInstaller struct {
58 77
59 // Skip checksum and signature verification 78 // Skip checksum and signature verification
60 SkipVerify bool 79 SkipVerify bool
80
81 Ui cli.Ui // Ui for output
61} 82}
62 83
63// Get is part of an implementation of type Installer, and attempts to download 84// Get is part of an implementation of type Installer, and attempts to download
@@ -98,6 +119,12 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
98 // sort them newest to oldest 119 // sort them newest to oldest
99 Versions(versions).Sort() 120 Versions(versions).Sort()
100 121
122 // Ensure that our installation directory exists
123 err = os.MkdirAll(i.Dir, os.ModePerm)
124 if err != nil {
125 return PluginMeta{}, fmt.Errorf("failed to create plugin dir %s: %s", i.Dir, err)
126 }
127
101 // take the first matching plugin we find 128 // take the first matching plugin we find
102 for _, v := range versions { 129 for _, v := range versions {
103 url := i.providerURL(provider, v.String()) 130 url := i.providerURL(provider, v.String())
@@ -116,8 +143,9 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
116 143
117 log.Printf("[DEBUG] fetching provider info for %s version %s", provider, v) 144 log.Printf("[DEBUG] fetching provider info for %s version %s", provider, v)
118 if checkPlugin(url, i.PluginProtocolVersion) { 145 if checkPlugin(url, i.PluginProtocolVersion) {
119 log.Printf("[DEBUG] getting provider %q version %q at %s", provider, v, url) 146 i.Ui.Info(fmt.Sprintf("- Downloading plugin for provider %q (%s)...", provider, v.String()))
120 err := getter.Get(i.Dir, url) 147 log.Printf("[DEBUG] getting provider %q version %q", provider, v)
148 err := i.install(provider, v, url)
121 if err != nil { 149 if err != nil {
122 return PluginMeta{}, err 150 return PluginMeta{}, err
123 } 151 }
@@ -164,6 +192,98 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
164 return PluginMeta{}, ErrorNoVersionCompatible 192 return PluginMeta{}, ErrorNoVersionCompatible
165} 193}
166 194
195func (i *ProviderInstaller) install(provider string, version Version, url string) error {
196 if i.Cache != nil {
197 log.Printf("[DEBUG] looking for provider %s %s in plugin cache", provider, version)
198 cached := i.Cache.CachedPluginPath("provider", provider, version)
199 if cached == "" {
200 log.Printf("[DEBUG] %s %s not yet in cache, so downloading %s", provider, version, url)
201 err := getter.Get(i.Cache.InstallDir(), url)
202 if err != nil {
203 return err
204 }
205 // should now be in cache
206 cached = i.Cache.CachedPluginPath("provider", provider, version)
207 if cached == "" {
208 // should never happen if the getter is behaving properly
209 // and the plugins are packaged properly.
210 return fmt.Errorf("failed to find downloaded plugin in cache %s", i.Cache.InstallDir())
211 }
212 }
213
214 // Link or copy the cached binary into our install dir so the
215 // normal resolution machinery can find it.
216 filename := filepath.Base(cached)
217 targetPath := filepath.Join(i.Dir, filename)
218
219 log.Printf("[DEBUG] installing %s %s to %s from local cache %s", provider, version, targetPath, cached)
220
221 // Delete if we can. If there's nothing there already then no harm done.
222 // This is important because we can't create a link if there's
223 // already a file of the same name present.
224 // (any other error here we'll catch below when we try to write here)
225 os.Remove(targetPath)
226
227 // We don't attempt linking on Windows because links are not
228 // comprehensively supported by all tools/apps in Windows and
229 // so we choose to be conservative to avoid creating any
230 // weird issues for Windows users.
231 linkErr := errors.New("link not supported for Windows") // placeholder error, never actually returned
232 if runtime.GOOS != "windows" {
233 // Try hard linking first. Hard links are preferable because this
234 // creates a self-contained directory that doesn't depend on the
235 // cache after install.
236 linkErr = os.Link(cached, targetPath)
237
238 // If that failed, try a symlink. This _does_ depend on the cache
239 // after install, so the user must manage the cache more carefully
240 // in this case, but avoids creating redundant copies of the
241 // plugins on disk.
242 if linkErr != nil {
243 linkErr = os.Symlink(cached, targetPath)
244 }
245 }
246
247 // If we still have an error then we'll try a copy as a fallback.
248 // In this case either the OS is Windows or the target filesystem
249 // can't support symlinks.
250 if linkErr != nil {
251 srcFile, err := os.Open(cached)
252 if err != nil {
253 return fmt.Errorf("failed to open cached plugin %s: %s", cached, err)
254 }
255 defer srcFile.Close()
256
257 destFile, err := os.OpenFile(targetPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, os.ModePerm)
258 if err != nil {
259 return fmt.Errorf("failed to create %s: %s", targetPath, err)
260 }
261
262 _, err = io.Copy(destFile, srcFile)
263 if err != nil {
264 destFile.Close()
265 return fmt.Errorf("failed to copy cached plugin from %s to %s: %s", cached, targetPath, err)
266 }
267
268 err = destFile.Close()
269 if err != nil {
270 return fmt.Errorf("error creating %s: %s", targetPath, err)
271 }
272 }
273
274 // One way or another, by the time we get here we should have either
275 // a link or a copy of the cached plugin within i.Dir, as expected.
276 } else {
277 log.Printf("[DEBUG] plugin cache is disabled, so downloading %s %s from %s", provider, version, url)
278 err := getter.Get(i.Dir, url)
279 if err != nil {
280 return err
281 }
282 }
283
284 return nil
285}
286
167func (i *ProviderInstaller) PurgeUnused(used map[string]PluginMeta) (PluginMetaSet, error) { 287func (i *ProviderInstaller) PurgeUnused(used map[string]PluginMeta) (PluginMetaSet, error) {
168 purge := make(PluginMetaSet) 288 purge := make(PluginMetaSet)
169 289
@@ -261,7 +381,7 @@ func checkPlugin(url string, pluginProtocolVersion uint) bool {
261 if proto == "" { 381 if proto == "" {
262 // The header isn't present, but we don't make this error fatal since 382 // The header isn't present, but we don't make this error fatal since
263 // the latest version will probably work. 383 // the latest version will probably work.
264 log.Printf("[WARNING] missing %s from: %s", protocolVersionHeader, url) 384 log.Printf("[WARN] missing %s from: %s", protocolVersionHeader, url)
265 return true 385 return true
266 } 386 }
267 387
@@ -422,3 +542,7 @@ func getFile(url string) ([]byte, error) {
422 } 542 }
423 return data, nil 543 return data, nil
424} 544}
545
546func GetReleaseHost() string {
547 return releaseHost
548}
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/get_cache.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/get_cache.go
new file mode 100644
index 0000000..1a10042
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/get_cache.go
@@ -0,0 +1,48 @@
1package discovery
2
3// PluginCache is an interface implemented by objects that are able to maintain
4// a cache of plugins.
5type PluginCache interface {
6 // CachedPluginPath returns a path where the requested plugin is already
7 // cached, or an empty string if the requested plugin is not yet cached.
8 CachedPluginPath(kind string, name string, version Version) string
9
10 // InstallDir returns the directory that new plugins should be installed into
11 // in order to populate the cache. This directory should be used as the
12 // first argument to getter.Get when downloading plugins with go-getter.
13 //
14 // After installing into this directory, use CachedPluginPath to obtain the
15 // path where the plugin was installed.
16 InstallDir() string
17}
18
19// NewLocalPluginCache returns a PluginCache that caches plugins in a
20// given local directory.
21func NewLocalPluginCache(dir string) PluginCache {
22 return &pluginCache{
23 Dir: dir,
24 }
25}
26
27type pluginCache struct {
28 Dir string
29}
30
31func (c *pluginCache) CachedPluginPath(kind string, name string, version Version) string {
32 allPlugins := FindPlugins(kind, []string{c.Dir})
33 plugins := allPlugins.WithName(name).WithVersion(version)
34
35 if plugins.Count() == 0 {
36 // nothing cached
37 return ""
38 }
39
40 // There should generally be only one plugin here; if there's more than
41 // one match for some reason then we'll just choose one arbitrarily.
42 plugin := plugins.Newest()
43 return plugin.Path
44}
45
46func (c *pluginCache) InstallDir() string {
47 return c.Dir
48}
diff --git a/vendor/github.com/hashicorp/terraform/plugin/resource_provider.go b/vendor/github.com/hashicorp/terraform/plugin/resource_provider.go
index 473f786..d6a433c 100644
--- a/vendor/github.com/hashicorp/terraform/plugin/resource_provider.go
+++ b/vendor/github.com/hashicorp/terraform/plugin/resource_provider.go
@@ -41,6 +41,24 @@ func (p *ResourceProvider) Stop() error {
41 return err 41 return err
42} 42}
43 43
44func (p *ResourceProvider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.ProviderSchema, error) {
45 var result ResourceProviderGetSchemaResponse
46 args := &ResourceProviderGetSchemaArgs{
47 Req: req,
48 }
49
50 err := p.Client.Call("Plugin.GetSchema", args, &result)
51 if err != nil {
52 return nil, err
53 }
54
55 if result.Error != nil {
56 err = result.Error
57 }
58
59 return result.Schema, err
60}
61
44func (p *ResourceProvider) Input( 62func (p *ResourceProvider) Input(
45 input terraform.UIInput, 63 input terraform.UIInput,
46 c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { 64 c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
@@ -312,6 +330,15 @@ type ResourceProviderStopResponse struct {
312 Error *plugin.BasicError 330 Error *plugin.BasicError
313} 331}
314 332
333type ResourceProviderGetSchemaArgs struct {
334 Req *terraform.ProviderSchemaRequest
335}
336
337type ResourceProviderGetSchemaResponse struct {
338 Schema *terraform.ProviderSchema
339 Error *plugin.BasicError
340}
341
315type ResourceProviderConfigureResponse struct { 342type ResourceProviderConfigureResponse struct {
316 Error *plugin.BasicError 343 Error *plugin.BasicError
317} 344}
@@ -418,6 +445,18 @@ func (s *ResourceProviderServer) Stop(
418 return nil 445 return nil
419} 446}
420 447
448func (s *ResourceProviderServer) GetSchema(
449 args *ResourceProviderGetSchemaArgs,
450 result *ResourceProviderGetSchemaResponse,
451) error {
452 schema, err := s.Provider.GetSchema(args.Req)
453 result.Schema = schema
454 if err != nil {
455 result.Error = plugin.NewBasicError(err)
456 }
457 return nil
458}
459
421func (s *ResourceProviderServer) Input( 460func (s *ResourceProviderServer) Input(
422 args *ResourceProviderInputArgs, 461 args *ResourceProviderInputArgs,
423 reply *ResourceProviderInputResponse) error { 462 reply *ResourceProviderInputResponse) error {
diff --git a/vendor/github.com/hashicorp/terraform/registry/client.go b/vendor/github.com/hashicorp/terraform/registry/client.go
new file mode 100644
index 0000000..a18e6b8
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/registry/client.go
@@ -0,0 +1,227 @@
1package registry
2
3import (
4 "encoding/json"
5 "fmt"
6 "io/ioutil"
7 "log"
8 "net/http"
9 "net/url"
10 "path"
11 "strings"
12 "time"
13
14 "github.com/hashicorp/terraform/httpclient"
15 "github.com/hashicorp/terraform/registry/regsrc"
16 "github.com/hashicorp/terraform/registry/response"
17 "github.com/hashicorp/terraform/svchost"
18 "github.com/hashicorp/terraform/svchost/disco"
19 "github.com/hashicorp/terraform/version"
20)
21
22const (
23 xTerraformGet = "X-Terraform-Get"
24 xTerraformVersion = "X-Terraform-Version"
25 requestTimeout = 10 * time.Second
26 serviceID = "modules.v1"
27)
28
29var tfVersion = version.String()
30
31// Client provides methods to query Terraform Registries.
32type Client struct {
33 // this is the client to be used for all requests.
34 client *http.Client
35
36 // services is a required *disco.Disco, which may have services and
37 // credentials pre-loaded.
38 services *disco.Disco
39}
40
41// NewClient returns a new initialized registry client.
42func NewClient(services *disco.Disco, client *http.Client) *Client {
43 if services == nil {
44 services = disco.New()
45 }
46
47 if client == nil {
48 client = httpclient.New()
49 client.Timeout = requestTimeout
50 }
51
52 services.Transport = client.Transport
53
54 return &Client{
55 client: client,
56 services: services,
57 }
58}
59
60// Discover queries the host, and returns the url for the registry.
61func (c *Client) Discover(host svchost.Hostname) (*url.URL, error) {
62 service, err := c.services.DiscoverServiceURL(host, serviceID)
63 if err != nil {
64 return nil, err
65 }
66 if !strings.HasSuffix(service.Path, "/") {
67 service.Path += "/"
68 }
69 return service, nil
70}
71
72// Versions queries the registry for a module, and returns the available versions.
73func (c *Client) Versions(module *regsrc.Module) (*response.ModuleVersions, error) {
74 host, err := module.SvcHost()
75 if err != nil {
76 return nil, err
77 }
78
79 service, err := c.Discover(host)
80 if err != nil {
81 return nil, err
82 }
83
84 p, err := url.Parse(path.Join(module.Module(), "versions"))
85 if err != nil {
86 return nil, err
87 }
88
89 service = service.ResolveReference(p)
90
91 log.Printf("[DEBUG] fetching module versions from %q", service)
92
93 req, err := http.NewRequest("GET", service.String(), nil)
94 if err != nil {
95 return nil, err
96 }
97
98 c.addRequestCreds(host, req)
99 req.Header.Set(xTerraformVersion, tfVersion)
100
101 resp, err := c.client.Do(req)
102 if err != nil {
103 return nil, err
104 }
105 defer resp.Body.Close()
106
107 switch resp.StatusCode {
108 case http.StatusOK:
109 // OK
110 case http.StatusNotFound:
111 return nil, &errModuleNotFound{addr: module}
112 default:
113 return nil, fmt.Errorf("error looking up module versions: %s", resp.Status)
114 }
115
116 var versions response.ModuleVersions
117
118 dec := json.NewDecoder(resp.Body)
119 if err := dec.Decode(&versions); err != nil {
120 return nil, err
121 }
122
123 for _, mod := range versions.Modules {
124 for _, v := range mod.Versions {
125 log.Printf("[DEBUG] found available version %q for %s", v.Version, mod.Source)
126 }
127 }
128
129 return &versions, nil
130}
131
132func (c *Client) addRequestCreds(host svchost.Hostname, req *http.Request) {
133 creds, err := c.services.CredentialsForHost(host)
134 if err != nil {
135 log.Printf("[WARN] Failed to get credentials for %s: %s (ignoring)", host, err)
136 return
137 }
138
139 if creds != nil {
140 creds.PrepareRequest(req)
141 }
142}
143
144// Location find the download location for a specific version module.
145// This returns a string, because the final location may contain special go-getter syntax.
146func (c *Client) Location(module *regsrc.Module, version string) (string, error) {
147 host, err := module.SvcHost()
148 if err != nil {
149 return "", err
150 }
151
152 service, err := c.Discover(host)
153 if err != nil {
154 return "", err
155 }
156
157 var p *url.URL
158 if version == "" {
159 p, err = url.Parse(path.Join(module.Module(), "download"))
160 } else {
161 p, err = url.Parse(path.Join(module.Module(), version, "download"))
162 }
163 if err != nil {
164 return "", err
165 }
166 download := service.ResolveReference(p)
167
168 log.Printf("[DEBUG] looking up module location from %q", download)
169
170 req, err := http.NewRequest("GET", download.String(), nil)
171 if err != nil {
172 return "", err
173 }
174
175 c.addRequestCreds(host, req)
176 req.Header.Set(xTerraformVersion, tfVersion)
177
178 resp, err := c.client.Do(req)
179 if err != nil {
180 return "", err
181 }
182 defer resp.Body.Close()
183
184 // there should be no body, but save it for logging
185 body, err := ioutil.ReadAll(resp.Body)
186 if err != nil {
187 return "", fmt.Errorf("error reading response body from registry: %s", err)
188 }
189
190 switch resp.StatusCode {
191 case http.StatusOK, http.StatusNoContent:
192 // OK
193 case http.StatusNotFound:
194 return "", fmt.Errorf("module %q version %q not found", module, version)
195 default:
196 // anything else is an error:
197 return "", fmt.Errorf("error getting download location for %q: %s resp:%s", module, resp.Status, body)
198 }
199
200 // the download location is in the X-Terraform-Get header
201 location := resp.Header.Get(xTerraformGet)
202 if location == "" {
203 return "", fmt.Errorf("failed to get download URL for %q: %s resp:%s", module, resp.Status, body)
204 }
205
206 // If location looks like it's trying to be a relative URL, treat it as
207 // one.
208 //
209 // We don't do this for just _any_ location, since the X-Terraform-Get
210 // header is a go-getter location rather than a URL, and so not all
211 // possible values will parse reasonably as URLs.)
212 //
213 // When used in conjunction with go-getter we normally require this header
214 // to be an absolute URL, but we are more liberal here because third-party
215 // registry implementations may not "know" their own absolute URLs if
216 // e.g. they are running behind a reverse proxy frontend, or such.
217 if strings.HasPrefix(location, "/") || strings.HasPrefix(location, "./") || strings.HasPrefix(location, "../") {
218 locationURL, err := url.Parse(location)
219 if err != nil {
220 return "", fmt.Errorf("invalid relative URL for %q: %s", module, err)
221 }
222 locationURL = download.ResolveReference(locationURL)
223 location = locationURL.String()
224 }
225
226 return location, nil
227}
diff --git a/vendor/github.com/hashicorp/terraform/registry/errors.go b/vendor/github.com/hashicorp/terraform/registry/errors.go
new file mode 100644
index 0000000..b8dcd31
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/registry/errors.go
@@ -0,0 +1,23 @@
1package registry
2
3import (
4 "fmt"
5
6 "github.com/hashicorp/terraform/registry/regsrc"
7)
8
9type errModuleNotFound struct {
10 addr *regsrc.Module
11}
12
13func (e *errModuleNotFound) Error() string {
14 return fmt.Sprintf("module %s not found", e.addr)
15}
16
17// IsModuleNotFound returns true only if the given error is a "module not found"
18// error. This allows callers to recognize this particular error condition
19// as distinct from operational errors such as poor network connectivity.
20func IsModuleNotFound(err error) bool {
21 _, ok := err.(*errModuleNotFound)
22 return ok
23}
diff --git a/vendor/github.com/hashicorp/terraform/registry/regsrc/friendly_host.go b/vendor/github.com/hashicorp/terraform/registry/regsrc/friendly_host.go
new file mode 100644
index 0000000..14b4dce
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/registry/regsrc/friendly_host.go
@@ -0,0 +1,140 @@
1package regsrc
2
3import (
4 "regexp"
5 "strings"
6
7 "github.com/hashicorp/terraform/svchost"
8)
9
10var (
11 // InvalidHostString is a placeholder returned when a raw host can't be
12 // converted by IDNA spec. It will never be returned for any host for which
13 // Valid() is true.
14 InvalidHostString = "<invalid host>"
15
16 // urlLabelEndSubRe is a sub-expression that matches any character that's
17 // allowed at the start or end of a URL label according to RFC1123.
18 urlLabelEndSubRe = "[0-9A-Za-z]"
19
20 // urlLabelEndSubRe is a sub-expression that matches any character that's
21 // allowed at in a non-start or end of a URL label according to RFC1123.
22 urlLabelMidSubRe = "[0-9A-Za-z-]"
23
24 // urlLabelUnicodeSubRe is a sub-expression that matches any non-ascii char
25 // in an IDN (Unicode) display URL. It's not strict - there are only ~15k
26 // valid Unicode points in IDN RFC (some with conditions). We are just going
27 // with being liberal with matching and then erroring if we fail to convert
28 // to punycode later (which validates chars fully). This at least ensures
29 // ascii chars dissalowed by the RC1123 parts above don't become legal
30 // again.
31 urlLabelUnicodeSubRe = "[^[:ascii:]]"
32
33 // hostLabelSubRe is the sub-expression that matches a valid hostname label.
34 // It does not anchor the start or end so it can be composed into more
35 // complex RegExps below. Note that for sanity we don't handle disallowing
36 // raw punycode in this regexp (esp. since re2 doesn't support negative
37 // lookbehind, but we can capture it's presence here to check later).
38 hostLabelSubRe = "" +
39 // Match valid initial char, or unicode char
40 "(?:" + urlLabelEndSubRe + "|" + urlLabelUnicodeSubRe + ")" +
41 // Optionally, match 0 to 61 valid URL or Unicode chars,
42 // followed by one valid end char or unicode char
43 "(?:" +
44 "(?:" + urlLabelMidSubRe + "|" + urlLabelUnicodeSubRe + "){0,61}" +
45 "(?:" + urlLabelEndSubRe + "|" + urlLabelUnicodeSubRe + ")" +
46 ")?"
47
48 // hostSubRe is the sub-expression that matches a valid host prefix.
49 // Allows custom port.
50 hostSubRe = hostLabelSubRe + "(?:\\." + hostLabelSubRe + ")+(?::\\d+)?"
51
52 // hostRe is a regexp that matches a valid host prefix. Additional
53 // validation of unicode strings is needed for matches.
54 hostRe = regexp.MustCompile("^" + hostSubRe + "$")
55)
56
57// FriendlyHost describes a registry instance identified in source strings by a
58// simple bare hostname like registry.terraform.io.
59type FriendlyHost struct {
60 Raw string
61}
62
63func NewFriendlyHost(host string) *FriendlyHost {
64 return &FriendlyHost{Raw: host}
65}
66
67// ParseFriendlyHost attempts to parse a valid "friendly host" prefix from the
68// given string. If no valid prefix is found, host will be nil and rest will
69// contain the full source string. The host prefix must terminate at the end of
70// the input or at the first / character. If one or more characters exist after
71// the first /, they will be returned as rest (without the / delimiter).
72// Hostnames containing punycode WILL be parsed successfully since they may have
73// come from an internal normalized source string, however should be considered
74// invalid if the string came from a user directly. This must be checked
75// explicitly for user-input strings by calling Valid() on the
76// returned host.
77func ParseFriendlyHost(source string) (host *FriendlyHost, rest string) {
78 parts := strings.SplitN(source, "/", 2)
79
80 if hostRe.MatchString(parts[0]) {
81 host = &FriendlyHost{Raw: parts[0]}
82 if len(parts) == 2 {
83 rest = parts[1]
84 }
85 return
86 }
87
88 // No match, return whole string as rest along with nil host
89 rest = source
90 return
91}
92
93// Valid returns whether the host prefix is considered valid in any case.
94// Example of invalid prefixes might include ones that don't conform to the host
95// name specifications. Not that IDN prefixes containing punycode are not valid
96// input which we expect to always be in user-input or normalised display form.
97func (h *FriendlyHost) Valid() bool {
98 return svchost.IsValid(h.Raw)
99}
100
101// Display returns the host formatted for display to the user in CLI or web
102// output.
103func (h *FriendlyHost) Display() string {
104 return svchost.ForDisplay(h.Raw)
105}
106
107// Normalized returns the host formatted for internal reference or comparison.
108func (h *FriendlyHost) Normalized() string {
109 host, err := svchost.ForComparison(h.Raw)
110 if err != nil {
111 return InvalidHostString
112 }
113 return string(host)
114}
115
116// String returns the host formatted as the user originally typed it assuming it
117// was parsed from user input.
118func (h *FriendlyHost) String() string {
119 return h.Raw
120}
121
122// Equal compares the FriendlyHost against another instance taking normalization
123// into account. Invalid hosts cannot be compared and will always return false.
124func (h *FriendlyHost) Equal(other *FriendlyHost) bool {
125 if other == nil {
126 return false
127 }
128
129 otherHost, err := svchost.ForComparison(other.Raw)
130 if err != nil {
131 return false
132 }
133
134 host, err := svchost.ForComparison(h.Raw)
135 if err != nil {
136 return false
137 }
138
139 return otherHost == host
140}
diff --git a/vendor/github.com/hashicorp/terraform/registry/regsrc/module.go b/vendor/github.com/hashicorp/terraform/registry/regsrc/module.go
new file mode 100644
index 0000000..325706e
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/registry/regsrc/module.go
@@ -0,0 +1,205 @@
1package regsrc
2
3import (
4 "errors"
5 "fmt"
6 "regexp"
7 "strings"
8
9 "github.com/hashicorp/terraform/svchost"
10)
11
12var (
13 ErrInvalidModuleSource = errors.New("not a valid registry module source")
14
15 // nameSubRe is the sub-expression that matches a valid module namespace or
16 // name. It's strictly a super-set of what GitHub allows for user/org and
17 // repo names respectively, but more restrictive than our original repo-name
18 // regex which allowed periods but could cause ambiguity with hostname
19 // prefixes. It does not anchor the start or end so it can be composed into
20 // more complex RegExps below. Alphanumeric with - and _ allowed in non
21 // leading or trailing positions. Max length 64 chars. (GitHub username is
22 // 38 max.)
23 nameSubRe = "[0-9A-Za-z](?:[0-9A-Za-z-_]{0,62}[0-9A-Za-z])?"
24
25 // providerSubRe is the sub-expression that matches a valid provider. It
26 // does not anchor the start or end so it can be composed into more complex
27 // RegExps below. Only lowercase chars and digits are supported in practice.
28 // Max length 64 chars.
29 providerSubRe = "[0-9a-z]{1,64}"
30
31 // moduleSourceRe is a regular expression that matches the basic
32 // namespace/name/provider[//...] format for registry sources. It assumes
33 // any FriendlyHost prefix has already been removed if present.
34 moduleSourceRe = regexp.MustCompile(
35 fmt.Sprintf("^(%s)\\/(%s)\\/(%s)(?:\\/\\/(.*))?$",
36 nameSubRe, nameSubRe, providerSubRe))
37
38 // NameRe is a regular expression defining the format allowed for namespace
39 // or name fields in module registry implementations.
40 NameRe = regexp.MustCompile("^" + nameSubRe + "$")
41
42 // ProviderRe is a regular expression defining the format allowed for
43 // provider fields in module registry implementations.
44 ProviderRe = regexp.MustCompile("^" + providerSubRe + "$")
45
46 // these hostnames are not allowed as registry sources, because they are
47 // already special case module sources in terraform.
48 disallowed = map[string]bool{
49 "github.com": true,
50 "bitbucket.org": true,
51 }
52)
53
54// Module describes a Terraform Registry Module source.
55type Module struct {
56 // RawHost is the friendly host prefix if one was present. It might be nil
57 // if the original source had no host prefix which implies
58 // PublicRegistryHost but is distinct from having an actual pointer to
59 // PublicRegistryHost since it encodes the fact the original string didn't
60 // include a host prefix at all which is significant for recovering actual
61 // input not just normalized form. Most callers should access it with Host()
62 // which will return public registry host instance if it's nil.
63 RawHost *FriendlyHost
64 RawNamespace string
65 RawName string
66 RawProvider string
67 RawSubmodule string
68}
69
70// NewModule construct a new module source from separate parts. Pass empty
71// string if host or submodule are not needed.
72func NewModule(host, namespace, name, provider, submodule string) (*Module, error) {
73 m := &Module{
74 RawNamespace: namespace,
75 RawName: name,
76 RawProvider: provider,
77 RawSubmodule: submodule,
78 }
79 if host != "" {
80 h := NewFriendlyHost(host)
81 if h != nil {
82 fmt.Println("HOST:", h)
83 if !h.Valid() || disallowed[h.Display()] {
84 return nil, ErrInvalidModuleSource
85 }
86 }
87 m.RawHost = h
88 }
89 return m, nil
90}
91
92// ParseModuleSource attempts to parse source as a Terraform registry module
93// source. If the string is not found to be in a valid format,
94// ErrInvalidModuleSource is returned. Note that this can only be used on
95// "input" strings, e.g. either ones supplied by the user or potentially
96// normalised but in Display form (unicode). It will fail to parse a source with
97// a punycoded domain since this is not permitted input from a user. If you have
98// an already normalized string internally, you can compare it without parsing
99// by comparing with the normalized version of the subject with the normal
100// string equality operator.
101func ParseModuleSource(source string) (*Module, error) {
102 // See if there is a friendly host prefix.
103 host, rest := ParseFriendlyHost(source)
104 if host != nil {
105 if !host.Valid() || disallowed[host.Display()] {
106 return nil, ErrInvalidModuleSource
107 }
108 }
109
110 matches := moduleSourceRe.FindStringSubmatch(rest)
111 if len(matches) < 4 {
112 return nil, ErrInvalidModuleSource
113 }
114
115 m := &Module{
116 RawHost: host,
117 RawNamespace: matches[1],
118 RawName: matches[2],
119 RawProvider: matches[3],
120 }
121
122 if len(matches) == 5 {
123 m.RawSubmodule = matches[4]
124 }
125
126 return m, nil
127}
128
129// Display returns the source formatted for display to the user in CLI or web
130// output.
131func (m *Module) Display() string {
132 return m.formatWithPrefix(m.normalizedHostPrefix(m.Host().Display()), false)
133}
134
135// Normalized returns the source formatted for internal reference or comparison.
136func (m *Module) Normalized() string {
137 return m.formatWithPrefix(m.normalizedHostPrefix(m.Host().Normalized()), false)
138}
139
140// String returns the source formatted as the user originally typed it assuming
141// it was parsed from user input.
142func (m *Module) String() string {
143 // Don't normalize public registry hostname - leave it exactly like the user
144 // input it.
145 hostPrefix := ""
146 if m.RawHost != nil {
147 hostPrefix = m.RawHost.String() + "/"
148 }
149 return m.formatWithPrefix(hostPrefix, true)
150}
151
152// Equal compares the module source against another instance taking
153// normalization into account.
154func (m *Module) Equal(other *Module) bool {
155 return m.Normalized() == other.Normalized()
156}
157
158// Host returns the FriendlyHost object describing which registry this module is
159// in. If the original source string had not host component this will return the
160// PublicRegistryHost.
161func (m *Module) Host() *FriendlyHost {
162 if m.RawHost == nil {
163 return PublicRegistryHost
164 }
165 return m.RawHost
166}
167
168func (m *Module) normalizedHostPrefix(host string) string {
169 if m.Host().Equal(PublicRegistryHost) {
170 return ""
171 }
172 return host + "/"
173}
174
175func (m *Module) formatWithPrefix(hostPrefix string, preserveCase bool) string {
176 suffix := ""
177 if m.RawSubmodule != "" {
178 suffix = "//" + m.RawSubmodule
179 }
180 str := fmt.Sprintf("%s%s/%s/%s%s", hostPrefix, m.RawNamespace, m.RawName,
181 m.RawProvider, suffix)
182
183 // lower case by default
184 if !preserveCase {
185 return strings.ToLower(str)
186 }
187 return str
188}
189
190// Module returns just the registry ID of the module, without a hostname or
191// suffix.
192func (m *Module) Module() string {
193 return fmt.Sprintf("%s/%s/%s", m.RawNamespace, m.RawName, m.RawProvider)
194}
195
196// SvcHost returns the svchost.Hostname for this module. Since FriendlyHost may
197// contain an invalid hostname, this also returns an error indicating if it
198// could be converted to a svchost.Hostname. If no host is specified, the
199// default PublicRegistryHost is returned.
200func (m *Module) SvcHost() (svchost.Hostname, error) {
201 if m.RawHost == nil {
202 return svchost.ForComparison(PublicRegistryHost.Raw)
203 }
204 return svchost.ForComparison(m.RawHost.Raw)
205}
diff --git a/vendor/github.com/hashicorp/terraform/registry/regsrc/regsrc.go b/vendor/github.com/hashicorp/terraform/registry/regsrc/regsrc.go
new file mode 100644
index 0000000..c430bf1
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/registry/regsrc/regsrc.go
@@ -0,0 +1,8 @@
1// Package regsrc provides helpers for working with source strings that identify
2// resources within a Terraform registry.
3package regsrc
4
5var (
6 // PublicRegistryHost is a FriendlyHost that represents the public registry.
7 PublicRegistryHost = NewFriendlyHost("registry.terraform.io")
8)
diff --git a/vendor/github.com/hashicorp/terraform/registry/response/module.go b/vendor/github.com/hashicorp/terraform/registry/response/module.go
new file mode 100644
index 0000000..3bd2b3d
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/registry/response/module.go
@@ -0,0 +1,93 @@
1package response
2
3import (
4 "time"
5)
6
7// Module is the response structure with the data for a single module version.
8type Module struct {
9 ID string `json:"id"`
10
11 //---------------------------------------------------------------
12 // Metadata about the overall module.
13
14 Owner string `json:"owner"`
15 Namespace string `json:"namespace"`
16 Name string `json:"name"`
17 Version string `json:"version"`
18 Provider string `json:"provider"`
19 Description string `json:"description"`
20 Source string `json:"source"`
21 PublishedAt time.Time `json:"published_at"`
22 Downloads int `json:"downloads"`
23 Verified bool `json:"verified"`
24}
25
26// ModuleDetail represents a module in full detail.
27type ModuleDetail struct {
28 Module
29
30 //---------------------------------------------------------------
31 // Metadata about the overall module. This is only available when
32 // requesting the specific module (not in list responses).
33
34 // Root is the root module.
35 Root *ModuleSubmodule `json:"root"`
36
37 // Submodules are the other submodules that are available within
38 // this module.
39 Submodules []*ModuleSubmodule `json:"submodules"`
40
41 //---------------------------------------------------------------
42 // The fields below are only set when requesting this specific
43 // module. They are available to easily know all available versions
44 // and providers without multiple API calls.
45
46 Providers []string `json:"providers"` // All available providers
47 Versions []string `json:"versions"` // All versions
48}
49
50// ModuleSubmodule is the metadata about a specific submodule within
51// a module. This includes the root module as a special case.
52type ModuleSubmodule struct {
53 Path string `json:"path"`
54 Readme string `json:"readme"`
55 Empty bool `json:"empty"`
56
57 Inputs []*ModuleInput `json:"inputs"`
58 Outputs []*ModuleOutput `json:"outputs"`
59 Dependencies []*ModuleDep `json:"dependencies"`
60 Resources []*ModuleResource `json:"resources"`
61}
62
63// ModuleInput is an input for a module.
64type ModuleInput struct {
65 Name string `json:"name"`
66 Description string `json:"description"`
67 Default string `json:"default"`
68}
69
70// ModuleOutput is an output for a module.
71type ModuleOutput struct {
72 Name string `json:"name"`
73 Description string `json:"description"`
74}
75
76// ModuleDep is an output for a module.
77type ModuleDep struct {
78 Name string `json:"name"`
79 Source string `json:"source"`
80 Version string `json:"version"`
81}
82
83// ModuleProviderDep is the output for a provider dependency
84type ModuleProviderDep struct {
85 Name string `json:"name"`
86 Version string `json:"version"`
87}
88
89// ModuleResource is an output for a module.
90type ModuleResource struct {
91 Name string `json:"name"`
92 Type string `json:"type"`
93}
diff --git a/vendor/github.com/hashicorp/terraform/registry/response/module_list.go b/vendor/github.com/hashicorp/terraform/registry/response/module_list.go
new file mode 100644
index 0000000..9783748
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/registry/response/module_list.go
@@ -0,0 +1,7 @@
1package response
2
3// ModuleList is the response structure for a pageable list of modules.
4type ModuleList struct {
5 Meta PaginationMeta `json:"meta"`
6 Modules []*Module `json:"modules"`
7}
diff --git a/vendor/github.com/hashicorp/terraform/registry/response/module_provider.go b/vendor/github.com/hashicorp/terraform/registry/response/module_provider.go
new file mode 100644
index 0000000..e48499d
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/registry/response/module_provider.go
@@ -0,0 +1,14 @@
1package response
2
3// ModuleProvider represents a single provider for modules.
4type ModuleProvider struct {
5 Name string `json:"name"`
6 Downloads int `json:"downloads"`
7 ModuleCount int `json:"module_count"`
8}
9
10// ModuleProviderList is the response structure for a pageable list of ModuleProviders.
11type ModuleProviderList struct {
12 Meta PaginationMeta `json:"meta"`
13 Providers []*ModuleProvider `json:"providers"`
14}
diff --git a/vendor/github.com/hashicorp/terraform/registry/response/module_versions.go b/vendor/github.com/hashicorp/terraform/registry/response/module_versions.go
new file mode 100644
index 0000000..f69e975
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/registry/response/module_versions.go
@@ -0,0 +1,32 @@
1package response
2
3// ModuleVersions is the response format that contains all metadata about module
4// versions needed for terraform CLI to resolve version constraints. See RFC
5// TF-042 for details on this format.
6type ModuleVersions struct {
7 Modules []*ModuleProviderVersions `json:"modules"`
8}
9
10// ModuleProviderVersions is the response format for a single module instance,
11// containing metadata about all versions and their dependencies.
12type ModuleProviderVersions struct {
13 Source string `json:"source"`
14 Versions []*ModuleVersion `json:"versions"`
15}
16
17// ModuleVersion is the output metadata for a given version needed by CLI to
18// resolve candidate versions to satisfy requirements.
19type ModuleVersion struct {
20 Version string `json:"version"`
21 Root VersionSubmodule `json:"root"`
22 Submodules []*VersionSubmodule `json:"submodules"`
23}
24
25// VersionSubmodule is the output metadata for a submodule within a given
26// version needed by CLI to resolve candidate versions to satisfy requirements.
27// When representing the Root in JSON the path is omitted.
28type VersionSubmodule struct {
29 Path string `json:"path,omitempty"`
30 Providers []*ModuleProviderDep `json:"providers"`
31 Dependencies []*ModuleDep `json:"dependencies"`
32}
diff --git a/vendor/github.com/hashicorp/terraform/registry/response/pagination.go b/vendor/github.com/hashicorp/terraform/registry/response/pagination.go
new file mode 100644
index 0000000..75a9254
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/registry/response/pagination.go
@@ -0,0 +1,65 @@
1package response
2
3import (
4 "net/url"
5 "strconv"
6)
7
8// PaginationMeta is a structure included in responses for pagination.
9type PaginationMeta struct {
10 Limit int `json:"limit"`
11 CurrentOffset int `json:"current_offset"`
12 NextOffset *int `json:"next_offset,omitempty"`
13 PrevOffset *int `json:"prev_offset,omitempty"`
14 NextURL string `json:"next_url,omitempty"`
15 PrevURL string `json:"prev_url,omitempty"`
16}
17
18// NewPaginationMeta populates pagination meta data from result parameters
19func NewPaginationMeta(offset, limit int, hasMore bool, currentURL string) PaginationMeta {
20 pm := PaginationMeta{
21 Limit: limit,
22 CurrentOffset: offset,
23 }
24
25 // Calculate next/prev offsets, leave nil if not valid pages
26 nextOffset := offset + limit
27 if hasMore {
28 pm.NextOffset = &nextOffset
29 }
30
31 prevOffset := offset - limit
32 if prevOffset < 0 {
33 prevOffset = 0
34 }
35 if prevOffset < offset {
36 pm.PrevOffset = &prevOffset
37 }
38
39 // If URL format provided, populate URLs. Intentionally swallow URL errors for now, API should
40 // catch missing URLs if we call with bad URL arg (and we care about them being present).
41 if currentURL != "" && pm.NextOffset != nil {
42 pm.NextURL, _ = setQueryParam(currentURL, "offset", *pm.NextOffset, 0)
43 }
44 if currentURL != "" && pm.PrevOffset != nil {
45 pm.PrevURL, _ = setQueryParam(currentURL, "offset", *pm.PrevOffset, 0)
46 }
47
48 return pm
49}
50
51func setQueryParam(baseURL, key string, val, defaultVal int) (string, error) {
52 u, err := url.Parse(baseURL)
53 if err != nil {
54 return "", err
55 }
56 q := u.Query()
57 if val == defaultVal {
58 // elide param if it's the default value
59 q.Del(key)
60 } else {
61 q.Set(key, strconv.Itoa(val))
62 }
63 u.RawQuery = q.Encode()
64 return u.String(), nil
65}
diff --git a/vendor/github.com/hashicorp/terraform/registry/response/redirect.go b/vendor/github.com/hashicorp/terraform/registry/response/redirect.go
new file mode 100644
index 0000000..d5eb49b
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/registry/response/redirect.go
@@ -0,0 +1,6 @@
1package response
2
3// Redirect causes the frontend to perform a window redirect.
4type Redirect struct {
5 URL string `json:"url"`
6}
diff --git a/vendor/github.com/hashicorp/terraform/svchost/auth/cache.go b/vendor/github.com/hashicorp/terraform/svchost/auth/cache.go
new file mode 100644
index 0000000..4f0d168
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/svchost/auth/cache.go
@@ -0,0 +1,45 @@
1package auth
2
3import (
4 "github.com/hashicorp/terraform/svchost"
5)
6
7// CachingCredentialsSource creates a new credentials source that wraps another
8// and caches its results in memory, on a per-hostname basis.
9//
10// No means is provided for expiration of cached credentials, so a caching
11// credentials source should have a limited lifetime (one Terraform operation,
12// for example) to ensure that time-limited credentials don't expire before
13// their cache entries do.
14func CachingCredentialsSource(source CredentialsSource) CredentialsSource {
15 return &cachingCredentialsSource{
16 source: source,
17 cache: map[svchost.Hostname]HostCredentials{},
18 }
19}
20
21type cachingCredentialsSource struct {
22 source CredentialsSource
23 cache map[svchost.Hostname]HostCredentials
24}
25
26// ForHost passes the given hostname on to the wrapped credentials source and
27// caches the result to return for future requests with the same hostname.
28//
29// Both credentials and non-credentials (nil) responses are cached.
30//
31// No cache entry is created if the wrapped source returns an error, to allow
32// the caller to retry the failing operation.
33func (s *cachingCredentialsSource) ForHost(host svchost.Hostname) (HostCredentials, error) {
34 if cache, cached := s.cache[host]; cached {
35 return cache, nil
36 }
37
38 result, err := s.source.ForHost(host)
39 if err != nil {
40 return result, err
41 }
42
43 s.cache[host] = result
44 return result, nil
45}
diff --git a/vendor/github.com/hashicorp/terraform/svchost/auth/credentials.go b/vendor/github.com/hashicorp/terraform/svchost/auth/credentials.go
new file mode 100644
index 0000000..0372c16
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/svchost/auth/credentials.go
@@ -0,0 +1,63 @@
1// Package auth contains types and functions to manage authentication
2// credentials for service hosts.
3package auth
4
5import (
6 "net/http"
7
8 "github.com/hashicorp/terraform/svchost"
9)
10
11// Credentials is a list of CredentialsSource objects that can be tried in
12// turn until one returns credentials for a host, or one returns an error.
13//
14// A Credentials is itself a CredentialsSource, wrapping its members.
15// In principle one CredentialsSource can be nested inside another, though
16// there is no good reason to do so.
17type Credentials []CredentialsSource
18
19// NoCredentials is an empty CredentialsSource that always returns nil
20// when asked for credentials.
21var NoCredentials CredentialsSource = Credentials{}
22
23// A CredentialsSource is an object that may be able to provide credentials
24// for a given host.
25//
26// Credentials lookups are not guaranteed to be concurrency-safe. Callers
27// using these facilities in concurrent code must use external concurrency
28// primitives to prevent race conditions.
29type CredentialsSource interface {
30 // ForHost returns a non-nil HostCredentials if the source has credentials
31 // available for the host, and a nil HostCredentials if it does not.
32 //
33 // If an error is returned, progress through a list of CredentialsSources
34 // is halted and the error is returned to the user.
35 ForHost(host svchost.Hostname) (HostCredentials, error)
36}
37
38// HostCredentials represents a single set of credentials for a particular
39// host.
40type HostCredentials interface {
41 // PrepareRequest modifies the given request in-place to apply the
42 // receiving credentials. The usual behavior of this method is to
43 // add some sort of Authorization header to the request.
44 PrepareRequest(req *http.Request)
45
46 // Token returns the authentication token.
47 Token() string
48}
49
50// ForHost iterates over the contained CredentialsSource objects and
51// tries to obtain credentials for the given host from each one in turn.
52//
53// If any source returns either a non-nil HostCredentials or a non-nil error
54// then this result is returned. Otherwise, the result is nil, nil.
55func (c Credentials) ForHost(host svchost.Hostname) (HostCredentials, error) {
56 for _, source := range c {
57 creds, err := source.ForHost(host)
58 if creds != nil || err != nil {
59 return creds, err
60 }
61 }
62 return nil, nil
63}
diff --git a/vendor/github.com/hashicorp/terraform/svchost/auth/from_map.go b/vendor/github.com/hashicorp/terraform/svchost/auth/from_map.go
new file mode 100644
index 0000000..f91006a
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/svchost/auth/from_map.go
@@ -0,0 +1,18 @@
1package auth
2
3// HostCredentialsFromMap converts a map of key-value pairs from a credentials
4// definition provided by the user (e.g. in a config file, or via a credentials
5// helper) into a HostCredentials object if possible, or returns nil if
6// no credentials could be extracted from the map.
7//
8// This function ignores map keys it is unfamiliar with, to allow for future
9// expansion of the credentials map format for new credential types.
10func HostCredentialsFromMap(m map[string]interface{}) HostCredentials {
11 if m == nil {
12 return nil
13 }
14 if token, ok := m["token"].(string); ok {
15 return HostCredentialsToken(token)
16 }
17 return nil
18}
diff --git a/vendor/github.com/hashicorp/terraform/svchost/auth/helper_program.go b/vendor/github.com/hashicorp/terraform/svchost/auth/helper_program.go
new file mode 100644
index 0000000..d72ffe3
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/svchost/auth/helper_program.go
@@ -0,0 +1,80 @@
1package auth
2
3import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "os/exec"
8 "path/filepath"
9
10 "github.com/hashicorp/terraform/svchost"
11)
12
13type helperProgramCredentialsSource struct {
14 executable string
15 args []string
16}
17
18// HelperProgramCredentialsSource returns a CredentialsSource that runs the
19// given program with the given arguments in order to obtain credentials.
20//
21// The given executable path must be an absolute path; it is the caller's
22// responsibility to validate and process a relative path or other input
23// provided by an end-user. If the given path is not absolute, this
24// function will panic.
25//
26// When credentials are requested, the program will be run in a child process
27// with the given arguments along with two additional arguments added to the
28// end of the list: the literal string "get", followed by the requested
29// hostname in ASCII compatibility form (punycode form).
30func HelperProgramCredentialsSource(executable string, args ...string) CredentialsSource {
31 if !filepath.IsAbs(executable) {
32 panic("NewCredentialsSourceHelperProgram requires absolute path to executable")
33 }
34
35 fullArgs := make([]string, len(args)+1)
36 fullArgs[0] = executable
37 copy(fullArgs[1:], args)
38
39 return &helperProgramCredentialsSource{
40 executable: executable,
41 args: fullArgs,
42 }
43}
44
45func (s *helperProgramCredentialsSource) ForHost(host svchost.Hostname) (HostCredentials, error) {
46 args := make([]string, len(s.args), len(s.args)+2)
47 copy(args, s.args)
48 args = append(args, "get")
49 args = append(args, string(host))
50
51 outBuf := bytes.Buffer{}
52 errBuf := bytes.Buffer{}
53
54 cmd := exec.Cmd{
55 Path: s.executable,
56 Args: args,
57 Stdin: nil,
58 Stdout: &outBuf,
59 Stderr: &errBuf,
60 }
61 err := cmd.Run()
62 if _, isExitErr := err.(*exec.ExitError); isExitErr {
63 errText := errBuf.String()
64 if errText == "" {
65 // Shouldn't happen for a well-behaved helper program
66 return nil, fmt.Errorf("error in %s, but it produced no error message", s.executable)
67 }
68 return nil, fmt.Errorf("error in %s: %s", s.executable, errText)
69 } else if err != nil {
70 return nil, fmt.Errorf("failed to run %s: %s", s.executable, err)
71 }
72
73 var m map[string]interface{}
74 err = json.Unmarshal(outBuf.Bytes(), &m)
75 if err != nil {
76 return nil, fmt.Errorf("malformed output from %s: %s", s.executable, err)
77 }
78
79 return HostCredentialsFromMap(m), nil
80}
diff --git a/vendor/github.com/hashicorp/terraform/svchost/auth/static.go b/vendor/github.com/hashicorp/terraform/svchost/auth/static.go
new file mode 100644
index 0000000..5373fdd
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/svchost/auth/static.go
@@ -0,0 +1,28 @@
1package auth
2
3import (
4 "github.com/hashicorp/terraform/svchost"
5)
6
7// StaticCredentialsSource is a credentials source that retrieves credentials
8// from the provided map. It returns nil if a requested hostname is not
9// present in the map.
10//
11// The caller should not modify the given map after passing it to this function.
12func StaticCredentialsSource(creds map[svchost.Hostname]map[string]interface{}) CredentialsSource {
13 return staticCredentialsSource(creds)
14}
15
16type staticCredentialsSource map[svchost.Hostname]map[string]interface{}
17
18func (s staticCredentialsSource) ForHost(host svchost.Hostname) (HostCredentials, error) {
19 if s == nil {
20 return nil, nil
21 }
22
23 if m, exists := s[host]; exists {
24 return HostCredentialsFromMap(m), nil
25 }
26
27 return nil, nil
28}
diff --git a/vendor/github.com/hashicorp/terraform/svchost/auth/token_credentials.go b/vendor/github.com/hashicorp/terraform/svchost/auth/token_credentials.go
new file mode 100644
index 0000000..9358bcb
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/svchost/auth/token_credentials.go
@@ -0,0 +1,25 @@
1package auth
2
3import (
4 "net/http"
5)
6
7// HostCredentialsToken is a HostCredentials implementation that represents a
8// single "bearer token", to be sent to the server via an Authorization header
9// with the auth type set to "Bearer"
10type HostCredentialsToken string
11
12// PrepareRequest alters the given HTTP request by setting its Authorization
13// header to the string "Bearer " followed by the encapsulated authentication
14// token.
15func (tc HostCredentialsToken) PrepareRequest(req *http.Request) {
16 if req.Header == nil {
17 req.Header = http.Header{}
18 }
19 req.Header.Set("Authorization", "Bearer "+string(tc))
20}
21
22// Token returns the authentication token.
23func (tc HostCredentialsToken) Token() string {
24 return string(tc)
25}
diff --git a/vendor/github.com/hashicorp/terraform/svchost/disco/disco.go b/vendor/github.com/hashicorp/terraform/svchost/disco/disco.go
new file mode 100644
index 0000000..1963cbd
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/svchost/disco/disco.go
@@ -0,0 +1,259 @@
1// Package disco handles Terraform's remote service discovery protocol.
2//
3// This protocol allows mapping from a service hostname, as produced by the
4// svchost package, to a set of services supported by that host and the
5// endpoint information for each supported service.
6package disco
7
8import (
9 "encoding/json"
10 "errors"
11 "fmt"
12 "io"
13 "io/ioutil"
14 "log"
15 "mime"
16 "net/http"
17 "net/url"
18 "time"
19
20 cleanhttp "github.com/hashicorp/go-cleanhttp"
21 "github.com/hashicorp/terraform/httpclient"
22 "github.com/hashicorp/terraform/svchost"
23 "github.com/hashicorp/terraform/svchost/auth"
24)
25
26const (
27 // Fixed path to the discovery manifest.
28 discoPath = "/.well-known/terraform.json"
29
30 // Arbitrary-but-small number to prevent runaway redirect loops.
31 maxRedirects = 3
32
33 // Arbitrary-but-small time limit to prevent UI "hangs" during discovery.
34 discoTimeout = 11 * time.Second
35
36 // 1MB - to prevent abusive services from using loads of our memory.
37 maxDiscoDocBytes = 1 * 1024 * 1024
38)
39
40// httpTransport is overridden during tests, to skip TLS verification.
41var httpTransport = cleanhttp.DefaultPooledTransport()
42
43// Disco is the main type in this package, which allows discovery on given
44// hostnames and caches the results by hostname to avoid repeated requests
45// for the same information.
46type Disco struct {
47 hostCache map[svchost.Hostname]*Host
48 credsSrc auth.CredentialsSource
49
50 // Transport is a custom http.RoundTripper to use.
51 Transport http.RoundTripper
52}
53
54// New returns a new initialized discovery object.
55func New() *Disco {
56 return NewWithCredentialsSource(nil)
57}
58
59// NewWithCredentialsSource returns a new discovery object initialized with
60// the given credentials source.
61func NewWithCredentialsSource(credsSrc auth.CredentialsSource) *Disco {
62 return &Disco{
63 hostCache: make(map[svchost.Hostname]*Host),
64 credsSrc: credsSrc,
65 Transport: httpTransport,
66 }
67}
68
69// SetCredentialsSource provides a credentials source that will be used to
70// add credentials to outgoing discovery requests, where available.
71//
72// If this method is never called, no outgoing discovery requests will have
73// credentials.
74func (d *Disco) SetCredentialsSource(src auth.CredentialsSource) {
75 d.credsSrc = src
76}
77
78// CredentialsForHost returns a non-nil HostCredentials if the embedded source has
79// credentials available for the host, and a nil HostCredentials if it does not.
80func (d *Disco) CredentialsForHost(hostname svchost.Hostname) (auth.HostCredentials, error) {
81 if d.credsSrc == nil {
82 return nil, nil
83 }
84 return d.credsSrc.ForHost(hostname)
85}
86
87// ForceHostServices provides a pre-defined set of services for a given
88// host, which prevents the receiver from attempting network-based discovery
89// for the given host. Instead, the given services map will be returned
90// verbatim.
91//
92// When providing "forced" services, any relative URLs are resolved against
93// the initial discovery URL that would have been used for network-based
94// discovery, yielding the same results as if the given map were published
95// at the host's default discovery URL, though using absolute URLs is strongly
96// recommended to make the configured behavior more explicit.
97func (d *Disco) ForceHostServices(hostname svchost.Hostname, services map[string]interface{}) {
98 if services == nil {
99 services = map[string]interface{}{}
100 }
101
102 d.hostCache[hostname] = &Host{
103 discoURL: &url.URL{
104 Scheme: "https",
105 Host: string(hostname),
106 Path: discoPath,
107 },
108 hostname: hostname.ForDisplay(),
109 services: services,
110 transport: d.Transport,
111 }
112}
113
114// Discover runs the discovery protocol against the given hostname (which must
115// already have been validated and prepared with svchost.ForComparison) and
116// returns an object describing the services available at that host.
117//
118// If a given hostname supports no Terraform services at all, a non-nil but
119// empty Host object is returned. When giving feedback to the end user about
120// such situations, we say "host <name> does not provide a <service> service",
121// regardless of whether that is due to that service specifically being absent
122// or due to the host not providing Terraform services at all, since we don't
123// wish to expose the detail of whole-host discovery to an end-user.
124func (d *Disco) Discover(hostname svchost.Hostname) (*Host, error) {
125 if host, cached := d.hostCache[hostname]; cached {
126 return host, nil
127 }
128
129 host, err := d.discover(hostname)
130 if err != nil {
131 return nil, err
132 }
133 d.hostCache[hostname] = host
134
135 return host, nil
136}
137
138// DiscoverServiceURL is a convenience wrapper for discovery on a given
139// hostname and then looking up a particular service in the result.
140func (d *Disco) DiscoverServiceURL(hostname svchost.Hostname, serviceID string) (*url.URL, error) {
141 host, err := d.Discover(hostname)
142 if err != nil {
143 return nil, err
144 }
145 return host.ServiceURL(serviceID)
146}
147
148// discover implements the actual discovery process, with its result cached
149// by the public-facing Discover method.
150func (d *Disco) discover(hostname svchost.Hostname) (*Host, error) {
151 discoURL := &url.URL{
152 Scheme: "https",
153 Host: hostname.String(),
154 Path: discoPath,
155 }
156
157 client := &http.Client{
158 Transport: d.Transport,
159 Timeout: discoTimeout,
160
161 CheckRedirect: func(req *http.Request, via []*http.Request) error {
162 log.Printf("[DEBUG] Service discovery redirected to %s", req.URL)
163 if len(via) > maxRedirects {
164 return errors.New("too many redirects") // this error will never actually be seen
165 }
166 return nil
167 },
168 }
169
170 req := &http.Request{
171 Header: make(http.Header),
172 Method: "GET",
173 URL: discoURL,
174 }
175 req.Header.Set("Accept", "application/json")
176 req.Header.Set("User-Agent", httpclient.UserAgentString())
177
178 creds, err := d.CredentialsForHost(hostname)
179 if err != nil {
180 log.Printf("[WARN] Failed to get credentials for %s: %s (ignoring)", hostname, err)
181 }
182 if creds != nil {
183 // Update the request to include credentials.
184 creds.PrepareRequest(req)
185 }
186
187 log.Printf("[DEBUG] Service discovery for %s at %s", hostname, discoURL)
188
189 resp, err := client.Do(req)
190 if err != nil {
191 return nil, fmt.Errorf("Failed to request discovery document: %v", err)
192 }
193 defer resp.Body.Close()
194
195 host := &Host{
196 // Use the discovery URL from resp.Request in
197 // case the client followed any redirects.
198 discoURL: resp.Request.URL,
199 hostname: hostname.ForDisplay(),
200 transport: d.Transport,
201 }
202
203 // Return the host without any services.
204 if resp.StatusCode == 404 {
205 return host, nil
206 }
207
208 if resp.StatusCode != 200 {
209 return nil, fmt.Errorf("Failed to request discovery document: %s", resp.Status)
210 }
211
212 contentType := resp.Header.Get("Content-Type")
213 mediaType, _, err := mime.ParseMediaType(contentType)
214 if err != nil {
215 return nil, fmt.Errorf("Discovery URL has a malformed Content-Type %q", contentType)
216 }
217 if mediaType != "application/json" {
218 return nil, fmt.Errorf("Discovery URL returned an unsupported Content-Type %q", mediaType)
219 }
220
221 // This doesn't catch chunked encoding, because ContentLength is -1 in that case.
222 if resp.ContentLength > maxDiscoDocBytes {
223 // Size limit here is not a contractual requirement and so we may
224 // adjust it over time if we find a different limit is warranted.
225 return nil, fmt.Errorf(
226 "Discovery doc response is too large (got %d bytes; limit %d)",
227 resp.ContentLength, maxDiscoDocBytes,
228 )
229 }
230
231 // If the response is using chunked encoding then we can't predict its
232 // size, but we'll at least prevent reading the entire thing into memory.
233 lr := io.LimitReader(resp.Body, maxDiscoDocBytes)
234
235 servicesBytes, err := ioutil.ReadAll(lr)
236 if err != nil {
237 return nil, fmt.Errorf("Error reading discovery document body: %v", err)
238 }
239
240 var services map[string]interface{}
241 err = json.Unmarshal(servicesBytes, &services)
242 if err != nil {
243 return nil, fmt.Errorf("Failed to decode discovery document as a JSON object: %v", err)
244 }
245 host.services = services
246
247 return host, nil
248}
249
250// Forget invalidates any cached record of the given hostname. If the host
251// has no cache entry then this is a no-op.
252func (d *Disco) Forget(hostname svchost.Hostname) {
253 delete(d.hostCache, hostname)
254}
255
256// ForgetAll is like Forget, but for all of the hostnames that have cache entries.
257func (d *Disco) ForgetAll() {
258 d.hostCache = make(map[svchost.Hostname]*Host)
259}
diff --git a/vendor/github.com/hashicorp/terraform/svchost/disco/host.go b/vendor/github.com/hashicorp/terraform/svchost/disco/host.go
new file mode 100644
index 0000000..ab9514c
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/svchost/disco/host.go
@@ -0,0 +1,264 @@
1package disco
2
3import (
4 "encoding/json"
5 "fmt"
6 "log"
7 "net/http"
8 "net/url"
9 "os"
10 "strconv"
11 "strings"
12 "time"
13
14 "github.com/hashicorp/go-version"
15 "github.com/hashicorp/terraform/httpclient"
16)
17
18const versionServiceID = "versions.v1"
19
20// Host represents a service discovered host.
21type Host struct {
22 discoURL *url.URL
23 hostname string
24 services map[string]interface{}
25 transport http.RoundTripper
26}
27
28// Constraints represents the version constraints of a service.
29type Constraints struct {
30 Service string `json:"service"`
31 Product string `json:"product"`
32 Minimum string `json:"minimum"`
33 Maximum string `json:"maximum"`
34 Excluding []string `json:"excluding"`
35}
36
37// ErrServiceNotProvided is returned when the service is not provided.
38type ErrServiceNotProvided struct {
39 hostname string
40 service string
41}
42
43// Error returns a customized error message.
44func (e *ErrServiceNotProvided) Error() string {
45 if e.hostname == "" {
46 return fmt.Sprintf("host does not provide a %s service", e.service)
47 }
48 return fmt.Sprintf("host %s does not provide a %s service", e.hostname, e.service)
49}
50
51// ErrVersionNotSupported is returned when the version is not supported.
52type ErrVersionNotSupported struct {
53 hostname string
54 service string
55 version string
56}
57
58// Error returns a customized error message.
59func (e *ErrVersionNotSupported) Error() string {
60 if e.hostname == "" {
61 return fmt.Sprintf("host does not support %s version %s", e.service, e.version)
62 }
63 return fmt.Sprintf("host %s does not support %s version %s", e.hostname, e.service, e.version)
64}
65
66// ErrNoVersionConstraints is returned when checkpoint was disabled
67// or the endpoint to query for version constraints was unavailable.
68type ErrNoVersionConstraints struct {
69 disabled bool
70}
71
72// Error returns a customized error message.
73func (e *ErrNoVersionConstraints) Error() string {
74 if e.disabled {
75 return "checkpoint disabled"
76 }
77 return "unable to contact versions service"
78}
79
80// ServiceURL returns the URL associated with the given service identifier,
81// which should be of the form "servicename.vN".
82//
83// A non-nil result is always an absolute URL with a scheme of either HTTPS
84// or HTTP.
85func (h *Host) ServiceURL(id string) (*url.URL, error) {
86 svc, ver, err := parseServiceID(id)
87 if err != nil {
88 return nil, err
89 }
90
91 // No services supported for an empty Host.
92 if h == nil || h.services == nil {
93 return nil, &ErrServiceNotProvided{service: svc}
94 }
95
96 urlStr, ok := h.services[id].(string)
97 if !ok {
98 // See if we have a matching service as that would indicate
99 // the service is supported, but not the requested version.
100 for serviceID := range h.services {
101 if strings.HasPrefix(serviceID, svc+".") {
102 return nil, &ErrVersionNotSupported{
103 hostname: h.hostname,
104 service: svc,
105 version: ver.Original(),
106 }
107 }
108 }
109
110 // No discovered services match the requested service.
111 return nil, &ErrServiceNotProvided{hostname: h.hostname, service: svc}
112 }
113
114 u, err := url.Parse(urlStr)
115 if err != nil {
116 return nil, fmt.Errorf("Failed to parse service URL: %v", err)
117 }
118
119 // Make relative URLs absolute using our discovery URL.
120 if !u.IsAbs() {
121 u = h.discoURL.ResolveReference(u)
122 }
123
124 if u.Scheme != "https" && u.Scheme != "http" {
125 return nil, fmt.Errorf("Service URL is using an unsupported scheme: %s", u.Scheme)
126 }
127 if u.User != nil {
128 return nil, fmt.Errorf("Embedded username/password information is not permitted")
129 }
130
131 // Fragment part is irrelevant, since we're not a browser.
132 u.Fragment = ""
133
134 return h.discoURL.ResolveReference(u), nil
135}
136
137// VersionConstraints returns the contraints for a given service identifier
138// (which should be of the form "servicename.vN") and product.
139//
140// When an exact (service and version) match is found, the constraints for
141// that service are returned.
142//
143// When the requested version is not provided but the service is, we will
144// search for all alternative versions. If mutliple alternative versions
145// are found, the contrains of the latest available version are returned.
146//
147// When a service is not provided at all an error will be returned instead.
148//
149// When checkpoint is disabled or when a 404 is returned after making the
150// HTTP call, an ErrNoVersionConstraints error will be returned.
151func (h *Host) VersionConstraints(id, product string) (*Constraints, error) {
152 svc, _, err := parseServiceID(id)
153 if err != nil {
154 return nil, err
155 }
156
157 // Return early if checkpoint is disabled.
158 if disabled := os.Getenv("CHECKPOINT_DISABLE"); disabled != "" {
159 return nil, &ErrNoVersionConstraints{disabled: true}
160 }
161
162 // No services supported for an empty Host.
163 if h == nil || h.services == nil {
164 return nil, &ErrServiceNotProvided{service: svc}
165 }
166
167 // Try to get the service URL for the version service and
168 // return early if the service isn't provided by the host.
169 u, err := h.ServiceURL(versionServiceID)
170 if err != nil {
171 return nil, err
172 }
173
174 // Check if we have an exact (service and version) match.
175 if _, ok := h.services[id].(string); !ok {
176 // If we don't have an exact match, we search for all matching
177 // services and then use the service ID of the latest version.
178 var services []string
179 for serviceID := range h.services {
180 if strings.HasPrefix(serviceID, svc+".") {
181 services = append(services, serviceID)
182 }
183 }
184
185 if len(services) == 0 {
186 // No discovered services match the requested service.
187 return nil, &ErrServiceNotProvided{hostname: h.hostname, service: svc}
188 }
189
190 // Set id to the latest service ID we found.
191 var latest *version.Version
192 for _, serviceID := range services {
193 if _, ver, err := parseServiceID(serviceID); err == nil {
194 if latest == nil || latest.LessThan(ver) {
195 id = serviceID
196 latest = ver
197 }
198 }
199 }
200 }
201
202 // Set a default timeout of 1 sec for the versions request (in milliseconds)
203 timeout := 1000
204 if v, err := strconv.Atoi(os.Getenv("CHECKPOINT_TIMEOUT")); err == nil {
205 timeout = v
206 }
207
208 client := &http.Client{
209 Transport: h.transport,
210 Timeout: time.Duration(timeout) * time.Millisecond,
211 }
212
213 // Prepare the service URL by setting the service and product.
214 v := u.Query()
215 v.Set("product", product)
216 u.Path += id
217 u.RawQuery = v.Encode()
218
219 // Create a new request.
220 req, err := http.NewRequest("GET", u.String(), nil)
221 if err != nil {
222 return nil, fmt.Errorf("Failed to create version constraints request: %v", err)
223 }
224 req.Header.Set("Accept", "application/json")
225 req.Header.Set("User-Agent", httpclient.UserAgentString())
226
227 log.Printf("[DEBUG] Retrieve version constraints for service %s and product %s", id, product)
228
229 resp, err := client.Do(req)
230 if err != nil {
231 return nil, fmt.Errorf("Failed to request version constraints: %v", err)
232 }
233 defer resp.Body.Close()
234
235 if resp.StatusCode == 404 {
236 return nil, &ErrNoVersionConstraints{disabled: false}
237 }
238
239 if resp.StatusCode != 200 {
240 return nil, fmt.Errorf("Failed to request version constraints: %s", resp.Status)
241 }
242
243 // Parse the constraints from the response body.
244 result := &Constraints{}
245 if err := json.NewDecoder(resp.Body).Decode(result); err != nil {
246 return nil, fmt.Errorf("Error parsing version constraints: %v", err)
247 }
248
249 return result, nil
250}
251
252func parseServiceID(id string) (string, *version.Version, error) {
253 parts := strings.SplitN(id, ".", 2)
254 if len(parts) != 2 {
255 return "", nil, fmt.Errorf("Invalid service ID format (i.e. service.vN): %s", id)
256 }
257
258 version, err := version.NewVersion(parts[1])
259 if err != nil {
260 return "", nil, fmt.Errorf("Invalid service version: %v", err)
261 }
262
263 return parts[0], version, nil
264}
diff --git a/vendor/github.com/hashicorp/terraform/svchost/label_iter.go b/vendor/github.com/hashicorp/terraform/svchost/label_iter.go
new file mode 100644
index 0000000..af8ccba
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/svchost/label_iter.go
@@ -0,0 +1,69 @@
1package svchost
2
3import (
4 "strings"
5)
6
7// A labelIter allows iterating over domain name labels.
8//
9// This type is copied from golang.org/x/net/idna, where it is used
10// to segment hostnames into their separate labels for analysis. We use
11// it for the same purpose here, in ForComparison.
12type labelIter struct {
13 orig string
14 slice []string
15 curStart int
16 curEnd int
17 i int
18}
19
20func (l *labelIter) reset() {
21 l.curStart = 0
22 l.curEnd = 0
23 l.i = 0
24}
25
26func (l *labelIter) done() bool {
27 return l.curStart >= len(l.orig)
28}
29
30func (l *labelIter) result() string {
31 if l.slice != nil {
32 return strings.Join(l.slice, ".")
33 }
34 return l.orig
35}
36
37func (l *labelIter) label() string {
38 if l.slice != nil {
39 return l.slice[l.i]
40 }
41 p := strings.IndexByte(l.orig[l.curStart:], '.')
42 l.curEnd = l.curStart + p
43 if p == -1 {
44 l.curEnd = len(l.orig)
45 }
46 return l.orig[l.curStart:l.curEnd]
47}
48
49// next sets the value to the next label. It skips the last label if it is empty.
50func (l *labelIter) next() {
51 l.i++
52 if l.slice != nil {
53 if l.i >= len(l.slice) || l.i == len(l.slice)-1 && l.slice[l.i] == "" {
54 l.curStart = len(l.orig)
55 }
56 } else {
57 l.curStart = l.curEnd + 1
58 if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
59 l.curStart = len(l.orig)
60 }
61 }
62}
63
64func (l *labelIter) set(s string) {
65 if l.slice == nil {
66 l.slice = strings.Split(l.orig, ".")
67 }
68 l.slice[l.i] = s
69}
diff --git a/vendor/github.com/hashicorp/terraform/svchost/svchost.go b/vendor/github.com/hashicorp/terraform/svchost/svchost.go
new file mode 100644
index 0000000..4eded14
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/svchost/svchost.go
@@ -0,0 +1,207 @@
1// Package svchost deals with the representations of the so-called "friendly
2// hostnames" that we use to represent systems that provide Terraform-native
3// remote services, such as module registry, remote operations, etc.
4//
5// Friendly hostnames are specified such that, as much as possible, they
6// are consistent with how web browsers think of hostnames, so that users
7// can bring their intuitions about how hostnames behave when they access
8// a Terraform Enterprise instance's web UI (or indeed any other website)
9// and have this behave in a similar way.
10package svchost
11
12import (
13 "errors"
14 "fmt"
15 "strconv"
16 "strings"
17
18 "golang.org/x/net/idna"
19)
20
21// Hostname is specialized name for string that indicates that the string
22// has been converted to (or was already in) the storage and comparison form.
23//
24// Hostname values are not suitable for display in the user-interface. Use
25// the ForDisplay method to obtain a form suitable for display in the UI.
26//
27// Unlike user-supplied hostnames, strings of type Hostname (assuming they
28// were constructed by a function within this package) can be compared for
29// equality using the standard Go == operator.
30type Hostname string
31
32// acePrefix is the ASCII Compatible Encoding prefix, used to indicate that
33// a domain name label is in "punycode" form.
34const acePrefix = "xn--"
35
36// displayProfile is a very liberal idna profile that we use to do
37// normalization for display without imposing validation rules.
38var displayProfile = idna.New(
39 idna.MapForLookup(),
40 idna.Transitional(true),
41)
42
43// ForDisplay takes a user-specified hostname and returns a normalized form of
44// it suitable for display in the UI.
45//
46// If the input is so invalid that no normalization can be performed then
47// this will return the input, assuming that the caller still wants to
48// display _something_. This function is, however, more tolerant than the
49// other functions in this package and will make a best effort to prepare
50// _any_ given hostname for display.
51//
52// For validation, use either IsValid (for explicit validation) or
53// ForComparison (which implicitly validates, returning an error if invalid).
54func ForDisplay(given string) string {
55 var portPortion string
56 if colonPos := strings.Index(given, ":"); colonPos != -1 {
57 given, portPortion = given[:colonPos], given[colonPos:]
58 }
59 portPortion, _ = normalizePortPortion(portPortion)
60
61 ascii, err := displayProfile.ToASCII(given)
62 if err != nil {
63 return given + portPortion
64 }
65 display, err := displayProfile.ToUnicode(ascii)
66 if err != nil {
67 return given + portPortion
68 }
69 return display + portPortion
70}
71
72// IsValid returns true if the given user-specified hostname is a valid
73// service hostname.
74//
75// Validity is determined by complying with the RFC 5891 requirements for
76// names that are valid for domain lookup (section 5), with the additional
77// requirement that user-supplied forms must not _already_ contain
78// Punycode segments.
79func IsValid(given string) bool {
80 _, err := ForComparison(given)
81 return err == nil
82}
83
84// ForComparison takes a user-specified hostname and returns a normalized
85// form of it suitable for storage and comparison. The result is not suitable
86// for display to end-users because it uses Punycode to represent non-ASCII
87// characters, and this form is unreadable for non-ASCII-speaking humans.
88//
89// The result is typed as Hostname -- a specialized name for string -- so that
90// other APIs can make it clear within the type system whether they expect a
91// user-specified or display-form hostname or a value already normalized for
92// comparison.
93//
94// The returned Hostname is not valid if the returned error is non-nil.
95func ForComparison(given string) (Hostname, error) {
96 var portPortion string
97 if colonPos := strings.Index(given, ":"); colonPos != -1 {
98 given, portPortion = given[:colonPos], given[colonPos:]
99 }
100
101 var err error
102 portPortion, err = normalizePortPortion(portPortion)
103 if err != nil {
104 return Hostname(""), err
105 }
106
107 if given == "" {
108 return Hostname(""), fmt.Errorf("empty string is not a valid hostname")
109 }
110
111 // First we'll apply our additional constraint that Punycode must not
112 // be given directly by the user. This is not an IDN specification
113 // requirement, but we prohibit it to force users to use human-readable
114 // hostname forms within Terraform configuration.
115 labels := labelIter{orig: given}
116 for ; !labels.done(); labels.next() {
117 label := labels.label()
118 if label == "" {
119 return Hostname(""), fmt.Errorf(
120 "hostname contains empty label (two consecutive periods)",
121 )
122 }
123 if strings.HasPrefix(label, acePrefix) {
124 return Hostname(""), fmt.Errorf(
125 "hostname label %q specified in punycode format; service hostnames must be given in unicode",
126 label,
127 )
128 }
129 }
130
131 result, err := idna.Lookup.ToASCII(given)
132 if err != nil {
133 return Hostname(""), err
134 }
135 return Hostname(result + portPortion), nil
136}
137
138// ForDisplay returns a version of the receiver that is appropriate for display
139// in the UI. This includes converting any punycode labels to their
140// corresponding Unicode characters.
141//
142// A round-trip through ForComparison and this ForDisplay method does not
143// guarantee the same result as calling this package's top-level ForDisplay
144// function, since a round-trip through the Hostname type implies stricter
145// handling than we do when doing basic display-only processing.
146func (h Hostname) ForDisplay() string {
147 given := string(h)
148 var portPortion string
149 if colonPos := strings.Index(given, ":"); colonPos != -1 {
150 given, portPortion = given[:colonPos], given[colonPos:]
151 }
152 // We don't normalize the port portion here because we assume it's
153 // already been normalized on the way in.
154
155 result, err := idna.Lookup.ToUnicode(given)
156 if err != nil {
157 // Should never happen, since type Hostname indicates that a string
158 // passed through our validation rules.
159 panic(fmt.Errorf("ForDisplay called on invalid Hostname: %s", err))
160 }
161 return result + portPortion
162}
163
164func (h Hostname) String() string {
165 return string(h)
166}
167
168func (h Hostname) GoString() string {
169 return fmt.Sprintf("svchost.Hostname(%q)", string(h))
170}
171
172// normalizePortPortion attempts to normalize the "port portion" of a hostname,
173// which begins with the first colon in the hostname and should be followed
174// by a string of decimal digits.
175//
176// If the port portion is valid, a normalized version of it is returned along
177// with a nil error.
178//
179// If the port portion is invalid, the input string is returned verbatim along
180// with a non-nil error.
181//
182// An empty string is a valid port portion representing the absense of a port.
183// If non-empty, the first character must be a colon.
184func normalizePortPortion(s string) (string, error) {
185 if s == "" {
186 return s, nil
187 }
188
189 if s[0] != ':' {
190 // should never happen, since caller tends to guarantee the presence
191 // of a colon due to how it's extracted from the string.
192 return s, errors.New("port portion is missing its initial colon")
193 }
194
195 numStr := s[1:]
196 num, err := strconv.Atoi(numStr)
197 if err != nil {
198 return s, errors.New("port portion contains non-digit characters")
199 }
200 if num == 443 {
201 return "", nil // ":443" is the default
202 }
203 if num > 65535 {
204 return s, errors.New("port number is greater than 65535")
205 }
206 return fmt.Sprintf(":%d", num), nil
207}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/context.go b/vendor/github.com/hashicorp/terraform/terraform/context.go
index a814a85..f133cc2 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/context.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/context.go
@@ -8,11 +8,13 @@ import (
8 "strings" 8 "strings"
9 "sync" 9 "sync"
10 10
11 "github.com/hashicorp/terraform/tfdiags"
12
11 "github.com/hashicorp/go-multierror" 13 "github.com/hashicorp/go-multierror"
12 "github.com/hashicorp/hcl" 14 "github.com/hashicorp/hcl"
13 "github.com/hashicorp/terraform/config" 15 "github.com/hashicorp/terraform/config"
14 "github.com/hashicorp/terraform/config/module" 16 "github.com/hashicorp/terraform/config/module"
15 "github.com/hashicorp/terraform/helper/experiment" 17 "github.com/hashicorp/terraform/version"
16) 18)
17 19
18// InputMode defines what sort of input will be asked for when Input 20// InputMode defines what sort of input will be asked for when Input
@@ -123,7 +125,7 @@ type Context struct {
123func NewContext(opts *ContextOpts) (*Context, error) { 125func NewContext(opts *ContextOpts) (*Context, error) {
124 // Validate the version requirement if it is given 126 // Validate the version requirement if it is given
125 if opts.Module != nil { 127 if opts.Module != nil {
126 if err := checkRequiredVersion(opts.Module); err != nil { 128 if err := CheckRequiredVersion(opts.Module); err != nil {
127 return nil, err 129 return nil, err
128 } 130 }
129 } 131 }
@@ -143,19 +145,14 @@ func NewContext(opts *ContextOpts) (*Context, error) {
143 145
144 // If our state is from the future, then error. Callers can avoid 146 // If our state is from the future, then error. Callers can avoid
145 // this error by explicitly setting `StateFutureAllowed`. 147 // this error by explicitly setting `StateFutureAllowed`.
146 if !opts.StateFutureAllowed && state.FromFutureTerraform() { 148 if err := CheckStateVersion(state); err != nil && !opts.StateFutureAllowed {
147 return nil, fmt.Errorf( 149 return nil, err
148 "Terraform doesn't allow running any operations against a state\n"+
149 "that was written by a future Terraform version. The state is\n"+
150 "reporting it is written by Terraform '%s'.\n\n"+
151 "Please run at least that version of Terraform to continue.",
152 state.TFVersion)
153 } 150 }
154 151
155 // Explicitly reset our state version to our current version so that 152 // Explicitly reset our state version to our current version so that
156 // any operations we do will write out that our latest version 153 // any operations we do will write out that our latest version
157 // has run. 154 // has run.
158 state.TFVersion = Version 155 state.TFVersion = version.Version
159 156
160 // Determine parallelism, default to 10. We do this both to limit 157 // Determine parallelism, default to 10. We do this both to limit
161 // CPU pressure but also to have an extra guard against rate throttling 158 // CPU pressure but also to have an extra guard against rate throttling
@@ -465,7 +462,7 @@ func (c *Context) Input(mode InputMode) error {
465 } 462 }
466 463
467 // Do the walk 464 // Do the walk
468 if _, err := c.walk(graph, nil, walkInput); err != nil { 465 if _, err := c.walk(graph, walkInput); err != nil {
469 return err 466 return err
470 } 467 }
471 } 468 }
@@ -490,6 +487,13 @@ func (c *Context) Input(mode InputMode) error {
490func (c *Context) Apply() (*State, error) { 487func (c *Context) Apply() (*State, error) {
491 defer c.acquireRun("apply")() 488 defer c.acquireRun("apply")()
492 489
490 // Check there are no empty target parameter values
491 for _, target := range c.targets {
492 if target == "" {
493 return nil, fmt.Errorf("Target parameter must not have empty value")
494 }
495 }
496
493 // Copy our own state 497 // Copy our own state
494 c.state = c.state.DeepCopy() 498 c.state = c.state.DeepCopy()
495 499
@@ -506,7 +510,7 @@ func (c *Context) Apply() (*State, error) {
506 } 510 }
507 511
508 // Walk the graph 512 // Walk the graph
509 walker, err := c.walk(graph, graph, operation) 513 walker, err := c.walk(graph, operation)
510 if len(walker.ValidationErrors) > 0 { 514 if len(walker.ValidationErrors) > 0 {
511 err = multierror.Append(err, walker.ValidationErrors...) 515 err = multierror.Append(err, walker.ValidationErrors...)
512 } 516 }
@@ -527,19 +531,27 @@ func (c *Context) Apply() (*State, error) {
527func (c *Context) Plan() (*Plan, error) { 531func (c *Context) Plan() (*Plan, error) {
528 defer c.acquireRun("plan")() 532 defer c.acquireRun("plan")()
529 533
534 // Check there are no empty target parameter values
535 for _, target := range c.targets {
536 if target == "" {
537 return nil, fmt.Errorf("Target parameter must not have empty value")
538 }
539 }
540
530 p := &Plan{ 541 p := &Plan{
531 Module: c.module, 542 Module: c.module,
532 Vars: c.variables, 543 Vars: c.variables,
533 State: c.state, 544 State: c.state,
534 Targets: c.targets, 545 Targets: c.targets,
535 546
536 TerraformVersion: VersionString(), 547 TerraformVersion: version.String(),
537 ProviderSHA256s: c.providerSHA256s, 548 ProviderSHA256s: c.providerSHA256s,
538 } 549 }
539 550
540 var operation walkOperation 551 var operation walkOperation
541 if c.destroy { 552 if c.destroy {
542 operation = walkPlanDestroy 553 operation = walkPlanDestroy
554 p.Destroy = true
543 } else { 555 } else {
544 // Set our state to be something temporary. We do this so that 556 // Set our state to be something temporary. We do this so that
545 // the plan can update a fake state so that variables work, then 557 // the plan can update a fake state so that variables work, then
@@ -575,7 +587,7 @@ func (c *Context) Plan() (*Plan, error) {
575 } 587 }
576 588
577 // Do the walk 589 // Do the walk
578 walker, err := c.walk(graph, graph, operation) 590 walker, err := c.walk(graph, operation)
579 if err != nil { 591 if err != nil {
580 return nil, err 592 return nil, err
581 } 593 }
@@ -630,7 +642,7 @@ func (c *Context) Refresh() (*State, error) {
630 } 642 }
631 643
632 // Do the walk 644 // Do the walk
633 if _, err := c.walk(graph, graph, walkRefresh); err != nil { 645 if _, err := c.walk(graph, walkRefresh); err != nil {
634 return nil, err 646 return nil, err
635 } 647 }
636 648
@@ -670,29 +682,27 @@ func (c *Context) Stop() {
670} 682}
671 683
672// Validate validates the configuration and returns any warnings or errors. 684// Validate validates the configuration and returns any warnings or errors.
673func (c *Context) Validate() ([]string, []error) { 685func (c *Context) Validate() tfdiags.Diagnostics {
674 defer c.acquireRun("validate")() 686 defer c.acquireRun("validate")()
675 687
676 var errs error 688 var diags tfdiags.Diagnostics
677 689
678 // Validate the configuration itself 690 // Validate the configuration itself
679 if err := c.module.Validate(); err != nil { 691 diags = diags.Append(c.module.Validate())
680 errs = multierror.Append(errs, err)
681 }
682 692
683 // This only needs to be done for the root module, since inter-module 693 // This only needs to be done for the root module, since inter-module
684 // variables are validated in the module tree. 694 // variables are validated in the module tree.
685 if config := c.module.Config(); config != nil { 695 if config := c.module.Config(); config != nil {
686 // Validate the user variables 696 // Validate the user variables
687 if err := smcUserVariables(config, c.variables); len(err) > 0 { 697 for _, err := range smcUserVariables(config, c.variables) {
688 errs = multierror.Append(errs, err...) 698 diags = diags.Append(err)
689 } 699 }
690 } 700 }
691 701
692 // If we have errors at this point, the graphing has no chance, 702 // If we have errors at this point, the graphing has no chance,
693 // so just bail early. 703 // so just bail early.
694 if errs != nil { 704 if diags.HasErrors() {
695 return nil, []error{errs} 705 return diags
696 } 706 }
697 707
698 // Build the graph so we can walk it and run Validate on nodes. 708 // Build the graph so we can walk it and run Validate on nodes.
@@ -701,24 +711,29 @@ func (c *Context) Validate() ([]string, []error) {
701 // graph again later after Planning. 711 // graph again later after Planning.
702 graph, err := c.Graph(GraphTypeValidate, nil) 712 graph, err := c.Graph(GraphTypeValidate, nil)
703 if err != nil { 713 if err != nil {
704 return nil, []error{err} 714 diags = diags.Append(err)
715 return diags
705 } 716 }
706 717
707 // Walk 718 // Walk
708 walker, err := c.walk(graph, graph, walkValidate) 719 walker, err := c.walk(graph, walkValidate)
709 if err != nil { 720 if err != nil {
710 return nil, multierror.Append(errs, err).Errors 721 diags = diags.Append(err)
711 } 722 }
712 723
713 // Return the result
714 rerrs := multierror.Append(errs, walker.ValidationErrors...)
715
716 sort.Strings(walker.ValidationWarnings) 724 sort.Strings(walker.ValidationWarnings)
717 sort.Slice(rerrs.Errors, func(i, j int) bool { 725 sort.Slice(walker.ValidationErrors, func(i, j int) bool {
718 return rerrs.Errors[i].Error() < rerrs.Errors[j].Error() 726 return walker.ValidationErrors[i].Error() < walker.ValidationErrors[j].Error()
719 }) 727 })
720 728
721 return walker.ValidationWarnings, rerrs.Errors 729 for _, warn := range walker.ValidationWarnings {
730 diags = diags.Append(tfdiags.SimpleWarning(warn))
731 }
732 for _, err := range walker.ValidationErrors {
733 diags = diags.Append(err)
734 }
735
736 return diags
722} 737}
723 738
724// Module returns the module tree associated with this context. 739// Module returns the module tree associated with this context.
@@ -792,33 +807,11 @@ func (c *Context) releaseRun() {
792 c.runContext = nil 807 c.runContext = nil
793} 808}
794 809
795func (c *Context) walk( 810func (c *Context) walk(graph *Graph, operation walkOperation) (*ContextGraphWalker, error) {
796 graph, shadow *Graph, operation walkOperation) (*ContextGraphWalker, error) {
797 // Keep track of the "real" context which is the context that does 811 // Keep track of the "real" context which is the context that does
798 // the real work: talking to real providers, modifying real state, etc. 812 // the real work: talking to real providers, modifying real state, etc.
799 realCtx := c 813 realCtx := c
800 814
801 // If we don't want shadowing, remove it
802 if !experiment.Enabled(experiment.X_shadow) {
803 shadow = nil
804 }
805
806 // Just log this so we can see it in a debug log
807 if !c.shadow {
808 log.Printf("[WARN] terraform: shadow graph disabled")
809 shadow = nil
810 }
811
812 // If we have a shadow graph, walk that as well
813 var shadowCtx *Context
814 var shadowCloser Shadow
815 if shadow != nil {
816 // Build the shadow context. In the process, override the real context
817 // with the one that is wrapped so that the shadow context can verify
818 // the results of the real.
819 realCtx, shadowCtx, shadowCloser = newShadowContext(c)
820 }
821
822 log.Printf("[DEBUG] Starting graph walk: %s", operation.String()) 815 log.Printf("[DEBUG] Starting graph walk: %s", operation.String())
823 816
824 walker := &ContextGraphWalker{ 817 walker := &ContextGraphWalker{
@@ -837,90 +830,6 @@ func (c *Context) walk(
837 close(watchStop) 830 close(watchStop)
838 <-watchWait 831 <-watchWait
839 832
840 // If we have a shadow graph and we interrupted the real graph, then
841 // we just close the shadow and never verify it. It is non-trivial to
842 // recreate the exact execution state up until an interruption so this
843 // isn't supported with shadows at the moment.
844 if shadowCloser != nil && c.sh.Stopped() {
845 // Ignore the error result, there is nothing we could care about
846 shadowCloser.CloseShadow()
847
848 // Set it to nil so we don't do anything
849 shadowCloser = nil
850 }
851
852 // If we have a shadow graph, wait for that to complete.
853 if shadowCloser != nil {
854 // Build the graph walker for the shadow. We also wrap this in
855 // a panicwrap so that panics are captured. For the shadow graph,
856 // we just want panics to be normal errors rather than to crash
857 // Terraform.
858 shadowWalker := GraphWalkerPanicwrap(&ContextGraphWalker{
859 Context: shadowCtx,
860 Operation: operation,
861 })
862
863 // Kick off the shadow walk. This will block on any operations
864 // on the real walk so it is fine to start first.
865 log.Printf("[INFO] Starting shadow graph walk: %s", operation.String())
866 shadowCh := make(chan error)
867 go func() {
868 shadowCh <- shadow.Walk(shadowWalker)
869 }()
870
871 // Notify the shadow that we're done
872 if err := shadowCloser.CloseShadow(); err != nil {
873 c.shadowErr = multierror.Append(c.shadowErr, err)
874 }
875
876 // Wait for the walk to end
877 log.Printf("[DEBUG] Waiting for shadow graph to complete...")
878 shadowWalkErr := <-shadowCh
879
880 // Get any shadow errors
881 if err := shadowCloser.ShadowError(); err != nil {
882 c.shadowErr = multierror.Append(c.shadowErr, err)
883 }
884
885 // Verify the contexts (compare)
886 if err := shadowContextVerify(realCtx, shadowCtx); err != nil {
887 c.shadowErr = multierror.Append(c.shadowErr, err)
888 }
889
890 // At this point, if we're supposed to fail on error, then
891 // we PANIC. Some tests just verify that there is an error,
892 // so simply appending it to realErr and returning could hide
893 // shadow problems.
894 //
895 // This must be done BEFORE appending shadowWalkErr since the
896 // shadowWalkErr may include expected errors.
897 //
898 // We only do this if we don't have a real error. In the case of
899 // a real error, we can't guarantee what nodes were and weren't
900 // traversed in parallel scenarios so we can't guarantee no
901 // shadow errors.
902 if c.shadowErr != nil && contextFailOnShadowError && realErr == nil {
903 panic(multierror.Prefix(c.shadowErr, "shadow graph:"))
904 }
905
906 // Now, if we have a walk error, we append that through
907 if shadowWalkErr != nil {
908 c.shadowErr = multierror.Append(c.shadowErr, shadowWalkErr)
909 }
910
911 if c.shadowErr == nil {
912 log.Printf("[INFO] Shadow graph success!")
913 } else {
914 log.Printf("[ERROR] Shadow graph error: %s", c.shadowErr)
915
916 // If we're supposed to fail on shadow errors, then report it
917 if contextFailOnShadowError {
918 realErr = multierror.Append(realErr, multierror.Prefix(
919 c.shadowErr, "shadow graph:"))
920 }
921 }
922 }
923
924 return walker, realErr 833 return walker, realErr
925} 834}
926 835
diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_import.go b/vendor/github.com/hashicorp/terraform/terraform/context_import.go
index f1d5776..e940143 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/context_import.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/context_import.go
@@ -66,7 +66,7 @@ func (c *Context) Import(opts *ImportOpts) (*State, error) {
66 } 66 }
67 67
68 // Walk it 68 // Walk it
69 if _, err := c.walk(graph, nil, walkImport); err != nil { 69 if _, err := c.walk(graph, walkImport); err != nil {
70 return c.state, err 70 return c.state, err
71 } 71 }
72 72
diff --git a/vendor/github.com/hashicorp/terraform/terraform/diff.go b/vendor/github.com/hashicorp/terraform/terraform/diff.go
index fd1687e..d6dc550 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/diff.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/diff.go
@@ -23,6 +23,12 @@ const (
23 DiffUpdate 23 DiffUpdate
24 DiffDestroy 24 DiffDestroy
25 DiffDestroyCreate 25 DiffDestroyCreate
26
27 // DiffRefresh is only used in the UI for displaying diffs.
28 // Managed resource reads never appear in plan, and when data source
29 // reads appear they are represented as DiffCreate in core before
30 // transforming to DiffRefresh in the UI layer.
31 DiffRefresh // TODO: Actually use DiffRefresh in core too, for less confusion
26) 32)
27 33
28// multiVal matches the index key to a flatmapped set, list or map 34// multiVal matches the index key to a flatmapped set, list or map
@@ -831,7 +837,14 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) {
831 } 837 }
832 } 838 }
833 839
834 // TODO: check for the same value if not computed 840 // We don't compare the values because we can't currently actually
841 // guarantee to generate the same value two two diffs created from
842 // the same state+config: we have some pesky interpolation functions
843 // that do not behave as pure functions (uuid, timestamp) and so they
844 // can be different each time a diff is produced.
845 // FIXME: Re-organize our config handling so that we don't re-evaluate
846 // expressions when we produce a second comparison diff during
847 // apply (for EvalCompareDiff).
835 } 848 }
836 849
837 // Check for leftover attributes 850 // Check for leftover attributes
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval.go b/vendor/github.com/hashicorp/terraform/terraform/eval.go
index 3cb088a..10d9c22 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/eval.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/eval.go
@@ -49,11 +49,11 @@ func EvalRaw(n EvalNode, ctx EvalContext) (interface{}, error) {
49 path = strings.Join(ctx.Path(), ".") 49 path = strings.Join(ctx.Path(), ".")
50 } 50 }
51 51
52 log.Printf("[DEBUG] %s: eval: %T", path, n) 52 log.Printf("[TRACE] %s: eval: %T", path, n)
53 output, err := n.Eval(ctx) 53 output, err := n.Eval(ctx)
54 if err != nil { 54 if err != nil {
55 if _, ok := err.(EvalEarlyExitError); ok { 55 if _, ok := err.(EvalEarlyExitError); ok {
56 log.Printf("[DEBUG] %s: eval: %T, err: %s", path, n, err) 56 log.Printf("[TRACE] %s: eval: %T, err: %s", path, n, err)
57 } else { 57 } else {
58 log.Printf("[ERROR] %s: eval: %T, err: %s", path, n, err) 58 log.Printf("[ERROR] %s: eval: %T, err: %s", path, n, err)
59 } 59 }
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go b/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go
index 2f6a497..b9b4806 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go
@@ -112,7 +112,7 @@ func (n *EvalApplyPre) Eval(ctx EvalContext) (interface{}, error) {
112 } 112 }
113 state.init() 113 state.init()
114 114
115 { 115 if resourceHasUserVisibleApply(n.Info) {
116 // Call post-apply hook 116 // Call post-apply hook
117 err := ctx.Hook(func(h Hook) (HookAction, error) { 117 err := ctx.Hook(func(h Hook) (HookAction, error) {
118 return h.PreApply(n.Info, state, diff) 118 return h.PreApply(n.Info, state, diff)
@@ -136,7 +136,7 @@ type EvalApplyPost struct {
136func (n *EvalApplyPost) Eval(ctx EvalContext) (interface{}, error) { 136func (n *EvalApplyPost) Eval(ctx EvalContext) (interface{}, error) {
137 state := *n.State 137 state := *n.State
138 138
139 { 139 if resourceHasUserVisibleApply(n.Info) {
140 // Call post-apply hook 140 // Call post-apply hook
141 err := ctx.Hook(func(h Hook) (HookAction, error) { 141 err := ctx.Hook(func(h Hook) (HookAction, error) {
142 return h.PostApply(n.Info, state, *n.Error) 142 return h.PostApply(n.Info, state, *n.Error)
@@ -149,6 +149,22 @@ func (n *EvalApplyPost) Eval(ctx EvalContext) (interface{}, error) {
149 return nil, *n.Error 149 return nil, *n.Error
150} 150}
151 151
152// resourceHasUserVisibleApply returns true if the given resource is one where
153// apply actions should be exposed to the user.
154//
155// Certain resources do apply actions only as an implementation detail, so
156// these should not be advertised to code outside of this package.
157func resourceHasUserVisibleApply(info *InstanceInfo) bool {
158 addr := info.ResourceAddress()
159
160 // Only managed resources have user-visible apply actions.
161 // In particular, this excludes data resources since we "apply" these
162 // only as an implementation detail of removing them from state when
163 // they are destroyed. (When reading, they don't get here at all because
164 // we present them as "Refresh" actions.)
165 return addr.Mode == config.ManagedResourceMode
166}
167
152// EvalApplyProvisioners is an EvalNode implementation that executes 168// EvalApplyProvisioners is an EvalNode implementation that executes
153// the provisioners for a resource. 169// the provisioners for a resource.
154// 170//
@@ -211,11 +227,8 @@ func (n *EvalApplyProvisioners) Eval(ctx EvalContext) (interface{}, error) {
211 state.Tainted = true 227 state.Tainted = true
212 } 228 }
213 229
214 if n.Error != nil { 230 *n.Error = multierror.Append(*n.Error, err)
215 *n.Error = multierror.Append(*n.Error, err) 231 return nil, err
216 } else {
217 return nil, err
218 }
219 } 232 }
220 233
221 { 234 {
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_context.go b/vendor/github.com/hashicorp/terraform/terraform/eval_context.go
index a1f815b..86481de 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/eval_context.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/eval_context.go
@@ -22,11 +22,11 @@ type EvalContext interface {
22 // Input is the UIInput object for interacting with the UI. 22 // Input is the UIInput object for interacting with the UI.
23 Input() UIInput 23 Input() UIInput
24 24
25 // InitProvider initializes the provider with the given name and 25 // InitProvider initializes the provider with the given type and name, and
26 // returns the implementation of the resource provider or an error. 26 // returns the implementation of the resource provider or an error.
27 // 27 //
28 // It is an error to initialize the same provider more than once. 28 // It is an error to initialize the same provider more than once.
29 InitProvider(string) (ResourceProvider, error) 29 InitProvider(typ string, name string) (ResourceProvider, error)
30 30
31 // Provider gets the provider instance with the given name (already 31 // Provider gets the provider instance with the given name (already
32 // initialized) or returns nil if the provider isn't initialized. 32 // initialized) or returns nil if the provider isn't initialized.
@@ -40,8 +40,6 @@ type EvalContext interface {
40 // is used to store the provider configuration for inheritance lookups 40 // is used to store the provider configuration for inheritance lookups
41 // with ParentProviderConfig(). 41 // with ParentProviderConfig().
42 ConfigureProvider(string, *ResourceConfig) error 42 ConfigureProvider(string, *ResourceConfig) error
43 SetProviderConfig(string, *ResourceConfig) error
44 ParentProviderConfig(string) *ResourceConfig
45 43
46 // ProviderInput and SetProviderInput are used to configure providers 44 // ProviderInput and SetProviderInput are used to configure providers
47 // from user input. 45 // from user input.
@@ -69,6 +67,13 @@ type EvalContext interface {
69 // that is currently being acted upon. 67 // that is currently being acted upon.
70 Interpolate(*config.RawConfig, *Resource) (*ResourceConfig, error) 68 Interpolate(*config.RawConfig, *Resource) (*ResourceConfig, error)
71 69
70 // InterpolateProvider takes a ProviderConfig and interpolates it with the
71 // stored interpolation scope. Since provider configurations can be
72 // inherited, the interpolation scope may be different from the current
73 // context path. Interplation is otherwise executed the same as in the
74 // Interpolation method.
75 InterpolateProvider(*config.ProviderConfig, *Resource) (*ResourceConfig, error)
76
72 // SetVariables sets the variables for the module within 77 // SetVariables sets the variables for the module within
73 // this context with the name n. This function call is additive: 78 // this context with the name n. This function call is additive:
74 // the second parameter is merged with any previous call. 79 // the second parameter is merged with any previous call.
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_context_builtin.go b/vendor/github.com/hashicorp/terraform/terraform/eval_context_builtin.go
index 3dcfb22..1b6ee5a 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/eval_context_builtin.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/eval_context_builtin.go
@@ -4,7 +4,6 @@ import (
4 "context" 4 "context"
5 "fmt" 5 "fmt"
6 "log" 6 "log"
7 "strings"
8 "sync" 7 "sync"
9 8
10 "github.com/hashicorp/terraform/config" 9 "github.com/hashicorp/terraform/config"
@@ -34,7 +33,6 @@ type BuiltinEvalContext struct {
34 Hooks []Hook 33 Hooks []Hook
35 InputValue UIInput 34 InputValue UIInput
36 ProviderCache map[string]ResourceProvider 35 ProviderCache map[string]ResourceProvider
37 ProviderConfigCache map[string]*ResourceConfig
38 ProviderInputConfig map[string]map[string]interface{} 36 ProviderInputConfig map[string]map[string]interface{}
39 ProviderLock *sync.Mutex 37 ProviderLock *sync.Mutex
40 ProvisionerCache map[string]ResourceProvisioner 38 ProvisionerCache map[string]ResourceProvisioner
@@ -80,12 +78,12 @@ func (ctx *BuiltinEvalContext) Input() UIInput {
80 return ctx.InputValue 78 return ctx.InputValue
81} 79}
82 80
83func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) { 81func (ctx *BuiltinEvalContext) InitProvider(typeName, name string) (ResourceProvider, error) {
84 ctx.once.Do(ctx.init) 82 ctx.once.Do(ctx.init)
85 83
86 // If we already initialized, it is an error 84 // If we already initialized, it is an error
87 if p := ctx.Provider(n); p != nil { 85 if p := ctx.Provider(name); p != nil {
88 return nil, fmt.Errorf("Provider '%s' already initialized", n) 86 return nil, fmt.Errorf("Provider '%s' already initialized", name)
89 } 87 }
90 88
91 // Warning: make sure to acquire these locks AFTER the call to Provider 89 // Warning: make sure to acquire these locks AFTER the call to Provider
@@ -93,18 +91,12 @@ func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error)
93 ctx.ProviderLock.Lock() 91 ctx.ProviderLock.Lock()
94 defer ctx.ProviderLock.Unlock() 92 defer ctx.ProviderLock.Unlock()
95 93
96 providerPath := make([]string, len(ctx.Path())+1) 94 p, err := ctx.Components.ResourceProvider(typeName, name)
97 copy(providerPath, ctx.Path())
98 providerPath[len(providerPath)-1] = n
99 key := PathCacheKey(providerPath)
100
101 typeName := strings.SplitN(n, ".", 2)[0]
102 p, err := ctx.Components.ResourceProvider(typeName, key)
103 if err != nil { 95 if err != nil {
104 return nil, err 96 return nil, err
105 } 97 }
106 98
107 ctx.ProviderCache[key] = p 99 ctx.ProviderCache[name] = p
108 return p, nil 100 return p, nil
109} 101}
110 102
@@ -114,11 +106,7 @@ func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider {
114 ctx.ProviderLock.Lock() 106 ctx.ProviderLock.Lock()
115 defer ctx.ProviderLock.Unlock() 107 defer ctx.ProviderLock.Unlock()
116 108
117 providerPath := make([]string, len(ctx.Path())+1) 109 return ctx.ProviderCache[n]
118 copy(providerPath, ctx.Path())
119 providerPath[len(providerPath)-1] = n
120
121 return ctx.ProviderCache[PathCacheKey(providerPath)]
122} 110}
123 111
124func (ctx *BuiltinEvalContext) CloseProvider(n string) error { 112func (ctx *BuiltinEvalContext) CloseProvider(n string) error {
@@ -127,15 +115,11 @@ func (ctx *BuiltinEvalContext) CloseProvider(n string) error {
127 ctx.ProviderLock.Lock() 115 ctx.ProviderLock.Lock()
128 defer ctx.ProviderLock.Unlock() 116 defer ctx.ProviderLock.Unlock()
129 117
130 providerPath := make([]string, len(ctx.Path())+1)
131 copy(providerPath, ctx.Path())
132 providerPath[len(providerPath)-1] = n
133
134 var provider interface{} 118 var provider interface{}
135 provider = ctx.ProviderCache[PathCacheKey(providerPath)] 119 provider = ctx.ProviderCache[n]
136 if provider != nil { 120 if provider != nil {
137 if p, ok := provider.(ResourceProviderCloser); ok { 121 if p, ok := provider.(ResourceProviderCloser); ok {
138 delete(ctx.ProviderCache, PathCacheKey(providerPath)) 122 delete(ctx.ProviderCache, n)
139 return p.Close() 123 return p.Close()
140 } 124 }
141 } 125 }
@@ -149,28 +133,9 @@ func (ctx *BuiltinEvalContext) ConfigureProvider(
149 if p == nil { 133 if p == nil {
150 return fmt.Errorf("Provider '%s' not initialized", n) 134 return fmt.Errorf("Provider '%s' not initialized", n)
151 } 135 }
152
153 if err := ctx.SetProviderConfig(n, cfg); err != nil {
154 return nil
155 }
156
157 return p.Configure(cfg) 136 return p.Configure(cfg)
158} 137}
159 138
160func (ctx *BuiltinEvalContext) SetProviderConfig(
161 n string, cfg *ResourceConfig) error {
162 providerPath := make([]string, len(ctx.Path())+1)
163 copy(providerPath, ctx.Path())
164 providerPath[len(providerPath)-1] = n
165
166 // Save the configuration
167 ctx.ProviderLock.Lock()
168 ctx.ProviderConfigCache[PathCacheKey(providerPath)] = cfg
169 ctx.ProviderLock.Unlock()
170
171 return nil
172}
173
174func (ctx *BuiltinEvalContext) ProviderInput(n string) map[string]interface{} { 139func (ctx *BuiltinEvalContext) ProviderInput(n string) map[string]interface{} {
175 ctx.ProviderLock.Lock() 140 ctx.ProviderLock.Lock()
176 defer ctx.ProviderLock.Unlock() 141 defer ctx.ProviderLock.Unlock()
@@ -203,27 +168,6 @@ func (ctx *BuiltinEvalContext) SetProviderInput(n string, c map[string]interface
203 ctx.ProviderLock.Unlock() 168 ctx.ProviderLock.Unlock()
204} 169}
205 170
206func (ctx *BuiltinEvalContext) ParentProviderConfig(n string) *ResourceConfig {
207 ctx.ProviderLock.Lock()
208 defer ctx.ProviderLock.Unlock()
209
210 // Make a copy of the path so we can safely edit it
211 path := ctx.Path()
212 pathCopy := make([]string, len(path)+1)
213 copy(pathCopy, path)
214
215 // Go up the tree.
216 for i := len(path) - 1; i >= 0; i-- {
217 pathCopy[i+1] = n
218 k := PathCacheKey(pathCopy[:i+2])
219 if v, ok := ctx.ProviderConfigCache[k]; ok {
220 return v
221 }
222 }
223
224 return nil
225}
226
227func (ctx *BuiltinEvalContext) InitProvisioner( 171func (ctx *BuiltinEvalContext) InitProvisioner(
228 n string) (ResourceProvisioner, error) { 172 n string) (ResourceProvisioner, error) {
229 ctx.once.Do(ctx.init) 173 ctx.once.Do(ctx.init)
@@ -289,6 +233,7 @@ func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error {
289 233
290func (ctx *BuiltinEvalContext) Interpolate( 234func (ctx *BuiltinEvalContext) Interpolate(
291 cfg *config.RawConfig, r *Resource) (*ResourceConfig, error) { 235 cfg *config.RawConfig, r *Resource) (*ResourceConfig, error) {
236
292 if cfg != nil { 237 if cfg != nil {
293 scope := &InterpolationScope{ 238 scope := &InterpolationScope{
294 Path: ctx.Path(), 239 Path: ctx.Path(),
@@ -311,6 +256,35 @@ func (ctx *BuiltinEvalContext) Interpolate(
311 return result, nil 256 return result, nil
312} 257}
313 258
259func (ctx *BuiltinEvalContext) InterpolateProvider(
260 pc *config.ProviderConfig, r *Resource) (*ResourceConfig, error) {
261
262 var cfg *config.RawConfig
263
264 if pc != nil && pc.RawConfig != nil {
265 scope := &InterpolationScope{
266 Path: ctx.Path(),
267 Resource: r,
268 }
269
270 cfg = pc.RawConfig
271
272 vs, err := ctx.Interpolater.Values(scope, cfg.Variables)
273 if err != nil {
274 return nil, err
275 }
276
277 // Do the interpolation
278 if err := cfg.Interpolate(vs); err != nil {
279 return nil, err
280 }
281 }
282
283 result := NewResourceConfig(cfg)
284 result.interpolateForce()
285 return result, nil
286}
287
314func (ctx *BuiltinEvalContext) Path() []string { 288func (ctx *BuiltinEvalContext) Path() []string {
315 return ctx.PathValue 289 return ctx.PathValue
316} 290}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_context_mock.go b/vendor/github.com/hashicorp/terraform/terraform/eval_context_mock.go
index 4f90d5b..6464517 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/eval_context_mock.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/eval_context_mock.go
@@ -45,14 +45,6 @@ type MockEvalContext struct {
45 ConfigureProviderConfig *ResourceConfig 45 ConfigureProviderConfig *ResourceConfig
46 ConfigureProviderError error 46 ConfigureProviderError error
47 47
48 SetProviderConfigCalled bool
49 SetProviderConfigName string
50 SetProviderConfigConfig *ResourceConfig
51
52 ParentProviderConfigCalled bool
53 ParentProviderConfigName string
54 ParentProviderConfigConfig *ResourceConfig
55
56 InitProvisionerCalled bool 48 InitProvisionerCalled bool
57 InitProvisionerName string 49 InitProvisionerName string
58 InitProvisionerProvisioner ResourceProvisioner 50 InitProvisionerProvisioner ResourceProvisioner
@@ -72,6 +64,12 @@ type MockEvalContext struct {
72 InterpolateConfigResult *ResourceConfig 64 InterpolateConfigResult *ResourceConfig
73 InterpolateError error 65 InterpolateError error
74 66
67 InterpolateProviderCalled bool
68 InterpolateProviderConfig *config.ProviderConfig
69 InterpolateProviderResource *Resource
70 InterpolateProviderConfigResult *ResourceConfig
71 InterpolateProviderError error
72
75 PathCalled bool 73 PathCalled bool
76 PathPath []string 74 PathPath []string
77 75
@@ -109,7 +107,7 @@ func (c *MockEvalContext) Input() UIInput {
109 return c.InputInput 107 return c.InputInput
110} 108}
111 109
112func (c *MockEvalContext) InitProvider(n string) (ResourceProvider, error) { 110func (c *MockEvalContext) InitProvider(t, n string) (ResourceProvider, error) {
113 c.InitProviderCalled = true 111 c.InitProviderCalled = true
114 c.InitProviderName = n 112 c.InitProviderName = n
115 return c.InitProviderProvider, c.InitProviderError 113 return c.InitProviderProvider, c.InitProviderError
@@ -134,20 +132,6 @@ func (c *MockEvalContext) ConfigureProvider(n string, cfg *ResourceConfig) error
134 return c.ConfigureProviderError 132 return c.ConfigureProviderError
135} 133}
136 134
137func (c *MockEvalContext) SetProviderConfig(
138 n string, cfg *ResourceConfig) error {
139 c.SetProviderConfigCalled = true
140 c.SetProviderConfigName = n
141 c.SetProviderConfigConfig = cfg
142 return nil
143}
144
145func (c *MockEvalContext) ParentProviderConfig(n string) *ResourceConfig {
146 c.ParentProviderConfigCalled = true
147 c.ParentProviderConfigName = n
148 return c.ParentProviderConfigConfig
149}
150
151func (c *MockEvalContext) ProviderInput(n string) map[string]interface{} { 135func (c *MockEvalContext) ProviderInput(n string) map[string]interface{} {
152 c.ProviderInputCalled = true 136 c.ProviderInputCalled = true
153 c.ProviderInputName = n 137 c.ProviderInputName = n
@@ -186,6 +170,14 @@ func (c *MockEvalContext) Interpolate(
186 return c.InterpolateConfigResult, c.InterpolateError 170 return c.InterpolateConfigResult, c.InterpolateError
187} 171}
188 172
173func (c *MockEvalContext) InterpolateProvider(
174 config *config.ProviderConfig, resource *Resource) (*ResourceConfig, error) {
175 c.InterpolateProviderCalled = true
176 c.InterpolateProviderConfig = config
177 c.InterpolateProviderResource = resource
178 return c.InterpolateProviderConfigResult, c.InterpolateError
179}
180
189func (c *MockEvalContext) Path() []string { 181func (c *MockEvalContext) Path() []string {
190 c.PathCalled = true 182 c.PathCalled = true
191 return c.PathPath 183 return c.PathPath
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_diff.go b/vendor/github.com/hashicorp/terraform/terraform/eval_diff.go
index c35f908..26205ce 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/eval_diff.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/eval_diff.go
@@ -6,6 +6,7 @@ import (
6 "strings" 6 "strings"
7 7
8 "github.com/hashicorp/terraform/config" 8 "github.com/hashicorp/terraform/config"
9 "github.com/hashicorp/terraform/version"
9) 10)
10 11
11// EvalCompareDiff is an EvalNode implementation that compares two diffs 12// EvalCompareDiff is an EvalNode implementation that compares two diffs
@@ -60,7 +61,7 @@ func (n *EvalCompareDiff) Eval(ctx EvalContext) (interface{}, error) {
60 "\n"+ 61 "\n"+
61 "Also include as much context as you can about your config, state, "+ 62 "Also include as much context as you can about your config, state, "+
62 "and the steps you performed to trigger this error.\n", 63 "and the steps you performed to trigger this error.\n",
63 n.Info.Id, Version, n.Info.Id, reason, one, two) 64 n.Info.Id, version.Version, n.Info.Id, reason, one, two)
64 } 65 }
65 66
66 return nil, nil 67 return nil, nil
@@ -255,11 +256,15 @@ func (n *EvalDiff) processIgnoreChanges(diff *InstanceDiff) error {
255 containers := groupContainers(diff) 256 containers := groupContainers(diff)
256 keep := map[string]bool{} 257 keep := map[string]bool{}
257 for _, v := range containers { 258 for _, v := range containers {
258 if v.keepDiff() { 259 if v.keepDiff(ignorableAttrKeys) {
259 // At least one key has changes, so list all the sibling keys 260 // At least one key has changes, so list all the sibling keys
260 // to keep in the diff. 261 // to keep in the diff
261 for k := range v { 262 for k := range v {
262 keep[k] = true 263 keep[k] = true
264 // this key may have been added by the user to ignore, but
265 // if it's a subkey in a container, we need to un-ignore it
266 // to keep the complete containter.
267 delete(ignorableAttrKeys, k)
263 } 268 }
264 } 269 }
265 } 270 }
@@ -291,10 +296,17 @@ func (n *EvalDiff) processIgnoreChanges(diff *InstanceDiff) error {
291// a group of key-*ResourceAttrDiff pairs from the same flatmapped container 296// a group of key-*ResourceAttrDiff pairs from the same flatmapped container
292type flatAttrDiff map[string]*ResourceAttrDiff 297type flatAttrDiff map[string]*ResourceAttrDiff
293 298
294// we need to keep all keys if any of them have a diff 299// we need to keep all keys if any of them have a diff that's not ignored
295func (f flatAttrDiff) keepDiff() bool { 300func (f flatAttrDiff) keepDiff(ignoreChanges map[string]bool) bool {
296 for _, v := range f { 301 for k, v := range f {
297 if !v.Empty() && !v.NewComputed { 302 ignore := false
303 for attr := range ignoreChanges {
304 if strings.HasPrefix(k, attr) {
305 ignore = true
306 }
307 }
308
309 if !v.Empty() && !v.NewComputed && !ignore {
298 return true 310 return true
299 } 311 }
300 } 312 }
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_interpolate.go b/vendor/github.com/hashicorp/terraform/terraform/eval_interpolate.go
index 6825ff5..6a78a6b 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/eval_interpolate.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/eval_interpolate.go
@@ -1,18 +1,50 @@
1package terraform 1package terraform
2 2
3import "github.com/hashicorp/terraform/config" 3import (
4 "log"
5
6 "github.com/hashicorp/terraform/config"
7)
4 8
5// EvalInterpolate is an EvalNode implementation that takes a raw 9// EvalInterpolate is an EvalNode implementation that takes a raw
6// configuration and interpolates it. 10// configuration and interpolates it.
7type EvalInterpolate struct { 11type EvalInterpolate struct {
8 Config *config.RawConfig 12 Config *config.RawConfig
9 Resource *Resource 13 Resource *Resource
10 Output **ResourceConfig 14 Output **ResourceConfig
15 ContinueOnErr bool
11} 16}
12 17
13func (n *EvalInterpolate) Eval(ctx EvalContext) (interface{}, error) { 18func (n *EvalInterpolate) Eval(ctx EvalContext) (interface{}, error) {
14 rc, err := ctx.Interpolate(n.Config, n.Resource) 19 rc, err := ctx.Interpolate(n.Config, n.Resource)
15 if err != nil { 20 if err != nil {
21 if n.ContinueOnErr {
22 log.Printf("[WARN] Interpolation %q failed: %s", n.Config.Key, err)
23 return nil, EvalEarlyExitError{}
24 }
25 return nil, err
26 }
27
28 if n.Output != nil {
29 *n.Output = rc
30 }
31
32 return nil, nil
33}
34
35// EvalInterpolateProvider is an EvalNode implementation that takes a
36// ProviderConfig and interpolates it. Provider configurations are the only
37// "inherited" type of configuration we have, and the original raw config may
38// have a different interpolation scope.
39type EvalInterpolateProvider struct {
40 Config *config.ProviderConfig
41 Resource *Resource
42 Output **ResourceConfig
43}
44
45func (n *EvalInterpolateProvider) Eval(ctx EvalContext) (interface{}, error) {
46 rc, err := ctx.InterpolateProvider(n.Config, n.Resource)
47 if err != nil {
16 return nil, err 48 return nil, err
17 } 49 }
18 50
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_local.go b/vendor/github.com/hashicorp/terraform/terraform/eval_local.go
new file mode 100644
index 0000000..a4b2a50
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/terraform/eval_local.go
@@ -0,0 +1,86 @@
1package terraform
2
3import (
4 "fmt"
5
6 "github.com/hashicorp/terraform/config"
7)
8
9// EvalLocal is an EvalNode implementation that evaluates the
10// expression for a local value and writes it into a transient part of
11// the state.
12type EvalLocal struct {
13 Name string
14 Value *config.RawConfig
15}
16
17func (n *EvalLocal) Eval(ctx EvalContext) (interface{}, error) {
18 cfg, err := ctx.Interpolate(n.Value, nil)
19 if err != nil {
20 return nil, fmt.Errorf("local.%s: %s", n.Name, err)
21 }
22
23 state, lock := ctx.State()
24 if state == nil {
25 return nil, fmt.Errorf("cannot write local value to nil state")
26 }
27
28 // Get a write lock so we can access the state
29 lock.Lock()
30 defer lock.Unlock()
31
32 // Look for the module state. If we don't have one, create it.
33 mod := state.ModuleByPath(ctx.Path())
34 if mod == nil {
35 mod = state.AddModule(ctx.Path())
36 }
37
38 // Get the value from the config
39 var valueRaw interface{} = config.UnknownVariableValue
40 if cfg != nil {
41 var ok bool
42 valueRaw, ok = cfg.Get("value")
43 if !ok {
44 valueRaw = ""
45 }
46 if cfg.IsComputed("value") {
47 valueRaw = config.UnknownVariableValue
48 }
49 }
50
51 if mod.Locals == nil {
52 // initialize
53 mod.Locals = map[string]interface{}{}
54 }
55 mod.Locals[n.Name] = valueRaw
56
57 return nil, nil
58}
59
60// EvalDeleteLocal is an EvalNode implementation that deletes a Local value
61// from the state. Locals aren't persisted, but we don't need to evaluate them
62// during destroy.
63type EvalDeleteLocal struct {
64 Name string
65}
66
67func (n *EvalDeleteLocal) Eval(ctx EvalContext) (interface{}, error) {
68 state, lock := ctx.State()
69 if state == nil {
70 return nil, nil
71 }
72
73 // Get a write lock so we can access this instance
74 lock.Lock()
75 defer lock.Unlock()
76
77 // Look for the module state. If we don't have one, create it.
78 mod := state.ModuleByPath(ctx.Path())
79 if mod == nil {
80 return nil, nil
81 }
82
83 delete(mod.Locals, n.Name)
84
85 return nil, nil
86}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_output.go b/vendor/github.com/hashicorp/terraform/terraform/eval_output.go
index cf61781..a834627 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/eval_output.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/eval_output.go
@@ -41,15 +41,16 @@ type EvalWriteOutput struct {
41 Name string 41 Name string
42 Sensitive bool 42 Sensitive bool
43 Value *config.RawConfig 43 Value *config.RawConfig
44 // ContinueOnErr allows interpolation to fail during Input
45 ContinueOnErr bool
44} 46}
45 47
46// TODO: test 48// TODO: test
47func (n *EvalWriteOutput) Eval(ctx EvalContext) (interface{}, error) { 49func (n *EvalWriteOutput) Eval(ctx EvalContext) (interface{}, error) {
50 // This has to run before we have a state lock, since interpolation also
51 // reads the state
48 cfg, err := ctx.Interpolate(n.Value, nil) 52 cfg, err := ctx.Interpolate(n.Value, nil)
49 if err != nil { 53 // handle the error after we have the module from the state
50 // Log error but continue anyway
51 log.Printf("[WARN] Output interpolation %q failed: %s", n.Name, err)
52 }
53 54
54 state, lock := ctx.State() 55 state, lock := ctx.State()
55 if state == nil { 56 if state == nil {
@@ -59,13 +60,27 @@ func (n *EvalWriteOutput) Eval(ctx EvalContext) (interface{}, error) {
59 // Get a write lock so we can access this instance 60 // Get a write lock so we can access this instance
60 lock.Lock() 61 lock.Lock()
61 defer lock.Unlock() 62 defer lock.Unlock()
62
63 // Look for the module state. If we don't have one, create it. 63 // Look for the module state. If we don't have one, create it.
64 mod := state.ModuleByPath(ctx.Path()) 64 mod := state.ModuleByPath(ctx.Path())
65 if mod == nil { 65 if mod == nil {
66 mod = state.AddModule(ctx.Path()) 66 mod = state.AddModule(ctx.Path())
67 } 67 }
68 68
69 // handling the interpolation error
70 if err != nil {
71 if n.ContinueOnErr || flagWarnOutputErrors {
72 log.Printf("[ERROR] Output interpolation %q failed: %s", n.Name, err)
73 // if we're continuing, make sure the output is included, and
74 // marked as unknown
75 mod.Outputs[n.Name] = &OutputState{
76 Type: "string",
77 Value: config.UnknownVariableValue,
78 }
79 return nil, EvalEarlyExitError{}
80 }
81 return nil, err
82 }
83
69 // Get the value from the config 84 // Get the value from the config
70 var valueRaw interface{} = config.UnknownVariableValue 85 var valueRaw interface{} = config.UnknownVariableValue
71 if cfg != nil { 86 if cfg != nil {
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_provider.go b/vendor/github.com/hashicorp/terraform/terraform/eval_provider.go
index 092fd18..61f6ff9 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/eval_provider.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/eval_provider.go
@@ -6,17 +6,6 @@ import (
6 "github.com/hashicorp/terraform/config" 6 "github.com/hashicorp/terraform/config"
7) 7)
8 8
9// EvalSetProviderConfig sets the parent configuration for a provider
10// without configuring that provider, validating it, etc.
11type EvalSetProviderConfig struct {
12 Provider string
13 Config **ResourceConfig
14}
15
16func (n *EvalSetProviderConfig) Eval(ctx EvalContext) (interface{}, error) {
17 return nil, ctx.SetProviderConfig(n.Provider, *n.Config)
18}
19
20// EvalBuildProviderConfig outputs a *ResourceConfig that is properly 9// EvalBuildProviderConfig outputs a *ResourceConfig that is properly
21// merged with parents and inputs on top of what is configured in the file. 10// merged with parents and inputs on top of what is configured in the file.
22type EvalBuildProviderConfig struct { 11type EvalBuildProviderConfig struct {
@@ -28,7 +17,7 @@ type EvalBuildProviderConfig struct {
28func (n *EvalBuildProviderConfig) Eval(ctx EvalContext) (interface{}, error) { 17func (n *EvalBuildProviderConfig) Eval(ctx EvalContext) (interface{}, error) {
29 cfg := *n.Config 18 cfg := *n.Config
30 19
31 // If we have a configuration set, then merge that in 20 // If we have an Input configuration set, then merge that in
32 if input := ctx.ProviderInput(n.Provider); input != nil { 21 if input := ctx.ProviderInput(n.Provider); input != nil {
33 // "input" is a map of the subset of config values that were known 22 // "input" is a map of the subset of config values that were known
34 // during the input walk, set by EvalInputProvider. Note that 23 // during the input walk, set by EvalInputProvider. Note that
@@ -40,13 +29,7 @@ func (n *EvalBuildProviderConfig) Eval(ctx EvalContext) (interface{}, error) {
40 return nil, err 29 return nil, err
41 } 30 }
42 31
43 merged := cfg.raw.Merge(rc) 32 merged := rc.Merge(cfg.raw)
44 cfg = NewResourceConfig(merged)
45 }
46
47 // Get the parent configuration if there is one
48 if parent := ctx.ParentProviderConfig(n.Provider); parent != nil {
49 merged := cfg.raw.Merge(parent.raw)
50 cfg = NewResourceConfig(merged) 33 cfg = NewResourceConfig(merged)
51 } 34 }
52 35
@@ -69,11 +52,12 @@ func (n *EvalConfigProvider) Eval(ctx EvalContext) (interface{}, error) {
69// and returns nothing. The provider can be retrieved again with the 52// and returns nothing. The provider can be retrieved again with the
70// EvalGetProvider node. 53// EvalGetProvider node.
71type EvalInitProvider struct { 54type EvalInitProvider struct {
72 Name string 55 TypeName string
56 Name string
73} 57}
74 58
75func (n *EvalInitProvider) Eval(ctx EvalContext) (interface{}, error) { 59func (n *EvalInitProvider) Eval(ctx EvalContext) (interface{}, error) {
76 return ctx.InitProvider(n.Name) 60 return ctx.InitProvider(n.TypeName, n.Name)
77} 61}
78 62
79// EvalCloseProvider is an EvalNode implementation that closes provider 63// EvalCloseProvider is an EvalNode implementation that closes provider
@@ -116,12 +100,8 @@ type EvalInputProvider struct {
116} 100}
117 101
118func (n *EvalInputProvider) Eval(ctx EvalContext) (interface{}, error) { 102func (n *EvalInputProvider) Eval(ctx EvalContext) (interface{}, error) {
119 // If we already configured this provider, then don't do this again
120 if v := ctx.ProviderInput(n.Name); v != nil {
121 return nil, nil
122 }
123
124 rc := *n.Config 103 rc := *n.Config
104 orig := rc.DeepCopy()
125 105
126 // Wrap the input into a namespace 106 // Wrap the input into a namespace
127 input := &PrefixUIInput{ 107 input := &PrefixUIInput{
@@ -138,27 +118,20 @@ func (n *EvalInputProvider) Eval(ctx EvalContext) (interface{}, error) {
138 "Error configuring %s: %s", n.Name, err) 118 "Error configuring %s: %s", n.Name, err)
139 } 119 }
140 120
141 // Set the input that we received so that child modules don't attempt 121 // We only store values that have changed through Input.
142 // to ask for input again. 122 // The goal is to cache cache input responses, not to provide a complete
123 // config for other providers.
124 confMap := make(map[string]interface{})
143 if config != nil && len(config.Config) > 0 { 125 if config != nil && len(config.Config) > 0 {
144 // This repository of provider input results on the context doesn't 126 // any values that weren't in the original ResourcConfig will be cached
145 // retain config.ComputedKeys, so we need to filter those out here 127 for k, v := range config.Config {
146 // in order that later users of this data won't try to use the unknown 128 if _, ok := orig.Config[k]; !ok {
147 // value placeholder as if it were a literal value. This map is just 129 confMap[k] = v
148 // of known values we've been able to complete so far; dynamic stuff
149 // will be merged in by EvalBuildProviderConfig on subsequent
150 // (post-input) walks.
151 confMap := config.Config
152 if config.ComputedKeys != nil {
153 for _, key := range config.ComputedKeys {
154 delete(confMap, key)
155 } 130 }
156 } 131 }
157
158 ctx.SetProviderInput(n.Name, confMap)
159 } else {
160 ctx.SetProviderInput(n.Name, map[string]interface{}{})
161 } 132 }
162 133
134 ctx.SetProviderInput(n.Name, confMap)
135
163 return nil, nil 136 return nil, nil
164} 137}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_state.go b/vendor/github.com/hashicorp/terraform/terraform/eval_state.go
index 126a0e6..1182690 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/eval_state.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/eval_state.go
@@ -1,6 +1,8 @@
1package terraform 1package terraform
2 2
3import "fmt" 3import (
4 "fmt"
5)
4 6
5// EvalReadState is an EvalNode implementation that reads the 7// EvalReadState is an EvalNode implementation that reads the
6// primary InstanceState for a specific resource out of the state. 8// primary InstanceState for a specific resource out of the state.
@@ -212,37 +214,6 @@ func writeInstanceToState(
212 return nil, nil 214 return nil, nil
213} 215}
214 216
215// EvalClearPrimaryState is an EvalNode implementation that clears the primary
216// instance from a resource state.
217type EvalClearPrimaryState struct {
218 Name string
219}
220
221func (n *EvalClearPrimaryState) Eval(ctx EvalContext) (interface{}, error) {
222 state, lock := ctx.State()
223
224 // Get a read lock so we can access this instance
225 lock.RLock()
226 defer lock.RUnlock()
227
228 // Look for the module state. If we don't have one, then it doesn't matter.
229 mod := state.ModuleByPath(ctx.Path())
230 if mod == nil {
231 return nil, nil
232 }
233
234 // Look for the resource state. If we don't have one, then it is okay.
235 rs := mod.Resources[n.Name]
236 if rs == nil {
237 return nil, nil
238 }
239
240 // Clear primary from the resource state
241 rs.Primary = nil
242
243 return nil, nil
244}
245
246// EvalDeposeState is an EvalNode implementation that takes the primary 217// EvalDeposeState is an EvalNode implementation that takes the primary
247// out of a state and makes it Deposed. This is done at the beginning of 218// out of a state and makes it Deposed. This is done at the beginning of
248// create-before-destroy calls so that the create can create while preserving 219// create-before-destroy calls so that the create can create while preserving
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go b/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go
index 478aa64..3e5a84c 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go
@@ -144,16 +144,20 @@ func (n *EvalValidateProvisioner) validateConnConfig(connConfig *ResourceConfig)
144 144
145 // For type=ssh only (enforced in ssh communicator) 145 // For type=ssh only (enforced in ssh communicator)
146 PrivateKey interface{} `mapstructure:"private_key"` 146 PrivateKey interface{} `mapstructure:"private_key"`
147 HostKey interface{} `mapstructure:"host_key"`
147 Agent interface{} `mapstructure:"agent"` 148 Agent interface{} `mapstructure:"agent"`
148 BastionHost interface{} `mapstructure:"bastion_host"` 149 BastionHost interface{} `mapstructure:"bastion_host"`
150 BastionHostKey interface{} `mapstructure:"bastion_host_key"`
149 BastionPort interface{} `mapstructure:"bastion_port"` 151 BastionPort interface{} `mapstructure:"bastion_port"`
150 BastionUser interface{} `mapstructure:"bastion_user"` 152 BastionUser interface{} `mapstructure:"bastion_user"`
151 BastionPassword interface{} `mapstructure:"bastion_password"` 153 BastionPassword interface{} `mapstructure:"bastion_password"`
152 BastionPrivateKey interface{} `mapstructure:"bastion_private_key"` 154 BastionPrivateKey interface{} `mapstructure:"bastion_private_key"`
155 AgentIdentity interface{} `mapstructure:"agent_identity"`
153 156
154 // For type=winrm only (enforced in winrm communicator) 157 // For type=winrm only (enforced in winrm communicator)
155 HTTPS interface{} `mapstructure:"https"` 158 HTTPS interface{} `mapstructure:"https"`
156 Insecure interface{} `mapstructure:"insecure"` 159 Insecure interface{} `mapstructure:"insecure"`
160 NTLM interface{} `mapstructure:"use_ntlm"`
157 CACert interface{} `mapstructure:"cacert"` 161 CACert interface{} `mapstructure:"cacert"`
158 } 162 }
159 163
diff --git a/vendor/github.com/hashicorp/terraform/terraform/evaltree_provider.go b/vendor/github.com/hashicorp/terraform/terraform/evaltree_provider.go
index 00392ef..0c3da48 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/evaltree_provider.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/evaltree_provider.go
@@ -1,17 +1,24 @@
1package terraform 1package terraform
2 2
3import ( 3import (
4 "strings"
5
4 "github.com/hashicorp/terraform/config" 6 "github.com/hashicorp/terraform/config"
5) 7)
6 8
7// ProviderEvalTree returns the evaluation tree for initializing and 9// ProviderEvalTree returns the evaluation tree for initializing and
8// configuring providers. 10// configuring providers.
9func ProviderEvalTree(n string, config *config.RawConfig) EvalNode { 11func ProviderEvalTree(n *NodeApplyableProvider, config *config.ProviderConfig) EvalNode {
10 var provider ResourceProvider 12 var provider ResourceProvider
11 var resourceConfig *ResourceConfig 13 var resourceConfig *ResourceConfig
12 14
15 typeName := strings.SplitN(n.NameValue, ".", 2)[0]
16
13 seq := make([]EvalNode, 0, 5) 17 seq := make([]EvalNode, 0, 5)
14 seq = append(seq, &EvalInitProvider{Name: n}) 18 seq = append(seq, &EvalInitProvider{
19 TypeName: typeName,
20 Name: n.Name(),
21 })
15 22
16 // Input stuff 23 // Input stuff
17 seq = append(seq, &EvalOpFilter{ 24 seq = append(seq, &EvalOpFilter{
@@ -19,20 +26,20 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode {
19 Node: &EvalSequence{ 26 Node: &EvalSequence{
20 Nodes: []EvalNode{ 27 Nodes: []EvalNode{
21 &EvalGetProvider{ 28 &EvalGetProvider{
22 Name: n, 29 Name: n.Name(),
23 Output: &provider, 30 Output: &provider,
24 }, 31 },
25 &EvalInterpolate{ 32 &EvalInterpolateProvider{
26 Config: config, 33 Config: config,
27 Output: &resourceConfig, 34 Output: &resourceConfig,
28 }, 35 },
29 &EvalBuildProviderConfig{ 36 &EvalBuildProviderConfig{
30 Provider: n, 37 Provider: n.NameValue,
31 Config: &resourceConfig, 38 Config: &resourceConfig,
32 Output: &resourceConfig, 39 Output: &resourceConfig,
33 }, 40 },
34 &EvalInputProvider{ 41 &EvalInputProvider{
35 Name: n, 42 Name: n.NameValue,
36 Provider: &provider, 43 Provider: &provider,
37 Config: &resourceConfig, 44 Config: &resourceConfig,
38 }, 45 },
@@ -45,15 +52,15 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode {
45 Node: &EvalSequence{ 52 Node: &EvalSequence{
46 Nodes: []EvalNode{ 53 Nodes: []EvalNode{
47 &EvalGetProvider{ 54 &EvalGetProvider{
48 Name: n, 55 Name: n.Name(),
49 Output: &provider, 56 Output: &provider,
50 }, 57 },
51 &EvalInterpolate{ 58 &EvalInterpolateProvider{
52 Config: config, 59 Config: config,
53 Output: &resourceConfig, 60 Output: &resourceConfig,
54 }, 61 },
55 &EvalBuildProviderConfig{ 62 &EvalBuildProviderConfig{
56 Provider: n, 63 Provider: n.NameValue,
57 Config: &resourceConfig, 64 Config: &resourceConfig,
58 Output: &resourceConfig, 65 Output: &resourceConfig,
59 }, 66 },
@@ -61,10 +68,6 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode {
61 Provider: &provider, 68 Provider: &provider,
62 Config: &resourceConfig, 69 Config: &resourceConfig,
63 }, 70 },
64 &EvalSetProviderConfig{
65 Provider: n,
66 Config: &resourceConfig,
67 },
68 }, 71 },
69 }, 72 },
70 }) 73 })
@@ -75,22 +78,18 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode {
75 Node: &EvalSequence{ 78 Node: &EvalSequence{
76 Nodes: []EvalNode{ 79 Nodes: []EvalNode{
77 &EvalGetProvider{ 80 &EvalGetProvider{
78 Name: n, 81 Name: n.Name(),
79 Output: &provider, 82 Output: &provider,
80 }, 83 },
81 &EvalInterpolate{ 84 &EvalInterpolateProvider{
82 Config: config, 85 Config: config,
83 Output: &resourceConfig, 86 Output: &resourceConfig,
84 }, 87 },
85 &EvalBuildProviderConfig{ 88 &EvalBuildProviderConfig{
86 Provider: n, 89 Provider: n.NameValue,
87 Config: &resourceConfig, 90 Config: &resourceConfig,
88 Output: &resourceConfig, 91 Output: &resourceConfig,
89 }, 92 },
90 &EvalSetProviderConfig{
91 Provider: n,
92 Config: &resourceConfig,
93 },
94 }, 93 },
95 }, 94 },
96 }) 95 })
@@ -102,7 +101,7 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode {
102 Node: &EvalSequence{ 101 Node: &EvalSequence{
103 Nodes: []EvalNode{ 102 Nodes: []EvalNode{
104 &EvalConfigProvider{ 103 &EvalConfigProvider{
105 Provider: n, 104 Provider: n.Name(),
106 Config: &resourceConfig, 105 Config: &resourceConfig,
107 }, 106 },
108 }, 107 },
diff --git a/vendor/github.com/hashicorp/terraform/terraform/features.go b/vendor/github.com/hashicorp/terraform/terraform/features.go
new file mode 100644
index 0000000..97c77bd
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/terraform/features.go
@@ -0,0 +1,7 @@
1package terraform
2
3import "os"
4
5// This file holds feature flags for the next release
6
7var flagWarnOutputErrors = os.Getenv("TF_WARN_OUTPUT_ERRORS") != ""
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph.go b/vendor/github.com/hashicorp/terraform/terraform/graph.go
index 48ce6a3..735ec4e 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/graph.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/graph.go
@@ -70,7 +70,7 @@ func (g *Graph) walk(walker GraphWalker) error {
70 // Walk the graph. 70 // Walk the graph.
71 var walkFn dag.WalkFunc 71 var walkFn dag.WalkFunc
72 walkFn = func(v dag.Vertex) (rerr error) { 72 walkFn = func(v dag.Vertex) (rerr error) {
73 log.Printf("[DEBUG] vertex '%s.%s': walking", path, dag.VertexName(v)) 73 log.Printf("[TRACE] vertex '%s.%s': walking", path, dag.VertexName(v))
74 g.DebugVisitInfo(v, g.debugName) 74 g.DebugVisitInfo(v, g.debugName)
75 75
76 // If we have a panic wrap GraphWalker and a panic occurs, recover 76 // If we have a panic wrap GraphWalker and a panic occurs, recover
@@ -118,7 +118,7 @@ func (g *Graph) walk(walker GraphWalker) error {
118 118
119 // Allow the walker to change our tree if needed. Eval, 119 // Allow the walker to change our tree if needed. Eval,
120 // then callback with the output. 120 // then callback with the output.
121 log.Printf("[DEBUG] vertex '%s.%s': evaluating", path, dag.VertexName(v)) 121 log.Printf("[TRACE] vertex '%s.%s': evaluating", path, dag.VertexName(v))
122 122
123 g.DebugVertexInfo(v, fmt.Sprintf("evaluating %T(%s)", v, path)) 123 g.DebugVertexInfo(v, fmt.Sprintf("evaluating %T(%s)", v, path))
124 124
@@ -132,7 +132,7 @@ func (g *Graph) walk(walker GraphWalker) error {
132 // If the node is dynamically expanded, then expand it 132 // If the node is dynamically expanded, then expand it
133 if ev, ok := v.(GraphNodeDynamicExpandable); ok { 133 if ev, ok := v.(GraphNodeDynamicExpandable); ok {
134 log.Printf( 134 log.Printf(
135 "[DEBUG] vertex '%s.%s': expanding/walking dynamic subgraph", 135 "[TRACE] vertex '%s.%s': expanding/walking dynamic subgraph",
136 path, 136 path,
137 dag.VertexName(v)) 137 dag.VertexName(v))
138 138
@@ -154,7 +154,7 @@ func (g *Graph) walk(walker GraphWalker) error {
154 // If the node has a subgraph, then walk the subgraph 154 // If the node has a subgraph, then walk the subgraph
155 if sn, ok := v.(GraphNodeSubgraph); ok { 155 if sn, ok := v.(GraphNodeSubgraph); ok {
156 log.Printf( 156 log.Printf(
157 "[DEBUG] vertex '%s.%s': walking subgraph", 157 "[TRACE] vertex '%s.%s': walking subgraph",
158 path, 158 path,
159 dag.VertexName(v)) 159 dag.VertexName(v))
160 160
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply.go
index 38a90f2..0c2b233 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply.go
@@ -87,12 +87,8 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
87 // Attach the state 87 // Attach the state
88 &AttachStateTransformer{State: b.State}, 88 &AttachStateTransformer{State: b.State},
89 89
90 // Create all the providers 90 // add providers
91 &MissingProviderTransformer{Providers: b.Providers, Concrete: concreteProvider}, 91 TransformProviders(b.Providers, concreteProvider, b.Module),
92 &ProviderTransformer{},
93 &DisableProviderTransformer{},
94 &ParentProviderTransformer{},
95 &AttachProviderConfigTransformer{Module: b.Module},
96 92
97 // Destruction ordering 93 // Destruction ordering
98 &DestroyEdgeTransformer{Module: b.Module, State: b.State}, 94 &DestroyEdgeTransformer{Module: b.Module, State: b.State},
@@ -108,15 +104,36 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
108 // Add root variables 104 // Add root variables
109 &RootVariableTransformer{Module: b.Module}, 105 &RootVariableTransformer{Module: b.Module},
110 106
107 // Add the local values
108 &LocalTransformer{Module: b.Module},
109
111 // Add the outputs 110 // Add the outputs
112 &OutputTransformer{Module: b.Module}, 111 &OutputTransformer{Module: b.Module},
113 112
114 // Add module variables 113 // Add module variables
115 &ModuleVariableTransformer{Module: b.Module}, 114 &ModuleVariableTransformer{Module: b.Module},
116 115
116 // Remove modules no longer present in the config
117 &RemovedModuleTransformer{Module: b.Module, State: b.State},
118
117 // Connect references so ordering is correct 119 // Connect references so ordering is correct
118 &ReferenceTransformer{}, 120 &ReferenceTransformer{},
119 121
122 // Handle destroy time transformations for output and local values.
123 // Reverse the edges from outputs and locals, so that
124 // interpolations don't fail during destroy.
125 // Create a destroy node for outputs to remove them from the state.
126 // Prune unreferenced values, which may have interpolations that can't
127 // be resolved.
128 GraphTransformIf(
129 func() bool { return b.Destroy },
130 GraphTransformMulti(
131 &DestroyValueReferenceTransformer{},
132 &DestroyOutputTransformer{},
133 &PruneUnusedValuesTransformer{},
134 ),
135 ),
136
120 // Add the node to fix the state count boundaries 137 // Add the node to fix the state count boundaries
121 &CountBoundaryTransformer{}, 138 &CountBoundaryTransformer{},
122 139
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_import.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_import.go
index 7070c59..07a1eaf 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_import.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_import.go
@@ -52,12 +52,7 @@ func (b *ImportGraphBuilder) Steps() []GraphTransformer {
52 // Add the import steps 52 // Add the import steps
53 &ImportStateTransformer{Targets: b.ImportTargets}, 53 &ImportStateTransformer{Targets: b.ImportTargets},
54 54
55 // Provider-related transformations 55 TransformProviders(b.Providers, concreteProvider, mod),
56 &MissingProviderTransformer{Providers: b.Providers, Concrete: concreteProvider},
57 &ProviderTransformer{},
58 &DisableProviderTransformer{},
59 &ParentProviderTransformer{},
60 &AttachProviderConfigTransformer{Module: mod},
61 56
62 // This validates that the providers only depend on variables 57 // This validates that the providers only depend on variables
63 &ImportProviderValidateTransformer{}, 58 &ImportProviderValidateTransformer{},
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan.go
index 4b29bbb..f8dd0fc 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan.go
@@ -71,6 +71,9 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
71 Module: b.Module, 71 Module: b.Module,
72 }, 72 },
73 73
74 // Add the local values
75 &LocalTransformer{Module: b.Module},
76
74 // Add the outputs 77 // Add the outputs
75 &OutputTransformer{Module: b.Module}, 78 &OutputTransformer{Module: b.Module},
76 79
@@ -81,6 +84,12 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
81 Module: b.Module, 84 Module: b.Module,
82 }, 85 },
83 86
87 // Create orphan output nodes
88 &OrphanOutputTransformer{
89 Module: b.Module,
90 State: b.State,
91 },
92
84 // Attach the configuration to any resources 93 // Attach the configuration to any resources
85 &AttachResourceConfigTransformer{Module: b.Module}, 94 &AttachResourceConfigTransformer{Module: b.Module},
86 95
@@ -90,12 +99,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
90 // Add root variables 99 // Add root variables
91 &RootVariableTransformer{Module: b.Module}, 100 &RootVariableTransformer{Module: b.Module},
92 101
93 // Create all the providers 102 TransformProviders(b.Providers, b.ConcreteProvider, b.Module),
94 &MissingProviderTransformer{Providers: b.Providers, Concrete: b.ConcreteProvider},
95 &ProviderTransformer{},
96 &DisableProviderTransformer{},
97 &ParentProviderTransformer{},
98 &AttachProviderConfigTransformer{Module: b.Module},
99 103
100 // Provisioner-related transformations. Only add these if requested. 104 // Provisioner-related transformations. Only add these if requested.
101 GraphTransformIf( 105 GraphTransformIf(
@@ -107,7 +111,12 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
107 ), 111 ),
108 112
109 // Add module variables 113 // Add module variables
110 &ModuleVariableTransformer{Module: b.Module}, 114 &ModuleVariableTransformer{
115 Module: b.Module,
116 },
117
118 // Remove modules no longer present in the config
119 &RemovedModuleTransformer{Module: b.Module, State: b.State},
111 120
112 // Connect so that the references are ready for targeting. We'll 121 // Connect so that the references are ready for targeting. We'll
113 // have to connect again later for providers and so on. 122 // have to connect again later for providers and so on.
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 3d3e968..9638d4c 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_refresh.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_refresh.go
@@ -126,12 +126,10 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer {
126 // Add root variables 126 // Add root variables
127 &RootVariableTransformer{Module: b.Module}, 127 &RootVariableTransformer{Module: b.Module},
128 128
129 // Create all the providers 129 TransformProviders(b.Providers, concreteProvider, b.Module),
130 &MissingProviderTransformer{Providers: b.Providers, Concrete: concreteProvider}, 130
131 &ProviderTransformer{}, 131 // Add the local values
132 &DisableProviderTransformer{}, 132 &LocalTransformer{Module: b.Module},
133 &ParentProviderTransformer{},
134 &AttachProviderConfigTransformer{Module: b.Module},
135 133
136 // Add the outputs 134 // Add the outputs
137 &OutputTransformer{Module: b.Module}, 135 &OutputTransformer{Module: b.Module},
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_walk_context.go b/vendor/github.com/hashicorp/terraform/terraform/graph_walk_context.go
index e63b460..89f376e 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/graph_walk_context.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/graph_walk_context.go
@@ -32,7 +32,6 @@ type ContextGraphWalker struct {
32 interpolaterVars map[string]map[string]interface{} 32 interpolaterVars map[string]map[string]interface{}
33 interpolaterVarLock sync.Mutex 33 interpolaterVarLock sync.Mutex
34 providerCache map[string]ResourceProvider 34 providerCache map[string]ResourceProvider
35 providerConfigCache map[string]*ResourceConfig
36 providerLock sync.Mutex 35 providerLock sync.Mutex
37 provisionerCache map[string]ResourceProvisioner 36 provisionerCache map[string]ResourceProvisioner
38 provisionerLock sync.Mutex 37 provisionerLock sync.Mutex
@@ -73,7 +72,6 @@ func (w *ContextGraphWalker) EnterPath(path []string) EvalContext {
73 InputValue: w.Context.uiInput, 72 InputValue: w.Context.uiInput,
74 Components: w.Context.components, 73 Components: w.Context.components,
75 ProviderCache: w.providerCache, 74 ProviderCache: w.providerCache,
76 ProviderConfigCache: w.providerConfigCache,
77 ProviderInputConfig: w.Context.providerInputConfig, 75 ProviderInputConfig: w.Context.providerInputConfig,
78 ProviderLock: &w.providerLock, 76 ProviderLock: &w.providerLock,
79 ProvisionerCache: w.provisionerCache, 77 ProvisionerCache: w.provisionerCache,
@@ -151,7 +149,6 @@ func (w *ContextGraphWalker) ExitEvalTree(
151func (w *ContextGraphWalker) init() { 149func (w *ContextGraphWalker) init() {
152 w.contexts = make(map[string]*BuiltinEvalContext, 5) 150 w.contexts = make(map[string]*BuiltinEvalContext, 5)
153 w.providerCache = make(map[string]ResourceProvider, 5) 151 w.providerCache = make(map[string]ResourceProvider, 5)
154 w.providerConfigCache = make(map[string]*ResourceConfig, 5)
155 w.provisionerCache = make(map[string]ResourceProvisioner, 5) 152 w.provisionerCache = make(map[string]ResourceProvisioner, 5)
156 w.interpolaterVars = make(map[string]map[string]interface{}, 5) 153 w.interpolaterVars = make(map[string]map[string]interface{}, 5)
157} 154}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graphtype_string.go b/vendor/github.com/hashicorp/terraform/terraform/graphtype_string.go
index e97b485..95ef4e9 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/graphtype_string.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/graphtype_string.go
@@ -2,7 +2,7 @@
2 2
3package terraform 3package terraform
4 4
5import "fmt" 5import "strconv"
6 6
7const _GraphType_name = "GraphTypeInvalidGraphTypeLegacyGraphTypeRefreshGraphTypePlanGraphTypePlanDestroyGraphTypeApplyGraphTypeInputGraphTypeValidate" 7const _GraphType_name = "GraphTypeInvalidGraphTypeLegacyGraphTypeRefreshGraphTypePlanGraphTypePlanDestroyGraphTypeApplyGraphTypeInputGraphTypeValidate"
8 8
@@ -10,7 +10,7 @@ var _GraphType_index = [...]uint8{0, 16, 31, 47, 60, 80, 94, 108, 125}
10 10
11func (i GraphType) String() string { 11func (i GraphType) String() string {
12 if i >= GraphType(len(_GraphType_index)-1) { 12 if i >= GraphType(len(_GraphType_index)-1) {
13 return fmt.Sprintf("GraphType(%d)", i) 13 return "GraphType(" + strconv.FormatInt(int64(i), 10) + ")"
14 } 14 }
15 return _GraphType_name[_GraphType_index[i]:_GraphType_index[i+1]] 15 return _GraphType_name[_GraphType_index[i]:_GraphType_index[i+1]]
16} 16}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/instancetype_string.go b/vendor/github.com/hashicorp/terraform/terraform/instancetype_string.go
index f69267c..b8e7d1f 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/instancetype_string.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/instancetype_string.go
@@ -2,7 +2,7 @@
2 2
3package terraform 3package terraform
4 4
5import "fmt" 5import "strconv"
6 6
7const _InstanceType_name = "TypeInvalidTypePrimaryTypeTaintedTypeDeposed" 7const _InstanceType_name = "TypeInvalidTypePrimaryTypeTaintedTypeDeposed"
8 8
@@ -10,7 +10,7 @@ var _InstanceType_index = [...]uint8{0, 11, 22, 33, 44}
10 10
11func (i InstanceType) String() string { 11func (i InstanceType) String() string {
12 if i < 0 || i >= InstanceType(len(_InstanceType_index)-1) { 12 if i < 0 || i >= InstanceType(len(_InstanceType_index)-1) {
13 return fmt.Sprintf("InstanceType(%d)", i) 13 return "InstanceType(" + strconv.FormatInt(int64(i), 10) + ")"
14 } 14 }
15 return _InstanceType_name[_InstanceType_index[i]:_InstanceType_index[i+1]] 15 return _InstanceType_name[_InstanceType_index[i]:_InstanceType_index[i+1]]
16} 16}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/interpolate.go b/vendor/github.com/hashicorp/terraform/terraform/interpolate.go
index 22ddce6..4f4e178 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/interpolate.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/interpolate.go
@@ -90,6 +90,8 @@ func (i *Interpolater) Values(
90 err = i.valueSimpleVar(scope, n, v, result) 90 err = i.valueSimpleVar(scope, n, v, result)
91 case *config.TerraformVariable: 91 case *config.TerraformVariable:
92 err = i.valueTerraformVar(scope, n, v, result) 92 err = i.valueTerraformVar(scope, n, v, result)
93 case *config.LocalVariable:
94 err = i.valueLocalVar(scope, n, v, result)
93 case *config.UserVariable: 95 case *config.UserVariable:
94 err = i.valueUserVar(scope, n, v, result) 96 err = i.valueUserVar(scope, n, v, result)
95 default: 97 default:
@@ -140,7 +142,6 @@ func (i *Interpolater) valueModuleVar(
140 n string, 142 n string,
141 v *config.ModuleVariable, 143 v *config.ModuleVariable,
142 result map[string]ast.Variable) error { 144 result map[string]ast.Variable) error {
143
144 // Build the path to the child module we want 145 // Build the path to the child module we want
145 path := make([]string, len(scope.Path), len(scope.Path)+1) 146 path := make([]string, len(scope.Path), len(scope.Path)+1)
146 copy(path, scope.Path) 147 copy(path, scope.Path)
@@ -317,7 +318,6 @@ func (i *Interpolater) valueTerraformVar(
317 n string, 318 n string,
318 v *config.TerraformVariable, 319 v *config.TerraformVariable,
319 result map[string]ast.Variable) error { 320 result map[string]ast.Variable) error {
320
321 // "env" is supported for backward compatibility, but it's deprecated and 321 // "env" is supported for backward compatibility, but it's deprecated and
322 // so we won't advertise it as being allowed in the error message. It will 322 // so we won't advertise it as being allowed in the error message. It will
323 // be removed in a future version of Terraform. 323 // be removed in a future version of Terraform.
@@ -335,6 +335,59 @@ func (i *Interpolater) valueTerraformVar(
335 return nil 335 return nil
336} 336}
337 337
338func (i *Interpolater) valueLocalVar(
339 scope *InterpolationScope,
340 n string,
341 v *config.LocalVariable,
342 result map[string]ast.Variable,
343) error {
344 i.StateLock.RLock()
345 defer i.StateLock.RUnlock()
346
347 modTree := i.Module
348 if len(scope.Path) > 1 {
349 modTree = i.Module.Child(scope.Path[1:])
350 }
351
352 // Get the resource from the configuration so we can verify
353 // that the resource is in the configuration and so we can access
354 // the configuration if we need to.
355 var cl *config.Local
356 for _, l := range modTree.Config().Locals {
357 if l.Name == v.Name {
358 cl = l
359 break
360 }
361 }
362
363 if cl == nil {
364 return fmt.Errorf("%s: no local value of this name has been declared", n)
365 }
366
367 // Get the relevant module
368 module := i.State.ModuleByPath(scope.Path)
369 if module == nil {
370 result[n] = unknownVariable()
371 return nil
372 }
373
374 rawV, exists := module.Locals[v.Name]
375 if !exists {
376 result[n] = unknownVariable()
377 return nil
378 }
379
380 varV, err := hil.InterfaceToVariable(rawV)
381 if err != nil {
382 // Should never happen, since interpolation should always produce
383 // something we can feed back in to interpolation.
384 return fmt.Errorf("%s: %s", n, err)
385 }
386
387 result[n] = varV
388 return nil
389}
390
338func (i *Interpolater) valueUserVar( 391func (i *Interpolater) valueUserVar(
339 scope *InterpolationScope, 392 scope *InterpolationScope,
340 n string, 393 n string,
@@ -465,6 +518,16 @@ func (i *Interpolater) computeResourceVariable(
465 return &v, err 518 return &v, err
466 } 519 }
467 520
521 // special case for the "id" field which is usually also an attribute
522 if v.Field == "id" && r.Primary.ID != "" {
523 // This is usually pulled from the attributes, but is sometimes missing
524 // during destroy. We can return the ID field in this case.
525 // FIXME: there should only be one ID to rule them all.
526 log.Printf("[WARN] resource %s missing 'id' attribute", v.ResourceId())
527 v, err := hil.InterfaceToVariable(r.Primary.ID)
528 return &v, err
529 }
530
468 // computed list or map attribute 531 // computed list or map attribute
469 _, isList = r.Primary.Attributes[v.Field+".#"] 532 _, isList = r.Primary.Attributes[v.Field+".#"]
470 _, isMap = r.Primary.Attributes[v.Field+".%"] 533 _, isMap = r.Primary.Attributes[v.Field+".%"]
@@ -602,6 +665,11 @@ func (i *Interpolater) computeResourceMultiVariable(
602 continue 665 continue
603 } 666 }
604 667
668 if v.Field == "id" && r.Primary.ID != "" {
669 log.Printf("[WARN] resource %s missing 'id' attribute", v.ResourceId())
670 values = append(values, r.Primary.ID)
671 }
672
605 // computed list or map attribute 673 // computed list or map attribute
606 _, isList := r.Primary.Attributes[v.Field+".#"] 674 _, isList := r.Primary.Attributes[v.Field+".#"]
607 _, isMap := r.Primary.Attributes[v.Field+".%"] 675 _, isMap := r.Primary.Attributes[v.Field+".%"]
@@ -646,7 +714,6 @@ func (i *Interpolater) computeResourceMultiVariable(
646func (i *Interpolater) interpolateComplexTypeAttribute( 714func (i *Interpolater) interpolateComplexTypeAttribute(
647 resourceID string, 715 resourceID string,
648 attributes map[string]string) (ast.Variable, error) { 716 attributes map[string]string) (ast.Variable, error) {
649
650 // We can now distinguish between lists and maps in state by the count field: 717 // We can now distinguish between lists and maps in state by the count field:
651 // - lists (and by extension, sets) use the traditional .# notation 718 // - lists (and by extension, sets) use the traditional .# notation
652 // - maps use the newer .% notation 719 // - maps use the newer .% notation
@@ -722,7 +789,8 @@ func (i *Interpolater) resourceCountMax(
722 // If we're NOT applying, then we assume we can read the count 789 // If we're NOT applying, then we assume we can read the count
723 // from the state. Plan and so on may not have any state yet so 790 // from the state. Plan and so on may not have any state yet so
724 // we do a full interpolation. 791 // we do a full interpolation.
725 if i.Operation != walkApply { 792 // Don't forget walkDestroy, which is a special case of walkApply
793 if !(i.Operation == walkApply || i.Operation == walkDestroy) {
726 if cr == nil { 794 if cr == nil {
727 return 0, nil 795 return 0, nil
728 } 796 }
@@ -753,7 +821,13 @@ func (i *Interpolater) resourceCountMax(
753 // use "cr.Count()" but that doesn't work if the count is interpolated 821 // use "cr.Count()" but that doesn't work if the count is interpolated
754 // and we can't guarantee that so we instead depend on the state. 822 // and we can't guarantee that so we instead depend on the state.
755 max := -1 823 max := -1
756 for k, _ := range ms.Resources { 824 for k, s := range ms.Resources {
825 // This resource may have been just removed, in which case the Primary
826 // may be nil, or just empty.
827 if s == nil || s.Primary == nil || len(s.Primary.Attributes) == 0 {
828 continue
829 }
830
757 // Get the index number for this resource 831 // Get the index number for this resource
758 index := "" 832 index := ""
759 if k == id { 833 if k == id {
diff --git a/vendor/github.com/hashicorp/terraform/terraform/module_dependencies.go b/vendor/github.com/hashicorp/terraform/terraform/module_dependencies.go
index b9f44a0..4594cb6 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/module_dependencies.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/module_dependencies.go
@@ -17,7 +17,6 @@ import (
17// present in the configuration. This is guaranteed not to happen for any 17// present in the configuration. This is guaranteed not to happen for any
18// configuration that has passed a call to Config.Validate(). 18// configuration that has passed a call to Config.Validate().
19func ModuleTreeDependencies(root *module.Tree, state *State) *moduledeps.Module { 19func ModuleTreeDependencies(root *module.Tree, state *State) *moduledeps.Module {
20
21 // First we walk the configuration tree to build the overall structure 20 // First we walk the configuration tree to build the overall structure
22 // and capture the explicit/implicit/inherited provider dependencies. 21 // and capture the explicit/implicit/inherited provider dependencies.
23 deps := moduleTreeConfigDependencies(root, nil) 22 deps := moduleTreeConfigDependencies(root, nil)
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 45129b3..d5ca641 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go
@@ -27,6 +27,7 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er
27 concreteResource := func(a *NodeAbstractResource) dag.Vertex { 27 concreteResource := func(a *NodeAbstractResource) dag.Vertex {
28 // Add the config and state since we don't do that via transforms 28 // Add the config and state since we don't do that via transforms
29 a.Config = n.Config 29 a.Config = n.Config
30 a.ResolvedProvider = n.ResolvedProvider
30 31
31 return &NodeRefreshableDataResourceInstance{ 32 return &NodeRefreshableDataResourceInstance{
32 NodeAbstractResource: a, 33 NodeAbstractResource: a,
@@ -107,7 +108,9 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
107 // Get the state if we have it, if not we build it 108 // Get the state if we have it, if not we build it
108 rs := n.ResourceState 109 rs := n.ResourceState
109 if rs == nil { 110 if rs == nil {
110 rs = &ResourceState{} 111 rs = &ResourceState{
112 Provider: n.ResolvedProvider,
113 }
111 } 114 }
112 115
113 // If the config isn't empty we update the state 116 // If the config isn't empty we update the state
@@ -145,7 +148,7 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
145 &EvalWriteState{ 148 &EvalWriteState{
146 Name: stateId, 149 Name: stateId,
147 ResourceType: rs.Type, 150 ResourceType: rs.Type,
148 Provider: rs.Provider, 151 Provider: n.ResolvedProvider,
149 Dependencies: rs.Dependencies, 152 Dependencies: rs.Dependencies,
150 State: &state, // state is nil here 153 State: &state, // state is nil here
151 }, 154 },
@@ -185,7 +188,7 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
185 // provider configurations that need this data during 188 // provider configurations that need this data during
186 // refresh/plan. 189 // refresh/plan.
187 &EvalGetProvider{ 190 &EvalGetProvider{
188 Name: n.ProvidedBy()[0], 191 Name: n.ResolvedProvider,
189 Output: &provider, 192 Output: &provider,
190 }, 193 },
191 194
@@ -207,7 +210,7 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
207 &EvalWriteState{ 210 &EvalWriteState{
208 Name: stateId, 211 Name: stateId,
209 ResourceType: rs.Type, 212 ResourceType: rs.Type,
210 Provider: rs.Provider, 213 Provider: n.ResolvedProvider,
211 Dependencies: rs.Dependencies, 214 Dependencies: rs.Dependencies,
212 State: &state, 215 State: &state,
213 }, 216 },
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_local.go b/vendor/github.com/hashicorp/terraform/terraform/node_local.go
new file mode 100644
index 0000000..d387222
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_local.go
@@ -0,0 +1,66 @@
1package terraform
2
3import (
4 "fmt"
5 "strings"
6
7 "github.com/hashicorp/terraform/config"
8)
9
10// NodeLocal represents a named local value in a particular module.
11//
12// Local value nodes only have one operation, common to all walk types:
13// evaluate the result and place it in state.
14type NodeLocal struct {
15 PathValue []string
16 Config *config.Local
17}
18
19func (n *NodeLocal) Name() string {
20 result := fmt.Sprintf("local.%s", n.Config.Name)
21 if len(n.PathValue) > 1 {
22 result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result)
23 }
24
25 return result
26}
27
28// GraphNodeSubPath
29func (n *NodeLocal) Path() []string {
30 return n.PathValue
31}
32
33// RemovableIfNotTargeted
34func (n *NodeLocal) RemoveIfNotTargeted() bool {
35 return true
36}
37
38// GraphNodeReferenceable
39func (n *NodeLocal) ReferenceableName() []string {
40 name := fmt.Sprintf("local.%s", n.Config.Name)
41 return []string{name}
42}
43
44// GraphNodeReferencer
45func (n *NodeLocal) References() []string {
46 var result []string
47 result = append(result, ReferencesFromConfig(n.Config.RawConfig)...)
48 for _, v := range result {
49 split := strings.Split(v, "/")
50 for i, s := range split {
51 split[i] = s + ".destroy"
52 }
53
54 result = append(result, strings.Join(split, "/"))
55 }
56
57 return result
58}
59
60// GraphNodeEvalable
61func (n *NodeLocal) EvalTree() EvalNode {
62 return &EvalLocal{
63 Name: n.Config.Name,
64 Value: n.Config.RawConfig,
65 }
66}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_module_destroy.go b/vendor/github.com/hashicorp/terraform/terraform/node_module_destroy.go
deleted file mode 100644
index 319df1e..0000000
--- a/vendor/github.com/hashicorp/terraform/terraform/node_module_destroy.go
+++ /dev/null
@@ -1,29 +0,0 @@
1package terraform
2
3import (
4 "fmt"
5)
6
7// NodeDestroyableModule represents a module destruction.
8type NodeDestroyableModuleVariable struct {
9 PathValue []string
10}
11
12func (n *NodeDestroyableModuleVariable) Name() string {
13 result := "plan-destroy"
14 if len(n.PathValue) > 1 {
15 result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result)
16 }
17
18 return result
19}
20
21// GraphNodeSubPath
22func (n *NodeDestroyableModuleVariable) Path() []string {
23 return n.PathValue
24}
25
26// GraphNodeEvalable
27func (n *NodeDestroyableModuleVariable) EvalTree() EvalNode {
28 return &EvalDiffDestroyModule{Path: n.PathValue}
29}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_module_removed.go b/vendor/github.com/hashicorp/terraform/terraform/node_module_removed.go
new file mode 100644
index 0000000..bb3e5ee
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_module_removed.go
@@ -0,0 +1,77 @@
1package terraform
2
3import (
4 "fmt"
5 "log"
6 "reflect"
7)
8
9// NodeModuleRemoved represents a module that is no longer in the
10// config.
11type NodeModuleRemoved struct {
12 PathValue []string
13}
14
15func (n *NodeModuleRemoved) Name() string {
16 return fmt.Sprintf("%s (removed)", modulePrefixStr(n.PathValue))
17}
18
19// GraphNodeSubPath
20func (n *NodeModuleRemoved) Path() []string {
21 return n.PathValue
22}
23
24// GraphNodeEvalable
25func (n *NodeModuleRemoved) EvalTree() EvalNode {
26 return &EvalOpFilter{
27 Ops: []walkOperation{walkRefresh, walkApply, walkDestroy},
28 Node: &EvalDeleteModule{
29 PathValue: n.PathValue,
30 },
31 }
32}
33
34func (n *NodeModuleRemoved) ReferenceGlobal() bool {
35 return true
36}
37
38func (n *NodeModuleRemoved) References() []string {
39 return []string{modulePrefixStr(n.PathValue)}
40}
41
42// EvalDeleteModule is an EvalNode implementation that removes an empty module
43// entry from the state.
44type EvalDeleteModule struct {
45 PathValue []string
46}
47
48func (n *EvalDeleteModule) Eval(ctx EvalContext) (interface{}, error) {
49 state, lock := ctx.State()
50 if state == nil {
51 return nil, nil
52 }
53
54 // Get a write lock so we can access this instance
55 lock.Lock()
56 defer lock.Unlock()
57
58 // Make sure we have a clean state
59 // Destroyed resources aren't deleted, they're written with an ID of "".
60 state.prune()
61
62 // find the module and delete it
63 for i, m := range state.Modules {
64 if reflect.DeepEqual(m.Path, n.PathValue) {
65 if !m.Empty() {
66 // a targeted apply may leave module resources even without a config,
67 // so just log this and return.
68 log.Printf("[DEBUG] cannot remove module %s, not empty", modulePrefixStr(n.PathValue))
69 break
70 }
71 state.Modules = append(state.Modules[:i], state.Modules[i+1:]...)
72 break
73 }
74 }
75
76 return nil, nil
77}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_module_variable.go b/vendor/github.com/hashicorp/terraform/terraform/node_module_variable.go
index 13fe8fc..66ff7d5 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_module_variable.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_module_variable.go
@@ -92,11 +92,24 @@ func (n *NodeApplyableModuleVariable) EvalTree() EvalNode {
92 // within the variables mapping. 92 // within the variables mapping.
93 var config *ResourceConfig 93 var config *ResourceConfig
94 variables := make(map[string]interface{}) 94 variables := make(map[string]interface{})
95
95 return &EvalSequence{ 96 return &EvalSequence{
96 Nodes: []EvalNode{ 97 Nodes: []EvalNode{
97 &EvalInterpolate{ 98 &EvalOpFilter{
98 Config: n.Value, 99 Ops: []walkOperation{walkInput},
99 Output: &config, 100 Node: &EvalInterpolate{
101 Config: n.Value,
102 Output: &config,
103 ContinueOnErr: true,
104 },
105 },
106 &EvalOpFilter{
107 Ops: []walkOperation{walkRefresh, walkPlan, walkApply,
108 walkDestroy, walkValidate},
109 Node: &EvalInterpolate{
110 Config: n.Value,
111 Output: &config,
112 },
100 }, 113 },
101 114
102 &EvalVariableBlock{ 115 &EvalVariableBlock{
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_output.go b/vendor/github.com/hashicorp/terraform/terraform/node_output.go
index 9017a63..83e9925 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_output.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_output.go
@@ -69,12 +69,22 @@ func (n *NodeApplyableOutput) References() []string {
69 69
70// GraphNodeEvalable 70// GraphNodeEvalable
71func (n *NodeApplyableOutput) EvalTree() EvalNode { 71func (n *NodeApplyableOutput) EvalTree() EvalNode {
72 return &EvalOpFilter{ 72 return &EvalSequence{
73 Ops: []walkOperation{walkRefresh, walkPlan, walkApply, 73 Nodes: []EvalNode{
74 walkDestroy, walkInput, walkValidate}, 74 &EvalOpFilter{
75 Node: &EvalSequence{ 75 // Don't let interpolation errors stop Input, since it happens
76 Nodes: []EvalNode{ 76 // before Refresh.
77 &EvalWriteOutput{ 77 Ops: []walkOperation{walkInput},
78 Node: &EvalWriteOutput{
79 Name: n.Config.Name,
80 Sensitive: n.Config.Sensitive,
81 Value: n.Config.RawConfig,
82 ContinueOnErr: true,
83 },
84 },
85 &EvalOpFilter{
86 Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkValidate, walkDestroy, walkPlanDestroy},
87 Node: &EvalWriteOutput{
78 Name: n.Config.Name, 88 Name: n.Config.Name,
79 Sensitive: n.Config.Sensitive, 89 Sensitive: n.Config.Sensitive,
80 Value: n.Config.RawConfig, 90 Value: n.Config.RawConfig,
@@ -83,3 +93,61 @@ func (n *NodeApplyableOutput) EvalTree() EvalNode {
83 }, 93 },
84 } 94 }
85} 95}
96
97// NodeDestroyableOutput represents an output that is "destroybale":
98// its application will remove the output from the state.
99type NodeDestroyableOutput struct {
100 PathValue []string
101 Config *config.Output // Config is the output in the config
102}
103
104func (n *NodeDestroyableOutput) Name() string {
105 result := fmt.Sprintf("output.%s (destroy)", n.Config.Name)
106 if len(n.PathValue) > 1 {
107 result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result)
108 }
109
110 return result
111}
112
113// GraphNodeSubPath
114func (n *NodeDestroyableOutput) Path() []string {
115 return n.PathValue
116}
117
118// RemovableIfNotTargeted
119func (n *NodeDestroyableOutput) RemoveIfNotTargeted() bool {
120 // We need to add this so that this node will be removed if
121 // it isn't targeted or a dependency of a target.
122 return true
123}
124
125// This will keep the destroy node in the graph if its corresponding output
126// node is also in the destroy graph.
127func (n *NodeDestroyableOutput) TargetDownstream(targetedDeps, untargetedDeps *dag.Set) bool {
128 return true
129}
130
131// GraphNodeReferencer
132func (n *NodeDestroyableOutput) References() []string {
133 var result []string
134 result = append(result, n.Config.DependsOn...)
135 result = append(result, ReferencesFromConfig(n.Config.RawConfig)...)
136 for _, v := range result {
137 split := strings.Split(v, "/")
138 for i, s := range split {
139 split[i] = s + ".destroy"
140 }
141
142 result = append(result, strings.Join(split, "/"))
143 }
144
145 return result
146}
147
148// GraphNodeEvalable
149func (n *NodeDestroyableOutput) EvalTree() EvalNode {
150 return &EvalDeleteOutput{
151 Name: n.Config.Name,
152 }
153}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_output_orphan.go b/vendor/github.com/hashicorp/terraform/terraform/node_output_orphan.go
index 636a15d..0fd1554 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_output_orphan.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_output_orphan.go
@@ -19,6 +19,11 @@ func (n *NodeOutputOrphan) Name() string {
19 return result 19 return result
20} 20}
21 21
22// GraphNodeReferenceable
23func (n *NodeOutputOrphan) ReferenceableName() []string {
24 return []string{"output." + n.OutputName}
25}
26
22// GraphNodeSubPath 27// GraphNodeSubPath
23func (n *NodeOutputOrphan) Path() []string { 28func (n *NodeOutputOrphan) Path() []string {
24 return n.PathValue 29 return n.PathValue
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_provider.go b/vendor/github.com/hashicorp/terraform/terraform/node_provider.go
index 8e2c176..2071ab1 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_provider.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_provider.go
@@ -7,5 +7,5 @@ type NodeApplyableProvider struct {
7 7
8// GraphNodeEvalable 8// GraphNodeEvalable
9func (n *NodeApplyableProvider) EvalTree() EvalNode { 9func (n *NodeApplyableProvider) EvalTree() EvalNode {
10 return ProviderEvalTree(n.NameValue, n.ProviderConfig()) 10 return ProviderEvalTree(n, n.ProviderConfig())
11} 11}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_provider_abstract.go b/vendor/github.com/hashicorp/terraform/terraform/node_provider_abstract.go
index 6cc8365..9e490f7 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_provider_abstract.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_provider_abstract.go
@@ -2,6 +2,7 @@ package terraform
2 2
3import ( 3import (
4 "fmt" 4 "fmt"
5 "strings"
5 6
6 "github.com/hashicorp/terraform/config" 7 "github.com/hashicorp/terraform/config"
7 "github.com/hashicorp/terraform/dag" 8 "github.com/hashicorp/terraform/dag"
@@ -24,13 +25,22 @@ type NodeAbstractProvider struct {
24 Config *config.ProviderConfig 25 Config *config.ProviderConfig
25} 26}
26 27
27func (n *NodeAbstractProvider) Name() string { 28func ResolveProviderName(name string, path []string) string {
28 result := fmt.Sprintf("provider.%s", n.NameValue) 29 if strings.Contains(name, "provider.") {
29 if len(n.PathValue) > 1 { 30 // already resolved
30 result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result) 31 return name
32 }
33
34 name = fmt.Sprintf("provider.%s", name)
35 if len(path) >= 1 {
36 name = fmt.Sprintf("%s.%s", modulePrefixStr(path), name)
31 } 37 }
32 38
33 return result 39 return name
40}
41
42func (n *NodeAbstractProvider) Name() string {
43 return ResolveProviderName(n.NameValue, n.PathValue)
34} 44}
35 45
36// GraphNodeSubPath 46// GraphNodeSubPath
@@ -60,12 +70,12 @@ func (n *NodeAbstractProvider) ProviderName() string {
60} 70}
61 71
62// GraphNodeProvider 72// GraphNodeProvider
63func (n *NodeAbstractProvider) ProviderConfig() *config.RawConfig { 73func (n *NodeAbstractProvider) ProviderConfig() *config.ProviderConfig {
64 if n.Config == nil { 74 if n.Config == nil {
65 return nil 75 return nil
66 } 76 }
67 77
68 return n.Config.RawConfig 78 return n.Config
69} 79}
70 80
71// GraphNodeAttachProvider 81// GraphNodeAttachProvider
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_provider_disabled.go b/vendor/github.com/hashicorp/terraform/terraform/node_provider_disabled.go
index 25e7e62..a00bc46 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_provider_disabled.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_provider_disabled.go
@@ -20,7 +20,7 @@ func (n *NodeDisabledProvider) EvalTree() EvalNode {
20 var resourceConfig *ResourceConfig 20 var resourceConfig *ResourceConfig
21 return &EvalSequence{ 21 return &EvalSequence{
22 Nodes: []EvalNode{ 22 Nodes: []EvalNode{
23 &EvalInterpolate{ 23 &EvalInterpolateProvider{
24 Config: n.ProviderConfig(), 24 Config: n.ProviderConfig(),
25 Output: &resourceConfig, 25 Output: &resourceConfig,
26 }, 26 },
@@ -29,10 +29,6 @@ func (n *NodeDisabledProvider) EvalTree() EvalNode {
29 Config: &resourceConfig, 29 Config: &resourceConfig,
30 Output: &resourceConfig, 30 Output: &resourceConfig,
31 }, 31 },
32 &EvalSetProviderConfig{
33 Provider: n.ProviderName(),
34 Config: &resourceConfig,
35 },
36 }, 32 },
37 } 33 }
38} 34}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go
index 50bb707..73509c8 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go
@@ -33,6 +33,9 @@ type NodeAbstractResource struct {
33 ResourceState *ResourceState // ResourceState is the ResourceState for this 33 ResourceState *ResourceState // ResourceState is the ResourceState for this
34 34
35 Targets []ResourceAddress // Set from GraphNodeTargetable 35 Targets []ResourceAddress // Set from GraphNodeTargetable
36
37 // The address of the provider this resource will use
38 ResolvedProvider string
36} 39}
37 40
38func (n *NodeAbstractResource) Name() string { 41func (n *NodeAbstractResource) Name() string {
@@ -170,20 +173,24 @@ func (n *NodeAbstractResource) StateReferences() []string {
170 return deps 173 return deps
171} 174}
172 175
176func (n *NodeAbstractResource) SetProvider(p string) {
177 n.ResolvedProvider = p
178}
179
173// GraphNodeProviderConsumer 180// GraphNodeProviderConsumer
174func (n *NodeAbstractResource) ProvidedBy() []string { 181func (n *NodeAbstractResource) ProvidedBy() string {
175 // If we have a config we prefer that above all else 182 // If we have a config we prefer that above all else
176 if n.Config != nil { 183 if n.Config != nil {
177 return []string{resourceProvider(n.Config.Type, n.Config.Provider)} 184 return resourceProvider(n.Config.Type, n.Config.Provider)
178 } 185 }
179 186
180 // If we have state, then we will use the provider from there 187 // If we have state, then we will use the provider from there
181 if n.ResourceState != nil && n.ResourceState.Provider != "" { 188 if n.ResourceState != nil && n.ResourceState.Provider != "" {
182 return []string{n.ResourceState.Provider} 189 return n.ResourceState.Provider
183 } 190 }
184 191
185 // Use our type 192 // Use our type
186 return []string{resourceProvider(n.Addr.Type, "")} 193 return resourceProvider(n.Addr.Type, "")
187} 194}
188 195
189// GraphNodeProvisionerConsumer 196// GraphNodeProvisionerConsumer
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go
index 3599782..40ee1cf 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go
@@ -124,6 +124,27 @@ func (n *NodeApplyableResource) evalTreeDataResource(
124 Then: EvalNoop{}, 124 Then: EvalNoop{},
125 }, 125 },
126 126
127 // Normally we interpolate count as a preparation step before
128 // a DynamicExpand, but an apply graph has pre-expanded nodes
129 // and so the count would otherwise never be interpolated.
130 //
131 // This is redundant when there are multiple instances created
132 // from the same config (count > 1) but harmless since the
133 // underlying structures have mutexes to make this concurrency-safe.
134 //
135 // In most cases this isn't actually needed because we dealt with
136 // all of the counts during the plan walk, but we do it here
137 // for completeness because other code assumes that the
138 // final count is always available during interpolation.
139 //
140 // Here we are just populating the interpolated value in-place
141 // inside this RawConfig object, like we would in
142 // NodeAbstractCountResource.
143 &EvalInterpolate{
144 Config: n.Config.RawCount,
145 ContinueOnErr: true,
146 },
147
127 // We need to re-interpolate the config here, rather than 148 // We need to re-interpolate the config here, rather than
128 // just using the diff's values directly, because we've 149 // just using the diff's values directly, because we've
129 // potentially learned more variable values during the 150 // potentially learned more variable values during the
@@ -135,7 +156,7 @@ func (n *NodeApplyableResource) evalTreeDataResource(
135 }, 156 },
136 157
137 &EvalGetProvider{ 158 &EvalGetProvider{
138 Name: n.ProvidedBy()[0], 159 Name: n.ResolvedProvider,
139 Output: &provider, 160 Output: &provider,
140 }, 161 },
141 162
@@ -158,7 +179,7 @@ func (n *NodeApplyableResource) evalTreeDataResource(
158 &EvalWriteState{ 179 &EvalWriteState{
159 Name: stateId, 180 Name: stateId,
160 ResourceType: n.Config.Type, 181 ResourceType: n.Config.Type,
161 Provider: n.Config.Provider, 182 Provider: n.ResolvedProvider,
162 Dependencies: stateDeps, 183 Dependencies: stateDeps,
163 State: &state, 184 State: &state,
164 }, 185 },
@@ -236,13 +257,35 @@ func (n *NodeApplyableResource) evalTreeManagedResource(
236 }, 257 },
237 }, 258 },
238 259
260 // Normally we interpolate count as a preparation step before
261 // a DynamicExpand, but an apply graph has pre-expanded nodes
262 // and so the count would otherwise never be interpolated.
263 //
264 // This is redundant when there are multiple instances created
265 // from the same config (count > 1) but harmless since the
266 // underlying structures have mutexes to make this concurrency-safe.
267 //
268 // In most cases this isn't actually needed because we dealt with
269 // all of the counts during the plan walk, but we need to do this
270 // in order to support interpolation of resource counts from
271 // apply-time-interpolated expressions, such as those in
272 // "provisioner" blocks.
273 //
274 // Here we are just populating the interpolated value in-place
275 // inside this RawConfig object, like we would in
276 // NodeAbstractCountResource.
277 &EvalInterpolate{
278 Config: n.Config.RawCount,
279 ContinueOnErr: true,
280 },
281
239 &EvalInterpolate{ 282 &EvalInterpolate{
240 Config: n.Config.RawConfig.Copy(), 283 Config: n.Config.RawConfig.Copy(),
241 Resource: resource, 284 Resource: resource,
242 Output: &resourceConfig, 285 Output: &resourceConfig,
243 }, 286 },
244 &EvalGetProvider{ 287 &EvalGetProvider{
245 Name: n.ProvidedBy()[0], 288 Name: n.ResolvedProvider,
246 Output: &provider, 289 Output: &provider,
247 }, 290 },
248 &EvalReadState{ 291 &EvalReadState{
@@ -283,7 +326,7 @@ func (n *NodeApplyableResource) evalTreeManagedResource(
283 }, 326 },
284 327
285 &EvalGetProvider{ 328 &EvalGetProvider{
286 Name: n.ProvidedBy()[0], 329 Name: n.ResolvedProvider,
287 Output: &provider, 330 Output: &provider,
288 }, 331 },
289 &EvalReadState{ 332 &EvalReadState{
@@ -308,7 +351,7 @@ func (n *NodeApplyableResource) evalTreeManagedResource(
308 &EvalWriteState{ 351 &EvalWriteState{
309 Name: stateId, 352 Name: stateId,
310 ResourceType: n.Config.Type, 353 ResourceType: n.Config.Type,
311 Provider: n.Config.Provider, 354 Provider: n.ResolvedProvider,
312 Dependencies: stateDeps, 355 Dependencies: stateDeps,
313 State: &state, 356 State: &state,
314 }, 357 },
@@ -332,7 +375,7 @@ func (n *NodeApplyableResource) evalTreeManagedResource(
332 Else: &EvalWriteState{ 375 Else: &EvalWriteState{
333 Name: stateId, 376 Name: stateId,
334 ResourceType: n.Config.Type, 377 ResourceType: n.Config.Type,
335 Provider: n.Config.Provider, 378 Provider: n.ResolvedProvider,
336 Dependencies: stateDeps, 379 Dependencies: stateDeps,
337 State: &state, 380 State: &state,
338 }, 381 },
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy.go
index c2efd2c..657bbee 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy.go
@@ -102,8 +102,9 @@ func (n *NodeDestroyResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
102 102
103 // We want deposed resources in the state to be destroyed 103 // We want deposed resources in the state to be destroyed
104 steps = append(steps, &DeposedTransformer{ 104 steps = append(steps, &DeposedTransformer{
105 State: state, 105 State: state,
106 View: n.Addr.stateId(), 106 View: n.Addr.stateId(),
107 ResolvedProvider: n.ResolvedProvider,
107 }) 108 })
108 109
109 // Target 110 // Target
@@ -148,7 +149,9 @@ func (n *NodeDestroyResource) EvalTree() EvalNode {
148 // Get our state 149 // Get our state
149 rs := n.ResourceState 150 rs := n.ResourceState
150 if rs == nil { 151 if rs == nil {
151 rs = &ResourceState{} 152 rs = &ResourceState{
153 Provider: n.ResolvedProvider,
154 }
152 } 155 }
153 156
154 var diffApply *InstanceDiff 157 var diffApply *InstanceDiff
@@ -188,7 +191,7 @@ func (n *NodeDestroyResource) EvalTree() EvalNode {
188 &EvalInstanceInfo{Info: info}, 191 &EvalInstanceInfo{Info: info},
189 192
190 &EvalGetProvider{ 193 &EvalGetProvider{
191 Name: n.ProvidedBy()[0], 194 Name: n.ResolvedProvider,
192 Output: &provider, 195 Output: &provider,
193 }, 196 },
194 &EvalReadState{ 197 &EvalReadState{
@@ -272,7 +275,7 @@ func (n *NodeDestroyResource) EvalTree() EvalNode {
272 &EvalWriteState{ 275 &EvalWriteState{
273 Name: stateId, 276 Name: stateId,
274 ResourceType: n.Addr.Type, 277 ResourceType: n.Addr.Type,
275 Provider: rs.Provider, 278 Provider: n.ResolvedProvider,
276 Dependencies: rs.Dependencies, 279 Dependencies: rs.Dependencies,
277 State: &state, 280 State: &state,
278 }, 281 },
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan.go
index 52bbf88..1afae7a 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan.go
@@ -27,6 +27,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
27 concreteResource := func(a *NodeAbstractResource) dag.Vertex { 27 concreteResource := func(a *NodeAbstractResource) dag.Vertex {
28 // Add the config and state since we don't do that via transforms 28 // Add the config and state since we don't do that via transforms
29 a.Config = n.Config 29 a.Config = n.Config
30 a.ResolvedProvider = n.ResolvedProvider
30 31
31 return &NodePlannableResourceInstance{ 32 return &NodePlannableResourceInstance{
32 NodeAbstractResource: a, 33 NodeAbstractResource: a,
@@ -37,6 +38,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
37 concreteResourceOrphan := func(a *NodeAbstractResource) dag.Vertex { 38 concreteResourceOrphan := func(a *NodeAbstractResource) dag.Vertex {
38 // Add the config and state since we don't do that via transforms 39 // Add the config and state since we don't do that via transforms
39 a.Config = n.Config 40 a.Config = n.Config
41 a.ResolvedProvider = n.ResolvedProvider
40 42
41 return &NodePlannableResourceOrphan{ 43 return &NodePlannableResourceOrphan{
42 NodeAbstractResource: a, 44 NodeAbstractResource: a,
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go
index b529569..7d9fcdd 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go
@@ -97,7 +97,7 @@ func (n *NodePlannableResourceInstance) evalTreeDataResource(
97 }, 97 },
98 98
99 &EvalGetProvider{ 99 &EvalGetProvider{
100 Name: n.ProvidedBy()[0], 100 Name: n.ResolvedProvider,
101 Output: &provider, 101 Output: &provider,
102 }, 102 },
103 103
@@ -112,7 +112,7 @@ func (n *NodePlannableResourceInstance) evalTreeDataResource(
112 &EvalWriteState{ 112 &EvalWriteState{
113 Name: stateId, 113 Name: stateId,
114 ResourceType: n.Config.Type, 114 ResourceType: n.Config.Type,
115 Provider: n.Config.Provider, 115 Provider: n.ResolvedProvider,
116 Dependencies: stateDeps, 116 Dependencies: stateDeps,
117 State: &state, 117 State: &state,
118 }, 118 },
@@ -143,7 +143,7 @@ func (n *NodePlannableResourceInstance) evalTreeManagedResource(
143 Output: &resourceConfig, 143 Output: &resourceConfig,
144 }, 144 },
145 &EvalGetProvider{ 145 &EvalGetProvider{
146 Name: n.ProvidedBy()[0], 146 Name: n.ResolvedProvider,
147 Output: &provider, 147 Output: &provider,
148 }, 148 },
149 // Re-run validation to catch any errors we missed, e.g. type 149 // Re-run validation to catch any errors we missed, e.g. type
@@ -177,7 +177,7 @@ func (n *NodePlannableResourceInstance) evalTreeManagedResource(
177 &EvalWriteState{ 177 &EvalWriteState{
178 Name: stateId, 178 Name: stateId,
179 ResourceType: n.Config.Type, 179 ResourceType: n.Config.Type,
180 Provider: n.Config.Provider, 180 Provider: n.ResolvedProvider,
181 Dependencies: stateDeps, 181 Dependencies: stateDeps,
182 State: &state, 182 State: &state,
183 }, 183 },
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 cd4fe92..697bd49 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_refresh.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_refresh.go
@@ -30,6 +30,7 @@ func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph,
30 concreteResource := func(a *NodeAbstractResource) dag.Vertex { 30 concreteResource := func(a *NodeAbstractResource) dag.Vertex {
31 // Add the config and state since we don't do that via transforms 31 // Add the config and state since we don't do that via transforms
32 a.Config = n.Config 32 a.Config = n.Config
33 a.ResolvedProvider = n.ResolvedProvider
33 34
34 return &NodeRefreshableManagedResourceInstance{ 35 return &NodeRefreshableManagedResourceInstance{
35 NodeAbstractResource: a, 36 NodeAbstractResource: a,
@@ -149,7 +150,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalN
149 return &EvalSequence{ 150 return &EvalSequence{
150 Nodes: []EvalNode{ 151 Nodes: []EvalNode{
151 &EvalGetProvider{ 152 &EvalGetProvider{
152 Name: n.ProvidedBy()[0], 153 Name: n.ResolvedProvider,
153 Output: &provider, 154 Output: &provider,
154 }, 155 },
155 &EvalReadState{ 156 &EvalReadState{
@@ -165,7 +166,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalN
165 &EvalWriteState{ 166 &EvalWriteState{
166 Name: stateId, 167 Name: stateId,
167 ResourceType: n.ResourceState.Type, 168 ResourceType: n.ResourceState.Type,
168 Provider: n.ResourceState.Provider, 169 Provider: n.ResolvedProvider,
169 Dependencies: n.ResourceState.Dependencies, 170 Dependencies: n.ResourceState.Dependencies,
170 State: &state, 171 State: &state,
171 }, 172 },
@@ -212,15 +213,21 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState(
212 // Determine the dependencies for the state. 213 // Determine the dependencies for the state.
213 stateDeps := n.StateReferences() 214 stateDeps := n.StateReferences()
214 215
216 // n.Config can be nil if the config and state don't match
217 var raw *config.RawConfig
218 if n.Config != nil {
219 raw = n.Config.RawConfig.Copy()
220 }
221
215 return &EvalSequence{ 222 return &EvalSequence{
216 Nodes: []EvalNode{ 223 Nodes: []EvalNode{
217 &EvalInterpolate{ 224 &EvalInterpolate{
218 Config: n.Config.RawConfig.Copy(), 225 Config: raw,
219 Resource: resource, 226 Resource: resource,
220 Output: &resourceConfig, 227 Output: &resourceConfig,
221 }, 228 },
222 &EvalGetProvider{ 229 &EvalGetProvider{
223 Name: n.ProvidedBy()[0], 230 Name: n.ResolvedProvider,
224 Output: &provider, 231 Output: &provider,
225 }, 232 },
226 // Re-run validation to catch any errors we missed, e.g. type 233 // Re-run validation to catch any errors we missed, e.g. type
@@ -250,7 +257,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState(
250 &EvalWriteState{ 257 &EvalWriteState{
251 Name: stateID, 258 Name: stateID,
252 ResourceType: n.Config.Type, 259 ResourceType: n.Config.Type,
253 Provider: n.Config.Provider, 260 Provider: n.ResolvedProvider,
254 Dependencies: stateDeps, 261 Dependencies: stateDeps,
255 State: &state, 262 State: &state,
256 }, 263 },
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate.go
index f528f24..0df223d 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate.go
@@ -39,6 +39,7 @@ func (n *NodeValidatableResource) DynamicExpand(ctx EvalContext) (*Graph, error)
39 concreteResource := func(a *NodeAbstractResource) dag.Vertex { 39 concreteResource := func(a *NodeAbstractResource) dag.Vertex {
40 // Add the config and state since we don't do that via transforms 40 // Add the config and state since we don't do that via transforms
41 a.Config = n.Config 41 a.Config = n.Config
42 a.ResolvedProvider = n.ResolvedProvider
42 43
43 return &NodeValidatableResourceInstance{ 44 return &NodeValidatableResourceInstance{
44 NodeAbstractResource: a, 45 NodeAbstractResource: a,
@@ -108,7 +109,7 @@ func (n *NodeValidatableResourceInstance) EvalTree() EvalNode {
108 Config: &n.Config.RawConfig, 109 Config: &n.Config.RawConfig,
109 }, 110 },
110 &EvalGetProvider{ 111 &EvalGetProvider{
111 Name: n.ProvidedBy()[0], 112 Name: n.ResolvedProvider,
112 Output: &provider, 113 Output: &provider,
113 }, 114 },
114 &EvalInterpolate{ 115 &EvalInterpolate{
diff --git a/vendor/github.com/hashicorp/terraform/terraform/path.go b/vendor/github.com/hashicorp/terraform/terraform/path.go
index ca99685..51dd412 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/path.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/path.go
@@ -1,24 +1,10 @@
1package terraform 1package terraform
2 2
3import ( 3import (
4 "crypto/md5" 4 "strings"
5 "encoding/hex"
6) 5)
7 6
8// PathCacheKey returns a cache key for a module path. 7// PathCacheKey returns a cache key for a module path.
9//
10// TODO: test
11func PathCacheKey(path []string) string { 8func PathCacheKey(path []string) string {
12 // There is probably a better way to do this, but this is working for now. 9 return strings.Join(path, "|")
13 // We just create an MD5 hash of all the MD5 hashes of all the path
14 // elements. This gets us the property that it is unique per ordering.
15 hash := md5.New()
16 for _, p := range path {
17 single := md5.Sum([]byte(p))
18 if _, err := hash.Write(single[:]); err != nil {
19 panic(err)
20 }
21 }
22
23 return hex.EncodeToString(hash.Sum(nil))
24} 10}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/plan.go b/vendor/github.com/hashicorp/terraform/terraform/plan.go
index 51d6652..30db195 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/plan.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/plan.go
@@ -10,6 +10,7 @@ import (
10 "sync" 10 "sync"
11 11
12 "github.com/hashicorp/terraform/config/module" 12 "github.com/hashicorp/terraform/config/module"
13 "github.com/hashicorp/terraform/version"
13) 14)
14 15
15func init() { 16func init() {
@@ -26,18 +27,54 @@ func init() {
26// necessary to make a change: the state, diff, config, backend config, etc. 27// necessary to make a change: the state, diff, config, backend config, etc.
27// This is so that it can run alone without any other data. 28// This is so that it can run alone without any other data.
28type Plan struct { 29type Plan struct {
29 Diff *Diff 30 // Diff describes the resource actions that must be taken when this
30 Module *module.Tree 31 // plan is applied.
31 State *State 32 Diff *Diff
32 Vars map[string]interface{} 33
34 // Module represents the entire configuration that was present when this
35 // plan was created.
36 Module *module.Tree
37
38 // State is the Terraform state that was current when this plan was
39 // created.
40 //
41 // It is not allowed to apply a plan that has a stale state, since its
42 // diff could be outdated.
43 State *State
44
45 // Vars retains the variables that were set when creating the plan, so
46 // that the same variables can be applied during apply.
47 Vars map[string]interface{}
48
49 // Targets, if non-empty, contains a set of resource address strings that
50 // identify graph nodes that were selected as targets for plan.
51 //
52 // When targets are set, any graph node that is not directly targeted or
53 // indirectly targeted via dependencies is excluded from the graph.
33 Targets []string 54 Targets []string
34 55
56 // TerraformVersion is the version of Terraform that was used to create
57 // this plan.
58 //
59 // It is not allowed to apply a plan created with a different version of
60 // Terraform, since the other fields of this structure may be interpreted
61 // in different ways between versions.
35 TerraformVersion string 62 TerraformVersion string
36 ProviderSHA256s map[string][]byte 63
64 // ProviderSHA256s is a map giving the SHA256 hashes of the exact binaries
65 // used as plugins for each provider during plan.
66 //
67 // These must match between plan and apply to ensure that the diff is
68 // correctly interpreted, since different provider versions may have
69 // different attributes or attribute value constraints.
70 ProviderSHA256s map[string][]byte
37 71
38 // Backend is the backend that this plan should use and store data with. 72 // Backend is the backend that this plan should use and store data with.
39 Backend *BackendState 73 Backend *BackendState
40 74
75 // Destroy indicates that this plan was created for a full destroy operation
76 Destroy bool
77
41 once sync.Once 78 once sync.Once
42} 79}
43 80
@@ -67,6 +104,7 @@ func (p *Plan) contextOpts(base *ContextOpts) (*ContextOpts, error) {
67 opts.Module = p.Module 104 opts.Module = p.Module
68 opts.Targets = p.Targets 105 opts.Targets = p.Targets
69 opts.ProviderSHA256s = p.ProviderSHA256s 106 opts.ProviderSHA256s = p.ProviderSHA256s
107 opts.Destroy = p.Destroy
70 108
71 if opts.State == nil { 109 if opts.State == nil {
72 opts.State = p.State 110 opts.State = p.State
@@ -79,10 +117,10 @@ func (p *Plan) contextOpts(base *ContextOpts) (*ContextOpts, error) {
79 // the state, there is little chance that these aren't actually equal. 117 // the state, there is little chance that these aren't actually equal.
80 // Log the error condition for reference, but continue with the state 118 // Log the error condition for reference, but continue with the state
81 // we have. 119 // we have.
82 log.Println("[WARNING] Plan state and ContextOpts state are not equal") 120 log.Println("[WARN] Plan state and ContextOpts state are not equal")
83 } 121 }
84 122
85 thisVersion := VersionString() 123 thisVersion := version.String()
86 if p.TerraformVersion != "" && p.TerraformVersion != thisVersion { 124 if p.TerraformVersion != "" && p.TerraformVersion != thisVersion {
87 return nil, fmt.Errorf( 125 return nil, fmt.Errorf(
88 "plan was created with a different version of Terraform (created with %s, but running %s)", 126 "plan was created with a different version of Terraform (created with %s, but running %s)",
diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource.go b/vendor/github.com/hashicorp/terraform/terraform/resource.go
index 0acf0be..2f5ebb5 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/resource.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/resource.go
@@ -88,6 +88,46 @@ func (i *InstanceInfo) HumanId() string {
88 i.Id) 88 i.Id)
89} 89}
90 90
91// ResourceAddress returns the address of the resource that the receiver is describing.
92func (i *InstanceInfo) ResourceAddress() *ResourceAddress {
93 // GROSS: for tainted and deposed instances, their status gets appended
94 // to i.Id to create a unique id for the graph node. Historically these
95 // ids were displayed to the user, so it's designed to be human-readable:
96 // "aws_instance.bar.0 (deposed #0)"
97 //
98 // So here we detect such suffixes and try to interpret them back to
99 // their original meaning so we can then produce a ResourceAddress
100 // with a suitable InstanceType.
101 id := i.Id
102 instanceType := TypeInvalid
103 if idx := strings.Index(id, " ("); idx != -1 {
104 remain := id[idx:]
105 id = id[:idx]
106
107 switch {
108 case strings.Contains(remain, "tainted"):
109 instanceType = TypeTainted
110 case strings.Contains(remain, "deposed"):
111 instanceType = TypeDeposed
112 }
113 }
114
115 addr, err := parseResourceAddressInternal(id)
116 if err != nil {
117 // should never happen, since that would indicate a bug in the
118 // code that constructed this InstanceInfo.
119 panic(fmt.Errorf("InstanceInfo has invalid Id %s", id))
120 }
121 if len(i.ModulePath) > 1 {
122 addr.Path = i.ModulePath[1:] // trim off "root" prefix, which is implied
123 }
124 if instanceType != TypeInvalid {
125 addr.InstanceTypeSet = true
126 addr.InstanceType = instanceType
127 }
128 return addr
129}
130
91func (i *InstanceInfo) uniqueId() string { 131func (i *InstanceInfo) uniqueId() string {
92 prefix := i.HumanId() 132 prefix := i.HumanId()
93 if v := i.uniqueExtra; v != "" { 133 if v := i.uniqueExtra; v != "" {
@@ -306,7 +346,7 @@ func (c *ResourceConfig) get(
306 if err != nil { 346 if err != nil {
307 return nil, false 347 return nil, false
308 } 348 }
309 if i >= int64(cv.Len()) { 349 if int(i) < 0 || int(i) >= cv.Len() {
310 return nil, false 350 return nil, false
311 } 351 }
312 current = cv.Index(int(i)).Interface() 352 current = cv.Index(int(i)).Interface()
diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_address.go b/vendor/github.com/hashicorp/terraform/terraform/resource_address.go
index 8badca8..a64f5d8 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/resource_address.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/resource_address.go
@@ -42,9 +42,9 @@ func (r *ResourceAddress) Copy() *ResourceAddress {
42 Type: r.Type, 42 Type: r.Type,
43 Mode: r.Mode, 43 Mode: r.Mode,
44 } 44 }
45 for _, p := range r.Path { 45
46 n.Path = append(n.Path, p) 46 n.Path = append(n.Path, r.Path...)
47 } 47
48 return n 48 return n
49} 49}
50 50
@@ -362,40 +362,41 @@ func (addr *ResourceAddress) Less(other *ResourceAddress) bool {
362 362
363 switch { 363 switch {
364 364
365 case len(addr.Path) < len(other.Path): 365 case len(addr.Path) != len(other.Path):
366 return true 366 return len(addr.Path) < len(other.Path)
367 367
368 case !reflect.DeepEqual(addr.Path, other.Path): 368 case !reflect.DeepEqual(addr.Path, other.Path):
369 // If the two paths are the same length but don't match, we'll just 369 // If the two paths are the same length but don't match, we'll just
370 // cheat and compare the string forms since it's easier than 370 // cheat and compare the string forms since it's easier than
371 // comparing all of the path segments in turn. 371 // comparing all of the path segments in turn, and lexicographic
372 // comparison is correct for the module path portion.
372 addrStr := addr.String() 373 addrStr := addr.String()
373 otherStr := other.String() 374 otherStr := other.String()
374 return addrStr < otherStr 375 return addrStr < otherStr
375 376
376 case addr.Mode == config.DataResourceMode && other.Mode != config.DataResourceMode: 377 case addr.Mode != other.Mode:
377 return true 378 return addr.Mode == config.DataResourceMode
378 379
379 case addr.Type < other.Type: 380 case addr.Type != other.Type:
380 return true 381 return addr.Type < other.Type
381 382
382 case addr.Name < other.Name: 383 case addr.Name != other.Name:
383 return true 384 return addr.Name < other.Name
384 385
385 case addr.Index < other.Index: 386 case addr.Index != other.Index:
386 // Since "Index" is -1 for an un-indexed address, this also conveniently 387 // Since "Index" is -1 for an un-indexed address, this also conveniently
387 // sorts unindexed addresses before indexed ones, should they both 388 // sorts unindexed addresses before indexed ones, should they both
388 // appear for some reason. 389 // appear for some reason.
389 return true 390 return addr.Index < other.Index
390 391
391 case other.InstanceTypeSet && !addr.InstanceTypeSet: 392 case addr.InstanceTypeSet != other.InstanceTypeSet:
392 return true 393 return !addr.InstanceTypeSet
393 394
394 case addr.InstanceType < other.InstanceType: 395 case addr.InstanceType != other.InstanceType:
395 // InstanceType is actually an enum, so this is just an arbitrary 396 // InstanceType is actually an enum, so this is just an arbitrary
396 // sort based on the enum numeric values, and thus not particularly 397 // sort based on the enum numeric values, and thus not particularly
397 // meaningful. 398 // meaningful.
398 return true 399 return addr.InstanceType < other.InstanceType
399 400
400 default: 401 default:
401 return false 402 return false
diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_provider.go b/vendor/github.com/hashicorp/terraform/terraform/resource_provider.go
index 7d78f67..93fd14f 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/resource_provider.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/resource_provider.go
@@ -21,6 +21,15 @@ type ResourceProvider interface {
21 * Functions related to the provider 21 * Functions related to the provider
22 *********************************************************************/ 22 *********************************************************************/
23 23
24 // ProviderSchema returns the config schema for the main provider
25 // configuration, as would appear in a "provider" block in the
26 // configuration files.
27 //
28 // Currently not all providers support schema. Callers must therefore
29 // first call Resources and DataSources and ensure that at least one
30 // resource or data source has the SchemaAvailable flag set.
31 GetSchema(*ProviderSchemaRequest) (*ProviderSchema, error)
32
24 // Input is called to ask the provider to ask the user for input 33 // Input is called to ask the provider to ask the user for input
25 // for completing the configuration if necesarry. 34 // for completing the configuration if necesarry.
26 // 35 //
@@ -183,11 +192,25 @@ type ResourceProviderCloser interface {
183type ResourceType struct { 192type ResourceType struct {
184 Name string // Name of the resource, example "instance" (no provider prefix) 193 Name string // Name of the resource, example "instance" (no provider prefix)
185 Importable bool // Whether this resource supports importing 194 Importable bool // Whether this resource supports importing
195
196 // SchemaAvailable is set if the provider supports the ProviderSchema,
197 // ResourceTypeSchema and DataSourceSchema methods. Although it is
198 // included on each resource type, it's actually a provider-wide setting
199 // that's smuggled here only because that avoids a breaking change to
200 // the plugin protocol.
201 SchemaAvailable bool
186} 202}
187 203
188// DataSource is a data source that a resource provider implements. 204// DataSource is a data source that a resource provider implements.
189type DataSource struct { 205type DataSource struct {
190 Name string 206 Name string
207
208 // SchemaAvailable is set if the provider supports the ProviderSchema,
209 // ResourceTypeSchema and DataSourceSchema methods. Although it is
210 // included on each resource type, it's actually a provider-wide setting
211 // that's smuggled here only because that avoids a breaking change to
212 // the plugin protocol.
213 SchemaAvailable bool
191} 214}
192 215
193// ResourceProviderResolver is an interface implemented by objects that are 216// ResourceProviderResolver is an interface implemented by objects that are
diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock.go b/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock.go
index f531533..4000e3d 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock.go
@@ -1,6 +1,8 @@
1package terraform 1package terraform
2 2
3import "sync" 3import (
4 "sync"
5)
4 6
5// MockResourceProvider implements ResourceProvider but mocks out all the 7// MockResourceProvider implements ResourceProvider but mocks out all the
6// calls for testing purposes. 8// calls for testing purposes.
@@ -12,6 +14,10 @@ type MockResourceProvider struct {
12 14
13 CloseCalled bool 15 CloseCalled bool
14 CloseError error 16 CloseError error
17 GetSchemaCalled bool
18 GetSchemaRequest *ProviderSchemaRequest
19 GetSchemaReturn *ProviderSchema
20 GetSchemaReturnError error
15 InputCalled bool 21 InputCalled bool
16 InputInput UIInput 22 InputInput UIInput
17 InputConfig *ResourceConfig 23 InputConfig *ResourceConfig
@@ -92,8 +98,19 @@ func (p *MockResourceProvider) Close() error {
92 return p.CloseError 98 return p.CloseError
93} 99}
94 100
101func (p *MockResourceProvider) GetSchema(req *ProviderSchemaRequest) (*ProviderSchema, error) {
102 p.Lock()
103 defer p.Unlock()
104
105 p.GetSchemaCalled = true
106 p.GetSchemaRequest = req
107 return p.GetSchemaReturn, p.GetSchemaReturnError
108}
109
95func (p *MockResourceProvider) Input( 110func (p *MockResourceProvider) Input(
96 input UIInput, c *ResourceConfig) (*ResourceConfig, error) { 111 input UIInput, c *ResourceConfig) (*ResourceConfig, error) {
112 p.Lock()
113 defer p.Unlock()
97 p.InputCalled = true 114 p.InputCalled = true
98 p.InputInput = input 115 p.InputInput = input
99 p.InputConfig = c 116 p.InputConfig = c
@@ -186,6 +203,7 @@ func (p *MockResourceProvider) Diff(
186 p.DiffInfo = info 203 p.DiffInfo = info
187 p.DiffState = state 204 p.DiffState = state
188 p.DiffDesired = desired 205 p.DiffDesired = desired
206
189 if p.DiffFn != nil { 207 if p.DiffFn != nil {
190 return p.DiffFn(info, state, desired) 208 return p.DiffFn(info, state, desired)
191 } 209 }
diff --git a/vendor/github.com/hashicorp/terraform/terraform/schemas.go b/vendor/github.com/hashicorp/terraform/terraform/schemas.go
new file mode 100644
index 0000000..ec46efc
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/terraform/schemas.go
@@ -0,0 +1,34 @@
1package terraform
2
3import (
4 "github.com/hashicorp/terraform/config/configschema"
5)
6
7type Schemas struct {
8 Providers ProviderSchemas
9}
10
11// ProviderSchemas is a map from provider names to provider schemas.
12//
13// The names in this map are the direct plugin name (e.g. "aws") rather than
14// any alias name (e.g. "aws.foo"), since.
15type ProviderSchemas map[string]*ProviderSchema
16
17// ProviderSchema represents the schema for a provider's own configuration
18// and the configuration for some or all of its resources and data sources.
19//
20// The completeness of this structure depends on how it was constructed.
21// When constructed for a configuration, it will generally include only
22// resource types and data sources used by that configuration.
23type ProviderSchema struct {
24 Provider *configschema.Block
25 ResourceTypes map[string]*configschema.Block
26 DataSources map[string]*configschema.Block
27}
28
29// ProviderSchemaRequest is used to describe to a ResourceProvider which
30// aspects of schema are required, when calling the GetSchema method.
31type ProviderSchemaRequest struct {
32 ResourceTypes []string
33 DataSources []string
34}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/shadow.go b/vendor/github.com/hashicorp/terraform/terraform/shadow.go
deleted file mode 100644
index 4632559..0000000
--- a/vendor/github.com/hashicorp/terraform/terraform/shadow.go
+++ /dev/null
@@ -1,28 +0,0 @@
1package terraform
2
3// Shadow is the interface that any "shadow" structures must implement.
4//
5// A shadow structure is an interface implementation (typically) that
6// shadows a real implementation and verifies that the same behavior occurs
7// on both. The semantics of this behavior are up to the interface itself.
8//
9// A shadow NEVER modifies real values or state. It must always be safe to use.
10//
11// For example, a ResourceProvider shadow ensures that the same operations
12// are done on the same resources with the same configurations.
13//
14// The typical usage of a shadow following this interface is to complete
15// the real operations, then call CloseShadow which tells the shadow that
16// the real side is done. Then, once the shadow is also complete, call
17// ShadowError to find any errors that may have been caught.
18type Shadow interface {
19 // CloseShadow tells the shadow that the REAL implementation is
20 // complete. Therefore, any calls that would block should now return
21 // immediately since no more changes will happen to the real side.
22 CloseShadow() error
23
24 // ShadowError returns the errors that the shadow has found.
25 // This should be called AFTER CloseShadow and AFTER the shadow is
26 // known to be complete (no more calls to it).
27 ShadowError() error
28}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/shadow_components.go b/vendor/github.com/hashicorp/terraform/terraform/shadow_components.go
deleted file mode 100644
index 116cf84..0000000
--- a/vendor/github.com/hashicorp/terraform/terraform/shadow_components.go
+++ /dev/null
@@ -1,273 +0,0 @@
1package terraform
2
3import (
4 "fmt"
5 "sync"
6
7 "github.com/hashicorp/go-multierror"
8 "github.com/hashicorp/terraform/helper/shadow"
9)
10
11// newShadowComponentFactory creates a shadowed contextComponentFactory
12// so that requests to create new components result in both a real and
13// shadow side.
14func newShadowComponentFactory(
15 f contextComponentFactory) (contextComponentFactory, *shadowComponentFactory) {
16 // Create the shared data
17 shared := &shadowComponentFactoryShared{contextComponentFactory: f}
18
19 // Create the real side
20 real := &shadowComponentFactory{
21 shadowComponentFactoryShared: shared,
22 }
23
24 // Create the shadow
25 shadow := &shadowComponentFactory{
26 shadowComponentFactoryShared: shared,
27 Shadow: true,
28 }
29
30 return real, shadow
31}
32
33// shadowComponentFactory is the shadow side. Any components created
34// with this factory are fake and will not cause real work to happen.
35//
36// Unlike other shadowers, the shadow component factory will allow the
37// shadow to create _any_ component even if it is never requested on the
38// real side. This is because errors will happen later downstream as function
39// calls are made to the shadows that are never matched on the real side.
40type shadowComponentFactory struct {
41 *shadowComponentFactoryShared
42
43 Shadow bool // True if this should return the shadow
44 lock sync.Mutex
45}
46
47func (f *shadowComponentFactory) ResourceProvider(
48 n, uid string) (ResourceProvider, error) {
49 f.lock.Lock()
50 defer f.lock.Unlock()
51
52 real, shadow, err := f.shadowComponentFactoryShared.ResourceProvider(n, uid)
53 var result ResourceProvider = real
54 if f.Shadow {
55 result = shadow
56 }
57
58 return result, err
59}
60
61func (f *shadowComponentFactory) ResourceProvisioner(
62 n, uid string) (ResourceProvisioner, error) {
63 f.lock.Lock()
64 defer f.lock.Unlock()
65
66 real, shadow, err := f.shadowComponentFactoryShared.ResourceProvisioner(n, uid)
67 var result ResourceProvisioner = real
68 if f.Shadow {
69 result = shadow
70 }
71
72 return result, err
73}
74
75// CloseShadow is called when the _real_ side is complete. This will cause
76// all future blocking operations to return immediately on the shadow to
77// ensure the shadow also completes.
78func (f *shadowComponentFactory) CloseShadow() error {
79 // If we aren't the shadow, just return
80 if !f.Shadow {
81 return nil
82 }
83
84 // Lock ourselves so we don't modify state
85 f.lock.Lock()
86 defer f.lock.Unlock()
87
88 // Grab our shared state
89 shared := f.shadowComponentFactoryShared
90
91 // If we're already closed, its an error
92 if shared.closed {
93 return fmt.Errorf("component factory shadow already closed")
94 }
95
96 // Close all the providers and provisioners and return the error
97 var result error
98 for _, n := range shared.providerKeys {
99 _, shadow, err := shared.ResourceProvider(n, n)
100 if err == nil && shadow != nil {
101 if err := shadow.CloseShadow(); err != nil {
102 result = multierror.Append(result, err)
103 }
104 }
105 }
106
107 for _, n := range shared.provisionerKeys {
108 _, shadow, err := shared.ResourceProvisioner(n, n)
109 if err == nil && shadow != nil {
110 if err := shadow.CloseShadow(); err != nil {
111 result = multierror.Append(result, err)
112 }
113 }
114 }
115
116 // Mark ourselves as closed
117 shared.closed = true
118
119 return result
120}
121
122func (f *shadowComponentFactory) ShadowError() error {
123 // If we aren't the shadow, just return
124 if !f.Shadow {
125 return nil
126 }
127
128 // Lock ourselves so we don't modify state
129 f.lock.Lock()
130 defer f.lock.Unlock()
131
132 // Grab our shared state
133 shared := f.shadowComponentFactoryShared
134
135 // If we're not closed, its an error
136 if !shared.closed {
137 return fmt.Errorf("component factory must be closed to retrieve errors")
138 }
139
140 // Close all the providers and provisioners and return the error
141 var result error
142 for _, n := range shared.providerKeys {
143 _, shadow, err := shared.ResourceProvider(n, n)
144 if err == nil && shadow != nil {
145 if err := shadow.ShadowError(); err != nil {
146 result = multierror.Append(result, err)
147 }
148 }
149 }
150
151 for _, n := range shared.provisionerKeys {
152 _, shadow, err := shared.ResourceProvisioner(n, n)
153 if err == nil && shadow != nil {
154 if err := shadow.ShadowError(); err != nil {
155 result = multierror.Append(result, err)
156 }
157 }
158 }
159
160 return result
161}
162
163// shadowComponentFactoryShared is shared data between the two factories.
164//
165// It is NOT SAFE to run any function on this struct in parallel. Lock
166// access to this struct.
167type shadowComponentFactoryShared struct {
168 contextComponentFactory
169
170 closed bool
171 providers shadow.KeyedValue
172 providerKeys []string
173 provisioners shadow.KeyedValue
174 provisionerKeys []string
175}
176
177// shadowResourceProviderFactoryEntry is the entry that is stored in
178// the Shadows key/value for a provider.
179type shadowComponentFactoryProviderEntry struct {
180 Real ResourceProvider
181 Shadow shadowResourceProvider
182 Err error
183}
184
185type shadowComponentFactoryProvisionerEntry struct {
186 Real ResourceProvisioner
187 Shadow shadowResourceProvisioner
188 Err error
189}
190
191func (f *shadowComponentFactoryShared) ResourceProvider(
192 n, uid string) (ResourceProvider, shadowResourceProvider, error) {
193 // Determine if we already have a value
194 raw, ok := f.providers.ValueOk(uid)
195 if !ok {
196 // Build the entry
197 var entry shadowComponentFactoryProviderEntry
198
199 // No value, initialize. Create the original
200 p, err := f.contextComponentFactory.ResourceProvider(n, uid)
201 if err != nil {
202 entry.Err = err
203 p = nil // Just to be sure
204 }
205
206 if p != nil {
207 // Create the shadow
208 real, shadow := newShadowResourceProvider(p)
209 entry.Real = real
210 entry.Shadow = shadow
211
212 if f.closed {
213 shadow.CloseShadow()
214 }
215 }
216
217 // Store the value
218 f.providers.SetValue(uid, &entry)
219 f.providerKeys = append(f.providerKeys, uid)
220 raw = &entry
221 }
222
223 // Read the entry
224 entry, ok := raw.(*shadowComponentFactoryProviderEntry)
225 if !ok {
226 return nil, nil, fmt.Errorf("Unknown value for shadow provider: %#v", raw)
227 }
228
229 // Return
230 return entry.Real, entry.Shadow, entry.Err
231}
232
233func (f *shadowComponentFactoryShared) ResourceProvisioner(
234 n, uid string) (ResourceProvisioner, shadowResourceProvisioner, error) {
235 // Determine if we already have a value
236 raw, ok := f.provisioners.ValueOk(uid)
237 if !ok {
238 // Build the entry
239 var entry shadowComponentFactoryProvisionerEntry
240
241 // No value, initialize. Create the original
242 p, err := f.contextComponentFactory.ResourceProvisioner(n, uid)
243 if err != nil {
244 entry.Err = err
245 p = nil // Just to be sure
246 }
247
248 if p != nil {
249 // For now, just create a mock since we don't support provisioners yet
250 real, shadow := newShadowResourceProvisioner(p)
251 entry.Real = real
252 entry.Shadow = shadow
253
254 if f.closed {
255 shadow.CloseShadow()
256 }
257 }
258
259 // Store the value
260 f.provisioners.SetValue(uid, &entry)
261 f.provisionerKeys = append(f.provisionerKeys, uid)
262 raw = &entry
263 }
264
265 // Read the entry
266 entry, ok := raw.(*shadowComponentFactoryProvisionerEntry)
267 if !ok {
268 return nil, nil, fmt.Errorf("Unknown value for shadow provisioner: %#v", raw)
269 }
270
271 // Return
272 return entry.Real, entry.Shadow, entry.Err
273}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/shadow_context.go b/vendor/github.com/hashicorp/terraform/terraform/shadow_context.go
deleted file mode 100644
index 5588af2..0000000
--- a/vendor/github.com/hashicorp/terraform/terraform/shadow_context.go
+++ /dev/null
@@ -1,158 +0,0 @@
1package terraform
2
3import (
4 "fmt"
5 "strings"
6
7 "github.com/hashicorp/go-multierror"
8 "github.com/mitchellh/copystructure"
9)
10
11// newShadowContext creates a new context that will shadow the given context
12// when walking the graph. The resulting context should be used _only once_
13// for a graph walk.
14//
15// The returned Shadow should be closed after the graph walk with the
16// real context is complete. Errors from the shadow can be retrieved there.
17//
18// Most importantly, any operations done on the shadow context (the returned
19// context) will NEVER affect the real context. All structures are deep
20// copied, no real providers or resources are used, etc.
21func newShadowContext(c *Context) (*Context, *Context, Shadow) {
22 // Copy the targets
23 targetRaw, err := copystructure.Copy(c.targets)
24 if err != nil {
25 panic(err)
26 }
27
28 // Copy the variables
29 varRaw, err := copystructure.Copy(c.variables)
30 if err != nil {
31 panic(err)
32 }
33
34 // Copy the provider inputs
35 providerInputRaw, err := copystructure.Copy(c.providerInputConfig)
36 if err != nil {
37 panic(err)
38 }
39
40 // The factories
41 componentsReal, componentsShadow := newShadowComponentFactory(c.components)
42
43 // Create the shadow
44 shadow := &Context{
45 components: componentsShadow,
46 destroy: c.destroy,
47 diff: c.diff.DeepCopy(),
48 hooks: nil,
49 meta: c.meta,
50 module: c.module,
51 state: c.state.DeepCopy(),
52 targets: targetRaw.([]string),
53 variables: varRaw.(map[string]interface{}),
54
55 // NOTE(mitchellh): This is not going to work for shadows that are
56 // testing that input results in the proper end state. At the time
57 // of writing, input is not used in any state-changing graph
58 // walks anyways, so this checks nothing. We set it to this to avoid
59 // any panics but even a "nil" value worked here.
60 uiInput: new(MockUIInput),
61
62 // Hardcoded to 4 since parallelism in the shadow doesn't matter
63 // a ton since we're doing far less compared to the real side
64 // and our operations are MUCH faster.
65 parallelSem: NewSemaphore(4),
66 providerInputConfig: providerInputRaw.(map[string]map[string]interface{}),
67 }
68
69 // Create the real context. This is effectively just a copy of
70 // the context given except we need to modify some of the values
71 // to point to the real side of a shadow so the shadow can compare values.
72 real := &Context{
73 // The fields below are changed.
74 components: componentsReal,
75
76 // The fields below are direct copies
77 destroy: c.destroy,
78 diff: c.diff,
79 // diffLock - no copy
80 hooks: c.hooks,
81 meta: c.meta,
82 module: c.module,
83 sh: c.sh,
84 state: c.state,
85 // stateLock - no copy
86 targets: c.targets,
87 uiInput: c.uiInput,
88 variables: c.variables,
89
90 // l - no copy
91 parallelSem: c.parallelSem,
92 providerInputConfig: c.providerInputConfig,
93 runContext: c.runContext,
94 runContextCancel: c.runContextCancel,
95 shadowErr: c.shadowErr,
96 }
97
98 return real, shadow, &shadowContextCloser{
99 Components: componentsShadow,
100 }
101}
102
103// shadowContextVerify takes the real and shadow context and verifies they
104// have equal diffs and states.
105func shadowContextVerify(real, shadow *Context) error {
106 var result error
107
108 // The states compared must be pruned so they're minimal/clean
109 real.state.prune()
110 shadow.state.prune()
111
112 // Compare the states
113 if !real.state.Equal(shadow.state) {
114 result = multierror.Append(result, fmt.Errorf(
115 "Real and shadow states do not match! "+
116 "Real state:\n\n%s\n\n"+
117 "Shadow state:\n\n%s\n\n",
118 real.state, shadow.state))
119 }
120
121 // Compare the diffs
122 if !real.diff.Equal(shadow.diff) {
123 result = multierror.Append(result, fmt.Errorf(
124 "Real and shadow diffs do not match! "+
125 "Real diff:\n\n%s\n\n"+
126 "Shadow diff:\n\n%s\n\n",
127 real.diff, shadow.diff))
128 }
129
130 return result
131}
132
133// shadowContextCloser is the io.Closer returned by newShadowContext that
134// closes all the shadows and returns the results.
135type shadowContextCloser struct {
136 Components *shadowComponentFactory
137}
138
139// Close closes the shadow context.
140func (c *shadowContextCloser) CloseShadow() error {
141 return c.Components.CloseShadow()
142}
143
144func (c *shadowContextCloser) ShadowError() error {
145 err := c.Components.ShadowError()
146 if err == nil {
147 return nil
148 }
149
150 // This is a sad edge case: if the configuration contains uuid() at
151 // any point, we cannot reason aboyt the shadow execution. Tested
152 // with Context2Plan_shadowUuid.
153 if strings.Contains(err.Error(), "uuid()") {
154 err = nil
155 }
156
157 return err
158}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provider.go b/vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provider.go
deleted file mode 100644
index 9741d7e..0000000
--- a/vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provider.go
+++ /dev/null
@@ -1,815 +0,0 @@
1package terraform
2
3import (
4 "fmt"
5 "log"
6 "sync"
7
8 "github.com/hashicorp/go-multierror"
9 "github.com/hashicorp/terraform/helper/shadow"
10)
11
12// shadowResourceProvider implements ResourceProvider for the shadow
13// eval context defined in eval_context_shadow.go.
14//
15// This is used to verify behavior with a real provider. This shouldn't
16// be used directly.
17type shadowResourceProvider interface {
18 ResourceProvider
19 Shadow
20}
21
22// newShadowResourceProvider creates a new shadowed ResourceProvider.
23//
24// This will assume a well behaved real ResourceProvider. For example,
25// it assumes that the `Resources` call underneath doesn't change values
26// since once it is called on the real provider, it will be cached and
27// returned in the shadow since number of calls to that shouldn't affect
28// actual behavior.
29//
30// However, with calls like Apply, call order is taken into account,
31// parameters are checked for equality, etc.
32func newShadowResourceProvider(p ResourceProvider) (ResourceProvider, shadowResourceProvider) {
33 // Create the shared data
34 shared := shadowResourceProviderShared{}
35
36 // Create the real provider that does actual work
37 real := &shadowResourceProviderReal{
38 ResourceProvider: p,
39 Shared: &shared,
40 }
41
42 // Create the shadow that watches the real value
43 shadow := &shadowResourceProviderShadow{
44 Shared: &shared,
45
46 resources: p.Resources(),
47 dataSources: p.DataSources(),
48 }
49
50 return real, shadow
51}
52
53// shadowResourceProviderReal is the real resource provider. Function calls
54// to this will perform real work. This records the parameters and return
55// values and call order for the shadow to reproduce.
56type shadowResourceProviderReal struct {
57 ResourceProvider
58
59 Shared *shadowResourceProviderShared
60}
61
62func (p *shadowResourceProviderReal) Close() error {
63 var result error
64 if c, ok := p.ResourceProvider.(ResourceProviderCloser); ok {
65 result = c.Close()
66 }
67
68 p.Shared.CloseErr.SetValue(result)
69 return result
70}
71
72func (p *shadowResourceProviderReal) Input(
73 input UIInput, c *ResourceConfig) (*ResourceConfig, error) {
74 cCopy := c.DeepCopy()
75
76 result, err := p.ResourceProvider.Input(input, c)
77 p.Shared.Input.SetValue(&shadowResourceProviderInput{
78 Config: cCopy,
79 Result: result.DeepCopy(),
80 ResultErr: err,
81 })
82
83 return result, err
84}
85
86func (p *shadowResourceProviderReal) Validate(c *ResourceConfig) ([]string, []error) {
87 warns, errs := p.ResourceProvider.Validate(c)
88 p.Shared.Validate.SetValue(&shadowResourceProviderValidate{
89 Config: c.DeepCopy(),
90 ResultWarn: warns,
91 ResultErr: errs,
92 })
93
94 return warns, errs
95}
96
97func (p *shadowResourceProviderReal) Configure(c *ResourceConfig) error {
98 cCopy := c.DeepCopy()
99
100 err := p.ResourceProvider.Configure(c)
101 p.Shared.Configure.SetValue(&shadowResourceProviderConfigure{
102 Config: cCopy,
103 Result: err,
104 })
105
106 return err
107}
108
109func (p *shadowResourceProviderReal) Stop() error {
110 return p.ResourceProvider.Stop()
111}
112
113func (p *shadowResourceProviderReal) ValidateResource(
114 t string, c *ResourceConfig) ([]string, []error) {
115 key := t
116 configCopy := c.DeepCopy()
117
118 // Real operation
119 warns, errs := p.ResourceProvider.ValidateResource(t, c)
120
121 // Initialize to ensure we always have a wrapper with a lock
122 p.Shared.ValidateResource.Init(
123 key, &shadowResourceProviderValidateResourceWrapper{})
124
125 // Get the result
126 raw := p.Shared.ValidateResource.Value(key)
127 wrapper, ok := raw.(*shadowResourceProviderValidateResourceWrapper)
128 if !ok {
129 // If this fails then we just continue with our day... the shadow
130 // will fail to but there isn't much we can do.
131 log.Printf(
132 "[ERROR] unknown value in ValidateResource shadow value: %#v", raw)
133 return warns, errs
134 }
135
136 // Lock the wrapper for writing and record our call
137 wrapper.Lock()
138 defer wrapper.Unlock()
139
140 wrapper.Calls = append(wrapper.Calls, &shadowResourceProviderValidateResource{
141 Config: configCopy,
142 Warns: warns,
143 Errors: errs,
144 })
145
146 // With it locked, call SetValue again so that it triggers WaitForChange
147 p.Shared.ValidateResource.SetValue(key, wrapper)
148
149 // Return the result
150 return warns, errs
151}
152
153func (p *shadowResourceProviderReal) Apply(
154 info *InstanceInfo,
155 state *InstanceState,
156 diff *InstanceDiff) (*InstanceState, error) {
157 // Thse have to be copied before the call since call can modify
158 stateCopy := state.DeepCopy()
159 diffCopy := diff.DeepCopy()
160
161 result, err := p.ResourceProvider.Apply(info, state, diff)
162 p.Shared.Apply.SetValue(info.uniqueId(), &shadowResourceProviderApply{
163 State: stateCopy,
164 Diff: diffCopy,
165 Result: result.DeepCopy(),
166 ResultErr: err,
167 })
168
169 return result, err
170}
171
172func (p *shadowResourceProviderReal) Diff(
173 info *InstanceInfo,
174 state *InstanceState,
175 desired *ResourceConfig) (*InstanceDiff, error) {
176 // Thse have to be copied before the call since call can modify
177 stateCopy := state.DeepCopy()
178 desiredCopy := desired.DeepCopy()
179
180 result, err := p.ResourceProvider.Diff(info, state, desired)
181 p.Shared.Diff.SetValue(info.uniqueId(), &shadowResourceProviderDiff{
182 State: stateCopy,
183 Desired: desiredCopy,
184 Result: result.DeepCopy(),
185 ResultErr: err,
186 })
187
188 return result, err
189}
190
191func (p *shadowResourceProviderReal) Refresh(
192 info *InstanceInfo,
193 state *InstanceState) (*InstanceState, error) {
194 // Thse have to be copied before the call since call can modify
195 stateCopy := state.DeepCopy()
196
197 result, err := p.ResourceProvider.Refresh(info, state)
198 p.Shared.Refresh.SetValue(info.uniqueId(), &shadowResourceProviderRefresh{
199 State: stateCopy,
200 Result: result.DeepCopy(),
201 ResultErr: err,
202 })
203
204 return result, err
205}
206
207func (p *shadowResourceProviderReal) ValidateDataSource(
208 t string, c *ResourceConfig) ([]string, []error) {
209 key := t
210 configCopy := c.DeepCopy()
211
212 // Real operation
213 warns, errs := p.ResourceProvider.ValidateDataSource(t, c)
214
215 // Initialize
216 p.Shared.ValidateDataSource.Init(
217 key, &shadowResourceProviderValidateDataSourceWrapper{})
218
219 // Get the result
220 raw := p.Shared.ValidateDataSource.Value(key)
221 wrapper, ok := raw.(*shadowResourceProviderValidateDataSourceWrapper)
222 if !ok {
223 // If this fails then we just continue with our day... the shadow
224 // will fail to but there isn't much we can do.
225 log.Printf(
226 "[ERROR] unknown value in ValidateDataSource shadow value: %#v", raw)
227 return warns, errs
228 }
229
230 // Lock the wrapper for writing and record our call
231 wrapper.Lock()
232 defer wrapper.Unlock()
233
234 wrapper.Calls = append(wrapper.Calls, &shadowResourceProviderValidateDataSource{
235 Config: configCopy,
236 Warns: warns,
237 Errors: errs,
238 })
239
240 // Set it
241 p.Shared.ValidateDataSource.SetValue(key, wrapper)
242
243 // Return the result
244 return warns, errs
245}
246
247func (p *shadowResourceProviderReal) ReadDataDiff(
248 info *InstanceInfo,
249 desired *ResourceConfig) (*InstanceDiff, error) {
250 // These have to be copied before the call since call can modify
251 desiredCopy := desired.DeepCopy()
252
253 result, err := p.ResourceProvider.ReadDataDiff(info, desired)
254 p.Shared.ReadDataDiff.SetValue(info.uniqueId(), &shadowResourceProviderReadDataDiff{
255 Desired: desiredCopy,
256 Result: result.DeepCopy(),
257 ResultErr: err,
258 })
259
260 return result, err
261}
262
263func (p *shadowResourceProviderReal) ReadDataApply(
264 info *InstanceInfo,
265 diff *InstanceDiff) (*InstanceState, error) {
266 // Thse have to be copied before the call since call can modify
267 diffCopy := diff.DeepCopy()
268
269 result, err := p.ResourceProvider.ReadDataApply(info, diff)
270 p.Shared.ReadDataApply.SetValue(info.uniqueId(), &shadowResourceProviderReadDataApply{
271 Diff: diffCopy,
272 Result: result.DeepCopy(),
273 ResultErr: err,
274 })
275
276 return result, err
277}
278
279// shadowResourceProviderShadow is the shadow resource provider. Function
280// calls never affect real resources. This is paired with the "real" side
281// which must be called properly to enable recording.
282type shadowResourceProviderShadow struct {
283 Shared *shadowResourceProviderShared
284
285 // Cached values that are expected to not change
286 resources []ResourceType
287 dataSources []DataSource
288
289 Error error // Error is the list of errors from the shadow
290 ErrorLock sync.Mutex
291}
292
293type shadowResourceProviderShared struct {
294 // NOTE: Anytime a value is added here, be sure to add it to
295 // the Close() method so that it is closed.
296
297 CloseErr shadow.Value
298 Input shadow.Value
299 Validate shadow.Value
300 Configure shadow.Value
301 ValidateResource shadow.KeyedValue
302 Apply shadow.KeyedValue
303 Diff shadow.KeyedValue
304 Refresh shadow.KeyedValue
305 ValidateDataSource shadow.KeyedValue
306 ReadDataDiff shadow.KeyedValue
307 ReadDataApply shadow.KeyedValue
308}
309
310func (p *shadowResourceProviderShared) Close() error {
311 return shadow.Close(p)
312}
313
314func (p *shadowResourceProviderShadow) CloseShadow() error {
315 err := p.Shared.Close()
316 if err != nil {
317 err = fmt.Errorf("close error: %s", err)
318 }
319
320 return err
321}
322
323func (p *shadowResourceProviderShadow) ShadowError() error {
324 return p.Error
325}
326
327func (p *shadowResourceProviderShadow) Resources() []ResourceType {
328 return p.resources
329}
330
331func (p *shadowResourceProviderShadow) DataSources() []DataSource {
332 return p.dataSources
333}
334
335func (p *shadowResourceProviderShadow) Close() error {
336 v := p.Shared.CloseErr.Value()
337 if v == nil {
338 return nil
339 }
340
341 return v.(error)
342}
343
344func (p *shadowResourceProviderShadow) Input(
345 input UIInput, c *ResourceConfig) (*ResourceConfig, error) {
346 // Get the result of the input call
347 raw := p.Shared.Input.Value()
348 if raw == nil {
349 return nil, nil
350 }
351
352 result, ok := raw.(*shadowResourceProviderInput)
353 if !ok {
354 p.ErrorLock.Lock()
355 defer p.ErrorLock.Unlock()
356 p.Error = multierror.Append(p.Error, fmt.Errorf(
357 "Unknown 'input' shadow value: %#v", raw))
358 return nil, nil
359 }
360
361 // Compare the parameters, which should be identical
362 if !c.Equal(result.Config) {
363 p.ErrorLock.Lock()
364 p.Error = multierror.Append(p.Error, fmt.Errorf(
365 "Input had unequal configurations (real, then shadow):\n\n%#v\n\n%#v",
366 result.Config, c))
367 p.ErrorLock.Unlock()
368 }
369
370 // Return the results
371 return result.Result, result.ResultErr
372}
373
374func (p *shadowResourceProviderShadow) Validate(c *ResourceConfig) ([]string, []error) {
375 // Get the result of the validate call
376 raw := p.Shared.Validate.Value()
377 if raw == nil {
378 return nil, nil
379 }
380
381 result, ok := raw.(*shadowResourceProviderValidate)
382 if !ok {
383 p.ErrorLock.Lock()
384 defer p.ErrorLock.Unlock()
385 p.Error = multierror.Append(p.Error, fmt.Errorf(
386 "Unknown 'validate' shadow value: %#v", raw))
387 return nil, nil
388 }
389
390 // Compare the parameters, which should be identical
391 if !c.Equal(result.Config) {
392 p.ErrorLock.Lock()
393 p.Error = multierror.Append(p.Error, fmt.Errorf(
394 "Validate had unequal configurations (real, then shadow):\n\n%#v\n\n%#v",
395 result.Config, c))
396 p.ErrorLock.Unlock()
397 }
398
399 // Return the results
400 return result.ResultWarn, result.ResultErr
401}
402
403func (p *shadowResourceProviderShadow) Configure(c *ResourceConfig) error {
404 // Get the result of the call
405 raw := p.Shared.Configure.Value()
406 if raw == nil {
407 return nil
408 }
409
410 result, ok := raw.(*shadowResourceProviderConfigure)
411 if !ok {
412 p.ErrorLock.Lock()
413 defer p.ErrorLock.Unlock()
414 p.Error = multierror.Append(p.Error, fmt.Errorf(
415 "Unknown 'configure' shadow value: %#v", raw))
416 return nil
417 }
418
419 // Compare the parameters, which should be identical
420 if !c.Equal(result.Config) {
421 p.ErrorLock.Lock()
422 p.Error = multierror.Append(p.Error, fmt.Errorf(
423 "Configure had unequal configurations (real, then shadow):\n\n%#v\n\n%#v",
424 result.Config, c))
425 p.ErrorLock.Unlock()
426 }
427
428 // Return the results
429 return result.Result
430}
431
432// Stop returns immediately.
433func (p *shadowResourceProviderShadow) Stop() error {
434 return nil
435}
436
437func (p *shadowResourceProviderShadow) ValidateResource(t string, c *ResourceConfig) ([]string, []error) {
438 // Unique key
439 key := t
440
441 // Get the initial value
442 raw := p.Shared.ValidateResource.Value(key)
443
444 // Find a validation with our configuration
445 var result *shadowResourceProviderValidateResource
446 for {
447 // Get the value
448 if raw == nil {
449 p.ErrorLock.Lock()
450 defer p.ErrorLock.Unlock()
451 p.Error = multierror.Append(p.Error, fmt.Errorf(
452 "Unknown 'ValidateResource' call for %q:\n\n%#v",
453 key, c))
454 return nil, nil
455 }
456
457 wrapper, ok := raw.(*shadowResourceProviderValidateResourceWrapper)
458 if !ok {
459 p.ErrorLock.Lock()
460 defer p.ErrorLock.Unlock()
461 p.Error = multierror.Append(p.Error, fmt.Errorf(
462 "Unknown 'ValidateResource' shadow value for %q: %#v", key, raw))
463 return nil, nil
464 }
465
466 // Look for the matching call with our configuration
467 wrapper.RLock()
468 for _, call := range wrapper.Calls {
469 if call.Config.Equal(c) {
470 result = call
471 break
472 }
473 }
474 wrapper.RUnlock()
475
476 // If we found a result, exit
477 if result != nil {
478 break
479 }
480
481 // Wait for a change so we can get the wrapper again
482 raw = p.Shared.ValidateResource.WaitForChange(key)
483 }
484
485 return result.Warns, result.Errors
486}
487
488func (p *shadowResourceProviderShadow) Apply(
489 info *InstanceInfo,
490 state *InstanceState,
491 diff *InstanceDiff) (*InstanceState, error) {
492 // Unique key
493 key := info.uniqueId()
494 raw := p.Shared.Apply.Value(key)
495 if raw == nil {
496 p.ErrorLock.Lock()
497 defer p.ErrorLock.Unlock()
498 p.Error = multierror.Append(p.Error, fmt.Errorf(
499 "Unknown 'apply' call for %q:\n\n%#v\n\n%#v",
500 key, state, diff))
501 return nil, nil
502 }
503
504 result, ok := raw.(*shadowResourceProviderApply)
505 if !ok {
506 p.ErrorLock.Lock()
507 defer p.ErrorLock.Unlock()
508 p.Error = multierror.Append(p.Error, fmt.Errorf(
509 "Unknown 'apply' shadow value for %q: %#v", key, raw))
510 return nil, nil
511 }
512
513 // Compare the parameters, which should be identical
514 if !state.Equal(result.State) {
515 p.ErrorLock.Lock()
516 p.Error = multierror.Append(p.Error, fmt.Errorf(
517 "Apply %q: state had unequal states (real, then shadow):\n\n%#v\n\n%#v",
518 key, result.State, state))
519 p.ErrorLock.Unlock()
520 }
521
522 if !diff.Equal(result.Diff) {
523 p.ErrorLock.Lock()
524 p.Error = multierror.Append(p.Error, fmt.Errorf(
525 "Apply %q: unequal diffs (real, then shadow):\n\n%#v\n\n%#v",
526 key, result.Diff, diff))
527 p.ErrorLock.Unlock()
528 }
529
530 return result.Result, result.ResultErr
531}
532
533func (p *shadowResourceProviderShadow) Diff(
534 info *InstanceInfo,
535 state *InstanceState,
536 desired *ResourceConfig) (*InstanceDiff, error) {
537 // Unique key
538 key := info.uniqueId()
539 raw := p.Shared.Diff.Value(key)
540 if raw == nil {
541 p.ErrorLock.Lock()
542 defer p.ErrorLock.Unlock()
543 p.Error = multierror.Append(p.Error, fmt.Errorf(
544 "Unknown 'diff' call for %q:\n\n%#v\n\n%#v",
545 key, state, desired))
546 return nil, nil
547 }
548
549 result, ok := raw.(*shadowResourceProviderDiff)
550 if !ok {
551 p.ErrorLock.Lock()
552 defer p.ErrorLock.Unlock()
553 p.Error = multierror.Append(p.Error, fmt.Errorf(
554 "Unknown 'diff' shadow value for %q: %#v", key, raw))
555 return nil, nil
556 }
557
558 // Compare the parameters, which should be identical
559 if !state.Equal(result.State) {
560 p.ErrorLock.Lock()
561 p.Error = multierror.Append(p.Error, fmt.Errorf(
562 "Diff %q had unequal states (real, then shadow):\n\n%#v\n\n%#v",
563 key, result.State, state))
564 p.ErrorLock.Unlock()
565 }
566 if !desired.Equal(result.Desired) {
567 p.ErrorLock.Lock()
568 p.Error = multierror.Append(p.Error, fmt.Errorf(
569 "Diff %q had unequal states (real, then shadow):\n\n%#v\n\n%#v",
570 key, result.Desired, desired))
571 p.ErrorLock.Unlock()
572 }
573
574 return result.Result, result.ResultErr
575}
576
577func (p *shadowResourceProviderShadow) Refresh(
578 info *InstanceInfo,
579 state *InstanceState) (*InstanceState, error) {
580 // Unique key
581 key := info.uniqueId()
582 raw := p.Shared.Refresh.Value(key)
583 if raw == nil {
584 p.ErrorLock.Lock()
585 defer p.ErrorLock.Unlock()
586 p.Error = multierror.Append(p.Error, fmt.Errorf(
587 "Unknown 'refresh' call for %q:\n\n%#v",
588 key, state))
589 return nil, nil
590 }
591
592 result, ok := raw.(*shadowResourceProviderRefresh)
593 if !ok {
594 p.ErrorLock.Lock()
595 defer p.ErrorLock.Unlock()
596 p.Error = multierror.Append(p.Error, fmt.Errorf(
597 "Unknown 'refresh' shadow value: %#v", raw))
598 return nil, nil
599 }
600
601 // Compare the parameters, which should be identical
602 if !state.Equal(result.State) {
603 p.ErrorLock.Lock()
604 p.Error = multierror.Append(p.Error, fmt.Errorf(
605 "Refresh %q had unequal states (real, then shadow):\n\n%#v\n\n%#v",
606 key, result.State, state))
607 p.ErrorLock.Unlock()
608 }
609
610 return result.Result, result.ResultErr
611}
612
613func (p *shadowResourceProviderShadow) ValidateDataSource(
614 t string, c *ResourceConfig) ([]string, []error) {
615 // Unique key
616 key := t
617
618 // Get the initial value
619 raw := p.Shared.ValidateDataSource.Value(key)
620
621 // Find a validation with our configuration
622 var result *shadowResourceProviderValidateDataSource
623 for {
624 // Get the value
625 if raw == nil {
626 p.ErrorLock.Lock()
627 defer p.ErrorLock.Unlock()
628 p.Error = multierror.Append(p.Error, fmt.Errorf(
629 "Unknown 'ValidateDataSource' call for %q:\n\n%#v",
630 key, c))
631 return nil, nil
632 }
633
634 wrapper, ok := raw.(*shadowResourceProviderValidateDataSourceWrapper)
635 if !ok {
636 p.ErrorLock.Lock()
637 defer p.ErrorLock.Unlock()
638 p.Error = multierror.Append(p.Error, fmt.Errorf(
639 "Unknown 'ValidateDataSource' shadow value: %#v", raw))
640 return nil, nil
641 }
642
643 // Look for the matching call with our configuration
644 wrapper.RLock()
645 for _, call := range wrapper.Calls {
646 if call.Config.Equal(c) {
647 result = call
648 break
649 }
650 }
651 wrapper.RUnlock()
652
653 // If we found a result, exit
654 if result != nil {
655 break
656 }
657
658 // Wait for a change so we can get the wrapper again
659 raw = p.Shared.ValidateDataSource.WaitForChange(key)
660 }
661
662 return result.Warns, result.Errors
663}
664
665func (p *shadowResourceProviderShadow) ReadDataDiff(
666 info *InstanceInfo,
667 desired *ResourceConfig) (*InstanceDiff, error) {
668 // Unique key
669 key := info.uniqueId()
670 raw := p.Shared.ReadDataDiff.Value(key)
671 if raw == nil {
672 p.ErrorLock.Lock()
673 defer p.ErrorLock.Unlock()
674 p.Error = multierror.Append(p.Error, fmt.Errorf(
675 "Unknown 'ReadDataDiff' call for %q:\n\n%#v",
676 key, desired))
677 return nil, nil
678 }
679
680 result, ok := raw.(*shadowResourceProviderReadDataDiff)
681 if !ok {
682 p.ErrorLock.Lock()
683 defer p.ErrorLock.Unlock()
684 p.Error = multierror.Append(p.Error, fmt.Errorf(
685 "Unknown 'ReadDataDiff' shadow value for %q: %#v", key, raw))
686 return nil, nil
687 }
688
689 // Compare the parameters, which should be identical
690 if !desired.Equal(result.Desired) {
691 p.ErrorLock.Lock()
692 p.Error = multierror.Append(p.Error, fmt.Errorf(
693 "ReadDataDiff %q had unequal configs (real, then shadow):\n\n%#v\n\n%#v",
694 key, result.Desired, desired))
695 p.ErrorLock.Unlock()
696 }
697
698 return result.Result, result.ResultErr
699}
700
701func (p *shadowResourceProviderShadow) ReadDataApply(
702 info *InstanceInfo,
703 d *InstanceDiff) (*InstanceState, error) {
704 // Unique key
705 key := info.uniqueId()
706 raw := p.Shared.ReadDataApply.Value(key)
707 if raw == nil {
708 p.ErrorLock.Lock()
709 defer p.ErrorLock.Unlock()
710 p.Error = multierror.Append(p.Error, fmt.Errorf(
711 "Unknown 'ReadDataApply' call for %q:\n\n%#v",
712 key, d))
713 return nil, nil
714 }
715
716 result, ok := raw.(*shadowResourceProviderReadDataApply)
717 if !ok {
718 p.ErrorLock.Lock()
719 defer p.ErrorLock.Unlock()
720 p.Error = multierror.Append(p.Error, fmt.Errorf(
721 "Unknown 'ReadDataApply' shadow value for %q: %#v", key, raw))
722 return nil, nil
723 }
724
725 // Compare the parameters, which should be identical
726 if !d.Equal(result.Diff) {
727 p.ErrorLock.Lock()
728 p.Error = multierror.Append(p.Error, fmt.Errorf(
729 "ReadDataApply: unequal diffs (real, then shadow):\n\n%#v\n\n%#v",
730 result.Diff, d))
731 p.ErrorLock.Unlock()
732 }
733
734 return result.Result, result.ResultErr
735}
736
737func (p *shadowResourceProviderShadow) ImportState(info *InstanceInfo, id string) ([]*InstanceState, error) {
738 panic("import not supported by shadow graph")
739}
740
741// The structs for the various function calls are put below. These structs
742// are used to carry call information across the real/shadow boundaries.
743
744type shadowResourceProviderInput struct {
745 Config *ResourceConfig
746 Result *ResourceConfig
747 ResultErr error
748}
749
750type shadowResourceProviderValidate struct {
751 Config *ResourceConfig
752 ResultWarn []string
753 ResultErr []error
754}
755
756type shadowResourceProviderConfigure struct {
757 Config *ResourceConfig
758 Result error
759}
760
761type shadowResourceProviderValidateResourceWrapper struct {
762 sync.RWMutex
763
764 Calls []*shadowResourceProviderValidateResource
765}
766
767type shadowResourceProviderValidateResource struct {
768 Config *ResourceConfig
769 Warns []string
770 Errors []error
771}
772
773type shadowResourceProviderApply struct {
774 State *InstanceState
775 Diff *InstanceDiff
776 Result *InstanceState
777 ResultErr error
778}
779
780type shadowResourceProviderDiff struct {
781 State *InstanceState
782 Desired *ResourceConfig
783 Result *InstanceDiff
784 ResultErr error
785}
786
787type shadowResourceProviderRefresh struct {
788 State *InstanceState
789 Result *InstanceState
790 ResultErr error
791}
792
793type shadowResourceProviderValidateDataSourceWrapper struct {
794 sync.RWMutex
795
796 Calls []*shadowResourceProviderValidateDataSource
797}
798
799type shadowResourceProviderValidateDataSource struct {
800 Config *ResourceConfig
801 Warns []string
802 Errors []error
803}
804
805type shadowResourceProviderReadDataDiff struct {
806 Desired *ResourceConfig
807 Result *InstanceDiff
808 ResultErr error
809}
810
811type shadowResourceProviderReadDataApply struct {
812 Diff *InstanceDiff
813 Result *InstanceState
814 ResultErr error
815}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provisioner.go b/vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provisioner.go
deleted file mode 100644
index 60a4908..0000000
--- a/vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provisioner.go
+++ /dev/null
@@ -1,282 +0,0 @@
1package terraform
2
3import (
4 "fmt"
5 "io"
6 "log"
7 "sync"
8
9 "github.com/hashicorp/go-multierror"
10 "github.com/hashicorp/terraform/helper/shadow"
11)
12
13// shadowResourceProvisioner implements ResourceProvisioner for the shadow
14// eval context defined in eval_context_shadow.go.
15//
16// This is used to verify behavior with a real provisioner. This shouldn't
17// be used directly.
18type shadowResourceProvisioner interface {
19 ResourceProvisioner
20 Shadow
21}
22
23// newShadowResourceProvisioner creates a new shadowed ResourceProvisioner.
24func newShadowResourceProvisioner(
25 p ResourceProvisioner) (ResourceProvisioner, shadowResourceProvisioner) {
26 // Create the shared data
27 shared := shadowResourceProvisionerShared{
28 Validate: shadow.ComparedValue{
29 Func: shadowResourceProvisionerValidateCompare,
30 },
31 }
32
33 // Create the real provisioner that does actual work
34 real := &shadowResourceProvisionerReal{
35 ResourceProvisioner: p,
36 Shared: &shared,
37 }
38
39 // Create the shadow that watches the real value
40 shadow := &shadowResourceProvisionerShadow{
41 Shared: &shared,
42 }
43
44 return real, shadow
45}
46
47// shadowResourceProvisionerReal is the real resource provisioner. Function calls
48// to this will perform real work. This records the parameters and return
49// values and call order for the shadow to reproduce.
50type shadowResourceProvisionerReal struct {
51 ResourceProvisioner
52
53 Shared *shadowResourceProvisionerShared
54}
55
56func (p *shadowResourceProvisionerReal) Close() error {
57 var result error
58 if c, ok := p.ResourceProvisioner.(ResourceProvisionerCloser); ok {
59 result = c.Close()
60 }
61
62 p.Shared.CloseErr.SetValue(result)
63 return result
64}
65
66func (p *shadowResourceProvisionerReal) Validate(c *ResourceConfig) ([]string, []error) {
67 warns, errs := p.ResourceProvisioner.Validate(c)
68 p.Shared.Validate.SetValue(&shadowResourceProvisionerValidate{
69 Config: c,
70 ResultWarn: warns,
71 ResultErr: errs,
72 })
73
74 return warns, errs
75}
76
77func (p *shadowResourceProvisionerReal) Apply(
78 output UIOutput, s *InstanceState, c *ResourceConfig) error {
79 err := p.ResourceProvisioner.Apply(output, s, c)
80
81 // Write the result, grab a lock for writing. This should nver
82 // block long since the operations below don't block.
83 p.Shared.ApplyLock.Lock()
84 defer p.Shared.ApplyLock.Unlock()
85
86 key := s.ID
87 raw, ok := p.Shared.Apply.ValueOk(key)
88 if !ok {
89 // Setup a new value
90 raw = &shadow.ComparedValue{
91 Func: shadowResourceProvisionerApplyCompare,
92 }
93
94 // Set it
95 p.Shared.Apply.SetValue(key, raw)
96 }
97
98 compareVal, ok := raw.(*shadow.ComparedValue)
99 if !ok {
100 // Just log and return so that we don't cause the real side
101 // any side effects.
102 log.Printf("[ERROR] unknown value in 'apply': %#v", raw)
103 return err
104 }
105
106 // Write the resulting value
107 compareVal.SetValue(&shadowResourceProvisionerApply{
108 Config: c,
109 ResultErr: err,
110 })
111
112 return err
113}
114
115func (p *shadowResourceProvisionerReal) Stop() error {
116 return p.ResourceProvisioner.Stop()
117}
118
119// shadowResourceProvisionerShadow is the shadow resource provisioner. Function
120// calls never affect real resources. This is paired with the "real" side
121// which must be called properly to enable recording.
122type shadowResourceProvisionerShadow struct {
123 Shared *shadowResourceProvisionerShared
124
125 Error error // Error is the list of errors from the shadow
126 ErrorLock sync.Mutex
127}
128
129type shadowResourceProvisionerShared struct {
130 // NOTE: Anytime a value is added here, be sure to add it to
131 // the Close() method so that it is closed.
132
133 CloseErr shadow.Value
134 Validate shadow.ComparedValue
135 Apply shadow.KeyedValue
136 ApplyLock sync.Mutex // For writing only
137}
138
139func (p *shadowResourceProvisionerShared) Close() error {
140 closers := []io.Closer{
141 &p.CloseErr,
142 }
143
144 for _, c := range closers {
145 // This should never happen, but we don't panic because a panic
146 // could affect the real behavior of Terraform and a shadow should
147 // never be able to do that.
148 if err := c.Close(); err != nil {
149 return err
150 }
151 }
152
153 return nil
154}
155
156func (p *shadowResourceProvisionerShadow) CloseShadow() error {
157 err := p.Shared.Close()
158 if err != nil {
159 err = fmt.Errorf("close error: %s", err)
160 }
161
162 return err
163}
164
165func (p *shadowResourceProvisionerShadow) ShadowError() error {
166 return p.Error
167}
168
169func (p *shadowResourceProvisionerShadow) Close() error {
170 v := p.Shared.CloseErr.Value()
171 if v == nil {
172 return nil
173 }
174
175 return v.(error)
176}
177
178func (p *shadowResourceProvisionerShadow) Validate(c *ResourceConfig) ([]string, []error) {
179 // Get the result of the validate call
180 raw := p.Shared.Validate.Value(c)
181 if raw == nil {
182 return nil, nil
183 }
184
185 result, ok := raw.(*shadowResourceProvisionerValidate)
186 if !ok {
187 p.ErrorLock.Lock()
188 defer p.ErrorLock.Unlock()
189 p.Error = multierror.Append(p.Error, fmt.Errorf(
190 "Unknown 'validate' shadow value: %#v", raw))
191 return nil, nil
192 }
193
194 // We don't need to compare configurations because we key on the
195 // configuration so just return right away.
196 return result.ResultWarn, result.ResultErr
197}
198
199func (p *shadowResourceProvisionerShadow) Apply(
200 output UIOutput, s *InstanceState, c *ResourceConfig) error {
201 // Get the value based on the key
202 key := s.ID
203 raw := p.Shared.Apply.Value(key)
204 if raw == nil {
205 return nil
206 }
207
208 compareVal, ok := raw.(*shadow.ComparedValue)
209 if !ok {
210 p.ErrorLock.Lock()
211 defer p.ErrorLock.Unlock()
212 p.Error = multierror.Append(p.Error, fmt.Errorf(
213 "Unknown 'apply' shadow value: %#v", raw))
214 return nil
215 }
216
217 // With the compared value, we compare against our config
218 raw = compareVal.Value(c)
219 if raw == nil {
220 return nil
221 }
222
223 result, ok := raw.(*shadowResourceProvisionerApply)
224 if !ok {
225 p.ErrorLock.Lock()
226 defer p.ErrorLock.Unlock()
227 p.Error = multierror.Append(p.Error, fmt.Errorf(
228 "Unknown 'apply' shadow value: %#v", raw))
229 return nil
230 }
231
232 return result.ResultErr
233}
234
235func (p *shadowResourceProvisionerShadow) Stop() error {
236 // For the shadow, we always just return nil since a Stop indicates
237 // that we were interrupted and shadows are disabled during interrupts
238 // anyways.
239 return nil
240}
241
242// The structs for the various function calls are put below. These structs
243// are used to carry call information across the real/shadow boundaries.
244
245type shadowResourceProvisionerValidate struct {
246 Config *ResourceConfig
247 ResultWarn []string
248 ResultErr []error
249}
250
251type shadowResourceProvisionerApply struct {
252 Config *ResourceConfig
253 ResultErr error
254}
255
256func shadowResourceProvisionerValidateCompare(k, v interface{}) bool {
257 c, ok := k.(*ResourceConfig)
258 if !ok {
259 return false
260 }
261
262 result, ok := v.(*shadowResourceProvisionerValidate)
263 if !ok {
264 return false
265 }
266
267 return c.Equal(result.Config)
268}
269
270func shadowResourceProvisionerApplyCompare(k, v interface{}) bool {
271 c, ok := k.(*ResourceConfig)
272 if !ok {
273 return false
274 }
275
276 result, ok := v.(*shadowResourceProvisionerApply)
277 if !ok {
278 return false
279 }
280
281 return c.Equal(result.Config)
282}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/state.go b/vendor/github.com/hashicorp/terraform/terraform/state.go
index 0c46194..04b14a6 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/state.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/state.go
@@ -9,6 +9,7 @@ import (
9 "io" 9 "io"
10 "io/ioutil" 10 "io/ioutil"
11 "log" 11 "log"
12 "os"
12 "reflect" 13 "reflect"
13 "sort" 14 "sort"
14 "strconv" 15 "strconv"
@@ -16,10 +17,12 @@ import (
16 "sync" 17 "sync"
17 18
18 "github.com/hashicorp/go-multierror" 19 "github.com/hashicorp/go-multierror"
20 "github.com/hashicorp/go-uuid"
19 "github.com/hashicorp/go-version" 21 "github.com/hashicorp/go-version"
20 "github.com/hashicorp/terraform/config" 22 "github.com/hashicorp/terraform/config"
21 "github.com/mitchellh/copystructure" 23 "github.com/mitchellh/copystructure"
22 "github.com/satori/go.uuid" 24
25 tfversion "github.com/hashicorp/terraform/version"
23) 26)
24 27
25const ( 28const (
@@ -664,7 +667,7 @@ func (s *State) FromFutureTerraform() bool {
664 } 667 }
665 668
666 v := version.Must(version.NewVersion(s.TFVersion)) 669 v := version.Must(version.NewVersion(s.TFVersion))
667 return SemVersion.LessThan(v) 670 return tfversion.SemVer.LessThan(v)
668} 671}
669 672
670func (s *State) Init() { 673func (s *State) Init() {
@@ -704,7 +707,11 @@ func (s *State) EnsureHasLineage() {
704 707
705func (s *State) ensureHasLineage() { 708func (s *State) ensureHasLineage() {
706 if s.Lineage == "" { 709 if s.Lineage == "" {
707 s.Lineage = uuid.NewV4().String() 710 lineage, err := uuid.GenerateUUID()
711 if err != nil {
712 panic(fmt.Errorf("Failed to generate lineage: %v", err))
713 }
714 s.Lineage = lineage
708 log.Printf("[DEBUG] New state was assigned lineage %q\n", s.Lineage) 715 log.Printf("[DEBUG] New state was assigned lineage %q\n", s.Lineage)
709 } else { 716 } else {
710 log.Printf("[TRACE] Preserving existing state lineage %q\n", s.Lineage) 717 log.Printf("[TRACE] Preserving existing state lineage %q\n", s.Lineage)
@@ -977,6 +984,10 @@ type ModuleState struct {
977 // always disjoint, so the path represents amodule tree 984 // always disjoint, so the path represents amodule tree
978 Path []string `json:"path"` 985 Path []string `json:"path"`
979 986
987 // Locals are kept only transiently in-memory, because we can always
988 // re-compute them.
989 Locals map[string]interface{} `json:"-"`
990
980 // Outputs declared by the module and maintained for each module 991 // Outputs declared by the module and maintained for each module
981 // even though only the root module technically needs to be kept. 992 // even though only the root module technically needs to be kept.
982 // This allows operators to inspect values at the boundaries. 993 // This allows operators to inspect values at the boundaries.
@@ -1083,7 +1094,7 @@ func (m *ModuleState) Orphans(c *config.Config) []string {
1083 defer m.Unlock() 1094 defer m.Unlock()
1084 1095
1085 keys := make(map[string]struct{}) 1096 keys := make(map[string]struct{})
1086 for k, _ := range m.Resources { 1097 for k := range m.Resources {
1087 keys[k] = struct{}{} 1098 keys[k] = struct{}{}
1088 } 1099 }
1089 1100
@@ -1091,7 +1102,7 @@ func (m *ModuleState) Orphans(c *config.Config) []string {
1091 for _, r := range c.Resources { 1102 for _, r := range c.Resources {
1092 delete(keys, r.Id()) 1103 delete(keys, r.Id())
1093 1104
1094 for k, _ := range keys { 1105 for k := range keys {
1095 if strings.HasPrefix(k, r.Id()+".") { 1106 if strings.HasPrefix(k, r.Id()+".") {
1096 delete(keys, k) 1107 delete(keys, k)
1097 } 1108 }
@@ -1100,7 +1111,32 @@ func (m *ModuleState) Orphans(c *config.Config) []string {
1100 } 1111 }
1101 1112
1102 result := make([]string, 0, len(keys)) 1113 result := make([]string, 0, len(keys))
1103 for k, _ := range keys { 1114 for k := range keys {
1115 result = append(result, k)
1116 }
1117
1118 return result
1119}
1120
1121// RemovedOutputs returns a list of outputs that are in the State but aren't
1122// present in the configuration itself.
1123func (m *ModuleState) RemovedOutputs(c *config.Config) []string {
1124 m.Lock()
1125 defer m.Unlock()
1126
1127 keys := make(map[string]struct{})
1128 for k := range m.Outputs {
1129 keys[k] = struct{}{}
1130 }
1131
1132 if c != nil {
1133 for _, o := range c.Outputs {
1134 delete(keys, o.Name)
1135 }
1136 }
1137
1138 result := make([]string, 0, len(keys))
1139 for k := range keys {
1104 result = append(result, k) 1140 result = append(result, k)
1105 } 1141 }
1106 1142
@@ -1308,6 +1344,10 @@ func (m *ModuleState) String() string {
1308 return buf.String() 1344 return buf.String()
1309} 1345}
1310 1346
1347func (m *ModuleState) Empty() bool {
1348 return len(m.Locals) == 0 && len(m.Outputs) == 0 && len(m.Resources) == 0
1349}
1350
1311// ResourceStateKey is a structured representation of the key used for the 1351// ResourceStateKey is a structured representation of the key used for the
1312// ModuleState.Resources mapping 1352// ModuleState.Resources mapping
1313type ResourceStateKey struct { 1353type ResourceStateKey struct {
@@ -1681,7 +1721,20 @@ func (s *InstanceState) Equal(other *InstanceState) bool {
1681 // We only do the deep check if both are non-nil. If one is nil 1721 // We only do the deep check if both are non-nil. If one is nil
1682 // we treat it as equal since their lengths are both zero (check 1722 // we treat it as equal since their lengths are both zero (check
1683 // above). 1723 // above).
1684 if !reflect.DeepEqual(s.Meta, other.Meta) { 1724 //
1725 // Since this can contain numeric values that may change types during
1726 // serialization, let's compare the serialized values.
1727 sMeta, err := json.Marshal(s.Meta)
1728 if err != nil {
1729 // marshaling primitives shouldn't ever error out
1730 panic(err)
1731 }
1732 otherMeta, err := json.Marshal(other.Meta)
1733 if err != nil {
1734 panic(err)
1735 }
1736
1737 if !bytes.Equal(sMeta, otherMeta) {
1685 return false 1738 return false
1686 } 1739 }
1687 } 1740 }
@@ -1824,11 +1877,19 @@ var ErrNoState = errors.New("no state")
1824// ReadState reads a state structure out of a reader in the format that 1877// ReadState reads a state structure out of a reader in the format that
1825// was written by WriteState. 1878// was written by WriteState.
1826func ReadState(src io.Reader) (*State, error) { 1879func ReadState(src io.Reader) (*State, error) {
1880 // check for a nil file specifically, since that produces a platform
1881 // specific error if we try to use it in a bufio.Reader.
1882 if f, ok := src.(*os.File); ok && f == nil {
1883 return nil, ErrNoState
1884 }
1885
1827 buf := bufio.NewReader(src) 1886 buf := bufio.NewReader(src)
1887
1828 if _, err := buf.Peek(1); err != nil { 1888 if _, err := buf.Peek(1); err != nil {
1829 // the error is either io.EOF or "invalid argument", and both are from 1889 if err == io.EOF {
1830 // an empty state. 1890 return nil, ErrNoState
1831 return nil, ErrNoState 1891 }
1892 return nil, err
1832 } 1893 }
1833 1894
1834 if err := testForV0State(buf); err != nil { 1895 if err := testForV0State(buf); err != nil {
@@ -1891,7 +1952,7 @@ func ReadState(src io.Reader) (*State, error) {
1891 result = v3State 1952 result = v3State
1892 default: 1953 default:
1893 return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", 1954 return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.",
1894 SemVersion.String(), versionIdentifier.Version) 1955 tfversion.SemVer.String(), versionIdentifier.Version)
1895 } 1956 }
1896 1957
1897 // If we reached this place we must have a result set 1958 // If we reached this place we must have a result set
@@ -1935,7 +1996,7 @@ func ReadStateV2(jsonBytes []byte) (*State, error) {
1935 // version that we don't understand 1996 // version that we don't understand
1936 if state.Version > StateVersion { 1997 if state.Version > StateVersion {
1937 return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", 1998 return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.",
1938 SemVersion.String(), state.Version) 1999 tfversion.SemVer.String(), state.Version)
1939 } 2000 }
1940 2001
1941 // Make sure the version is semantic 2002 // Make sure the version is semantic
@@ -1970,7 +2031,7 @@ func ReadStateV3(jsonBytes []byte) (*State, error) {
1970 // version that we don't understand 2031 // version that we don't understand
1971 if state.Version > StateVersion { 2032 if state.Version > StateVersion {
1972 return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", 2033 return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.",
1973 SemVersion.String(), state.Version) 2034 tfversion.SemVer.String(), state.Version)
1974 } 2035 }
1975 2036
1976 // Make sure the version is semantic 2037 // Make sure the version is semantic
@@ -2126,6 +2187,19 @@ func (s moduleStateSort) Swap(i, j int) {
2126 s[i], s[j] = s[j], s[i] 2187 s[i], s[j] = s[j], s[i]
2127} 2188}
2128 2189
2190// StateCompatible returns an error if the state is not compatible with the
2191// current version of terraform.
2192func CheckStateVersion(state *State) error {
2193 if state == nil {
2194 return nil
2195 }
2196
2197 if state.FromFutureTerraform() {
2198 return fmt.Errorf(stateInvalidTerraformVersionErr, state.TFVersion)
2199 }
2200 return nil
2201}
2202
2129const stateValidateErrMultiModule = ` 2203const stateValidateErrMultiModule = `
2130Multiple modules with the same path: %s 2204Multiple modules with the same path: %s
2131 2205
@@ -2134,3 +2208,11 @@ in your state file that point to the same module. This will cause Terraform
2134to behave in unexpected and error prone ways and is invalid. Please back up 2208to behave in unexpected and error prone ways and is invalid. Please back up
2135and modify your state file manually to resolve this. 2209and modify your state file manually to resolve this.
2136` 2210`
2211
2212const stateInvalidTerraformVersionErr = `
2213Terraform doesn't allow running any operations against a state
2214that was written by a future Terraform version. The state is
2215reporting it is written by Terraform '%s'
2216
2217Please run at least that version of Terraform to continue.
2218`
diff --git a/vendor/github.com/hashicorp/terraform/terraform/test_failure b/vendor/github.com/hashicorp/terraform/terraform/test_failure
deleted file mode 100644
index 5d3ad1a..0000000
--- a/vendor/github.com/hashicorp/terraform/terraform/test_failure
+++ /dev/null
@@ -1,9 +0,0 @@
1--- FAIL: TestContext2Plan_moduleProviderInherit (0.01s)
2 context_plan_test.go:552: bad: []string{"child"}
3map[string]dag.Vertex{}
4"module.middle.null"
5map[string]dag.Vertex{}
6"module.middle.module.inner.null"
7map[string]dag.Vertex{}
8"aws"
9FAIL
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform.go b/vendor/github.com/hashicorp/terraform/terraform/transform.go
index f4a431a..0e47f20 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/transform.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform.go
@@ -1,6 +1,8 @@
1package terraform 1package terraform
2 2
3import ( 3import (
4 "log"
5
4 "github.com/hashicorp/terraform/dag" 6 "github.com/hashicorp/terraform/dag"
5) 7)
6 8
@@ -40,6 +42,9 @@ func (t *graphTransformerMulti) Transform(g *Graph) error {
40 if err := t.Transform(g); err != nil { 42 if err := t.Transform(g); err != nil {
41 return err 43 return err
42 } 44 }
45 log.Printf(
46 "[TRACE] Graph after step %T:\n\n%s",
47 t, g.StringWithNodeTypes())
43 } 48 }
44 49
45 return nil 50 return nil
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_attach_config_provider.go b/vendor/github.com/hashicorp/terraform/terraform/transform_attach_config_provider.go
index 10506ea..39cf097 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/transform_attach_config_provider.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform_attach_config_provider.go
@@ -1,10 +1,7 @@
1package terraform 1package terraform
2 2
3import ( 3import (
4 "log"
5
6 "github.com/hashicorp/terraform/config" 4 "github.com/hashicorp/terraform/config"
7 "github.com/hashicorp/terraform/config/module"
8) 5)
9 6
10// GraphNodeAttachProvider is an interface that must be implemented by nodes 7// GraphNodeAttachProvider is an interface that must be implemented by nodes
@@ -19,62 +16,3 @@ type GraphNodeAttachProvider interface {
19 // Sets the configuration 16 // Sets the configuration
20 AttachProvider(*config.ProviderConfig) 17 AttachProvider(*config.ProviderConfig)
21} 18}
22
23// AttachProviderConfigTransformer goes through the graph and attaches
24// provider configuration structures to nodes that implement the interfaces
25// above.
26//
27// The attached configuration structures are directly from the configuration.
28// If they're going to be modified, a copy should be made.
29type AttachProviderConfigTransformer struct {
30 Module *module.Tree // Module is the root module for the config
31}
32
33func (t *AttachProviderConfigTransformer) Transform(g *Graph) error {
34 if err := t.attachProviders(g); err != nil {
35 return err
36 }
37
38 return nil
39}
40
41func (t *AttachProviderConfigTransformer) attachProviders(g *Graph) error {
42 // Go through and find GraphNodeAttachProvider
43 for _, v := range g.Vertices() {
44 // Only care about GraphNodeAttachProvider implementations
45 apn, ok := v.(GraphNodeAttachProvider)
46 if !ok {
47 continue
48 }
49
50 // Determine what we're looking for
51 path := normalizeModulePath(apn.Path())
52 path = path[1:]
53 name := apn.ProviderName()
54 log.Printf("[TRACE] Attach provider request: %#v %s", path, name)
55
56 // Get the configuration.
57 tree := t.Module.Child(path)
58 if tree == nil {
59 continue
60 }
61
62 // Go through the provider configs to find the matching config
63 for _, p := range tree.Config().ProviderConfigs {
64 // Build the name, which is "name.alias" if an alias exists
65 current := p.Name
66 if p.Alias != "" {
67 current += "." + p.Alias
68 }
69
70 // If the configs match then attach!
71 if current == name {
72 log.Printf("[TRACE] Attaching provider config: %#v", p)
73 apn.AttachProvider(p)
74 break
75 }
76 }
77 }
78
79 return nil
80}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_deposed.go b/vendor/github.com/hashicorp/terraform/terraform/transform_deposed.go
index 2148cef..87a1f9c 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/transform_deposed.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform_deposed.go
@@ -12,6 +12,9 @@ type DeposedTransformer struct {
12 // View, if non-empty, is the ModuleState.View used around the state 12 // View, if non-empty, is the ModuleState.View used around the state
13 // to find deposed resources. 13 // to find deposed resources.
14 View string 14 View string
15
16 // The provider used by the resourced which were deposed
17 ResolvedProvider string
15} 18}
16 19
17func (t *DeposedTransformer) Transform(g *Graph) error { 20func (t *DeposedTransformer) Transform(g *Graph) error {
@@ -33,14 +36,16 @@ func (t *DeposedTransformer) Transform(g *Graph) error {
33 if len(rs.Deposed) == 0 { 36 if len(rs.Deposed) == 0 {
34 continue 37 continue
35 } 38 }
39
36 deposed := rs.Deposed 40 deposed := rs.Deposed
37 41
38 for i, _ := range deposed { 42 for i, _ := range deposed {
39 g.Add(&graphNodeDeposedResource{ 43 g.Add(&graphNodeDeposedResource{
40 Index: i, 44 Index: i,
41 ResourceName: k, 45 ResourceName: k,
42 ResourceType: rs.Type, 46 ResourceType: rs.Type,
43 Provider: rs.Provider, 47 ProviderName: rs.Provider,
48 ResolvedProvider: t.ResolvedProvider,
44 }) 49 })
45 } 50 }
46 } 51 }
@@ -50,18 +55,23 @@ func (t *DeposedTransformer) Transform(g *Graph) error {
50 55
51// graphNodeDeposedResource is the graph vertex representing a deposed resource. 56// graphNodeDeposedResource is the graph vertex representing a deposed resource.
52type graphNodeDeposedResource struct { 57type graphNodeDeposedResource struct {
53 Index int 58 Index int
54 ResourceName string 59 ResourceName string
55 ResourceType string 60 ResourceType string
56 Provider string 61 ProviderName string
62 ResolvedProvider string
57} 63}
58 64
59func (n *graphNodeDeposedResource) Name() string { 65func (n *graphNodeDeposedResource) Name() string {
60 return fmt.Sprintf("%s (deposed #%d)", n.ResourceName, n.Index) 66 return fmt.Sprintf("%s (deposed #%d)", n.ResourceName, n.Index)
61} 67}
62 68
63func (n *graphNodeDeposedResource) ProvidedBy() []string { 69func (n *graphNodeDeposedResource) ProvidedBy() string {
64 return []string{resourceProvider(n.ResourceName, n.Provider)} 70 return resourceProvider(n.ResourceName, n.ProviderName)
71}
72
73func (n *graphNodeDeposedResource) SetProvider(p string) {
74 n.ResolvedProvider = p
65} 75}
66 76
67// GraphNodeEvalable impl. 77// GraphNodeEvalable impl.
@@ -81,7 +91,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
81 Node: &EvalSequence{ 91 Node: &EvalSequence{
82 Nodes: []EvalNode{ 92 Nodes: []EvalNode{
83 &EvalGetProvider{ 93 &EvalGetProvider{
84 Name: n.ProvidedBy()[0], 94 Name: n.ResolvedProvider,
85 Output: &provider, 95 Output: &provider,
86 }, 96 },
87 &EvalReadStateDeposed{ 97 &EvalReadStateDeposed{
@@ -98,7 +108,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
98 &EvalWriteStateDeposed{ 108 &EvalWriteStateDeposed{
99 Name: n.ResourceName, 109 Name: n.ResourceName,
100 ResourceType: n.ResourceType, 110 ResourceType: n.ResourceType,
101 Provider: n.Provider, 111 Provider: n.ResolvedProvider,
102 State: &state, 112 State: &state,
103 Index: n.Index, 113 Index: n.Index,
104 }, 114 },
@@ -114,7 +124,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
114 Node: &EvalSequence{ 124 Node: &EvalSequence{
115 Nodes: []EvalNode{ 125 Nodes: []EvalNode{
116 &EvalGetProvider{ 126 &EvalGetProvider{
117 Name: n.ProvidedBy()[0], 127 Name: n.ResolvedProvider,
118 Output: &provider, 128 Output: &provider,
119 }, 129 },
120 &EvalReadStateDeposed{ 130 &EvalReadStateDeposed{
@@ -147,7 +157,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
147 &EvalWriteStateDeposed{ 157 &EvalWriteStateDeposed{
148 Name: n.ResourceName, 158 Name: n.ResourceName,
149 ResourceType: n.ResourceType, 159 ResourceType: n.ResourceType,
150 Provider: n.Provider, 160 Provider: n.ResolvedProvider,
151 State: &state, 161 State: &state,
152 Index: n.Index, 162 Index: n.Index,
153 }, 163 },
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go b/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go
index 22be1ab..a06ff29 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go
@@ -119,17 +119,15 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
119 return &NodeApplyableProvider{NodeAbstractProvider: a} 119 return &NodeApplyableProvider{NodeAbstractProvider: a}
120 } 120 }
121 steps := []GraphTransformer{ 121 steps := []GraphTransformer{
122 // Add the local values
123 &LocalTransformer{Module: t.Module},
124
122 // Add outputs and metadata 125 // Add outputs and metadata
123 &OutputTransformer{Module: t.Module}, 126 &OutputTransformer{Module: t.Module},
124 &AttachResourceConfigTransformer{Module: t.Module}, 127 &AttachResourceConfigTransformer{Module: t.Module},
125 &AttachStateTransformer{State: t.State}, 128 &AttachStateTransformer{State: t.State},
126 129
127 // Add providers since they can affect destroy order as well 130 TransformProviders(nil, providerFn, t.Module),
128 &MissingProviderTransformer{AllowAny: true, Concrete: providerFn},
129 &ProviderTransformer{},
130 &DisableProviderTransformer{},
131 &ParentProviderTransformer{},
132 &AttachProviderConfigTransformer{Module: t.Module},
133 131
134 // Add all the variables. We can depend on resources through 132 // Add all the variables. We can depend on resources through
135 // variables due to module parameters, and we need to properly 133 // variables due to module parameters, and we need to properly
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go b/vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go
index 081df2f..fcbff65 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go
@@ -21,9 +21,9 @@ func (t *ImportStateTransformer) Transform(g *Graph) error {
21 } 21 }
22 22
23 nodes = append(nodes, &graphNodeImportState{ 23 nodes = append(nodes, &graphNodeImportState{
24 Addr: addr, 24 Addr: addr,
25 ID: target.ID, 25 ID: target.ID,
26 Provider: target.Provider, 26 ProviderName: target.Provider,
27 }) 27 })
28 } 28 }
29 29
@@ -36,9 +36,10 @@ func (t *ImportStateTransformer) Transform(g *Graph) error {
36} 36}
37 37
38type graphNodeImportState struct { 38type graphNodeImportState struct {
39 Addr *ResourceAddress // Addr is the resource address to import to 39 Addr *ResourceAddress // Addr is the resource address to import to
40 ID string // ID is the ID to import as 40 ID string // ID is the ID to import as
41 Provider string // Provider string 41 ProviderName string // Provider string
42 ResolvedProvider string // provider node address
42 43
43 states []*InstanceState 44 states []*InstanceState
44} 45}
@@ -47,8 +48,12 @@ func (n *graphNodeImportState) Name() string {
47 return fmt.Sprintf("%s (import id: %s)", n.Addr, n.ID) 48 return fmt.Sprintf("%s (import id: %s)", n.Addr, n.ID)
48} 49}
49 50
50func (n *graphNodeImportState) ProvidedBy() []string { 51func (n *graphNodeImportState) ProvidedBy() string {
51 return []string{resourceProvider(n.Addr.Type, n.Provider)} 52 return resourceProvider(n.Addr.Type, n.ProviderName)
53}
54
55func (n *graphNodeImportState) SetProvider(p string) {
56 n.ResolvedProvider = p
52} 57}
53 58
54// GraphNodeSubPath 59// GraphNodeSubPath
@@ -72,7 +77,7 @@ func (n *graphNodeImportState) EvalTree() EvalNode {
72 return &EvalSequence{ 77 return &EvalSequence{
73 Nodes: []EvalNode{ 78 Nodes: []EvalNode{
74 &EvalGetProvider{ 79 &EvalGetProvider{
75 Name: n.ProvidedBy()[0], 80 Name: n.ResolvedProvider,
76 Output: &provider, 81 Output: &provider,
77 }, 82 },
78 &EvalImportState{ 83 &EvalImportState{
@@ -149,10 +154,11 @@ func (n *graphNodeImportState) DynamicExpand(ctx EvalContext) (*Graph, error) {
149 // is safe. 154 // is safe.
150 for i, state := range n.states { 155 for i, state := range n.states {
151 g.Add(&graphNodeImportStateSub{ 156 g.Add(&graphNodeImportStateSub{
152 Target: addrs[i], 157 Target: addrs[i],
153 Path_: n.Path(), 158 Path_: n.Path(),
154 State: state, 159 State: state,
155 Provider: n.Provider, 160 ProviderName: n.ProviderName,
161 ResolvedProvider: n.ResolvedProvider,
156 }) 162 })
157 } 163 }
158 164
@@ -170,10 +176,11 @@ func (n *graphNodeImportState) DynamicExpand(ctx EvalContext) (*Graph, error) {
170// and is part of the subgraph. This node is responsible for refreshing 176// and is part of the subgraph. This node is responsible for refreshing
171// and adding a resource to the state once it is imported. 177// and adding a resource to the state once it is imported.
172type graphNodeImportStateSub struct { 178type graphNodeImportStateSub struct {
173 Target *ResourceAddress 179 Target *ResourceAddress
174 State *InstanceState 180 State *InstanceState
175 Path_ []string 181 Path_ []string
176 Provider string 182 ProviderName string
183 ResolvedProvider string
177} 184}
178 185
179func (n *graphNodeImportStateSub) Name() string { 186func (n *graphNodeImportStateSub) Name() string {
@@ -216,7 +223,7 @@ func (n *graphNodeImportStateSub) EvalTree() EvalNode {
216 return &EvalSequence{ 223 return &EvalSequence{
217 Nodes: []EvalNode{ 224 Nodes: []EvalNode{
218 &EvalGetProvider{ 225 &EvalGetProvider{
219 Name: resourceProvider(info.Type, n.Provider), 226 Name: n.ResolvedProvider,
220 Output: &provider, 227 Output: &provider,
221 }, 228 },
222 &EvalRefresh{ 229 &EvalRefresh{
@@ -233,7 +240,7 @@ func (n *graphNodeImportStateSub) EvalTree() EvalNode {
233 &EvalWriteState{ 240 &EvalWriteState{
234 Name: key.String(), 241 Name: key.String(),
235 ResourceType: info.Type, 242 ResourceType: info.Type,
236 Provider: resourceProvider(info.Type, n.Provider), 243 Provider: n.ResolvedProvider,
237 State: &state, 244 State: &state,
238 }, 245 },
239 }, 246 },
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_local.go b/vendor/github.com/hashicorp/terraform/terraform/transform_local.go
new file mode 100644
index 0000000..95ecfc0
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform_local.go
@@ -0,0 +1,40 @@
1package terraform
2
3import (
4 "github.com/hashicorp/terraform/config/module"
5)
6
7// LocalTransformer is a GraphTransformer that adds all the local values
8// from the configuration to the graph.
9type LocalTransformer struct {
10 Module *module.Tree
11}
12
13func (t *LocalTransformer) Transform(g *Graph) error {
14 return t.transformModule(g, t.Module)
15}
16
17func (t *LocalTransformer) transformModule(g *Graph, m *module.Tree) error {
18 if m == nil {
19 // Can't have any locals if there's no config
20 return nil
21 }
22
23 for _, local := range m.Config().Locals {
24 node := &NodeLocal{
25 PathValue: normalizeModulePath(m.Path()),
26 Config: local,
27 }
28
29 g.Add(node)
30 }
31
32 // Also populate locals for child modules
33 for _, c := range m.Children() {
34 if err := t.transformModule(g, c); err != nil {
35 return err
36 }
37 }
38
39 return nil
40}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_orphan_output.go b/vendor/github.com/hashicorp/terraform/terraform/transform_orphan_output.go
index 49568d5..aea2bd0 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/transform_orphan_output.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform_orphan_output.go
@@ -21,43 +21,32 @@ func (t *OrphanOutputTransformer) Transform(g *Graph) error {
21 return nil 21 return nil
22 } 22 }
23 23
24 return t.transform(g, t.Module) 24 for _, ms := range t.State.Modules {
25} 25 if err := t.transform(g, ms); err != nil {
26 26 return err
27func (t *OrphanOutputTransformer) transform(g *Graph, m *module.Tree) error {
28 // Get our configuration, and recurse into children
29 var c *config.Config
30 if m != nil {
31 c = m.Config()
32 for _, child := range m.Children() {
33 if err := t.transform(g, child); err != nil {
34 return err
35 }
36 } 27 }
37 } 28 }
29 return nil
30}
38 31
39 // Get the state. If there is no state, then we have no orphans! 32func (t *OrphanOutputTransformer) transform(g *Graph, ms *ModuleState) error {
40 path := normalizeModulePath(m.Path()) 33 if ms == nil {
41 state := t.State.ModuleByPath(path)
42 if state == nil {
43 return nil 34 return nil
44 } 35 }
45 36
46 // Make a map of the valid outputs 37 path := normalizeModulePath(ms.Path)
47 valid := make(map[string]struct{})
48 for _, o := range c.Outputs {
49 valid[o.Name] = struct{}{}
50 }
51 38
52 // Go through the outputs and find the ones that aren't in our config. 39 // Get the config for this path, which is nil if the entire module has been
53 for n, _ := range state.Outputs { 40 // removed.
54 // If it is in the valid map, then ignore 41 var c *config.Config
55 if _, ok := valid[n]; ok { 42 if m := t.Module.Child(path[1:]); m != nil {
56 continue 43 c = m.Config()
57 } 44 }
58 45
59 // Orphan! 46 // add all the orphaned outputs to the graph
47 for _, n := range ms.RemovedOutputs(c) {
60 g.Add(&NodeOutputOrphan{OutputName: n, PathValue: path}) 48 g.Add(&NodeOutputOrphan{OutputName: n, PathValue: path})
49
61 } 50 }
62 51
63 return nil 52 return nil
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_output.go b/vendor/github.com/hashicorp/terraform/terraform/transform_output.go
index b260f4c..faa25e4 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/transform_output.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform_output.go
@@ -1,7 +1,10 @@
1package terraform 1package terraform
2 2
3import ( 3import (
4 "log"
5
4 "github.com/hashicorp/terraform/config/module" 6 "github.com/hashicorp/terraform/config/module"
7 "github.com/hashicorp/terraform/dag"
5) 8)
6 9
7// OutputTransformer is a GraphTransformer that adds all the outputs 10// OutputTransformer is a GraphTransformer that adds all the outputs
@@ -41,11 +44,6 @@ func (t *OutputTransformer) transform(g *Graph, m *module.Tree) error {
41 44
42 // Add all outputs here 45 // Add all outputs here
43 for _, o := range os { 46 for _, o := range os {
44 // Build the node.
45 //
46 // NOTE: For now this is just an "applyable" output. As we build
47 // new graph builders for the other operations I suspect we'll
48 // find a way to parameterize this, require new transforms, etc.
49 node := &NodeApplyableOutput{ 47 node := &NodeApplyableOutput{
50 PathValue: normalizeModulePath(m.Path()), 48 PathValue: normalizeModulePath(m.Path()),
51 Config: o, 49 Config: o,
@@ -57,3 +55,41 @@ func (t *OutputTransformer) transform(g *Graph, m *module.Tree) error {
57 55
58 return nil 56 return nil
59} 57}
58
59// DestroyOutputTransformer is a GraphTransformer that adds nodes to delete
60// outputs during destroy. We need to do this to ensure that no stale outputs
61// are ever left in the state.
62type DestroyOutputTransformer struct {
63}
64
65func (t *DestroyOutputTransformer) Transform(g *Graph) error {
66 for _, v := range g.Vertices() {
67 output, ok := v.(*NodeApplyableOutput)
68 if !ok {
69 continue
70 }
71
72 // create the destroy node for this output
73 node := &NodeDestroyableOutput{
74 PathValue: output.PathValue,
75 Config: output.Config,
76 }
77
78 log.Printf("[TRACE] creating %s", node.Name())
79 g.Add(node)
80
81 deps, err := g.Descendents(v)
82 if err != nil {
83 return err
84 }
85
86 // the destroy node must depend on the eval node
87 deps.Add(v)
88
89 for _, d := range deps.List() {
90 log.Printf("[TRACE] %s depends on %s", node.Name(), dag.VertexName(d))
91 g.Connect(dag.BasicEdge(node, d))
92 }
93 }
94 return nil
95}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_provider.go b/vendor/github.com/hashicorp/terraform/terraform/transform_provider.go
index b9695d5..c4772b4 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/transform_provider.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform_provider.go
@@ -1,19 +1,46 @@
1package terraform 1package terraform
2 2
3import ( 3import (
4 "errors"
4 "fmt" 5 "fmt"
5 "log" 6 "log"
6 "strings" 7 "strings"
7 8
8 "github.com/hashicorp/go-multierror" 9 "github.com/hashicorp/go-multierror"
10 "github.com/hashicorp/terraform/config"
11 "github.com/hashicorp/terraform/config/module"
9 "github.com/hashicorp/terraform/dag" 12 "github.com/hashicorp/terraform/dag"
10) 13)
11 14
15func TransformProviders(providers []string, concrete ConcreteProviderNodeFunc, mod *module.Tree) GraphTransformer {
16 return GraphTransformMulti(
17 // Add providers from the config
18 &ProviderConfigTransformer{
19 Module: mod,
20 Providers: providers,
21 Concrete: concrete,
22 },
23 // Add any remaining missing providers
24 &MissingProviderTransformer{
25 Providers: providers,
26 Concrete: concrete,
27 },
28 // Connect the providers
29 &ProviderTransformer{},
30 // Remove unused providers and proxies
31 &PruneProviderTransformer{},
32 // Connect provider to their parent provider nodes
33 &ParentProviderTransformer{},
34 )
35}
36
12// GraphNodeProvider is an interface that nodes that can be a provider 37// GraphNodeProvider is an interface that nodes that can be a provider
13// must implement. The ProviderName returned is the name of the provider 38// must implement.
14// they satisfy. 39// ProviderName returns the name of the provider this satisfies.
40// Name returns the full name of the provider in the config.
15type GraphNodeProvider interface { 41type GraphNodeProvider interface {
16 ProviderName() string 42 ProviderName() string
43 Name() string
17} 44}
18 45
19// GraphNodeCloseProvider is an interface that nodes that can be a close 46// GraphNodeCloseProvider is an interface that nodes that can be a close
@@ -25,9 +52,12 @@ type GraphNodeCloseProvider interface {
25 52
26// GraphNodeProviderConsumer is an interface that nodes that require 53// GraphNodeProviderConsumer is an interface that nodes that require
27// a provider must implement. ProvidedBy must return the name of the provider 54// a provider must implement. ProvidedBy must return the name of the provider
28// to use. 55// to use. This may be a provider by type, type.alias or a fully resolved
56// provider name
29type GraphNodeProviderConsumer interface { 57type GraphNodeProviderConsumer interface {
30 ProvidedBy() []string 58 ProvidedBy() string
59 // Set the resolved provider address for this resource.
60 SetProvider(string)
31} 61}
32 62
33// ProviderTransformer is a GraphTransformer that maps resources to 63// ProviderTransformer is a GraphTransformer that maps resources to
@@ -41,18 +71,52 @@ func (t *ProviderTransformer) Transform(g *Graph) error {
41 m := providerVertexMap(g) 71 m := providerVertexMap(g)
42 for _, v := range g.Vertices() { 72 for _, v := range g.Vertices() {
43 if pv, ok := v.(GraphNodeProviderConsumer); ok { 73 if pv, ok := v.(GraphNodeProviderConsumer); ok {
44 for _, p := range pv.ProvidedBy() { 74 p := pv.ProvidedBy()
45 target := m[providerMapKey(p, pv)] 75
46 if target == nil { 76 key := providerMapKey(p, pv)
47 println(fmt.Sprintf("%#v\n\n%#v", m, providerMapKey(p, pv))) 77 target := m[key]
48 err = multierror.Append(err, fmt.Errorf( 78
49 "%s: provider %s couldn't be found", 79 sp, ok := pv.(GraphNodeSubPath)
50 dag.VertexName(v), p)) 80 if !ok && target == nil {
51 continue 81 // no target, and no path to walk up
82 err = multierror.Append(err, fmt.Errorf(
83 "%s: provider %s couldn't be found",
84 dag.VertexName(v), p))
85 break
86 }
87
88 // if we don't have a provider at this level, walk up the path looking for one
89 for i := 1; target == nil; i++ {
90 path := normalizeModulePath(sp.Path())
91 if len(path) < i {
92 break
93 }
94
95 key = ResolveProviderName(p, path[:len(path)-i])
96 target = m[key]
97 if target != nil {
98 break
52 } 99 }
100 }
101
102 if target == nil {
103 err = multierror.Append(err, fmt.Errorf(
104 "%s: configuration for %s is not present; a provider configuration block is required for all operations",
105 dag.VertexName(v), p,
106 ))
107 break
108 }
53 109
54 g.Connect(dag.BasicEdge(v, target)) 110 // see if this in an inherited provider
111 if p, ok := target.(*graphNodeProxyProvider); ok {
112 g.Remove(p)
113 target = p.Target()
114 key = target.(GraphNodeProvider).Name()
55 } 115 }
116
117 log.Printf("[DEBUG] resource %s using provider %s", dag.VertexName(pv), key)
118 pv.SetProvider(key)
119 g.Connect(dag.BasicEdge(v, target))
56 } 120 }
57 } 121 }
58 122
@@ -67,36 +131,32 @@ type CloseProviderTransformer struct{}
67 131
68func (t *CloseProviderTransformer) Transform(g *Graph) error { 132func (t *CloseProviderTransformer) Transform(g *Graph) error {
69 pm := providerVertexMap(g) 133 pm := providerVertexMap(g)
70 cpm := closeProviderVertexMap(g) 134 cpm := make(map[string]*graphNodeCloseProvider)
71 var err error 135 var err error
72 for _, v := range g.Vertices() {
73 if pv, ok := v.(GraphNodeProviderConsumer); ok {
74 for _, p := range pv.ProvidedBy() {
75 key := p
76 source := cpm[key]
77
78 if source == nil {
79 // Create a new graphNodeCloseProvider and add it to the graph
80 source = &graphNodeCloseProvider{ProviderNameValue: p}
81 g.Add(source)
82
83 // Close node needs to depend on provider
84 provider, ok := pm[key]
85 if !ok {
86 err = multierror.Append(err, fmt.Errorf(
87 "%s: provider %s couldn't be found for closing",
88 dag.VertexName(v), p))
89 continue
90 }
91 g.Connect(dag.BasicEdge(source, provider))
92
93 // Make sure we also add the new graphNodeCloseProvider to the map
94 // so we don't create and add any duplicate graphNodeCloseProviders.
95 cpm[key] = source
96 }
97 136
98 // Close node depends on all nodes provided by the provider 137 for _, v := range pm {
99 g.Connect(dag.BasicEdge(source, v)) 138 p := v.(GraphNodeProvider)
139
140 // get the close provider of this type if we alread created it
141 closer := cpm[p.Name()]
142
143 if closer == nil {
144 // create a closer for this provider type
145 closer = &graphNodeCloseProvider{ProviderNameValue: p.Name()}
146 g.Add(closer)
147 cpm[p.Name()] = closer
148 }
149
150 // Close node depends on the provider itself
151 // this is added unconditionally, so it will connect to all instances
152 // of the provider. Extra edges will be removed by transitive
153 // reduction.
154 g.Connect(dag.BasicEdge(closer, p))
155
156 // connect all the provider's resources to the close node
157 for _, s := range g.UpEdges(p).List() {
158 if _, ok := s.(GraphNodeProviderConsumer); ok {
159 g.Connect(dag.BasicEdge(closer, s))
100 } 160 }
101 } 161 }
102 } 162 }
@@ -104,18 +164,14 @@ func (t *CloseProviderTransformer) Transform(g *Graph) error {
104 return err 164 return err
105} 165}
106 166
107// MissingProviderTransformer is a GraphTransformer that adds nodes 167// MissingProviderTransformer is a GraphTransformer that adds nodes for all
108// for missing providers into the graph. Specifically, it creates provider 168// required providers into the graph. Specifically, it creates provider
109// configuration nodes for all the providers that we support. These are 169// configuration nodes for all the providers that we support. These are pruned
110// pruned later during an optimization pass. 170// later during an optimization pass.
111type MissingProviderTransformer struct { 171type MissingProviderTransformer struct {
112 // Providers is the list of providers we support. 172 // Providers is the list of providers we support.
113 Providers []string 173 Providers []string
114 174
115 // AllowAny will not check that a provider is supported before adding
116 // it to the graph.
117 AllowAny bool
118
119 // Concrete, if set, overrides how the providers are made. 175 // Concrete, if set, overrides how the providers are made.
120 Concrete ConcreteProviderNodeFunc 176 Concrete ConcreteProviderNodeFunc
121} 177}
@@ -128,99 +184,57 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error {
128 } 184 }
129 } 185 }
130 186
131 // Create a set of our supported providers 187 var err error
132 supported := make(map[string]struct{}, len(t.Providers))
133 for _, v := range t.Providers {
134 supported[v] = struct{}{}
135 }
136
137 // Get the map of providers we already have in our graph
138 m := providerVertexMap(g) 188 m := providerVertexMap(g)
139 189 for _, v := range g.Vertices() {
140 // Go through all the provider consumers and make sure we add
141 // that provider if it is missing. We use a for loop here instead
142 // of "range" since we'll modify check as we go to add more to check.
143 check := g.Vertices()
144 for i := 0; i < len(check); i++ {
145 v := check[i]
146
147 pv, ok := v.(GraphNodeProviderConsumer) 190 pv, ok := v.(GraphNodeProviderConsumer)
148 if !ok { 191 if !ok {
149 continue 192 continue
150 } 193 }
151 194
152 // If this node has a subpath, then we use that as a prefix 195 p := pv.ProvidedBy()
153 // into our map to check for an existing provider. 196 // this may be the resolved provider from the state, so we need to get
154 var path []string 197 // the base provider name.
155 if sp, ok := pv.(GraphNodeSubPath); ok { 198 parts := strings.SplitAfter(p, "provider.")
156 raw := normalizeModulePath(sp.Path()) 199 p = parts[len(parts)-1]
157 if len(raw) > len(rootModulePath) {
158 path = raw
159 }
160 }
161 200
162 for _, p := range pv.ProvidedBy() { 201 key := ResolveProviderName(p, nil)
163 key := providerMapKey(p, pv) 202 provider := m[key]
164 if _, ok := m[key]; ok {
165 // This provider already exists as a configure node
166 continue
167 }
168 203
169 // If the provider has an alias in it, we just want the type 204 // we already have it
170 ptype := p 205 if provider != nil {
171 if idx := strings.IndexRune(p, '.'); idx != -1 { 206 continue
172 ptype = p[:idx] 207 }
173 }
174 208
175 if !t.AllowAny { 209 // we don't implicitly create aliased providers
176 if _, ok := supported[ptype]; !ok { 210 if strings.Contains(p, ".") {
177 // If we don't support the provider type, skip it. 211 log.Println("[DEBUG] not adding missing provider alias:", p)
178 // Validation later will catch this as an error. 212 continue
179 continue 213 }
180 }
181 }
182 214
183 // Add the missing provider node to the graph 215 log.Println("[DEBUG] adding missing provider:", p)
184 v := t.Concrete(&NodeAbstractProvider{
185 NameValue: p,
186 PathValue: path,
187 }).(dag.Vertex)
188 if len(path) > 0 {
189 // We'll need the parent provider as well, so let's
190 // add a dummy node to check to make sure that we add
191 // that parent provider.
192 check = append(check, &graphNodeProviderConsumerDummy{
193 ProviderValue: p,
194 PathValue: path[:len(path)-1],
195 })
196 }
197 216
198 m[key] = g.Add(v) 217 // create the misisng top-level provider
199 } 218 provider = t.Concrete(&NodeAbstractProvider{
219 NameValue: p,
220 }).(dag.Vertex)
221
222 m[key] = g.Add(provider)
200 } 223 }
201 224
202 return nil 225 return err
203} 226}
204 227
205// ParentProviderTransformer connects provider nodes to their parents. 228// ParentProviderTransformer connects provider nodes to their parents.
206// 229//
207// This works by finding nodes that are both GraphNodeProviders and 230// This works by finding nodes that are both GraphNodeProviders and
208// GraphNodeSubPath. It then connects the providers to their parent 231// GraphNodeSubPath. It then connects the providers to their parent
209// path. 232// path. The parent provider is always at the root level.
210type ParentProviderTransformer struct{} 233type ParentProviderTransformer struct{}
211 234
212func (t *ParentProviderTransformer) Transform(g *Graph) error { 235func (t *ParentProviderTransformer) Transform(g *Graph) error {
213 // Make a mapping of path to dag.Vertex, where path is: "path.name" 236 pm := providerVertexMap(g)
214 m := make(map[string]dag.Vertex) 237 for _, v := range g.Vertices() {
215
216 // Also create a map that maps a provider to its parent
217 parentMap := make(map[dag.Vertex]string)
218 for _, raw := range g.Vertices() {
219 // If it is the flat version, then make it the non-flat version.
220 // We eventually want to get rid of the flat version entirely so
221 // this is a stop-gap while it still exists.
222 var v dag.Vertex = raw
223
224 // Only care about providers 238 // Only care about providers
225 pn, ok := v.(GraphNodeProvider) 239 pn, ok := v.(GraphNodeProvider)
226 if !ok || pn.ProviderName() == "" { 240 if !ok || pn.ProviderName() == "" {
@@ -228,53 +242,48 @@ func (t *ParentProviderTransformer) Transform(g *Graph) error {
228 } 242 }
229 243
230 // Also require a subpath, if there is no subpath then we 244 // Also require a subpath, if there is no subpath then we
231 // just totally ignore it. The expectation of this transform is 245 // can't have a parent.
232 // that it is used with a graph builder that is already flattened. 246 if pn, ok := v.(GraphNodeSubPath); ok {
233 var path []string 247 if len(normalizeModulePath(pn.Path())) <= 1 {
234 if pn, ok := raw.(GraphNodeSubPath); ok { 248 continue
235 path = pn.Path() 249 }
236 }
237 path = normalizeModulePath(path)
238
239 // Build the key with path.name i.e. "child.subchild.aws"
240 key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName())
241 m[key] = raw
242
243 // Determine the parent if we're non-root. This is length 1 since
244 // the 0 index should be "root" since we normalize above.
245 if len(path) > 1 {
246 path = path[:len(path)-1]
247 key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName())
248 parentMap[raw] = key
249 } 250 }
250 }
251 251
252 // Connect! 252 // this provider may be disabled, but we can only get it's name from
253 for v, key := range parentMap { 253 // the ProviderName string
254 if parent, ok := m[key]; ok { 254 name := ResolveProviderName(strings.SplitN(pn.ProviderName(), " ", 2)[0], nil)
255 parent := pm[name]
256 if parent != nil {
255 g.Connect(dag.BasicEdge(v, parent)) 257 g.Connect(dag.BasicEdge(v, parent))
256 } 258 }
257 }
258 259
260 }
259 return nil 261 return nil
260} 262}
261 263
262// PruneProviderTransformer is a GraphTransformer that prunes all the 264// PruneProviderTransformer removes any providers that are not actually used by
263// providers that aren't needed from the graph. A provider is unneeded if 265// anything, and provider proxies. This avoids the provider being initialized
264// no resource or module is using that provider. 266// and configured. This both saves resources but also avoids errors since
267// configuration may imply initialization which may require auth.
265type PruneProviderTransformer struct{} 268type PruneProviderTransformer struct{}
266 269
267func (t *PruneProviderTransformer) Transform(g *Graph) error { 270func (t *PruneProviderTransformer) Transform(g *Graph) error {
268 for _, v := range g.Vertices() { 271 for _, v := range g.Vertices() {
269 // We only care about the providers 272 // We only care about providers
270 if pn, ok := v.(GraphNodeProvider); !ok || pn.ProviderName() == "" { 273 pn, ok := v.(GraphNodeProvider)
274 if !ok || pn.ProviderName() == "" {
271 continue 275 continue
272 } 276 }
273 // Does anything depend on this? If not, then prune it. 277
274 if s := g.UpEdges(v); s.Len() == 0 { 278 // ProxyProviders will have up edges, but we're now done with them in the graph
275 if nv, ok := v.(dag.NamedVertex); ok { 279 if _, ok := v.(*graphNodeProxyProvider); ok {
276 log.Printf("[DEBUG] Pruning provider with no dependencies: %s", nv.Name()) 280 log.Printf("[DEBUG] pruning proxy provider %s", dag.VertexName(v))
277 } 281 g.Remove(v)
282 }
283
284 // Remove providers with no dependencies.
285 if g.UpEdges(v).Len() == 0 {
286 log.Printf("[DEBUG] pruning unused provider %s", dag.VertexName(v))
278 g.Remove(v) 287 g.Remove(v)
279 } 288 }
280 } 289 }
@@ -285,23 +294,26 @@ func (t *PruneProviderTransformer) Transform(g *Graph) error {
285// providerMapKey is a helper that gives us the key to use for the 294// providerMapKey is a helper that gives us the key to use for the
286// maps returned by things such as providerVertexMap. 295// maps returned by things such as providerVertexMap.
287func providerMapKey(k string, v dag.Vertex) string { 296func providerMapKey(k string, v dag.Vertex) string {
288 pathPrefix := "" 297 if strings.Contains(k, "provider.") {
289 if sp, ok := v.(GraphNodeSubPath); ok { 298 // this is already resolved
290 raw := normalizeModulePath(sp.Path()) 299 return k
291 if len(raw) > len(rootModulePath) {
292 pathPrefix = modulePrefixStr(raw) + "."
293 }
294 } 300 }
295 301
296 return pathPrefix + k 302 // we create a dummy provider to
303 var path []string
304 if sp, ok := v.(GraphNodeSubPath); ok {
305 path = normalizeModulePath(sp.Path())
306 }
307 return ResolveProviderName(k, path)
297} 308}
298 309
299func providerVertexMap(g *Graph) map[string]dag.Vertex { 310func providerVertexMap(g *Graph) map[string]dag.Vertex {
300 m := make(map[string]dag.Vertex) 311 m := make(map[string]dag.Vertex)
301 for _, v := range g.Vertices() { 312 for _, v := range g.Vertices() {
302 if pv, ok := v.(GraphNodeProvider); ok { 313 if pv, ok := v.(GraphNodeProvider); ok {
303 key := providerMapKey(pv.ProviderName(), v) 314 // TODO: The Name may have meta info, like " (disabled)"
304 m[key] = v 315 name := strings.SplitN(pv.Name(), " ", 2)[0]
316 m[name] = v
305 } 317 }
306 } 318 }
307 319
@@ -324,7 +336,7 @@ type graphNodeCloseProvider struct {
324} 336}
325 337
326func (n *graphNodeCloseProvider) Name() string { 338func (n *graphNodeCloseProvider) Name() string {
327 return fmt.Sprintf("provider.%s (close)", n.ProviderNameValue) 339 return n.ProviderNameValue + " (close)"
328} 340}
329 341
330// GraphNodeEvalable impl. 342// GraphNodeEvalable impl.
@@ -362,19 +374,233 @@ func (n *graphNodeCloseProvider) RemoveIfNotTargeted() bool {
362 return true 374 return true
363} 375}
364 376
365// graphNodeProviderConsumerDummy is a struct that never enters the real 377// graphNodeProxyProvider is a GraphNodeProvider implementation that is used to
366// graph (though it could to no ill effect). It implements 378// store the name and value of a provider node for inheritance between modules.
367// GraphNodeProviderConsumer and GraphNodeSubpath as a way to force 379// These nodes are only used to store the data while loading the provider
368// certain transformations. 380// configurations, and are removed after all the resources have been connected
369type graphNodeProviderConsumerDummy struct { 381// to their providers.
370 ProviderValue string 382type graphNodeProxyProvider struct {
371 PathValue []string 383 nameValue string
384 path []string
385 target GraphNodeProvider
386}
387
388func (n *graphNodeProxyProvider) ProviderName() string {
389 return n.Target().ProviderName()
390}
391
392func (n *graphNodeProxyProvider) Name() string {
393 return ResolveProviderName(n.nameValue, n.path)
394}
395
396// find the concrete provider instance
397func (n *graphNodeProxyProvider) Target() GraphNodeProvider {
398 switch t := n.target.(type) {
399 case *graphNodeProxyProvider:
400 return t.Target()
401 default:
402 return n.target
403 }
404}
405
406// ProviderConfigTransformer adds all provider nodes from the configuration and
407// attaches the configs.
408type ProviderConfigTransformer struct {
409 Providers []string
410 Concrete ConcreteProviderNodeFunc
411
412 // each provider node is stored here so that the proxy nodes can look up
413 // their targets by name.
414 providers map[string]GraphNodeProvider
415 // record providers that can be overriden with a proxy
416 proxiable map[string]bool
417
418 // Module is the module to add resources from.
419 Module *module.Tree
372} 420}
373 421
374func (n *graphNodeProviderConsumerDummy) Path() []string { 422func (t *ProviderConfigTransformer) Transform(g *Graph) error {
375 return n.PathValue 423 // If no module is given, we don't do anything
424 if t.Module == nil {
425 return nil
426 }
427
428 // If the module isn't loaded, that is simply an error
429 if !t.Module.Loaded() {
430 return errors.New("module must be loaded for ProviderConfigTransformer")
431 }
432
433 t.providers = make(map[string]GraphNodeProvider)
434 t.proxiable = make(map[string]bool)
435
436 // Start the transformation process
437 if err := t.transform(g, t.Module); err != nil {
438 return err
439 }
440
441 // finally attach the configs to the new nodes
442 return t.attachProviderConfigs(g)
376} 443}
377 444
378func (n *graphNodeProviderConsumerDummy) ProvidedBy() []string { 445func (t *ProviderConfigTransformer) transform(g *Graph, m *module.Tree) error {
379 return []string{n.ProviderValue} 446 // If no config, do nothing
447 if m == nil {
448 return nil
449 }
450
451 // Add our resources
452 if err := t.transformSingle(g, m); err != nil {
453 return err
454 }
455
456 // Transform all the children.
457 for _, c := range m.Children() {
458 if err := t.transform(g, c); err != nil {
459 return err
460 }
461 }
462 return nil
463}
464
465func (t *ProviderConfigTransformer) transformSingle(g *Graph, m *module.Tree) error {
466 log.Printf("[TRACE] ProviderConfigTransformer: Starting for path: %v", m.Path())
467
468 // Get the configuration for this module
469 conf := m.Config()
470
471 // Build the path we're at
472 path := m.Path()
473 if len(path) > 0 {
474 path = append([]string{RootModuleName}, path...)
475 }
476
477 // add all providers from the configuration
478 for _, p := range conf.ProviderConfigs {
479 name := p.Name
480 if p.Alias != "" {
481 name += "." + p.Alias
482 }
483
484 v := t.Concrete(&NodeAbstractProvider{
485 NameValue: name,
486 PathValue: path,
487 })
488
489 // Add it to the graph
490 g.Add(v)
491 fullName := ResolveProviderName(name, path)
492 t.providers[fullName] = v.(GraphNodeProvider)
493 t.proxiable[fullName] = len(p.RawConfig.RawMap()) == 0
494 }
495
496 // Now replace the provider nodes with proxy nodes if a provider was being
497 // passed in, and create implicit proxies if there was no config. Any extra
498 // proxies will be removed in the prune step.
499 return t.addProxyProviders(g, m)
500}
501
502func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, m *module.Tree) error {
503 path := m.Path()
504
505 // can't add proxies at the root
506 if len(path) == 0 {
507 return nil
508 }
509
510 parentPath := path[:len(path)-1]
511 parent := t.Module.Child(parentPath)
512 if parent == nil {
513 return nil
514 }
515
516 var parentCfg *config.Module
517 for _, mod := range parent.Config().Modules {
518 if mod.Name == m.Name() {
519 parentCfg = mod
520 break
521 }
522 }
523
524 if parentCfg == nil {
525 // this can't really happen during normal execution.
526 return fmt.Errorf("parent module config not found for %s", m.Name())
527 }
528
529 // Go through all the providers the parent is passing in, and add proxies to
530 // the parent provider nodes.
531 for name, parentName := range parentCfg.Providers {
532 fullName := ResolveProviderName(name, path)
533 fullParentName := ResolveProviderName(parentName, parentPath)
534
535 parentProvider := t.providers[fullParentName]
536
537 if parentProvider == nil {
538 return fmt.Errorf("missing provider %s", fullParentName)
539 }
540
541 proxy := &graphNodeProxyProvider{
542 nameValue: name,
543 path: path,
544 target: parentProvider,
545 }
546
547 concreteProvider := t.providers[fullName]
548
549 // replace the concrete node with the provider passed in
550 if concreteProvider != nil && t.proxiable[fullName] {
551 g.Replace(concreteProvider, proxy)
552 t.providers[fullName] = proxy
553 continue
554 }
555
556 // aliased providers can't be implicitly passed in
557 if strings.Contains(name, ".") {
558 continue
559 }
560
561 // There was no concrete provider, so add this as an implicit provider.
562 // The extra proxy will be pruned later if it's unused.
563 g.Add(proxy)
564 t.providers[fullName] = proxy
565 }
566 return nil
567}
568
569func (t *ProviderConfigTransformer) attachProviderConfigs(g *Graph) error {
570 for _, v := range g.Vertices() {
571 // Only care about GraphNodeAttachProvider implementations
572 apn, ok := v.(GraphNodeAttachProvider)
573 if !ok {
574 continue
575 }
576
577 // Determine what we're looking for
578 path := normalizeModulePath(apn.Path())[1:]
579 name := apn.ProviderName()
580 log.Printf("[TRACE] Attach provider request: %#v %s", path, name)
581
582 // Get the configuration.
583 tree := t.Module.Child(path)
584 if tree == nil {
585 continue
586 }
587
588 // Go through the provider configs to find the matching config
589 for _, p := range tree.Config().ProviderConfigs {
590 // Build the name, which is "name.alias" if an alias exists
591 current := p.Name
592 if p.Alias != "" {
593 current += "." + p.Alias
594 }
595
596 // If the configs match then attach!
597 if current == name {
598 log.Printf("[TRACE] Attaching provider config: %#v", p)
599 apn.AttachProvider(p)
600 break
601 }
602 }
603 }
604
605 return nil
380} 606}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_provider_disable.go b/vendor/github.com/hashicorp/terraform/terraform/transform_provider_disable.go
deleted file mode 100644
index d9919f3..0000000
--- a/vendor/github.com/hashicorp/terraform/terraform/transform_provider_disable.go
+++ /dev/null
@@ -1,50 +0,0 @@
1package terraform
2
3import (
4 "fmt"
5
6 "github.com/hashicorp/terraform/dag"
7)
8
9// DisableProviderTransformer "disables" any providers that are not actually
10// used by anything. This avoids the provider being initialized and configured.
11// This both saves resources but also avoids errors since configuration
12// may imply initialization which may require auth.
13type DisableProviderTransformer struct{}
14
15func (t *DisableProviderTransformer) Transform(g *Graph) error {
16 for _, v := range g.Vertices() {
17 // We only care about providers
18 pn, ok := v.(GraphNodeProvider)
19 if !ok || pn.ProviderName() == "" {
20 continue
21 }
22
23 // If we have dependencies, then don't disable
24 if g.UpEdges(v).Len() > 0 {
25 continue
26 }
27
28 // Get the path
29 var path []string
30 if pn, ok := v.(GraphNodeSubPath); ok {
31 path = pn.Path()
32 }
33
34 // Disable the provider by replacing it with a "disabled" provider
35 disabled := &NodeDisabledProvider{
36 NodeAbstractProvider: &NodeAbstractProvider{
37 NameValue: pn.ProviderName(),
38 PathValue: path,
39 },
40 }
41
42 if !g.Replace(v, disabled) {
43 panic(fmt.Sprintf(
44 "vertex disappeared from under us: %s",
45 dag.VertexName(v)))
46 }
47 }
48
49 return nil
50}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_reference.go b/vendor/github.com/hashicorp/terraform/terraform/transform_reference.go
index c545235..be8c7f9 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/transform_reference.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform_reference.go
@@ -76,6 +76,85 @@ func (t *ReferenceTransformer) Transform(g *Graph) error {
76 return nil 76 return nil
77} 77}
78 78
79// DestroyReferenceTransformer is a GraphTransformer that reverses the edges
80// for locals and outputs that depend on other nodes which will be
81// removed during destroy. If a destroy node is evaluated before the local or
82// output value, it will be removed from the state, and the later interpolation
83// will fail.
84type DestroyValueReferenceTransformer struct{}
85
86func (t *DestroyValueReferenceTransformer) Transform(g *Graph) error {
87 vs := g.Vertices()
88 for _, v := range vs {
89 switch v.(type) {
90 case *NodeApplyableOutput, *NodeLocal:
91 // OK
92 default:
93 continue
94 }
95
96 // reverse any outgoing edges so that the value is evaluated first.
97 for _, e := range g.EdgesFrom(v) {
98 target := e.Target()
99
100 // only destroy nodes will be evaluated in reverse
101 if _, ok := target.(GraphNodeDestroyer); !ok {
102 continue
103 }
104
105 log.Printf("[TRACE] output dep: %s", dag.VertexName(target))
106
107 g.RemoveEdge(e)
108 g.Connect(&DestroyEdge{S: target, T: v})
109 }
110 }
111
112 return nil
113}
114
115// PruneUnusedValuesTransformer is s GraphTransformer that removes local and
116// output values which are not referenced in the graph. Since outputs and
117// locals always need to be evaluated, if they reference a resource that is not
118// available in the state the interpolation could fail.
119type PruneUnusedValuesTransformer struct{}
120
121func (t *PruneUnusedValuesTransformer) Transform(g *Graph) error {
122 // this might need multiple runs in order to ensure that pruning a value
123 // doesn't effect a previously checked value.
124 for removed := 0; ; removed = 0 {
125 for _, v := range g.Vertices() {
126 switch v.(type) {
127 case *NodeApplyableOutput, *NodeLocal:
128 // OK
129 default:
130 continue
131 }
132
133 dependants := g.UpEdges(v)
134
135 switch dependants.Len() {
136 case 0:
137 // nothing at all depends on this
138 g.Remove(v)
139 removed++
140 case 1:
141 // because an output's destroy node always depends on the output,
142 // we need to check for the case of a single destroy node.
143 d := dependants.List()[0]
144 if _, ok := d.(*NodeDestroyableOutput); ok {
145 g.Remove(v)
146 removed++
147 }
148 }
149 }
150 if removed == 0 {
151 break
152 }
153 }
154
155 return nil
156}
157
79// ReferenceMap is a structure that can be used to efficiently check 158// ReferenceMap is a structure that can be used to efficiently check
80// for references on a graph. 159// for references on a graph.
81type ReferenceMap struct { 160type ReferenceMap struct {
@@ -96,6 +175,7 @@ func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []string) {
96 var matches []dag.Vertex 175 var matches []dag.Vertex
97 var missing []string 176 var missing []string
98 prefix := m.prefix(v) 177 prefix := m.prefix(v)
178
99 for _, ns := range rn.References() { 179 for _, ns := range rn.References() {
100 found := false 180 found := false
101 for _, n := range strings.Split(ns, "/") { 181 for _, n := range strings.Split(ns, "/") {
@@ -108,19 +188,14 @@ func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []string) {
108 // Mark that we found a match 188 // Mark that we found a match
109 found = true 189 found = true
110 190
111 // Make sure this isn't a self reference, which isn't included
112 selfRef := false
113 for _, p := range parents { 191 for _, p := range parents {
192 // don't include self-references
114 if p == v { 193 if p == v {
115 selfRef = true 194 continue
116 break
117 } 195 }
118 } 196 matches = append(matches, p)
119 if selfRef {
120 continue
121 } 197 }
122 198
123 matches = append(matches, parents...)
124 break 199 break
125 } 200 }
126 201
@@ -296,14 +371,21 @@ func ReferenceFromInterpolatedVar(v config.InterpolatedVariable) []string {
296 return []string{fmt.Sprintf("%s.%d/%s.N", id, idx, id)} 371 return []string{fmt.Sprintf("%s.%d/%s.N", id, idx, id)}
297 case *config.UserVariable: 372 case *config.UserVariable:
298 return []string{fmt.Sprintf("var.%s", v.Name)} 373 return []string{fmt.Sprintf("var.%s", v.Name)}
374 case *config.LocalVariable:
375 return []string{fmt.Sprintf("local.%s", v.Name)}
299 default: 376 default:
300 return nil 377 return nil
301 } 378 }
302} 379}
303 380
304func modulePrefixStr(p []string) string { 381func modulePrefixStr(p []string) string {
382 // strip "root"
383 if len(p) > 0 && p[0] == rootModulePath[0] {
384 p = p[1:]
385 }
386
305 parts := make([]string, 0, len(p)*2) 387 parts := make([]string, 0, len(p)*2)
306 for _, p := range p[1:] { 388 for _, p := range p {
307 parts = append(parts, "module", p) 389 parts = append(parts, "module", p)
308 } 390 }
309 391
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_removed_modules.go b/vendor/github.com/hashicorp/terraform/terraform/transform_removed_modules.go
new file mode 100644
index 0000000..2e05edb
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform_removed_modules.go
@@ -0,0 +1,32 @@
1package terraform
2
3import (
4 "log"
5
6 "github.com/hashicorp/terraform/config/module"
7)
8
9// RemoveModuleTransformer implements GraphTransformer to add nodes indicating
10// when a module was removed from the configuration.
11type RemovedModuleTransformer struct {
12 Module *module.Tree // root module
13 State *State
14}
15
16func (t *RemovedModuleTransformer) Transform(g *Graph) error {
17 // nothing to remove if there's no state!
18 if t.State == nil {
19 return nil
20 }
21
22 for _, m := range t.State.Modules {
23 c := t.Module.Child(m.Path[1:])
24 if c != nil {
25 continue
26 }
27
28 log.Printf("[DEBUG] module %s no longer in config\n", modulePrefixStr(m.Path))
29 g.Add(&NodeModuleRemoved{PathValue: m.Path})
30 }
31 return nil
32}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_resource_count.go b/vendor/github.com/hashicorp/terraform/terraform/transform_resource_count.go
index cda35cb..e528b37 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/transform_resource_count.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform_resource_count.go
@@ -37,7 +37,9 @@ func (t *ResourceCountTransformer) Transform(g *Graph) error {
37 addr.Index = index 37 addr.Index = index
38 38
39 // Build the abstract node and the concrete one 39 // Build the abstract node and the concrete one
40 abstract := &NodeAbstractResource{Addr: addr} 40 abstract := &NodeAbstractResource{
41 Addr: addr,
42 }
41 var node dag.Vertex = abstract 43 var node dag.Vertex = abstract
42 if f := t.Concrete; f != nil { 44 if f := t.Concrete; f != nil {
43 node = f(abstract) 45 node = f(abstract)
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_targets.go b/vendor/github.com/hashicorp/terraform/terraform/transform_targets.go
index 4f117b4..af6defe 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/transform_targets.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform_targets.go
@@ -73,9 +73,11 @@ func (t *TargetsTransformer) Transform(g *Graph) error {
73 if _, ok := v.(GraphNodeResource); ok { 73 if _, ok := v.(GraphNodeResource); ok {
74 removable = true 74 removable = true
75 } 75 }
76
76 if vr, ok := v.(RemovableIfNotTargeted); ok { 77 if vr, ok := v.(RemovableIfNotTargeted); ok {
77 removable = vr.RemoveIfNotTargeted() 78 removable = vr.RemoveIfNotTargeted()
78 } 79 }
80
79 if removable && !targetedNodes.Include(v) { 81 if removable && !targetedNodes.Include(v) {
80 log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v)) 82 log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v))
81 g.Remove(v) 83 g.Remove(v)
@@ -135,7 +137,10 @@ func (t *TargetsTransformer) selectTargetedNodes(
135 } 137 }
136 } 138 }
137 } 139 }
140 return t.addDependencies(targetedNodes, g)
141}
138 142
143func (t *TargetsTransformer) addDependencies(targetedNodes *dag.Set, g *Graph) (*dag.Set, error) {
139 // Handle nodes that need to be included if their dependencies are included. 144 // Handle nodes that need to be included if their dependencies are included.
140 // This requires multiple passes since we need to catch transitive 145 // This requires multiple passes since we need to catch transitive
141 // dependencies if and only if they are via other nodes that also 146 // dependencies if and only if they are via other nodes that also
@@ -157,11 +162,6 @@ func (t *TargetsTransformer) selectTargetedNodes(
157 } 162 }
158 163
159 dependers = dependers.Filter(func(dv interface{}) bool { 164 dependers = dependers.Filter(func(dv interface{}) bool {
160 // Can ignore nodes that are already targeted
161 /*if targetedNodes.Include(dv) {
162 return false
163 }*/
164
165 _, ok := dv.(GraphNodeTargetDownstream) 165 _, ok := dv.(GraphNodeTargetDownstream)
166 return ok 166 return ok
167 }) 167 })
@@ -180,6 +180,7 @@ func (t *TargetsTransformer) selectTargetedNodes(
180 // depending on in case that informs its decision about whether 180 // depending on in case that informs its decision about whether
181 // it is safe to be targeted. 181 // it is safe to be targeted.
182 deps := g.DownEdges(v) 182 deps := g.DownEdges(v)
183
183 depsTargeted := deps.Intersection(targetedNodes) 184 depsTargeted := deps.Intersection(targetedNodes)
184 depsUntargeted := deps.Difference(depsTargeted) 185 depsUntargeted := deps.Difference(depsTargeted)
185 186
@@ -193,7 +194,50 @@ func (t *TargetsTransformer) selectTargetedNodes(
193 } 194 }
194 } 195 }
195 196
196 return targetedNodes, nil 197 return targetedNodes.Filter(func(dv interface{}) bool {
198 return filterPartialOutputs(dv, targetedNodes, g)
199 }), nil
200}
201
202// Outputs may have been included transitively, but if any of their
203// dependencies have been pruned they won't be resolvable.
204// If nothing depends on the output, and the output is missing any
205// dependencies, remove it from the graph.
206// This essentially maintains the previous behavior where interpolation in
207// outputs would fail silently, but can now surface errors where the output
208// is required.
209func filterPartialOutputs(v interface{}, targetedNodes *dag.Set, g *Graph) bool {
210 // should this just be done with TargetDownstream?
211 if _, ok := v.(*NodeApplyableOutput); !ok {
212 return true
213 }
214
215 dependers := g.UpEdges(v)
216 for _, d := range dependers.List() {
217 if _, ok := d.(*NodeCountBoundary); ok {
218 continue
219 }
220
221 if !targetedNodes.Include(d) {
222 // this one is going to be removed, so it doesn't count
223 continue
224 }
225
226 // as soon as we see a real dependency, we mark this as
227 // non-removable
228 return true
229 }
230
231 depends := g.DownEdges(v)
232
233 for _, d := range depends.List() {
234 if !targetedNodes.Include(d) {
235 log.Printf("[WARN] %s missing targeted dependency %s, removing from the graph",
236 dag.VertexName(v), dag.VertexName(d))
237 return false
238 }
239 }
240 return true
197} 241}
198 242
199func (t *TargetsTransformer) nodeIsTarget( 243func (t *TargetsTransformer) nodeIsTarget(
diff --git a/vendor/github.com/hashicorp/terraform/terraform/ui_output_mock.go b/vendor/github.com/hashicorp/terraform/terraform/ui_output_mock.go
index 7852bc4..d828c92 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/ui_output_mock.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/ui_output_mock.go
@@ -1,13 +1,18 @@
1package terraform 1package terraform
2 2
3import "sync"
4
3// MockUIOutput is an implementation of UIOutput that can be used for tests. 5// MockUIOutput is an implementation of UIOutput that can be used for tests.
4type MockUIOutput struct { 6type MockUIOutput struct {
7 sync.Mutex
5 OutputCalled bool 8 OutputCalled bool
6 OutputMessage string 9 OutputMessage string
7 OutputFn func(string) 10 OutputFn func(string)
8} 11}
9 12
10func (o *MockUIOutput) Output(v string) { 13func (o *MockUIOutput) Output(v string) {
14 o.Lock()
15 defer o.Unlock()
11 o.OutputCalled = true 16 o.OutputCalled = true
12 o.OutputMessage = v 17 o.OutputMessage = v
13 if o.OutputFn != nil { 18 if o.OutputFn != nil {
diff --git a/vendor/github.com/hashicorp/terraform/terraform/user_agent.go b/vendor/github.com/hashicorp/terraform/terraform/user_agent.go
index 700be2a..a42613e 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/user_agent.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/user_agent.go
@@ -1,14 +1,13 @@
1package terraform 1package terraform
2 2
3import ( 3import (
4 "fmt" 4 "github.com/hashicorp/terraform/httpclient"
5 "runtime"
6) 5)
7 6
8// The standard Terraform User-Agent format
9const UserAgent = "Terraform %s (%s)"
10
11// Generate a UserAgent string 7// Generate a UserAgent string
8//
9// Deprecated: Use httpclient.UserAgentString if you are setting your
10// own User-Agent header.
12func UserAgentString() string { 11func UserAgentString() string {
13 return fmt.Sprintf(UserAgent, VersionString(), runtime.Version()) 12 return httpclient.UserAgentString()
14} 13}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/version.go b/vendor/github.com/hashicorp/terraform/terraform/version.go
index d61b11e..ac73015 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/version.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/version.go
@@ -1,31 +1,10 @@
1package terraform 1package terraform
2 2
3import ( 3import (
4 "fmt" 4 "github.com/hashicorp/terraform/version"
5
6 "github.com/hashicorp/go-version"
7) 5)
8 6
9// The main version number that is being run at the moment. 7// TODO: update providers to use the version package directly
10const Version = "0.10.0"
11
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
14// such as "dev" (in development), "beta", "rc1", etc.
15var VersionPrerelease = "dev"
16
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
19// proper semantic version, which should always be the case.
20var SemVersion = version.Must(version.NewVersion(Version))
21
22// VersionHeader is the header name used to send the current terraform version
23// in http requests.
24const VersionHeader = "Terraform-Version"
25
26func VersionString() string { 8func VersionString() string {
27 if VersionPrerelease != "" { 9 return version.String()
28 return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
29 }
30 return Version
31} 10}
diff --git a/vendor/github.com/hashicorp/terraform/terraform/version_required.go b/vendor/github.com/hashicorp/terraform/terraform/version_required.go
index 3cbbf56..1f43045 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/version_required.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/version_required.go
@@ -6,19 +6,21 @@ import (
6 "github.com/hashicorp/go-version" 6 "github.com/hashicorp/go-version"
7 "github.com/hashicorp/terraform/config" 7 "github.com/hashicorp/terraform/config"
8 "github.com/hashicorp/terraform/config/module" 8 "github.com/hashicorp/terraform/config/module"
9
10 tfversion "github.com/hashicorp/terraform/version"
9) 11)
10 12
11// checkRequiredVersion verifies that any version requirements specified by 13// CheckRequiredVersion verifies that any version requirements specified by
12// the configuration are met. 14// the configuration are met.
13// 15//
14// This checks the root module as well as any additional version requirements 16// This checks the root module as well as any additional version requirements
15// from child modules. 17// from child modules.
16// 18//
17// This is tested in context_test.go. 19// This is tested in context_test.go.
18func checkRequiredVersion(m *module.Tree) error { 20func CheckRequiredVersion(m *module.Tree) error {
19 // Check any children 21 // Check any children
20 for _, c := range m.Children() { 22 for _, c := range m.Children() {
21 if err := checkRequiredVersion(c); err != nil { 23 if err := CheckRequiredVersion(c); err != nil {
22 return err 24 return err
23 } 25 }
24 } 26 }
@@ -49,7 +51,7 @@ func checkRequiredVersion(m *module.Tree) error {
49 tf.RequiredVersion, err) 51 tf.RequiredVersion, err)
50 } 52 }
51 53
52 if !cs.Check(SemVersion) { 54 if !cs.Check(tfversion.SemVer) {
53 return fmt.Errorf( 55 return fmt.Errorf(
54 "The currently running version of Terraform doesn't meet the\n"+ 56 "The currently running version of Terraform doesn't meet the\n"+
55 "version requirements explicitly specified by the configuration.\n"+ 57 "version requirements explicitly specified by the configuration.\n"+
@@ -62,7 +64,7 @@ func checkRequiredVersion(m *module.Tree) error {
62 " Current version: %s", 64 " Current version: %s",
63 module, 65 module,
64 tf.RequiredVersion, 66 tf.RequiredVersion,
65 SemVersion) 67 tfversion.SemVer)
66 } 68 }
67 69
68 return nil 70 return nil
diff --git a/vendor/github.com/hashicorp/terraform/terraform/walkoperation_string.go b/vendor/github.com/hashicorp/terraform/terraform/walkoperation_string.go
index cbd78dd..4cfc528 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/walkoperation_string.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/walkoperation_string.go
@@ -2,7 +2,7 @@
2 2
3package terraform 3package terraform
4 4
5import "fmt" 5import "strconv"
6 6
7const _walkOperation_name = "walkInvalidwalkInputwalkApplywalkPlanwalkPlanDestroywalkRefreshwalkValidatewalkDestroywalkImport" 7const _walkOperation_name = "walkInvalidwalkInputwalkApplywalkPlanwalkPlanDestroywalkRefreshwalkValidatewalkDestroywalkImport"
8 8
@@ -10,7 +10,7 @@ var _walkOperation_index = [...]uint8{0, 11, 20, 29, 37, 52, 63, 75, 86, 96}
10 10
11func (i walkOperation) String() string { 11func (i walkOperation) String() string {
12 if i >= walkOperation(len(_walkOperation_index)-1) { 12 if i >= walkOperation(len(_walkOperation_index)-1) {
13 return fmt.Sprintf("walkOperation(%d)", i) 13 return "walkOperation(" + strconv.FormatInt(int64(i), 10) + ")"
14 } 14 }
15 return _walkOperation_name[_walkOperation_index[i]:_walkOperation_index[i+1]] 15 return _walkOperation_name[_walkOperation_index[i]:_walkOperation_index[i+1]]
16} 16}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go
new file mode 100644
index 0000000..2c23f76
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go
@@ -0,0 +1,26 @@
1package tfdiags
2
3type Diagnostic interface {
4 Severity() Severity
5 Description() Description
6 Source() Source
7}
8
9type Severity rune
10
11//go:generate stringer -type=Severity
12
13const (
14 Error Severity = 'E'
15 Warning Severity = 'W'
16)
17
18type Description struct {
19 Summary string
20 Detail string
21}
22
23type Source struct {
24 Subject *SourceRange
25 Context *SourceRange
26}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go
new file mode 100644
index 0000000..667ba80
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go
@@ -0,0 +1,181 @@
1package tfdiags
2
3import (
4 "bytes"
5 "fmt"
6
7 "github.com/hashicorp/errwrap"
8 multierror "github.com/hashicorp/go-multierror"
9 "github.com/hashicorp/hcl2/hcl"
10)
11
12// Diagnostics is a list of diagnostics. Diagnostics is intended to be used
13// where a Go "error" might normally be used, allowing richer information
14// to be conveyed (more context, support for warnings).
15//
16// A nil Diagnostics is a valid, empty diagnostics list, thus allowing
17// heap allocation to be avoided in the common case where there are no
18// diagnostics to report at all.
19type Diagnostics []Diagnostic
20
21// Append is the main interface for constructing Diagnostics lists, taking
22// an existing list (which may be nil) and appending the new objects to it
23// after normalizing them to be implementations of Diagnostic.
24//
25// The usual pattern for a function that natively "speaks" diagnostics is:
26//
27// // Create a nil Diagnostics at the start of the function
28// var diags diag.Diagnostics
29//
30// // At later points, build on it if errors / warnings occur:
31// foo, err := DoSomethingRisky()
32// if err != nil {
33// diags = diags.Append(err)
34// }
35//
36// // Eventually return the result and diagnostics in place of error
37// return result, diags
38//
39// Append accepts a variety of different diagnostic-like types, including
40// native Go errors and HCL diagnostics. It also knows how to unwrap
41// a multierror.Error into separate error diagnostics. It can be passed
42// another Diagnostics to concatenate the two lists. If given something
43// it cannot handle, this function will panic.
44func (diags Diagnostics) Append(new ...interface{}) Diagnostics {
45 for _, item := range new {
46 if item == nil {
47 continue
48 }
49
50 switch ti := item.(type) {
51 case Diagnostic:
52 diags = append(diags, ti)
53 case Diagnostics:
54 diags = append(diags, ti...) // flatten
55 case diagnosticsAsError:
56 diags = diags.Append(ti.Diagnostics) // unwrap
57 case hcl.Diagnostics:
58 for _, hclDiag := range ti {
59 diags = append(diags, hclDiagnostic{hclDiag})
60 }
61 case *hcl.Diagnostic:
62 diags = append(diags, hclDiagnostic{ti})
63 case *multierror.Error:
64 for _, err := range ti.Errors {
65 diags = append(diags, nativeError{err})
66 }
67 case error:
68 switch {
69 case errwrap.ContainsType(ti, Diagnostics(nil)):
70 // If we have an errwrap wrapper with a Diagnostics hiding
71 // inside then we'll unpick it here to get access to the
72 // individual diagnostics.
73 diags = diags.Append(errwrap.GetType(ti, Diagnostics(nil)))
74 case errwrap.ContainsType(ti, hcl.Diagnostics(nil)):
75 // Likewise, if we have HCL diagnostics we'll unpick that too.
76 diags = diags.Append(errwrap.GetType(ti, hcl.Diagnostics(nil)))
77 default:
78 diags = append(diags, nativeError{ti})
79 }
80 default:
81 panic(fmt.Errorf("can't construct diagnostic(s) from %T", item))
82 }
83 }
84
85 // Given the above, we should never end up with a non-nil empty slice
86 // here, but we'll make sure of that so callers can rely on empty == nil
87 if len(diags) == 0 {
88 return nil
89 }
90
91 return diags
92}
93
94// HasErrors returns true if any of the diagnostics in the list have
95// a severity of Error.
96func (diags Diagnostics) HasErrors() bool {
97 for _, diag := range diags {
98 if diag.Severity() == Error {
99 return true
100 }
101 }
102 return false
103}
104
105// ForRPC returns a version of the receiver that has been simplified so that
106// it is friendly to RPC protocols.
107//
108// Currently this means that it can be serialized with encoding/gob and
109// subsequently re-inflated. It may later grow to include other serialization
110// formats.
111//
112// Note that this loses information about the original objects used to
113// construct the diagnostics, so e.g. the errwrap API will not work as
114// expected on an error-wrapped Diagnostics that came from ForRPC.
115func (diags Diagnostics) ForRPC() Diagnostics {
116 ret := make(Diagnostics, len(diags))
117 for i := range diags {
118 ret[i] = makeRPCFriendlyDiag(diags[i])
119 }
120 return ret
121}
122
123// Err flattens a diagnostics list into a single Go error, or to nil
124// if the diagnostics list does not include any error-level diagnostics.
125//
126// This can be used to smuggle diagnostics through an API that deals in
127// native errors, but unfortunately it will lose naked warnings (warnings
128// that aren't accompanied by at least one error) since such APIs have no
129// mechanism through which to report these.
130//
131// return result, diags.Error()
132func (diags Diagnostics) Err() error {
133 if !diags.HasErrors() {
134 return nil
135 }
136 return diagnosticsAsError{diags}
137}
138
139type diagnosticsAsError struct {
140 Diagnostics
141}
142
143func (dae diagnosticsAsError) Error() string {
144 diags := dae.Diagnostics
145 switch {
146 case len(diags) == 0:
147 // should never happen, since we don't create this wrapper if
148 // there are no diagnostics in the list.
149 return "no errors"
150 case len(diags) == 1:
151 desc := diags[0].Description()
152 if desc.Detail == "" {
153 return desc.Summary
154 }
155 return fmt.Sprintf("%s: %s", desc.Summary, desc.Detail)
156 default:
157 var ret bytes.Buffer
158 fmt.Fprintf(&ret, "%d problems:\n", len(diags))
159 for _, diag := range dae.Diagnostics {
160 desc := diag.Description()
161 if desc.Detail == "" {
162 fmt.Fprintf(&ret, "\n- %s", desc.Summary)
163 } else {
164 fmt.Fprintf(&ret, "\n- %s: %s", desc.Summary, desc.Detail)
165 }
166 }
167 return ret.String()
168 }
169}
170
171// WrappedErrors is an implementation of errwrap.Wrapper so that an error-wrapped
172// diagnostics object can be picked apart by errwrap-aware code.
173func (dae diagnosticsAsError) WrappedErrors() []error {
174 var errs []error
175 for _, diag := range dae.Diagnostics {
176 if wrapper, isErr := diag.(nativeError); isErr {
177 errs = append(errs, wrapper.err)
178 }
179 }
180 return errs
181}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/doc.go b/vendor/github.com/hashicorp/terraform/tfdiags/doc.go
new file mode 100644
index 0000000..c427879
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/doc.go
@@ -0,0 +1,16 @@
1// Package tfdiags is a utility package for representing errors and
2// warnings in a manner that allows us to produce good messages for the
3// user.
4//
5// "diag" is short for "diagnostics", and is meant as a general word for
6// feedback to a user about potential or actual problems.
7//
8// A design goal for this package is for it to be able to provide rich
9// messaging where possible but to also be pragmatic about dealing with
10// generic errors produced by system components that _can't_ provide
11// such rich messaging. As a consequence, the main types in this package --
12// Diagnostics and Diagnostic -- are designed so that they can be "smuggled"
13// over an error channel and then be unpacked at the other end, so that
14// error diagnostics (at least) can transit through APIs that are not
15// aware of this package.
16package tfdiags
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/error.go b/vendor/github.com/hashicorp/terraform/tfdiags/error.go
new file mode 100644
index 0000000..35edc30
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/error.go
@@ -0,0 +1,23 @@
1package tfdiags
2
3// nativeError is a Diagnostic implementation that wraps a normal Go error
4type nativeError struct {
5 err error
6}
7
8var _ Diagnostic = nativeError{}
9
10func (e nativeError) Severity() Severity {
11 return Error
12}
13
14func (e nativeError) Description() Description {
15 return Description{
16 Summary: e.err.Error(),
17 }
18}
19
20func (e nativeError) Source() Source {
21 // No source information available for a native error
22 return Source{}
23}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go b/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go
new file mode 100644
index 0000000..24851f4
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go
@@ -0,0 +1,77 @@
1package tfdiags
2
3import (
4 "github.com/hashicorp/hcl2/hcl"
5)
6
7// hclDiagnostic is a Diagnostic implementation that wraps a HCL Diagnostic
8type hclDiagnostic struct {
9 diag *hcl.Diagnostic
10}
11
12var _ Diagnostic = hclDiagnostic{}
13
14func (d hclDiagnostic) Severity() Severity {
15 switch d.diag.Severity {
16 case hcl.DiagWarning:
17 return Warning
18 default:
19 return Error
20 }
21}
22
23func (d hclDiagnostic) Description() Description {
24 return Description{
25 Summary: d.diag.Summary,
26 Detail: d.diag.Detail,
27 }
28}
29
30func (d hclDiagnostic) Source() Source {
31 var ret Source
32 if d.diag.Subject != nil {
33 rng := SourceRangeFromHCL(*d.diag.Subject)
34 ret.Subject = &rng
35 }
36 if d.diag.Context != nil {
37 rng := SourceRangeFromHCL(*d.diag.Context)
38 ret.Context = &rng
39 }
40 return ret
41}
42
43// SourceRangeFromHCL constructs a SourceRange from the corresponding range
44// type within the HCL package.
45func SourceRangeFromHCL(hclRange hcl.Range) SourceRange {
46 return SourceRange{
47 Filename: hclRange.Filename,
48 Start: SourcePos{
49 Line: hclRange.Start.Line,
50 Column: hclRange.Start.Column,
51 Byte: hclRange.Start.Byte,
52 },
53 End: SourcePos{
54 Line: hclRange.End.Line,
55 Column: hclRange.End.Column,
56 Byte: hclRange.End.Byte,
57 },
58 }
59}
60
61// ToHCL constructs a HCL Range from the receiving SourceRange. This is the
62// opposite of SourceRangeFromHCL.
63func (r SourceRange) ToHCL() hcl.Range {
64 return hcl.Range{
65 Filename: r.Filename,
66 Start: hcl.Pos{
67 Line: r.Start.Line,
68 Column: r.Start.Column,
69 Byte: r.Start.Byte,
70 },
71 End: hcl.Pos{
72 Line: r.End.Line,
73 Column: r.End.Column,
74 Byte: r.End.Byte,
75 },
76 }
77}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/rpc_friendly.go b/vendor/github.com/hashicorp/terraform/tfdiags/rpc_friendly.go
new file mode 100644
index 0000000..6cc95cc
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/rpc_friendly.go
@@ -0,0 +1,53 @@
1package tfdiags
2
3import (
4 "encoding/gob"
5)
6
7type rpcFriendlyDiag struct {
8 Severity_ Severity
9 Summary_ string
10 Detail_ string
11 Subject_ *SourceRange
12 Context_ *SourceRange
13}
14
15// rpcFriendlyDiag transforms a given diagnostic so that is more friendly to
16// RPC.
17//
18// In particular, it currently returns an object that can be serialized and
19// later re-inflated using gob. This definition may grow to include other
20// serializations later.
21func makeRPCFriendlyDiag(diag Diagnostic) Diagnostic {
22 desc := diag.Description()
23 source := diag.Source()
24 return &rpcFriendlyDiag{
25 Severity_: diag.Severity(),
26 Summary_: desc.Summary,
27 Detail_: desc.Detail,
28 Subject_: source.Subject,
29 Context_: source.Context,
30 }
31}
32
33func (d *rpcFriendlyDiag) Severity() Severity {
34 return d.Severity_
35}
36
37func (d *rpcFriendlyDiag) Description() Description {
38 return Description{
39 Summary: d.Summary_,
40 Detail: d.Detail_,
41 }
42}
43
44func (d *rpcFriendlyDiag) Source() Source {
45 return Source{
46 Subject: d.Subject_,
47 Context: d.Context_,
48 }
49}
50
51func init() {
52 gob.Register((*rpcFriendlyDiag)(nil))
53}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/severity_string.go b/vendor/github.com/hashicorp/terraform/tfdiags/severity_string.go
new file mode 100644
index 0000000..0b1249b
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/severity_string.go
@@ -0,0 +1,21 @@
1// Code generated by "stringer -type=Severity"; DO NOT EDIT.
2
3package tfdiags
4
5import "strconv"
6
7const (
8 _Severity_name_0 = "Error"
9 _Severity_name_1 = "Warning"
10)
11
12func (i Severity) String() string {
13 switch {
14 case i == 69:
15 return _Severity_name_0
16 case i == 87:
17 return _Severity_name_1
18 default:
19 return "Severity(" + strconv.FormatInt(int64(i), 10) + ")"
20 }
21}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/simple_warning.go b/vendor/github.com/hashicorp/terraform/tfdiags/simple_warning.go
new file mode 100644
index 0000000..fb3ac98
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/simple_warning.go
@@ -0,0 +1,25 @@
1package tfdiags
2
3type simpleWarning string
4
5var _ Diagnostic = simpleWarning("")
6
7// SimpleWarning constructs a simple (summary-only) warning diagnostic.
8func SimpleWarning(msg string) Diagnostic {
9 return simpleWarning(msg)
10}
11
12func (e simpleWarning) Severity() Severity {
13 return Warning
14}
15
16func (e simpleWarning) Description() Description {
17 return Description{
18 Summary: string(e),
19 }
20}
21
22func (e simpleWarning) Source() Source {
23 // No source information available for a native error
24 return Source{}
25}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/source_range.go b/vendor/github.com/hashicorp/terraform/tfdiags/source_range.go
new file mode 100644
index 0000000..3031168
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/source_range.go
@@ -0,0 +1,35 @@
1package tfdiags
2
3import (
4 "fmt"
5 "os"
6 "path/filepath"
7)
8
9type SourceRange struct {
10 Filename string
11 Start, End SourcePos
12}
13
14type SourcePos struct {
15 Line, Column, Byte int
16}
17
18// StartString returns a string representation of the start of the range,
19// including the filename and the line and column numbers.
20func (r SourceRange) StartString() string {
21 filename := r.Filename
22
23 // We'll try to relative-ize our filename here so it's less verbose
24 // in the common case of being in the current working directory. If not,
25 // we'll just show the full path.
26 wd, err := os.Getwd()
27 if err == nil {
28 relFn, err := filepath.Rel(wd, filename)
29 if err == nil {
30 filename = relFn
31 }
32 }
33
34 return fmt.Sprintf("%s:%d,%d", filename, r.Start.Line, r.Start.Column)
35}
diff --git a/vendor/github.com/hashicorp/terraform/version/version.go b/vendor/github.com/hashicorp/terraform/version/version.go
new file mode 100644
index 0000000..b21b297
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/version/version.go
@@ -0,0 +1,36 @@
1// The version package provides a location to set the release versions for all
2// packages to consume, without creating import cycles.
3//
4// This package should not import any other terraform packages.
5package version
6
7import (
8 "fmt"
9
10 version "github.com/hashicorp/go-version"
11)
12
13// The main version number that is being run at the moment.
14var Version = "0.11.12"
15
16// A pre-release marker for the version. If this is "" (empty string)
17// then it means that it is a final release. Otherwise, this is a pre-release
18// such as "dev" (in development), "beta", "rc1", etc.
19var Prerelease = "dev"
20
21// SemVer is an instance of version.Version. This has the secondary
22// benefit of verifying during tests and init time that our version is a
23// proper semantic version, which should always be the case.
24var SemVer = version.Must(version.NewVersion(Version))
25
26// Header is the header name used to send the current terraform version
27// in http requests.
28const Header = "Terraform-Version"
29
30// String returns the complete version string, including prerelease
31func String() string {
32 if Prerelease != "" {
33 return fmt.Sprintf("%s-%s", Version, Prerelease)
34 }
35 return Version
36}