Просмотр исходного кода

[POR-873] Custom namespaces for branch based preview deployments (#2620)

* custom namespaces for branch deploys

* update get deployment

* first check for branch based deployments

* more fixes
Mohammed Nafees 3 лет назад
Родитель
Сommit
08344192db

+ 12 - 2
api/server/handlers/environment/common.go

@@ -76,9 +76,9 @@ func validateGetDeploymentRequest(
 	request *types.GetDeploymentRequest,
 	repo repository.Repository,
 ) (*models.Deployment, apierrors.RequestError) {
-	if request.PRNumber == 0 && request.DeploymentID == 0 && request.Namespace == "" {
+	if request.PRNumber == 0 && request.DeploymentID == 0 && request.Namespace == "" && request.Branch == "" {
 		return nil, apierrors.NewErrPassThroughToClient(
-			fmt.Errorf("one of id, pr_number or namespace must be present in request body"), http.StatusBadRequest,
+			fmt.Errorf("one of id, pr_number, namespace or branch must be present in request body"), http.StatusBadRequest,
 		)
 	}
 
@@ -109,6 +109,16 @@ func validateGetDeploymentRequest(
 	} else if request.Namespace != "" {
 		depl, err = repo.Environment().ReadDeployment(envID, request.Namespace)
 
+		if err != nil {
+			if errors.Is(err, gorm.ErrRecordNotFound) {
+				return nil, apierrors.NewErrNotFound(errDeploymentNotFound)
+			}
+
+			return nil, apierrors.NewErrInternal(err)
+		}
+	} else if request.Branch != "" {
+		depl, err = repo.Environment().ReadDeploymentForBranch(envID, owner, name, request.Branch)
+
 		if err != nil {
 			if errors.Is(err, gorm.ErrRecordNotFound) {
 				return nil, apierrors.NewErrNotFound(errDeploymentNotFound)

+ 1 - 7
api/server/handlers/environment/delete_deployment.go

@@ -68,13 +68,7 @@ func (c *DeleteDeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
 
 	// make sure we do not delete any kubernetes "system" namespaces
 	if !isSystemNamespace(depl.Namespace) {
-		err = agent.DeleteNamespace(depl.Namespace)
-
-		if err != nil {
-			c.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error deleting preview deployment namespace: %w",
-				err)))
-			return
-		}
+		agent.DeleteNamespace(depl.Namespace)
 	}
 
 	// check that the environment belongs to the project and cluster IDs

+ 27 - 24
api/server/handlers/environment/update_deployment.go

@@ -50,9 +50,9 @@ func (c *UpdateDeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
 		return
 	}
 
-	if request.Namespace == "" && request.PRNumber == 0 {
+	if request.Namespace == "" && request.PRNumber == 0 && request.PRBranchFrom == "" {
 		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
-			fmt.Errorf("either namespace or pr_number must be present in request body"), http.StatusBadRequest,
+			fmt.Errorf("either namespace, pr_number or pr_branch_from must be present in request body"), http.StatusBadRequest,
 		))
 		return
 	}
@@ -67,32 +67,38 @@ func (c *UpdateDeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
 		return
 	}
 
-	var depl *models.Deployment
+	depl, err := c.Repo().Environment().ReadDeploymentForBranch(env.ID, owner, name, request.PRBranchFrom)
 
-	// read the deployment
-	if request.PRNumber != 0 {
-		depl, err = c.Repo().Environment().ReadDeploymentByGitDetails(env.ID, owner, name, request.PRNumber)
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
 
-		if err != nil {
-			if errors.Is(err, gorm.ErrRecordNotFound) {
-				c.HandleAPIError(w, r, apierrors.NewErrNotFound(errDeploymentNotFound))
+	if depl == nil {
+		if request.PRNumber != 0 {
+			depl, err = c.Repo().Environment().ReadDeploymentByGitDetails(env.ID, owner, name, request.PRNumber)
+
+			if err != nil {
+				if errors.Is(err, gorm.ErrRecordNotFound) {
+					c.HandleAPIError(w, r, apierrors.NewErrNotFound(errDeploymentNotFound))
+					return
+				}
+
+				c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 				return
 			}
+		} else if request.Namespace != "" {
+			depl, err = c.Repo().Environment().ReadDeployment(env.ID, request.Namespace)
 
-			c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-			return
-		}
-	} else if request.Namespace != "" {
-		depl, err = c.Repo().Environment().ReadDeployment(env.ID, request.Namespace)
+			if err != nil {
+				if errors.Is(err, gorm.ErrRecordNotFound) {
+					c.HandleAPIError(w, r, apierrors.NewErrNotFound(errDeploymentNotFound))
+					return
+				}
 
-		if err != nil {
-			if errors.Is(err, gorm.ErrRecordNotFound) {
-				c.HandleAPIError(w, r, apierrors.NewErrNotFound(errDeploymentNotFound))
+				c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 				return
 			}
-
-			c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-			return
 		}
 	}
 
@@ -135,10 +141,7 @@ func (c *UpdateDeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
 		return
 	}
 
-	if !depl.IsBranchDeploy() {
-		depl.Namespace = request.Namespace
-	}
-
+	depl.Namespace = request.Namespace
 	depl.GHDeploymentID = ghDeployment.GetID()
 	depl.CommitSHA = request.CommitSHA
 

+ 24 - 18
api/server/handlers/environment/update_deployment_status.go

@@ -72,32 +72,38 @@ func (c *UpdateDeploymentStatusHandler) ServeHTTP(w http.ResponseWriter, r *http
 		return
 	}
 
-	var depl *models.Deployment
+	depl, err := c.Repo().Environment().ReadDeploymentForBranch(env.ID, owner, name, request.PRBranchFrom)
 
-	// read the deployment
-	if request.PRNumber != 0 {
-		depl, err = c.Repo().Environment().ReadDeploymentByGitDetails(env.ID, owner, name, request.PRNumber)
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
 
-		if err != nil {
-			if errors.Is(err, gorm.ErrRecordNotFound) {
-				c.HandleAPIError(w, r, apierrors.NewErrNotFound(errDeploymentNotFound))
+	if depl == nil {
+		if request.PRNumber != 0 {
+			depl, err = c.Repo().Environment().ReadDeploymentByGitDetails(env.ID, owner, name, request.PRNumber)
+
+			if err != nil {
+				if errors.Is(err, gorm.ErrRecordNotFound) {
+					c.HandleAPIError(w, r, apierrors.NewErrNotFound(errDeploymentNotFound))
+					return
+				}
+
+				c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 				return
 			}
+		} else if request.Namespace != "" {
+			depl, err = c.Repo().Environment().ReadDeployment(env.ID, request.Namespace)
 
-			c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-			return
-		}
-	} else if request.Namespace != "" {
-		depl, err = c.Repo().Environment().ReadDeployment(env.ID, request.Namespace)
+			if err != nil {
+				if errors.Is(err, gorm.ErrRecordNotFound) {
+					c.HandleAPIError(w, r, apierrors.NewErrNotFound(errDeploymentNotFound))
+					return
+				}
 
-		if err != nil {
-			if errors.Is(err, gorm.ErrRecordNotFound) {
-				c.HandleAPIError(w, r, apierrors.NewErrNotFound(errDeploymentNotFound))
+				c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 				return
 			}
-
-			c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-			return
 		}
 	}
 

+ 1 - 9
api/server/handlers/webhook/github_incoming.go

@@ -395,21 +395,13 @@ func (c *GithubIncomingWebhookHandler) processPushEvent(event *github.PushEvent,
 		return fmt.Errorf("[webhookID: %s, owner: %s, repo: %s] error creating github client: %w", webhookID, owner, repo, err)
 	}
 
-	namespace := fmt.Sprintf("previewbranch-%s-%s-%s", branch, strings.ReplaceAll(strings.ToLower(owner), "_", "-"),
-		strings.ReplaceAll(strings.ToLower(repo), "_", "-"))
-
-	if len(namespace) > 63 {
-		namespace = namespace[:63] // Kubernetes' DNS 1123 label requirement
-	}
-
 	var deplID uint
 
-	depl, err := c.Repo().Environment().ReadDeployment(env.ID, namespace)
+	depl, err := c.Repo().Environment().ReadDeploymentForBranch(env.ID, owner, repo, branch)
 
 	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
 		depl, err := c.Repo().Environment().CreateDeployment(&models.Deployment{
 			EnvironmentID: env.ID,
-			Namespace:     namespace,
 			Status:        types.DeploymentStatusCreating,
 			PRName:        fmt.Sprintf("Deployment for branch %s", branch),
 			RepoName:      repo,

+ 1 - 0
api/types/environment.go

@@ -126,6 +126,7 @@ type GetDeploymentRequest struct {
 	DeploymentID uint   `schema:"id"`
 	PRNumber     uint   `schema:"pr_number"`
 	Namespace    string `schema:"namespace"`
+	Branch       string `schema:branch`
 }
 
 type PullRequest struct {

+ 3 - 7
cli/cmd/apply.go

@@ -788,10 +788,6 @@ func (t *DeploymentHook) PreApply() error {
 		return fmt.Errorf("could not find environment for deployment")
 	}
 
-	if t.isBranchDeploy() {
-		t.namespace = preview.GetNamespaceForBranchDeploy(t.branchFrom, t.repoOwner, t.repoName)
-	}
-
 	nsList, err := t.client.GetK8sNamespaces(
 		context.Background(), t.projectID, t.clusterID,
 	)
@@ -841,7 +837,7 @@ func (t *DeploymentHook) PreApply() error {
 			context.Background(),
 			t.projectID, t.clusterID, t.envID,
 			&types.GetDeploymentRequest{
-				Namespace: t.namespace,
+				Branch: t.branchFrom,
 			},
 		)
 	} else {
@@ -1021,7 +1017,7 @@ func (t *DeploymentHook) OnError(error) {
 			context.Background(),
 			t.projectID, t.clusterID, t.envID,
 			&types.GetDeploymentRequest{
-				Namespace: t.namespace,
+				Branch: t.branchFrom,
 			},
 		)
 	} else {
@@ -1067,7 +1063,7 @@ func (t *DeploymentHook) OnConsolidatedErrors(allErrors map[string]error) {
 			context.Background(),
 			t.projectID, t.clusterID, t.envID,
 			&types.GetDeploymentRequest{
-				Namespace: t.namespace,
+				Branch: t.branchFrom,
 			},
 		)
 	} else {

+ 1 - 30
cli/cmd/preview/utils.go

@@ -5,7 +5,6 @@ import (
 	"fmt"
 	"os"
 	"strconv"
-	"strings"
 
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/config"
@@ -136,7 +135,7 @@ func GetTarget(resourceName string, input map[string]interface{}) (*preview.Targ
 		output.Cluster = uint(cluster)
 	}
 
-	output.Namespace = getNamespace()
+	output.Namespace = os.Getenv("PORTER_NAMESPACE")
 
 	// next, check for values in the YAML file
 	if output.Project == 0 {
@@ -192,34 +191,6 @@ func GetTarget(resourceName string, input map[string]interface{}) (*preview.Targ
 	return output, nil
 }
 
-func GetNamespaceForBranchDeploy(branch, owner, name string) string {
-	namespace := fmt.Sprintf("previewbranch-%s-%s-%s", branch,
-		strings.ReplaceAll(strings.ToLower(owner), "_", "-"),
-		strings.ReplaceAll(strings.ToLower(name), "_", "-"))
-
-	if len(namespace) > 63 {
-		namespace = namespace[:63] // Kubernetes' DNS 1123 label requirement
-	}
-
-	return namespace
-}
-
-func getNamespace() string {
-	if owner, ok := os.LookupEnv("PORTER_REPO_OWNER"); ok {
-		if repo, ok := os.LookupEnv("PORTER_REPO_NAME"); ok {
-			if branchFrom, ok := os.LookupEnv("PORTER_BRANCH_FROM"); ok {
-				if branchInto, ok := os.LookupEnv("PORTER_BRANCH_INTO"); ok {
-					if branchInto == branchFrom { // branch deploy
-						return GetNamespaceForBranchDeploy(branchInto, owner, repo)
-					}
-				}
-			}
-		}
-	}
-
-	return os.Getenv("PORTER_NAMESPACE")
-}
-
 func existsInRepo(projectID uint, name, version, url string) (map[string]interface{}, error) {
 	chart, err := config.GetAPIClient().GetTemplate(
 		context.Background(),

+ 2 - 0
internal/repository/environment.go

@@ -11,10 +11,12 @@ type EnvironmentRepository interface {
 	ListEnvironments(projectID, clusterID uint) ([]*models.Environment, error)
 	UpdateEnvironment(environment *models.Environment) (*models.Environment, error)
 	DeleteEnvironment(env *models.Environment) (*models.Environment, error)
+
 	CreateDeployment(deployment *models.Deployment) (*models.Deployment, error)
 	ReadDeployment(environmentID uint, namespace string) (*models.Deployment, error)
 	ReadDeploymentByID(projectID, clusterID, id uint) (*models.Deployment, error)
 	ReadDeploymentByGitDetails(environmentID uint, owner, repo string, prNumber uint) (*models.Deployment, error)
+	ReadDeploymentForBranch(environmentID uint, owner, name, branch string) (*models.Deployment, error)
 	ListDeploymentsByCluster(projectID, clusterID uint, states ...string) ([]*models.Deployment, error)
 	ListDeployments(environmentID uint, states ...string) ([]*models.Deployment, error)
 	UpdateDeployment(deployment *models.Deployment) (*models.Deployment, error)

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

@@ -195,6 +195,29 @@ func (repo *EnvironmentRepository) ReadDeploymentByGitDetails(
 	return depl, nil
 }
 
+func (repo *EnvironmentRepository) ReadDeploymentForBranch(environmentID uint, owner, name, branch string) (*models.Deployment, error) {
+	depl := &models.Deployment{}
+
+	switch repo.db.Dialector.Name() {
+	case "sqlite":
+		if err := repo.db.Order("id desc").
+			Where("environment_id = ? AND repo_owner LIKE ? AND repo_name LIKE ? AND pr_branch_from = ? AND pr_branch_into = ?",
+				environmentID, owner, name, branch, branch).
+			First(&depl).Error; err != nil {
+			return nil, err
+		}
+	case "postgres":
+		if err := repo.db.Order("id desc").
+			Where("environment_id = ? AND repo_owner iLIKE ? AND repo_name iLIKE ? AND pr_branch_from = ? AND pr_branch_into = ?",
+				environmentID, owner, name, branch, branch).
+			First(&depl).Error; err != nil {
+			return nil, err
+		}
+	}
+
+	return depl, nil
+}
+
 func (repo *EnvironmentRepository) ListDeploymentsByCluster(projectID, clusterID uint, states ...string) ([]*models.Deployment, error) {
 	query := repo.db.
 		Order("deployments.updated_at desc").

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

@@ -70,6 +70,10 @@ func (repo *EnvironmentRepository) ReadDeploymentByGitDetails(environmentID uint
 	panic("unimplemented")
 }
 
+func (repo *EnvironmentRepository) ReadDeploymentForBranch(environmentID uint, owner, name, branch string) (*models.Deployment, error) {
+	panic("unimplemented")
+}
+
 func (repo *EnvironmentRepository) ListDeploymentsByCluster(projectID, clusterID uint, states ...string) ([]*models.Deployment, error) {
 	panic("unimplemented")
 }