From 0250c17d6ef6774f700b933d1ce4b8a47b9d07fe Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 9 Sep 2014 21:33:08 -0700 Subject: [PATCH] helper/schema: DefaultFunc for dynamic defaults /cc @c4milo - This might be useful to you as well. --- helper/schema/schema.go | 37 ++++++++++++- helper/schema/schema_test.go | 103 +++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 1 deletion(-) diff --git a/helper/schema/schema.go b/helper/schema/schema.go index 0e3d4cb9f..d1d96e9ac 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -64,7 +64,15 @@ type Schema struct { // If this is non-nil, then this will be a default value that is used // when this item is not set in the configuration/state. - Default interface{} + // + // DefaultFunc can be specified if you want a dynamic default value. + // Only one of Default or DefaultFunc can be set. + // + // If Required is true above, then Default cannot be set. DefaultFunc + // can be set with Required. If the DefaultFunc returns nil, then there + // will no default and the user will be asked to fill it in. + Default interface{} + DefaultFunc SchemaDefaultFunc // The fields below relate to diffs. // @@ -104,6 +112,10 @@ type Schema struct { ComputedWhen []string } +// SchemaDefaultFunc is a function called to return a default value for +// a field. +type SchemaDefaultFunc func() (interface{}, error) + // SchemaSetFunc is a function that must return a unique ID for the given // element. This unique ID is used to store the element in a hash. type SchemaSetFunc func(interface{}) int @@ -288,6 +300,10 @@ func (m schemaMap) InternalValidate() error { return fmt.Errorf("%s: Default must be nil if computed", k) } + if v.Required && v.Default != nil { + return fmt.Errorf("%s: Default cannot be set with Required", k) + } + if len(v.ComputedWhen) > 0 && !v.Computed { return fmt.Errorf("%s: ComputedWhen can only be set with Computed", k) } @@ -506,6 +522,13 @@ func (m schemaMap) diffString( o, n, _ := d.diffChange(k) if n == nil { n = schema.Default + if schema.DefaultFunc != nil { + var err error + n, err = schema.DefaultFunc() + if err != nil { + return fmt.Errorf("%s, error loading default: %s", err) + } + } } if schema.StateFunc != nil { originalN = n @@ -551,6 +574,18 @@ func (m schemaMap) validate( schema *Schema, c *terraform.ResourceConfig) ([]string, []error) { raw, ok := c.Get(k) + if !ok && schema.DefaultFunc != nil { + // We have a dynamic default. Check if we have a value. + var err error + raw, err = schema.DefaultFunc() + if err != nil { + return nil, []error{fmt.Errorf( + "%s, error loading default: %s", k, err)} + } + + // We're okay as long as we had a value set + ok = raw != nil + } if !ok { if schema.Required { return nil, []error{fmt.Errorf( diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index 94a9d0901..6bd70649b 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -124,6 +124,64 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, + // DefaultFunc, value + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.ResourceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + }, + }, + }, + + Err: false, + }, + + // DefaultFunc, configuration set + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "bar", + }, + + Diff: &terraform.ResourceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + }, + }, + }, + + Err: false, + }, + // String with StateFunc { Schema: map[string]*Schema{ @@ -1055,6 +1113,19 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, + // Required but has default + { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + Required: true, + Default: "foo", + }, + }, + true, + }, + // List element not set { map[string]*Schema{ @@ -1227,6 +1298,38 @@ func TestSchemaMap_Validate(t *testing.T) { Err: true, }, + // Required but has DefaultFunc + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Required: true, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + }, + }, + + Config: nil, + }, + + // Required but has DefaultFunc return nil + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Required: true, + DefaultFunc: func() (interface{}, error) { + return nil, nil + }, + }, + }, + + Config: nil, + + Err: true, + }, + // Optional sub-resource { Schema: map[string]*Schema{