-
Notifications
You must be signed in to change notification settings - Fork 69
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
Implement policy-level tag regex filtering #75
Conversation
@@ -0,0 +1,70 @@ | |||
/* |
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.
Not super happy about how this was laid out, couldn't think of a nicer way to implement it at the moment though.
@relu can this be used for automating Minio releases? https://hub.docker.com/r/minio/minio/tags?page=1&ordering=last_updated note that users will want the latest multi-arch image (doesn't end in arm/amd/etc) and only the stable releases that start with RELEASE. |
@stefanprodan yes, since it uses the RFC3339 format it should be lexicographically sortable, using the alphabetical ordering policy it would go something like this: filterTags:
pattern: '^RELEASE\.(?P<timestamp>.*)Z$'
extract: '$timestamp'
policy:
alphabetical:
order: asc I think it can even be simplified to this: filterTags:
pattern: '^RELEASE.*'
policy:
alphabetical:
order: asc |
3dca47f
to
bc7f16e
Compare
internal/policy/filter.go
Outdated
@@ -0,0 +1,70 @@ | |||
/* | |||
Copyright 2020 The Flux authors |
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.
Elsewhere I have used 2020, 2021
for files that are either new, or modified this year. 2020
is OK, since the important bit is the year of first publication -- adding 2021 is just more information for readers.
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.
Yes, I didn't bother with this because I already had this laying around since last year. I think 2021 is better for this particular file because it was pretty much rewritten entirely today.
internal/policy/filter.go
Outdated
func NewRegexFilter(pattern string, replace string) (*RegexFilter, error) { | ||
m, err := regexp.Compile(pattern) | ||
if err != nil { | ||
return nil, fmt.Errorf("invalid regular expression pattern '%s': %s", pattern, err.Error()) |
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.
return nil, fmt.Errorf("invalid regular expression pattern '%s': %s", pattern, err.Error()) | |
return nil, fmt.Errorf("invalid regular expression pattern '%s': %w", pattern, err) |
internal/policy/filter.go
Outdated
if f.Regexp.MatchString(item) { | ||
tag := item | ||
if f.Replace != "" { | ||
tag = f.Regexp.ReplaceAllString(item, f.Replace) |
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.
I think you can do this without repeating work, using Find*Index and Expand.
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.
Not sure I follow here, what do you refer to as repeated work? I would assume it'll look kind of like this, right?
if submatches := f.Regexp.FindStringSubmatchIndex(item); len(submatches) > 0 {
tag := item
if f.Replace != "" {
result := []byte{}
result = f.Regexp.ExpandString(result, f.Replace, item, submatches)
tag = string(result)
}
f.filtered[tag] = item
}
The above works just fine but I'm not sure I see the benefit 😕
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.
Both MatchString
and ReplaceAllString
"run" the regular expression -- it's just that the former disregards submatches, and the latter also does substitution. On the other hand, FindStringSubmatchIndex
runs the regular expression to get the submatches (and whether it matches at all), but ExpandString does only the substitution work.
It is unlikely to make a vast difference to any individual match, but all else being equal, it's worth avoiding extra calculation.
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.
Got it, thanks for the explanation, I just looked into more detail in the implementation of ReplaceAllString
vs ExpandString
and I did notice now what you mean. I will refactor.
} | ||
filter.Apply(tags) | ||
} | ||
latest, err = policer.Latest(tags, filter) |
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.
Running the flow of control the other way would be clearer here. This way around, you supply a filter to the Policer
, and it has to know how to deal with it. But if you filter here, and give the Policer
the extracted names, it can determine which one it wants without having to know whether they are filtered or not. Then you can map back to the original again here (if there was a filter in the first place).
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.
One issue, whichever way around you do it, is what to do if the extracted strings are duplicated? E.g., if you have (dev|stage)-(.*)
, and extract $2
, you will get a duplicate from the tags {"dev-v1.0", "stage-v1.0"}
. At present the last one encountered wins (since RegexpFilter keeps track of them in a map). But this isn't stable -- you can get different results, without anything actually changing.
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.
Thanks for the feedback, I think your suggestion makes a lot of sense and would simplify things significantly in the policies Latest
implementation.
As for the second comment, I thought about this as well when writing the tests, I wasn't able to think of a real-world scenario where this would become an issue but I think it does make sense to specify this in the documentation since it's indeed an edge case some might stumble upon. I don't know if we can do anything about this conflict aside from warning users about it.
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.
it does make sense to specify this in the documentation since it's indeed an edge case some might stumble upon
I agree with this, and
warning users about it
with this. If it happens, it could be pretty mysterious, so we could log a message with a reference to docs or decent search term.
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.
(we can discuss these alternatives a bit more in #77)
bc7f16e
to
5a0089e
Compare
How would this work if you were adding a build ID to the end? For example, you have two images:
And your policy is defined as:
Would the |
@nomeelnoj you will want to include the pre-release part (what you mentioned as being the build id) to the capture group. filterTags:
pattern: '^dev-v(.*)'
extract: '$1'
policy:
semver:
range: 1.2.x-0 Notice that I've added a |
@relu thanks! very excited for this to be merged, as I believe this is the final blocker from getting us over to v2 (v1 is less than functional because the helm operator is now having issues with the old stable repo being deprecated). How long after this is merged will a release be cut? |
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.
LGTM
Thanks @relu 🥇
fd8a131
to
1434254
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.
A very welcome addition to the API, thank you @relu.
I'll take a look at those test failures ... |
25a1ccd
to
81624b5
Compare
Tag regex filtering allows the user to filter tags based on a regular expression pattern and enables tag version extraction through capture group replacement reference. Fixes #73 Signed-off-by: Aurel Canciu <[email protected]>
81624b5
to
cbcad12
Compare
Tag regex filtering allows the user to filter tags based on a regular
expression pattern and enables tag version extraction through capture
group replacement reference.
Fixes #73
Example use cases (for documentation):
etc.