Merge pull request #10720 from hashicorp/b-merge
config: Merge respects Terraform blocks, provider aliases, and more
This commit is contained in:
commit
8a6b267577
|
@ -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))
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -240,15 +240,25 @@ 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{})
|
||||||
|
if r2 != nil {
|
||||||
for k, v := range r2.Raw {
|
for k, v := range r2.Raw {
|
||||||
raw[k] = v
|
raw[k] = v
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result, err := NewRawConfig(raw)
|
result, err := NewRawConfig(raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue