2014-07-18 20:37:27 +02:00
|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2014-08-19 18:56:50 +02:00
|
|
|
"io/ioutil"
|
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
|
|
|
"regexp"
|
2014-07-18 20:37:27 +02:00
|
|
|
"strings"
|
2014-07-18 23:00:40 +02:00
|
|
|
|
2014-08-19 18:56:50 +02:00
|
|
|
"github.com/hashicorp/hcl"
|
2014-09-09 05:43:59 +02:00
|
|
|
"github.com/mitchellh/go-homedir"
|
2014-07-18 20:37:27 +02:00
|
|
|
)
|
|
|
|
|
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
|
|
|
// FlagTypedKVis a flag.Value implementation for parsing user variables
|
|
|
|
// from the command-line in the format of '-var key=value', where value is
|
|
|
|
// a type intended for use as a Terraform variable
|
|
|
|
type FlagTypedKV map[string]interface{}
|
2014-07-18 20:37:27 +02:00
|
|
|
|
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
|
|
|
func (v *FlagTypedKV) String() string {
|
2014-07-18 20:37:27 +02:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
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
|
|
|
func (v *FlagTypedKV) Set(raw string) error {
|
|
|
|
key, value, err := parseVarFlagAsHCL(raw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if *v == nil {
|
|
|
|
*v = make(map[string]interface{})
|
|
|
|
}
|
|
|
|
|
|
|
|
(*v)[key] = value
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FlagStringKV is a flag.Value implementation for parsing user variables
|
|
|
|
// from the command-line in the format of '-var key=value', where value is
|
|
|
|
// only ever a primitive.
|
|
|
|
type FlagStringKV map[string]string
|
|
|
|
|
|
|
|
func (v *FlagStringKV) String() string {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *FlagStringKV) Set(raw string) error {
|
2014-07-18 20:37:27 +02:00
|
|
|
idx := strings.Index(raw, "=")
|
|
|
|
if idx == -1 {
|
|
|
|
return fmt.Errorf("No '=' value in arg: %s", raw)
|
|
|
|
}
|
|
|
|
|
|
|
|
if *v == nil {
|
|
|
|
*v = make(map[string]string)
|
|
|
|
}
|
|
|
|
|
|
|
|
key, value := raw[0:idx], raw[idx+1:]
|
|
|
|
(*v)[key] = value
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-02-22 03:30:41 +01:00
|
|
|
// FlagKVFile is a flag.Value implementation for parsing user variables
|
2014-07-18 20:37:27 +02:00
|
|
|
// from the command line in the form of files. i.e. '-var-file=foo'
|
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
|
|
|
type FlagKVFile map[string]interface{}
|
2014-07-18 20:37:27 +02:00
|
|
|
|
2015-02-22 03:30:41 +01:00
|
|
|
func (v *FlagKVFile) String() string {
|
2014-07-18 20:37:27 +02:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2015-02-22 03:30:41 +01:00
|
|
|
func (v *FlagKVFile) Set(raw string) error {
|
|
|
|
vs, err := loadKVFile(raw)
|
2014-07-18 23:00:40 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if *v == nil {
|
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
|
|
|
*v = make(map[string]interface{})
|
2014-07-18 23:00:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for key, value := range vs {
|
|
|
|
(*v)[key] = value
|
|
|
|
}
|
|
|
|
|
2014-07-18 20:37:27 +02:00
|
|
|
return nil
|
|
|
|
}
|
2014-07-18 23:00:40 +02:00
|
|
|
|
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
|
|
|
func loadKVFile(rawPath string) (map[string]interface{}, error) {
|
2014-09-09 05:43:59 +02:00
|
|
|
path, err := homedir.Expand(rawPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"Error expanding path: %s", err)
|
|
|
|
}
|
|
|
|
|
2014-08-19 18:56:50 +02:00
|
|
|
// Read the HCL file and prepare for parsing
|
|
|
|
d, err := ioutil.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"Error reading %s: %s", path, err)
|
2014-07-18 23:00:40 +02:00
|
|
|
}
|
|
|
|
|
2014-08-19 18:56:50 +02:00
|
|
|
// Parse it
|
|
|
|
obj, err := hcl.Parse(string(d))
|
2014-07-18 23:00:40 +02:00
|
|
|
if err != nil {
|
2014-08-19 18:56:50 +02:00
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"Error parsing %s: %s", path, err)
|
2014-07-18 23:00:40 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
var result map[string]interface{}
|
2014-08-19 18:56:50 +02:00
|
|
|
if err := hcl.DecodeObject(&result, obj); err != nil {
|
2015-06-26 02:56:37 +02:00
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"Error decoding Terraform vars file: %s\n\n"+
|
|
|
|
"The vars file should be in the format of `key = \"value\"`.\n"+
|
|
|
|
"Decoding errors are usually caused by an invalid format.",
|
|
|
|
err)
|
2014-07-18 23:00:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
2015-03-24 17:18:15 +01:00
|
|
|
|
|
|
|
// FlagStringSlice is a flag.Value implementation for parsing targets from the
|
|
|
|
// command line, e.g. -target=aws_instance.foo -target=aws_vpc.bar
|
|
|
|
|
|
|
|
type FlagStringSlice []string
|
|
|
|
|
|
|
|
func (v *FlagStringSlice) String() string {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
func (v *FlagStringSlice) Set(raw string) error {
|
|
|
|
*v = append(*v, raw)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
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
|
|
|
|
|
|
|
// parseVarFlagAsHCL parses the value of a single variable as would have been specified
|
|
|
|
// on the command line via -var or in an environment variable named TF_VAR_x, where x is
|
|
|
|
// the name of the variable. In order to get around the restriction of HCL requiring a
|
|
|
|
// top level object, we prepend a sentinel key, decode the user-specified value as its
|
|
|
|
// value and pull the value back out of the resulting map.
|
|
|
|
func parseVarFlagAsHCL(input string) (string, interface{}, error) {
|
|
|
|
idx := strings.Index(input, "=")
|
|
|
|
if idx == -1 {
|
|
|
|
return "", nil, fmt.Errorf("No '=' value in variable: %s", input)
|
|
|
|
}
|
|
|
|
probablyName := input[0:idx]
|
|
|
|
|
|
|
|
parsed, err := hcl.Parse(input)
|
|
|
|
if err != nil {
|
|
|
|
// This covers flags of the form `foo=bar` which is not valid HCL
|
|
|
|
// At this point, probablyName is actually the name, and the remainder
|
|
|
|
// of the expression after the equals sign is the value.
|
|
|
|
if regexp.MustCompile(`Unknown token: \d+:\d+ IDENT`).Match([]byte(err.Error())) {
|
|
|
|
value := input[idx+1:]
|
|
|
|
return probablyName, value, nil
|
|
|
|
}
|
|
|
|
return "", nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL: %s", probablyName, input, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var decoded map[string]interface{}
|
|
|
|
if hcl.DecodeObject(&decoded, parsed); err != nil {
|
|
|
|
return "", nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL: %s", probablyName, input, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cover cases such as key=
|
|
|
|
if len(decoded) == 0 {
|
|
|
|
return probablyName, "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(decoded) > 1 {
|
|
|
|
return "", nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. Only one value may be specified.", probablyName, input)
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range decoded {
|
|
|
|
return k, v, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Should be unreachable
|
|
|
|
return "", nil, fmt.Errorf("No value for variable: %s", input)
|
|
|
|
}
|