2014-06-05 20:53:07 +02:00
|
|
|
package terraform
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2016-12-09 21:58:24 +01:00
|
|
|
"strings"
|
2014-06-05 20:53:07 +02:00
|
|
|
|
2015-01-24 02:52:51 +01:00
|
|
|
"github.com/hashicorp/go-multierror"
|
2014-06-05 20:53:07 +02:00
|
|
|
"github.com/hashicorp/terraform/config"
|
2015-01-24 02:52:51 +01:00
|
|
|
"github.com/hashicorp/terraform/dag"
|
2014-06-05 20:53:07 +02:00
|
|
|
)
|
|
|
|
|
2015-01-24 02:52:51 +01:00
|
|
|
// GraphSemanticChecker is the interface that semantic checks across
|
|
|
|
// the entire Terraform graph implement.
|
|
|
|
//
|
|
|
|
// The graph should NOT be modified by the semantic checker.
|
|
|
|
type GraphSemanticChecker interface {
|
|
|
|
Check(*dag.Graph) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnorderedSemanticCheckRunner is an implementation of GraphSemanticChecker
|
|
|
|
// that runs a list of SemanticCheckers against the vertices of the graph
|
|
|
|
// in no specified order.
|
|
|
|
type UnorderedSemanticCheckRunner struct {
|
|
|
|
Checks []SemanticChecker
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sc *UnorderedSemanticCheckRunner) Check(g *dag.Graph) error {
|
|
|
|
var err error
|
|
|
|
for _, v := range g.Vertices() {
|
|
|
|
for _, check := range sc.Checks {
|
|
|
|
if e := check.Check(g, v); e != nil {
|
|
|
|
err = multierror.Append(err, e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// SemanticChecker is the interface that semantic checks across the
|
|
|
|
// Terraform graph implement. Errors are accumulated. Even after an error
|
|
|
|
// is returned, child vertices in the graph will still be visited.
|
|
|
|
//
|
|
|
|
// The graph should NOT be modified by the semantic checker.
|
|
|
|
//
|
|
|
|
// The order in which vertices are visited is left unspecified, so the
|
|
|
|
// semantic checks should not rely on that.
|
|
|
|
type SemanticChecker interface {
|
|
|
|
Check(*dag.Graph, dag.Vertex) error
|
|
|
|
}
|
|
|
|
|
2014-07-03 05:47:10 +02:00
|
|
|
// smcUserVariables does all the semantic checks to verify that the
|
|
|
|
// variables given satisfy the configuration itself.
|
2016-07-18 19:52:10 +02:00
|
|
|
func smcUserVariables(c *config.Config, vs map[string]interface{}) []error {
|
2014-06-05 20:53:07 +02:00
|
|
|
var errs []error
|
|
|
|
|
2014-07-22 18:27:28 +02:00
|
|
|
cvs := make(map[string]*config.Variable)
|
|
|
|
for _, v := range c.Variables {
|
|
|
|
cvs[v.Name] = v
|
|
|
|
}
|
|
|
|
|
2014-06-05 20:53:07 +02:00
|
|
|
// Check that all required variables are present
|
|
|
|
required := make(map[string]struct{})
|
2014-07-19 07:13:15 +02:00
|
|
|
for _, v := range c.Variables {
|
2014-06-05 20:53:07 +02:00
|
|
|
if v.Required() {
|
2014-07-19 07:13:15 +02:00
|
|
|
required[v.Name] = struct{}{}
|
2014-06-05 20:53:07 +02:00
|
|
|
}
|
|
|
|
}
|
2014-07-03 05:47:10 +02:00
|
|
|
for k, _ := range vs {
|
2014-06-05 20:53:07 +02:00
|
|
|
delete(required, k)
|
|
|
|
}
|
|
|
|
if len(required) > 0 {
|
|
|
|
for k, _ := range required {
|
|
|
|
errs = append(errs, fmt.Errorf(
|
|
|
|
"Required variable not set: %s", k))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-22 18:27:28 +02:00
|
|
|
// Check that types match up
|
core: Allow lists and maps as variable overrides
Terraform 0.7 introduces lists and maps as first-class values for
variables, in addition to string values which were previously available.
However, there was previously no way to override the default value of a
list or map, and the functionality for overriding specific map keys was
broken.
Using the environment variable method for setting variable values, there
was previously no way to give a variable a value of a list or map. These
now support HCL for individual values - specifying:
TF_VAR_test='["Hello", "World"]'
will set the variable `test` to a two-element list containing "Hello"
and "World". Specifying
TF_VAR_test_map='{"Hello = "World", "Foo" = "bar"}'
will set the variable `test_map` to a two-element map with keys "Hello"
and "Foo", and values "World" and "bar" respectively.
The same logic is applied to `-var` flags, and the file parsed by
`-var-files` ("autoVariables").
Note that care must be taken to not run into shell expansion for `-var-`
flags and environment variables.
We also merge map keys where appropriate. The override syntax has
changed (to be noted in CHANGELOG as a breaking change), so several
tests needed their syntax updating from the old `amis.us-east-1 =
"newValue"` style to `amis = "{ "us-east-1" = "newValue"}"` style as
defined in TF-002.
In order to continue supporting the `-var "foo=bar"` type of variable
flag (which is not valid HCL), a special case error is checked after HCL
parsing fails, and the old code path runs instead.
2016-07-21 03:38:26 +02:00
|
|
|
for name, proposedValue := range vs {
|
2016-12-09 21:58:24 +01:00
|
|
|
// Check for "map.key" fields. These stopped working with Terraform
|
|
|
|
// 0.7 but we do this to surface a better error message informing
|
|
|
|
// the user what happened.
|
|
|
|
if idx := strings.Index(name, "."); idx > 0 {
|
|
|
|
key := name[:idx]
|
|
|
|
if _, ok := cvs[key]; ok {
|
|
|
|
errs = append(errs, fmt.Errorf(
|
|
|
|
"%s: Overriding map keys with the format `name.key` is no "+
|
|
|
|
"longer allowed. You may still override keys by setting "+
|
|
|
|
"`name = { key = value }`. The maps will be merged. This "+
|
|
|
|
"behavior appeared in 0.7.0.", name))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
core: Allow lists and maps as variable overrides
Terraform 0.7 introduces lists and maps as first-class values for
variables, in addition to string values which were previously available.
However, there was previously no way to override the default value of a
list or map, and the functionality for overriding specific map keys was
broken.
Using the environment variable method for setting variable values, there
was previously no way to give a variable a value of a list or map. These
now support HCL for individual values - specifying:
TF_VAR_test='["Hello", "World"]'
will set the variable `test` to a two-element list containing "Hello"
and "World". Specifying
TF_VAR_test_map='{"Hello = "World", "Foo" = "bar"}'
will set the variable `test_map` to a two-element map with keys "Hello"
and "Foo", and values "World" and "bar" respectively.
The same logic is applied to `-var` flags, and the file parsed by
`-var-files` ("autoVariables").
Note that care must be taken to not run into shell expansion for `-var-`
flags and environment variables.
We also merge map keys where appropriate. The override syntax has
changed (to be noted in CHANGELOG as a breaking change), so several
tests needed their syntax updating from the old `amis.us-east-1 =
"newValue"` style to `amis = "{ "us-east-1" = "newValue"}"` style as
defined in TF-002.
In order to continue supporting the `-var "foo=bar"` type of variable
flag (which is not valid HCL), a special case error is checked after HCL
parsing fails, and the old code path runs instead.
2016-07-21 03:38:26 +02:00
|
|
|
schema, ok := cvs[name]
|
2014-07-22 18:27:28 +02:00
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
core: Allow lists and maps as variable overrides
Terraform 0.7 introduces lists and maps as first-class values for
variables, in addition to string values which were previously available.
However, there was previously no way to override the default value of a
list or map, and the functionality for overriding specific map keys was
broken.
Using the environment variable method for setting variable values, there
was previously no way to give a variable a value of a list or map. These
now support HCL for individual values - specifying:
TF_VAR_test='["Hello", "World"]'
will set the variable `test` to a two-element list containing "Hello"
and "World". Specifying
TF_VAR_test_map='{"Hello = "World", "Foo" = "bar"}'
will set the variable `test_map` to a two-element map with keys "Hello"
and "Foo", and values "World" and "bar" respectively.
The same logic is applied to `-var` flags, and the file parsed by
`-var-files` ("autoVariables").
Note that care must be taken to not run into shell expansion for `-var-`
flags and environment variables.
We also merge map keys where appropriate. The override syntax has
changed (to be noted in CHANGELOG as a breaking change), so several
tests needed their syntax updating from the old `amis.us-east-1 =
"newValue"` style to `amis = "{ "us-east-1" = "newValue"}"` style as
defined in TF-002.
In order to continue supporting the `-var "foo=bar"` type of variable
flag (which is not valid HCL), a special case error is checked after HCL
parsing fails, and the old code path runs instead.
2016-07-21 03:38:26 +02:00
|
|
|
declaredType := schema.Type()
|
|
|
|
|
|
|
|
switch declaredType {
|
|
|
|
case config.VariableTypeString:
|
|
|
|
switch proposedValue.(type) {
|
|
|
|
case string:
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
case config.VariableTypeMap:
|
2016-09-27 19:04:40 +02:00
|
|
|
switch v := proposedValue.(type) {
|
core: Allow lists and maps as variable overrides
Terraform 0.7 introduces lists and maps as first-class values for
variables, in addition to string values which were previously available.
However, there was previously no way to override the default value of a
list or map, and the functionality for overriding specific map keys was
broken.
Using the environment variable method for setting variable values, there
was previously no way to give a variable a value of a list or map. These
now support HCL for individual values - specifying:
TF_VAR_test='["Hello", "World"]'
will set the variable `test` to a two-element list containing "Hello"
and "World". Specifying
TF_VAR_test_map='{"Hello = "World", "Foo" = "bar"}'
will set the variable `test_map` to a two-element map with keys "Hello"
and "Foo", and values "World" and "bar" respectively.
The same logic is applied to `-var` flags, and the file parsed by
`-var-files` ("autoVariables").
Note that care must be taken to not run into shell expansion for `-var-`
flags and environment variables.
We also merge map keys where appropriate. The override syntax has
changed (to be noted in CHANGELOG as a breaking change), so several
tests needed their syntax updating from the old `amis.us-east-1 =
"newValue"` style to `amis = "{ "us-east-1" = "newValue"}"` style as
defined in TF-002.
In order to continue supporting the `-var "foo=bar"` type of variable
flag (which is not valid HCL), a special case error is checked after HCL
parsing fails, and the old code path runs instead.
2016-07-21 03:38:26 +02:00
|
|
|
case map[string]interface{}:
|
|
|
|
continue
|
2016-09-27 19:04:40 +02:00
|
|
|
case []map[string]interface{}:
|
|
|
|
// if we have a list of 1 map, it will get coerced later as needed
|
|
|
|
if len(v) == 1 {
|
|
|
|
continue
|
|
|
|
}
|
core: Allow lists and maps as variable overrides
Terraform 0.7 introduces lists and maps as first-class values for
variables, in addition to string values which were previously available.
However, there was previously no way to override the default value of a
list or map, and the functionality for overriding specific map keys was
broken.
Using the environment variable method for setting variable values, there
was previously no way to give a variable a value of a list or map. These
now support HCL for individual values - specifying:
TF_VAR_test='["Hello", "World"]'
will set the variable `test` to a two-element list containing "Hello"
and "World". Specifying
TF_VAR_test_map='{"Hello = "World", "Foo" = "bar"}'
will set the variable `test_map` to a two-element map with keys "Hello"
and "Foo", and values "World" and "bar" respectively.
The same logic is applied to `-var` flags, and the file parsed by
`-var-files` ("autoVariables").
Note that care must be taken to not run into shell expansion for `-var-`
flags and environment variables.
We also merge map keys where appropriate. The override syntax has
changed (to be noted in CHANGELOG as a breaking change), so several
tests needed their syntax updating from the old `amis.us-east-1 =
"newValue"` style to `amis = "{ "us-east-1" = "newValue"}"` style as
defined in TF-002.
In order to continue supporting the `-var "foo=bar"` type of variable
flag (which is not valid HCL), a special case error is checked after HCL
parsing fails, and the old code path runs instead.
2016-07-21 03:38:26 +02:00
|
|
|
}
|
|
|
|
case config.VariableTypeList:
|
|
|
|
switch proposedValue.(type) {
|
|
|
|
case []interface{}:
|
|
|
|
continue
|
|
|
|
}
|
2014-07-22 18:27:28 +02:00
|
|
|
}
|
2016-09-27 19:04:40 +02:00
|
|
|
errs = append(errs, fmt.Errorf("variable %s should be type %s, got %s",
|
|
|
|
name, declaredType.Printable(), hclTypeName(proposedValue)))
|
2014-07-22 18:27:28 +02:00
|
|
|
}
|
|
|
|
|
2014-06-05 20:53:07 +02:00
|
|
|
// TODO(mitchellh): variables that are unknown
|
|
|
|
|
|
|
|
return errs
|
|
|
|
}
|