mirror of
https://github.com/portainer/portainer.git
synced 2025-07-23 07:19:41 +02:00
fix(edgegroups): avoid a last-write-wins situation when updating edge groups concurrently EE-3732 (#8101)
This commit is contained in:
parent
6dc1841c14
commit
e26a607d28
8 changed files with 266 additions and 65 deletions
109
api/http/handler/endpoints/endpoint_delete_test.go
Normal file
109
api/http/handler/endpoints/endpoint_delete_test.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
package endpoints
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/apikey"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
"github.com/portainer/portainer/api/demo"
|
||||
"github.com/portainer/portainer/api/http/proxy"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/jwt"
|
||||
)
|
||||
|
||||
func TestEndpointDeleteEdgeGroupsConcurrently(t *testing.T) {
|
||||
const endpointsCount = 100
|
||||
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, false)
|
||||
defer teardown()
|
||||
|
||||
user := &portainer.User{ID: 2, Username: "admin", Role: portainer.AdministratorRole}
|
||||
err := store.User().Create(user)
|
||||
if err != nil {
|
||||
t.Fatal("could not create admin user:", err)
|
||||
}
|
||||
|
||||
jwtService, err := jwt.NewService("1h", store)
|
||||
if err != nil {
|
||||
t.Fatal("could not initialize the JWT service:", err)
|
||||
}
|
||||
|
||||
apiKeyService := apikey.NewAPIKeyService(store.APIKeyRepository(), store.User())
|
||||
rawAPIKey, _, err := apiKeyService.GenerateApiKey(*user, "test")
|
||||
if err != nil {
|
||||
t.Fatal("could not generate API key:", err)
|
||||
}
|
||||
|
||||
bouncer := security.NewRequestBouncer(store, jwtService, apiKeyService)
|
||||
|
||||
handler := NewHandler(bouncer, demo.NewService())
|
||||
handler.DataStore = store
|
||||
handler.ProxyManager = proxy.NewManager(nil, nil, nil, nil, nil, nil, nil)
|
||||
|
||||
// Create all the environments and add them to the same edge group
|
||||
|
||||
var endpointIDs []portainer.EndpointID
|
||||
|
||||
for i := 0; i < endpointsCount; i++ {
|
||||
endpointID := portainer.EndpointID(i) + 1
|
||||
|
||||
err = store.Endpoint().Create(&portainer.Endpoint{
|
||||
ID: endpointID,
|
||||
Name: "env-" + strconv.Itoa(int(endpointID)),
|
||||
Type: portainer.EdgeAgentOnDockerEnvironment,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("could not create endpoint:", err)
|
||||
}
|
||||
|
||||
endpointIDs = append(endpointIDs, endpointID)
|
||||
}
|
||||
|
||||
err = store.EdgeGroup().Create(&portainer.EdgeGroup{
|
||||
ID: 1,
|
||||
Name: "edgegroup-1",
|
||||
Endpoints: endpointIDs,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("could not create edge group:", err)
|
||||
}
|
||||
|
||||
// Remove the environments concurrently
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(endpointIDs))
|
||||
|
||||
for _, endpointID := range endpointIDs {
|
||||
go func(ID portainer.EndpointID) {
|
||||
defer wg.Done()
|
||||
|
||||
req, err := http.NewRequest(http.MethodDelete, "/endpoints/"+strconv.Itoa(int(ID)), nil)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
return
|
||||
}
|
||||
req.Header.Add("X-Api-Key", rawAPIKey)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rec, req)
|
||||
}(endpointID)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// Check that the edge group is consistent
|
||||
|
||||
edgeGroup, err := handler.DataStore.EdgeGroup().EdgeGroup(1)
|
||||
if err != nil {
|
||||
t.Fatal("could not retrieve the edge group:", err)
|
||||
}
|
||||
|
||||
if len(edgeGroup.Endpoints) > 0 {
|
||||
t.Fatal("the edge group is not consistent")
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue