Skip to content

Commit

Permalink
helper/schema: Adding of MinItems as a validation to Lists and Maps
Browse files Browse the repository at this point in the history
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
  • Loading branch information
stack72 committed Oct 4, 2016
1 parent a879323 commit 5a537cd
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 2 deletions.
15 changes: 13 additions & 2 deletions helper/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,14 @@ type Schema struct {
// 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
// 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{}
MaxItems int
MinItems int

// The following fields are only valid for a TypeSet type.
//
Expand Down Expand Up @@ -597,8 +603,8 @@ func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error {
}
}
} else {
if v.MaxItems > 0 {
return fmt.Errorf("%s: MaxItems is only supported on lists or sets", k)
if v.MaxItems > 0 || v.MinItems > 0 {
return fmt.Errorf("%s: MaxItems and MinItems are only supported on lists or sets", k)
}
}

Expand Down Expand Up @@ -1128,6 +1134,11 @@ func (m schemaMap) validateList(
"%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{}
raws := make([]interface{}, rawV.Len())
for i, _ := range raws {
Expand Down
91 changes: 91 additions & 0 deletions helper/schema/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
}

0 comments on commit 5a537cd

Please sign in to comment.