Skip to content
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

test(manifest): add integ test making sure all structs implement Validate #2895

Merged
merged 6 commits into from
Oct 6, 2021

Conversation

iamhopaul123
Copy link
Contributor

This PR

  1. Add integ test to make sure every manifest struct or advanced type implements Validate().
  2. Fix a bug that network.placement should not be required even if network is not empty.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the Apache 2.0 License.

@iamhopaul123 iamhopaul123 requested a review from a team as a code owner October 4, 2021 23:06
@iamhopaul123 iamhopaul123 requested review from efekarakus and removed request for a team October 4, 2021 23:06
@iamhopaul123 iamhopaul123 force-pushed the manifest-validation-audit branch from 02e9895 to 45bd195 Compare October 4, 2021 23:13
@@ -44,7 +44,26 @@ var (
)

// Validate returns nil if LoadBalancedWebService is configured correctly.
func (l *LoadBalancedWebService) Validate() error {
func (l LoadBalancedWebService) Validate() error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why we switched the receivers from pointers to values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it won't work otherwise since Validate() would be the method of * LoadBalancedWebService. Also I feel it doesn't make the code much cleaner with pointer receiver.

@@ -44,7 +44,26 @@ var (
)

// Validate returns nil if LoadBalancedWebService is configured correctly.
func (l *LoadBalancedWebService) Validate() error {
func (l LoadBalancedWebService) Validate() error {
var err error
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we remove the variable with:

if err := l...Validate();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm wouldn't this avoid defining err repetitively?

return nil
}
if val := int(*p); val < 0 || val > 100 {
func (p Percentage) Validate() error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok so by moving it to value receivers we're assuming the callers will do the nil-check from now on right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that's right.

Comment on lines 70 to 77
var val validator
validatorType := reflect.TypeOf(&val).Elem()
// template.Parser is not a manifest struct.
var tpl template.Parser
templaterType := reflect.TypeOf(&tpl).Elem()
if !typ.Implements(templaterType) && !typ.Implements(validatorType) {
return fmt.Errorf(`%v does not implement "Validate()"`, typ)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if instead of ignoring template.Parser as a type we can check if the field has a "yaml" struct tag and if so check it? https://stackoverflow.com/a/23507821/1201381

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea!

@iamhopaul123 iamhopaul123 force-pushed the manifest-validation-audit branch from 06e827a to e5e75ef Compare October 5, 2021 21:12
Copy link
Contributor

@Lou1415926 Lou1415926 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two questions 🤚🏼

}
for i := 0; i < typ.NumField(); i++ {
// Skip if it is not a manifest yaml field.
if typ.Field(i).Tag.Get("yaml") == "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about fields such as HealthCheckArgs that does not have a tag?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true. I guess I'd rather revert this change @efekarakus

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😭 I'm just not a fan because it feels so fragile otherwise.
As a requirement, it makes sense to me to read "any field used during unmarshalling YAML should be validated", but now we lose that nice definition.

Isn't it good enough to have the Validate method on HealthCheckArgsOrString ? since that already will call internally Validate on any of its exclusive subfields. I'd rather have us keep typ.Field(i).Tag.Get("yaml") == ""

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since that already will call internally Validate on any of its exclusive subfields.

Problem is this is not always guaranteed...other people could forget to do so, which is the whole point to have this test

@iamhopaul123 iamhopaul123 force-pushed the manifest-validation-audit branch from e5e75ef to 45bd195 Compare October 5, 2021 23:05
}
// For slice and map, validate individual member.
if typ.Kind() == reflect.Array || typ.Kind() == reflect.Slice {
for i := 0; i < v.Len(); i++ {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a thought 💭 .

Since isValid validates whether a type is implementing an interface as expected, should we use t reflect.Type as its parameter, instead of v reflect.Vlaue?

Then here we can use elemTyp := typ.Elem() to retrieve the element type of the array/slice, and then call isValid(elemTyp).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we are now skipping private fields, we have to use value instead of type

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but i guess you are right. we only need to check the first element since they are all the same

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discussed offline now use structfield.IsExported() to check so that we can use type as the parameter.

}
if typ.Kind() == reflect.Map {
for _, k := range v.MapKeys() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And also use typ.Key() and typ.Elem() here.

})
}
}
// func TestPlatformString_Validate(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this intentionally commented out?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦

for _, k := range basicKinds {
types = append(types, k.String())
}
types = append(types, reflect.TypeOf(map[string]string{}).String())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about this:

In isValid, we detect the map's key and value types, and recursively call isValid if they are of non-basic types.

Therefore, we don't need special treatment for map[string]string - since string and string are basic, they won't get called on isValid.

Same for slice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is what i was doing! i forgot to remove those

@iamhopaul123 iamhopaul123 force-pushed the manifest-validation-audit branch from 13094ba to 4834a9e Compare October 6, 2021 21:49
@iamhopaul123 iamhopaul123 force-pushed the manifest-validation-audit branch from d2675f0 to 703d597 Compare October 6, 2021 22:05
Copy link
Contributor

@Lou1415926 Lou1415926 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hooray!

@mergify mergify bot merged commit 46743e7 into aws:mainline Oct 6, 2021
thrau pushed a commit to localstack/copilot-cli-local that referenced this pull request Dec 9, 2022
…date (aws#2895)

<!-- Provide summary of changes -->
This PR
1. Add integ test to make sure every manifest struct or advanced type implements `Validate()`.
2. Fix a bug that `network.placement` should not be required even if `network` is not empty.

<!-- Issue number, if available. E.g. "Fixes aws#31", "Addresses aws#42, 77" -->

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the Apache 2.0 License.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants