diff --git a/config/raw_config.go b/config/raw_config.go index 753e7f1b8e..f8fcc2ca74 100644 --- a/config/raw_config.go +++ b/config/raw_config.go @@ -92,6 +92,51 @@ func (r *RawConfig) Interpolate(vs map[string]ast.Variable) error { }) } +// Merge merges another RawConfig into this one (overriding any conflicting +// values in this config) and returns a new config. The original config +// is not modified. +func (r *RawConfig) Merge(other *RawConfig) *RawConfig { + // Merge the raw configurations + raw := make(map[string]interface{}) + for k, v := range r.Raw { + raw[k] = v + } + for k, v := range other.Raw { + raw[k] = v + } + + // Create the result + result, err := NewRawConfig(raw) + if err != nil { + panic(err) + } + + // Merge the interpolated results + result.config = make(map[string]interface{}) + for k, v := range r.config { + result.config[k] = v + } + for k, v := range other.config { + result.config[k] = v + } + + // Build the unknown keys + unknownKeys := make(map[string]struct{}) + for _, k := range r.unknownKeys { + unknownKeys[k] = struct{}{} + } + for _, k := range other.unknownKeys { + unknownKeys[k] = struct{}{} + } + + result.unknownKeys = make([]string, 0, len(unknownKeys)) + for k, _ := range unknownKeys { + result.unknownKeys = append(result.unknownKeys, k) + } + + return result +} + func (r *RawConfig) init() error { r.config = r.Raw r.Interpolations = nil diff --git a/config/raw_config_test.go b/config/raw_config_test.go index 49e13a45a2..324f98ee62 100644 --- a/config/raw_config_test.go +++ b/config/raw_config_test.go @@ -114,6 +114,87 @@ func TestRawConfig_double(t *testing.T) { } } +func TestRawConfig_merge(t *testing.T) { + raw1 := map[string]interface{}{ + "foo": "${var.foo}", + "bar": "${var.bar}", + } + + rc1, err := NewRawConfig(raw1) + if err != nil { + t.Fatalf("err: %s", err) + } + + { + vars := map[string]ast.Variable{ + "var.foo": ast.Variable{ + Value: "foovalue", + Type: ast.TypeString, + }, + "var.bar": ast.Variable{ + Value: "nope", + Type: ast.TypeString, + }, + } + if err := rc1.Interpolate(vars); err != nil { + t.Fatalf("err: %s", err) + } + } + + raw2 := map[string]interface{}{ + "bar": "${var.bar}", + "baz": "${var.baz}", + } + + rc2, err := NewRawConfig(raw2) + if err != nil { + t.Fatalf("err: %s", err) + } + + { + vars := map[string]ast.Variable{ + "var.bar": ast.Variable{ + Value: "barvalue", + Type: ast.TypeString, + }, + "var.baz": ast.Variable{ + Value: UnknownVariableValue, + Type: ast.TypeString, + }, + } + if err := rc2.Interpolate(vars); err != nil { + t.Fatalf("err: %s", err) + } + } + + // Merge the two + rc3 := rc1.Merge(rc2) + + // Raw should be merged + raw3 := map[string]interface{}{ + "foo": "${var.foo}", + "bar": "${var.bar}", + "baz": "${var.baz}", + } + if !reflect.DeepEqual(rc3.Raw, raw3) { + t.Fatalf("bad: %#v", rc3.Raw) + } + + actual := rc3.Config() + expected := map[string]interface{}{ + "foo": "foovalue", + "bar": "barvalue", + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } + + expectedKeys := []string{"baz"} + if !reflect.DeepEqual(rc3.UnknownKeys(), expectedKeys) { + t.Fatalf("bad: %#v", rc3.UnknownKeys()) + } +} + func TestRawConfig_syntax(t *testing.T) { raw := map[string]interface{}{ "foo": "${var",