This repository has been archived by the owner on Nov 1, 2022. It is now read-only.
/
pattern.go
146 lines (122 loc) · 3.39 KB
/
pattern.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package policy
import (
"regexp"
"strings"
"github.com/Masterminds/semver/v3"
"github.com/fluxcd/flux/pkg/image"
"github.com/ryanuber/go-glob"
)
const (
globPrefix = "glob:"
semverPrefix = "semver:"
regexpPrefix = "regexp:"
regexpAltPrefix = "regex:"
)
var (
// PatternAll matches everything.
PatternAll = NewPattern(globPrefix + "*")
PatternLatest = NewPattern(globPrefix + "latest")
)
// Pattern provides an interface to match image tags.
type Pattern interface {
// Matches returns true if the given image tag matches the pattern.
Matches(tag string) bool
// String returns the prefixed string representation.
String() string
// Newer returns true if image `a` is newer than image `b`.
Newer(a, b *image.Info) bool
// Valid returns true if the pattern is considered valid.
Valid() bool
// RequiresTimestamp returns true if the pattern orders based on timestamp data.
RequiresTimestamp() bool
}
type GlobPattern string
// SemverPattern matches by semantic versioning.
// See https://semver.org/
type SemverPattern struct {
pattern string // pattern without prefix
constraints *semver.Constraints
}
// 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 {
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}
case strings.HasPrefix(pattern, regexpAltPrefix):
pattern = strings.TrimPrefix(pattern, regexpAltPrefix)
r, _ := regexp.Compile(pattern)
return RegexpPattern{pattern, r}
default:
return GlobPattern(strings.TrimPrefix(pattern, globPrefix))
}
}
func (g GlobPattern) Matches(tag string) bool {
return glob.Glob(string(g), tag)
}
func (g GlobPattern) String() string {
return globPrefix + string(g)
}
func (g GlobPattern) Newer(a, b *image.Info) bool {
return image.NewerByCreated(a, b)
}
func (g GlobPattern) Valid() bool {
return true
}
func (g GlobPattern) RequiresTimestamp() bool {
return true
}
func (s SemverPattern) Matches(tag string) bool {
v, err := semver.NewVersion(tag)
if err != nil {
return false
}
if s.constraints == nil {
// Invalid constraints match anything
return true
}
return s.constraints.Check(v)
}
func (s SemverPattern) String() string {
return semverPrefix + s.pattern
}
func (s SemverPattern) Newer(a, b *image.Info) bool {
return image.NewerBySemver(a, b)
}
func (s SemverPattern) Valid() bool {
return s.constraints != nil
}
func (s SemverPattern) RequiresTimestamp() bool {
return false
}
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
}
func (r RegexpPattern) RequiresTimestamp() bool {
return true
}