helper/schema: DefaultFunc for dynamic defaults

/cc @c4milo - This might be useful to you as well.
This commit is contained in:
Mitchell Hashimoto 2014-09-09 21:33:08 -07:00
parent e8d09da6fa
commit 0250c17d6e
2 changed files with 139 additions and 1 deletions

View File

@ -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(

View File

@ -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{