terraform/terraform/semantics.go

133 lines
3.4 KiB
Go
Raw Normal View History

package terraform
import (
"fmt"
"strings"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/dag"
)
// 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
}
// smcUserVariables does all the semantic checks to verify that the
// variables given satisfy the configuration itself.
func smcUserVariables(c *config.Config, vs map[string]interface{}) []error {
var errs []error
cvs := make(map[string]*config.Variable)
for _, v := range c.Variables {
cvs[v.Name] = v
}
// Check that all required variables are present
required := make(map[string]struct{})
for _, v := range c.Variables {
if v.Required() {
required[v.Name] = struct{}{}
}
}
for k, _ := range vs {
delete(required, k)
}
if len(required) > 0 {
for k, _ := range required {
errs = append(errs, fmt.Errorf(
"Required variable not set: %s", k))
}
}
// 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 {
// 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]
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:
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
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
}
}
errs = append(errs, fmt.Errorf("variable %s should be type %s, got %s",
name, declaredType.Printable(), hclTypeName(proposedValue)))
}
// TODO(mitchellh): variables that are unknown
return errs
}