Merge pull request #1307 from hashicorp/b-provider-config-merge

terraform: merge provider configs before validate [GH-1282]
This commit is contained in:
Mitchell Hashimoto 2015-03-25 16:54:53 -07:00
commit 19c7f8cff4
5 changed files with 96 additions and 18 deletions

View File

@ -2505,6 +2505,9 @@ func TestContext2Input_provider(t *testing.T) {
actual = c.Config["foo"] actual = c.Config["foo"]
return nil return nil
} }
p.ValidateFn = func(c *ResourceConfig) ([]string, []error) {
return nil, c.CheckSet([]string{"foo"})
}
if err := ctx.Input(InputModeStd); err != nil { if err := ctx.Input(InputModeStd); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)

View File

@ -6,17 +6,18 @@ import (
"github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config"
) )
// EvalConfigProvider is an EvalNode implementation that configures // EvalBuildProviderConfig outputs a *ResourceConfig that is properly
// a provider that is already initialized and retrieved. // merged with parents and inputs on top of what is configured in the file.
type EvalConfigProvider struct { type EvalBuildProviderConfig struct {
Provider string Provider string
Config **ResourceConfig Config **ResourceConfig
Output **ResourceConfig
} }
func (n *EvalConfigProvider) Eval(ctx EvalContext) (interface{}, error) { func (n *EvalBuildProviderConfig) Eval(ctx EvalContext) (interface{}, error) {
cfg := *n.Config cfg := *n.Config
// If we have a configuration set, then use that // If we have a configuration set, then merge that in
if input := ctx.ProviderInput(n.Provider); input != nil { if input := ctx.ProviderInput(n.Provider); input != nil {
rc, err := config.NewRawConfig(input) rc, err := config.NewRawConfig(input)
if err != nil { if err != nil {
@ -33,7 +34,19 @@ func (n *EvalConfigProvider) Eval(ctx EvalContext) (interface{}, error) {
cfg = NewResourceConfig(merged) cfg = NewResourceConfig(merged)
} }
return nil, ctx.ConfigureProvider(n.Provider, cfg) *n.Output = cfg
return nil, nil
}
// EvalConfigProvider is an EvalNode implementation that configures
// a provider that is already initialized and retrieved.
type EvalConfigProvider struct {
Provider string
Config **ResourceConfig
}
func (n *EvalConfigProvider) Eval(ctx EvalContext) (interface{}, error) {
return nil, ctx.ConfigureProvider(n.Provider, *n.Config)
} }
// EvalInitProvider is an EvalNode implementation that initializes a provider // EvalInitProvider is an EvalNode implementation that initializes a provider

View File

@ -5,6 +5,71 @@ import (
"testing" "testing"
) )
func TestEvalBuildProviderConfig_impl(t *testing.T) {
var _ EvalNode = new(EvalBuildProviderConfig)
}
func TestEvalBuildProviderConfig(t *testing.T) {
config := testResourceConfig(t, map[string]interface{}{})
provider := "foo"
n := &EvalBuildProviderConfig{
Provider: provider,
Config: &config,
Output: &config,
}
ctx := &MockEvalContext{
ParentProviderConfigConfig: testResourceConfig(t, map[string]interface{}{
"foo": "bar",
}),
ProviderInputConfig: map[string]interface{}{
"bar": "baz",
},
}
if _, err := n.Eval(ctx); err != nil {
t.Fatalf("err: %s", err)
}
expected := map[string]interface{}{
"foo": "bar",
"bar": "baz",
}
if !reflect.DeepEqual(config.Raw, expected) {
t.Fatalf("bad: %#v", config.Raw)
}
}
func TestEvalBuildProviderConfig_parentPriority(t *testing.T) {
config := testResourceConfig(t, map[string]interface{}{})
provider := "foo"
n := &EvalBuildProviderConfig{
Provider: provider,
Config: &config,
Output: &config,
}
ctx := &MockEvalContext{
ParentProviderConfigConfig: testResourceConfig(t, map[string]interface{}{
"foo": "bar",
}),
ProviderInputConfig: map[string]interface{}{
"foo": "baz",
},
}
if _, err := n.Eval(ctx); err != nil {
t.Fatalf("err: %s", err)
}
expected := map[string]interface{}{
"foo": "bar",
}
if !reflect.DeepEqual(config.Raw, expected) {
t.Fatalf("bad: %#v", config.Raw)
}
}
func TestEvalConfigProvider_impl(t *testing.T) { func TestEvalConfigProvider_impl(t *testing.T) {
var _ EvalNode = new(EvalConfigProvider) var _ EvalNode = new(EvalConfigProvider)
} }

View File

@ -57,21 +57,14 @@ RETURN:
// EvalValidateProvider is an EvalNode implementation that validates // EvalValidateProvider is an EvalNode implementation that validates
// the configuration of a resource. // the configuration of a resource.
type EvalValidateProvider struct { type EvalValidateProvider struct {
ProviderName string Provider *ResourceProvider
Provider *ResourceProvider Config **ResourceConfig
Config **ResourceConfig
} }
func (n *EvalValidateProvider) Eval(ctx EvalContext) (interface{}, error) { func (n *EvalValidateProvider) Eval(ctx EvalContext) (interface{}, error) {
provider := *n.Provider provider := *n.Provider
config := *n.Config config := *n.Config
// Get the parent configuration if there is one
if parent := ctx.ParentProviderConfig(n.ProviderName); parent != nil {
merged := parent.raw.Merge(config.raw)
config = NewResourceConfig(merged)
}
warns, errs := provider.Validate(config) warns, errs := provider.Validate(config)
if len(warns) == 0 && len(errs) == 0 { if len(warns) == 0 && len(errs) == 0 {
return nil, nil return nil, nil

View File

@ -44,10 +44,14 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode {
Config: config, Config: config,
Output: &resourceConfig, Output: &resourceConfig,
}, },
&EvalBuildProviderConfig{
Provider: n,
Config: &resourceConfig,
Output: &resourceConfig,
},
&EvalValidateProvider{ &EvalValidateProvider{
ProviderName: n, Provider: &provider,
Provider: &provider, Config: &resourceConfig,
Config: &resourceConfig,
}, },
&EvalConfigProvider{ &EvalConfigProvider{
Provider: n, Provider: n,