-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Checks: semantics validation on attribute reference #163
New Checks: semantics validation on attribute reference #163
Conversation
bfc47a3
to
2a7b858
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @magodo 👋 Thanks for submitting this. Please see the below initial feedback and let me know if you have any questions.
passes/S038/README.md
Outdated
@@ -0,0 +1,86 @@ | |||
# S038 | |||
|
|||
The S038 analyzer reports cases of Schemas which include `ConflictsWith` and have invalid schema attribute references. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The S038 analyzer reports cases of Schemas which include `ConflictsWith` and have invalid schema attribute references. | |
The S038 analyzer reports cases of Schemas which include `AtLeastOneOf` and have invalid schema attribute references. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the mistake 😅
curSchema := curSchemaOrResource.(*tfschema.Schema) | ||
if curSchema.MaxItems != 1 || curSchema.Type != tfschema.TypeList { | ||
return nil, fmt.Errorf("%q configuration block attribute references are only valid for TypeList and MaxItems: 1 attributes", attributeReference) | ||
} | ||
curSchemaOrResource = curSchema.Elem |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please introduce type assertion safety here with a switch
statement utilizing .(type)
:
curSchema := curSchemaOrResource.(*tfschema.Schema) | |
if curSchema.MaxItems != 1 || curSchema.Type != tfschema.TypeList { | |
return nil, fmt.Errorf("%q configuration block attribute references are only valid for TypeList and MaxItems: 1 attributes", attributeReference) | |
} | |
curSchemaOrResource = curSchema.Elem | |
switch current := curSchemaOrResource.(type) { | |
case *tfschema.Schema: | |
if current.MaxItems != 1 || current.Type != tfschema.TypeList { | |
return nil, fmt.Errorf("%q configuration block attribute references are only valid for TypeList and MaxItems: 1 attributes", attributeReference) | |
} | |
curSchemaOrResource = current.Elem | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. Also for the default case (which means we are not capable to deal with for now), I think we shall just return nil
to avoid false positive.
// For even part, ensure it references to defined attribute | ||
schema := curSchemaOrResource.(*tfschema.Resource).Schema[attributeReferencePart] | ||
if schema == nil { | ||
return nil, fmt.Errorf("%q references to unknown attribute", attributeReference) | ||
} | ||
curSchemaOrResource = schema |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. We will want to be careful for unexpected types and missing key references:
// For even part, ensure it references to defined attribute | |
schema := curSchemaOrResource.(*tfschema.Resource).Schema[attributeReferencePart] | |
if schema == nil { | |
return nil, fmt.Errorf("%q references to unknown attribute", attributeReference) | |
} | |
curSchemaOrResource = schema | |
// For even part, ensure it references to defined attribute | |
switch current := curSchemaOrResource.(type) { | |
case *tfschema.Resource: | |
if current.Schema == nil { | |
return nil, fmt.Errorf("%q references attribute without schema") | |
} | |
schema, ok := current.Schema[attributeReferencePart] | |
if !ok || schema == nil { | |
return nil, fmt.Errorf("%q references to unknown attribute", attributeReference) | |
} | |
curSchemaOrResource = schema | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A little improvement of this might be just using schema := current.Schema[attributeReferencePart]
instead, and in absent key case, it will return the zero value of the Value, in which case it is nil
(since we do not differ between absent key and nil value).
passes/S038/README.md
Outdated
|
||
The S038 analyzer reports cases of Schemas which include `ConflictsWith` and have invalid schema attribute references. | ||
|
||
NOTE: This pass only works with Terraform resources that are fully defined in a single function. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Terraform resources that meet the criteria for this check can come from a variety of source code, e.g. variables, inline declarations in schema.Provider.ResourcesMap
, etc.
NOTE: This pass only works with Terraform resources that are fully defined in a single function. | |
NOTE: This pass only works with `schema.Resource` that are wholly declared. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right
passes/S038/README.md
Outdated
## Flagged Code | ||
|
||
```go | ||
// Attribute reference in multi nested block is not supported |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Attribute reference in multi nested block is not supported | |
// Attribute reference in configuration block with potentially more than one element is not supported |
}, | ||
}, | ||
} | ||
// Failing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's ensure the following additional failure cases are tested:
- TypeSet
- Root attribute referencing non-existing root attribute
- Root attribute referencing existing configuration block attribute with non-existing nested attribute
- Configuration block nested attribute referencing non-existing root attribute
- Configuration block nested attribute referencing existing configuration block attribute with non-existing nested attribute
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, and I will also add the corresponding pass test cases for those scenarios.
}, | ||
} | ||
|
||
// Passing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's ensure the following additional passing cases are tested:
- Root attribute referencing existing root attribute (e.g. https://github.com/terraform-providers/terraform-provider-aws/blob/2cb4e3f7f017feb338e0ba2ffdf409d6b0c91793/aws/resource_aws_cognito_user_pool.go#L90)
- Root attribute referencing existing configuration block attribute with existing nested attribute (e.g. https://github.com/terraform-providers/terraform-provider-aws/blob/2cb4e3f7f017feb338e0ba2ffdf409d6b0c91793/aws/resource_aws_cognito_user_pool.go#L176)
- Configuration block nested attribute referencing existing root attribute (e.g. https://github.com/terraform-providers/terraform-provider-aws/blob/2cb4e3f7f017feb338e0ba2ffdf409d6b0c91793/aws/resource_aws_cognito_user_pool.go#L517)
- Configuration block nested attribute referencing existing configuration block attribute with existing nested attribute (e.g. https://github.com/terraform-providers/terraform-provider-aws/blob/2cb4e3f7f017feb338e0ba2ffdf409d6b0c91793/aws/resource_aws_cognito_user_pool.go#L307)
- Deeper level configuration block attribute references (e.g. https://github.com/terraform-providers/terraform-provider-aws/blob/2cb4e3f7f017feb338e0ba2ffdf409d6b0c91793/aws/resource_aws_appmesh_virtual_node.go#L227)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
1. Add more test cases for different scenarios, both pass and failure cases. 2. Add type assertion to avoid crash on unepxected schema declarations. 3. Modify the signature of `ValidateAttributeReference()` to only return the validation resutl.
@bflad Thank you for the quick response👍 |
Hi again @magodo 👋 After much thought about this and the recent release of Terraform Plugin SDK v2, which includes unit testing for |
@bflad No worry 🙃 Also thanks for the unit testing news in the new SDK! |
This is similar to S035-S037, except rather than syntax checks, it applies a semantics validation. The terraform acc test could do some semantics validation on attribute reference, while it failed to test for other cases like referring to attribute to multi nested blocks.
For example, the following is the explanation of the S038 implemented in this PR:
S038
The S038 analyzer reports cases of Schemas which include
ConflictsWith
and have invalid schema attribute references.NOTE: This pass only works with Terraform resources that are fully defined in a single function.
Flagged Code
or
Passing Code
Ignoring Reports
Singular reports can be ignored by adding the a
//lintignore:S038
Go code comment at the end of the offending line or on the line immediately proceding, e.g.