From 95032e4710be1524bccad5b8828b1e21df15a872 Mon Sep 17 00:00:00 2001 From: Anis Eleuch Date: Tue, 27 Feb 2024 01:01:20 +0100 Subject: [PATCH] ilm: Select an object when all AND tags are satisfied (#19134) Currently, if one object tag matches with one lifecycle tag filter, ILM will select it, however, this is wrong. All the Tag filters in the lifecycle document should be satisfied. --- internal/bucket/lifecycle/filter.go | 15 ++-- internal/bucket/lifecycle/filter_test.go | 100 +++++++++++++++++++++++ 2 files changed, 107 insertions(+), 8 deletions(-) diff --git a/internal/bucket/lifecycle/filter.go b/internal/bucket/lifecycle/filter.go index 68449fa6e..446397a50 100644 --- a/internal/bucket/lifecycle/filter.go +++ b/internal/bucket/lifecycle/filter.go @@ -232,21 +232,20 @@ func (f Filter) TestTags(userTags string) bool { } tagsMap := parsedTags.ToMap() - // This filter has tags configured but this object - // does not have any tag, skip this object - if len(tagsMap) == 0 { + // Not enough tags on object to satisfy the rule filter's tags + if len(tagsMap) < len(f.cachedTags) { return false } - // Both filter and object have tags, find a match, - // skip this object otherwise + var mismatch bool for k, cv := range f.cachedTags { v, ok := tagsMap[k] - if ok && v == cv { - return true + if !ok || v != cv { + mismatch = true + break } } - return false + return !mismatch } // BySize returns true if sz satisfies one of ObjectSizeGreaterThan, diff --git a/internal/bucket/lifecycle/filter_test.go b/internal/bucket/lifecycle/filter_test.go index f1ad691aa..31de24a10 100644 --- a/internal/bucket/lifecycle/filter_test.go +++ b/internal/bucket/lifecycle/filter_test.go @@ -241,3 +241,103 @@ func TestObjectSizeFilters(t *testing.T) { }) } } + +func TestTestTags(t *testing.T) { + noTags := Filter{ + set: true, + And: And{ + Tags: []Tag{}, + }, + andSet: true, + } + + oneTag := Filter{ + set: true, + And: And{ + Tags: []Tag{{Key: "FOO", Value: "1"}}, + }, + andSet: true, + } + + twoTags := Filter{ + set: true, + And: And{ + Tags: []Tag{{Key: "FOO", Value: "1"}, {Key: "BAR", Value: "2"}}, + }, + andSet: true, + } + + tests := []struct { + filter Filter + userTags string + want bool + }{ + { + filter: noTags, + userTags: "", + want: true, + }, + { + filter: noTags, + userTags: "A=3", + want: true, + }, + { + filter: oneTag, + userTags: "A=3", + want: false, + }, + { + filter: oneTag, + userTags: "FOO=1", + want: true, + }, + { + filter: oneTag, + userTags: "A=B&FOO=1", + want: true, + }, + { + filter: twoTags, + userTags: "", + want: false, + }, + { + filter: twoTags, + userTags: "FOO=1", + want: false, + }, + { + filter: twoTags, + userTags: "BAR=2", + want: false, + }, + { + filter: twoTags, + userTags: "FOO=2&BAR=2", + want: false, + }, + { + filter: twoTags, + userTags: "F=1&B=2", + want: false, + }, + { + filter: twoTags, + userTags: "FOO=1&BAR=2", + want: true, + }, + { + filter: twoTags, + userTags: "BAR=2&FOO=1", + want: true, + }, + } + for i, test := range tests { + t.Run(fmt.Sprintf("Test %d", i+1), func(t *testing.T) { + if got := test.filter.TestTags(test.userTags); got != test.want { + t.Errorf("Expected %v but got %v", test.want, got) + } + }) + } +}