Просмотр исходного кода

choose default deployment target for cluster if none is provided (#4182)

d-g-town 2 лет назад
Родитель
Сommit
c3e1493836

+ 25 - 18
api/client/porter_app.go

@@ -351,22 +351,33 @@ func (c *Client) DefaultDeploymentTarget(
 	return resp, err
 }
 
+// CurrentAppRevisionInput is the input struct to CurrentAppRevision
+type CurrentAppRevisionInput struct {
+	ProjectID uint
+	ClusterID uint
+	AppName   string
+	// DeploymentTargetName is the name of the deployment target to get the current app revision for. One of this or DeploymentTargetID must be set.
+	DeploymentTargetName string
+	// DeploymentTargetID is the id of the deployment target to get the current app revision for. One of this or DeploymentTargetName must be set.
+	DeploymentTargetID string
+}
+
 // CurrentAppRevision returns the currently deployed app revision for a given project, app name and deployment target
 func (c *Client) CurrentAppRevision(
 	ctx context.Context,
-	projectID uint, clusterID uint,
-	appName string, deploymentTarget string,
+	input CurrentAppRevisionInput,
 ) (*porter_app.LatestAppRevisionResponse, error) {
 	resp := &porter_app.LatestAppRevisionResponse{}
 
 	req := &porter_app.LatestAppRevisionRequest{
-		DeploymentTargetID: deploymentTarget,
+		DeploymentTargetName: input.DeploymentTargetName,
+		DeploymentTargetID:   input.DeploymentTargetID,
 	}
 
 	err := c.getRequest(
 		fmt.Sprintf(
 			"/projects/%d/clusters/%d/apps/%s/latest",
-			projectID, clusterID, appName,
+			input.ProjectID, input.ClusterID, input.AppName,
 		),
 		req,
 		resp,
@@ -757,12 +768,12 @@ func (c *Client) RollbackRevision(
 	ctx context.Context,
 	projectID, clusterID uint,
 	appName string,
-	deploymentTargetID string,
+	deploymentTargetName string,
 ) (*porter_app.RollbackAppRevisionResponse, error) {
 	resp := &porter_app.RollbackAppRevisionResponse{}
 
 	req := &porter_app.RollbackAppRevisionRequest{
-		DeploymentTargetID: deploymentTargetID,
+		DeploymentTargetName: deploymentTargetName,
 	}
 
 	err := c.postRequest(
@@ -804,13 +815,13 @@ func (c *Client) RunAppJob(
 	ctx context.Context,
 	projectID, clusterID uint,
 	appName string, jobName string,
-	deploymentTargetID string,
+	deploymentTargetName string,
 ) (*porter_app.RunAppJobResponse, error) {
 	resp := &porter_app.RunAppJobResponse{}
 
 	req := &porter_app.RunAppJobRequest{
-		ServiceName:        jobName,
-		DeploymentTargetID: deploymentTargetID,
+		ServiceName:          jobName,
+		DeploymentTargetName: deploymentTargetName,
 	}
 
 	err := c.postRequest(
@@ -834,11 +845,8 @@ type RunAppJobStatusInput struct {
 	// Cluster is the id of the cluster against which to retrieve a helm agent for
 	ClusterID uint
 
-	// DeploymentTargetID is the id of the deployment target the job was run against
-	DeploymentTargetID string
-
-	// DeploymentTargetNamespace is the namespace in which the job was deployed
-	DeploymentTargetNamespace string
+	// DeploymentTargetName is the id of the deployment target the job was run against
+	DeploymentTargetName string
 
 	// ServiceName is the name of the app service that was triggered
 	ServiceName string
@@ -858,10 +866,9 @@ func (c *Client) RunAppJobStatus(
 	resp := &porter_app.AppJobRunStatusResponse{}
 
 	req := &porter_app.AppJobRunStatusRequest{
-		DeploymentTargetID: input.DeploymentTargetID,
-		JobRunID:           input.JobRunID,
-		Namespace:          input.DeploymentTargetNamespace,
-		ServiceName:        input.ServiceName,
+		DeploymentTargetName: input.DeploymentTargetName,
+		JobRunID:             input.JobRunID,
+		ServiceName:          input.ServiceName,
 	}
 
 	err := c.getRequest(

+ 25 - 12
api/server/handlers/porter_app/current_app_revision.go

@@ -10,8 +10,6 @@ import (
 
 	porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
 
-	"github.com/google/uuid"
-
 	"github.com/porter-dev/porter/internal/porter_app"
 	"github.com/porter-dev/porter/internal/telemetry"
 
@@ -43,7 +41,8 @@ func NewLatestAppRevisionHandler(
 
 // LatestAppRevisionRequest is the request object for the /apps/{porter_app_name}/latest endpoint
 type LatestAppRevisionRequest struct {
-	DeploymentTargetID string `schema:"deployment_target_id"`
+	DeploymentTargetID   string `schema:"deployment_target_id,omitempty"`
+	DeploymentTargetName string `schema:"deployment_target_name,omitempty"`
 }
 
 // LatestAppRevisionResponse is the response object for the /apps/{porter_app_name}/latest endpoint
@@ -82,13 +81,24 @@ func (c *LatestAppRevisionHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 		return
 	}
 
-	_, err := uuid.Parse(request.DeploymentTargetID)
-	if err != nil {
-		err := telemetry.Error(ctx, span, err, "error parsing deployment target id")
-		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
-		return
+	deploymentTargetName := request.DeploymentTargetName
+	if request.DeploymentTargetName == "" && request.DeploymentTargetID == "" {
+		defaultDeploymentTarget, err := defaultDeploymentTarget(ctx, defaultDeploymentTargetInput{
+			ProjectID:                 project.ID,
+			ClusterID:                 cluster.ID,
+			ClusterControlPlaneClient: c.Config().ClusterControlPlaneClient,
+		})
+		if err != nil {
+			err := telemetry.Error(ctx, span, err, "error getting default deployment target")
+			c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+			return
+		}
+		deploymentTargetName = defaultDeploymentTarget.Name
 	}
-	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "deployment-target-id", Value: request.DeploymentTargetID})
+	telemetry.WithAttributes(span,
+		telemetry.AttributeKV{Key: "deployment-target-name", Value: deploymentTargetName},
+		telemetry.AttributeKV{Key: "deployment-target-id", Value: request.DeploymentTargetID},
+	)
 
 	porterApps, err := c.Repo().PorterApp().ReadPorterAppsByProjectIDAndName(project.ID, appName)
 	if err != nil {
@@ -117,9 +127,12 @@ func (c *LatestAppRevisionHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 	}
 
 	currentAppRevisionReq := connect.NewRequest(&porterv1.CurrentAppRevisionRequest{
-		ProjectId:          int64(project.ID),
-		AppId:              int64(appId),
-		DeploymentTargetId: request.DeploymentTargetID,
+		ProjectId: int64(project.ID),
+		AppId:     int64(appId),
+		DeploymentTargetIdentifier: &porterv1.DeploymentTargetIdentifier{
+			Id:   request.DeploymentTargetID,
+			Name: deploymentTargetName,
+		},
 	})
 
 	currentAppRevisionResp, err := c.Config().ClusterControlPlaneClient.CurrentAppRevision(ctx, currentAppRevisionReq)

+ 58 - 30
api/server/handlers/porter_app/default_deployment_target.go

@@ -1,9 +1,12 @@
 package porter_app
 
 import (
+	"context"
 	"net/http"
 	"time"
 
+	"github.com/porter-dev/api-contracts/generated/go/porter/v1/porterv1connect"
+
 	"github.com/google/uuid"
 
 	"connectrpc.com/connect"
@@ -65,53 +68,78 @@ func (c *DefaultDeploymentTargetHandler) ServeHTTP(w http.ResponseWriter, r *htt
 		telemetry.AttributeKV{Key: "cluster-id", Value: cluster.ID},
 	)
 
-	defaultDeploymentTargetReq := connect.NewRequest(&porterv1.DefaultDeploymentTargetRequest{
-		ProjectId: int64(project.ID),
-		ClusterId: int64(cluster.ID),
+	defaultDeploymentTarget, err := defaultDeploymentTarget(ctx, defaultDeploymentTargetInput{
+		ProjectID:                 project.ID,
+		ClusterID:                 cluster.ID,
+		ClusterControlPlaneClient: c.Config().ClusterControlPlaneClient,
 	})
-
-	defaultDeploymentTargetResp, err := c.Config().ClusterControlPlaneClient.DefaultDeploymentTarget(ctx, defaultDeploymentTargetReq)
 	if err != nil {
 		err := telemetry.Error(ctx, span, err, "error getting default deployment target")
 		c.WriteResult(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
 		return
 	}
 
+	response := &DefaultDeploymentTargetResponse{
+		DeploymentTargetID: defaultDeploymentTarget.ID.String(),
+		DeploymentTarget:   defaultDeploymentTarget,
+	}
+
+	c.WriteResult(w, r, response)
+}
+
+type defaultDeploymentTargetInput struct {
+	ProjectID                 uint
+	ClusterID                 uint
+	ClusterControlPlaneClient porterv1connect.ClusterControlPlaneServiceClient
+}
+
+func defaultDeploymentTarget(ctx context.Context, input defaultDeploymentTargetInput) (types.DeploymentTarget, error) {
+	ctx, span := telemetry.NewSpan(ctx, "default-deployment-target")
+	defer span.End()
+
+	var defaultDeploymentTarget types.DeploymentTarget
+
+	telemetry.WithAttributes(span,
+		telemetry.AttributeKV{Key: "project-id", Value: input.ProjectID},
+		telemetry.AttributeKV{Key: "cluster-id", Value: input.ClusterID},
+	)
+
+	defaultDeploymentTargetReq := connect.NewRequest(&porterv1.DefaultDeploymentTargetRequest{
+		ProjectId: int64(input.ProjectID),
+		ClusterId: int64(input.ClusterID),
+	})
+
+	defaultDeploymentTargetResp, err := input.ClusterControlPlaneClient.DefaultDeploymentTarget(ctx, defaultDeploymentTargetReq)
+	if err != nil {
+		return defaultDeploymentTarget, telemetry.Error(ctx, span, err, "error getting default deployment target")
+	}
+
 	if defaultDeploymentTargetResp == nil || defaultDeploymentTargetResp.Msg == nil {
-		err := telemetry.Error(ctx, span, nil, "default deployment target response is nil")
-		c.WriteResult(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
-		return
+		return defaultDeploymentTarget, telemetry.Error(ctx, span, nil, "default deployment target response is nil")
 	}
 
-	defaultDeploymentTarget := defaultDeploymentTargetResp.Msg.DeploymentTarget
+	deploymentTargetProto := defaultDeploymentTargetResp.Msg.DeploymentTarget
 
-	id, err := uuid.Parse(defaultDeploymentTarget.Id)
+	id, err := uuid.Parse(deploymentTargetProto.Id)
 	if err != nil {
-		err := telemetry.Error(ctx, span, err, "error parsing default deployment target id")
-		c.WriteResult(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
-		return
+		return defaultDeploymentTarget, telemetry.Error(ctx, span, err, "error parsing default deployment target id")
 	}
 
 	if id == uuid.Nil {
-		err := telemetry.Error(ctx, span, nil, "default deployment target id is nil")
-		c.WriteResult(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
-		return
+		return defaultDeploymentTarget, telemetry.Error(ctx, span, nil, "default deployment target id is nil")
 	}
 
-	response := &DefaultDeploymentTargetResponse{
-		DeploymentTargetID: defaultDeploymentTarget.Id,
-		DeploymentTarget: types.DeploymentTarget{
-			ID:        id,
-			ProjectID: uint(defaultDeploymentTarget.ProjectId),
-			ClusterID: uint(defaultDeploymentTarget.ClusterId),
-			Name:      defaultDeploymentTarget.Name,
-			Namespace: defaultDeploymentTarget.Namespace,
-			IsPreview: defaultDeploymentTarget.IsPreview,
-			IsDefault: defaultDeploymentTarget.IsDefault,
-			CreatedAt: time.Time{}, // not provided by default deployment target response
-			UpdatedAt: time.Time{}, // not provided by default deployment target response
-		},
+	defaultDeploymentTarget = types.DeploymentTarget{
+		ID:        id,
+		ProjectID: uint(deploymentTargetProto.ProjectId),
+		ClusterID: uint(deploymentTargetProto.ClusterId),
+		Name:      deploymentTargetProto.Name,
+		Namespace: deploymentTargetProto.Namespace,
+		IsPreview: deploymentTargetProto.IsPreview,
+		IsDefault: deploymentTargetProto.IsDefault,
+		CreatedAt: time.Time{}, // not provided by default deployment target response
+		UpdatedAt: time.Time{}, // not provided by default deployment target response
 	}
 
-	c.WriteResult(w, r, response)
+	return defaultDeploymentTarget, nil
 }

+ 21 - 1
api/server/handlers/porter_app/pod_status.go

@@ -70,11 +70,31 @@ func (c *PodStatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		telemetry.AttributeKV{Key: "input-deployment-target-name", Value: request.DeploymentTargetName},
 	)
 
+	deploymentTargetName := request.DeploymentTargetName
+	if request.DeploymentTargetName == "" && request.DeploymentTargetID == "" {
+		defaultDeploymentTarget, err := defaultDeploymentTarget(ctx, defaultDeploymentTargetInput{
+			ProjectID:                 project.ID,
+			ClusterID:                 cluster.ID,
+			ClusterControlPlaneClient: c.Config().ClusterControlPlaneClient,
+		})
+		if err != nil {
+			err := telemetry.Error(ctx, span, err, "error getting default deployment target")
+			c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+			return
+		}
+		deploymentTargetName = defaultDeploymentTarget.Name
+	}
+
+	telemetry.WithAttributes(span,
+		telemetry.AttributeKV{Key: "deployment-target-id", Value: request.DeploymentTargetID},
+		telemetry.AttributeKV{Key: "deployment-target-name", Value: request.DeploymentTargetName},
+	)
+
 	deploymentTarget, err := deployment_target.DeploymentTargetDetails(ctx, deployment_target.DeploymentTargetDetailsInput{
 		ProjectID:            int64(project.ID),
 		ClusterID:            int64(cluster.ID),
 		DeploymentTargetID:   request.DeploymentTargetID,
-		DeploymentTargetName: request.DeploymentTargetName,
+		DeploymentTargetName: deploymentTargetName,
 		CCPClient:            c.Config().ClusterControlPlaneClient,
 	})
 	if err != nil {

+ 30 - 19
api/server/handlers/porter_app/rollback_revision.go

@@ -4,7 +4,6 @@ import (
 	"net/http"
 
 	"connectrpc.com/connect"
-	"github.com/google/uuid"
 	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"
@@ -37,8 +36,9 @@ func NewRollbackAppRevisionHandler(
 
 // RollbackAppRevisionRequest is the request body for the /apps/{porter_app_name}/rollback endpoint
 type RollbackAppRevisionRequest struct {
-	DeploymentTargetID string `json:"deployment_target_id"`
-	AppRevisionID      string `json:"app_revision_id"`
+	DeploymentTargetID   string `json:"deployment_target_id"`
+	DeploymentTargetName string `json:"deployment_target_name"`
+	AppRevisionID        string `json:"app_revision_id"`
 }
 
 // RollbackAppRevisionResponse is the response body for the /apps/{porter_app_name}/rollback endpoint
@@ -67,18 +67,6 @@ func (c *RollbackAppRevisionHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
 		return
 	}
 
-	deploymentTargetID, err := uuid.Parse(request.DeploymentTargetID)
-	if err != nil {
-		err := telemetry.Error(ctx, span, err, "error parsing deployment target id")
-		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
-		return
-	}
-	if deploymentTargetID == uuid.Nil {
-		err := telemetry.Error(ctx, span, nil, "deployment target id is nil")
-		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
-		return
-	}
-
 	appName, reqErr := requestutils.GetURLParamString(r, types.URLParamPorterAppName)
 	if reqErr != nil {
 		err := telemetry.Error(ctx, span, nil, "error parsing porter app name")
@@ -99,11 +87,34 @@ func (c *RollbackAppRevisionHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
 		return
 	}
 
+	deploymentTargetName := request.DeploymentTargetName
+	if request.DeploymentTargetName == "" && request.DeploymentTargetID == "" {
+		defaultDeploymentTarget, err := defaultDeploymentTarget(ctx, defaultDeploymentTargetInput{
+			ProjectID:                 project.ID,
+			ClusterID:                 cluster.ID,
+			ClusterControlPlaneClient: c.Config().ClusterControlPlaneClient,
+		})
+		if err != nil {
+			err := telemetry.Error(ctx, span, err, "error getting default deployment target")
+			c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+			return
+		}
+		deploymentTargetName = defaultDeploymentTarget.Name
+	}
+
+	telemetry.WithAttributes(span,
+		telemetry.AttributeKV{Key: "deployment-target-id", Value: request.DeploymentTargetID},
+		telemetry.AttributeKV{Key: "deployment-target-name", Value: request.DeploymentTargetName},
+	)
+
 	rollbackReq := connect.NewRequest(&porterv1.RollbackRevisionRequest{
-		ProjectId:          int64(project.ID),
-		AppId:              int64(app.ID),
-		DeploymentTargetId: deploymentTargetID.String(),
-		AppRevisionId:      request.AppRevisionID,
+		ProjectId: int64(project.ID),
+		AppId:     int64(app.ID),
+		DeploymentTargetIdentifier: &porterv1.DeploymentTargetIdentifier{
+			Id:   request.DeploymentTargetID,
+			Name: deploymentTargetName,
+		},
+		AppRevisionId: request.AppRevisionID,
 	})
 	ccpResp, err := c.Config().ClusterControlPlaneClient.RollbackRevision(ctx, rollbackReq)
 	if err != nil {

+ 24 - 7
api/server/handlers/porter_app/run_app_job.go

@@ -40,8 +40,11 @@ func NewRunAppJobHandler(
 
 // RunAppJobRequest is the request object for the /apps/{porter_app_name}/run endpoint
 type RunAppJobRequest struct {
-	ServiceName        string `json:"service_name"`
+	ServiceName string `json:"service_name"`
+	// DeploymentTargetID is the id of the deployment target the job should be run against. One of DeploymentTargetID or DeploymentTargetName is required
 	DeploymentTargetID string `json:"deployment_target_id"`
+	// DeploymentTargetName is the name of the deployment target the job should be run against. One of DeploymentTargetID or DeploymentTargetName is required
+	DeploymentTargetName string `json:"deployment_target_name"`
 	// Optional field to override the default run command for the job
 	RunCommand string `json:"run_command"`
 	// Image is an optional field to override the image used for the job
@@ -59,6 +62,7 @@ func (c *RunAppJobHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	defer span.End()
 
 	project, _ := ctx.Value(types.ProjectScope).(*models.Project)
+	cluster, _ := ctx.Value(types.ClusterScope).(*models.Cluster)
 
 	appName, reqErr := requestutils.GetURLParamString(r, types.URLParamPorterAppName)
 	if reqErr != nil {
@@ -83,12 +87,24 @@ func (c *RunAppJobHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	}
 	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "service-name", Value: request.ServiceName})
 
-	if request.DeploymentTargetID == "" {
-		err := telemetry.Error(ctx, span, nil, "deployment target id is required")
-		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
-		return
+	deploymentTargetName := request.DeploymentTargetName
+	if request.DeploymentTargetName == "" && request.DeploymentTargetID == "" {
+		defaultDeploymentTarget, err := defaultDeploymentTarget(ctx, defaultDeploymentTargetInput{
+			ProjectID:                 project.ID,
+			ClusterID:                 cluster.ID,
+			ClusterControlPlaneClient: c.Config().ClusterControlPlaneClient,
+		})
+		if err != nil {
+			err := telemetry.Error(ctx, span, err, "error getting default deployment target")
+			c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+			return
+		}
+		deploymentTargetName = defaultDeploymentTarget.Name
 	}
-	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "deployment-target-id", Value: request.DeploymentTargetID})
+	telemetry.WithAttributes(span,
+		telemetry.AttributeKV{Key: "deployment-target-name", Value: deploymentTargetName},
+		telemetry.AttributeKV{Key: "deployment-target-id", Value: request.DeploymentTargetID},
+	)
 
 	var commandOptional *string
 	if request.RunCommand != "" {
@@ -114,7 +130,8 @@ func (c *RunAppJobHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		Command:     commandOptional,
 		Image:       imageOverrideOptional,
 		DeploymentTargetIdentifier: &porterv1.DeploymentTargetIdentifier{
-			Id: request.DeploymentTargetID,
+			Id:   request.DeploymentTargetID,
+			Name: deploymentTargetName,
 		},
 	})
 

+ 47 - 16
api/server/handlers/porter_app/run_app_job_status.go

@@ -6,6 +6,10 @@ import (
 	"net/http"
 	"strings"
 
+	"connectrpc.com/connect"
+
+	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/shared/requestutils"
 
@@ -44,14 +48,14 @@ type AppJobRunStatusRequest struct {
 	// DeploymentTargetID is the id of the deployment target the job was run against
 	DeploymentTargetID string `json:"deployment_target_id"`
 
+	// DeploymentTargetName is the name of the deployment target the job was run against
+	DeploymentTargetName string `json:"deployment_target_name"`
+
 	// JobRunID is the UID returned from the /apps/{porter_app_name}/run endpoint
 	JobRunID string `json:"job_id"`
 
 	// ServiceName is the name of the app service that was triggered
 	ServiceName string `json:"service_name"`
-
-	// Namespace is the namespace in which the job was deployed
-	Namespace string `json:"namespace"`
 }
 
 // AppJobRunStatusResponse is the response object for the /apps/{porter_app_name}/run-status endpoint
@@ -64,6 +68,7 @@ func (c *AppJobRunStatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
 	ctx, span := telemetry.NewSpan(r.Context(), "serve-app-job-run-status")
 	defer span.End()
 
+	project, _ := ctx.Value(types.ProjectScope).(*models.Project)
 	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
 
 	appName, reqErr := requestutils.GetURLParamString(r, types.URLParamPorterAppName)
@@ -89,13 +94,6 @@ func (c *AppJobRunStatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
 	}
 	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "job-run-id", Value: request.JobRunID})
 
-	if request.Namespace == "" {
-		err := telemetry.Error(ctx, span, nil, "namespace is required")
-		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
-		return
-	}
-	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "namespace", Value: request.Namespace})
-
 	if request.ServiceName == "" {
 		err := telemetry.Error(ctx, span, nil, "service name is required")
 		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
@@ -103,12 +101,45 @@ func (c *AppJobRunStatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
 	}
 	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "service-name", Value: request.ServiceName})
 
-	if request.DeploymentTargetID == "" {
-		err := telemetry.Error(ctx, span, nil, "deployment target id is required")
-		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
+	deploymentTargetName := request.DeploymentTargetName
+	if request.DeploymentTargetName == "" && request.DeploymentTargetID == "" {
+		defaultDeploymentTarget, err := defaultDeploymentTarget(ctx, defaultDeploymentTargetInput{
+			ProjectID:                 project.ID,
+			ClusterID:                 cluster.ID,
+			ClusterControlPlaneClient: c.Config().ClusterControlPlaneClient,
+		})
+		if err != nil {
+			err := telemetry.Error(ctx, span, err, "error getting default deployment target")
+			c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+			return
+		}
+		deploymentTargetName = defaultDeploymentTarget.Name
+	}
+	telemetry.WithAttributes(span,
+		telemetry.AttributeKV{Key: "deployment-target-name", Value: deploymentTargetName},
+		telemetry.AttributeKV{Key: "deployment-target-id", Value: request.DeploymentTargetID},
+	)
+
+	details, err := c.Config().ClusterControlPlaneClient.DeploymentTargetDetails(ctx, connect.NewRequest(&porterv1.DeploymentTargetDetailsRequest{
+		ProjectId: int64(project.ID),
+		DeploymentTargetIdentifier: &porterv1.DeploymentTargetIdentifier{
+			Id:   request.DeploymentTargetID,
+			Name: deploymentTargetName,
+		},
+	}))
+	if err != nil {
+		err := telemetry.Error(ctx, span, err, "error getting deployment target details")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+		return
+	}
+
+	if details == nil || details.Msg == nil || details.Msg.DeploymentTarget == nil {
+		err := telemetry.Error(ctx, span, err, "deployment target details are nil")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
 		return
 	}
-	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "deployment-target-id", Value: request.DeploymentTargetID})
+
+	namespace := details.Msg.DeploymentTarget.Namespace
 
 	agent, err := c.GetAgent(r, cluster, "")
 	if err != nil {
@@ -125,10 +156,10 @@ func (c *AppJobRunStatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
 
 	status, err := c.getJobStatus(ctx, getJobStatusInput{
 		AppName:            appName,
-		DeploymentTargetID: request.DeploymentTargetID,
+		DeploymentTargetID: details.Msg.DeploymentTarget.Id,
 		ClusterK8sAgent:    *agent,
 		JobRunID:           request.JobRunID,
-		Namespace:          request.Namespace,
+		Namespace:          namespace,
 		ServiceName:        request.ServiceName,
 	})
 	if err != nil {

+ 24 - 5
api/server/handlers/porter_app/update_image.go

@@ -36,7 +36,7 @@ func NewUpdateImageHandler(
 
 // UpdateImageRequest is the request object for the /apps/{porter_app_name}/update-image endpoint
 type UpdateImageRequest struct {
-	DeploymentTargetId   string `json:"deployment_target_id"`
+	DeploymentTargetID   string `json:"deployment_target_id"`
 	DeploymentTargetName string `json:"deployment_target_name"`
 	Repository           string `json:"repository"`
 	Tag                  string `json:"tag"`
@@ -54,6 +54,7 @@ func (c *UpdateImageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	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")
@@ -77,20 +78,38 @@ func (c *UpdateImageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	}
 
 	telemetry.WithAttributes(span,
-		telemetry.AttributeKV{Key: "deployment-target-id", Value: request.DeploymentTargetId},
-		telemetry.AttributeKV{Key: "deployment-target-name", Value: request.DeploymentTargetName},
 		telemetry.AttributeKV{Key: "repository", Value: request.Repository},
 		telemetry.AttributeKV{Key: "tag", Value: request.Tag},
 	)
 
+	deploymentTargetName := request.DeploymentTargetName
+	if request.DeploymentTargetName == "" && request.DeploymentTargetID == "" {
+		defaultDeploymentTarget, err := defaultDeploymentTarget(ctx, defaultDeploymentTargetInput{
+			ProjectID:                 project.ID,
+			ClusterID:                 cluster.ID,
+			ClusterControlPlaneClient: c.Config().ClusterControlPlaneClient,
+		})
+		if err != nil {
+			err := telemetry.Error(ctx, span, err, "error getting default deployment target")
+			c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+			return
+		}
+		deploymentTargetName = defaultDeploymentTarget.Name
+	}
+
+	telemetry.WithAttributes(span,
+		telemetry.AttributeKV{Key: "deployment-target-id", Value: request.DeploymentTargetID},
+		telemetry.AttributeKV{Key: "deployment-target-name", Value: request.DeploymentTargetName},
+	)
+
 	updateImageReq := connect.NewRequest(&porterv1.UpdateAppImageRequest{
 		ProjectId:     int64(project.ID),
 		RepositoryUrl: request.Repository,
 		Tag:           request.Tag,
 		AppName:       appName,
 		DeploymentTargetIdentifier: &porterv1.DeploymentTargetIdentifier{
-			Id:   request.DeploymentTargetId,
-			Name: request.DeploymentTargetName,
+			Id:   request.DeploymentTargetID,
+			Name: deploymentTargetName,
 		},
 	})
 	ccpResp, err := c.Config().ClusterControlPlaneClient.UpdateAppImage(ctx, updateImageReq)

+ 22 - 21
cli/cmd/commands/app.go

@@ -35,17 +35,17 @@ import (
 )
 
 var (
-	appContainerName string
-	appCpuMilli      int
-	appExistingPod   bool
-	appInteractive   bool
-	appMemoryMi      int
-	appNamespace     string
-	appTag           string
-	appVerbose       bool
-	appWait          bool
-	deploymentTarget string
-	jobName          string
+	appContainerName     string
+	appCpuMilli          int
+	appExistingPod       bool
+	appInteractive       bool
+	appMemoryMi          int
+	appNamespace         string
+	appTag               string
+	appVerbose           bool
+	appWait              bool
+	deploymentTargetName string
+	jobName              string
 )
 
 const (
@@ -62,11 +62,11 @@ func registerCommand_App(cliConf config.CLIConfig) *cobra.Command {
 	}
 
 	appCmd.PersistentFlags().StringVarP(
-		&deploymentTarget,
+		&deploymentTargetName,
 		"target",
 		"x",
-		"default",
-		"the deployment target for the app, default is \"default\"",
+		"",
+		"the name of the deployment target for the app",
 	)
 
 	// appRunCmd represents the "porter app run" subcommand
@@ -274,11 +274,12 @@ func appRun(ctx context.Context, _ *types.GetAuthenticatedUserResponse, client a
 		}
 
 		return v2.RunAppJob(ctx, v2.RunAppJobInput{
-			CLIConfig:   cliConfig,
-			Client:      client,
-			AppName:     args[0],
-			JobName:     jobName,
-			WaitForExit: appWait,
+			CLIConfig:            cliConfig,
+			Client:               client,
+			DeploymentTargetName: deploymentTargetName,
+			AppName:              args[0],
+			JobName:              jobName,
+			WaitForExit:          appWait,
 		})
 	}
 
@@ -300,7 +301,7 @@ func appRun(ctx context.Context, _ *types.GetAuthenticatedUserResponse, client a
 	// updated exec args includes launcher command prepended if needed, otherwise it is the same as execArgs
 	var updatedExecArgs []string
 	if project.ValidateApplyV2 {
-		podsSimple, updatedExecArgs, namespace, err = getPodsFromV2PorterYaml(ctx, execArgs, client, cliConfig, args[0], deploymentTarget)
+		podsSimple, updatedExecArgs, namespace, err = getPodsFromV2PorterYaml(ctx, execArgs, client, cliConfig, args[0], deploymentTargetName)
 		if err != nil {
 			return err
 		}
@@ -1270,7 +1271,7 @@ func appUpdateTag(ctx context.Context, user *types.GetAuthenticatedUserResponse,
 			ProjectID:                   cliConf.Project,
 			ClusterID:                   cliConf.Cluster,
 			AppName:                     args[0],
-			DeploymentTargetName:        deploymentTarget,
+			DeploymentTargetName:        deploymentTargetName,
 			Tag:                         appTag,
 			Client:                      client,
 			WaitForSuccessfulDeployment: appWait,

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

@@ -230,7 +230,13 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 			return err
 		}
 
-		currentAppRevisionResp, err := client.CurrentAppRevision(ctx, cliConf.Project, cliConf.Cluster, appName, deploymentTargetID)
+		currentAppRevisionResp, err := client.CurrentAppRevision(ctx, api.CurrentAppRevisionInput{
+			ProjectID:            cliConf.Project,
+			ClusterID:            cliConf.Cluster,
+			AppName:              appName,
+			DeploymentTargetName: "",
+			DeploymentTargetID:   deploymentTargetID,
+		})
 		if err != nil {
 			err := fmt.Errorf("error getting current app revision: %w", err)
 			reportBuildFailureInput.buildError = err

+ 1 - 0
cli/cmd/v2/deployment_target.go

@@ -0,0 +1 @@
+package v2

+ 3 - 7
cli/cmd/v2/rollback.go

@@ -17,19 +17,15 @@ type RollbackInput struct {
 	Client api.Client
 	// AppName is the name of the app to rollback
 	AppName string
+	// DeploymentTargetName is the name of the deployment target to rollback
+	DeploymentTargetName string
 }
 
 // Rollback deploys the previous successful revision of an app
 func Rollback(ctx context.Context, inp RollbackInput) error {
-	targetResp, err := inp.Client.DefaultDeploymentTarget(ctx, inp.CLIConfig.Project, inp.CLIConfig.Cluster)
-	if err != nil {
-		return fmt.Errorf("error calling default deployment target endpoint: %w", err)
-	}
-	deploymentTargetID := targetResp.DeploymentTargetID
-
 	color.New(color.FgGreen).Printf("Rolling back to last deployed revision ...\n") // nolint:errcheck,gosec
 
-	rollbackResp, err := inp.Client.RollbackRevision(ctx, inp.CLIConfig.Project, inp.CLIConfig.Cluster, inp.AppName, deploymentTargetID)
+	rollbackResp, err := inp.Client.RollbackRevision(ctx, inp.CLIConfig.Project, inp.CLIConfig.Cluster, inp.AppName, inp.DeploymentTargetName)
 	if err != nil {
 		return fmt.Errorf("error calling rollback revision endpoint: %w", err)
 	}

+ 16 - 14
cli/cmd/v2/run_app_job.go

@@ -24,6 +24,8 @@ type RunAppJobInput struct {
 	CLIConfig config.CLIConfig
 	// Client is the Porter API client
 	Client api.Client
+	// DeploymentTargetName is the name of deployment target to run the job on
+	DeploymentTargetName string
 
 	AppName string
 	JobName string
@@ -34,17 +36,18 @@ type RunAppJobInput struct {
 
 // RunAppJob triggers a job run for an app and returns without waiting for the job to complete
 func RunAppJob(ctx context.Context, inp RunAppJobInput) error {
-	targetResp, err := inp.Client.DefaultDeploymentTarget(ctx, inp.CLIConfig.Project, inp.CLIConfig.Cluster)
-	if err != nil {
-		return fmt.Errorf("error calling default deployment target endpoint: %w", err)
-	}
-
-	currentAppRevisionResp, err := inp.Client.CurrentAppRevision(ctx, inp.CLIConfig.Project, inp.CLIConfig.Cluster, inp.AppName, targetResp.DeploymentTargetID) // nolint:staticcheck
+	currentAppRevisionResp, err := inp.Client.CurrentAppRevision(ctx, api.CurrentAppRevisionInput{
+		ProjectID:            inp.CLIConfig.Project,
+		ClusterID:            inp.CLIConfig.Cluster,
+		AppName:              inp.AppName,
+		DeploymentTargetName: inp.DeploymentTargetName,
+		DeploymentTargetID:   "",
+	})
 	if err != nil {
 		return fmt.Errorf("error getting current app revision: %w", err)
 	}
 
-	resp, err := inp.Client.RunAppJob(ctx, inp.CLIConfig.Project, inp.CLIConfig.Cluster, inp.AppName, inp.JobName, targetResp.DeploymentTargetID) // nolint:staticcheck
+	resp, err := inp.Client.RunAppJob(ctx, inp.CLIConfig.Project, inp.CLIConfig.Cluster, inp.AppName, inp.JobName, inp.DeploymentTargetName) // nolint:staticcheck
 	if err != nil {
 		return fmt.Errorf("unable to run job: %w", err)
 	}
@@ -88,13 +91,12 @@ func RunAppJob(ctx context.Context, inp RunAppJobInput) error {
 	time.Sleep(2 * time.Second)
 
 	input := api.RunAppJobStatusInput{
-		AppName:                   inp.AppName,
-		ClusterID:                 inp.CLIConfig.Cluster,
-		DeploymentTargetID:        targetResp.DeploymentTargetID, // nolint:staticcheck
-		DeploymentTargetNamespace: targetResp.Namespace,
-		ServiceName:               inp.JobName,
-		JobRunID:                  resp.JobRunID,
-		ProjectID:                 inp.CLIConfig.Project,
+		AppName:              inp.AppName,
+		ClusterID:            inp.CLIConfig.Cluster,
+		DeploymentTargetName: inp.DeploymentTargetName,
+		ServiceName:          inp.JobName,
+		JobRunID:             resp.JobRunID,
+		ProjectID:            inp.CLIConfig.Project,
 	}
 
 	for time.Now().Before(deadline) {

+ 0 - 5
cli/cmd/v2/update_image.go

@@ -2,7 +2,6 @@ package v2
 
 import (
 	"context"
-	"errors"
 	"fmt"
 
 	"github.com/fatih/color"
@@ -23,10 +22,6 @@ type UpdateImageInput struct {
 
 // UpdateImage updates the image of an application
 func UpdateImage(ctx context.Context, input UpdateImageInput) error {
-	if input.DeploymentTargetName == "" {
-		return errors.New("please provide a deployment target")
-	}
-
 	tag := input.Tag
 	if tag == "" {
 		tag = "latest"