Add schema.ConflictsWith[]
- this will allow defining logically conflicting attributes
This commit is contained in:
parent
1c0f2f136c
commit
34f48b3e06
|
@ -114,6 +114,9 @@ type Schema struct {
|
||||||
// NOTE: This currently does not work.
|
// NOTE: This currently does not work.
|
||||||
ComputedWhen []string
|
ComputedWhen []string
|
||||||
|
|
||||||
|
// ConflictsWith is a set of schema keys that conflict with this schema
|
||||||
|
ConflictsWith []string
|
||||||
|
|
||||||
// When Deprecated is set, this attribute is deprecated.
|
// When Deprecated is set, this attribute is deprecated.
|
||||||
//
|
//
|
||||||
// A deprecated field still works, but will probably stop working in near
|
// A deprecated field still works, but will probably stop working in near
|
||||||
|
@ -436,6 +439,22 @@ func (m schemaMap) InternalValidate() error {
|
||||||
return fmt.Errorf("%s: ComputedWhen can only be set with Computed", k)
|
return fmt.Errorf("%s: ComputedWhen can only be set with Computed", k)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(v.ConflictsWith) > 0 && v.Required {
|
||||||
|
return fmt.Errorf("%s: ConflictsWith cannot be set with Required", k)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(v.ConflictsWith) > 0 {
|
||||||
|
for _, key := range v.ConflictsWith {
|
||||||
|
if m[key].Required {
|
||||||
|
return fmt.Errorf("%s: ConflictsWith cannot contain Required attribute (%s)", k, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m[key].Computed || len(m[key].ComputedWhen) > 0 {
|
||||||
|
return fmt.Errorf("%s: ConflictsWith cannot contain Computed(When) attribute (%s)", k, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if v.Type == TypeList || v.Type == TypeSet {
|
if v.Type == TypeList || v.Type == TypeSet {
|
||||||
if v.Elem == nil {
|
if v.Elem == nil {
|
||||||
return fmt.Errorf("%s: Elem must be set for lists", k)
|
return fmt.Errorf("%s: Elem must be set for lists", k)
|
||||||
|
@ -913,9 +932,33 @@ func (m schemaMap) validate(
|
||||||
"%q: this field cannot be set", k)}
|
"%q: this field cannot be set", k)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := m.validateConflictingAttributes(k, schema, c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, []error{err}
|
||||||
|
}
|
||||||
|
|
||||||
return m.validateType(k, raw, schema, c)
|
return m.validateType(k, raw, schema, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m schemaMap) validateConflictingAttributes(
|
||||||
|
k string,
|
||||||
|
schema *Schema,
|
||||||
|
c *terraform.ResourceConfig) error {
|
||||||
|
|
||||||
|
if len(schema.ConflictsWith) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, conflicting_key := range schema.ConflictsWith {
|
||||||
|
if value, ok := c.Get(conflicting_key); ok {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%q: conflicts with %s (%#v)", k, conflicting_key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m schemaMap) validateList(
|
func (m schemaMap) validateList(
|
||||||
k string,
|
k string,
|
||||||
raw interface{},
|
raw interface{},
|
||||||
|
|
|
@ -2555,6 +2555,66 @@ func TestSchemaMap_InternalValidate(t *testing.T) {
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Conflicting attributes cannot be required
|
||||||
|
{
|
||||||
|
map[string]*Schema{
|
||||||
|
"blacklist": &Schema{
|
||||||
|
Type: TypeBool,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"whitelist": &Schema{
|
||||||
|
Type: TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
ConflictsWith: []string{"blacklist"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Attribute with conflicts cannot be required
|
||||||
|
{
|
||||||
|
map[string]*Schema{
|
||||||
|
"whitelist": &Schema{
|
||||||
|
Type: TypeBool,
|
||||||
|
Required: true,
|
||||||
|
ConflictsWith: []string{"blacklist"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// ConflictsWith cannot be used w/ Computed
|
||||||
|
{
|
||||||
|
map[string]*Schema{
|
||||||
|
"blacklist": &Schema{
|
||||||
|
Type: TypeBool,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"whitelist": &Schema{
|
||||||
|
Type: TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
ConflictsWith: []string{"blacklist"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// ConflictsWith cannot be used w/ ComputedWhen
|
||||||
|
{
|
||||||
|
map[string]*Schema{
|
||||||
|
"blacklist": &Schema{
|
||||||
|
Type: TypeBool,
|
||||||
|
ComputedWhen: []string{"foor"},
|
||||||
|
},
|
||||||
|
"whitelist": &Schema{
|
||||||
|
Type: TypeBool,
|
||||||
|
Required: true,
|
||||||
|
ConflictsWith: []string{"blacklist"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
|
||||||
// Sub-resource invalid
|
// Sub-resource invalid
|
||||||
{
|
{
|
||||||
map[string]*Schema{
|
map[string]*Schema{
|
||||||
|
@ -2594,7 +2654,10 @@ func TestSchemaMap_InternalValidate(t *testing.T) {
|
||||||
for i, tc := range cases {
|
for i, tc := range cases {
|
||||||
err := schemaMap(tc.In).InternalValidate()
|
err := schemaMap(tc.In).InternalValidate()
|
||||||
if (err != nil) != tc.Err {
|
if (err != nil) != tc.Err {
|
||||||
t.Fatalf("%d: bad: %s\n\n%#v", i, err, tc.In)
|
if tc.Err {
|
||||||
|
t.Fatalf("%d: Expected error did not occur:\n\n%#v", i, tc.In)
|
||||||
|
}
|
||||||
|
t.Fatalf("%d: Unexpected error occured:\n\n%#v", i, tc.In)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3103,6 +3166,74 @@ func TestSchemaMap_Validate(t *testing.T) {
|
||||||
|
|
||||||
Err: false,
|
Err: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"Conflicting attributes generate error": {
|
||||||
|
Schema: map[string]*Schema{
|
||||||
|
"whitelist": &Schema{
|
||||||
|
Type: TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"blacklist": &Schema{
|
||||||
|
Type: TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ConflictsWith: []string{"whitelist"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"whitelist": "white-val",
|
||||||
|
"blacklist": "black-val",
|
||||||
|
},
|
||||||
|
|
||||||
|
Err: true,
|
||||||
|
Errors: []error{
|
||||||
|
fmt.Errorf("\"blacklist\": conflicts with whitelist (\"white-val\")"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"Required attribute & undefined conflicting optional are good": {
|
||||||
|
Schema: map[string]*Schema{
|
||||||
|
"required_att": &Schema{
|
||||||
|
Type: TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"optional_att": &Schema{
|
||||||
|
Type: TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ConflictsWith: []string{"required_att"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"required_att": "required-val",
|
||||||
|
},
|
||||||
|
|
||||||
|
Err: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
"Required conflicting attribute & defined optional generate error": {
|
||||||
|
Schema: map[string]*Schema{
|
||||||
|
"required_att": &Schema{
|
||||||
|
Type: TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"optional_att": &Schema{
|
||||||
|
Type: TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ConflictsWith: []string{"required_att"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"required_att": "required-val",
|
||||||
|
"optional_att": "optional-val",
|
||||||
|
},
|
||||||
|
|
||||||
|
Err: true,
|
||||||
|
Errors: []error{
|
||||||
|
fmt.Errorf("\"optional_att\": conflicts with required_att (\"required-val\")"),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for tn, tc := range cases {
|
for tn, tc := range cases {
|
||||||
|
|
Loading…
Reference in New Issue