Browse Source

Merge pull request #2178 from porter-dev/master

Preview env improvements -> staging
abelanger5 3 years ago
parent
commit
f16d98f793

+ 3 - 23
api/client/environment.go

@@ -10,10 +10,10 @@ import (
 func (c *Client) ListEnvironments(
 func (c *Client) ListEnvironments(
 	ctx context.Context,
 	ctx context.Context,
 	projID, clusterID uint,
 	projID, clusterID uint,
-) ([]*types.Environment, error) {
-	var resp []*types.Environment
+) (*types.ListEnvironmentsResponse, error) {
+	resp := &types.ListEnvironmentsResponse{}
 
 
-	err := c.postRequest(
+	err := c.getRequest(
 		fmt.Sprintf("/projects/%d/clusters/%d/environments", projID, clusterID),
 		fmt.Sprintf("/projects/%d/clusters/%d/environments", projID, clusterID),
 		nil,
 		nil,
 		resp,
 		resp,
@@ -22,26 +22,6 @@ func (c *Client) ListEnvironments(
 	return resp, err
 	return resp, err
 }
 }
 
 
-func (c *Client) CreateDeployment(
-	ctx context.Context,
-	projID, gitInstallationID, clusterID uint,
-	gitRepoOwner, gitRepoName string,
-	req *types.CreateDeploymentRequest,
-) (*types.Deployment, error) {
-	resp := &types.Deployment{}
-
-	err := c.postRequest(
-		fmt.Sprintf(
-			"/projects/%d/gitrepos/%d/%s/%s/clusters/%d/deployment",
-			projID, gitInstallationID, gitRepoOwner, gitRepoName, clusterID,
-		),
-		req,
-		resp,
-	)
-
-	return resp, err
-}
-
 func (c *Client) GetDeployment(
 func (c *Client) GetDeployment(
 	ctx context.Context,
 	ctx context.Context,
 	projID, clusterID, envID uint,
 	projID, clusterID, envID uint,

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

@@ -142,7 +142,7 @@ func createDeployment(
 	// Create Deployment Status to indicate it's in progress
 	// Create Deployment Status to indicate it's in progress
 
 
 	state := "in_progress"
 	state := "in_progress"
-	log_url := fmt.Sprintf("https://github.com/%s/%s/runs/%d", env.GitRepoOwner, env.GitRepoName, actionID)
+	log_url := fmt.Sprintf("https://github.com/%s/%s/actions/runs/%d", env.GitRepoOwner, env.GitRepoName, actionID)
 
 
 	deploymentStatusRequest := github.DeploymentStatusRequest{
 	deploymentStatusRequest := github.DeploymentStatusRequest{
 		State:  &state,
 		State:  &state,

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

@@ -35,7 +35,7 @@ func (c *ListEnvironmentHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
 		return
 		return
 	}
 	}
 
 
-	res := make([]*types.Environment, 0)
+	var res types.ListEnvironmentsResponse
 
 
 	for _, env := range envs {
 	for _, env := range envs {
 		environment := env.ToEnvironmentType()
 		environment := env.ToEnvironmentType()

+ 113 - 4
api/server/handlers/webhook/github_incoming.go

@@ -6,6 +6,8 @@ import (
 	"net/http"
 	"net/http"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
+	"sync"
+	"time"
 
 
 	"github.com/bradleyfalzon/ghinstallation/v2"
 	"github.com/bradleyfalzon/ghinstallation/v2"
 	"github.com/google/go-github/v41/github"
 	"github.com/google/go-github/v41/github"
@@ -91,7 +93,50 @@ func (c *GithubIncomingWebhookHandler) processPullRequestEvent(event *github.Pul
 	}
 	}
 
 
 	if env.Mode == "auto" && event.GetAction() == "opened" {
 	if env.Mode == "auto" && event.GetAction() == "opened" {
-		_, err := client.Actions.CreateWorkflowDispatchEventByFileName(
+		depl := &models.Deployment{
+			EnvironmentID: env.ID,
+			Namespace: fmt.Sprintf("pr-%d-%s", event.GetPullRequest().GetNumber(),
+				strings.ToLower(strings.ReplaceAll(repo, "_", "-"))),
+			Status:        types.DeploymentStatusCreating,
+			PullRequestID: uint(event.GetPullRequest().GetNumber()),
+			PRName:        event.GetPullRequest().GetTitle(),
+			RepoName:      repo,
+			RepoOwner:     owner,
+			CommitSHA:     event.GetPullRequest().GetHead().GetSHA()[:7],
+			PRBranchFrom:  event.GetPullRequest().GetHead().GetRef(),
+			PRBranchInto:  event.GetPullRequest().GetBase().GetRef(),
+		}
+
+		_, err = c.Repo().Environment().CreateDeployment(depl)
+
+		if err != nil {
+			return fmt.Errorf("[webhookID: %s, owner: %s, repo: %s, environmentID: %d, prNumber: %d] "+
+				"error creating new deployment: %w", webhookID, owner, repo, env.ID, event.GetPullRequest().GetNumber(), err)
+		}
+
+		cluster, err := c.Repo().Cluster().ReadCluster(env.ProjectID, env.ClusterID)
+
+		if err != nil {
+			return fmt.Errorf("[projectID: %d, clusterID: %d] error reading cluster when creating new deployment: %w",
+				env.ProjectID, env.ClusterID, err)
+		}
+
+		// create the backing namespace
+		agent, err := c.GetAgent(r, cluster, "")
+
+		if err != nil {
+			return fmt.Errorf("[webhookID: %s, owner: %s, repo: %s, environmentID: %d, prNumber: %d] "+
+				"error getting k8s agent: %w", webhookID, owner, repo, env.ID, event.GetPullRequest().GetNumber(), err)
+		}
+
+		_, err = agent.CreateNamespace(depl.Namespace)
+
+		if err != nil {
+			return fmt.Errorf("[webhookID: %s, owner: %s, repo: %s, environmentID: %d, prNumber: %d] "+
+				"error creating k8s namespace: %w", webhookID, owner, repo, env.ID, event.GetPullRequest().GetNumber(), err)
+		}
+
+		_, err = client.Actions.CreateWorkflowDispatchEventByFileName(
 			r.Context(), owner, repo, fmt.Sprintf("porter_%s_env.yml", env.Name),
 			r.Context(), owner, repo, fmt.Sprintf("porter_%s_env.yml", env.Name),
 			github.CreateWorkflowDispatchEventRequest{
 			github.CreateWorkflowDispatchEventRequest{
 				Ref: event.GetPullRequest().GetHead().GetRef(),
 				Ref: event.GetPullRequest().GetHead().GetRef(),
@@ -142,12 +187,75 @@ func (c *GithubIncomingWebhookHandler) processPullRequestEvent(event *github.Pul
 					event.GetPullRequest().GetNumber(), err)
 					event.GetPullRequest().GetNumber(), err)
 			}
 			}
 		} else {
 		} else {
+			// check for already running workflows we should be cancelling
+			var wg sync.WaitGroup
+			statuses := []string{"in_progress", "queued", "requested", "waiting"}
+
+			wg.Add(len(statuses))
+
+			errChan := make(chan error)
+
+			for _, status := range statuses {
+				go func(status string) {
+					defer wg.Done()
+
+					reqCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+					defer cancel()
+
+					runs, _, err := client.Actions.ListWorkflowRunsByFileName(
+						reqCtx, owner, repo, fmt.Sprintf("porter_%s_env.yml", env.Name),
+						&github.ListWorkflowRunsOptions{
+							Branch: event.GetPullRequest().GetHead().GetRef(),
+							Status: status,
+						},
+					)
+
+					if err == nil && runs.GetTotalCount() > 0 {
+						wg.Add(runs.GetTotalCount())
+
+						for _, run := range runs.WorkflowRuns {
+							go func(id int64, url string) {
+								defer wg.Done()
+
+								reqCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+								defer cancel()
+
+								_, err := client.Actions.CancelWorkflowRunByID(reqCtx, owner, repo, id)
+
+								if err != nil {
+									errChan <- fmt.Errorf("error cancelling %s: %w", url, err)
+								}
+							}(run.GetID(), run.GetHTMLURL())
+						}
+					} else if err != nil {
+						errChan <- fmt.Errorf("error listing workflows for status %s: %w", status, err)
+					}
+				}(status)
+			}
+
+			wg.Wait()
+
+			chanErr := fmt.Errorf("")
+
+			for err := range errChan {
+				chanErr = fmt.Errorf("%s: %w", chanErr.Error(), err)
+			}
+
 			err = c.deleteDeployment(r, depl, env, client)
 			err = c.deleteDeployment(r, depl, env, client)
 
 
 			if err != nil {
 			if err != nil {
+				deleteErr := fmt.Errorf("[webhookID: %s, owner: %s, repo: %s, environmentID: %d, deploymentID: %d, prNumber: %d] "+
+					"error deleting deployment: %w", webhookID, owner, repo, env.ID, depl.ID, event.GetPullRequest().GetNumber(), err)
+
+				if chanErr.Error() != "" {
+					deleteErr = fmt.Errorf("%s. errors found while trying to cancel active workflow runs %w", deleteErr.Error(), chanErr)
+				}
+
+				return deleteErr
+			} else if chanErr.Error() != "" {
 				return fmt.Errorf("[webhookID: %s, owner: %s, repo: %s, environmentID: %d, deploymentID: %d, prNumber: %d] "+
 				return fmt.Errorf("[webhookID: %s, owner: %s, repo: %s, environmentID: %d, deploymentID: %d, prNumber: %d] "+
-					"error deleting deployment: %w", webhookID, owner, repo, env.ID, depl.ID,
-					event.GetPullRequest().GetNumber(), err)
+					"deployment deleted but errors found while trying to cancel active workflow runs %w", webhookID, owner, repo, env.ID, depl.ID,
+					event.GetPullRequest().GetNumber(), chanErr)
 			}
 			}
 		}
 		}
 	}
 	}
@@ -164,7 +272,8 @@ func (c *GithubIncomingWebhookHandler) deleteDeployment(
 	cluster, err := c.Repo().Cluster().ReadCluster(env.ProjectID, env.ClusterID)
 	cluster, err := c.Repo().Cluster().ReadCluster(env.ProjectID, env.ClusterID)
 
 
 	if err != nil {
 	if err != nil {
-		return fmt.Errorf("[projectID: %d, clusterID: %d] error reading cluster: %w", env.ProjectID, env.ClusterID, err)
+		return fmt.Errorf("[projectID: %d, clusterID: %d] error reading cluster when deleting existing deployment: %w",
+			env.ProjectID, env.ClusterID, err)
 	}
 	}
 
 
 	agent, err := c.GetAgent(r, cluster, "")
 	agent, err := c.GetAgent(r, cluster, "")

+ 1 - 1
api/server/router/git_installation.go

@@ -188,7 +188,7 @@ func getGitInstallationRoutes(
 		})
 		})
 
 
 		// GET /api/projects/{project_id}/gitrepos/{git_installation_id}/{owner}/{name}/clusters/{cluster_id}/deployment ->
 		// GET /api/projects/{project_id}/gitrepos/{git_installation_id}/{owner}/{name}/clusters/{cluster_id}/deployment ->
-		// environment.NewCreateDeploymentHandler
+		// environment.NewGetDeploymentHandler
 		getDeploymentEndpoint := factory.NewAPIEndpoint(
 		getDeploymentEndpoint := factory.NewAPIEndpoint(
 			&types.APIRequestMetadata{
 			&types.APIRequestMetadata{
 				Verb:   types.APIVerbGet,
 				Verb:   types.APIVerbGet,

+ 2 - 0
api/types/environment.go

@@ -110,3 +110,5 @@ type PullRequest struct {
 	BranchFrom string `json:"branch_from"`
 	BranchFrom string `json:"branch_from"`
 	BranchInto string `json:"branch_into"`
 	BranchInto string `json:"branch_into"`
 }
 }
+
+type ListEnvironmentsResponse []*Environment

+ 4 - 25
cli/cmd/apply.go

@@ -721,7 +721,9 @@ func (t *DeploymentHook) PreApply() error {
 		return err
 		return err
 	}
 	}
 
 
-	for _, env := range envList {
+	envs := *envList
+
+	for _, env := range envs {
 		if env.GitRepoOwner == t.repoOwner && env.GitRepoName == t.repoName && env.GitInstallationID == t.gitInstallationID {
 		if env.GitRepoOwner == t.repoOwner && env.GitRepoName == t.repoName && env.GitInstallationID == t.gitInstallationID {
 			t.envID = env.ID
 			t.envID = env.ID
 			break
 			break
@@ -741,30 +743,7 @@ func (t *DeploymentHook) PreApply() error {
 		},
 		},
 	)
 	)
 
 
-	// TODO: case this on the response status code rather than text
-	if err != nil && strings.Contains(err.Error(), "not found") {
-		// in this case, create the deployment
-		_, 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,
-				},
-			},
-		)
-	} else if err == nil {
+	if err == nil {
 		_, err = t.client.UpdateDeployment(
 		_, err = t.client.UpdateDeployment(
 			context.Background(),
 			context.Background(),
 			t.projectID, t.gitInstallationID, t.clusterID,
 			t.projectID, t.gitInstallationID, t.clusterID,

+ 8 - 8
internal/repository/gorm/environment.go

@@ -29,9 +29,9 @@ func (repo *EnvironmentRepository) CreateEnvironment(env *models.Environment) (*
 func (repo *EnvironmentRepository) ReadEnvironment(projectID, clusterID, gitInstallationID uint, gitRepoOwner, gitRepoName string) (*models.Environment, error) {
 func (repo *EnvironmentRepository) ReadEnvironment(projectID, clusterID, gitInstallationID uint, gitRepoOwner, gitRepoName string) (*models.Environment, error) {
 	env := &models.Environment{}
 	env := &models.Environment{}
 	if err := repo.db.Order("id desc").Where(
 	if err := repo.db.Order("id desc").Where(
-		"project_id = ? AND cluster_id = ? AND git_installation_id = ? AND git_repo_owner = ? AND git_repo_name = ?",
+		"project_id = ? AND cluster_id = ? AND git_installation_id = ? AND git_repo_owner = LOWER(?) AND git_repo_name = LOWER(?)",
 		projectID, clusterID, gitInstallationID,
 		projectID, clusterID, gitInstallationID,
-		gitRepoOwner, gitRepoName,
+		strings.ToLower(gitRepoOwner), strings.ToLower(gitRepoName),
 	).First(&env).Error; err != nil {
 	).First(&env).Error; err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -56,8 +56,8 @@ func (repo *EnvironmentRepository) ReadEnvironmentByOwnerRepoName(
 	gitRepoOwner, gitRepoName string,
 	gitRepoOwner, gitRepoName string,
 ) (*models.Environment, error) {
 ) (*models.Environment, error) {
 	env := &models.Environment{}
 	env := &models.Environment{}
-	if err := repo.db.Order("id desc").Where("project_id = ? AND cluster_id = ? AND git_repo_owner = ? AND git_repo_name = ?",
-		projectID, clusterID, gitRepoOwner, gitRepoName,
+	if err := repo.db.Order("id desc").Where("project_id = ? AND cluster_id = ? AND git_repo_owner = LOWER(?) AND git_repo_name = LOWER(?)",
+		projectID, clusterID, strings.ToLower(gitRepoOwner), strings.ToLower(gitRepoName),
 	).First(&env).Error; err != nil {
 	).First(&env).Error; err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -68,8 +68,8 @@ func (repo *EnvironmentRepository) ReadEnvironmentByWebhookIDOwnerRepoName(
 	webhookID, gitRepoOwner, gitRepoName string,
 	webhookID, gitRepoOwner, gitRepoName string,
 ) (*models.Environment, error) {
 ) (*models.Environment, error) {
 	env := &models.Environment{}
 	env := &models.Environment{}
-	if err := repo.db.Order("id desc").Where("webhook_id = ? AND git_repo_owner = ? AND git_repo_name = ?",
-		webhookID, gitRepoOwner, gitRepoName,
+	if err := repo.db.Order("id desc").Where("webhook_id = ? AND git_repo_owner = LOWER(?) AND git_repo_name = LOWER(?)",
+		webhookID, strings.ToLower(gitRepoOwner), strings.ToLower(gitRepoName),
 	).First(&env).Error; err != nil {
 	).First(&env).Error; err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -149,8 +149,8 @@ func (repo *EnvironmentRepository) ReadDeploymentByGitDetails(
 	depl := &models.Deployment{}
 	depl := &models.Deployment{}
 
 
 	if err := repo.db.Order("id asc").
 	if err := repo.db.Order("id asc").
-		Where("environment_id = ? AND repo_owner = ? AND repo_name = ? AND pull_request_id = ?",
-			environmentID, gitRepoOwner, gitRepoName, prNumber).
+		Where("environment_id = ? AND repo_owner = LOWER(?) AND repo_name = LOWER(?) AND pull_request_id = ?",
+			environmentID, strings.ToLower(gitRepoOwner), strings.ToLower(gitRepoName), prNumber).
 		First(&depl).Error; err != nil {
 		First(&depl).Error; err != nil {
 		return nil, err
 		return nil, err
 	}
 	}