config: Merge respects Terraform blocks, provider aliases, and more
Fixes #10715 `config.Merge` was not updated to support a number of new features. This updates the codepath to merge various fields, including the `terraform` block which was the issue in #10715. The `Merge` API is called when an `_override` file is present to _merge_ configurations. Normally configurations are _appended_. Only an override file triggers a _merge_. I started working on a generic library to do this automatically awhile back but never finished it. This might motivate me to do so. In the interest of getting a fix out though, we'll continue the manual approach.
This commit is contained in:
parent
014b414839
commit
3878b8b093
|
@ -915,7 +915,10 @@ func (o *Output) mergerMerge(m merger) merger {
|
|||
|
||||
result := *o
|
||||
result.Name = o2.Name
|
||||
result.Description = o2.Description
|
||||
result.RawConfig = result.RawConfig.merge(o2.RawConfig)
|
||||
result.Sensitive = o2.Sensitive
|
||||
result.DependsOn = o2.DependsOn
|
||||
|
||||
return &result
|
||||
}
|
||||
|
@ -943,6 +946,10 @@ func (c *ProviderConfig) mergerMerge(m merger) merger {
|
|||
result.Name = c2.Name
|
||||
result.RawConfig = result.RawConfig.merge(c2.RawConfig)
|
||||
|
||||
if c2.Alias != "" {
|
||||
result.Alias = c2.Alias
|
||||
}
|
||||
|
||||
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.
|
||||
result.Name = v2.Name
|
||||
|
||||
if v2.DeclaredType != "" {
|
||||
result.DeclaredType = v2.DeclaredType
|
||||
}
|
||||
if v2.Default != nil {
|
||||
result.Default = v2.Default
|
||||
}
|
||||
|
|
|
@ -32,6 +32,12 @@ func Merge(c1, c2 *Config) (*Config, error) {
|
|||
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
|
||||
// in Go, there is some hoop-jumping involved to make this merging a
|
||||
// little more test-friendly and less repetitive. Ironically, making it
|
||||
|
|
|
@ -153,6 +153,287 @@ func TestMerge(t *testing.T) {
|
|||
|
||||
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 {
|
||||
|
|
|
@ -240,15 +240,25 @@ func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error {
|
|||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
raw := rawRaw.(map[string]interface{})
|
||||
if r2 != nil {
|
||||
for k, v := range r2.Raw {
|
||||
raw[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
result, err := NewRawConfig(raw)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue