diff --git a/config/config.go b/config/config.go index 0336e11f0..c074f0de2 100644 --- a/config/config.go +++ b/config/config.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/hashicorp/terraform/depgraph" - "github.com/mitchellh/reflectwalk" ) // ResourceGraphRoot is the name of the resource graph root that should be @@ -77,6 +76,20 @@ type UserVariable struct { key string } +// ReplaceVariables replaces the variables in the configuration +// with the given values. +// +// This replacement is not in place. Instead, this function will +// return a new resource with the variables replaced. +func (r *ProviderConfig) ReplaceVariables( + vs map[string]string) *ProviderConfig { + result := *r + if err := replaceVariables(result.Config, vs); err != nil { + panic(err) + } + return &result +} + // A unique identifier for this resource. func (r *Resource) Id() string { return fmt.Sprintf("%s.%s", r.Type, r.Name) @@ -103,20 +116,18 @@ func (r *Resource) ProviderConfigName(pcs map[string]*ProviderConfig) string { // return a new resource with the variables replaced. func (r *Resource) ReplaceVariables(vs map[string]string) *Resource { result := *r - - w := &variableReplaceWalker{ - Values: vs, - } - - if err := reflectwalk.Walk(result.Config, w); err != nil { + if err := replaceVariables(result.Config, vs); err != nil { panic(err) } - return &result } // ResourceGraph returns a dependency graph of the resources from this // Terraform configuration. +// +// The graph can contain both *Resource and *ProviderConfig. When consuming +// the graph, you'll have to use type inference to determine what it is +// and the proper behavior. func (c *Config) ResourceGraph() *depgraph.Graph { // This tracks all the resource nouns nouns := make(map[string]*depgraph.Noun) diff --git a/config/config_test.go b/config/config_test.go index 27162838f..8e93cd43d 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -64,6 +64,45 @@ func TestNewUserVariable(t *testing.T) { } } +func TestProviderConfigReplaceVariables(t *testing.T) { + r := &ProviderConfig{ + Config: map[string]interface{}{ + "foo": "${var.bar}", + }, + } + + values := map[string]string{ + "var.bar": "value", + } + + r2 := r.ReplaceVariables(values) + + expected := &ProviderConfig{ + Config: map[string]interface{}{ + "foo": "value", + }, + } + if !reflect.DeepEqual(r2, expected) { + t.Fatalf("bad: %#v", r2) + } + + /* + TODO(mitchellh): Eventually, preserve original config... + + expectedOriginal := &Resource{ + Name: "foo", + Type: "bar", + Config: map[string]interface{}{ + "foo": "${var.bar}", + }, + } + + if !reflect.DeepEqual(r, expectedOriginal) { + t.Fatalf("bad: %#v", r) + } + */ +} + func TestResourceProviderConfigName(t *testing.T) { r := &Resource{ Name: "foo", diff --git a/config/variable.go b/config/variable.go index cf7a9cbe2..e1933e599 100644 --- a/config/variable.go +++ b/config/variable.go @@ -16,6 +16,16 @@ func init() { varRegexp = regexp.MustCompile(`(?i)(\$+)\{([-.a-z0-9_]+)\}`) } +// replaceVariables takes a configuration and a mapping of variables +// and performs the structure walking necessary to properly replace +// all the variables. +func replaceVariables( + c map[string]interface{}, + vs map[string]string) error { + w := &variableReplaceWalker{Values: vs} + return reflectwalk.Walk(c, w) +} + // variableDetectWalker implements interfaces for the reflectwalk package // (github.com/mitchellh/reflectwalk) that can be used to automatically // pull out the variables that need replacing.