Skip to content

Commit

Permalink
POR-1435 create app templates and commit preview env workflow (#3715)
Browse files Browse the repository at this point in the history
  • Loading branch information
ianedwards authored Oct 3, 2023
1 parent 4d5b847 commit a916d38
Show file tree
Hide file tree
Showing 13 changed files with 508 additions and 111 deletions.
67 changes: 66 additions & 1 deletion api/server/handlers/porter_app/create_app_template.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
package porter_app

import (
"context"
"encoding/base64"
"net/http"
"time"

"github.com/google/uuid"
"github.com/porter-dev/api-contracts/generated/go/helpers"
porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
"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/config"
"github.com/porter-dev/porter/api/server/shared/requestutils"
"github.com/porter-dev/porter/api/types"
"github.com/porter-dev/porter/internal/kubernetes"
"github.com/porter-dev/porter/internal/kubernetes/environment_groups"
"github.com/porter-dev/porter/internal/models"
"github.com/porter-dev/porter/internal/porter_app"
Expand Down Expand Up @@ -135,11 +140,19 @@ func (c *CreateAppTemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
appTemplate = &models.AppTemplate{
ProjectID: int(project.ID),
PorterAppID: int(porterApps[0].ID),
Base64App: request.B64AppProto,
}
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "update-app-template", Value: false})
}

protoWithoutDefaultAppEnvGroups, err := filterDefaultAppEnvGroups(ctx, request.B64AppProto, agent)
if err != nil {
err := telemetry.Error(ctx, span, err, "error filtering default app env groups")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}

appTemplate.Base64App = protoWithoutDefaultAppEnvGroups

