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

branch based preview deployments

Mohammed Nafees 3 лет назад
Родитель
Сommit
92d62e4e13

+ 1 - 1
api/server/handlers/environment/enable_pull_request.go

@@ -121,7 +121,7 @@ func (c *EnablePullRequestHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 			Ref: request.BranchFrom,
 			Inputs: map[string]interface{}{
 				"pr_number":      strconv.FormatUint(uint64(request.Number), 10),
-				"pr_title":       *pr.Title,
+				"pr_title":       pr.GetTitle(),
 				"pr_branch_from": request.BranchFrom,
 				"pr_branch_into": request.BranchInto,
 			},

+ 1 - 1
api/server/handlers/environment/finalize_deployment.go

@@ -148,7 +148,7 @@ func (c *FinalizeDeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
 		return
 	}
 
-	if depl.PRBranchFrom != depl.PRBranchInto {
+	if !depl.IsBranchDeploy() {
 		// add a check for the PR to be open before creating a comment
 		prClosed, err := isGithubPRClosed(client, owner, name, int(depl.PullRequestID))
 

+ 1 - 1
api/server/handlers/environment/finalize_deployment_with_errors.go

@@ -133,7 +133,7 @@ func (c *FinalizeDeploymentWithErrorsHandler) ServeHTTP(w http.ResponseWriter, r
 		},
 	)
 
-	if depl.PRBranchFrom != depl.PRBranchInto {
+	if !depl.IsBranchDeploy() {
 		// add a check for the PR to be open before creating a comment
 		prClosed, err := isGithubPRClosed(client, owner, name, int(depl.PullRequestID))
 

+ 14 - 0
api/server/handlers/environment/list_deployments_by_cluster.go

@@ -281,6 +281,10 @@ func fetchOpenPullRequests(
 			}
 		}
 
+		if isDeployBranch(pr.GetHead().GetRef(), env) {
+			continue
+		}
+
 		if _, ok := deplInfoMap[fmt.Sprintf("%s-%s-%d", env.GitRepoOwner, env.GitRepoName, pr.GetNumber())]; !ok {
 			prs = append(prs, &types.PullRequest{
 				Title:      pr.GetTitle(),
@@ -295,3 +299,13 @@ func fetchOpenPullRequests(
 
 	return prs, nil
 }
+
+func isDeployBranch(branch string, env *models.Environment) bool {
+	for _, b := range env.ToEnvironmentType().GitDeployBranches {
+		if b == branch {
+			return true
+		}
+	}
+
+	return false
+}

+ 44 - 0
api/server/handlers/webhook/github_incoming.go

@@ -387,7 +387,51 @@ func (c *GithubIncomingWebhookHandler) processPushEvent(event *github.PushEvent,
 		}
 	}
 
+	client, err := getGithubClientFromEnvironment(c.Config(), env)
+
+	if err != nil {
+		return fmt.Errorf("[webhookID: %s, owner: %s, repo: %s] error creating github client: %w",
+			webhookID, owner, repo, err)
+	}
+
+	depl := &models.Deployment{
+		EnvironmentID: env.ID,
+		Namespace:     "",
+		Status:        types.DeploymentStatusCreating,
+		PullRequestID: 0,
+		PRName:        fmt.Sprintf("Deployment for branch %s", branch),
+		RepoName:      repo,
+		RepoOwner:     owner,
+		CommitSHA:     event.GetHeadCommit().GetSHA()[:7],
+		PRBranchFrom:  branch,
+		PRBranchInto:  branch,
+	}
+
+	depl, err = c.Repo().Environment().CreateDeployment(depl)
+
+	if err != nil {
+		return fmt.Errorf("[webhookID: %s, owner: %s, repo: %s, environmentID: %d, branch: %s] "+
+			"error creating new deployment: %w", webhookID, owner, repo, env.ID, branch, err)
+	}
+
 	// FIXME: we should case on if env mode is auto or manual
+	_, err = client.Actions.CreateWorkflowDispatchEventByFileName(
+		r.Context(), owner, repo, fmt.Sprintf("porter_%s_env.yml", env.Name),
+		github.CreateWorkflowDispatchEventRequest{
+			Ref: branch,
+			Inputs: map[string]interface{}{
+				"pr_number":      depl.ID,
+				"pr_title":       fmt.Sprintf("Deployment for branch %s", branch),
+				"pr_branch_from": branch,
+				"pr_branch_into": branch,
+			},
+		},
+	)
+
+	if err != nil {
+		return fmt.Errorf("[webhookID: %s, owner: %s, repo: %s, environmentID: %d, branch: %s] "+
+			"error creating workflow dispatch event: %w", webhookID, owner, repo, env.ID, branch, err)
+	}
 
 	return nil
 }

+ 7 - 17
api/types/environment.go

@@ -86,18 +86,14 @@ type FinalizeDeploymentRequest struct {
 	SuccessfulResources []*SuccessfullyDeployedResource `json:"successful_resources"`
 	Subdomain           string                          `json:"subdomain"`
 	PRNumber            uint                            `json:"pr_number"`
-
-	// legacy usage for backwards compatibility
-	Namespace string `json:"namespace"`
+	Namespace           string                          `json:"namespace"`
 }
 
 type FinalizeDeploymentWithErrorsRequest struct {
 	SuccessfulResources []*SuccessfullyDeployedResource `json:"successful_resources"`
 	Errors              map[string]string               `json:"errors" form:"required"`
 	PRNumber            uint                            `json:"pr_number"`
-
-	// legacy usage for backwards compatibility
-	Namespace string `json:"namespace"`
+	Namespace           string                          `json:"namespace"`
 }
 
 type UpdateDeploymentRequest struct {
@@ -106,9 +102,7 @@ type UpdateDeploymentRequest struct {
 	PRBranchFrom string `json:"gh_pr_branch_from" form:"required"`
 	CommitSHA    string `json:"commit_sha" form:"required"`
 	PRNumber     uint   `json:"pr_number"`
-
-	// legacy usage for backwards compatibility
-	Namespace string `json:"namespace"`
+	Namespace    string `json:"namespace"`
 }
 
 type ListDeploymentRequest struct {
@@ -121,9 +115,7 @@ type UpdateDeploymentStatusRequest struct {
 	PRBranchFrom string `json:"gh_pr_branch_from" form:"required"`
 	Status       string `json:"status" form:"required,oneof=created creating inactive failed"`
 	PRNumber     uint   `json:"pr_number"`
-
-	// legacy usage for backwards compatibility
-	Namespace string `json:"namespace"`
+	Namespace    string `json:"namespace"`
 }
 
 type DeleteDeploymentRequest struct {
@@ -131,11 +123,9 @@ type DeleteDeploymentRequest struct {
 }
 
 type GetDeploymentRequest struct {
-	DeploymentID uint `schema:"id"`
-	PRNumber     uint `schema:"pr_number"`
-
-	// legacy usage for backwards compatibility
-	Namespace string `schema:"namespace"`
+	DeploymentID uint   `schema:"id"`
+	PRNumber     uint   `schema:"pr_number"`
+	Namespace    string `schema:"namespace"`
 }
 
 type PullRequest struct {

+ 129 - 64
cli/cmd/apply.go

@@ -754,6 +754,10 @@ func NewDeploymentHook(client *api.Client, resourceGroup *switchboardTypes.Resou
 	return res, nil
 }
 
+func (t *DeploymentHook) isBranchDeploy() bool {
+	return t.branchFrom != "" && t.branchInto != "" && t.branchFrom == t.branchInto
+}
+
 func (t *DeploymentHook) PreApply() error {
 	if isSystemNamespace(t.namespace) {
 		color.New(color.FgYellow).Printf("attempting to deploy to system namespace '%s'\n", t.namespace)
@@ -826,51 +830,72 @@ func (t *DeploymentHook) PreApply() error {
 		}
 	}
 
-	// attempt to read the deployment -- if it doesn't exist, create it
-	_, err = t.client.GetDeployment(
-		context.Background(),
-		t.projectID, t.clusterID, t.envID,
-		&types.GetDeploymentRequest{
-			PRNumber: t.prID,
-		},
-	)
+	var deplErr error
+
+	if t.isBranchDeploy() {
+		_, deplErr = t.client.GetDeployment(
+			context.Background(),
+			t.projectID, t.clusterID, t.envID,
+			&types.GetDeploymentRequest{
+				Namespace: t.namespace,
+			},
+		)
+	} else {
+		_, deplErr = t.client.GetDeployment(
+			context.Background(),
+			t.projectID, t.clusterID, t.envID,
+			&types.GetDeploymentRequest{
+				PRNumber: t.prID,
+			},
+		)
+	}
 
-	if err != nil && strings.Contains(err.Error(), "not found") {
+	if deplErr != nil && strings.Contains(deplErr.Error(), "not found") {
 		// in this case, create the deployment
+		createReq := &types.CreateDeploymentRequest{
+			Namespace:     t.namespace,
+			PullRequestID: t.prID,
+			CreateGHDeploymentRequest: &types.CreateGHDeploymentRequest{
+				ActionID: t.actionID,
+			},
+			GitHubMetadata: &types.GitHubMetadata{
+				PRName:       t.prName,
+				RepoName:     t.repoName,
+				RepoOwner:    t.repoOwner,
+				CommitSHA:    t.commitSHA,
+				PRBranchFrom: t.branchFrom,
+				PRBranchInto: t.branchInto,
+			},
+		}
+
+		if t.isBranchDeploy() {
+			createReq.PullRequestID = 0
+		}
+
 		_, err = t.client.CreateDeployment(
 			context.Background(),
 			t.projectID, t.gitInstallationID, t.clusterID,
-			t.repoOwner, t.repoName,
-			&types.CreateDeploymentRequest{
-				Namespace:     t.namespace,
-				PullRequestID: t.prID,
-				CreateGHDeploymentRequest: &types.CreateGHDeploymentRequest{
-					ActionID: t.actionID,
-				},
-				GitHubMetadata: &types.GitHubMetadata{
-					PRName:       t.prName,
-					RepoName:     t.repoName,
-					RepoOwner:    t.repoOwner,
-					CommitSHA:    t.commitSHA,
-					PRBranchFrom: t.branchFrom,
-					PRBranchInto: t.branchInto,
-				},
-			},
+			t.repoOwner, t.repoName, createReq,
 		)
 	} else if err == nil {
+		updateReq := &types.UpdateDeploymentRequest{
+			Namespace: t.namespace,
+			PRNumber:  t.prID,
+			CreateGHDeploymentRequest: &types.CreateGHDeploymentRequest{
+				ActionID: t.actionID,
+			},
+			PRBranchFrom: t.branchFrom,
+			CommitSHA:    t.commitSHA,
+		}
+
+		if t.isBranchDeploy() {
+			updateReq.PRNumber = 0
+		}
+
 		_, err = t.client.UpdateDeployment(
 			context.Background(),
 			t.projectID, t.gitInstallationID, t.clusterID,
-			t.repoOwner, t.repoName,
-			&types.UpdateDeploymentRequest{
-				Namespace: t.namespace,
-				PRNumber:  t.prID,
-				CreateGHDeploymentRequest: &types.CreateGHDeploymentRequest{
-					ActionID: t.actionID,
-				},
-				PRBranchFrom: t.branchFrom,
-				CommitSHA:    t.commitSHA,
-			},
+			t.repoOwner, t.repoName, updateReq,
 		)
 	}
 
@@ -953,10 +978,15 @@ func (t *DeploymentHook) PostApply(populatedData map[string]interface{}) error {
 	}
 
 	req := &types.FinalizeDeploymentRequest{
-		PRNumber:  t.prID,
 		Subdomain: strings.Join(subdomains, ", "),
 	}
 
+	if t.isBranchDeploy() {
+		req.Namespace = t.namespace
+	} else {
+		req.PRNumber = t.prID
+	}
+
 	for _, res := range t.resourceGroup.Resources {
 		releaseType := getReleaseType(res)
 		releaseName := getReleaseName(res)
@@ -980,47 +1010,82 @@ func (t *DeploymentHook) PostApply(populatedData map[string]interface{}) error {
 }
 
 func (t *DeploymentHook) OnError(error) {
+	var deplErr error
+
+	if t.isBranchDeploy() {
+		_, deplErr = t.client.GetDeployment(
+			context.Background(),
+			t.projectID, t.clusterID, t.envID,
+			&types.GetDeploymentRequest{
+				Namespace: t.namespace,
+			},
+		)
+	} else {
+		_, deplErr = t.client.GetDeployment(
+			context.Background(),
+			t.projectID, t.clusterID, t.envID,
+			&types.GetDeploymentRequest{
+				PRNumber: t.prID,
+			},
+		)
+	}
+
 	// if the deployment exists, throw an error for that deployment
-	_, err := t.client.GetDeployment(
-		context.Background(),
-		t.projectID, t.clusterID, t.envID,
-		&types.GetDeploymentRequest{
-			PRNumber: t.prID,
-		},
-	)
+	if deplErr == nil {
+		req := &types.UpdateDeploymentStatusRequest{
+			CreateGHDeploymentRequest: &types.CreateGHDeploymentRequest{
+				ActionID: t.actionID,
+			},
+			PRBranchFrom: t.branchFrom,
+			Status:       string(types.DeploymentStatusFailed),
+		}
+
+		if t.isBranchDeploy() {
+			req.Namespace = t.namespace
+		} else {
+			req.PRNumber = t.prID
+		}
 
-	if err == nil {
 		// FIXME: try to use the error with a custom logger
 		t.client.UpdateDeploymentStatus(
 			context.Background(),
 			t.projectID, t.gitInstallationID, t.clusterID,
-			t.repoOwner, t.repoName,
-			&types.UpdateDeploymentStatusRequest{
-				PRNumber: t.prID,
-				CreateGHDeploymentRequest: &types.CreateGHDeploymentRequest{
-					ActionID: t.actionID,
-				},
-				PRBranchFrom: t.branchFrom,
-				Status:       string(types.DeploymentStatusFailed),
-			},
+			t.repoOwner, t.repoName, req,
 		)
 	}
 }
 
 func (t *DeploymentHook) OnConsolidatedErrors(allErrors map[string]error) {
-	// if the deployment exists, throw an error for that deployment
-	_, getDeplErr := t.client.GetDeployment(
-		context.Background(),
-		t.projectID, t.clusterID, t.envID,
-		&types.GetDeploymentRequest{
-			PRNumber: t.prID,
-		},
-	)
+	var deplErr error
 
-	if getDeplErr == nil {
+	if t.isBranchDeploy() {
+		_, deplErr = t.client.GetDeployment(
+			context.Background(),
+			t.projectID, t.clusterID, t.envID,
+			&types.GetDeploymentRequest{
+				Namespace: t.namespace,
+			},
+		)
+	} else {
+		_, deplErr = t.client.GetDeployment(
+			context.Background(),
+			t.projectID, t.clusterID, t.envID,
+			&types.GetDeploymentRequest{
+				PRNumber: t.prID,
+			},
+		)
+	}
+
+	// if the deployment exists, throw an error for that deployment
+	if deplErr == nil {
 		req := &types.FinalizeDeploymentWithErrorsRequest{
-			PRNumber: t.prID,
-			Errors:   make(map[string]string),
+			Errors: make(map[string]string),
+		}
+
+		if t.isBranchDeploy() {
+			req.Namespace = t.namespace
+		} else {
+			req.PRNumber = t.prID
 		}
 
 		for _, res := range t.resourceGroup.Resources {

+ 4 - 1
internal/models/environment.go

@@ -118,7 +118,6 @@ type Deployment struct {
 }
 
 func (d *Deployment) ToDeploymentType() *types.Deployment {
-
 	ghMetadata := &types.GitHubMetadata{
 		DeploymentID: d.GHDeploymentID,
 		PRName:       d.PRName,
@@ -141,3 +140,7 @@ func (d *Deployment) ToDeploymentType() *types.Deployment {
 		GitHubMetadata: ghMetadata,
 	}
 }
+
+func (d *Deployment) IsBranchDeploy() bool {
+	return d.PullRequestID == 0 && d.PRBranchFrom != "" && d.PRBranchInto != "" && d.PRBranchFrom == d.PRBranchInto
+}