diff --git a/builtin/providers/aws/resource_provider.go b/builtin/providers/aws/resource_provider.go index 287a3e842..465fb2e3d 100644 --- a/builtin/providers/aws/resource_provider.go +++ b/builtin/providers/aws/resource_provider.go @@ -7,8 +7,13 @@ import ( type ResourceProvider struct { } -func (p *ResourceProvider) Validate(*terraform.ResourceConfig) ([]string, []error) { - return nil, nil +func (p *ResourceProvider) Validate(c *terraform.ResourceConfig) ([]string, []error) { + errs := c.CheckSet([]string{ + "access_key", + "secret_key", + }) + + return nil, errs } func (p *ResourceProvider) Configure(*terraform.ResourceConfig) error { diff --git a/terraform/resource_provider.go b/terraform/resource_provider.go index 6fe006f55..6ef9d3bd3 100644 --- a/terraform/resource_provider.go +++ b/terraform/resource_provider.go @@ -1,6 +1,10 @@ package terraform import ( + "fmt" + "reflect" + "strings" + "github.com/hashicorp/terraform/config" ) @@ -66,6 +70,69 @@ func NewResourceConfig(c *config.RawConfig) *ResourceConfig { } } +// CheckSet checks that the given list of configuration keys is +// properly set. If not, errors are returned for each unset key. +// +// This is useful to be called in the Validate method of a ResourceProvider. +func (c *ResourceConfig) CheckSet(keys []string) []error { + var errs []error + + for _, k := range keys { + if !c.IsSet(k) { + errs = append(errs, fmt.Errorf("%s must be set", k)) + } + } + + return errs +} + +// Get looks up a configuration value by key and returns the value. +// +// The second return value is true if the get was successful. Get will +// not succeed if the value is being computed. +func (c *ResourceConfig) Get(k string) (interface{}, bool) { + parts := strings.Split(k, ".") + + var current interface{} = c.Raw + for _, part := range parts { + if current == nil { + return nil, false + } + + cv := reflect.ValueOf(current) + switch cv.Kind() { + case reflect.Map: + v := cv.MapIndex(reflect.ValueOf(part)) + if !v.IsValid() { + return nil, false + } + current = v.Interface() + } + } + + return current, true +} + +// IsSet checks if the key in the configuration is set. A key is set if +// it has a value or the value is being computed (is unknown currently). +// +// This function should be used rather than checking the keys of the +// raw configuration itself, since a key may be omitted from the raw +// configuration if it is being computed. +func (c *ResourceConfig) IsSet(k string) bool { + for _, ck := range c.ComputedKeys { + if ck == k { + return true + } + } + + if _, ok := c.Get(k); ok { + return true + } + + return false +} + func ProviderSatisfies(p ResourceProvider, n string) bool { for _, rt := range p.Resources() { if rt.Name == n { diff --git a/terraform/resource_provider_test.go b/terraform/resource_provider_test.go new file mode 100644 index 000000000..c62ff46f1 --- /dev/null +++ b/terraform/resource_provider_test.go @@ -0,0 +1,103 @@ +package terraform + +import ( + "testing" +) + +func TestResourceConfig_CheckSet(t *testing.T) { + cases := []struct { + Raw map[string]interface{} + Computed []string + Input []string + Errs bool + }{ + { + map[string]interface{}{ + "foo": "bar", + }, + nil, + []string{"foo"}, + false, + }, + { + map[string]interface{}{ + "foo": "bar", + }, + nil, + []string{"foo", "bar"}, + true, + }, + { + map[string]interface{}{ + "foo": "bar", + }, + []string{"bar"}, + []string{"foo", "bar"}, + false, + }, + } + + for i, tc := range cases { + rc := &ResourceConfig{ + ComputedKeys: tc.Computed, + Raw: tc.Raw, + } + + errs := rc.CheckSet(tc.Input) + if tc.Errs != (len(errs) > 0) { + t.Fatalf("bad: %d", i) + } + } +} + +func TestResourceConfig_IsSet(t *testing.T) { + cases := []struct { + Raw map[string]interface{} + Computed []string + Input string + Output bool + }{ + { + map[string]interface{}{ + "foo": "bar", + }, + nil, + "foo", + true, + }, + { + map[string]interface{}{}, + nil, + "foo", + false, + }, + { + map[string]interface{}{}, + []string{"foo"}, + "foo", + true, + }, + { + map[string]interface{}{ + "foo": map[interface{}]interface{}{ + "bar": "baz", + }, + }, + nil, + "foo.bar", + true, + }, + } + + for i, tc := range cases { + rc := &ResourceConfig{ + ComputedKeys: tc.Computed, + Raw: tc.Raw, + } + + actual := rc.IsSet(tc.Input) + if actual != tc.Output { + t.Fatalf("fail case: %d", i) + } + } +}