Merge pull request #10720 from hashicorp/b-merge

config: Merge respects Terraform blocks, provider aliases, and more
This commit is contained in:
Mitchell Hashimoto 2016-12-14 13:49:26 -08:00 committed by GitHub
commit 8a6b267577
6 changed files with 345 additions and 2 deletions

View File

@ -35,6 +35,11 @@ func Append(c1, c2 *Config) (*Config, error) {
c.Atlas = c2.Atlas c.Atlas = c2.Atlas
} }
c.Terraform = c1.Terraform
if c2.Terraform != nil {
c.Terraform = c2.Terraform
}
if len(c1.Modules) > 0 || len(c2.Modules) > 0 { if len(c1.Modules) > 0 || len(c2.Modules) > 0 {
c.Modules = make( c.Modules = make(
[]*Module, 0, len(c1.Modules)+len(c2.Modules)) []*Module, 0, len(c1.Modules)+len(c2.Modules))

View File

@ -87,6 +87,37 @@ func TestAppend(t *testing.T) {
false, false,
}, },
// Terraform block
{
&Config{
Terraform: &Terraform{
RequiredVersion: "A",
},
},
&Config{},
&Config{
Terraform: &Terraform{
RequiredVersion: "A",
},
},
false,
},
{
&Config{},
&Config{
Terraform: &Terraform{
RequiredVersion: "A",
},
},
&Config{
Terraform: &Terraform{
RequiredVersion: "A",
},
},
false,
},
} }
for i, tc := range cases { for i, tc := range cases {

View File

@ -915,7 +915,10 @@ func (o *Output) mergerMerge(m merger) merger {
result := *o result := *o
result.Name = o2.Name result.Name = o2.Name
result.Description = o2.Description
result.RawConfig = result.RawConfig.merge(o2.RawConfig) result.RawConfig = result.RawConfig.merge(o2.RawConfig)
result.Sensitive = o2.Sensitive
result.DependsOn = o2.DependsOn
return &result return &result
} }
@ -943,6 +946,10 @@ func (c *ProviderConfig) mergerMerge(m merger) merger {
result.Name = c2.Name result.Name = c2.Name
result.RawConfig = result.RawConfig.merge(c2.RawConfig) result.RawConfig = result.RawConfig.merge(c2.RawConfig)
if c2.Alias != "" {
result.Alias = c2.Alias
}
return &result return &result
} }
@ -978,6 +985,9 @@ func (v *Variable) Merge(v2 *Variable) *Variable {
// The names should be the same, but the second name always wins. // The names should be the same, but the second name always wins.
result.Name = v2.Name result.Name = v2.Name
if v2.DeclaredType != "" {
result.DeclaredType = v2.DeclaredType
}
if v2.Default != nil { if v2.Default != nil {
result.Default = v2.Default result.Default = v2.Default
} }

View File

@ -32,6 +32,12 @@ func Merge(c1, c2 *Config) (*Config, error) {
c.Atlas = c2.Atlas c.Atlas = c2.Atlas
} }
// Merge the Terraform configuration, which is a complete overwrite.
c.Terraform = c1.Terraform
if c2.Terraform != nil {
c.Terraform = c2.Terraform
}
// NOTE: Everything below is pretty gross. Due to the lack of generics // 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 // in Go, there is some hoop-jumping involved to make this merging a
// little more test-friendly and less repetitive. Ironically, making it // little more test-friendly and less repetitive. Ironically, making it

View File

