From 8a2eb95a42c83ceb9f1cc88bfd3fe8ac84d87be2 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 12 Jun 2014 22:30:09 -0700 Subject: [PATCH] terraform: Validate called on provider --- terraform/resource_provider.go | 21 +++++++++++++++++++ terraform/resource_provider_mock.go | 10 +++++++++ terraform/terraform.go | 21 ++++++++++++++++++- terraform/terraform_test.go | 20 ++++++++++++++++++ .../new-provider-validate/main.tf | 5 +++++ 5 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 terraform/test-fixtures/new-provider-validate/main.tf diff --git a/terraform/resource_provider.go b/terraform/resource_provider.go index 4b40bb3c6..6fe006f55 100644 --- a/terraform/resource_provider.go +++ b/terraform/resource_provider.go @@ -1,9 +1,22 @@ package terraform +import ( + "github.com/hashicorp/terraform/config" +) + // ResourceProvider is an interface that must be implemented by any // resource provider: the thing that creates and manages the resources in // a Terraform configuration. type ResourceProvider interface { + // Validate is called once at the beginning with the raw configuration + // (no interpolation done) and can return a list of warnings and/or + // errors. + // + // This should not assume that any values of the configurations are valid. + // The primary use case of this call is to check that required keys are + // set. + Validate(*ResourceConfig) ([]string, []error) + // Configure configures the provider itself with the configuration // given. This is useful for setting things like access keys. // @@ -45,6 +58,14 @@ type ResourceType struct { // of a resource provider. type ResourceProviderFactory func() (ResourceProvider, error) +// NewResourceConfig creates a new ResourceConfig from a config.RawConfig. +func NewResourceConfig(c *config.RawConfig) *ResourceConfig { + return &ResourceConfig{ + ComputedKeys: c.UnknownKeys(), + Raw: c.Raw, + } +} + func ProviderSatisfies(p ResourceProvider, n string) bool { for _, rt := range p.Resources() { if rt.Name == n { diff --git a/terraform/resource_provider_mock.go b/terraform/resource_provider_mock.go index 4c9c1cb9c..a973f7927 100644 --- a/terraform/resource_provider_mock.go +++ b/terraform/resource_provider_mock.go @@ -17,6 +17,16 @@ type MockResourceProvider struct { DiffReturnError error ResourcesCalled bool ResourcesReturn []ResourceType + ValidateCalled bool + ValidateConfig *ResourceConfig + ValidateReturnWarns []string + ValidateReturnErrors []error +} + +func (p *MockResourceProvider) Validate(c *ResourceConfig) ([]string, []error) { + p.ValidateCalled = true + p.ValidateConfig = c + return p.ValidateReturnWarns, p.ValidateReturnErrors } func (p *MockResourceProvider) Configure(c *ResourceConfig) error { diff --git a/terraform/terraform.go b/terraform/terraform.go index f31db7a69..c2424143e 100644 --- a/terraform/terraform.go +++ b/terraform/terraform.go @@ -60,6 +60,25 @@ func New(c *Config) (*Terraform, error) { errs = append(errs, err...) } + // Validate all the configurations, once. + tps := make(map[*terraformProvider]struct{}) + for _, tp := range mapping { + if _, ok := tps[tp]; !ok { + tps[tp] = struct{}{} + } + } + for tp, _ := range tps { + var rc *ResourceConfig + if tp.Config != nil { + rc = NewResourceConfig(tp.Config.RawConfig) + } + + _, tpErrs := tp.Provider.Validate(rc) + if len(tpErrs) > 0 { + errs = append(errs, tpErrs...) + } + } + // Build the resource graph graph := c.Config.Graph() if err := graph.Validate(); err != nil { @@ -190,7 +209,7 @@ func (t *terraformProvider) init(vars map[string]string) (err error) { rc = &ResourceConfig{ ComputedKeys: t.Config.RawConfig.UnknownKeys(), - Raw: t.Config.RawConfig.Config(), + Raw: t.Config.RawConfig.Config(), } } diff --git a/terraform/terraform_test.go b/terraform/terraform_test.go index 3015dd044..82b06fb89 100644 --- a/terraform/terraform_test.go +++ b/terraform/terraform_test.go @@ -139,6 +139,26 @@ func TestNew_providerConfigCache(t *testing.T) { } } +func TestNew_providerValidate(t *testing.T) { + config := testConfig(t, "new-provider-validate") + tfConfig := &Config{ + Config: config, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFunc("aws", []string{"aws_instance"}), + }, + } + + tf, err := New(tfConfig) + if err != nil { + t.Fatalf("err: %s", err) + } + + p := testProviderMock(testProvider(tf, "aws_instance.foo")) + if !p.ValidateCalled { + t.Fatal("validate should be called") + } +} + func TestNew_variables(t *testing.T) { config := testConfig(t, "new-variables") tfConfig := &Config{ diff --git a/terraform/test-fixtures/new-provider-validate/main.tf b/terraform/test-fixtures/new-provider-validate/main.tf new file mode 100644 index 000000000..1a3bc66e1 --- /dev/null +++ b/terraform/test-fixtures/new-provider-validate/main.tf @@ -0,0 +1,5 @@ +provider "aws" { + foo = "bar" +} + +resource "aws_instance" "foo" {}