@@ -11,95 +11,161 @@ import (
1111
1212var validName = regexp .MustCompile (`^[a-z0-9_]+$` )
1313
14- // InternalValidate returns an error if the receiving block and its child
15- // schema definitions have any consistencies with the documented rules for
16- // valid schema.
14+ // InternalValidate returns an error if the receiving block and its child schema
15+ // definitions have any inconsistencies with the documented rules for valid
16+ // schema.
1717//
18- // This is intended to be used within unit tests to detect when a given
19- // schema is invalid .
18+ // This can be used within unit tests to detect when a given schema is invalid,
19+ // and is run when terraform loads provider schemas during NewContext .
2020func (b * Block ) InternalValidate () error {
2121 if b == nil {
2222 return fmt .Errorf ("top-level block schema is nil" )
2323 }
24- return b .internalValidate ("" , nil )
25-
24+ return b .internalValidate ("" )
2625}
2726
28- func (b * Block ) internalValidate (prefix string , err error ) error {
27+ func (b * Block ) internalValidate (prefix string ) error {
28+ var multiErr * multierror.Error
29+
2930 for name , attrS := range b .Attributes {
3031 if attrS == nil {
31- err = multierror .Append (err , fmt .Errorf ("%s%s: attribute schema is nil" , prefix , name ))
32+ multiErr = multierror .Append (multiErr , fmt .Errorf ("%s%s: attribute schema is nil" , prefix , name ))
3233 continue
3334 }
34- if ! validName .MatchString (name ) {
35- err = multierror .Append (err , fmt .Errorf ("%s%s: name may contain only lowercase letters, digits and underscores" , prefix , name ))
36- }
37- if ! attrS .Optional && ! attrS .Required && ! attrS .Computed {
38- err = multierror .Append (err , fmt .Errorf ("%s%s: must set Optional, Required or Computed" , prefix , name ))
39- }
40- if attrS .Optional && attrS .Required {
41- err = multierror .Append (err , fmt .Errorf ("%s%s: cannot set both Optional and Required" , prefix , name ))
42- }
43- if attrS .Computed && attrS .Required {
44- err = multierror .Append (err , fmt .Errorf ("%s%s: cannot set both Computed and Required" , prefix , name ))
45- }
46- if attrS .Type == cty .NilType {
47- err = multierror .Append (err , fmt .Errorf ("%s%s: Type must be set to something other than cty.NilType" , prefix , name ))
48- }
35+ multiErr = multierror .Append (multiErr , attrS .internalValidate (name , prefix ))
4936 }
5037
5138 for name , blockS := range b .BlockTypes {
5239 if blockS == nil {
53- err = multierror .Append (err , fmt .Errorf ("%s%s: block schema is nil" , prefix , name ))
40+ multiErr = multierror .Append (multiErr , fmt .Errorf ("%s%s: block schema is nil" , prefix , name ))
5441 continue
5542 }
5643
5744 if _ , isAttr := b .Attributes [name ]; isAttr {
58- err = multierror .Append (err , fmt .Errorf ("%s%s: name defined as both attribute and child block type" , prefix , name ))
45+ multiErr = multierror .Append (multiErr , fmt .Errorf ("%s%s: name defined as both attribute and child block type" , prefix , name ))
5946 } else if ! validName .MatchString (name ) {
60- err = multierror .Append (err , fmt .Errorf ("%s%s: name may contain only lowercase letters, digits and underscores" , prefix , name ))
47+ multiErr = multierror .Append (multiErr , fmt .Errorf ("%s%s: name may contain only lowercase letters, digits and underscores" , prefix , name ))
6148 }
6249
6350 if blockS .MinItems < 0 || blockS .MaxItems < 0 {
64- err = multierror .Append (err , fmt .Errorf ("%s%s: MinItems and MaxItems must both be greater than zero" , prefix , name ))
51+ multiErr = multierror .Append (multiErr , fmt .Errorf ("%s%s: MinItems and MaxItems must both be greater than zero" , prefix , name ))
6552 }
6653
6754 switch blockS .Nesting {
6855 case NestingSingle :
6956 switch {
7057 case blockS .MinItems != blockS .MaxItems :
71- err = multierror .Append (err , fmt .Errorf ("%s%s: MinItems and MaxItems must match in NestingSingle mode" , prefix , name ))
58+ multiErr = multierror .Append (multiErr , fmt .Errorf ("%s%s: MinItems and MaxItems must match in NestingSingle mode" , prefix , name ))
7259 case blockS .MinItems < 0 || blockS .MinItems > 1 :
73- err = multierror .Append (err , fmt .Errorf ("%s%s: MinItems and MaxItems must be set to either 0 or 1 in NestingSingle mode" , prefix , name ))
60+ multiErr = multierror .Append (multiErr , fmt .Errorf ("%s%s: MinItems and MaxItems must be set to either 0 or 1 in NestingSingle mode" , prefix , name ))
7461 }
7562 case NestingGroup :
7663 if blockS .MinItems != 0 || blockS .MaxItems != 0 {
77- err = multierror .Append (err , fmt .Errorf ("%s%s: MinItems and MaxItems cannot be used in NestingGroup mode" , prefix , name ))
64+ multiErr = multierror .Append (multiErr , fmt .Errorf ("%s%s: MinItems and MaxItems cannot be used in NestingGroup mode" , prefix , name ))
7865 }
7966 case NestingList , NestingSet :
8067 if blockS .MinItems > blockS .MaxItems && blockS .MaxItems != 0 {
81- err = multierror .Append (err , fmt .Errorf ("%s%s: MinItems must be less than or equal to MaxItems in %s mode" , prefix , name , blockS .Nesting ))
68+ multiErr = multierror .Append (multiErr , fmt .Errorf ("%s%s: MinItems must be less than or equal to MaxItems in %s mode" , prefix , name , blockS .Nesting ))
8269 }
8370 if blockS .Nesting == NestingSet {
8471 ety := blockS .Block .ImpliedType ()
8572 if ety .HasDynamicTypes () {
8673 // This is not permitted because the HCL (cty) set implementation
8774 // needs to know the exact type of set elements in order to
8875 // properly hash them, and so can't support mixed types.
89- err = multierror .Append (err , fmt .Errorf ("%s%s: NestingSet blocks may not contain attributes of cty.DynamicPseudoType" , prefix , name ))
76+ multiErr = multierror .Append (multiErr , fmt .Errorf ("%s%s: NestingSet blocks may not contain attributes of cty.DynamicPseudoType" , prefix , name ))
9077 }
9178 }
9279 case NestingMap :
9380 if blockS .MinItems != 0 || blockS .MaxItems != 0 {
94- err = multierror .Append (err , fmt .Errorf ("%s%s: MinItems and MaxItems must both be 0 in NestingMap mode" , prefix , name ))
81+ multiErr = multierror .Append (multiErr , fmt .Errorf ("%s%s: MinItems and MaxItems must both be 0 in NestingMap mode" , prefix , name ))
9582 }
9683 default :
97- err = multierror .Append (err , fmt .Errorf ("%s%s: invalid nesting mode %s" , prefix , name , blockS .Nesting ))
84+ multiErr = multierror .Append (multiErr , fmt .Errorf ("%s%s: invalid nesting mode %s" , prefix , name , blockS .Nesting ))
9885 }
9986
10087 subPrefix := prefix + name + "."
101- err = blockS .Block .internalValidate (subPrefix , err )
88+ multiErr = multierror .Append (multiErr , blockS .Block .internalValidate (subPrefix ))
89+ }
90+
91+ return multiErr .ErrorOrNil ()
92+ }
93+
94+ // InternalValidate returns an error if the receiving attribute and its child
95+ // schema definitions have any inconsistencies with the documented rules for
96+ // valid schema.
97+ func (a * Attribute ) InternalValidate (name string ) error {
98+ if a == nil {
99+ return fmt .Errorf ("attribute schema is nil" )
100+ }
101+ return a .internalValidate (name , "" )
102+ }
103+
104+ func (a * Attribute ) internalValidate (name , prefix string ) error {
105+ var err * multierror.Error
106+
107+ /* FIXME: this validation breaks certain existing providers and cannot be enforced without coordination.
108+ if !validName.MatchString(name) {
109+ err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name))
110+ }
111+ */
112+ if ! a .Optional && ! a .Required && ! a .Computed {
113+ err = multierror .Append (err , fmt .Errorf ("%s%s: must set Optional, Required or Computed" , prefix , name ))
114+ }
115+ if a .Optional && a .Required {
116+ err = multierror .Append (err , fmt .Errorf ("%s%s: cannot set both Optional and Required" , prefix , name ))
117+ }
118+ if a .Computed && a .Required {
119+ err = multierror .Append (err , fmt .Errorf ("%s%s: cannot set both Computed and Required" , prefix , name ))
120+ }
121+
122+ if a .Type == cty .NilType && a .NestedType == nil {
123+ err = multierror .Append (err , fmt .Errorf ("%s%s: either Type or NestedType must be defined" , prefix , name ))
124+ }
125+
126+ if a .Type != cty .NilType {
127+ if a .NestedType != nil {
128+ err = multierror .Append (fmt .Errorf ("%s: Type and NestedType cannot both be set" , name ))
129+ }
130+ }
131+
132+ if a .NestedType != nil {
133+ switch a .NestedType .Nesting {
134+ case NestingSingle :
135+ switch {
136+ case a .NestedType .MinItems != a .NestedType .MaxItems :
137+ err = multierror .Append (err , fmt .Errorf ("%s%s: MinItems and MaxItems must match in NestingSingle mode" , prefix , name ))
138+ case a .NestedType .MinItems < 0 || a .NestedType .MinItems > 1 :
139+ err = multierror .Append (err , fmt .Errorf ("%s%s: MinItems and MaxItems must be set to either 0 or 1 in NestingSingle mode" , prefix , name ))
140+ }
141+ case NestingList , NestingSet :
142+ if a .NestedType .MinItems > a .NestedType .MaxItems && a .NestedType .MaxItems != 0 {
143+ err = multierror .Append (err , fmt .Errorf ("%s%s: MinItems must be less than or equal to MaxItems in %s mode" , prefix , name , a .NestedType .Nesting ))
144+ }
145+ if a .NestedType .Nesting == NestingSet {
146+ ety := a .NestedType .ImpliedType ()
147+ if ety .HasDynamicTypes () {
148+ // This is not permitted because the HCL (cty) set implementation
149+ // needs to know the exact type of set elements in order to
150+ // properly hash them, and so can't support mixed types.
151+ err = multierror .Append (err , fmt .Errorf ("%s%s: NestingSet blocks may not contain attributes of cty.DynamicPseudoType" , prefix , name ))
152+ }
153+ }
154+ case NestingMap :
155+ if a .NestedType .MinItems != 0 || a .NestedType .MaxItems != 0 {
156+ err = multierror .Append (err , fmt .Errorf ("%s%s: MinItems and MaxItems must both be 0 in NestingMap mode" , prefix , name ))
157+ }
158+ default :
159+ err = multierror .Append (err , fmt .Errorf ("%s%s: invalid nesting mode %s" , prefix , name , a .NestedType .Nesting ))
160+ }
161+ for name , attrS := range a .NestedType .Attributes {
162+ if attrS == nil {
163+ err = multierror .Append (err , fmt .Errorf ("%s%s: attribute schema is nil" , prefix , name ))
164+ continue
165+ }
166+ err = multierror .Append (err , attrS .internalValidate (name , prefix ))
167+ }
102168 }
103169
104- return err
170+ return err . ErrorOrNil ()
105171}
0 commit comments