mirror of https://github.com/portainer/portainer
fix(api/endpoints): edge stack status type filter no longer always include Pending envs (#1230)
parent
4e7d4b60a5
commit
6ea16c0060
|
@ -256,7 +256,7 @@ func (handler *Handler) filterEndpointsByQuery(
|
|||
return filteredEndpoints, totalAvailableEndpoints, nil
|
||||
}
|
||||
|
||||
func endpointStatusInStackMatchesFilter(stackStatus *portainer.EdgeStackStatusForEnv, envId portainer.EndpointID, statusFilter portainer.EdgeStackStatusType) bool {
|
||||
func endpointStatusInStackMatchesFilter(stackStatus *portainer.EdgeStackStatusForEnv, statusFilter portainer.EdgeStackStatusType) bool {
|
||||
// consider that if the env has no status in the stack it is in Pending state
|
||||
if statusFilter == portainer.EdgeStackStatusPending {
|
||||
return stackStatus == nil || len(stackStatus.Status) == 0
|
||||
|
@ -272,22 +272,24 @@ func endpointStatusInStackMatchesFilter(stackStatus *portainer.EdgeStackStatusFo
|
|||
}
|
||||
|
||||
func filterEndpointsByEdgeStack(endpoints []portainer.Endpoint, edgeStackId portainer.EdgeStackID, statusFilter *portainer.EdgeStackStatusType, datastore dataservices.DataStore) ([]portainer.Endpoint, error) {
|
||||
stack, err := datastore.EdgeStack().EdgeStack(edgeStackId)
|
||||
var filteredEndpoints []portainer.Endpoint
|
||||
if err := datastore.ViewTx(func(tx dataservices.DataStoreTx) error {
|
||||
stack, err := tx.EdgeStack().EdgeStack(edgeStackId)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "Unable to retrieve edge stack from the database")
|
||||
return errors.WithMessage(err, "Unable to retrieve edge stack from the database")
|
||||
}
|
||||
|
||||
envIds := roar.Roar[portainer.EndpointID]{}
|
||||
for _, edgeGroupdId := range stack.EdgeGroups {
|
||||
edgeGroup, err := datastore.EdgeGroup().Read(edgeGroupdId)
|
||||
for _, edgeGroupId := range stack.EdgeGroups {
|
||||
edgeGroup, err := tx.EdgeGroup().Read(edgeGroupId)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "Unable to retrieve edge group from the database")
|
||||
return errors.WithMessage(err, "Unable to retrieve edge group from the database")
|
||||
}
|
||||
|
||||
if edgeGroup.Dynamic {
|
||||
endpointIDs, err := edgegroups.GetEndpointsByTags(datastore, edgeGroup.TagIDs, edgeGroup.PartialMatch)
|
||||
endpointIDs, err := edgegroups.GetEndpointsByTags(tx, edgeGroup.TagIDs, edgeGroup.PartialMatch)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "Unable to retrieve environments and environment groups for Edge group")
|
||||
return errors.WithMessage(err, "Unable to retrieve environments and environment groups for Edge group")
|
||||
}
|
||||
edgeGroup.EndpointIDs = roar.FromSlice(endpointIDs)
|
||||
}
|
||||
|
@ -295,32 +297,37 @@ func filterEndpointsByEdgeStack(endpoints []portainer.Endpoint, edgeStackId port
|
|||
envIds.Union(edgeGroup.EndpointIDs)
|
||||
}
|
||||
|
||||
filteredEnvIds := roar.Roar[portainer.EndpointID]{}
|
||||
filteredEnvIds.Union(envIds)
|
||||
|
||||
if statusFilter != nil {
|
||||
var innerErr error
|
||||
|
||||
envIds.Iterate(func(envId portainer.EndpointID) bool {
|
||||
edgeStackStatus, err := datastore.EdgeStackStatus().Read(edgeStackId, envId)
|
||||
if dataservices.IsErrObjectNotFound(err) {
|
||||
return true
|
||||
} else if err != nil {
|
||||
edgeStackStatus, err := tx.EdgeStackStatus().Read(edgeStackId, envId)
|
||||
if err != nil && !dataservices.IsErrObjectNotFound(err) {
|
||||
innerErr = errors.WithMessagef(err, "Unable to retrieve edge stack status for environment %d", envId)
|
||||
return false
|
||||
}
|
||||
|
||||
if !endpointStatusInStackMatchesFilter(edgeStackStatus, portainer.EndpointID(envId), *statusFilter) {
|
||||
envIds.Remove(envId)
|
||||
if !endpointStatusInStackMatchesFilter(edgeStackStatus, *statusFilter) {
|
||||
filteredEnvIds.Remove(envId)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
if innerErr != nil {
|
||||
return nil, innerErr
|
||||
return innerErr
|
||||
}
|
||||
}
|
||||
|
||||
filteredEndpoints := filteredEndpointsByIds(endpoints, envIds)
|
||||
filteredEndpoints = filteredEndpointsByIds(endpoints, filteredEnvIds)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return filteredEndpoints, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
|
@ -304,42 +305,103 @@ func TestFilterEndpointsByEdgeStack(t *testing.T) {
|
|||
_, store := datastore.MustNewTestStore(t, false, false)
|
||||
|
||||
endpoints := []portainer.Endpoint{
|
||||
{ID: 1, Name: "Endpoint 1"},
|
||||
{ID: 2, Name: "Endpoint 2"},
|
||||
{ID: 3, Name: "Endpoint 3"},
|
||||
{ID: 1, Name: "Endpoint 1", Type: portainer.EdgeAgentOnDockerEnvironment, UserTrusted: true},
|
||||
{ID: 2, Name: "Endpoint 2", TagIDs: []portainer.TagID{1}, Type: portainer.EdgeAgentOnDockerEnvironment, UserTrusted: true},
|
||||
{ID: 3, Name: "Endpoint 3", TagIDs: []portainer.TagID{1}, Type: portainer.EdgeAgentOnDockerEnvironment, UserTrusted: true},
|
||||
{ID: 4, Name: "Endpoint 4"},
|
||||
}
|
||||
|
||||
edgeStackId := portainer.EdgeStackID(1)
|
||||
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
require.NoError(t, tx.Tag().Create(&portainer.Tag{ID: 1, Name: "tag", Endpoints: map[portainer.EndpointID]bool{2: true, 3: true}}))
|
||||
|
||||
err := store.EdgeStack().Create(edgeStackId, &portainer.EdgeStack{
|
||||
for i := range endpoints {
|
||||
require.NoError(t, tx.Endpoint().Create(&endpoints[i]))
|
||||
}
|
||||
|
||||
require.NoError(t, tx.EdgeStack().Create(edgeStackId, &portainer.EdgeStack{
|
||||
ID: edgeStackId,
|
||||
Name: "Test Edge Stack",
|
||||
EdgeGroups: []portainer.EdgeGroupID{1, 2},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
|
||||
err = store.EdgeGroup().Create(&portainer.EdgeGroup{
|
||||
require.NoError(t, tx.EdgeGroup().Create(&portainer.EdgeGroup{
|
||||
ID: 1,
|
||||
Name: "Edge Group 1",
|
||||
EndpointIDs: roar.FromSlice([]portainer.EndpointID{1}),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
|
||||
err = store.EdgeGroup().Create(&portainer.EdgeGroup{
|
||||
require.NoError(t, tx.EdgeGroup().Create(&portainer.EdgeGroup{
|
||||
ID: 2,
|
||||
Name: "Edge Group 2",
|
||||
EndpointIDs: roar.FromSlice([]portainer.EndpointID{2, 3}),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
Dynamic: true,
|
||||
TagIDs: []portainer.TagID{1},
|
||||
}))
|
||||
|
||||
es, err := filterEndpointsByEdgeStack(endpoints, edgeStackId, nil, store)
|
||||
require.NoError(t, tx.EdgeStackStatus().Create(edgeStackId, endpoints[0].ID, &portainer.EdgeStackStatusForEnv{
|
||||
Status: []portainer.EdgeStackDeploymentStatus{{Type: portainer.EdgeStackStatusAcknowledged}}}))
|
||||
|
||||
return nil
|
||||
}))
|
||||
|
||||
test := func(status *portainer.EdgeStackStatusType, expected []portainer.Endpoint) {
|
||||
tmp := make([]portainer.Endpoint, len(endpoints))
|
||||
require.Equal(t, 4, copy(tmp, endpoints))
|
||||
es, err := filterEndpointsByEdgeStack(tmp, edgeStackId, status, store)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, es, 3)
|
||||
require.Contains(t, es, endpoints[0]) // Endpoint 1
|
||||
require.Contains(t, es, endpoints[1]) // Endpoint 2
|
||||
require.Contains(t, es, endpoints[2]) // Endpoint 3
|
||||
require.NotContains(t, es, endpoints[3]) // Endpoint 4
|
||||
// validate that the len is the same
|
||||
require.Len(t, es, len(expected))
|
||||
// and that all items are the expected ones
|
||||
for i := range expected {
|
||||
require.Contains(t, es, expected[i])
|
||||
}
|
||||
}
|
||||
|
||||
test(nil, []portainer.Endpoint{endpoints[0], endpoints[1], endpoints[2]})
|
||||
|
||||
status := portainer.EdgeStackStatusPending
|
||||
test(&status, []portainer.Endpoint{endpoints[1], endpoints[2]})
|
||||
|
||||
status = portainer.EdgeStackStatusCompleted
|
||||
test(&status, []portainer.Endpoint{})
|
||||
|
||||
status = portainer.EdgeStackStatusAcknowledged
|
||||
test(&status, []portainer.Endpoint{endpoints[0]}) // that's the only one with an edge stack status in DB
|
||||
}
|
||||
|
||||
func TestErrorsFilterEndpointsByEdgeStack(t *testing.T) {
|
||||
t.Run("must error by edge stack not found", func(t *testing.T) {
|
||||
_, store := datastore.MustNewTestStore(t, false, false)
|
||||
require.NotNil(t, store)
|
||||
|
||||
_, err := filterEndpointsByEdgeStack([]portainer.Endpoint{}, 1, nil, store)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("must error by edge group not found", func(t *testing.T) {
|
||||
_, store := datastore.MustNewTestStore(t, false, false)
|
||||
require.NotNil(t, store)
|
||||
|
||||
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
require.NoError(t, tx.EdgeStack().Create(1, &portainer.EdgeStack{ID: 1, Name: "1", EdgeGroups: []portainer.EdgeGroupID{1}}))
|
||||
return nil
|
||||
}))
|
||||
_, err := filterEndpointsByEdgeStack([]portainer.Endpoint{}, 1, nil, store)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("must error by env tag not found", func(t *testing.T) {
|
||||
_, store := datastore.MustNewTestStore(t, false, false)
|
||||
require.NotNil(t, store)
|
||||
|
||||
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
require.NoError(t, tx.EdgeStack().Create(1, &portainer.EdgeStack{ID: 1, Name: "1", EdgeGroups: []portainer.EdgeGroupID{1}}))
|
||||
require.NoError(t, tx.EdgeGroup().Create(&portainer.EdgeGroup{ID: 1, Name: "edge group", Dynamic: true, TagIDs: []portainer.TagID{1}}))
|
||||
return nil
|
||||
}))
|
||||
_, err := filterEndpointsByEdgeStack([]portainer.Endpoint{}, 1, nil, store)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFilterEndpointsByEdgeGroup(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue