Przeglądaj źródła

add helm delete handler and CLI command

Mohammed Nafees 3 lat temu
rodzic
commit
db7fd0a0b4

+ 35 - 1
api/client/registry.go

@@ -27,7 +27,7 @@ func (c *Client) CreateRegistry(
 	return resp, err
 }
 
-// CreateRegistry creates a new registry integration
+// CreateHelmRepo creates a new helm repo in the project
 func (c *Client) CreateHelmRepo(
 	ctx context.Context,
 	projectID uint,
@@ -47,6 +47,40 @@ func (c *Client) CreateHelmRepo(
 	return resp, err
 }
 
+// ListHelmRepos list helm repos in the project
+func (c *Client) ListHelmRepos(
+	ctx context.Context,
+	projectID uint,
+) ([]*types.HelmRepo, error) {
+	var resp []*types.HelmRepo
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/helmrepos",
+			projectID,
+		),
+		nil,
+		&resp,
+	)
+
+	return resp, err
+}
+
+// DeleteHelmRepo deletes a helm repo from the project
+func (c *Client) DeleteHelmRepo(
+	ctx context.Context,
+	projectID, helmRepoID uint,
+) error {
+	return c.deleteRequest(
+		fmt.Sprintf(
+			"/projects/%d/helmrepos/%d",
+			projectID, helmRepoID,
+		),
+		nil,
+		nil,
+	)
+}
+
 // ListRegistries returns a list of registries for a project
 func (c *Client) ListRegistries(
 	ctx context.Context,

+ 78 - 0
api/server/handlers/helmrepo/delete.go

@@ -0,0 +1,78 @@
+package helmrepo
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/requestutils"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+	"gorm.io/gorm"
+)
+
+type HelmRepoDeleteHandler struct {
+	handlers.PorterHandlerReadWriter
+}
+
+func NewHelmRepoDeleteHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *HelmRepoDeleteHandler {
+	return &HelmRepoDeleteHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+	}
+}
+
+func (p *HelmRepoDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+
+	helmRepoID, reqErr := requestutils.GetURLParamUint(r, "helm_repo_id")
+
+	if reqErr != nil {
+		p.HandleAPIError(w, r, reqErr)
+		return
+	}
+
+	helmRepo, err := p.Repo().HelmRepo().ReadHelmRepo(proj.ID, helmRepoID)
+
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			p.HandleAPIError(w, r, apierrors.NewErrNotFound(fmt.Errorf("no such helm repo")))
+			return
+		}
+
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	if helmRepo.BasicAuthIntegrationID != 0 {
+		basicAuthInt, err := p.Repo().BasicIntegration().ReadBasicIntegration(proj.ID, helmRepo.BasicAuthIntegrationID)
+
+		if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
+			p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			return
+		} else if err == nil {
+			_, err = p.Repo().BasicIntegration().DeleteBasicIntegration(basicAuthInt)
+
+			if err != nil {
+				p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+				return
+			}
+		}
+	}
+
+	err = p.Repo().HelmRepo().DeleteHelmRepo(helmRepo)
+
+	if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	p.WriteResult(w, r, helmRepo.ToHelmRepoType())
+}

+ 29 - 0
api/server/router/helm_repo.go

@@ -81,6 +81,35 @@ func getHelmRepoRoutes(
 		Router:   r,
 	})
 
+	// DELETE /api/projects/{project_id}/helmrepos/{helm_repo_id} -> registry.NewHelmRepoDeleteHandler
+	deleteEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbDelete,
+			Method: types.HTTPVerbDelete,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath,
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.HelmRepoScope,
+			},
+		},
+	)
+
+	deleteHandler := helmrepo.NewHelmRepoDeleteHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: deleteEndpoint,
+		Handler:  deleteHandler,
+		Router:   r,
+	})
+
 	//  GET /api/projects/{project_id}/helmrepos/{helm_repo_id}/charts -> helmrepo.NewChartListHandler
 	hrListEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 49 - 0
cli/cmd/delete.go

@@ -85,6 +85,21 @@ var deleteAddonsCmd = &cobra.Command{
 	},
 }
 
