Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8cbd23c059 | |||
| 3800a958da | |||
| 09348b8a25 | |||
| 1ef9c249b7 | |||
| 33ac61c600 |
@@ -941,6 +941,6 @@
|
||||
}
|
||||
],
|
||||
"version": {
|
||||
"VERSION": "{\"SchemaVersion\":\"2.21.1\",\"MigratorCount\":0,\"Edition\":1,\"InstanceID\":\"463d5c47-0ea5-4aca-85b1-405ceefee254\"}"
|
||||
"VERSION": "{\"SchemaVersion\":\"2.21.3\",\"MigratorCount\":0,\"Edition\":1,\"InstanceID\":\"463d5c47-0ea5-4aca-85b1-405ceefee254\"}"
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ func (handler *Handler) imagesList(w http.ResponseWriter, r *http.Request) *http
|
||||
|
||||
imagesList := make([]ImageResponse, len(images))
|
||||
for i, image := range images {
|
||||
if (image.RepoTags == nil || len(image.RepoTags) == 0) && (image.RepoDigests != nil && len(image.RepoDigests) > 0) {
|
||||
if len(image.RepoTags) == 0 && len(image.RepoDigests) > 0 {
|
||||
for _, repoDigest := range image.RepoDigests {
|
||||
image.RepoTags = append(image.RepoTags, repoDigest[0:strings.Index(repoDigest, "@")]+":<none>")
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ func TestMissingEdgeIdentifier(t *testing.T) {
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusForbidden {
|
||||
t.Fatalf(fmt.Sprintf("expected a %d response, found: %d without Edge identifier", http.StatusForbidden, rec.Code))
|
||||
t.Fatalf("expected a %d response, found: %d without Edge identifier", http.StatusForbidden, rec.Code)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ func TestWithEndpoints(t *testing.T) {
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != test.expectedStatusCode {
|
||||
t.Fatalf(fmt.Sprintf("expected a %d response, found: %d for endpoint ID: %d", test.expectedStatusCode, rec.Code, test.endpoint.ID))
|
||||
t.Fatalf("expected a %d response, found: %d for endpoint ID: %d", test.expectedStatusCode, rec.Code, test.endpoint.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,7 +219,7 @@ func TestLastCheckInDateIncreases(t *testing.T) {
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf(fmt.Sprintf("expected a %d response, found: %d", http.StatusOK, rec.Code))
|
||||
t.Fatalf("expected a %d response, found: %d", http.StatusOK, rec.Code)
|
||||
}
|
||||
|
||||
updatedEndpoint, err := handler.DataStore.Endpoint().Endpoint(endpoint.ID)
|
||||
@@ -262,7 +262,7 @@ func TestEmptyEdgeIdWithAgentPlatformHeader(t *testing.T) {
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf(fmt.Sprintf("expected a %d response, found: %d with empty edge ID", http.StatusOK, rec.Code))
|
||||
t.Fatalf("expected a %d response, found: %d with empty edge ID", http.StatusOK, rec.Code)
|
||||
}
|
||||
|
||||
updatedEndpoint, err := handler.DataStore.Endpoint().Endpoint(endpoint.ID)
|
||||
@@ -326,7 +326,7 @@ func TestEdgeStackStatus(t *testing.T) {
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf(fmt.Sprintf("expected a %d response, found: %d", http.StatusOK, rec.Code))
|
||||
t.Fatalf("expected a %d response, found: %d", http.StatusOK, rec.Code)
|
||||
}
|
||||
|
||||
var data endpointEdgeStatusInspectResponse
|
||||
@@ -391,7 +391,7 @@ func TestEdgeJobsResponse(t *testing.T) {
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf(fmt.Sprintf("expected a %d response, found: %d", http.StatusOK, rec.Code))
|
||||
t.Fatalf("expected a %d response, found: %d", http.StatusOK, rec.Code)
|
||||
}
|
||||
|
||||
var data endpointEdgeStatusInspectResponse
|
||||
|
||||
@@ -96,8 +96,8 @@ func (handler *Handler) updateEndpointGroup(tx dataservices.DataStoreTx, endpoin
|
||||
payloadTagSet := tag.Set(payload.TagIDs)
|
||||
endpointGroupTagSet := tag.Set((endpointGroup.TagIDs))
|
||||
union := tag.Union(payloadTagSet, endpointGroupTagSet)
|
||||
intersection := tag.Intersection(payloadTagSet, endpointGroupTagSet)
|
||||
tagsChanged = len(union) > len(intersection)
|
||||
intersection := tag.IntersectionCount(payloadTagSet, endpointGroupTagSet)
|
||||
tagsChanged = len(union) > intersection
|
||||
|
||||
if tagsChanged {
|
||||
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")
|
||||
}
|
||||
|
||||
tagsMap := make(map[portainer.TagID]string)
|
||||
tagsMap := make(map[portainer.TagID]string, len(tags))
|
||||
for _, tag := range tags {
|
||||
tagsMap[tag.ID] = tag.Name
|
||||
}
|
||||
@@ -302,8 +302,7 @@ func filterEndpointsBySearchCriteria(
|
||||
) []portainer.Endpoint {
|
||||
n := 0
|
||||
for _, endpoint := range endpoints {
|
||||
endpointTags := convertTagIDsToTags(tagsMap, endpoint.TagIDs)
|
||||
if endpointMatchSearchCriteria(&endpoint, endpointTags, searchCriteria) {
|
||||
if endpointMatchSearchCriteria(&endpoint, tagsMap, searchCriteria) {
|
||||
endpoints[n] = endpoint
|
||||
n++
|
||||
|
||||
@@ -317,7 +316,7 @@ func filterEndpointsBySearchCriteria(
|
||||
continue
|
||||
}
|
||||
|
||||
if edgeGroupMatchSearchCriteria(&endpoint, edgeGroups, searchCriteria, endpoints, endpointGroups) {
|
||||
if edgeGroupMatchSearchCriteria(&endpoint, edgeGroups, searchCriteria, endpointGroups) {
|
||||
endpoints[n] = endpoint
|
||||
n++
|
||||
|
||||
@@ -363,7 +362,7 @@ func filterEndpointsByStatuses(endpoints []portainer.Endpoint, statuses []portai
|
||||
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) {
|
||||
return true
|
||||
}
|
||||
@@ -378,8 +377,8 @@ func endpointMatchSearchCriteria(endpoint *portainer.Endpoint, tags []string, se
|
||||
return true
|
||||
}
|
||||
|
||||
for _, tag := range tags {
|
||||
if strings.Contains(strings.ToLower(tag), searchCriteria) {
|
||||
for _, tagID := range endpoint.TagIDs {
|
||||
if strings.Contains(strings.ToLower(tagsMap[tagID]), searchCriteria) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -389,16 +388,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 {
|
||||
for _, group := range endpointGroups {
|
||||
if group.ID == endpoint.GroupID {
|
||||
if strings.Contains(strings.ToLower(group.Name), searchCriteria) {
|
||||
return true
|
||||
}
|
||||
if group.ID != endpoint.GroupID {
|
||||
continue
|
||||
}
|
||||
|
||||
tags := convertTagIDsToTags(tagsMap, group.TagIDs)
|
||||
for _, tag := range tags {
|
||||
if strings.Contains(strings.ToLower(tag), searchCriteria) {
|
||||
return true
|
||||
}
|
||||
if strings.Contains(strings.ToLower(group.Name), searchCriteria) {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, tagID := range group.TagIDs {
|
||||
if strings.Contains(strings.ToLower(tagsMap[tagID]), searchCriteria) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -411,11 +411,10 @@ func edgeGroupMatchSearchCriteria(
|
||||
endpoint *portainer.Endpoint,
|
||||
edgeGroups []portainer.EdgeGroup,
|
||||
searchCriteria string,
|
||||
endpoints []portainer.Endpoint,
|
||||
endpointGroups []portainer.EndpointGroup,
|
||||
) bool {
|
||||
for _, edgeGroup := range edgeGroups {
|
||||
relatedEndpointIDs := edge.EdgeGroupRelatedEndpoints(&edgeGroup, endpoints, endpointGroups)
|
||||
relatedEndpointIDs := edge.EdgeGroupRelatedEndpoints(&edgeGroup, []portainer.Endpoint{*endpoint}, endpointGroups)
|
||||
|
||||
for _, endpointID := range relatedEndpointIDs {
|
||||
if endpointID == endpoint.ID {
|
||||
@@ -446,16 +445,6 @@ func filterEndpointsByTypes(endpoints []portainer.Endpoint, endpointTypes []port
|
||||
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 {
|
||||
n := 0
|
||||
for _, endpoint := range endpoints {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package endpoints
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
@@ -148,6 +149,103 @@ func Test_Filter_excludeIDs(t *testing.T) {
|
||||
runTests(tests, t, handler, environments)
|
||||
}
|
||||
|
||||
func BenchmarkFilterEndpointsBySearchCriteria_PartialMatch(b *testing.B) {
|
||||
n := 10000
|
||||
|
||||
endpointIDs := []portainer.EndpointID{}
|
||||
|
||||
endpoints := []portainer.Endpoint{}
|
||||
for i := 0; i < n; i++ {
|
||||
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 := 0; i < 1000; i++ {
|
||||
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 := 0; i < 10; i++ {
|
||||
tagsMap[portainer.TagID(i+1)] = "tag-" + strconv.Itoa(i+1)
|
||||
}
|
||||
|
||||
searchString := "edge-group"
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
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 := 0; i < n; i++ {
|
||||
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 := 0; i < 1000; i++ {
|
||||
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 := 0; i < 10; i++ {
|
||||
tagsMap[portainer.TagID(i+1)] = "tag-" + strconv.Itoa(i+1)
|
||||
}
|
||||
|
||||
searchString := "edge-group"
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
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) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.title, func(t *testing.T) {
|
||||
|
||||
@@ -85,7 +85,7 @@ type Handler struct {
|
||||
}
|
||||
|
||||
// @title PortainerCE API
|
||||
// @version 2.21.1
|
||||
// @version 2.21.3
|
||||
// @description.markdown api-description.md
|
||||
// @termsOfService
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package stacks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
@@ -95,7 +96,7 @@ func (handler *Handler) stackAssociate(w http.ResponseWriter, r *http.Request) *
|
||||
}
|
||||
if !canManage {
|
||||
errMsg := "Stack management is disabled for non-admin users"
|
||||
return httperror.Forbidden(errMsg, fmt.Errorf(errMsg))
|
||||
return httperror.Forbidden(errMsg, errors.New(errMsg))
|
||||
}
|
||||
|
||||
stack.EndpointID = portainer.EndpointID(endpointID)
|
||||
|
||||
@@ -111,7 +111,7 @@ func (handler *Handler) stackDelete(w http.ResponseWriter, r *http.Request) *htt
|
||||
}
|
||||
if !canManage {
|
||||
errMsg := "stack deletion is disabled for non-admin users"
|
||||
return httperror.Forbidden(errMsg, fmt.Errorf(errMsg))
|
||||
return httperror.Forbidden(errMsg, errors.New(errMsg))
|
||||
}
|
||||
|
||||
// stop scheduler updates of the stack before removal
|
||||
@@ -338,7 +338,7 @@ func (handler *Handler) stackDeleteKubernetesByName(w http.ResponseWriter, r *ht
|
||||
}
|
||||
if !canManage {
|
||||
errMsg := "stack deletion is disabled for non-admin users"
|
||||
return httperror.Forbidden(errMsg, fmt.Errorf(errMsg))
|
||||
return httperror.Forbidden(errMsg, errors.New(errMsg))
|
||||
}
|
||||
|
||||
stacksToDelete = append(stacksToDelete, stack)
|
||||
|
||||
@@ -77,6 +77,7 @@ func edgeGroupRelatedToEndpoint(edgeGroup *portainer.EdgeGroup, endpoint *portai
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -84,12 +85,10 @@ func edgeGroupRelatedToEndpoint(edgeGroup *portainer.EdgeGroup, endpoint *portai
|
||||
if endpointGroup.TagIDs != nil {
|
||||
endpointTags = tag.Union(endpointTags, tag.Set(endpointGroup.TagIDs))
|
||||
}
|
||||
edgeGroupTags := tag.Set(edgeGroup.TagIDs)
|
||||
|
||||
if edgeGroup.PartialMatch {
|
||||
intersection := tag.Intersection(endpointTags, edgeGroupTags)
|
||||
return len(intersection) != 0
|
||||
return tag.PartialMatch(edgeGroup.TagIDs, endpointTags)
|
||||
}
|
||||
|
||||
return tag.FullMatch(edgeGroupTags, endpointTags)
|
||||
return tag.FullMatch(edgeGroup.TagIDs, endpointTags)
|
||||
}
|
||||
|
||||
+26
-27
@@ -1,64 +1,63 @@
|
||||
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
|
||||
func Set(tagIDs []portainer.TagID) tagSet {
|
||||
set := map[portainer.TagID]bool{}
|
||||
set := map[portainer.TagID]struct{}{}
|
||||
for _, tagID := range tagIDs {
|
||||
set[tagID] = true
|
||||
set[tagID] = struct{}{}
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
// Intersection returns a set intersection of the provided sets
|
||||
func Intersection(sets ...tagSet) tagSet {
|
||||
intersection := tagSet{}
|
||||
if len(sets) == 0 {
|
||||
return intersection
|
||||
// IntersectionCount returns the element count of the intersection of the sets
|
||||
func IntersectionCount(setA, setB tagSet) int {
|
||||
if len(setA) > len(setB) {
|
||||
setA, setB = setB, setA
|
||||
}
|
||||
setA := sets[0]
|
||||
|
||||
count := 0
|
||||
|
||||
for tag := range setA {
|
||||
inAll := true
|
||||
for _, setB := range sets {
|
||||
if !setB[tag] {
|
||||
inAll = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if inAll {
|
||||
intersection[tag] = true
|
||||
if _, ok := setB[tag]; ok {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return intersection
|
||||
return count
|
||||
}
|
||||
|
||||
// Union returns a set union of provided sets
|
||||
func Union(sets ...tagSet) tagSet {
|
||||
union := tagSet{}
|
||||
|
||||
for _, set := range sets {
|
||||
for tag := range set {
|
||||
union[tag] = true
|
||||
union[tag] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return union
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return false
|
||||
}
|
||||
|
||||
for tag := range setB {
|
||||
if !setA[tag] {
|
||||
for _, tag := range setB {
|
||||
if _, ok := setA[tag]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -67,8 +66,8 @@ func Difference(setA tagSet, setB tagSet) tagSet {
|
||||
set := tagSet{}
|
||||
|
||||
for tag := range setA {
|
||||
if !setB[tag] {
|
||||
set[tag] = true
|
||||
if _, ok := setB[tag]; !ok {
|
||||
set[tag] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
package tag
|
||||
|
||||
import portainer "github.com/portainer/portainer/api"
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// PartialMatch returns true if environment tags matches at least one edge group tag
|
||||
func PartialMatch(edgeGroupTags tagSet, environmentTags tagSet) bool {
|
||||
return len(Intersection(edgeGroupTags, environmentTags)) != 0
|
||||
func PartialMatch(edgeGroupTags []portainer.TagID, environmentTags tagSet) bool {
|
||||
for _, tagID := range edgeGroupTags {
|
||||
if _, ok := environmentTags[tagID]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -9,49 +9,49 @@ import (
|
||||
func TestFullMatch(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
edgeGroupTags tagSet
|
||||
edgeGroupTags []portainer.TagID
|
||||
environmentTag tagSet
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
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}),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
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}),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
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}),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
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}),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "edge group has no tags and environment has tags",
|
||||
edgeGroupTags: Set([]portainer.TagID{}),
|
||||
edgeGroupTags: []portainer.TagID{},
|
||||
environmentTag: Set([]portainer.TagID{1, 2}),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
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{}),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "both edge group and environment have no tags",
|
||||
edgeGroupTags: Set([]portainer.TagID{}),
|
||||
edgeGroupTags: []portainer.TagID{},
|
||||
environmentTag: Set([]portainer.TagID{}),
|
||||
expected: false,
|
||||
},
|
||||
@@ -70,55 +70,55 @@ func TestFullMatch(t *testing.T) {
|
||||
func TestPartialMatch(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
edgeGroupTags tagSet
|
||||
edgeGroupTags []portainer.TagID
|
||||
environmentTag tagSet
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
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}),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
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}),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
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}),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
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}),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
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}),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "edge group has no tags and environment has tags",
|
||||
edgeGroupTags: Set([]portainer.TagID{}),
|
||||
edgeGroupTags: []portainer.TagID{},
|
||||
environmentTag: Set([]portainer.TagID{1, 2}),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
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{}),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "both edge group and environment have no tags",
|
||||
edgeGroupTags: Set([]portainer.TagID{}),
|
||||
edgeGroupTags: []portainer.TagID{},
|
||||
environmentTag: Set([]portainer.TagID{}),
|
||||
expected: false,
|
||||
},
|
||||
|
||||
@@ -7,49 +7,49 @@ import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
func TestIntersection(t *testing.T) {
|
||||
func TestIntersectionCount(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
setA tagSet
|
||||
setB tagSet
|
||||
expected tagSet
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
name: "positive numbers set intersection",
|
||||
setA: Set([]portainer.TagID{1, 2, 3, 4, 5}),
|
||||
setB: Set([]portainer.TagID{4, 5, 6, 7}),
|
||||
expected: Set([]portainer.TagID{4, 5}),
|
||||
expected: 2,
|
||||
},
|
||||
{
|
||||
name: "empty setA intersection",
|
||||
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||
setB: Set([]portainer.TagID{}),
|
||||
expected: Set([]portainer.TagID{}),
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "empty setB intersection",
|
||||
setA: Set([]portainer.TagID{}),
|
||||
setB: Set([]portainer.TagID{1, 2, 3}),
|
||||
expected: Set([]portainer.TagID{}),
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "no common elements sets intersection",
|
||||
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||
setB: Set([]portainer.TagID{4, 5, 6}),
|
||||
expected: Set([]portainer.TagID{}),
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "equal sets intersection",
|
||||
setA: 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 {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := Intersection(tc.setA, tc.setB)
|
||||
if !reflect.DeepEqual(result, tc.expected) {
|
||||
result := IntersectionCount(tc.setA, tc.setB)
|
||||
if result != tc.expected {
|
||||
t.Errorf("Expected %v, got %v", tc.expected, result)
|
||||
}
|
||||
})
|
||||
@@ -109,49 +109,49 @@ func TestContains(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
setA tagSet
|
||||
setB tagSet
|
||||
setB []portainer.TagID
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "setA contains setB",
|
||||
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||
setB: Set([]portainer.TagID{1, 2}),
|
||||
setB: []portainer.TagID{1, 2},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "setA equals to setB",
|
||||
setA: Set([]portainer.TagID{1, 2}),
|
||||
setB: Set([]portainer.TagID{1, 2}),
|
||||
setB: []portainer.TagID{1, 2},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "setA contains parts of setB",
|
||||
setA: Set([]portainer.TagID{1, 2}),
|
||||
setB: Set([]portainer.TagID{1, 2, 3}),
|
||||
setB: []portainer.TagID{1, 2, 3},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "setA does not contain setB",
|
||||
setA: Set([]portainer.TagID{1, 2}),
|
||||
setB: Set([]portainer.TagID{3, 4}),
|
||||
setB: []portainer.TagID{3, 4},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "setA is empty and setB is not empty",
|
||||
setA: Set([]portainer.TagID{}),
|
||||
setB: Set([]portainer.TagID{1, 2}),
|
||||
setB: []portainer.TagID{1, 2},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "setA is not empty and setB is empty",
|
||||
setA: Set([]portainer.TagID{1, 2}),
|
||||
setB: Set([]portainer.TagID{}),
|
||||
setB: []portainer.TagID{},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "setA is empty and setB is empty",
|
||||
setA: Set([]portainer.TagID{}),
|
||||
setB: Set([]portainer.TagID{}),
|
||||
setB: []portainer.TagID{},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
+1
-1
@@ -1601,7 +1601,7 @@ type (
|
||||
|
||||
const (
|
||||
// APIVersion is the version number of the Portainer API
|
||||
APIVersion = "2.21.1"
|
||||
APIVersion = "2.21.3"
|
||||
// Edition is what this edition of Portainer is called
|
||||
Edition = PortainerCE
|
||||
// ComposeSyntaxMaxVersion is a maximum supported version of the docker compose syntax
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="409.333" height="166.751"><defs><clipPath id="a" clipPathUnits="userSpaceOnUse"><path d="M0 0h612v792H0Z"/></clipPath><clipPath id="b" clipPathUnits="userSpaceOnUse"><path d="M0 0h612v792H0Z"/></clipPath></defs><g style="fill:#09c;fill-opacity:1"><path d="M0 0c-6.127 1.862-10.58 7.517-10.58 14.209 0 6.763 4.548 12.465 10.768 14.279.637.189.472.59-.306.59-8.271 0-14.986-6.669-14.986-14.869S-8.389-.66-.118-.66C.66-.66.707-.212 0 0" style="fill:#09c;fill-opacity:1;fill-rule:nonzero;stroke:none" transform="matrix(5.60732 0 0 -5.60732 84.693 163.05)"/></g><g style="fill:#000"><path d="M0 0a10 10 0 0 0-.071 1.202 11.797 11.797 0 0 0 11.806 11.805c6.173 0 8.011-2.757 8.247-2.568.259.188-2.239 5.655-9.473 5.655A11.796 11.796 0 0 1-1.296 4.289c0-1.508.283-2.946.801-4.265C-.283-.542.047-.542 0 0" style="fill:#09c;fill-opacity:1;fill-rule:nonzero;stroke:none" transform="matrix(5.60732 0 0 -5.60732 41.355 102.799)"/></g><g style="fill:#09c;fill-opacity:1"><path d="M0 0c3.063 1.343 6.928 1.39 10.721.047 2.545-.895 4.03-2.168 4.148-2.097.212.094-1.485 2.757-4.525 3.912A10.79 10.79 0 0 1-.165.259C-.495 0-.377-.165 0 0" style="fill:#09c;fill-opacity:1;fill-rule:nonzero;stroke:none" transform="matrix(5.60732 0 0 -5.60732 69.102 55.1)"/></g><g style="fill:#f93;fill-opacity:1"><path d="M0 0c0-.848-.707-1.555-1.555-1.555-.849 0-1.555.683-1.555 1.555 0 .848.683 1.555 1.555 1.555S0 .872 0 0" style="fill:#f93;fill-opacity:1;fill-rule:nonzero;stroke:none" transform="matrix(5.60732 0 0 -5.60732 409.333 86.283)"/></g><path d="m525.457 729.914.188-2.074h3.276l-1.108 12.041h-4.877l-6.174-12.04h3.346l1.037 2.073zm-.165 2.333H522.3l2.57 5.207h.022zM533.469 733.096h.495l2.333 3.18h3.039l-3.228-4.052 1.98-4.383h-3.229l-1.296 3.417h-.471l-.73-3.417h-2.757l2.544 12.04h2.757z" style="fill:#f93;fill-opacity:1;fill-rule:nonzero;stroke:none" transform="matrix(5.60732 0 0 -5.60732 -2820.89 4227.904)"/><g clip-path="url(#b)" style="fill:#f93;fill-opacity:1" transform="matrix(5.60732 0 0 -5.60732 -2820.89 4227.904)"><g style="fill:#f93;fill-opacity:1"><path d="M0 0h2.757l1.107 5.255C4.477 8.153 3.37 8.506.542 8.506c-1.979 0-3.888.024-4.43-2.591h2.757c.165.754.636.918 1.32.918 1.201 0 1.154-.494.989-1.272L.895 4.218H.778c-.095.966-1.32.942-2.098.942-2.002 0-3.181-.636-3.605-2.662-.447-2.144.566-2.616 2.498-2.616.966 0 2.262.189 2.71 1.343h.094Zm-.778 3.487c.896 0 1.485-.07 1.343-.777C.377 1.838 0 1.673-1.155 1.673c-.424 0-1.201 0-1.013.919.165.778.707.895 1.39.895" style="fill:#f93;fill-opacity:1;fill-rule:nonzero;stroke:none" transform="translate(543.649 727.84)"/></g><g style="fill:#f93;fill-opacity:1"><path d="m0 0-.259-1.178h.118C.401-.188 1.508.094 2.451.094c1.178 0 2.356-.212 2.191-1.649h.118C5.16-.353 6.386.094 7.446.094c1.956 0 2.781-.801 2.356-2.757L8.577-8.436H5.82l1.037 4.878c.141.872.283 1.532-.778 1.532-1.084 0-1.437-.707-1.626-1.626L3.44-8.436H.683l1.084 5.114c.142.777.189 1.296-.777 1.296-1.131 0-1.485-.613-1.697-1.626L-1.72-8.436h-2.757L-2.686 0Z" style="fill:#f93;fill-opacity:1;fill-rule:nonzero;stroke:none" transform="translate(551.99 736.277)"/></g><g style="fill:#f93;fill-opacity:1"><path d="M0 0h2.757l1.131 5.255c.613 2.898-.495 3.251-3.322 3.251-1.98 0-3.889.024-4.43-2.591h2.757c.164.754.636.918 1.319.918 1.202 0 1.155-.494.99-1.272L.919 4.218H.801c-.094.966-1.319.942-2.097.942-2.003 0-3.181-.636-3.605-2.662-.448-2.144.565-2.616 2.497-2.616.967 0 2.263.189 2.71 1.343h.095Zm-.754 3.487c.895 0 1.484-.07 1.343-.777-.188-.872-.565-1.037-1.72-1.037-.424 0-1.202 0-1.013.919.165.778.707.895 1.39.895" style="fill:#f93;fill-opacity:1;fill-rule:nonzero;stroke:none" transform="translate(566.906 727.84)"/></g></g><path d="M573.669 727.84h-2.757l1.767 8.437h2.78z" style="fill:#f93;fill-opacity:1;fill-rule:nonzero;stroke:none" transform="matrix(5.60732 0 0 -5.60732 -2820.89 4227.904)"/></svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
Vendored
-1
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 32 32"><path d="M9.545 14.42l-1.2-8.258L3.37 3.074l1.612 7.8 4.562 3.556zm1.38 9.443l-.852-5.823-4.356-3.63 1.17 5.648 4.038 3.804zm-3.383-.64l.862 4.165 3.596 3.817L11.386 27l-3.842-3.78zm11.644-1.806l-1.837-1.402.014.33a.19.19 0 0 1-.084.166l-1.386.934 1.507 1.23c.02.02.03.027.035.036l.022.042c.008.027.01.037.01.048l.064 1.45 1.7 1.423-.036-4.26zm6.3-4.507l-.36 4.153-1.2-.828.13-2.118c0-.024-.002-.033-.003-.04-.006-.032-.012-.046-.02-.06s-.02-.028-.032-.04a.23.23 0 0 0-.032-.028l-2.56-1.69.037-1.856 4.03 2.51" fill="#123d10"/><path d="M16.59 11.116l-.335-7.84-7.53 2.894 1.23 8.4 6.635-3.453zm.4 9.135l-.246-5.78-6.27 3.57.88 6.01 5.638-3.798zm.127 2.93l-5.333 3.816.648 4.422 4.872-3.88-.186-4.357zm2.465-1.762l.036 4.275 3.8-3.032.253-4.17-4.1 2.926zm9.48-6.782l-.534 3.955-2.998 2.4.352-4.068 3.18-2.276" fill="#33b652"/><path d="M17.472 22.812l-.008-.042a.21.21 0 0 0-.019-.044c-.015-.024-.023-.032-.03-.04l-1.52-1.24 1.386-.934a.19.19 0 0 0 .084-.166l-.014-.33 1.837 1.402.036 4.26-1.7-1.423-.062-1.44zm-7.398-4.772l.852 5.823-4.038-3.804-1.17-5.648 4.356 3.63zm6.904 2.212L11.34 24.05l-.88-6.01 6.27-3.57.246 5.78zm-.725-16.975l.335 7.84-6.635 3.453-1.23-8.4 7.53-2.894zM8.335 6.16l1.2 8.258-4.562-3.556-1.612-7.8L8.335 6.16zm.07 21.225l-.862-4.165L11.386 27l.615 4.203-3.596-3.817zm8.885.152l-4.872 3.88-.648-4.422 5.333-3.816.186 4.357zm6.116-4.876l-3.8 3.032-.036-4.275 4.1-2.926-.253 4.17zm.53-2.428l.13-2.118c0-.024-.002-.033-.003-.04-.006-.032-.012-.046-.02-.06s-.02-.028-.032-.04a.23.23 0 0 0-.032-.028l-2.56-1.69.037-1.856 4.03 2.51-.36 4.153-1.2-.828zm1.58.747l.352-4.068 3.18-2.276-.534 3.955-2.998 2.4zm3.97-6.77l-.006-.03c-.002-.01-.006-.02-.01-.03a.23.23 0 0 0-.027-.045c-.02-.023-.03-.03-.04-.038l-4.368-2.42c-.06-.033-.133-.032-.192.008l-3.674 2.246c-.006 0-.01.01-.016.013s-.013.01-.02.015l-.016.02c-.005.008-.01.01-.014.018s-.008.017-.01.026-.006.013-.008.02-.003.02-.004.03l-.042 1.97-1.494-.987c-.062-.04-.142-.042-.205 0l-2.15 1.314-.093-2.186-.007-.042c-.002-.008-.004-.013-.007-.02a.19.19 0 0 0-.011-.024c-.004-.008-.008-.013-.013-.02s-.01-.013-.015-.02-.012-.01-.02-.016l-2.25-1.514 2.094-1.1c.066-.034.106-.104.103-.178l-.352-8.228c-.001-.01-.003-.02-.005-.03-.006-.03-.013-.045-.022-.06s-.022-.03-.032-.04c-.017-.017-.022-.02-.028-.024-.017-.008-.02-.008-.022-.015L10.873.115a.19.19 0 0 0-.14-.011L3.036 2.502l-.05.028-.04.037c-.006.008-.01.015-.014.023s-.01.015-.013.024-.006.02-.01.03c-.006.03-.005.04-.005.05s0 .018.001.027l1.718 8.302c.01.044.034.084.07.112l2.33 1.817-1.685.802c-.02.008-.022.015-.026.016l-.027.023c-.022.024-.028.036-.034.047-.014.028-.02.045-.02.062a.24.24 0 0 0 .002.055l1.292 6.25a.19.19 0 0 0 .056.1l1.622 1.528-1.075.658c-.014.008-.026.02-.038.03-.017.02-.025.033-.032.045a.22.22 0 0 0-.021.065c-.002.018-.001.036.003.055l1 4.842c.007.034.024.066.048.092l4.048 4.298c.006.008.013.01.02.017.02.017.033.024.047.03.027.008.05.013.072.013s.038 0 .056-.01.022-.008.027-.015c.008 0 .014-.01.02-.014l5.223-4.157c.048-.04.074-.097.072-.157l-.122-2.85 1.74 1.464c.02.015.03.023.04.028s.02.008.025.015c.02.008.038.01.057.01s.037-.008.056-.01c.017-.008.022-.008.026-.015.01-.008.017-.01.026-.017l4.186-3.337c.043-.034.068-.084.072-.138l.127-2.09 1.27.884c.012.008.015.015.02.015.007.008.015.008.023.01.033.015.05.015.067.015s.038 0 .056-.01.02-.008.026-.015c.01-.008.02-.012.03-.018l3.415-2.722c.04-.03.064-.076.07-.124l.604-4.47.001-.037" fill="#231f20"/></svg>
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
@@ -27,7 +27,7 @@ import google from '@/assets/ico/vendor/google.svg?c';
|
||||
import googlecloud from '@/assets/ico/vendor/googlecloud.svg?c';
|
||||
import kubernetes from '@/assets/ico/vendor/kubernetes.svg?c';
|
||||
import helm from '@/assets/ico/vendor/helm.svg?c';
|
||||
import linode from '@/assets/ico/vendor/linode.svg?c';
|
||||
import akamai from '@/assets/ico/vendor/akamai.svg?c';
|
||||
import microsoft from '@/assets/ico/vendor/microsoft.svg?c';
|
||||
import microsofticon from '@/assets/ico/vendor/microsoft-icon.svg?c';
|
||||
import openldap from '@/assets/ico/vendor/openldap.svg?c';
|
||||
@@ -62,7 +62,7 @@ export const SvgIcons = {
|
||||
googlecloud,
|
||||
kubernetes,
|
||||
helm,
|
||||
linode,
|
||||
akamai,
|
||||
microsoft,
|
||||
microsofticon,
|
||||
openldap,
|
||||
|
||||
+3
-3
@@ -56,9 +56,9 @@ export function KubeConfigTeaserForm() {
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Note: Officially supported cloud providers are Civo, Linode,
|
||||
DigitalOcean and Microsoft Azure (others are not guaranteed to
|
||||
work at present)
|
||||
Note: Officially supported cloud providers are Civo, Akamai
|
||||
Connected Cloud, DigitalOcean and Microsoft Azure (others are
|
||||
not guaranteed to work at present)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"author": "Portainer.io",
|
||||
"name": "portainer",
|
||||
"homepage": "http://portainer.io",
|
||||
"version": "2.21.1",
|
||||
"version": "2.21.3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:portainer/portainer.git"
|
||||
|
||||
Reference in New Issue
Block a user