Browse Source

create deployment in auto mode when a new PR is opened from the github incoming webhook

Mohammed Nafees 3 years ago
parent
commit
1f14a1bf1e

+ 0 - 20
api/client/environment.go

@@ -7,26 +7,6 @@ import (
 	"github.com/porter-dev/porter/api/types"
 )
 
-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(
 	ctx context.Context,
 	projID, gitInstallationID, clusterID uint,

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

@@ -1,165 +0,0 @@
-package environment
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-
-	"github.com/google/go-github/v41/github"
-	"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"
-	"github.com/porter-dev/porter/api/server/shared/commonutils"
-	"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"
-)
-
-type CreateDeploymentHandler struct {
-	handlers.PorterHandlerReadWriter
-	authz.KubernetesAgentGetter
-}
-
-func NewCreateDeploymentHandler(
-	config *config.Config,
-	decoderValidator shared.RequestDecoderValidator,
-	writer shared.ResultWriter,
-) *CreateDeploymentHandler {
-	return &CreateDeploymentHandler{
-		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
-		KubernetesAgentGetter:   authz.NewOutOfClusterAgentGetter(config),
-	}
-}
-
-func (c *CreateDeploymentHandler) 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)
-
-	owner, name, ok := commonutils.GetOwnerAndNameParams(c, w, r)
-
-	if !ok {
-		return
-	}
-
-	request := &types.CreateDeploymentRequest{}
-
-	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), owner, name)
-
-	if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-		return
-	}
-
-	// create deployment on GitHub API
-	client, err := getGithubClientFromEnvironment(c.Config(), env)
-
-	if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-		return
-	}
-
-	ghDeployment, err := createDeployment(client, env, request.PRBranchFrom, request.ActionID)
-
-	if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-		return
-	}
-
-	// create the deployment
-	depl, err := c.Repo().Environment().CreateDeployment(&models.Deployment{
-		EnvironmentID:  env.ID,
-		Namespace:      request.Namespace,
-		Status:         types.DeploymentStatusCreating,
-		PullRequestID:  request.PullRequestID,
-		GHDeploymentID: ghDeployment.GetID(),
-		RepoOwner:      request.GitHubMetadata.RepoOwner,
-		RepoName:       request.GitHubMetadata.RepoName,
-		PRName:         request.GitHubMetadata.PRName,
-		CommitSHA:      request.GitHubMetadata.CommitSHA,
-		PRBranchFrom:   request.GitHubMetadata.PRBranchFrom,
-		PRBranchInto:   request.GitHubMetadata.PRBranchInto,
-	})
-
-	if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-		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())
-}
-
-func createDeployment(
-	client *github.Client,
-	env *models.Environment,
-	branchFrom string,
-	actionID uint,
-) (*github.Deployment, error) {
-	requiredContexts := []string{}
-
-	deploymentRequest := github.DeploymentRequest{
-		Ref:              github.String(branchFrom),
-		Environment:      github.String(env.Name),
-		AutoMerge:        github.Bool(false),
-		RequiredContexts: &requiredContexts,
-	}
-
-	deployment, _, err := client.Repositories.CreateDeployment(
-		context.Background(),
-		env.GitRepoOwner,
-		env.GitRepoName,
-		&deploymentRequest,
-	)
-
-	if err != nil {
-		return nil, err
-	}
-
-	depID := deployment.GetID()
-
-	// Create Deployment Status to indicate it's in progress
-
-	state := "in_progress"
-	log_url := fmt.Sprintf("https://github.com/%s/%s/runs/%d", env.GitRepoOwner, env.GitRepoName, actionID)
-
-	deploymentStatusRequest := github.DeploymentStatusRequest{
-		State:  &state,
-		LogURL: &log_url, // link to actions tab
-	}
-
-	_, _, err = client.Repositories.CreateDeploymentStatus(
-		context.Background(),
-		env.GitRepoOwner,
-		env.GitRepoName,
-		depID,
-		&deploymentStatusRequest,
-	)
-
-	if err != nil {
-		return nil, err
-	}
-
-	return deployment, nil
-}

+ 56 - 0
api/server/handlers/environment/update_deployment.go

