helper/schema: Adding of MinItems as a validation to Lists and Maps

This is required for the times when the configuration cannot have an
empty configuration. An example would be in AzureRM, when you create a
LoadBalancer with a configuration, you can delete *all* but 1 of these
configurations
This commit is contained in:
stack72 2016-10-04 18:37:23 +01:00
parent a879323d23
commit 5a537cdbf9
No known key found for this signature in database
GPG Key ID: 8619A619B085CB16
2 changed files with 104 additions and 2 deletions

View File

@ -113,8 +113,14 @@ type Schema struct {
// TypeSet or TypeList. Specific use cases would be if a TypeSet is being // TypeSet or TypeList. Specific use cases would be if a TypeSet is being
// used to wrap a complex structure, however more than one instance would // used to wrap a complex structure, however more than one instance would
// cause instability. // cause instability.
//
// MinItems defines a minimum amount of items that can exist within a
// TypeSet or TypeList. Specific use cases would be if a TypeSet is being
// used to wrap a complex structure, however less than one instance would
// cause instability.
Elem interface{} Elem interface{}
MaxItems int MaxItems int
MinItems int
// The following fields are only valid for a TypeSet type. // The following fields are only valid for a TypeSet type.
// //
@ -597,8 +603,8 @@ func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error {
} }
} }
} else { } else {
if v.MaxItems > 0 { if v.MaxItems > 0 || v.MinItems > 0 {
return fmt.Errorf("%s: MaxItems is only supported on lists or sets", k) return fmt.Errorf("%s: MaxItems and MinItems are only supported on lists or sets", k)
} }
} }
@ -1128,6 +1134,11 @@ func (m schemaMap) validateList(
"%s: attribute supports %d item maximum, config has %d declared", k, schema.MaxItems, rawV.Len())} "%s: attribute supports %d item maximum, config has %d declared", k, schema.MaxItems, rawV.Len())}
} }
if schema.MinItems > 0 && rawV.Len() < schema.MinItems {
return nil, []error{fmt.Errorf(
"%s: attribute supports %d item as a minimum, config has %d declared", k, schema.MinItems, rawV.Len())}
}
// Now build the []interface{} // Now build the []interface{}
raws := make([]interface{}, rawV.Len()) raws := make([]interface{}, rawV.Len())
for i, _ := range raws { for i, _ := range raws {

View File

@ -3895,3 +3895,94 @@ func TestSchemaSet_ValidateMaxItems(t *testing.T) {
} }
} }
} }
func TestSchemaSet_ValidateMinItems(t *testing.T) {
cases := map[string]struct {
Schema map[string]*Schema
State *terraform.InstanceState
Config map[string]interface{}
ConfigVariables map[string]string
Diff *terraform.InstanceDiff
Err bool
Errors []error
}{
"#0": {
Schema: map[string]*Schema{
"aliases": &Schema{
Type: TypeSet,
Optional: true,
MinItems: 2,
Elem: &Schema{Type: TypeString},
},
},
State: nil,
Config: map[string]interface{}{
"aliases": []interface{}{"foo", "bar"},
},
Diff: nil,
Err: false,
Errors: nil,
},
"#1": {
Schema: map[string]*Schema{
"aliases": &Schema{
Type: TypeSet,
Optional: true,
Elem: &Schema{Type: TypeString},
},
},
State: nil,
Config: map[string]interface{}{
"aliases": []interface{}{"foo", "bar"},
},
Diff: nil,
Err: false,
Errors: nil,
},
"#2": {
Schema: map[string]*Schema{
"aliases": &Schema{
Type: TypeSet,
Optional: true,
MinItems: 2,
Elem: &Schema{Type: TypeString},
},
},
State: nil,
Config: map[string]interface{}{
"aliases": []interface{}{"foo"},
},
Diff: nil,
Err: true,
Errors: []error{
fmt.Errorf("aliases: attribute supports 2 item as a minimum, config has 1 declared"),
},
},
}
for tn, tc := range cases {
c, err := config.NewRawConfig(tc.Config)
if err != nil {
t.Fatalf("%q: err: %s", tn, err)
}
_, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c))
if len(es) > 0 != tc.Err {
if len(es) == 0 {
t.Errorf("%q: no errors", tn)
}
for _, e := range es {
t.Errorf("%q: err: %s", tn, e)
}
t.FailNow()
}
if tc.Errors != nil {
if !reflect.DeepEqual(es, tc.Errors) {
t.Fatalf("%q: expected: %q\ngot: %q", tn, tc.Errors, es)
}
}
}
}