+// deleteHelmCmd represents the "porter delete helm" subcommand
+var deleteHelmCmd = &cobra.Command{
+	Use:     "helm",
+	Aliases: []string{"helmrepo", "helmrepos"},
+	Short:   "Deletes an existing helm repo",
+	Args:    cobra.ExactArgs(1),
+	Run: func(cmd *cobra.Command, args []string) {
+		err := checkLoginAndRun(args, deleteHelm)
+
+		if err != nil {
+			os.Exit(1)
+		}
+	},
+}
+
 func init() {
 	deleteCmd.PersistentFlags().StringVar(
 		&namespace,
@@ -96,6 +111,7 @@ func init() {
 	deleteCmd.AddCommand(deleteAppsCmd)
 	deleteCmd.AddCommand(deleteJobsCmd)
 	deleteCmd.AddCommand(deleteAddonsCmd)
+	deleteCmd.AddCommand(deleteHelmCmd)
 
 	rootCmd.AddCommand(deleteCmd)
 }
@@ -221,3 +237,36 @@ func deleteAddon(_ *types.GetAuthenticatedUserResponse, client *api.Client, args
 
 	return nil
 }
+
+func deleteHelm(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
+	name := args[0]
+
+	resp, err := client.ListHelmRepos(context.Background(), cliConf.Project)
+
+	if err != nil {
+		return err
+	}
+
+	var repo *types.HelmRepo
+
+	for _, r := range resp {
+		if r.Name == name {
+			repo = r
+			break
+		}
+	}
+
+	if repo == nil {
+		return fmt.Errorf("no helm repo found with name: %s", name)
+	}
+
+	color.New(color.FgBlue).Printf("Deleting helm repo: %s\n", name)
+
+	err = client.DeleteHelmRepo(context.Background(), cliConf.Project, repo.ID)
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}

+ 27 - 0
internal/repository/gorm/auth.go

@@ -304,6 +304,33 @@ func (repo *BasicIntegrationRepository) ListBasicIntegrationsByProjectID(
 	return basics, nil
 }
 
+// DeleteBasicIntegration deletes an existing basic auth mechanism
+func (repo *BasicIntegrationRepository) DeleteBasicIntegration(
+	am *ints.BasicIntegration,
+) (*ints.BasicIntegration, error) {
+	project := &models.Project{}
+
+	if err := repo.db.Where("id = ?", am.ProjectID).First(&project).Error; err != nil {
+		return nil, err
+	}
+
+	assoc := repo.db.Model(&project).Association("BasicIntegrations")
+
+	if assoc.Error != nil {
+		return nil, assoc.Error
+	}
+
+	if err := assoc.Delete(am); err != nil {
+		return nil, err
+	}
+
+	if err := repo.db.Delete(am).Error; err != nil {
+		return nil, err
+	}
+
+	return am, nil
+}
+
 // EncryptBasicIntegrationData will encrypt the basic integration data before
 // writing to the DB
 func (repo *BasicIntegrationRepository) EncryptBasicIntegrationData(

+ 1 - 0
internal/repository/integrations.go

@@ -18,6 +18,7 @@ type BasicIntegrationRepository interface {
 	CreateBasicIntegration(am *ints.BasicIntegration) (*ints.BasicIntegration, error)
 	ReadBasicIntegration(projectID, id uint) (*ints.BasicIntegration, error)
 	ListBasicIntegrationsByProjectID(projectID uint) ([]*ints.BasicIntegration, error)
+	DeleteBasicIntegration(am *ints.BasicIntegration) (*ints.BasicIntegration, error)
 }
 
 // OIDCIntegrationRepository represents the set of queries on the OIDC auth

+ 21 - 0
internal/repository/test/auth.go

@@ -137,6 +137,27 @@ func (repo *BasicIntegrationRepository) ListBasicIntegrationsByProjectID(
 	return res, nil
 }
 
+// DeleteBasicIntegration deletes a basic integration
+func (repo *BasicIntegrationRepository) DeleteBasicIntegration(
+	am *ints.BasicIntegration,
+) (*ints.BasicIntegration, error) {
+	if !repo.canQuery {
+		return nil, errors.New("Cannot read from database")
+	}
+
+	var newInts []*ints.BasicIntegration
+
+	for _, basicInt := range repo.basicIntegrations {
+		if basicInt.ID != am.ID {
+			newInts = append(newInts, basicInt)
+		}
+	}
+
+	repo.basicIntegrations = newInts
+
+	return am, nil
+}
+
 // OIDCIntegrationRepository implements repository.OIDCIntegrationRepository
 type OIDCIntegrationRepository struct {
 	canQuery         bool