Skip to content

Commit

Permalink
create default app template env (#3710)
Browse files Browse the repository at this point in the history
  • Loading branch information
ianedwards authored Oct 2, 2023
1 parent 3dfc881 commit dc18991
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 17 deletions.
54 changes: 53 additions & 1 deletion api/server/handlers/porter_app/create_app_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@ package porter_app

import (
"net/http"
"time"

"github.com/google/uuid"
"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/environment_groups"
"github.com/porter-dev/porter/internal/models"
"github.com/porter-dev/porter/internal/porter_app"
"github.com/porter-dev/porter/internal/telemetry"
)

// CreateAppTemplateHandler is the handler for the /app-template endpoint
type CreateAppTemplateHandler struct {
handlers.PorterHandlerReadWriter
authz.KubernetesAgentGetter
}

// NewCreateAppTemplateHandler handles POST requests to the endpoint /app-template
Expand All @@ -27,12 +32,15 @@ func NewCreateAppTemplateHandler(
) *CreateAppTemplateHandler {
return &CreateAppTemplateHandler{
PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
}
}

// CreateAppTemplateRequest is the request object for the /app-template POST endpoint
type CreateAppTemplateRequest struct {
B64AppProto string `json:"b64_app_proto"`
B64AppProto string `json:"b64_app_proto"`
Variables map[string]string `json:"variables"`
Secrets map[string]string `json:"secrets"`
}

// CreateAppTemplateResponse is the response object for the /app-template POST endpoint
Expand All @@ -46,6 +54,7 @@ func (c *CreateAppTemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
defer span.End()

project, _ := ctx.Value(types.ProjectScope).(*models.Project)
cluster, _ := ctx.Value(types.ClusterScope).(*models.Cluster)

if !project.GetFeatureFlag(models.ValidateApplyV2, c.Config().LaunchDarklyClient) {
err := telemetry.Error(ctx, span, nil, "project does not have validate apply v2 enabled")
Expand Down Expand Up @@ -74,6 +83,13 @@ func (c *CreateAppTemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
return
}

agent, err := c.GetAgent(r, cluster, "")
if err != nil {
err := telemetry.Error(ctx, span, err, "error getting agent")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}

porterApps, err := c.Repo().PorterApp().ReadPorterAppsByProjectIDAndName(project.ID, appName)
if err != nil {
err := telemetry.Error(ctx, span, err, "error getting porter app from repo")
Expand Down Expand Up @@ -142,6 +158,42 @@ func (c *CreateAppTemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
return
}

previewTemplateEnvName, err := porter_app.AppTemplateEnvGroupName(ctx, appName, cluster.ID, c.Repo().PorterApp())
if err != nil {
err := telemetry.Error(ctx, span, err, "unable to get app template env group name")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}

envGroup, err := environment_groups.LatestBaseEnvironmentGroup(ctx, agent, previewTemplateEnvName)
if err != nil {
err := telemetry.Error(ctx, span, err, "unable to get latest base environment group")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}

if envGroup.Name == "" {
envGroup = environment_groups.EnvironmentGroup{
Name: previewTemplateEnvName,
CreatedAtUTC: time.Now().UTC(),
}
}
envGroup.Variables = request.Variables
envGroup.SecretVariables = request.Secrets

additionalEnvGroupLabels := map[string]string{
LabelKey_AppName: appName,
environment_groups.LabelKey_DefaultAppEnvironment: "true",
LabelKey_PorterManaged: "true",
}

err = environment_groups.CreateOrUpdateBaseEnvironmentGroup(ctx, agent, envGroup, additionalEnvGroupLabels)
if err != nil {
err := telemetry.Error(ctx, span, err, "unable to create or update base environment group")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}

res := &CreateAppTemplateResponse{
AppTemplateID: updatedAppTemplate.ID.String(),
}
Expand Down
45 changes: 32 additions & 13 deletions api/server/handlers/porter_app/update_app_environment_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,10 @@ func (c *UpdateAppEnvironmentHandler) ServeHTTP(w http.ResponseWriter, r *http.R
}

namespace := deploymentTargetDetailsResp.Msg.Namespace
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "namespace", Value: namespace})
isPreview := deploymentTargetDetailsResp.Msg.IsPreview

telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "is-preview", Value: isPreview})
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "namespace", Value: namespace})
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "hard-update", Value: request.HardUpdate})

appEnvGroupName, err := porter_app.AppEnvGroupName(ctx, appName, request.DeploymentTargetID, cluster.ID, c.Repo().PorterApp())
Expand All @@ -187,6 +189,21 @@ func (c *UpdateAppEnvironmentHandler) ServeHTTP(w http.ResponseWriter, r *http.R

telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "env-group-exists", Value: latestEnvironmentGroup.Name != ""})

previewTemplateEnvName, err := porter_app.AppTemplateEnvGroupName(ctx, appName, cluster.ID, c.Repo().PorterApp())
if err != nil {
err := telemetry.Error(ctx, span, err, "error getting preview template env name")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}

// filter out preview template and app env groups
filteredEnvGroups := []*porterv1.EnvGroup{}
for _, envGroup := range appProto.EnvGroups {
if envGroup.GetName() != previewTemplateEnvName && envGroup.GetName() != appEnvGroupName {
filteredEnvGroups = append(filteredEnvGroups, envGroup)
}
}

