From 2abe40b78671bffce4497323131b2e474e399a2a Mon Sep 17 00:00:00 2001 From: Oscar Zhou <100548325+oscarzhou-portainer@users.noreply.github.com> Date: Thu, 16 Jan 2025 09:16:09 +1300 Subject: [PATCH] fix(edgestack): remove project folder after deleting edgestack [BE-11559] (#320) --- .../handler/edgestacks/edgestack_delete.go | 9 +++- .../edgestacks/edgestack_delete_test.go | 51 +++++++++++++++++++ api/http/handler/edgestacks/edgestack_test.go | 12 +++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/api/http/handler/edgestacks/edgestack_delete.go b/api/http/handler/edgestacks/edgestack_delete.go index 91fb6927a..3d71f2bce 100644 --- a/api/http/handler/edgestacks/edgestack_delete.go +++ b/api/http/handler/edgestacks/edgestack_delete.go @@ -3,6 +3,7 @@ package edgestacks import ( "errors" "net/http" + "strconv" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/dataservices" @@ -52,10 +53,14 @@ func (handler *Handler) deleteEdgeStack(tx dataservices.DataStoreTx, edgeStackID return httperror.InternalServerError("Unable to find an edge stack with the specified identifier inside the database", err) } - err = handler.edgeStacksService.DeleteEdgeStack(tx, edgeStack.ID, edgeStack.EdgeGroups) - if err != nil { + if err := handler.edgeStacksService.DeleteEdgeStack(tx, edgeStack.ID, edgeStack.EdgeGroups); err != nil { return httperror.InternalServerError("Unable to delete edge stack", err) } + stackFolder := handler.FileService.GetEdgeStackProjectPath(strconv.Itoa(int(edgeStack.ID))) + if err := handler.FileService.RemoveDirectory(stackFolder); err != nil { + return httperror.InternalServerError("Unable to remove edge stack project folder", err) + } + return nil } diff --git a/api/http/handler/edgestacks/edgestack_delete_test.go b/api/http/handler/edgestacks/edgestack_delete_test.go index 212dccb7e..ef25ae45c 100644 --- a/api/http/handler/edgestacks/edgestack_delete_test.go +++ b/api/http/handler/edgestacks/edgestack_delete_test.go @@ -1,12 +1,14 @@ package edgestacks import ( + "bytes" "fmt" "net/http" "net/http/httptest" "testing" portainer "github.com/portainer/portainer/api" + "github.com/stretchr/testify/assert" "github.com/segmentio/encoding/json" ) @@ -101,3 +103,52 @@ func TestDeleteInvalidEdgeStack(t *testing.T) { }) } } + +func TestDeleteEdgeStack_RemoveProjectFolder(t *testing.T) { + handler, rawAPIKey := setupHandler(t) + + edgeGroup := createEdgeGroup(t, handler.DataStore) + + payload := edgeStackFromStringPayload{ + Name: "test-stack", + DeploymentType: portainer.EdgeStackDeploymentCompose, + EdgeGroups: []portainer.EdgeGroupID{edgeGroup.ID}, + StackFileContent: "version: '3.7'\nservices:\n test:\n image: test", + } + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(payload); err != nil { + t.Fatal("error encoding payload:", err) + } + + // Create + req, err := http.NewRequest(http.MethodPost, "/edge_stacks/create/string", &buf) + if err != nil { + t.Fatal("request error:", err) + } + + req.Header.Add("x-api-key", rawAPIKey) + rec := httptest.NewRecorder() + handler.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("expected a %d response, found: %d", http.StatusNoContent, rec.Code) + } + + assert.DirExists(t, handler.FileService.GetEdgeStackProjectPath("1")) + + // Delete + if req, err = http.NewRequest(http.MethodDelete, "/edge_stacks/1", nil); err != nil { + t.Fatal("request error:", err) + } + + req.Header.Add("x-api-key", rawAPIKey) + rec = httptest.NewRecorder() + handler.ServeHTTP(rec, req) + + if rec.Code != http.StatusNoContent { + t.Fatalf("expected a %d response, found: %d", http.StatusNoContent, rec.Code) + } + + assert.NoDirExists(t, handler.FileService.GetEdgeStackProjectPath("1")) +} diff --git a/api/http/handler/edgestacks/edgestack_test.go b/api/http/handler/edgestacks/edgestack_test.go index 1db87a920..496f2de53 100644 --- a/api/http/handler/edgestacks/edgestack_test.go +++ b/api/http/handler/edgestacks/edgestack_test.go @@ -144,3 +144,15 @@ func createEdgeStack(t *testing.T, store dataservices.DataStore, endpointID port return edgeStack } + +func createEdgeGroup(t *testing.T, store dataservices.DataStore) portainer.EdgeGroup { + edgeGroup := portainer.EdgeGroup{ + ID: 1, + Name: "EdgeGroup 1", + } + + if err := store.EdgeGroup().Create(&edgeGroup); err != nil { + t.Fatal(err) + } + return edgeGroup +}