0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2025-04-08 02:54:41 -05:00

feat(sync): add tag excludeRegex filter (#2906)

Fix #2902

Signed-off-by: Vladimir Ermakov <vooon341@gmail.com>
This commit is contained in:
Vladimir Ermakov 2025-01-26 19:29:02 +01:00 committed by GitHub
parent d0de12d2d3
commit 22864a95c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 80 additions and 2 deletions

View file

@ -41,6 +41,12 @@
},
{
"prefix": "/repo3/**"
},
{
"prefix": "/repo4/**",
"tags": {
"excludeRegex": ".*-(amd64|arm64)$"
}
}
]
},

View file

@ -1148,6 +1148,16 @@ func validateSync(config *config.Config, log zlog.Logger) error {
}
}
if content.Tags != nil && content.Tags.ExcludeRegex != nil {
_, err := regexp.Compile(*content.Tags.ExcludeRegex)
if err != nil {
msg := "sync content excludeRegex could not be compiled"
log.Error().Err(glob.ErrBadPattern).Str("excludeRegex", *content.Tags.ExcludeRegex).Msg(msg)
return fmt.Errorf("%w: %s: %s", zerr.ErrBadConfig, msg, *content.Tags.ExcludeRegex)
}
}
if content.StripPrefix && !strings.Contains(content.Prefix, "/*") && content.Destination == "/" {
msg := "can not use stripPrefix true and destination '/' without using glob patterns in prefix"
log.Error().Err(zerr.ErrBadConfig).

View file

@ -43,6 +43,7 @@ type Content struct {
}
type Tags struct {
Regex *string
Semver *bool
Regex *string
ExcludeRegex *string
Semver *bool
}

View file

@ -63,6 +63,13 @@ func (cm ContentManager) FilterTags(repo string, tags []string) ([]string, error
}
}
if content.Tags.ExcludeRegex != nil {
tags, err = excludeTagsByRegex(tags, *content.Tags.ExcludeRegex, cm.log)
if err != nil {
return []string{}, err
}
}
if content.Tags.Semver != nil && *content.Tags.Semver {
tags = filterTagsBySemver(tags, cm.log)
}
@ -240,6 +247,32 @@ func filterTagsByRegex(tags []string, regex string, log log.Logger) ([]string, e
return filteredTags, nil
}
// excludeTagsByRegex filter-out images by tag regex given in the config.
func excludeTagsByRegex(tags []string, regex string, log log.Logger) ([]string, error) {
if len(tags) == 0 || regex == "" {
return tags, nil
}
filteredTags := make([]string, 0, len(tags))
log.Info().Str("excludeRegex", regex).Msg("filtering out tags using regex")
tagReg, err := regexp.Compile(regex)
if err != nil {
log.Error().Err(err).Str("excludeRegex", regex).Msg("failed to compile regex")
return filteredTags, err
}
for _, tag := range tags {
if !tagReg.MatchString(tag) {
filteredTags = append(filteredTags, tag)
}
}
return filteredTags, nil
}
// filterTagsBySemver filters tags by checking if they are semver compliant.
func filterTagsBySemver(tags []string, log log.Logger) []string {
filteredTags := []string{}

View file

@ -170,6 +170,7 @@ func TestGetContentByLocalRepo(t *testing.T) {
func TestFilterTags(t *testing.T) {
allTagsRegex := ".*"
badRegex := "[*"
excludeArchRegex := ".*(x86_64|aarch64|amd64|arm64)$"
semverFalse := false
semverTrue := true
testCases := []struct {
@ -234,6 +235,33 @@ func TestFilterTags(t *testing.T) {
filteredTags: []string{},
err: false,
},
{
repo: "alpine",
content: []syncconf.Content{
{Prefix: "**", Tags: &syncconf.Tags{ExcludeRegex: &allTagsRegex}},
},
tags: []string{"v1", "v2", "v3"},
filteredTags: []string{},
err: false,
},
{
repo: "alpine",
content: []syncconf.Content{
{Prefix: "**", Tags: &syncconf.Tags{ExcludeRegex: &excludeArchRegex}},
},
tags: []string{"v1", "v2-x86_64", "v3-aarch64"},
filteredTags: []string{"v1"},
err: false,
},
{
repo: "repo",
content: []syncconf.Content{
{Prefix: "repo*", Tags: &syncconf.Tags{ExcludeRegex: &badRegex}},
},
tags: []string{"latest", "v2.0.1"},
filteredTags: []string{},
err: true,
},
}
Convey("Test FilterTags()", t, func() {