Skip to content

Commit

Permalink
Add MaxItems attribute to Schema
Browse files Browse the repository at this point in the history
 * MaxItems defines a maximum 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 more than one instance would
   cause instability.
  • Loading branch information
Chris Marchesi committed Feb 24, 2016
1 parent 3e69e2e commit 8c5354b
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 1 deletion.
18 changes: 17 additions & 1 deletion helper/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,13 @@ type Schema struct {
// TypeList, and represents what the element type is. If it is *Schema,
// the element type is just a simple value. If it is *Resource, the
// element type is a complex structure, potentially with its own lifecycle.
Elem interface{}
//
// MaxItems defines a maximum 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 more than one instance would
// cause instability.
Elem interface{}
MaxItems int

// The following fields are only valid for a TypeSet type.
//
Expand Down Expand Up @@ -541,6 +547,10 @@ func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error {
"%s: Elem must have only Type set", k)
}
}
} else {
if v.MaxItems > 0 {
return fmt.Errorf("%s: MaxItems is only supported on lists or sets", k)
}
}

if v.ValidateFunc != nil {
Expand Down Expand Up @@ -1049,6 +1059,12 @@ func (m schemaMap) validateList(
"%s: should be a list", k)}
}

// Validate length
if schema.MaxItems > 0 && rawV.Len() > schema.MaxItems {
return nil, []error{fmt.Errorf(
"%s: attribute supports %d item maximum, config has %d declared", k, schema.MaxItems, 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 @@ -3647,3 +3647,94 @@ func TestSchemaMap_Validate(t *testing.T) {
}
}
}

func TestSchemaSet_ValidateMaxItems(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,
MaxItems: 1,
Elem: &Schema{Type: TypeString},
},
},
State: nil,
Config: map[string]interface{}{
"aliases": []interface{}{"foo", "bar"},
},
Diff: nil,
Err: true,
Errors: []error{
fmt.Errorf("aliases: attribute supports 1 item maximum, config has 2 declared"),
},
},
"#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,
MaxItems: 1,
Elem: &Schema{Type: TypeString},
},
},
State: nil,
Config: map[string]interface{}{
"aliases": []interface{}{"foo"},
},
Diff: nil,
Err: false,
Errors: nil,
},
}

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 8c5354b

Please sign in to comment.