summaryrefslogtreecommitdiff
path: root/vendor/github.com/hashicorp/terraform/config/merge.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config/merge.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/config/merge.go193
1 files changed, 193 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/merge.go b/vendor/github.com/hashicorp/terraform/config/merge.go
new file mode 100644
index 00000000..db214be4
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/merge.go
@@ -0,0 +1,193 @@
+package config
+
+// Merge merges two configurations into a single configuration.
+//
+// Merge allows for the two configurations to have duplicate resources,
+// because the resources will be merged. This differs from a single
+// Config which must only have unique resources.
+func Merge(c1, c2 *Config) (*Config, error) {
+ c := new(Config)
+
+ // Merge unknown keys
+ unknowns := make(map[string]struct{})
+ for _, k := range c1.unknownKeys {
+ _, present := unknowns[k]
+ if !present {
+ unknowns[k] = struct{}{}
+ c.unknownKeys = append(c.unknownKeys, k)
+ }
+ }
+ for _, k := range c2.unknownKeys {
+ _, present := unknowns[k]
+ if !present {
+ unknowns[k] = struct{}{}
+ c.unknownKeys = append(c.unknownKeys, k)
+ }
+ }
+
+ // Merge Atlas configuration. This is a dumb one overrides the other
+ // sort of merge.
+ c.Atlas = c1.Atlas
+ if c2.Atlas != nil {
+ c.Atlas = c2.Atlas
+ }
+
+ // Merge the Terraform configuration
+ if c1.Terraform != nil {
+ c.Terraform = c1.Terraform
+ if c2.Terraform != nil {
+ c.Terraform.Merge(c2.Terraform)
+ }
+ } else {
+ c.Terraform = c2.Terraform
+ }
+
+ // NOTE: Everything below is pretty gross. Due to the lack of generics
+ // in Go, there is some hoop-jumping involved to make this merging a
+ // little more test-friendly and less repetitive. Ironically, making it
+ // less repetitive involves being a little repetitive, but I prefer to
+ // be repetitive with things that are less error prone than things that
+ // are more error prone (more logic). Type conversions to an interface
+ // are pretty low-error.
+
+ var m1, m2, mresult []merger
+
+ // Modules
+ m1 = make([]merger, 0, len(c1.Modules))
+ m2 = make([]merger, 0, len(c2.Modules))
+ for _, v := range c1.Modules {
+ m1 = append(m1, v)
+ }
+ for _, v := range c2.Modules {
+ m2 = append(m2, v)
+ }
+ mresult = mergeSlice(m1, m2)
+ if len(mresult) > 0 {
+ c.Modules = make([]*Module, len(mresult))
+ for i, v := range mresult {
+ c.Modules[i] = v.(*Module)
+ }
+ }
+
+ // Outputs
+ m1 = make([]merger, 0, len(c1.Outputs))
+ m2 = make([]merger, 0, len(c2.Outputs))
+ for _, v := range c1.Outputs {
+ m1 = append(m1, v)
+ }
+ for _, v := range c2.Outputs {
+ m2 = append(m2, v)
+ }
+ mresult = mergeSlice(m1, m2)
+ if len(mresult) > 0 {
+ c.Outputs = make([]*Output, len(mresult))
+ for i, v := range mresult {
+ c.Outputs[i] = v.(*Output)
+ }
+ }
+
+ // Provider Configs
+ m1 = make([]merger, 0, len(c1.ProviderConfigs))
+ m2 = make([]merger, 0, len(c2.ProviderConfigs))
+ for _, v := range c1.ProviderConfigs {
+ m1 = append(m1, v)
+ }
+ for _, v := range c2.ProviderConfigs {
+ m2 = append(m2, v)
+ }
+ mresult = mergeSlice(m1, m2)
+ if len(mresult) > 0 {
+ c.ProviderConfigs = make([]*ProviderConfig, len(mresult))
+ for i, v := range mresult {
+ c.ProviderConfigs[i] = v.(*ProviderConfig)
+ }
+ }
+
+ // Resources
+ m1 = make([]merger, 0, len(c1.Resources))
+ m2 = make([]merger, 0, len(c2.Resources))
+ for _, v := range c1.Resources {
+ m1 = append(m1, v)
+ }
+ for _, v := range c2.Resources {
+ m2 = append(m2, v)
+ }
+ mresult = mergeSlice(m1, m2)
+ if len(mresult) > 0 {
+ c.Resources = make([]*Resource, len(mresult))
+ for i, v := range mresult {
+ c.Resources[i] = v.(*Resource)
+ }
+ }
+
+ // Variables
+ m1 = make([]merger, 0, len(c1.Variables))
+ m2 = make([]merger, 0, len(c2.Variables))
+ for _, v := range c1.Variables {
+ m1 = append(m1, v)
+ }
+ for _, v := range c2.Variables {
+ m2 = append(m2, v)
+ }
+ mresult = mergeSlice(m1, m2)
+ if len(mresult) > 0 {
+ c.Variables = make([]*Variable, len(mresult))
+ for i, v := range mresult {
+ c.Variables[i] = v.(*Variable)
+ }
+ }
+
+ return c, nil
+}
+
+// merger is an interface that must be implemented by types that are
+// merge-able. This simplifies the implementation of Merge for the various
+// components of a Config.
+type merger interface {
+ mergerName() string
+ mergerMerge(merger) merger
+}
+
+// mergeSlice merges a slice of mergers.
+func mergeSlice(m1, m2 []merger) []merger {
+ r := make([]merger, len(m1), len(m1)+len(m2))
+ copy(r, m1)
+
+ m := map[string]struct{}{}
+ for _, v2 := range m2 {
+ // If we already saw it, just append it because its a
+ // duplicate and invalid...
+ name := v2.mergerName()
+ if _, ok := m[name]; ok {
+ r = append(r, v2)
+ continue
+ }
+ m[name] = struct{}{}
+
+ // Find an original to override
+ var original merger
+ originalIndex := -1
+ for i, v := range m1 {
+ if v.mergerName() == name {
+ originalIndex = i
+ original = v
+ break
+ }
+ }
+
+ var v merger
+ if original == nil {
+ v = v2
+ } else {
+ v = original.mergerMerge(v2)
+ }
+
+ if originalIndex == -1 {
+ r = append(r, v)
+ } else {
+ r[originalIndex] = v
+ }
+ }
+
+ return r
+}