Browse Source

track build and predeploy events (#3493)

ianedwards 2 years ago
parent
commit
3ec9ceb825
2 changed files with 117 additions and 1 deletions
  1. 99 0
      cli/cmd/v2/app_events.go
  2. 18 1
      cli/cmd/v2/apply.go

+ 99 - 0
cli/cmd/v2/app_events.go

@@ -0,0 +1,99 @@
+package v2
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"strconv"
+	"strings"
+	"time"
+
+	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/telemetry"
+)
+
+func createBuildEvent(ctx context.Context, client api.Client, applicationName string, projectId, clusterId uint) (string, error) {
+	ctx, span := telemetry.NewSpan(ctx, "create-build-event")
+	defer span.End()
+
+	req := &types.CreateOrUpdatePorterAppEventRequest{
+		Status:             types.PorterAppEventStatus_Progressing,
+		Type:               types.PorterAppEventType_Build,
+		TypeExternalSource: "GITHUB",
+		Metadata:           make(map[string]interface{}),
+	}
+
+	actionRunID := os.Getenv("GITHUB_RUN_ID")
+	if actionRunID != "" {
+		arid, err := strconv.Atoi(actionRunID)
+		if err != nil {
+			fmt.Println("could not parse action run id")
+			return "", telemetry.Error(ctx, span, err, "could not parse action run id")
+		}
+		req.Metadata["action_run_id"] = arid
+
+		repoName := os.Getenv("GITHUB_REPOSITORY")
+		parsedRepoName := strings.Split(repoName, "/")
+		if len(parsedRepoName) != 2 {
+			fmt.Println("repo name is not in the format owner/name")
+			return "", telemetry.Error(ctx, span, nil, "repo name is not in the format owner/name")
+		}
+		req.Metadata["repo"] = parsedRepoName[1]
+
+		repoOwnerAccountID := os.Getenv("GITHUB_REPOSITORY_OWNER_ID")
+		if repoOwnerAccountID != "" {
+			arid, err := strconv.Atoi(repoOwnerAccountID)
+			if err != nil {
+				fmt.Println("could not parse repo owner account id")
+				return "", telemetry.Error(ctx, span, err, "could not parse repo owner account id")
+			}
+			req.Metadata["github_account_id"] = arid
+		}
+	}
+
+	event, err := client.CreateOrUpdatePorterAppEvent(ctx, projectId, clusterId, applicationName, req)
+	if err != nil {
+		fmt.Println("could not create build event")
+		return "", telemetry.Error(ctx, span, err, "could not create build event")
+	}
+
+	return event.ID, nil
+}
+
+func createPredeployEvent(ctx context.Context, client api.Client, applicationName string, projectId, clusterId uint, createdAt time.Time) (string, error) {
+	ctx, span := telemetry.NewSpan(ctx, "create-predeploy-event")
+	defer span.End()
+
+	req := &types.CreateOrUpdatePorterAppEventRequest{
+		Status:   types.PorterAppEventStatus_Progressing,
+		Type:     types.PorterAppEventType_PreDeploy,
+		Metadata: make(map[string]interface{}),
+	}
+	req.Metadata["start_time"] = createdAt
+
+	event, err := client.CreateOrUpdatePorterAppEvent(ctx, projectId, clusterId, applicationName, req)
+	if err != nil {
+		return "", telemetry.Error(ctx, span, err, "could not create predeploy event")
+	}
+
+	return event.ID, nil
+}
+
+func updateExistingEvent(ctx context.Context, client api.Client, applicationName string, projectId, clusterId uint, eventID string, status types.PorterAppEventStatus, metadata map[string]interface{}) error {
+	ctx, span := telemetry.NewSpan(ctx, "update-existing-event")
+	defer span.End()
+
+	req := &types.CreateOrUpdatePorterAppEventRequest{
+		ID:       eventID,
+		Status:   status,
+		Metadata: metadata,
+	}
+
+	_, err := client.CreateOrUpdatePorterAppEvent(ctx, projectId, clusterId, applicationName, req)
+	if err != nil {
+		return telemetry.Error(ctx, span, err, "could not update existing app event")
+	}
+
+	return nil
+}

