Explorar o código

move namespace logic and add delete endpoint for deployments

Alexander Belanger %!s(int64=4) %!d(string=hai) anos
pai
achega
b92f7f4662

+ 18 - 0
api/server/handlers/environment/create_deployment.go

@@ -3,6 +3,7 @@ package environment
 import (
 	"net/http"
 
+	"github.com/porter-dev/porter/api/server/authz"
 	"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"
@@ -14,6 +15,7 @@ import (
 
 type CreateDeploymentHandler struct {
 	handlers.PorterHandlerReadWriter
+	authz.KubernetesAgentGetter
 }
 
 func NewCreateDeploymentHandler(
@@ -23,6 +25,7 @@ func NewCreateDeploymentHandler(
 ) *CreateDeploymentHandler {
 	return &CreateDeploymentHandler{
 		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+		KubernetesAgentGetter:   authz.NewOutOfClusterAgentGetter(config),
 	}
 }
 
@@ -58,5 +61,20 @@ func (c *CreateDeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
 		return
 	}
 
+	// create the backing namespace
+	agent, err := c.GetAgent(r, cluster, "")
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	_, err = agent.CreateNamespace(depl.Namespace)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
 	c.WriteResult(w, r, depl.ToDeploymentType())
 }

+ 22 - 0
api/server/handlers/environment/delete_deployment.go

@@ -2,7 +2,9 @@ package environment
 
 import (
 	"net/http"
+	"strings"
 
+	"github.com/porter-dev/porter/api/server/authz"
 	"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"
@@ -14,6 +16,7 @@ import (
 
 type DeleteDeploymentHandler struct {
 	handlers.PorterHandlerReadWriter
+	authz.KubernetesAgentGetter
 }
 
 func NewDeleteDeploymentHandler(
@@ -23,6 +26,7 @@ func NewDeleteDeploymentHandler(
 ) *DeleteDeploymentHandler {
 	return &DeleteDeploymentHandler{
 		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+		KubernetesAgentGetter:   authz.NewOutOfClusterAgentGetter(config),
 	}
 }
 
@@ -53,6 +57,24 @@ func (c *DeleteDeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
 		return
 	}
 
+	// delete corresponding namespace
+	agent, err := c.GetAgent(r, cluster, "")
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	// make sure we don't delete default or kube-system by checking for prefix, for now
+	if strings.Contains(depl.Namespace, "pr-") {
+		err = agent.DeleteNamespace(depl.Namespace)
+
+		if err != nil {
+			c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			return
+		}
+	}
+
 	// TODO: delete corresponding Github actions files using the client
 	// client, err := getGithubClientFromEnvironment(c.Config(), env)
 

+ 71 - 0
api/server/handlers/environment/list_deployments.go

@@ -0,0 +1,71 @@
+package environment
+
+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/types"
+	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/internal/models/integrations"
+	"gorm.io/gorm"
+)
+
+type ListDeploymentsHandler struct {
+	handlers.PorterHandlerReadWriter
+}
+
+func NewListDeploymentsHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *ListDeploymentsHandler {
+	return &ListDeploymentsHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+	}
+}
+
+func (c *ListDeploymentsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	ga, _ := r.Context().Value(types.GitInstallationScope).(*integrations.GithubAppInstallation)
+	project, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+
+	request := &types.GetDeploymentRequest{}
+
+	if ok := c.DecodeAndValidate(w, r, request); !ok {
+		return
+	}
+
+	// read the environment to get the environment id
+	env, err := c.Repo().Environment().ReadEnvironment(project.ID, cluster.ID, uint(ga.InstallationID))
+
+	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
+			fmt.Errorf("environment not found: is the environment enabled for this git installation?"),
+			http.StatusNotFound,
+		))
+		return
+	} else if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	depls, err := c.Repo().Environment().ListDeployments(env.ID)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	res := make([]*types.Deployment, 0)
+
+	for _, depl := range depls {
+		res = append(res, depl.ToDeploymentType())
+	}
+
+	c.WriteResult(w, r, res)
+}

+ 31 - 0
api/server/router/git_installation.go

@@ -176,6 +176,37 @@ func getGitInstallationRoutes(
 		Router:   r,
 	})
 
+	// GET /api/projects/{project_id}/gitrepos/{git_installation_id}/clusters/{cluster_id}/deployments ->
+	// environment.NewCreateDeploymentHandler
+	listDeploymentsEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/clusters/{cluster_id}/deployments",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.GitInstallationScope,
+				types.ClusterScope,
+			},
+		},
+	)
+
+	listDeploymentsHandler := environment.NewListDeploymentsHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: listDeploymentsEndpoint,
+		Handler:  listDeploymentsHandler,
+		Router:   r,
+	})
+
 	// POST /api/projects/{project_id}/gitrepos/{git_installation_id}/clusters/{cluster_id}/deployment/finalize ->
 	// environment.NewFinalizeDeploymentHandler
 	finalizeDeploymentEndpoint := factory.NewAPIEndpoint(

+ 1 - 0
internal/repository/environment.go

@@ -9,6 +9,7 @@ type EnvironmentRepository interface {
 	DeleteEnvironment(env *models.Environment) (*models.Environment, error)
 	CreateDeployment(deployment *models.Deployment) (*models.Deployment, error)
 	ReadDeployment(environmentID uint, namespace string) (*models.Deployment, error)
+	ListDeployments(environmentID uint) ([]*models.Deployment, error)
 	UpdateDeployment(deployment *models.Deployment) (*models.Deployment, error)
 	DeleteDeployment(deployment *models.Deployment) (*models.Deployment, error)
 }

+ 10 - 0
internal/repository/gorm/environment.go

@@ -72,6 +72,16 @@ func (repo *EnvironmentRepository) ReadDeployment(environmentID uint, namespace
 	return depl, nil
 }
 
+func (repo *EnvironmentRepository) ListDeployments(environmentID uint) ([]*models.Deployment, error) {
+	depls := make([]*models.Deployment, 0)
+
+	if err := repo.db.Order("id asc").Where("environment_id = ?", environmentID).Find(&depls).Error; err != nil {
+		return nil, err
+	}
+
+	return depls, nil
+}
+
 func (repo *EnvironmentRepository) DeleteDeployment(deployment *models.Deployment) (*models.Deployment, error) {
 	if err := repo.db.Delete(deployment).Error; err != nil {
 		return nil, err

+ 4 - 0
internal/repository/test/environment.go

@@ -43,6 +43,10 @@ func (repo *EnvironmentRepository) ReadDeployment(environmentID uint, namespace
 	panic("unimplemented")
 }
 
+func (repo *EnvironmentRepository) ListDeployments(environmentID uint) ([]*models.Deployment, error) {
+	panic("unimplemented")
+}
+
 func (repo *EnvironmentRepository) DeleteDeployment(deployment *models.Deployment) (*models.Deployment, error) {
 	panic("unimplemented")
 }