@ -153,6 +153,287 @@ func TestMerge(t *testing.T) {
false, false,
}, },
// Terraform block
{
&Config{
Terraform: &Terraform{
RequiredVersion: "A",
},
},
&Config{},
&Config{
Terraform: &Terraform{
RequiredVersion: "A",
},
},
false,
},
{
&Config{},
&Config{
Terraform: &Terraform{
RequiredVersion: "A",
},
},
&Config{
Terraform: &Terraform{
RequiredVersion: "A",
},
},
false,
},
// Provider alias
{
&Config{
ProviderConfigs: []*ProviderConfig{
&ProviderConfig{Alias: "foo"},
},
},
&Config{},
&Config{
ProviderConfigs: []*ProviderConfig{
&ProviderConfig{Alias: "foo"},
},
},
false,
},
{
&Config{},
&Config{
ProviderConfigs: []*ProviderConfig{
&ProviderConfig{Alias: "foo"},
},
},
&Config{
ProviderConfigs: []*ProviderConfig{
&ProviderConfig{Alias: "foo"},
},
},
false,
},
{
&Config{
ProviderConfigs: []*ProviderConfig{
&ProviderConfig{Alias: "bar"},
},
},
&Config{
ProviderConfigs: []*ProviderConfig{
&ProviderConfig{Alias: "foo"},
},
},
&Config{
ProviderConfigs: []*ProviderConfig{
&ProviderConfig{Alias: "foo"},
},
},
false,
},
// Variable type
{
&Config{
Variables: []*Variable{
&Variable{DeclaredType: "foo"},
},
},
&Config{},
&Config{
Variables: []*Variable{
&Variable{DeclaredType: "foo"},
},
},
false,
},
{
&Config{},
&Config{
Variables: []*Variable{
&Variable{DeclaredType: "foo"},
},
},
&Config{
Variables: []*Variable{
&Variable{DeclaredType: "foo"},
},
},
false,
},
{
&Config{
Variables: []*Variable{
&Variable{DeclaredType: "bar"},
},
},
&Config{
Variables: []*Variable{
&Variable{DeclaredType: "foo"},
},
},
&Config{
Variables: []*Variable{
&Variable{DeclaredType: "foo"},
},
},
false,
},
// Output description
{
&Config{
Outputs: []*Output{
&Output{Description: "foo"},
},
},
&Config{},
&Config{
Outputs: []*Output{
&Output{Description: "foo"},
},
},
false,
},
{
&Config{},
&Config{
Outputs: []*Output{
&Output{Description: "foo"},
},
},
&Config{
Outputs: []*Output{
&Output{Description: "foo"},
},
},
false,
},
{
&Config{
Outputs: []*Output{
&Output{Description: "bar"},
},
},
&Config{
Outputs: []*Output{
&Output{Description: "foo"},
},
},
&Config{
Outputs: []*Output{
&Output{Description: "foo"},
},
},
false,
},
// Output depends_on
{
&Config{
Outputs: []*Output{
&Output{DependsOn: []string{"foo"}},
},
},
&Config{},
&Config{
Outputs: []*Output{
&Output{DependsOn: []string{"foo"}},
},
},
false,
},
{
&Config{},
&Config{
Outputs: []*Output{
&Output{DependsOn: []string{"foo"}},
},
},
&Config{
Outputs: []*Output{
&Output{DependsOn: []string{"foo"}},
},
},
false,
},
{
&Config{
Outputs: []*Output{
&Output{DependsOn: []string{"bar"}},
},
},
&Config{
Outputs: []*Output{
&Output{DependsOn: []string{"foo"}},
},
},
&Config{
Outputs: []*Output{
&Output{DependsOn: []string{"foo"}},
},
},
false,
},
// Output sensitive
{
&Config{
Outputs: []*Output{
&Output{Sensitive: true},
},
},
&Config{},
&Config{
Outputs: []*Output{
&Output{Sensitive: true},
},
},
false,
},
{
&Config{},
&Config{
Outputs: []*Output{
&Output{Sensitive: true},
},
},
&Config{
Outputs: []*Output{
&Output{Sensitive: true},
},
},
false,
},
{
&Config{
Outputs: []*Output{
&Output{Sensitive: false},
},
},
&Config{
Outputs: []*Output{
&Output{Sensitive: true},
},
},
&Config{
Outputs: []*Output{
&Output{Sensitive: true},
},
},
false,
},
} }
for i, tc := range cases { for i, tc := range cases {

View File

@ -240,14 +240,24 @@ func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error {
} }
func (r *RawConfig) merge(r2 *RawConfig) *RawConfig { func (r *RawConfig) merge(r2 *RawConfig) *RawConfig {
if r == nil && r2 == nil {
return nil
}
if r == nil {
r = &RawConfig{}
}
rawRaw, err := copystructure.Copy(r.Raw) rawRaw, err := copystructure.Copy(r.Raw)
if err != nil { if err != nil {
panic(err) panic(err)
} }
raw := rawRaw.(map[string]interface{}) raw := rawRaw.(map[string]interface{})
for k, v := range r2.Raw { if r2 != nil {
raw[k] = v for k, v := range r2.Raw {
raw[k] = v
}
} }
result, err := NewRawConfig(raw) result, err := NewRawConfig(raw)