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

Support wildcards with transformation targets #2325

Merged
merged 19 commits into from
Jun 9, 2022

Conversation

theunrepentantgeek
Copy link
Member

What this PR does / why we need it:

Despite the appearance of fragments like this in our configuration

  - name: "*"
    property: Copy
    ifType:
      group: definitions
      version: "*"
      name: ResourceCopy
      optional: true
    remove: true
    because: ... elided... 

We did not support wildcards within the ifType section; the matching on version * was accidentally working, due to the way our package references filtered out non-alphanumeric characters during creation.

In PR #2294 I'm removing this filtering to improve our diagnostics, breaking this match.

This PR fixes the matching to properly work with wildcards. We could do some further work to improve diagnostics, etc, but I figured this was change enough.

I've included caching to improve the performance of the transforms, and to reduce the amount of heap debris, hopefully lowering the amount of time spent doing garbage collection. Crudely measures, this seems to help:

Without caching:

8/64: Modify property types using configured transforms, completed in 2.162s
8/64: Modify property types using configured transforms, completed in 2.328s

With caching

8/64: Modify property types using configured transforms, completed in 1.221s
8/64: Modify property types using configured transforms, completed in 1.099s

Special notes for your reviewer:

I've left in tests I wrote during troubleshooting.

How does this PR make you feel:
gif

If applicable:

  • this PR contains tests

@@ -16,6 +16,8 @@ type StringMatcher interface {
Matches(value string) bool
// WasMatched returns nil if the matcher had a match, otherwise returning a diagnostic error
WasMatched() error
// IsRestrictive returns true if the matcher is populated and will restrict matches
IsRestrictive() bool
Copy link
Member

Choose a reason for hiding this comment

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

This name threw me for a bit before I understood what it was doing. I'm not sure that I necessarily have a better name for it, but wondering if there is one.

NeedsMatch or RequiresMatch? Not really sure those are any better...

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think those are better - they imply that tha match is required, whereas the semantics I'm looking for are "is there a matcher here that will reduce the number of matches?"

return nil
}

func (target TransformTarget) produceTargetType(
Copy link
Member

Choose a reason for hiding this comment

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

be consistent with ptr or non-ptr. This one is non-ptr while all the rest are ptr

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

}

func (target *TransformTarget) appliesToType(t astmodel.Type) bool {
if target == nil {
Copy link
Member

Choose a reason for hiding this comment

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

Does this ever actually happen? Do we actually call appliesToType on a nil target? Guarding against this is sorta weird but it'd be weird if this ever happened too?

Copy link
Member Author

Choose a reason for hiding this comment

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

It seemed like an easy way to remove boilerplate from calling methods. A nil value can happen if a matcher is never populated (say, if it is not used in the YAML).

inspect := t
if target.Optional {
// Need optional
opt, ok := astmodel.AsOptionalType(inspect)
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this is what target.Optional meant in the old code? Looking at the original TransformTarget, optional was used like this in produceTargetType:

if target.Optional {
	result = astmodel.NewOptionalType(result)
}

That's the only place it was used. Maybe you're fixing a bug here where it just was ignored when used on ifType? I think that's what's happening but just wanna confirm.

Copy link
Member Author

Choose a reason for hiding this comment

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

That result type was used with a Equals() to see if we had the type that needed to be modified; this approach doesn't work with wildcards. Instead, we check to see if we need optional, and if we do, whether we have it.

opt, ok := astmodel.AsOptionalType(inspect)
if !ok {
// but don't have optional
return false
Copy link
Member

Choose a reason for hiding this comment

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

This could be a bit weird though, because what error is going to result when you set Optional: True, then match a non-optional type. Is it going to say "I couldn't find that property"? That's not actually correct though because it can find it, it just didn't match the optional requirement.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll tweak the error message to say "matched types but all properties were excluded by name or type" (the bold bit is new)

@@ -36,11 +39,9 @@ type TypeTransformer struct {

// IfType only performs the transform if the original type matches (only usable with Property at the moment)
IfType *TransformTarget `yaml:"ifType,omitempty"`
ifType astmodel.Type // cache the astmodel type
Copy link
Member

Choose a reason for hiding this comment

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

Previously this was using produceTargetType for both the IfType and the Target sides of the equation. Then it would produce an astmodel.Type which we wanted to match and use Type.Equals to determine if the match worked (for ifType, and then swap it with the targetType.

While that was sorta "tricky", it did mean that the produceTargetType code was pulling double duty and we could lean on type.Equals. I think I understand that now we can't actually do that because by support wildcards (which type.Equals doesn't understand) we need to do our own matching, hence your AppliesToType. is that right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Excactly.

@theunrepentantgeek theunrepentantgeek enabled auto-merge (squash) June 9, 2022 01:40
@theunrepentantgeek theunrepentantgeek merged commit b631588 into main Jun 9, 2022
@theunrepentantgeek theunrepentantgeek deleted the fix/type-target-wildcards branch June 9, 2022 03:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Development

Successfully merging this pull request may close these issues.

2 participants