+ 18 - 1
cli/cmd/v2/apply.go

@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"github.com/porter-dev/porter/api/server/handlers/porter_app"
+	"github.com/porter-dev/porter/api/types"
 
 	"github.com/cli/cli/git"
 
@@ -105,6 +106,8 @@ func Apply(ctx context.Context, cliConf config.CLIConfig, client api.Client, por
 	if applyResp.CLIAction == porterv1.EnumCLIAction_ENUM_CLI_ACTION_BUILD {
 		color.New(color.FgGreen).Printf("Building new image...\n") // nolint:errcheck,gosec
 
+		eventID, _ := createBuildEvent(ctx, client, appName, cliConf.Project, cliConf.Cluster)
+
 		if commitSHA == "" {
 			return errors.New("Build is required but commit SHA cannot be identified. Please set the PORTER_COMMIT_SHA environment variable or run apply in git repository with access to the git CLI.")
 		}
@@ -138,11 +141,14 @@ func Apply(ctx context.Context, cliConf config.CLIConfig, client api.Client, por
 
 		err = build(ctx, client, buildSettings)
 		if err != nil {
+			_ = updateExistingEvent(ctx, client, appName, cliConf.Project, cliConf.Cluster, eventID, types.PorterAppEventStatus_Failed, nil)
 			return fmt.Errorf("error building app: %w", err)
 		}
 
 		color.New(color.FgGreen).Printf("Successfully built image (tag: %s)\n", buildSettings.ImageTag) // nolint:errcheck,gosec
 
+		_ = updateExistingEvent(ctx, client, appName, cliConf.Project, cliConf.Cluster, eventID, types.PorterAppEventStatus_Success, nil)
+
 		applyResp, err = client.ApplyPorterApp(ctx, cliConf.Project, cliConf.Cluster, "", "", applyResp.AppRevisionId)
 		if err != nil {
 			return fmt.Errorf("apply error post-build: %w", err)
@@ -153,6 +159,9 @@ func Apply(ctx context.Context, cliConf config.CLIConfig, client api.Client, por
 		color.New(color.FgGreen).Printf("Waiting for predeploy to complete...\n") // nolint:errcheck,gosec
 
 		now := time.Now().UTC()
+		eventID, _ := createPredeployEvent(ctx, client, appName, cliConf.Project, cliConf.Cluster, now)
+
+		eventStatus := types.PorterAppEventStatus_Success
 		for {
 			if time.Since(now) > checkPredeployTimeout {
 				return errors.New("timed out waiting for predeploy to complete")
@@ -163,13 +172,21 @@ func Apply(ctx context.Context, cliConf config.CLIConfig, client api.Client, por
 				return fmt.Errorf("error calling predeploy status endpoint: %w", err)
 			}
 
-			if predeployStatusResp.Status == porter_app.PredeployStatus_Failed || predeployStatusResp.Status == porter_app.PredeployStatus_Successful {
+			if predeployStatusResp.Status == porter_app.PredeployStatus_Failed {
+				eventStatus = types.PorterAppEventStatus_Failed
+				break
+			}
+			if predeployStatusResp.Status == porter_app.PredeployStatus_Successful {
 				break
 			}
 
 			time.Sleep(checkPredeployFrequency)
 		}
 
+		metadata := make(map[string]interface{})
+		metadata["end_time"] = time.Now().UTC()
+		_ = updateExistingEvent(ctx, client, appName, cliConf.Project, cliConf.Cluster, eventID, eventStatus, metadata)
+
 		applyResp, err = client.ApplyPorterApp(ctx, cliConf.Project, cliConf.Cluster, "", "", applyResp.AppRevisionId)
 		if err != nil {
 			return fmt.Errorf("apply error post-predeploy: %w", err)