updatedAppTemplate, err := c.Repo().AppTemplate().CreateAppTemplate(appTemplate)
if err != nil {
err := telemetry.Error(ctx, span, err, "error creating app template")
Expand Down Expand Up @@ -200,3 +213,55 @@ func (c *CreateAppTemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ

c.WriteResult(w, r, res)
}

// filterDefaultAppEnvGroups filters out any default app env groups found when creating an app template
// app templates are based on the latest version of a given app, so it is possible for this env group to be included
// however, the app template will get its own default env group when used to deploy to a preview environment
func filterDefaultAppEnvGroups(ctx context.Context, b64AppProto string, agent *kubernetes.Agent) (string, error) {
ctx, span := telemetry.NewSpan(ctx, "filter-default-app-env-groups")
defer span.End()

var finalAppProto string

if b64AppProto == "" {
return finalAppProto, telemetry.Error(ctx, span, nil, "b64 app proto is empty")
}
if agent == nil {
return finalAppProto, telemetry.Error(ctx, span, nil, "agent is nil")
}

decoded, err := base64.StdEncoding.DecodeString(b64AppProto)
if err != nil {
return finalAppProto, telemetry.Error(ctx, span, err, "error decoding base app")
}

appProto := &porterv1.PorterApp{}
err = helpers.UnmarshalContractObject(decoded, appProto)
if err != nil {
return finalAppProto, telemetry.Error(ctx, span, err, "error unmarshalling app proto")
}

filteredEnvGroups := []*porterv1.EnvGroup{}
for _, envGroup := range appProto.EnvGroups {
baseEnvGroup, err := environment_groups.LatestBaseEnvironmentGroup(ctx, agent, envGroup.Name)
if err != nil {
return finalAppProto, telemetry.Error(ctx, span, err, "unable to get latest base environment group")
}
if baseEnvGroup.DefaultAppEnvironment {
continue
}

filteredEnvGroups = append(filteredEnvGroups, envGroup)
}

appProto.EnvGroups = filteredEnvGroups

encoded, err := helpers.MarshalContractObject(ctx, appProto)
if err != nil {
return finalAppProto, telemetry.Error(ctx, span, err, "error marshalling app proto")
}

finalAppProto = base64.StdEncoding.EncodeToString(encoded)

return finalAppProto, nil
}
57 changes: 42 additions & 15 deletions api/server/handlers/porter_app/create_secret_and_open_pr.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ func (c *OpenStackPRHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}

if request.Branch == "" {
err := telemetry.Error(ctx, span, nil, "branch cannot be empty")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}
if request.PreviewsWorkflowFilename != "" && request.DeleteWorkflowFilename != "" {
err := telemetry.Error(ctx, span, nil, "both preview and delete workflow filenames cannot be set")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}

client, err := getGithubClient(c.Config(), request.GithubAppInstallationID)
if err != nil {
err := telemetry.Error(ctx, span, err, "error creating github client")
Expand Down Expand Up @@ -100,24 +111,40 @@ func (c *OpenStackPRHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var prRequestBody string
if request.DeleteWorkflowFilename == "" {
prRequestBody = "Hello 👋 from Porter! Please merge this PR to finish setting up your application."
} else {
} else if request.PreviewsWorkflowFilename == "" {
prRequestBody = "Please merge this PR to delete the workflow file associated with your application."
} else {
prRequestBody = "Hello 👋 from Porter! Please merge this PR to enable preview environments for your application."
}

if request.OpenPr || request.DeleteWorkflowFilename != "" {
pr, err = actions.OpenGithubPR(&actions.GithubPROpts{
Client: client,
GitRepoOwner: request.GithubRepoOwner,
GitRepoName: request.GithubRepoName,
StackName: appName,
ProjectID: project.ID,
ClusterID: cluster.ID,
ServerURL: c.Config().ServerConf.ServerURL,
DefaultBranch: request.Branch,
SecretName: secretName,
PorterYamlPath: request.PorterYamlPath,
Body: prRequestBody,
DeleteWorkflowFilename: request.DeleteWorkflowFilename,
})
openPRInput := &actions.GithubPROpts{
PRAction: actions.GithubPRAction_NewAppWorkflow,
Client: client,
GitRepoOwner: request.GithubRepoOwner,
GitRepoName: request.GithubRepoName,
StackName: appName,
ProjectID: project.ID,
ClusterID: cluster.ID,
ServerURL: c.Config().ServerConf.ServerURL,
DefaultBranch: request.Branch,
SecretName: secretName,
PorterYamlPath: request.PorterYamlPath,
Body: prRequestBody,
PRBranch: "porter-stack",
}
if request.DeleteWorkflowFilename != "" {
openPRInput.PRAction = actions.GithubPRAction_DeleteAppWorkflow
openPRInput.WorkflowFileName = request.DeleteWorkflowFilename
openPRInput.PRBranch = "porter-stack-delete"
}
if request.PreviewsWorkflowFilename != "" {
openPRInput.PRAction = actions.GithubPRAction_PreviewAppWorkflow
openPRInput.WorkflowFileName = request.PreviewsWorkflowFilename
openPRInput.PRBranch = "porter-stack-preview"
}

pr, err = actions.OpenGithubPR(openPRInput)
}

if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion api/server/handlers/porter_app/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func (c *ValidatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
} else {
decoded, err := base64.StdEncoding.DecodeString(request.Base64AppProto)
if err != nil {
err := telemetry.Error(ctx, span, err, "error decoding base yaml")
err := telemetry.Error(ctx, span, err, "error decoding base yaml")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}
Expand Down
15 changes: 8 additions & 7 deletions api/types/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ type ImageInfo struct {
}

type CreateSecretAndOpenGHPRRequest struct {
GithubAppInstallationID int64 `json:"github_app_installation_id" form:"required"`
GithubRepoOwner string `json:"github_repo_owner" form:"required"`
GithubRepoName string `json:"github_repo_name" form:"required"`
OpenPr bool `json:"open_pr"`
Branch string `json:"branch"`
PorterYamlPath string `json:"porter_yaml_path"`
DeleteWorkflowFilename string `json:"delete_workflow_filename"`
GithubAppInstallationID int64 `json:"github_app_installation_id" form:"required"`
GithubRepoOwner string `json:"github_repo_owner" form:"required"`
GithubRepoName string `json:"github_repo_name" form:"required"`
OpenPr bool `json:"open_pr"`
Branch string `json:"branch"`
PorterYamlPath string `json:"porter_yaml_path"`
DeleteWorkflowFilename string `json:"delete_workflow_filename"`
PreviewsWorkflowFilename string `json:"previews_workflow_filename"`
}

type CreateSecretAndOpenGHPRResponse struct {
Expand Down
Loading

0 comments on commit a916d38

Please sign in to comment.