mirror of https://github.com/portainer/portainer
fix(edge): stack associated no dynamic group being deployed [EE-5531] (#10224)
parent
490e4ec655
commit
440f4e8dda
|
@ -125,8 +125,8 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
||||||
if payload.GroupID != nil {
|
if payload.GroupID != nil {
|
||||||
groupID := portainer.EndpointGroupID(*payload.GroupID)
|
groupID := portainer.EndpointGroupID(*payload.GroupID)
|
||||||
|
|
||||||
endpoint.GroupID = groupID
|
|
||||||
updateRelations = updateRelations || groupID != endpoint.GroupID
|
updateRelations = updateRelations || groupID != endpoint.GroupID
|
||||||
|
endpoint.GroupID = groupID
|
||||||
}
|
}
|
||||||
|
|
||||||
if payload.TagIDs != nil {
|
if payload.TagIDs != nil {
|
||||||
|
|
|
@ -69,7 +69,7 @@ func GetEndpointsFromEdgeGroups(edgeGroupIDs []portainer.EdgeGroupID, datastore
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// edgeGroupRelatedToEndpoint returns true is edgeGroup is associated with environment(endpoint)
|
// edgeGroupRelatedToEndpoint returns true if edgeGroup is associated with environment(endpoint)
|
||||||
func edgeGroupRelatedToEndpoint(edgeGroup *portainer.EdgeGroup, endpoint *portainer.Endpoint, endpointGroup *portainer.EndpointGroup) bool {
|
func edgeGroupRelatedToEndpoint(edgeGroup *portainer.EdgeGroup, endpoint *portainer.Endpoint, endpointGroup *portainer.EndpointGroup) bool {
|
||||||
if !edgeGroup.Dynamic {
|
if !edgeGroup.Dynamic {
|
||||||
for _, endpointID := range edgeGroup.Endpoints {
|
for _, endpointID := range edgeGroup.Endpoints {
|
||||||
|
@ -91,5 +91,5 @@ func edgeGroupRelatedToEndpoint(edgeGroup *portainer.EdgeGroup, endpoint *portai
|
||||||
return len(intersection) != 0
|
return len(intersection) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return tag.Contains(edgeGroupTags, endpointTags)
|
return tag.FullMatch(edgeGroupTags, endpointTags)
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,13 +50,16 @@ func Union(sets ...tagSet) tagSet {
|
||||||
|
|
||||||
// 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 tagSet) bool {
|
||||||
containedTags := 0
|
if len(setA) == 0 || len(setB) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
for tag := range setB {
|
for tag := range setB {
|
||||||
if setA[tag] {
|
if !setA[tag] {
|
||||||
containedTags++
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return containedTags == len(setA)
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Difference returns the set difference tagsA - tagsB
|
// Difference returns the set difference tagsA - tagsB
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package tag
|
||||||
|
|
||||||
|
// FullMatch returns true if environment tags matches all edge group tags
|
||||||
|
func FullMatch(edgeGroupTags tagSet, 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
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package tag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
portainer "github.com/portainer/portainer/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFullMatch(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
edgeGroupTags tagSet
|
||||||
|
environmentTag tagSet
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "environment tag partially match edge group tags",
|
||||||
|
edgeGroupTags: Set([]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}),
|
||||||
|
environmentTag: Set([]portainer.TagID{1, 2}),
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "environment tags fully match edge group tags",
|
||||||
|
edgeGroupTags: Set([]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}),
|
||||||
|
environmentTag: Set([]portainer.TagID{3, 4}),
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "edge group has no tags and environment has tags",
|
||||||
|
edgeGroupTags: Set([]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}),
|
||||||
|
environmentTag: Set([]portainer.TagID{}),
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "both edge group and environment have no tags",
|
||||||
|
edgeGroupTags: Set([]portainer.TagID{}),
|
||||||
|
environmentTag: Set([]portainer.TagID{}),
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
result := FullMatch(tc.edgeGroupTags, tc.environmentTag)
|
||||||
|
if result != tc.expected {
|
||||||
|
t.Errorf("Expected %v, got %v", tc.expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartialMatch(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
edgeGroupTags tagSet
|
||||||
|
environmentTag tagSet
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "environment tags partially match edge group tags 1",
|
||||||
|
edgeGroupTags: Set([]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}),
|
||||||
|
environmentTag: Set([]portainer.TagID{1, 4, 5}),
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "edge group tags equal to environment tags",
|
||||||
|
edgeGroupTags: Set([]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}),
|
||||||
|
environmentTag: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "environment tags do not match edge group tags",
|
||||||
|
edgeGroupTags: Set([]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{}),
|
||||||
|
environmentTag: Set([]portainer.TagID{1, 2}),
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "edge group has tags and environment has no tags",
|
||||||
|
edgeGroupTags: Set([]portainer.TagID{1, 2}),
|
||||||
|
environmentTag: Set([]portainer.TagID{}),
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "both edge group and environment have no tags",
|
||||||
|
edgeGroupTags: Set([]portainer.TagID{}),
|
||||||
|
environmentTag: Set([]portainer.TagID{}),
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
result := PartialMatch(tc.edgeGroupTags, tc.environmentTag)
|
||||||
|
if result != tc.expected {
|
||||||
|
t.Errorf("Expected %v, got %v", tc.expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,204 @@
|
||||||
|
package tag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
portainer "github.com/portainer/portainer/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIntersection(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
setA tagSet
|
||||||
|
setB tagSet
|
||||||
|
expected tagSet
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
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}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty setA intersection",
|
||||||
|
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
setB: Set([]portainer.TagID{}),
|
||||||
|
expected: Set([]portainer.TagID{}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty setB intersection",
|
||||||
|
setA: Set([]portainer.TagID{}),
|
||||||
|
setB: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
expected: Set([]portainer.TagID{}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no common elements sets intersection",
|
||||||
|
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
setB: Set([]portainer.TagID{4, 5, 6}),
|
||||||
|
expected: Set([]portainer.TagID{}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
result := Intersection(tc.setA, tc.setB)
|
||||||
|
if !reflect.DeepEqual(result, tc.expected) {
|
||||||
|
t.Errorf("Expected %v, got %v", tc.expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnion(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
setA tagSet
|
||||||
|
setB tagSet
|
||||||
|
expected tagSet
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "non-duplicate set union",
|
||||||
|
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
setB: Set([]portainer.TagID{4, 5, 6}),
|
||||||
|
expected: Set([]portainer.TagID{1, 2, 3, 4, 5, 6}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty setA union",
|
||||||
|
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
setB: Set([]portainer.TagID{}),
|
||||||
|
expected: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty setB union",
|
||||||
|
setA: Set([]portainer.TagID{}),
|
||||||
|
setB: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
expected: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duplicate elements in set union",
|
||||||
|
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
setB: Set([]portainer.TagID{3, 4, 5}),
|
||||||
|
expected: Set([]portainer.TagID{1, 2, 3, 4, 5}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "equal sets union",
|
||||||
|
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
setB: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
expected: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
result := Union(tc.setA, tc.setB)
|
||||||
|
if !reflect.DeepEqual(result, tc.expected) {
|
||||||
|
t.Errorf("Expected %v, got %v", tc.expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContains(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
setA tagSet
|
||||||
|
setB tagSet
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "setA contains setB",
|
||||||
|
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
setB: Set([]portainer.TagID{1, 2}),
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "setA equals to setB",
|
||||||
|
setA: Set([]portainer.TagID{1, 2}),
|
||||||
|
setB: Set([]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}),
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "setA does not contain setB",
|
||||||
|
setA: Set([]portainer.TagID{1, 2}),
|
||||||
|
setB: Set([]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}),
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "setA is not empty and setB is empty",
|
||||||
|
setA: Set([]portainer.TagID{1, 2}),
|
||||||
|
setB: Set([]portainer.TagID{}),
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "setA is empty and setB is empty",
|
||||||
|
setA: Set([]portainer.TagID{}),
|
||||||
|
setB: Set([]portainer.TagID{}),
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
result := Contains(tc.setA, tc.setB)
|
||||||
|
if result != tc.expected {
|
||||||
|
t.Errorf("Expected %v, got %v", tc.expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDifference(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
setA tagSet
|
||||||
|
setB tagSet
|
||||||
|
expected tagSet
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "positive numbers set difference",
|
||||||
|
setA: Set([]portainer.TagID{1, 2, 3, 4, 5}),
|
||||||
|
setB: Set([]portainer.TagID{4, 5, 6, 7}),
|
||||||
|
expected: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty set difference",
|
||||||
|
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
setB: Set([]portainer.TagID{}),
|
||||||
|
expected: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "equal sets difference",
|
||||||
|
setA: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
setB: Set([]portainer.TagID{1, 2, 3}),
|
||||||
|
expected: Set([]portainer.TagID{}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
result := Difference(tc.setA, tc.setB)
|
||||||
|
if !reflect.DeepEqual(result, tc.expected) {
|
||||||
|
t.Errorf("Expected %v, got %v", tc.expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue