瀏覽代碼

cluster and registry deletion

Alexander Belanger 5 年之前
父節點
當前提交
ca821deb1c

+ 1 - 0
internal/repository/cluster.go

@@ -17,4 +17,5 @@ type ClusterRepository interface {
 	ReadCluster(id uint) (*models.Cluster, error)
 	ListClustersByProjectID(projectID uint) ([]*models.Cluster, error)
 	UpdateClusterTokenCache(tokenCache *ints.TokenCache) (*models.Cluster, error)
+	DeleteCluster(cluster *models.Cluster) error
 }

+ 22 - 0
internal/repository/gorm/cluster.go

@@ -223,6 +223,28 @@ func (repo *ClusterRepository) UpdateClusterTokenCache(
 	return cluster, nil
 }
 
+// DeleteCluster removes a cluster from the db
+func (repo *ClusterRepository) DeleteCluster(
+	cluster *models.Cluster,
+) error {
+	// clear TokenCache association
+	assoc := repo.db.Model(cluster).Association("TokenCache")
+
+	if assoc.Error != nil {
+		return assoc.Error
+	}
+
+	if err := assoc.Clear(); err != nil {
+		return err
+	}
+
+	if err := repo.db.Where("id = ?", cluster.ID).Delete(&models.Cluster{}).Error; err != nil {
+		return err
+	}
+
+	return nil
+}
+
 // EncryptClusterData will encrypt the user's service account data before writing
 // to the DB
 func (repo *ClusterRepository) EncryptClusterData(

+ 39 - 0
internal/repository/gorm/cluster_test.go

@@ -373,3 +373,42 @@ func TestUpdateClusterToken(t *testing.T) {
 		t.Errorf("incorrect token in cache: expected %s, got %s\n", "token-2", cluster.TokenCache.Token)
 	}
 }
+
+func TestDeleteCluster(t *testing.T) {
+	tester := &tester{
+		dbFileName: "./porter_delete_cluster.db",
+	}
+
+	setupTestEnv(tester, t)
+	initProject(tester, t)
+	initCluster(tester, t)
+	defer cleanup(tester, t)
+
+	cluster, err := tester.repo.Cluster.ReadCluster(tester.initClusters[0].Model.ID)
+
+	if err != nil {
+		t.Fatalf("%v\n", err)
+	}
+
+	err = tester.repo.Cluster.DeleteCluster(cluster)
+
+	if err != nil {
+		t.Fatalf("%v\n", err)
+	}
+
+	_, err = tester.repo.Cluster.ReadCluster(tester.initClusters[0].Model.ID)
+
+	if err != orm.ErrRecordNotFound {
+		t.Fatalf("incorrect error: expected %v, got %v\n", orm.ErrRecordNotFound, err)
+	}
+
+	clusters, err := tester.repo.Cluster.ListClustersByProjectID(tester.initProjects[0].Model.ID)
+
+	if err != nil {
+		t.Fatalf("%v\n", err)
+	}
+
+	if len(clusters) != 0 {
+		t.Fatalf("length of clusters was not 0")
+	}
+}

+ 22 - 0
internal/repository/gorm/registry.go

@@ -124,6 +124,28 @@ func (repo *RegistryRepository) UpdateRegistryTokenCache(
 	return registry, nil
 }
 
+// DeleteRegistry removes a registry from the db
+func (repo *RegistryRepository) DeleteRegistry(
+	reg *models.Registry,
+) error {
+	// clear TokenCache association
+	assoc := repo.db.Model(reg).Association("TokenCache")
+
+	if assoc.Error != nil {
+		return assoc.Error
+	}
+
+	if err := assoc.Clear(); err != nil {
+		return err
+	}
+
+	if err := repo.db.Where("id = ?", reg.ID).Delete(&models.Registry{}).Error; err != nil {
+		return err
+	}
+
+	return nil
+}
+
 // EncryptRegistryData will encrypt the user's registry data before writing
 // to the DB
 func (repo *RegistryRepository) EncryptRegistryData(

+ 40 - 0
internal/repository/gorm/registry_test.go

@@ -8,6 +8,7 @@ import (
 	"github.com/porter-dev/porter/internal/models"
 	ints "github.com/porter-dev/porter/internal/models/integrations"
 	"gorm.io/gorm"
+	orm "gorm.io/gorm"
 )
 
 func TestCreateRegistry(t *testing.T) {
@@ -159,3 +160,42 @@ func TestUpdateRegistryToken(t *testing.T) {
 		t.Errorf("incorrect token in cache: expected %s, got %s\n", "token-2", reg.TokenCache.Token)
 	}
 }
+
+func TestDeleteRegistry(t *testing.T) {
+	tester := &tester{
+		dbFileName: "./porter_delete_registry.db",
+	}
+
+	setupTestEnv(tester, t)
+	initProject(tester, t)
+	initRegistry(tester, t)
+	defer cleanup(tester, t)
+
+	reg, err := tester.repo.Registry.ReadRegistry(tester.initRegs[0].Model.ID)
+
+	if err != nil {
+		t.Fatalf("%v\n", err)
+	}
+
+	err = tester.repo.Registry.DeleteRegistry(reg)
+
+	if err != nil {
+		t.Fatalf("%v\n", err)
+	}
+
+	_, err = tester.repo.Registry.ReadRegistry(tester.initRegs[0].Model.ID)
+
+	if err != orm.ErrRecordNotFound {
+		t.Fatalf("incorrect error: expected %v, got %v\n", orm.ErrRecordNotFound, err)
+	}
+
+	regs, err := tester.repo.Registry.ListRegistriesByProjectID(tester.initProjects[0].Model.ID)
+
+	if err != nil {
+		t.Fatalf("%v\n", err)
+	}
+
+	if len(regs) != 0 {
+		t.Fatalf("length of clusters was not 0")
+	}
+}

+ 1 - 0
internal/repository/registry.go

@@ -11,4 +11,5 @@ type RegistryRepository interface {
 	ReadRegistry(id uint) (*models.Registry, error)
 	ListRegistriesByProjectID(projectID uint) ([]*models.Registry, error)
 	UpdateRegistryTokenCache(tokenCache *ints.RegTokenCache) (*models.Registry, error)
+	DeleteRegistry(reg *models.Registry) error
 }

+ 21 - 3
internal/repository/test/cluster.go

@@ -135,9 +135,9 @@ func (repo *ClusterRepository) ListClustersByProjectID(
 
 	res := make([]*models.Cluster, 0)
 
-	for _, sa := range repo.clusters {
-		if sa.ProjectID == projectID {
-			res = append(res, sa)
+	for _, cluster := range repo.clusters {
+		if cluster != nil && cluster.ProjectID == projectID {
+			res = append(res, cluster)
 		}
 	}
 
@@ -158,3 +158,21 @@ func (repo *ClusterRepository) UpdateClusterTokenCache(
 
 	return repo.clusters[index], nil
 }
+
+// DeleteCluster removes a cluster from the array by setting it to nil
+func (repo *ClusterRepository) DeleteCluster(
+	cluster *models.Cluster,
+) error {
+	if !repo.canQuery {
+		return errors.New("Cannot write database")
+	}
+
+	if int(cluster.ID-1) >= len(repo.clusters) || repo.clusters[cluster.ID-1] == nil {
+		return gorm.ErrRecordNotFound
+	}
+
+	index := int(cluster.ID - 1)
+	repo.clusters[index] = nil
+
+	return nil
+}

+ 1 - 1
internal/repository/test/gitrepo.go

@@ -58,7 +58,7 @@ func (repo *GitRepoRepository) ListGitReposByProjectID(projectID uint) ([]*model
 	res := make([]*models.GitRepo, 0)
 
 	for _, gr := range repo.gitRepos {
-		if gr.ProjectID == projectID {
+		if gr != nil && gr.ProjectID == projectID {
 			res = append(res, gr)
 		}
 	}

+ 19 - 1
internal/repository/test/registry.go

@@ -65,7 +65,7 @@ func (repo *RegistryRepository) ListRegistriesByProjectID(
 	res := make([]*models.Registry, 0)
 
 	for _, reg := range repo.registries {
-		if reg.ProjectID == projectID {
+		if reg != nil && reg.ProjectID == projectID {
 			res = append(res, reg)
 		}
 	}
@@ -87,3 +87,21 @@ func (repo *RegistryRepository) UpdateRegistryTokenCache(
 
 	return repo.registries[index], nil
 }
+
+// DeleteRegistry removes a registry from the array by setting it to nil
+func (repo *RegistryRepository) DeleteRegistry(
+	reg *models.Registry,
+) error {
+	if !repo.canQuery {
+		return errors.New("Cannot write database")
+	}
+
+	if int(reg.ID-1) >= len(repo.registries) || repo.registries[reg.ID-1] == nil {
+		return gorm.ErrRecordNotFound
+	}
+
+	index := int(reg.ID - 1)
+	repo.registries[index] = nil
+
+	return nil
+}

+ 26 - 0
server/api/cluster_handler.go

@@ -119,6 +119,32 @@ func (app *App) HandleListProjectClusters(w http.ResponseWriter, r *http.Request
 	}
 }
 
+// HandleDeleteProjectCluster handles the deletion of a Cluster via the cluster ID
+func (app *App) HandleDeleteProjectCluster(w http.ResponseWriter, r *http.Request) {
+	id, err := strconv.ParseUint(chi.URLParam(r, "cluster_id"), 0, 64)
+
+	if err != nil || id == 0 {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	cluster, err := app.repo.Cluster.ReadCluster(uint(id))
+
+	if err != nil {
+		app.handleErrorRead(err, ErrProjectDataRead, w)
+		return
+	}
+
+	err = app.repo.Cluster.DeleteCluster(cluster)
+
+	if err != nil {
+		app.handleErrorRead(err, ErrProjectDataRead, w)
+		return
+	}
+
+	w.WriteHeader(http.StatusOK)
+}
+
 // HandleCreateProjectClusterCandidates handles the creation of ClusterCandidates using
 // a kubeconfig and a project id
 func (app *App) HandleCreateProjectClusterCandidates(w http.ResponseWriter, r *http.Request) {

+ 47 - 1
server/api/cluster_handler_test.go

@@ -3,6 +3,7 @@ package api_test
 import (
 	"encoding/json"
 	"net/http"
+	"net/http/httptest"
 	"strings"
 	"testing"
 
@@ -116,7 +117,7 @@ var readProjectClusterTest = []*clusterTest{
 	},
 }
 
-func TestHandleReadProjectSA(t *testing.T) {
+func TestHandleReadProjectCluster(t *testing.T) {
 	testClusterRequests(t, readProjectClusterTest, true)
 }
 
@@ -144,6 +145,51 @@ func TestHandleListProjectClusters(t *testing.T) {
 	testClusterRequests(t, listProjectClustersTest, true)
 }
 
+var deleteClusterTests = []*clusterTest{
+	&clusterTest{
+		initializers: []func(t *tester){
+			initUserDefault,
+			initProject,
+			initProjectClusterDefault,
+		},
+		msg:       "Delete cluster",
+		method:    "DELETE",
+		endpoint:  "/api/projects/1/clusters/1",
+		body:      ``,
+		expStatus: http.StatusOK,
+		expBody:   ``,
+		useCookie: true,
+		validators: []func(c *clusterTest, tester *tester, t *testing.T){
+			func(c *clusterTest, tester *tester, t *testing.T) {
+				req, err := http.NewRequest(
+					"GET",
+					"/api/projects/1/clusters/1",
+					strings.NewReader(""),
+				)
+
+				req.AddCookie(tester.cookie)
+
+				if err != nil {
+					t.Fatal(err)
+				}
+
+				rr2 := httptest.NewRecorder()
+
+				tester.router.ServeHTTP(rr2, req)
+
+				if status := rr2.Code; status != 403 {
+					t.Errorf("DELETE cluster validation, handler returned wrong status code: got %v want %v",
+						status, 403)
+				}
+			},
+		},
+	},
+}
+
+func TestHandleDeleteCluster(t *testing.T) {
+	testClusterRequests(t, deleteClusterTests, true)
+}
+
 var createProjectClusterCandidatesTests = []*clusterTest{
 	&clusterTest{
 		initializers: []func(t *tester){

+ 26 - 0
server/api/registry_handler.go

@@ -95,6 +95,32 @@ func (app *App) HandleListProjectRegistries(w http.ResponseWriter, r *http.Reque
 	}
 }
 
+// HandleDeleteProjectRegistry handles the deletion of a Registry via the registry ID
+func (app *App) HandleDeleteProjectRegistry(w http.ResponseWriter, r *http.Request) {
+	id, err := strconv.ParseUint(chi.URLParam(r, "registry_id"), 0, 64)
+
+	if err != nil || id == 0 {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	reg, err := app.repo.Registry.ReadRegistry(uint(id))
+
+	if err != nil {
+		app.handleErrorRead(err, ErrProjectDataRead, w)
+		return
+	}
+
+	err = app.repo.Registry.DeleteRegistry(reg)
+
+	if err != nil {
+		app.handleErrorRead(err, ErrProjectDataRead, w)
+		return
+	}
+
+	w.WriteHeader(http.StatusOK)
+}
+
 // HandleListRepositories returns a list of repositories for a given registry
 func (app *App) HandleListRepositories(w http.ResponseWriter, r *http.Request) {
 	regID, err := strconv.ParseUint(chi.URLParam(r, "registry_id"), 0, 64)

+ 56 - 0
server/api/registry_handler_test.go

@@ -3,6 +3,7 @@ package api_test
 import (
 	"encoding/json"
 	"net/http"
+	"net/http/httptest"
 	"strings"
 	"testing"
 
@@ -170,6 +171,61 @@ func TestHandleListRegistries(t *testing.T) {
 	testRegistryRequests(t, listRegistryTests, true)
 }
 
+var deleteRegTests = []*regTest{
+	&regTest{
+		initializers: []func(t *tester){
+			initUserDefault,
+			initProject,
+			initRegistry,
+		},
+		msg:       "Delete registry",
+		method:    "DELETE",
+		endpoint:  "/api/projects/1/registries/1",
+		body:      ``,
+		expStatus: http.StatusOK,
+		expBody:   ``,
+		useCookie: true,
+		validators: []func(c *regTest, tester *tester, t *testing.T){
+			func(c *regTest, tester *tester, t *testing.T) {
+				req, err := http.NewRequest(
+					"GET",
+					"/api/projects/1/registries",
+					strings.NewReader(""),
+				)
+
+				req.AddCookie(tester.cookie)
+
+				if err != nil {
+					t.Fatal(err)
+				}
+
+				rr2 := httptest.NewRecorder()
+
+				tester.router.ServeHTTP(rr2, req)
+
+				if status := rr2.Code; status != 200 {
+					t.Errorf("DELETE registry validation, handler returned wrong status code: got %v want %v",
+						status, 200)
+				}
+
+				gotBody := make([]*models.RegistryExternal, 0)
+				expBody := make([]*models.RegistryExternal, 0)
+
+				json.Unmarshal(rr2.Body.Bytes(), &gotBody)
+
+				if diff := deep.Equal(gotBody, expBody); diff != nil {
+					t.Errorf("handler returned wrong body:\n")
+					t.Error(diff)
+				}
+			},
+		},
+	},
+}
+
+func TestHandleDeleteRegistry(t *testing.T) {
+	testRegistryRequests(t, deleteRegTests, true)
+}
+
 // ------------------------- INITIALIZERS AND VALIDATORS ------------------------- //
 
 func initRegistry(tester *tester) {

+ 28 - 0
server/router/router.go

@@ -202,6 +202,20 @@ func New(
 			),
 		)
 
+		r.Method(
+			"DELETE",
+			"/projects/{project_id}/clusters/{cluster_id}",
+			auth.DoesUserHaveProjectAccess(
+				auth.DoesUserHaveClusterAccess(
+					requestlog.NewHandler(a.HandleDeleteProjectCluster, l),
+					mw.URLParam,
+					mw.URLParam,
+				),
+				mw.URLParam,
+				mw.WriteAccess,
+			),
+		)
+
 		// /api/projects/{project_id}/clusters/candidates routes
 		r.Method(
 			"POST",
@@ -275,6 +289,20 @@ func New(
 			),
 		)
 
+		r.Method(
+			"DELETE",
+			"/projects/{project_id}/registries/{registry_id}",
+			auth.DoesUserHaveProjectAccess(
+				auth.DoesUserHaveRegistryAccess(
+					requestlog.NewHandler(a.HandleDeleteProjectRegistry, l),
+					mw.URLParam,
+					mw.URLParam,
+				),
+				mw.URLParam,
+				mw.WriteAccess,
+			),
+		)
+
 		// /api/projects/{project_id}/registries/{registry_id}/repositories routes
 		r.Method(
 			"GET",