mirror of
https://github.com/portainer/portainer.git
synced 2025-07-19 13:29:41 +02:00
fix(endpoints): optimize the search performance BE-11267 (#12262)
Some checks are pending
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run
Some checks are pending
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run
This commit is contained in:
parent
c0db48b29d
commit
f742937359
8 changed files with 191 additions and 98 deletions
|
@ -98,8 +98,8 @@ func (handler *Handler) updateEndpointGroup(tx dataservices.DataStoreTx, endpoin
|
||||||
payloadTagSet := tag.Set(payload.TagIDs)
|
payloadTagSet := tag.Set(payload.TagIDs)
|
||||||
endpointGroupTagSet := tag.Set((endpointGroup.TagIDs))
|
endpointGroupTagSet := tag.Set((endpointGroup.TagIDs))
|
||||||
union := tag.Union(payloadTagSet, endpointGroupTagSet)
|
union := tag.Union(payloadTagSet, endpointGroupTagSet)
|
||||||
intersection := tag.Intersection(payloadTagSet, endpointGroupTagSet)
|
intersection := tag.IntersectionCount(payloadTagSet, endpointGroupTagSet)
|
||||||
tagsChanged = len(union) > len(intersection)
|
tagsChanged = len(union) > intersection
|
||||||
|
|
||||||
if tagsChanged {
|
if tagsChanged {
|
||||||
removeTags := tag.Difference(endpointGroupTagSet, payloadTagSet)
|
removeTags := tag.Difference(endpointGroupTagSet, payloadTagSet)
|
||||||
|
|
|
@ -193,7 +193,7 @@ func (handler *Handler) filterEndpointsByQuery(
|
||||||
return nil, 0, errors.WithMessage(err, "Unable to retrieve tags from the database")
|
return nil, 0, errors.WithMessage(err, "Unable to retrieve tags from the database")
|
||||||
}
|
}
|
||||||
|
|
||||||
tagsMap := make(map[portainer.TagID]string)
|
tagsMap := make(map[portainer.TagID]string, len(tags))
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
tagsMap[tag.ID] = tag.Name
|
tagsMap[tag.ID] = tag.Name
|
||||||
}
|
}
|
||||||
|
@ -304,8 +304,7 @@ func filterEndpointsBySearchCriteria(
|
||||||
) []portainer.Endpoint {
|
) []portainer.Endpoint {
|
||||||
n := 0
|
n := 0
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints {
|
||||||
endpointTags := convertTagIDsToTags(tagsMap, endpoint.TagIDs)
|
if endpointMatchSearchCriteria(&endpoint, tagsMap, searchCriteria) {
|
||||||
if endpointMatchSearchCriteria(&endpoint, endpointTags, searchCriteria) {
|
|
||||||
endpoints[n] = endpoint
|
endpoints[n] = endpoint
|
||||||
n++
|
n++
|
||||||
|
|
||||||
|
@ -319,7 +318,7 @@ func filterEndpointsBySearchCriteria(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if edgeGroupMatchSearchCriteria(&endpoint, edgeGroups, searchCriteria, endpoints, endpointGroups) {
|
if edgeGroupMatchSearchCriteria(&endpoint, edgeGroups, searchCriteria, endpointGroups) {
|
||||||
endpoints[n] = endpoint
|
endpoints[n] = endpoint
|
||||||
n++
|
n++
|
||||||
|
|
||||||
|
@ -365,7 +364,7 @@ func filterEndpointsByStatuses(endpoints []portainer.Endpoint, statuses []portai
|
||||||
return endpoints[:n]
|
return endpoints[:n]
|
||||||
}
|
}
|
||||||
|
|
||||||
func endpointMatchSearchCriteria(endpoint *portainer.Endpoint, tags []string, searchCriteria string) bool {
|
func endpointMatchSearchCriteria(endpoint *portainer.Endpoint, tagsMap map[portainer.TagID]string, searchCriteria string) bool {
|
||||||
if strings.Contains(strings.ToLower(endpoint.Name), searchCriteria) {
|
if strings.Contains(strings.ToLower(endpoint.Name), searchCriteria) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -380,8 +379,8 @@ func endpointMatchSearchCriteria(endpoint *portainer.Endpoint, tags []string, se
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tag := range tags {
|
for _, tagID := range endpoint.TagIDs {
|
||||||
if strings.Contains(strings.ToLower(tag), searchCriteria) {
|
if strings.Contains(strings.ToLower(tagsMap[tagID]), searchCriteria) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -391,16 +390,17 @@ func endpointMatchSearchCriteria(endpoint *portainer.Endpoint, tags []string, se
|
||||||
|
|
||||||
func endpointGroupMatchSearchCriteria(endpoint *portainer.Endpoint, endpointGroups []portainer.EndpointGroup, tagsMap map[portainer.TagID]string, searchCriteria string) bool {
|
func endpointGroupMatchSearchCriteria(endpoint *portainer.Endpoint, endpointGroups []portainer.EndpointGroup, tagsMap map[portainer.TagID]string, searchCriteria string) bool {
|
||||||
for _, group := range endpointGroups {
|
for _, group := range endpointGroups {
|
||||||
if group.ID == endpoint.GroupID {
|
if group.ID != endpoint.GroupID {
|
||||||
if strings.Contains(strings.ToLower(group.Name), searchCriteria) {
|
continue
|
||||||
return true
|
}
|
||||||
}
|
|
||||||
|
|
||||||
tags := convertTagIDsToTags(tagsMap, group.TagIDs)
|
if strings.Contains(strings.ToLower(group.Name), searchCriteria) {
|
||||||
for _, tag := range tags {
|
return true
|
||||||
if strings.Contains(strings.ToLower(tag), searchCriteria) {
|
}
|
||||||
return true
|
|
||||||
}
|
for _, tagID := range group.TagIDs {
|
||||||
|
if strings.Contains(strings.ToLower(tagsMap[tagID]), searchCriteria) {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -413,11 +413,10 @@ func edgeGroupMatchSearchCriteria(
|
||||||
endpoint *portainer.Endpoint,
|
endpoint *portainer.Endpoint,
|
||||||
edgeGroups []portainer.EdgeGroup,
|
edgeGroups []portainer.EdgeGroup,
|
||||||
searchCriteria string,
|
searchCriteria string,
|
||||||
endpoints []portainer.Endpoint,
|
|
||||||
endpointGroups []portainer.EndpointGroup,
|
endpointGroups []portainer.EndpointGroup,
|
||||||
) bool {
|
) bool {
|
||||||
for _, edgeGroup := range edgeGroups {
|
for _, edgeGroup := range edgeGroups {
|
||||||
relatedEndpointIDs := edge.EdgeGroupRelatedEndpoints(&edgeGroup, endpoints, endpointGroups)
|
relatedEndpointIDs := edge.EdgeGroupRelatedEndpoints(&edgeGroup, []portainer.Endpoint{*endpoint}, endpointGroups)
|
||||||
|
|
||||||
for _, endpointID := range relatedEndpointIDs {
|
for _, endpointID := range relatedEndpointIDs {
|
||||||
if endpointID == endpoint.ID {
|
if endpointID == endpoint.ID {
|
||||||
|
@ -448,16 +447,6 @@ func filterEndpointsByTypes(endpoints []portainer.Endpoint, endpointTypes []port
|
||||||
return endpoints[:n]
|
return endpoints[:n]
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertTagIDsToTags(tagsMap map[portainer.TagID]string, tagIDs []portainer.TagID) []string {
|
|
||||||
tags := make([]string, 0, len(tagIDs))
|
|
||||||
|
|
||||||
for _, tagID := range tagIDs {
|
|
||||||
tags = append(tags, tagsMap[tagID])
|
|
||||||
}
|
|
||||||
|
|
||||||
return tags
|
|
||||||
}
|
|
||||||
|
|
||||||
func filteredEndpointsByTags(endpoints []portainer.Endpoint, tagIDs []portainer.TagID, endpointGroups []portainer.EndpointGroup, partialMatch bool) []portainer.Endpoint {
|
func filteredEndpointsByTags(endpoints []portainer.Endpoint, tagIDs []portainer.TagID, endpointGroups []portainer.EndpointGroup, partialMatch bool) []portainer.Endpoint {
|
||||||
n := 0
|
n := 0
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package endpoints
|
package endpoints
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
|
@ -148,6 +149,103 @@ func Test_Filter_excludeIDs(t *testing.T) {
|
||||||
runTests(tests, t, handler, environments)
|
runTests(tests, t, handler, environments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkFilterEndpointsBySearchCriteria_PartialMatch(b *testing.B) {
|
||||||
|
n := 10000
|
||||||
|
|
||||||
|
endpointIDs := []portainer.EndpointID{}
|
||||||
|
|
||||||
|
endpoints := []portainer.Endpoint{}
|
||||||
|
for i := range n {
|
||||||
|
endpoints = append(endpoints, portainer.Endpoint{
|
||||||
|
ID: portainer.EndpointID(i + 1),
|
||||||
|
Name: "endpoint-" + strconv.Itoa(i+1),
|
||||||
|
GroupID: 1,
|
||||||
|
TagIDs: []portainer.TagID{1},
|
||||||
|
Type: portainer.EdgeAgentOnDockerEnvironment,
|
||||||
|
})
|
||||||
|
|
||||||
|
endpointIDs = append(endpointIDs, portainer.EndpointID(i+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
endpointGroups := []portainer.EndpointGroup{}
|
||||||
|
|
||||||
|
edgeGroups := []portainer.EdgeGroup{}
|
||||||
|
for i := range 1000 {
|
||||||
|
edgeGroups = append(edgeGroups, portainer.EdgeGroup{
|
||||||
|
ID: portainer.EdgeGroupID(i + 1),
|
||||||
|
Name: "edge-group-" + strconv.Itoa(i+1),
|
||||||
|
Endpoints: append([]portainer.EndpointID{}, endpointIDs...),
|
||||||
|
Dynamic: true,
|
||||||
|
TagIDs: []portainer.TagID{1, 2, 3},
|
||||||
|
PartialMatch: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
tagsMap := map[portainer.TagID]string{}
|
||||||
|
for i := range 10 {
|
||||||
|
tagsMap[portainer.TagID(i+1)] = "tag-" + strconv.Itoa(i+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
searchString := "edge-group"
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for range b.N {
|
||||||
|
e := filterEndpointsBySearchCriteria(endpoints, endpointGroups, edgeGroups, tagsMap, searchString)
|
||||||
|
if len(e) != n {
|
||||||
|
b.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkFilterEndpointsBySearchCriteria_FullMatch(b *testing.B) {
|
||||||
|
n := 10000
|
||||||
|
|
||||||
|
endpointIDs := []portainer.EndpointID{}
|
||||||
|
|
||||||
|
endpoints := []portainer.Endpoint{}
|
||||||
|
for i := range n {
|
||||||
|
endpoints = append(endpoints, portainer.Endpoint{
|
||||||
|
ID: portainer.EndpointID(i + 1),
|
||||||
|
Name: "endpoint-" + strconv.Itoa(i+1),
|
||||||
|
GroupID: 1,
|
||||||
|
TagIDs: []portainer.TagID{1, 2, 3},
|
||||||
|
Type: portainer.EdgeAgentOnDockerEnvironment,
|
||||||
|
})
|
||||||
|
|
||||||
|
endpointIDs = append(endpointIDs, portainer.EndpointID(i+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
endpointGroups := []portainer.EndpointGroup{}
|
||||||
|
|
||||||
|
edgeGroups := []portainer.EdgeGroup{}
|
||||||
|
for i := range 1000 {
|
||||||
|
edgeGroups = append(edgeGroups, portainer.EdgeGroup{
|
||||||
|
ID: portainer.EdgeGroupID(i + 1),
|
||||||
|
Name: "edge-group-" + strconv.Itoa(i+1),
|
||||||
|
Endpoints: append([]portainer.EndpointID{}, endpointIDs...),
|
||||||
|
Dynamic: true,
|
||||||
|
TagIDs: []portainer.TagID{1},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
tagsMap := map[portainer.TagID]string{}
|
||||||
|
for i := range 10 {
|
||||||
|
tagsMap[portainer.TagID(i+1)] = "tag-" + strconv.Itoa(i+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
searchString := "edge-group"
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for range b.N {
|
||||||
|
e := filterEndpointsBySearchCriteria(endpoints, endpointGroups, edgeGroups, tagsMap, searchString)
|
||||||
|
if len(e) != n {
|
||||||
|
b.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func runTests(tests []filterTest, t *testing.T, handler *Handler, endpoints []portainer.Endpoint) {
|
func runTests(tests []filterTest, t *testing.T, handler *Handler, endpoints []portainer.Endpoint) {
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.title, func(t *testing.T) {
|
t.Run(test.title, func(t *testing.T) {
|
||||||
|
|
|
@ -77,6 +77,7 @@ func edgeGroupRelatedToEndpoint(edgeGroup *portainer.EdgeGroup, endpoint *portai
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,12 +85,10 @@ func edgeGroupRelatedToEndpoint(edgeGroup *portainer.EdgeGroup, endpoint *portai
|
||||||
if endpointGroup.TagIDs != nil {
|
if endpointGroup.TagIDs != nil {
|
||||||
endpointTags = tag.Union(endpointTags, tag.Set(endpointGroup.TagIDs))
|
endpointTags = tag.Union(endpointTags, tag.Set(endpointGroup.TagIDs))
|
||||||
}
|
}
|
||||||
edgeGroupTags := tag.Set(edgeGroup.TagIDs)
|
|
||||||
|
|
||||||
if edgeGroup.PartialMatch {
|
if edgeGroup.PartialMatch {
|
||||||
intersection := tag.Intersection(endpointTags, edgeGroupTags)
|
return tag.PartialMatch(edgeGroup.TagIDs, endpointTags)
|
||||||
return len(intersection) != 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tag.FullMatch(edgeGroupTags, endpointTags)
|
return tag.FullMatch(edgeGroup.TagIDs, endpointTags)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +1,63 @@
|
||||||
package tag
|
package tag
|
||||||
|
|
||||||
import portainer "github.com/portainer/portainer/api"
|
import (
|
||||||
|
portainer "github.com/portainer/portainer/api"
|
||||||
|
)
|
||||||
|
|
||||||
type tagSet map[portainer.TagID]bool
|
type tagSet map[portainer.TagID]struct{}
|
||||||
|
|
||||||
// Set converts an array of ids to a set
|
// Set converts an array of ids to a set
|
||||||
func Set(tagIDs []portainer.TagID) tagSet {
|
func Set(tagIDs []portainer.TagID) tagSet {
|
||||||
set := map[portainer.TagID]bool{}
|
set := map[portainer.TagID]struct{}{}
|
||||||
for _, tagID := range tagIDs {
|
for _, tagID := range tagIDs {
|
||||||
set[tagID] = true
|
set[tagID] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intersection returns a set intersection of the provided sets
|
// IntersectionCount returns the element count of the intersection of the sets
|
||||||
func Intersection(sets ...tagSet) tagSet {
|
func IntersectionCount(setA, setB tagSet) int {
|
||||||
intersection := tagSet{}
|
if len(setA) > len(setB) {
|
||||||
if len(sets) == 0 {
|
setA, setB = setB, setA
|
||||||
return intersection
|
|
||||||
}
|
}
|
||||||
setA := sets[0]
|
|
||||||
|
count := 0
|
||||||
|
|
||||||
for tag := range setA {
|
for tag := range setA {
|
||||||
inAll := true
|
if _, ok := setB[tag]; ok {
|
||||||
for _, setB := range sets {
|
count++
|
||||||
if !setB[tag] {
|
|
||||||
inAll = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if inAll {
|
|
||||||
intersection[tag] = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return intersection
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
// Union returns a set union of provided sets
|
// Union returns a set union of provided sets
|
||||||
func Union(sets ...tagSet) tagSet {
|
func Union(sets ...tagSet) tagSet {
|
||||||
union := tagSet{}
|
union := tagSet{}
|
||||||
|
|
||||||
for _, set := range sets {
|
for _, set := range sets {
|
||||||
for tag := range set {
|
for tag := range set {
|
||||||
union[tag] = true
|
union[tag] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return union
|
return union
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains return true if setA contains setB
|
// Contains return true if setA contains setB
|
||||||
func Contains(setA tagSet, setB tagSet) bool {
|
func Contains(setA tagSet, setB []portainer.TagID) bool {
|
||||||
if len(setA) == 0 || len(setB) == 0 {
|
if len(setA) == 0 || len(setB) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for tag := range setB {
|
for _, tag := range setB {
|
||||||
if !setA[tag] {
|
if _, ok := setA[tag]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,8 +66,8 @@ func Difference(setA tagSet, setB tagSet) tagSet {
|
||||||
set := tagSet{}
|
set := tagSet{}
|
||||||
|
|
||||||
for tag := range setA {
|
for tag := range setA {
|
||||||
if !setB[tag] {
|
if _, ok := setB[tag]; !ok {
|
||||||
set[tag] = true
|
set[tag] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
package tag
|
package tag
|
||||||
|
|
||||||
|
import portainer "github.com/portainer/portainer/api"
|
||||||
|
|
||||||
// FullMatch returns true if environment tags matches all edge group tags
|
// FullMatch returns true if environment tags matches all edge group tags
|
||||||
func FullMatch(edgeGroupTags tagSet, environmentTags tagSet) bool {
|
func FullMatch(edgeGroupTags []portainer.TagID, environmentTags tagSet) bool {
|
||||||
return Contains(environmentTags, edgeGroupTags)
|
return Contains(environmentTags, edgeGroupTags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PartialMatch returns true if environment tags matches at least one edge group tag
|
// PartialMatch returns true if environment tags matches at least one edge group tag
|
||||||
func PartialMatch(edgeGroupTags tagSet, environmentTags tagSet) bool {
|
func PartialMatch(edgeGroupTags []portainer.TagID, environmentTags tagSet) bool {
|
||||||
return len(Intersection(edgeGroupTags, environmentTags)) != 0
|
for _, tagID := range edgeGroupTags {
|
||||||
|
if _, ok := environmentTags[tagID]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,49 +9,49 @@ import (
|
||||||
func TestFullMatch(t *testing.T) {
|
func TestFullMatch(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
edgeGroupTags tagSet
|
edgeGroupTags []portainer.TagID
|
||||||
environmentTag tagSet
|
environmentTag tagSet
|
||||||
expected bool
|
expected bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "environment tag partially match edge group tags",
|
name: "environment tag partially match edge group tags",
|
||||||
edgeGroupTags: Set([]portainer.TagID{1, 2, 3}),
|
edgeGroupTags: []portainer.TagID{1, 2, 3},
|
||||||
environmentTag: Set([]portainer.TagID{1, 2}),
|
environmentTag: Set([]portainer.TagID{1, 2}),
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "edge group tags equal to environment tags",
|
name: "edge group tags equal to environment tags",
|
||||||
edgeGroupTags: Set([]portainer.TagID{1, 2}),
|
edgeGroupTags: []portainer.TagID{1, 2},
|
||||||
environmentTag: Set([]portainer.TagID{1, 2}),
|
environmentTag: Set([]portainer.TagID{1, 2}),
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "environment tags fully match edge group tags",
|
name: "environment tags fully match edge group tags",
|
||||||
edgeGroupTags: Set([]portainer.TagID{1, 2}),
|
edgeGroupTags: []portainer.TagID{1, 2},
|
||||||
environmentTag: Set([]portainer.TagID{1, 2, 3}),
|
environmentTag: Set([]portainer.TagID{1, 2, 3}),
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "environment tags do not match edge group tags",
|
name: "environment tags do not match edge group tags",
|
||||||
edgeGroupTags: Set([]portainer.TagID{1, 2}),
|
edgeGroupTags: []portainer.TagID{1, 2},
|
||||||
environmentTag: Set([]portainer.TagID{3, 4}),
|
environmentTag: Set([]portainer.TagID{3, 4}),
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "edge group has no tags and environment has tags",
|
name: "edge group has no tags and environment has tags",
|
||||||
edgeGroupTags: Set([]portainer.TagID{}),
|
edgeGroupTags: []portainer.TagID{},
|
||||||
environmentTag: Set([]portainer.TagID{1, 2}),
|
environmentTag: Set([]portainer.TagID{1, 2}),
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "edge group has tags and environment has no tags",
|
name: "edge group has tags and environment has no tags",
|
||||||
edgeGroupTags: Set([]portainer.TagID{1, 2}),
|
edgeGroupTags: []portainer.TagID{1, 2},
|
||||||
environmentTag: Set([]portainer.TagID{}),
|
environmentTag: Set([]portainer.TagID{}),
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "both edge group and environment have no tags",
|
name: "both edge group and environment have no tags",
|
||||||
edgeGroupTags: Set([]portainer.TagID{}),
|
edgeGroupTags: []portainer.TagID{},
|
||||||
environmentTag: Set([]portainer.TagID{}),
|
environmentTag: Set([]portainer.TagID{}),
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
|
@ -70,55 +70,55 @@ func TestFullMatch(t *testing.T) {
|
||||||
func TestPartialMatch(t *testing.T) {
|
func TestPartialMatch(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
edgeGroupTags tagSet
|
edgeGroupTags []portainer.TagID
|
||||||
environmentTag tagSet
|
environmentTag tagSet
|
||||||
expected bool
|
expected bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "environment tags partially match edge group tags 1",
|
name: "environment tags partially match edge group tags 1",
|
||||||
edgeGroupTags: Set([]portainer.TagID{1, 2, 3}),
|
edgeGroupTags: []portainer.TagID{1, 2, 3},
|
||||||
environmentTag: Set([]portainer.TagID{1, 2}),
|
environmentTag: Set([]portainer.TagID{1, 2}),
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "environment tags partially match edge group tags 2",
|
name: "environment tags partially match edge group tags 2",
|
||||||
edgeGroupTags: Set([]portainer.TagID{1, 2, 3}),
|
edgeGroupTags: []portainer.TagID{1, 2, 3},
|
||||||
environmentTag: Set([]portainer.TagID{1, 4, 5}),
|
environmentTag: Set([]portainer.TagID{1, 4, 5}),
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "edge group tags equal to environment tags",
|
name: "edge group tags equal to environment tags",
|
||||||
edgeGroupTags: Set([]portainer.TagID{1, 2}),
|
edgeGroupTags: []portainer.TagID{1, 2},
|
||||||
environmentTag: Set([]portainer.TagID{1, 2}),
|
environmentTag: Set([]portainer.TagID{1, 2}),
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "environment tags fully match edge group tags",
|
name: "environment tags fully match edge group tags",
|
||||||
edgeGroupTags: Set([]portainer.TagID{1, 2}),
|
edgeGroupTags: []portainer.TagID{1, 2},
|
||||||
environmentTag: Set([]portainer.TagID{1, 2, 3}),
|
environmentTag: Set([]portainer.TagID{1, 2, 3}),
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "environment tags do not match edge group tags",
|
name: "environment tags do not match edge group tags",
|
||||||
edgeGroupTags: Set([]portainer.TagID{1, 2}),
|
edgeGroupTags: []portainer.TagID{1, 2},
|
||||||
environmentTag: Set([]portainer.TagID{3, 4}),
|
environmentTag: Set([]portainer.TagID{3, 4}),
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "edge group has no tags and environment has tags",
|
name: "edge group has no tags and environment has tags",
|
||||||
edgeGroupTags: Set([]portainer.TagID{}),
|
edgeGroupTags: []portainer.TagID{},
|
||||||
environmentTag: Set([]portainer.TagID{1, 2}),
|
environmentTag: Set([]portainer.TagID{1, 2}),
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "edge group has tags and environment has no tags",
|
name: "edge group has tags and environment has no tags",
|
||||||
edgeGroupTags: Set([]portainer.TagID{1, 2}),
|
edgeGroupTags: []portainer.TagID{1, 2},
|
||||||
environmentTag: Set([]portainer.TagID{}),
|
environmentTag: Set([]portainer.TagID{}),
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "both edge group and environment have no tags",
|
name: "both edge group and environment have no tags",
|
||||||
edgeGroupTags: Set([]portainer.TagID{}),
|
edgeGroupTags: []portainer.TagID{},
|
||||||
environmentTag: Set([]portainer.TagID{}),
|
environmentTag: Set([]portainer.TagID{}),
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,49 +7,49 @@ import (
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIntersection(t *testing.T) {
|
func TestIntersectionCount(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
setA tagSet
|
setA tagSet
|
||||||
setB tagSet
|
setB tagSet
|
||||||
expected tagSet
|
expected int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "positive numbers set intersection",
|
name: "positive numbers set intersection",
|
||||||
setA: Set([]portainer.TagID{1, 2, 3, 4, 5}),
|
setA: Set([]portainer.TagID{1, 2, 3, 4, 5}),
|
||||||
setB: Set([]portainer.TagID{4, 5, 6, 7}),
|
setB: Set([]portainer.TagID{4, 5, 6, 7}),
|
||||||
expected: Set([]portainer.TagID{4, 5}),
|
expected: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty setA intersection",
|
name: "empty setA intersection",
|
||||||
setA: Set([]portainer.TagID{1, 2, 3}),
|
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||||
setB: Set([]portainer.TagID{}),
|
setB: Set([]portainer.TagID{}),
|
||||||
expected: Set([]portainer.TagID{}),
|
expected: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty setB intersection",
|
name: "empty setB intersection",
|
||||||
setA: Set([]portainer.TagID{}),
|
setA: Set([]portainer.TagID{}),
|
||||||
setB: Set([]portainer.TagID{1, 2, 3}),
|
setB: Set([]portainer.TagID{1, 2, 3}),
|
||||||
expected: Set([]portainer.TagID{}),
|
expected: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no common elements sets intersection",
|
name: "no common elements sets intersection",
|
||||||
setA: Set([]portainer.TagID{1, 2, 3}),
|
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||||
setB: Set([]portainer.TagID{4, 5, 6}),
|
setB: Set([]portainer.TagID{4, 5, 6}),
|
||||||
expected: Set([]portainer.TagID{}),
|
expected: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "equal sets intersection",
|
name: "equal sets intersection",
|
||||||
setA: Set([]portainer.TagID{1, 2, 3}),
|
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||||
setB: Set([]portainer.TagID{1, 2, 3}),
|
setB: Set([]portainer.TagID{1, 2, 3}),
|
||||||
expected: Set([]portainer.TagID{1, 2, 3}),
|
expected: 3,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
result := Intersection(tc.setA, tc.setB)
|
result := IntersectionCount(tc.setA, tc.setB)
|
||||||
if !reflect.DeepEqual(result, tc.expected) {
|
if result != tc.expected {
|
||||||
t.Errorf("Expected %v, got %v", tc.expected, result)
|
t.Errorf("Expected %v, got %v", tc.expected, result)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -109,49 +109,49 @@ func TestContains(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
setA tagSet
|
setA tagSet
|
||||||
setB tagSet
|
setB []portainer.TagID
|
||||||
expected bool
|
expected bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "setA contains setB",
|
name: "setA contains setB",
|
||||||
setA: Set([]portainer.TagID{1, 2, 3}),
|
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||||
setB: Set([]portainer.TagID{1, 2}),
|
setB: []portainer.TagID{1, 2},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "setA equals to setB",
|
name: "setA equals to setB",
|
||||||
setA: Set([]portainer.TagID{1, 2}),
|
setA: Set([]portainer.TagID{1, 2}),
|
||||||
setB: Set([]portainer.TagID{1, 2}),
|
setB: []portainer.TagID{1, 2},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "setA contains parts of setB",
|
name: "setA contains parts of setB",
|
||||||
setA: Set([]portainer.TagID{1, 2}),
|
setA: Set([]portainer.TagID{1, 2}),
|
||||||
setB: Set([]portainer.TagID{1, 2, 3}),
|
setB: []portainer.TagID{1, 2, 3},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "setA does not contain setB",
|
name: "setA does not contain setB",
|
||||||
setA: Set([]portainer.TagID{1, 2}),
|
setA: Set([]portainer.TagID{1, 2}),
|
||||||
setB: Set([]portainer.TagID{3, 4}),
|
setB: []portainer.TagID{3, 4},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "setA is empty and setB is not empty",
|
name: "setA is empty and setB is not empty",
|
||||||
setA: Set([]portainer.TagID{}),
|
setA: Set([]portainer.TagID{}),
|
||||||
setB: Set([]portainer.TagID{1, 2}),
|
setB: []portainer.TagID{1, 2},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "setA is not empty and setB is empty",
|
name: "setA is not empty and setB is empty",
|
||||||
setA: Set([]portainer.TagID{1, 2}),
|
setA: Set([]portainer.TagID{1, 2}),
|
||||||
setB: Set([]portainer.TagID{}),
|
setB: []portainer.TagID{},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "setA is empty and setB is empty",
|
name: "setA is empty and setB is empty",
|
||||||
setA: Set([]portainer.TagID{}),
|
setA: Set([]portainer.TagID{}),
|
||||||
setB: Set([]portainer.TagID{}),
|
setB: []portainer.TagID{},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue