From e445f8db38f30a021415919e0454743a57b9b3d0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 12 Jun 2014 17:24:55 -0700 Subject: [PATCH] config: RawConfig works, plus tests --- config/raw_config.go | 46 ++++++++++++++++++++++-- config/raw_config_test.go | 76 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 2 deletions(-) diff --git a/config/raw_config.go b/config/raw_config.go index 13d72f503..2b0aefbb5 100644 --- a/config/raw_config.go +++ b/config/raw_config.go @@ -1,5 +1,10 @@ package config +import ( + "github.com/mitchellh/copystructure" + "github.com/mitchellh/reflectwalk" +) + // UnknownVariableValue is a sentinel value that can be used // to denote that the value of a variable is unknown at this time. // RawConfig uses this information to build up data about @@ -18,6 +23,23 @@ const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66" type RawConfig struct { Raw map[string]interface{} Variables map[string]InterpolatedVariable + + config map[string]interface{} + unknownKeys []string +} + +// NewRawConfig creates a new RawConfig structure and populates the +// publicly readable struct fields. +func NewRawConfig(raw map[string]interface{}) (*RawConfig, error) { + walker := new(variableDetectWalker) + if err := reflectwalk.Walk(raw, walker); err != nil { + return nil, err + } + + return &RawConfig{ + Raw: raw, + Variables: walker.Variables, + }, nil } // Interpolate uses the given mapping of variable values and uses @@ -27,7 +49,21 @@ type RawConfig struct { // Any prior calls to Interpolate are replaced with this one. // // If a variable key is missing, this will panic. -func (r *RawConfig) Interpolate(map[string]string) { +func (r *RawConfig) Interpolate(vs map[string]string) error { + config, err := copystructure.Copy(r.Raw) + if err != nil { + return err + } + + w := &variableReplaceWalker{Values: vs} + r.config = config.(map[string]interface{}) + err = reflectwalk.Walk(r.config, w) + if err != nil { + return err + } + + r.unknownKeys = w.UnknownKeys + return nil } // Config returns the entire configuration with the variables @@ -42,5 +78,11 @@ func (r *RawConfig) Interpolate(map[string]string) { // structure will always successfully decode into its ultimate // structure using something like mapstructure. func (r *RawConfig) Config() map[string]interface{} { - return nil + return r.config +} + +// UnknownKeys returns the keys of the configuration that are unknown +// because they had interpolated variables that must be computed. +func (r *RawConfig) UnknownKeys() []string { + return r.unknownKeys } diff --git a/config/raw_config_test.go b/config/raw_config_test.go index d912156be..6e318a8f1 100644 --- a/config/raw_config_test.go +++ b/config/raw_config_test.go @@ -1 +1,77 @@ package config + +import ( + "reflect" + "testing" +) + +func TestNewRawConfig(t *testing.T) { + raw := map[string]interface{}{ + "foo": "${var.bar}", + } + + rc, err := NewRawConfig(raw) + if err != nil { + t.Fatalf("err: %s", err) + } + + if len(rc.Variables) != 1 { + t.Fatalf("bad: %#v", rc.Variables) + } +} + +func TestRawConfig(t *testing.T) { + raw := map[string]interface{}{ + "foo": "${var.bar}", + } + + rc, err := NewRawConfig(raw) + if err != nil { + t.Fatalf("err: %s", err) + } + + vars := map[string]string{"var.bar": "baz"} + if err := rc.Interpolate(vars); err != nil { + t.Fatalf("err: %s", err) + } + + actual := rc.Config() + expected := map[string]interface{}{ + "foo": "baz", + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } + if len(rc.UnknownKeys()) != 0 { + t.Fatalf("bad: %#v", rc.UnknownKeys()) + } +} + +func TestRawConfig_unknown(t *testing.T) { + raw := map[string]interface{}{ + "foo": "${var.bar}", + } + + rc, err := NewRawConfig(raw) + if err != nil { + t.Fatalf("err: %s", err) + } + + vars := map[string]string{"var.bar": UnknownVariableValue} + if err := rc.Interpolate(vars); err != nil { + t.Fatalf("err: %s", err) + } + + actual := rc.Config() + expected := map[string]interface{}{} + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } + + expectedKeys := []string{"foo"} + if !reflect.DeepEqual(rc.UnknownKeys(), expectedKeys) { + t.Fatalf("bad: %#v", rc.UnknownKeys()) + } +}