@@ -1,8 +1,11 @@
 package environment
 
 import (
+	"context"
+	"fmt"
 	"net/http"
 
+	"github.com/google/go-github/v41/github"
 	"github.com/porter-dev/porter/api/server/authz"
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/shared"
@@ -96,3 +99,56 @@ func (c *UpdateDeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
 
 	c.WriteResult(w, r, depl.ToDeploymentType())
 }
+
+func createDeployment(
+	client *github.Client,
+	env *models.Environment,
+	branchFrom string,
+	actionID uint,
+) (*github.Deployment, error) {
+	requiredContexts := []string{}
+
+	deploymentRequest := github.DeploymentRequest{
+		Ref:              github.String(branchFrom),
+		Environment:      github.String(env.Name),
+		AutoMerge:        github.Bool(false),
+		RequiredContexts: &requiredContexts,
+	}
+
+	deployment, _, err := client.Repositories.CreateDeployment(
+		context.Background(),
+		env.GitRepoOwner,
+		env.GitRepoName,
+		&deploymentRequest,
+	)
+
+	if err != nil {
+		return nil, err
+	}
+
+	depID := deployment.GetID()
+
+	// Create Deployment Status to indicate it's in progress
+
+	state := "in_progress"
+	log_url := fmt.Sprintf("https://github.com/%s/%s/runs/%d", env.GitRepoOwner, env.GitRepoName, actionID)
+
+	deploymentStatusRequest := github.DeploymentStatusRequest{
+		State:  &state,
+		LogURL: &log_url, // link to actions tab
+	}
+
+	_, _, err = client.Repositories.CreateDeploymentStatus(
+		context.Background(),
+		env.GitRepoOwner,
+		env.GitRepoName,
+		depID,
+		&deploymentStatusRequest,
+	)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return deployment, nil
+}

+ 46 - 2
api/server/handlers/webhook/github_incoming.go

@@ -91,7 +91,50 @@ func (c *GithubIncomingWebhookHandler) processPullRequestEvent(event *github.Pul
 	}
 
 	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),
 			github.CreateWorkflowDispatchEventRequest{
 				Ref: event.GetPullRequest().GetHead().GetRef(),
@@ -164,7 +207,8 @@ func (c *GithubIncomingWebhookHandler) deleteDeployment(
 	cluster, err := c.Repo().Cluster().ReadCluster(env.ProjectID, env.ClusterID)
 
 	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, "")

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

@@ -151,44 +151,8 @@ func getGitInstallationRoutes(
 			Router:   r,
 		})
 
-		// POST /api/projects/{project_id}/gitrepos/{git_installation_id}/{owner}/{name}/clusters/{cluster_id}/deployment ->
-		// environment.NewCreateDeploymentHandler
-		createDeploymentEndpoint := factory.NewAPIEndpoint(
-			&types.APIRequestMetadata{
-				Verb:   types.APIVerbCreate,
-				Method: types.HTTPVerbPost,
-				Path: &types.Path{
-					Parent: basePath,
-					RelativePath: fmt.Sprintf(
-						"%s/{%s}/{%s}/clusters/{cluster_id}/deployment",
-						relPath,
-						types.URLParamGitRepoOwner,
-						types.URLParamGitRepoName,
-					),
-				},
-				Scopes: []types.PermissionScope{
-					types.UserScope,
-					types.ProjectScope,
-					types.GitInstallationScope,
-					types.ClusterScope,
-				},
-			},
-		)
-
-		createDeploymentHandler := environment.NewCreateDeploymentHandler(
-			config,
-			factory.GetDecoderValidator(),
-			factory.GetResultWriter(),
-		)
-
-		routes = append(routes, &router.Route{
-			Endpoint: createDeploymentEndpoint,
-			Handler:  createDeploymentHandler,
-			Router:   r,
-		})
-
 		// GET /api/projects/{project_id}/gitrepos/{git_installation_id}/{owner}/{name}/clusters/{cluster_id}/deployment ->
-		// environment.NewCreateDeploymentHandler
+		// environment.NewGetDeploymentHandler
 		getDeploymentEndpoint := factory.NewAPIEndpoint(
 			&types.APIRequestMetadata{
 				Verb:   types.APIVerbGet,

+ 1 - 24
cli/cmd/apply.go

@@ -723,30 +723,7 @@ func (t *DeploymentHook) PreApply() error {
 		},
 	)
 
-	// TODO: case this on the response status code rather than text
-	if err != nil && strings.Contains(err.Error(), "deployment 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(
 			context.Background(),
 			t.projectID, t.gitInstallationID, t.clusterID,