if latestEnvironmentGroup.Name != "" {
sameEnvGroup := true
for key, newValue := range request.Variables {
Expand Down Expand Up @@ -217,9 +234,8 @@ func (c *UpdateAppEnvironmentHandler) ServeHTTP(w http.ResponseWriter, r *http.R
if sameEnvGroup {
// even if the env group is the same, we still need to sync the latest versions of the other env groups
syncInp := syncLatestEnvGroupVersionsInput{
envGroups: appProto.EnvGroups,
envGroups: filteredEnvGroups,
appName: appName,
appEnvName: appEnvGroupName,
namespace: namespace,
deploymentTargetID: request.DeploymentTargetID,
k8sAgent: agent,
Expand All @@ -245,6 +261,18 @@ func (c *UpdateAppEnvironmentHandler) ServeHTTP(w http.ResponseWriter, r *http.R
}
}

// if this app does not have a default env group for this deployment target and is a preview
// then use the preview template env group as the default
// this should only run when the app is first deployed to a given deployment target
if latestEnvironmentGroup.Name == "" && isPreview {
latestEnvironmentGroup, err = environment_groups.LatestBaseEnvironmentGroup(ctx, agent, previewTemplateEnvName)
if err != nil {
err := telemetry.Error(ctx, span, err, "unable to get latest base environment group")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}
}

variables := make(map[string]string)
secrets := make(map[string]string)

Expand Down Expand Up @@ -299,9 +327,8 @@ func (c *UpdateAppEnvironmentHandler) ServeHTTP(w http.ResponseWriter, r *http.R
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "env-group-versioned-name", Value: syncedAppEnvironment.EnvironmentGroupVersionedName})

syncInp := syncLatestEnvGroupVersionsInput{
envGroups: appProto.EnvGroups,
envGroups: filteredEnvGroups,
appName: appName,
appEnvName: appEnvGroupName,
namespace: namespace,
deploymentTargetID: request.DeploymentTargetID,
k8sAgent: agent,
Expand Down Expand Up @@ -344,8 +371,6 @@ type syncLatestEnvGroupVersionsInput struct {
envGroups []*porterv1.EnvGroup
// appName is the name of the app
appName string
// appEnvName is the name of the app env. This is the env group created when the app is created for storing app-specific variables
appEnvName string
// namespace is the namespace to sync the latest versions to
namespace string
// deploymentTargetID is the id of the deployment target
Expand All @@ -367,9 +392,6 @@ func syncLatestEnvGroupVersions(ctx context.Context, inp syncLatestEnvGroupVersi
if inp.appName == "" {
return envGroups, telemetry.Error(ctx, span, nil, "app name is empty")
}
if inp.appEnvName == "" {
return envGroups, telemetry.Error(ctx, span, nil, "app env name is empty")
}
if inp.namespace == "" {
return envGroups, telemetry.Error(ctx, span, nil, "namespace is empty")
}
Expand All @@ -381,9 +403,6 @@ func syncLatestEnvGroupVersions(ctx context.Context, inp syncLatestEnvGroupVersi
if envGroup == nil {
continue
}
if envGroup.GetName() == inp.appEnvName {
continue
}

additionalEnvGroupLabels := map[string]string{
LabelKey_AppName: inp.appName,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ require (
github.com/matryer/is v1.4.0
github.com/nats-io/nats.go v1.24.0
github.com/open-policy-agent/opa v0.44.0
github.com/porter-dev/api-contracts v0.2.6
github.com/porter-dev/api-contracts v0.2.8
github.com/riandyrn/otelchi v0.5.1
github.com/santhosh-tekuri/jsonschema/v5 v5.0.1
github.com/stefanmcshane/helm v0.0.0-20221213002717-88a4a2c6e77d
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1516,8 +1516,8 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw=
github.com/porter-dev/api-contracts v0.2.6 h1:5Z/Qr1Qv6iAM4rCUfpa9+HougO8K2HFjGOeSLDZFfDw=
github.com/porter-dev/api-contracts v0.2.6/go.mod h1:fX6JmP5QuzxDLvqP3evFOTXjI4dHxsG0+VKNTjImZU8=
github.com/porter-dev/api-contracts v0.2.8 h1:z5BZihdZ75J5Dz3jCeX6ziE/wt6h4yhFqaYoO3BqBY8=
github.com/porter-dev/api-contracts v0.2.8/go.mod h1:fX6JmP5QuzxDLvqP3evFOTXjI4dHxsG0+VKNTjImZU8=
github.com/porter-dev/switchboard v0.0.3 h1:dBuYkiVLa5Ce7059d6qTe9a1C2XEORFEanhbtV92R+M=
github.com/porter-dev/switchboard v0.0.3/go.mod h1:xSPzqSFMQ6OSbp42fhCi4AbGbQbsm6nRvOkrblFeXU4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
Expand Down
24 changes: 24 additions & 0 deletions internal/porter_app/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,27 @@ func AppEnvGroupName(ctx context.Context, appName string, deploymentTargetId str

return fmt.Sprintf("%d-%s", porterApp.ID, deploymentTargetId[:6]), nil
}

// AppTemplateEnvGroupName returns the name of the environment group for an app template
func AppTemplateEnvGroupName(ctx context.Context, appName string, clusterID uint, porterAppRepository repository.PorterAppRepository) (string, error) {
ctx, span := telemetry.NewSpan(ctx, "app-template-env-group-name")
defer span.End()

if appName == "" {
return "", telemetry.Error(ctx, span, nil, "app name is empty")
}
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "app-name", Value: appName})

if clusterID == 0 {
return "", telemetry.Error(ctx, span, nil, "cluster id is empty")
}
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "cluster-id", Value: clusterID})

porterApp, err := porterAppRepository.ReadPorterAppByName(clusterID, appName)
if err != nil {
return "", telemetry.Error(ctx, span, err, "error reading porter app by name")
}
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-app-id", Value: porterApp.ID})

return fmt.Sprintf("%d-template-preview", porterApp.ID), nil
}

0 comments on commit dc18991

Please sign in to comment.