Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Commit

Permalink
Merge pull request #1292 from hiddeco/1239-regex-filter
Browse files Browse the repository at this point in the history
Image filtering with regular expressions
  • Loading branch information
squaremo authored Aug 17, 2018
2 parents 580a688 + 0adcc0a commit 1b3eec0
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 5 deletions.
17 changes: 17 additions & 0 deletions cluster/kubernetes/policies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,23 @@ func TestUpdatePolicies(t *testing.T) {
},
wantErr: true,
},
{
name: "add regexp tag policy",
in: nil,
out: []string{"flux.weave.works/tag.nginx", "regexp:(.*?)"},
update: policy.Update{
Add: policy.Set{policy.TagPrefix("nginx"): "regexp:(.*?)"},
},
},
{
name: "add invalid regexp tag policy",
in: nil,
out: []string{"flux.weave.works/tag.nginx", "regexp:(.*?)"},
update: policy.Update{
Add: policy.Set{policy.TagPrefix("nginx"): "regexp:*"},
},
wantErr: true,
},
} {
t.Run(c.name, func(t *testing.T) {
caseIn := templToString(t, annotationsTemplate, c.in)
Expand Down
43 changes: 38 additions & 5 deletions policy/pattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import (
"github.com/ryanuber/go-glob"
"github.com/weaveworks/flux/image"
"strings"
"regexp"
)

const (
globPrefix = "glob:"
semverPrefix = "semver:"
regexpPrefix = "regexp:"
)

var (
Expand Down Expand Up @@ -39,17 +41,28 @@ type SemverPattern struct {
constraints *semver.Constraints
}

// NewPattern instantiates a Pattern according to the prefix
// it finds. The prefix can be either `glob:` (default if omitted)
// or `semver:`.
// RegexpPattern matches by regular expression.
type RegexpPattern struct {
pattern string // pattern without prefix
regexp *regexp.Regexp
}

// NewPattern instantiates a Pattern according to the prefix
// it finds. The prefix can be either `glob:` (default if omitted),
// `semver:` or `regexp:`.
func NewPattern(pattern string) Pattern {
if strings.HasPrefix(pattern, semverPrefix) {
switch {
case strings.HasPrefix(pattern, semverPrefix):
pattern = strings.TrimPrefix(pattern, semverPrefix)
c, _ := semver.NewConstraint(pattern)
return SemverPattern{pattern, c}
case strings.HasPrefix(pattern, regexpPrefix):
pattern = strings.TrimPrefix(pattern, regexpPrefix)
r, _ := regexp.Compile(pattern)
return RegexpPattern{pattern, r}
default:
return GlobPattern(strings.TrimPrefix(pattern, globPrefix))
}
return GlobPattern(strings.TrimPrefix(pattern, globPrefix))
}

func (g GlobPattern) Matches(tag string) bool {
Expand Down Expand Up @@ -91,3 +104,23 @@ func (s SemverPattern) Newer(a, b *image.Info) bool {
func (s SemverPattern) Valid() bool {
return s.constraints != nil
}

func (r RegexpPattern) Matches(tag string) bool {
if r.regexp == nil {
// Invalid regexp match anything
return true
}
return r.regexp.MatchString(tag)
}

func (r RegexpPattern) String() string {
return regexpPrefix + r.pattern
}

func (r RegexpPattern) Newer(a, b *image.Info) bool {
return image.NewerByCreated(a, b)
}

func (r RegexpPattern) Valid() bool {
return r.regexp != nil
}
35 changes: 35 additions & 0 deletions policy/pattern_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,38 @@ func TestSemverPattern_Matches(t *testing.T) {
}
}
}

func TestRegexpPattern_Matches(t *testing.T) {
for _, tt := range []struct {
name string
pattern string
true []string
false []string
}{
{
name: "all prefixed",
pattern: "regexp:(.*?)",
true: []string{"", "1", "foo"},
false: nil,
},
{
name: "regexp",
pattern: "regexp:^([a-zA-Z]+)$",
true: []string{"foo", "BAR", "fooBAR"},
false: []string{"1", "foo-1"},
},
} {
pattern := NewPattern(tt.pattern)
assert.IsType(t, RegexpPattern{}, pattern)
for _, tag := range tt.true {
t.Run(fmt.Sprintf("%s[%q]", tt.name, tag), func(t *testing.T) {
assert.True(t, pattern.Matches(tag))
})
}
for _, tag := range tt.false {
t.Run(fmt.Sprintf("%s[%q]", tt.name, tag), func(t *testing.T) {
assert.False(t, pattern.Matches(tag))
})
}
}
}
24 changes: 24 additions & 0 deletions site/using.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,20 @@ fluxctl release --controller=default:deployment/helloworld --update-all-images -

Please note that automation might immediately undo this.

## Filter pattern types

Flux currently offers support for `glob`, `semver` and `regexp` based filtering.

### Glob

The glob (`*`) filter is the simplest filter Flux supports, a filter can contain
multiple globs:
```
fluxctl policy --controller=default:deployment/helloworld --tag-all='glob:master-v1.*.*'
```

### Semver

If your images use [semantic versioning](https://semver.org) you can filter by image tags
that adhere to certain constraints:
```
Expand All @@ -430,6 +444,16 @@ fluxctl policy --controller=default:deployment/helloworld --tag-all='semver:*'
Using a semver filter will also affect how flux sorts images, so
that the higher versions will be considered newer.

### Regexp

If your images have complex tags you can filter by regular expression:
```
fluxctl policy --controller=default:deployment/helloworld --tag-all='regexp:^([a-zA-Z]+)$'
```

Please bear in mind that if you want to match the whole tag,
you must bookend your pattern with `^` and `$`.

## Actions triggered through `fluxctl`

`fluxctl` provides the following flags for the message and author customization:
Expand Down

0 comments on commit 1b3eec0

Please sign in to comment.