|
|
@@ -3,6 +3,7 @@ package porter_app
|
|
|
import (
|
|
|
"context"
|
|
|
"encoding/base64"
|
|
|
+ "encoding/json"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
"net/http"
|
|
|
@@ -18,8 +19,10 @@ import (
|
|
|
"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/features"
|
|
|
"github.com/porter-dev/porter/api/server/shared/requestutils"
|
|
|
"github.com/porter-dev/porter/api/types"
|
|
|
+ utils "github.com/porter-dev/porter/api/utils/porter_app"
|
|
|
"github.com/porter-dev/porter/internal/helm"
|
|
|
"github.com/porter-dev/porter/internal/helm/loader"
|
|
|
"github.com/porter-dev/porter/internal/models"
|
|
|
@@ -47,6 +50,7 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
ctx := r.Context()
|
|
|
project, _ := ctx.Value(types.ProjectScope).(*models.Project)
|
|
|
cluster, _ := ctx.Value(types.ClusterScope).(*models.Cluster)
|
|
|
+ user, _ := ctx.Value(types.UserScope).(*models.User)
|
|
|
|
|
|
ctx, span := telemetry.NewSpan(r.Context(), "serve-create-porter-app")
|
|
|
defer span.End()
|
|
|
@@ -58,14 +62,14 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- stackName, reqErr := requestutils.GetURLParamString(r, types.URLParamStackName)
|
|
|
+ appName, reqErr := requestutils.GetURLParamString(r, types.URLParamPorterAppName)
|
|
|
if reqErr != nil {
|
|
|
err := telemetry.Error(ctx, span, reqErr, "error getting stack name from url")
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
|
|
|
return
|
|
|
}
|
|
|
- namespace := fmt.Sprintf("porter-stack-%s", stackName)
|
|
|
- telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "application-name", Value: stackName})
|
|
|
+ namespace := utils.NamespaceFromPorterAppName(appName)
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "application-name", Value: appName})
|
|
|
|
|
|
helmAgent, err := c.GetHelmAgent(ctx, r, cluster, namespace)
|
|
|
if err != nil {
|
|
|
@@ -81,13 +85,14 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- helmRelease, err := helmAgent.GetRelease(ctx, stackName, 0, false)
|
|
|
+ helmRelease, err := helmAgent.GetRelease(ctx, appName, 0, false)
|
|
|
shouldCreate := err != nil
|
|
|
|
|
|
porterYamlBase64 := request.PorterYAMLBase64
|
|
|
porterYaml, err := base64.StdEncoding.DecodeString(porterYamlBase64)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error decoding porter yaml")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
|
|
|
return
|
|
|
}
|
|
|
@@ -95,6 +100,7 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
registries, err := c.Repo().Registry().ListRegistriesByProjectID(cluster.ProjectID)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error listing registries")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
}
|
|
|
@@ -113,6 +119,7 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
imageInfo, err = attemptToGetImageInfoFromFullHelmValues(request.FullHelmValues)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error getting image info from full helm values")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
|
|
|
return
|
|
|
}
|
|
|
@@ -128,7 +135,7 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
|
|
|
if request.Builder == "" {
|
|
|
// attempt to get builder from db
|
|
|
- app, err := c.Repo().PorterApp().ReadPorterAppByName(cluster.ID, stackName)
|
|
|
+ app, err := c.Repo().PorterApp().ReadPorterAppByName(cluster.ID, appName)
|
|
|
if err == nil {
|
|
|
request.Builder = app.Builder
|
|
|
}
|
|
|
@@ -142,19 +149,36 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
_, err = k8sAgent.CreateNamespace(namespace, nil)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error creating namespace")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
}
|
|
|
cloneEnvGroup(c, w, r, k8sAgent, request.EnvGroups, namespace)
|
|
|
}
|
|
|
+
|
|
|
+ if imageInfo.Repository == "" || imageInfo.Tag == "" {
|
|
|
+ err = telemetry.Error(ctx, span, nil, "incomplete image info provided: must provide both repository and tag")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
+ c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ var addCustomNodeSelector bool
|
|
|
+ if (cluster.ProvisionedBy == "CAPI" && cluster.CloudProvider == "GCP") || cluster.GCPIntegrationID != 0 {
|
|
|
+ addCustomNodeSelector = true
|
|
|
+ }
|
|
|
+
|
|
|
chart, values, preDeployJobValues, err := parse(
|
|
|
+ ctx,
|
|
|
ParseConf{
|
|
|
+ PorterAppName: appName,
|
|
|
PorterYaml: porterYaml,
|
|
|
ImageInfo: imageInfo,
|
|
|
ServerConfig: c.Config(),
|
|
|
ProjectID: cluster.ProjectID,
|
|
|
UserUpdate: request.UserUpdate,
|
|
|
EnvGroups: request.EnvGroups,
|
|
|
+ EnvironmentGroups: request.EnvironmentGroups,
|
|
|
Namespace: namespace,
|
|
|
ExistingHelmValues: releaseValues,
|
|
|
ExistingChartDependencies: releaseDependencies,
|
|
|
@@ -163,15 +187,17 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
dnsRepo: c.Repo().DNSRecord(),
|
|
|
powerDnsClient: c.Config().PowerDNSClient,
|
|
|
appRootDomain: c.Config().ServerConf.AppRootDomain,
|
|
|
- stackName: stackName,
|
|
|
+ stackName: appName,
|
|
|
},
|
|
|
InjectLauncherToStartCommand: injectLauncher,
|
|
|
ShouldValidateHelmValues: shouldCreate,
|
|
|
FullHelmValues: request.FullHelmValues,
|
|
|
+ AddCustomNodeSelector: addCustomNodeSelector,
|
|
|
},
|
|
|
)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "parse error")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
}
|
|
|
@@ -182,9 +208,9 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
// create the release job chart if it does not exist (only done by front-end currently, where we set overrideRelease=true)
|
|
|
if request.OverrideRelease && preDeployJobValues != nil {
|
|
|
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "installing-pre-deploy-job", Value: true})
|
|
|
- conf, err := createReleaseJobChart(
|
|
|
+ conf, err := createPreDeployJobChart(
|
|
|
ctx,
|
|
|
- stackName,
|
|
|
+ appName,
|
|
|
preDeployJobValues,
|
|
|
c.Config().ServerConf.DefaultApplicationHelmRepoURL,
|
|
|
registries,
|
|
|
@@ -193,6 +219,7 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error making config for pre-deploy job chart")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
}
|
|
|
@@ -200,8 +227,9 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error installing pre-deploy job chart")
|
|
|
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "install-pre-deploy-job-error", Value: err})
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
- _, uninstallChartErr := helmAgent.UninstallChart(ctx, fmt.Sprintf("%s-r", stackName))
|
|
|
+ _, uninstallChartErr := helmAgent.UninstallChart(ctx, fmt.Sprintf("%s-r", appName))
|
|
|
if uninstallChartErr != nil {
|
|
|
uninstallChartErr = telemetry.Error(ctx, span, err, "error uninstalling pre-deploy job chart after failed install")
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(uninstallChartErr, http.StatusInternalServerError))
|
|
|
@@ -212,7 +240,7 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
|
|
|
conf := &helm.InstallChartConfig{
|
|
|
Chart: chart,
|
|
|
- Name: stackName,
|
|
|
+ Name: appName,
|
|
|
Namespace: namespace,
|
|
|
Values: values,
|
|
|
Cluster: cluster,
|
|
|
@@ -221,12 +249,12 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
}
|
|
|
|
|
|
// create the app chart
|
|
|
- _, err = helmAgent.InstallChart(ctx, conf, c.Config().DOConf, c.Config().ServerConf.DisablePullSecretsInjection)
|
|
|
+ release, err := helmAgent.InstallChart(ctx, conf, c.Config().DOConf, c.Config().ServerConf.DisablePullSecretsInjection)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error installing app chart")
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
|
|
|
-
|
|
|
- _, err = helmAgent.UninstallChart(ctx, stackName)
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
+ _, err = helmAgent.UninstallChart(ctx, appName)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error uninstalling app chart after failed install")
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
@@ -235,19 +263,21 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- existing, err := c.Repo().PorterApp().ReadPorterAppByName(cluster.ID, stackName)
|
|
|
+ existing, err := c.Repo().PorterApp().ReadPorterAppByName(cluster.ID, appName)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error reading app from DB")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
} else if existing.Name != "" {
|
|
|
err = telemetry.Error(ctx, span, err, "app with name already exists in project")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusForbidden))
|
|
|
return
|
|
|
}
|
|
|
|
|
|
app := &models.PorterApp{
|
|
|
- Name: stackName,
|
|
|
+ Name: appName,
|
|
|
ClusterID: cluster.ID,
|
|
|
ProjectID: project.ID,
|
|
|
RepoName: request.RepoName,
|
|
|
@@ -267,44 +297,52 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
porterApp, err := c.Repo().PorterApp().UpdatePorterApp(app)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error writing app to DB")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- _, err = createPorterAppEvent(ctx, "SUCCESS", porterApp.ID, 1, imageInfo.Tag, c.Repo().PorterAppEvent())
|
|
|
+ if features.AreAgentDeployEventsEnabled(user.Email, k8sAgent) {
|
|
|
+ serviceDeploymentStatusMap := getServiceDeploymentMetadataFromValues(values, types.PorterAppEventStatus_Progressing)
|
|
|
+ _, err = createNewPorterAppDeployEvent(ctx, serviceDeploymentStatusMap, types.PorterAppEventStatus_Progressing, porterApp.ID, 1, imageInfo.Tag, c.Repo().PorterAppEvent())
|
|
|
+ } else {
|
|
|
+ _, err = createOldPorterAppDeployEvent(ctx, types.PorterAppEventStatus_Success, porterApp.ID, 1, imageInfo.Tag, c.Repo().PorterAppEvent())
|
|
|
+ }
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error creating porter app event")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- c.WriteResult(w, r, porterApp.ToPorterAppType())
|
|
|
+ c.WriteResult(w, r, porterApp.ToPorterAppTypeWithRevision(release.Version))
|
|
|
} else {
|
|
|
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "upgrading-application", Value: true})
|
|
|
|
|
|
- // create/update the release job chart
|
|
|
+ // create/update the pre-deploy job chart
|
|
|
if request.OverrideRelease {
|
|
|
if preDeployJobValues == nil {
|
|
|
- releaseJobName := fmt.Sprintf("%s-r", stackName)
|
|
|
- _, err := helmAgent.GetRelease(ctx, releaseJobName, 0, false)
|
|
|
+ preDeployJobName := fmt.Sprintf("%s-r", appName)
|
|
|
+ _, err := helmAgent.GetRelease(ctx, preDeployJobName, 0, false)
|
|
|
if err == nil {
|
|
|
// handle exception where the user has chosen to delete the release job
|
|
|
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "deleting-pre-deploy-job", Value: true})
|
|
|
- _, err = helmAgent.UninstallChart(ctx, releaseJobName)
|
|
|
+ _, err = helmAgent.UninstallChart(ctx, preDeployJobName)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error uninstalling pre-deploy job chart")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
- releaseJobName := fmt.Sprintf("%s-r", stackName)
|
|
|
- helmRelease, err := helmAgent.GetRelease(ctx, releaseJobName, 0, false)
|
|
|
+ preDeployJobName := fmt.Sprintf("%s-r", appName)
|
|
|
+ helmRelease, err := helmAgent.GetRelease(ctx, preDeployJobName, 0, false)
|
|
|
if err != nil {
|
|
|
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "creating-pre-deploy-job", Value: true})
|
|
|
- conf, err := createReleaseJobChart(
|
|
|
+ conf, err := createPreDeployJobChart(
|
|
|
ctx,
|
|
|
- stackName,
|
|
|
+ appName,
|
|
|
preDeployJobValues,
|
|
|
c.Config().ServerConf.DefaultApplicationHelmRepoURL,
|
|
|
registries,
|
|
|
@@ -313,6 +351,7 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error making config for pre-deploy job chart")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
}
|
|
|
@@ -322,11 +361,12 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
err = telemetry.Error(ctx, span, err, "error installing pre-deploy job chart")
|
|
|
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "install-pre-deploy-job-error", Value: err})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
- _, uninstallChartErr := helmAgent.UninstallChart(ctx, fmt.Sprintf("%s-r", stackName))
|
|
|
+ _, uninstallChartErr := helmAgent.UninstallChart(ctx, fmt.Sprintf("%s-r", appName))
|
|
|
if uninstallChartErr != nil {
|
|
|
uninstallChartErr = telemetry.Error(ctx, span, err, "error uninstalling pre-deploy job chart after failed install")
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(uninstallChartErr, http.StatusInternalServerError))
|
|
|
}
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
return
|
|
|
}
|
|
|
} else {
|
|
|
@@ -334,6 +374,7 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
chart, err := loader.LoadChartPublic(ctx, c.Config().Metadata.DefaultAppHelmRepoURL, "job", "")
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error loading latest job chart")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
}
|
|
|
@@ -349,6 +390,7 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
_, err = helmAgent.UpgradeReleaseByValues(ctx, conf, c.Config().DOConf, c.Config().ServerConf.DisablePullSecretsInjection, false)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error upgrading pre-deploy job chart")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
}
|
|
|
@@ -359,30 +401,34 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
// update the app chart
|
|
|
conf := &helm.InstallChartConfig{
|
|
|
Chart: chart,
|
|
|
- Name: stackName,
|
|
|
+ Name: appName,
|
|
|
Namespace: namespace,
|
|
|
Values: values,
|
|
|
Cluster: cluster,
|
|
|
Repo: c.Repo(),
|
|
|
Registries: registries,
|
|
|
}
|
|
|
+
|
|
|
// update the chart
|
|
|
- _, err = helmAgent.UpgradeInstallChart(ctx, conf, c.Config().DOConf, c.Config().ServerConf.DisablePullSecretsInjection)
|
|
|
+ release, err := helmAgent.UpgradeInstallChart(ctx, conf, c.Config().DOConf, c.Config().ServerConf.DisablePullSecretsInjection)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error upgrading application")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// update the DB entry
|
|
|
- app, err := c.Repo().PorterApp().ReadPorterAppByName(cluster.ID, stackName)
|
|
|
+ app, err := c.Repo().PorterApp().ReadPorterAppByName(cluster.ID, appName)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error reading app from DB")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
}
|
|
|
if app == nil {
|
|
|
err = telemetry.Error(ctx, span, nil, "app with name does not exist in project")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusForbidden))
|
|
|
return
|
|
|
}
|
|
|
@@ -440,26 +486,37 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
|
|
updatedPorterApp, err := c.Repo().PorterApp().UpdatePorterApp(app)
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error writing updated app to DB")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- _, err = createPorterAppEvent(ctx, "SUCCESS", updatedPorterApp.ID, helmRelease.Version+1, imageInfo.Tag, c.Repo().PorterAppEvent())
|
|
|
+ if features.AreAgentDeployEventsEnabled(user.Email, k8sAgent) {
|
|
|
+ serviceDeploymentStatusMap := getServiceDeploymentMetadataFromValues(values, types.PorterAppEventStatus_Progressing)
|
|
|
+ _, err = createNewPorterAppDeployEvent(ctx, serviceDeploymentStatusMap, types.PorterAppEventStatus_Progressing, updatedPorterApp.ID, helmRelease.Version+1, imageInfo.Tag, c.Repo().PorterAppEvent())
|
|
|
+ } else {
|
|
|
+ _, err = createOldPorterAppDeployEvent(ctx, types.PorterAppEventStatus_Success, updatedPorterApp.ID, helmRelease.Version+1, imageInfo.Tag, c.Repo().PorterAppEvent())
|
|
|
+ }
|
|
|
if err != nil {
|
|
|
err = telemetry.Error(ctx, span, err, "error creating porter app event")
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-yaml-base64", Value: porterYamlBase64})
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- c.WriteResult(w, r, updatedPorterApp.ToPorterAppType())
|
|
|
+ c.WriteResult(w, r, updatedPorterApp.ToPorterAppTypeWithRevision(release.Version))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// createPorterAppEvent creates an event for use in the activity feed
|
|
|
-func createPorterAppEvent(ctx context.Context, status string, appID uint, revision int, tag string, repo repository.PorterAppEventRepository) (*models.PorterAppEvent, error) {
|
|
|
+// createOldPorterAppDeployEvent creates an event for use in the activity feed
|
|
|
+// TODO: remove this method and all call-sites if this span no longer exists in telemetry for 4 consecutive weeks
|
|
|
+func createOldPorterAppDeployEvent(ctx context.Context, status types.PorterAppEventStatus, appID uint, revision int, tag string, repo repository.PorterAppEventRepository) (*models.PorterAppEvent, error) {
|
|
|
+ ctx, span := telemetry.NewSpan(ctx, "create-old-porter-app-deploy-event")
|
|
|
+ defer span.End()
|
|
|
+
|
|
|
event := models.PorterAppEvent{
|
|
|
ID: uuid.New(),
|
|
|
- Status: status,
|
|
|
+ Status: string(status),
|
|
|
Type: "DEPLOY",
|
|
|
TypeExternalSource: "KUBERNETES",
|
|
|
PorterAppID: appID,
|
|
|
@@ -481,7 +538,117 @@ func createPorterAppEvent(ctx context.Context, status string, appID uint, revisi
|
|
|
return &event, nil
|
|
|
}
|
|
|
|
|
|
-func createReleaseJobChart(
|
|
|
+// createNewPorterAppDeployEvent creates an event for use in the activity feed, supplemented with information about the
|
|
|
+// deployed services in serviceStatusMap as well as the image tag being deployed
|
|
|
+func createNewPorterAppDeployEvent(
|
|
|
+ ctx context.Context,
|
|
|
+ serviceStatusMap map[string]types.ServiceDeploymentMetadata,
|
|
|
+ status types.PorterAppEventStatus,
|
|
|
+ appID uint,
|
|
|
+ revision int,
|
|
|
+ tag string,
|
|
|
+ repo repository.PorterAppEventRepository,
|
|
|
+) (*models.PorterAppEvent, error) {
|
|
|
+ ctx, span := telemetry.NewSpan(ctx, "create-new-porter-app-deploy-event")
|
|
|
+ defer span.End()
|
|
|
+
|
|
|
+ // mark all pending deployments from the deploy event of the previous revision as canceled
|
|
|
+ updatePreviousPorterAppDeployEvent(ctx, appID, revision, repo)
|
|
|
+
|
|
|
+ event := models.PorterAppEvent{
|
|
|
+ ID: uuid.New(),
|
|
|
+ Status: string(status),
|
|
|
+ Type: "DEPLOY",
|
|
|
+ TypeExternalSource: "KUBERNETES",
|
|
|
+ PorterAppID: appID,
|
|
|
+ Metadata: map[string]any{
|
|
|
+ "revision": revision,
|
|
|
+ "image_tag": tag,
|
|
|
+ "service_deployment_metadata": serviceStatusMap,
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "revision", Value: revision}, telemetry.AttributeKV{Key: "image-tag", Value: tag})
|
|
|
+
|
|
|
+ err := repo.CreateEvent(ctx, &event)
|
|
|
+ if err != nil {
|
|
|
+ err = telemetry.Error(ctx, span, err, "error creating porter app event")
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ if event.ID == uuid.Nil {
|
|
|
+ return nil, telemetry.Error(ctx, span, nil, "event id for newly created app event is nil")
|
|
|
+ }
|
|
|
+
|
|
|
+ return &event, nil
|
|
|
+}
|
|
|
+
|
|
|
+// updatePreviousPorterAppDeployEvent updates the previous deploy event to change the event status as well as all service statuses to CANCELED
|
|
|
+// if it is still in the PROGRESSING state. This is done to prevent the activity feed from showing an old deploy event as still in progress.
|
|
|
+func updatePreviousPorterAppDeployEvent(ctx context.Context, appID uint, revision int, repo repository.PorterAppEventRepository) {
|
|
|
+ ctx, span := telemetry.NewSpan(ctx, "update-previous-porter-app-deploy-event")
|
|
|
+ defer span.End()
|
|
|
+
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "updating-previous-event", Value: false}, telemetry.AttributeKV{Key: "new-revision", Value: revision})
|
|
|
+ if revision <= 1 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ revisionFloat64 := float64(revision - 1)
|
|
|
+ matchEvent, err := repo.ReadDeployEventByRevision(ctx, appID, revisionFloat64)
|
|
|
+ if err != nil {
|
|
|
+ _ = telemetry.Error(ctx, span, err, "error reading deploy event by revision")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if matchEvent.ID == uuid.Nil {
|
|
|
+ _ = telemetry.Error(ctx, span, nil, "could not find previous deploy event")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if matchEvent.Status != string(types.PorterAppEventStatus_Progressing) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ serviceStatus, ok := matchEvent.Metadata["service_deployment_metadata"]
|
|
|
+ if !ok {
|
|
|
+ _ = telemetry.Error(ctx, span, nil, "service deployment metadata not found in deploy event metadata")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ serviceDeploymentGenericMap, ok := serviceStatus.(map[string]interface{})
|
|
|
+ if !ok {
|
|
|
+ _ = telemetry.Error(ctx, span, nil, "service deployment metadata is not map[string]interface{}")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ serviceDeploymentMap := make(map[string]types.ServiceDeploymentMetadata)
|
|
|
+ for k, v := range serviceDeploymentGenericMap {
|
|
|
+ by, err := json.Marshal(v)
|
|
|
+ if err != nil {
|
|
|
+ _ = telemetry.Error(ctx, span, nil, "unable to marshal")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ var serviceDeploymentMetadata types.ServiceDeploymentMetadata
|
|
|
+ err = json.Unmarshal(by, &serviceDeploymentMetadata)
|
|
|
+ if err != nil {
|
|
|
+ _ = telemetry.Error(ctx, span, nil, "unable to unmarshal")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ serviceDeploymentMap[k] = serviceDeploymentMetadata
|
|
|
+ }
|
|
|
+ for key, serviceDeploymentMetadata := range serviceDeploymentMap {
|
|
|
+ if serviceDeploymentMetadata.Status == types.PorterAppEventStatus_Progressing {
|
|
|
+ serviceDeploymentMetadata.Status = types.PorterAppEventStatus_Canceled
|
|
|
+ serviceDeploymentMap[key] = serviceDeploymentMetadata
|
|
|
+ }
|
|
|
+ }
|
|
|
+ matchEvent.Metadata["service_deployment_metadata"] = serviceDeploymentMap
|
|
|
+ matchEvent.Status = string(types.PorterAppEventStatus_Canceled)
|
|
|
+ err = repo.UpdateEvent(ctx, &matchEvent)
|
|
|
+ if err != nil {
|
|
|
+ _ = telemetry.Error(ctx, span, err, "error updating deploy event")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "updating-previous-event", Value: true})
|
|
|
+}
|
|
|
+
|
|
|
+func createPreDeployJobChart(
|
|
|
ctx context.Context,
|
|
|
stackName string,
|
|
|
values map[string]interface{},
|
|
|
@@ -495,8 +662,8 @@ func createReleaseJobChart(
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
- releaseName := fmt.Sprintf("%s-r", stackName)
|
|
|
- namespace := fmt.Sprintf("porter-stack-%s", stackName)
|
|
|
+ releaseName := utils.PredeployJobNameFromPorterAppName(stackName)
|
|
|
+ namespace := utils.NamespaceFromPorterAppName(stackName)
|
|
|
|
|
|
return &helm.InstallChartConfig{
|
|
|
Chart: chart,
|