diff --git a/internal/provider/common.go b/internal/provider/common.go index 953e3ac3a..d6c0491a0 100644 --- a/internal/provider/common.go +++ b/internal/provider/common.go @@ -2,6 +2,7 @@ package provider import ( "regexp" + "slices" "strconv" "strings" @@ -17,6 +18,19 @@ var ( metadataKeyRegexp = regexp.MustCompile(`^[` + metadataKeyChars + `]+$`) ) +// MergeTags merges image-level tag filters over default tag filters. +func MergeTags(defaultTags, imageTags []string) []string { + tags := slices.Concat(defaultTags, imageTags) + seen := make(map[string]struct{}, len(tags)) + return slices.DeleteFunc(tags, func(tag string) bool { + if _, ok := seen[tag]; ok { + return true + } + seen[tag] = struct{}{} + return false + }) +} + // ValidateImage returns a standard image through Docker labels func ValidateImage(image string, metadata, labels map[string]string, watchByDef bool, defaults *model.Defaults) (img model.Image, err error) { img = model.Image{ @@ -28,8 +42,8 @@ func ValidateImage(image string, metadata, labels map[string]string, watchByDef img.NotifyOn = defaults.NotifyOn img.MaxTags = defaults.MaxTags img.SortTags = defaults.SortTags - img.IncludeTags = defaults.IncludeTags - img.ExcludeTags = defaults.ExcludeTags + img.IncludeTags = MergeTags(defaults.IncludeTags, nil) + img.ExcludeTags = MergeTags(defaults.ExcludeTags, nil) img.Metadata = defaults.Metadata } @@ -81,9 +95,9 @@ func ValidateImage(image string, metadata, labels map[string]string, watchByDef return img, errors.Wrapf(err, "cannot parse %q value of label %s", value, key) } case key == "diun.include_tags": - img.IncludeTags = strings.Split(value, ";") + img.IncludeTags = MergeTags(img.IncludeTags, strings.Split(value, ";")) case key == "diun.exclude_tags": - img.ExcludeTags = strings.Split(value, ";") + img.ExcludeTags = MergeTags(img.ExcludeTags, strings.Split(value, ";")) case key == "diun.hub_tpl": img.HubTpl = value case key == "diun.hub_link": diff --git a/internal/provider/common_test.go b/internal/provider/common_test.go index 1ed6fd7e2..6ee7cedae 100644 --- a/internal/provider/common_test.go +++ b/internal/provider/common_test.go @@ -406,7 +406,7 @@ func TestValidateImage(t *testing.T) { expectedErr: nil, }, { - name: "Override default include_tags", + name: "Merge default include_tags", image: "myimg", watchByDef: true, labels: map[string]string{ @@ -417,7 +417,7 @@ func TestValidateImage(t *testing.T) { }, expectedImage: model.Image{ Name: "myimg", - IncludeTags: []string{"ubuntu"}, + IncludeTags: []string{"alpine", "ubuntu"}, }, expectedErr: nil, }, @@ -462,7 +462,7 @@ func TestValidateImage(t *testing.T) { expectedErr: nil, }, { - name: "Override default exclude_tags", + name: "Merge default exclude_tags", image: "myimg", watchByDef: true, labels: map[string]string{ @@ -473,7 +473,7 @@ func TestValidateImage(t *testing.T) { }, expectedImage: model.Image{ Name: "myimg", - ExcludeTags: []string{"ubuntu"}, + ExcludeTags: []string{"alpine", "ubuntu"}, }, expectedErr: nil, }, diff --git a/internal/provider/file/file_test.go b/internal/provider/file/file_test.go index 1e4c6857c..7184a1ced 100644 --- a/internal/provider/file/file_test.go +++ b/internal/provider/file/file_test.go @@ -85,6 +85,7 @@ var ( SortTags: registry.SortTagSemver, MaxTags: 25, IncludeTags: []string{ + `^(0|[1-9]\d*)\..*`, `^1\.2\..*`, }, ExcludeTags: []string{ @@ -120,6 +121,7 @@ var ( `^(0|[1-9]\d*)\..*`, }, ExcludeTags: []string{ + `^0\.0\..*`, `latest`, }, }, @@ -183,6 +185,7 @@ var ( SortTags: registry.SortTagReverse, MaxTags: 25, IncludeTags: []string{ + `^(0|[1-9]\d*)\..*`, `^1\..*`, }, ExcludeTags: []string{ diff --git a/internal/provider/file/image.go b/internal/provider/file/image.go index 33e9e83c7..7a69d1a6d 100644 --- a/internal/provider/file/image.go +++ b/internal/provider/file/image.go @@ -8,6 +8,7 @@ import ( "github.com/containerd/platforms" "github.com/crazy-max/diun/v4/internal/model" + "github.com/crazy-max/diun/v4/internal/provider" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "gopkg.in/yaml.v3" ) @@ -88,15 +89,8 @@ func (c *Client) listFileImage() []model.Image { item.MaxTags = c.defaults.MaxTags } - // Set default IncludeTags - if len(item.IncludeTags) == 0 { - item.IncludeTags = c.defaults.IncludeTags - } - - // Set default ExcludeTags - if len(item.ExcludeTags) == 0 { - item.ExcludeTags = c.defaults.ExcludeTags - } + item.IncludeTags = provider.MergeTags(c.defaults.IncludeTags, item.IncludeTags) + item.ExcludeTags = provider.MergeTags(c.defaults.ExcludeTags, item.ExcludeTags) images = append(images, item) }