Skip to content

Commit

Permalink
scope dashboard to a given deployment target
Browse files Browse the repository at this point in the history
  • Loading branch information
ianedwards committed Sep 26, 2023
1 parent 7c54d20 commit 8981127
Show file tree
Hide file tree
Showing 25 changed files with 985 additions and 250 deletions.
81 changes: 81 additions & 0 deletions api/server/handlers/deployment_target/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package deployment_target

import (
"net/http"

"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/types"
"github.com/porter-dev/porter/internal/models"
"github.com/porter-dev/porter/internal/telemetry"
)

// ListDeploymentTargetsHandler is the handler for the /deployment-targets endpoint
type ListDeploymentTargetsHandler struct {
handlers.PorterHandlerReadWriter
}

// NewListDeploymentTargetsHandler handles GET requests to the endpoint /deployment-targets
func NewListDeploymentTargetsHandler(
config *config.Config,
decoderValidator shared.RequestDecoderValidator,
writer shared.ResultWriter,
) *ListDeploymentTargetsHandler {
return &ListDeploymentTargetsHandler{
PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
}
}

// ListDeploymentTargetsRequest is the request object for the /deployment-targets GET endpoint
type ListDeploymentTargetsRequest struct {
Preview bool `json:"preview"`
}

// ListDeploymentTargetsResponse is the response object for the /deployment-targets GET endpoint
type ListDeploymentTargetsResponse struct {
DeploymentTargets []types.DeploymentTarget `json:"deployment_targets"`
}

func (c *ListDeploymentTargetsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx, span := telemetry.NewSpan(r.Context(), "serve-list-deployment-targets")
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")
c.HandleAPIError(w, r, apierrors.NewErrForbidden(err))
return
}

request := &ListDeploymentTargetsRequest{}
if ok := c.DecodeAndValidate(w, r, request); !ok {
err := telemetry.Error(ctx, span, nil, "error decoding request")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}

deploymentTargets, err := c.Repo().DeploymentTarget().List(project.ID, cluster.ID, request.Preview)
if err != nil {
err := telemetry.Error(ctx, span, err, "error retrieving deployment targets")
c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
return
}

response := ListDeploymentTargetsResponse{
DeploymentTargets: make([]types.DeploymentTarget, 0),
}

for _, dt := range deploymentTargets {
if dt == nil {
continue
}

response.DeploymentTargets = append(response.DeploymentTargets, *dt.ToDeploymentTargetType())
}

c.WriteResult(w, r, response)
}
26 changes: 17 additions & 9 deletions api/server/handlers/porter_app/latest_app_revisions.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ func NewLatestAppRevisionsHandler(
}
}

// LatestAppRevisionsRequest represents the response from the /apps/revisions endpoint
type LatestAppRevisionsRequest struct{}
// LatestAppRevisionsRequest represents the request for the /apps/revisions endpoint
type LatestAppRevisionsRequest struct {
DeploymentTargetID string `schema:"deployment_target_id"`
}

// LatestRevisionWithSource is an app revision and its source porter app
type LatestRevisionWithSource struct {
Expand All @@ -53,22 +55,28 @@ func (c *LatestAppRevisionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
project, _ := r.Context().Value(types.ProjectScope).(*models.Project)
cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)

// todo(ianedwards): once we have a way to select a deployment target, we can add it to the request
defaultDeploymentTarget, err := c.Repo().DeploymentTarget().DeploymentTargetBySelectorAndSelectorType(project.ID, cluster.ID, DeploymentTargetSelector_Default, DeploymentTargetSelectorType_Default)
request := &LatestAppRevisionsRequest{}
if ok := c.DecodeAndValidate(w, r, request); !ok {
err := telemetry.Error(ctx, span, nil, "error decoding request")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}

deploymentTargetID, err := uuid.Parse(request.DeploymentTargetID)
if err != nil {
err := telemetry.Error(ctx, span, err, "error getting default deployment target from repo")
err := telemetry.Error(ctx, span, err, "error parsing deployment target id")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}
if defaultDeploymentTarget.ID == uuid.Nil {
err := telemetry.Error(ctx, span, err, "default deployment target not found")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
if deploymentTargetID == uuid.Nil {
err := telemetry.Error(ctx, span, err, "deployment target id is nil")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}

listAppRevisionsReq := connect.NewRequest(&porterv1.LatestAppRevisionsRequest{
ProjectId: int64(project.ID),
DeploymentTargetId: defaultDeploymentTarget.ID.String(),
DeploymentTargetId: deploymentTargetID.String(),
})

latestAppRevisionsResp, err := c.Config().ClusterControlPlaneClient.LatestAppRevisions(ctx, listAppRevisionsReq)
Expand Down
29 changes: 29 additions & 0 deletions api/server/router/deployment_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,34 @@ func getDeploymentTargetRoutes(
Router: r,
})

// GET /api/projects/{project_id}/clusters/{cluster_id}/deployment-targets -> deployment_target.ListDeploymentTargetsHandler
listDeploymentTargetsEndpoint := factory.NewAPIEndpoint(
&types.APIRequestMetadata{
Verb: types.APIVerbList,
Method: types.HTTPVerbGet,
Path: &types.Path{
Parent: basePath,
RelativePath: relPath,
},
Scopes: []types.PermissionScope{
types.UserScope,
types.ProjectScope,
types.ClusterScope,
},
},
)

listDeploymentTargetsHandler := deployment_target.NewListDeploymentTargetsHandler(
config,
factory.GetDecoderValidator(),
factory.GetResultWriter(),
)

routes = append(routes, &router.Route{
Endpoint: listDeploymentTargetsEndpoint,
Handler: listDeploymentTargetsHandler,
Router: r,
})

return routes, newPath
}
18 changes: 18 additions & 0 deletions api/types/deployment_target.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package types

import (
"time"

"github.com/google/uuid"
)

type DeploymentTarget struct {
ID uuid.UUID `json:"id"`
ProjectID uint `json:"project_id"`
ClusterID uint `json:"cluster_id"`

Selector string `json:"selector"`
SelectorType string `json:"selector_type"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
1 change: 1 addition & 0 deletions dashboard/src/lib/revisions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const appRevisionValidator = z.object({
]),
b64_app_proto: z.string(),
revision_number: z.number(),
deployment_target_id: z.string(),
id: z.string(),
created_at: z.string(),
updated_at: z.string(),
Expand Down
Loading

0 comments on commit 8981127

Please sign in to comment.