Skip to content

Commit

Permalink
support target CLI (#4465)
Browse files Browse the repository at this point in the history
  • Loading branch information
d-g-town authored Mar 28, 2024
1 parent 361483f commit 52808da
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 55 deletions.
44 changes: 28 additions & 16 deletions api/client/deployment_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,40 @@ import (
"context"
"fmt"

"github.com/porter-dev/porter/api/server/handlers/deployment_target"
"github.com/porter-dev/porter/api/types"
)

// CreateDeploymentTarget creates a new deployment target for a given project and cluster with the provided name
// CreateDeploymentTarget creates a deployment target with the given request options
func (c *Client) CreateDeploymentTarget(
ctx context.Context,
projectID, clusterID uint,
selector string,
preview bool,
) (*deployment_target.CreateDeploymentTargetResponse, error) {
resp := &deployment_target.CreateDeploymentTargetResponse{}

req := &deployment_target.CreateDeploymentTargetRequest{
Selector: selector,
Preview: preview,
}
projectId uint,
req *types.CreateDeploymentTargetRequest,
) (*types.CreateDeploymentTargetResponse, error) {
resp := &types.CreateDeploymentTargetResponse{}

err := c.postRequest(
fmt.Sprintf(
"/projects/%d/clusters/%d/deployment-targets",
projectID, clusterID,
),
fmt.Sprintf("/projects/%d/targets", projectId),
req,
resp,
)

return resp, err
}

// ListDeploymentTargets retrieves all deployment targets in a project
func (c *Client) ListDeploymentTargets(
ctx context.Context,
projectId uint,
includePreviews bool,
) (*types.ListDeploymentTargetsResponse, error) {
resp := &types.ListDeploymentTargetsResponse{}

req := &types.ListDeploymentTargetsRequest{
Preview: includePreviews,
}

err := c.getRequest(
fmt.Sprintf("/projects/%d/targets", projectId),
req,
resp,
)
Expand Down
33 changes: 16 additions & 17 deletions api/server/handlers/deployment_target/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,34 +30,21 @@ func NewCreateDeploymentTargetHandler(
}
}

// CreateDeploymentTargetRequest is the request object for the /deployment-targets POST endpoint
type CreateDeploymentTargetRequest struct {
// Deprecated: use name instead
Selector string `json:"selector"`
Name string `json:"name,omitempty"`
Preview bool `json:"preview"`
}

// CreateDeploymentTargetResponse is the response object for the /deployment-targets POST endpoint
type CreateDeploymentTargetResponse struct {
DeploymentTargetID string `json:"deployment_target_id"`
}

// ServeHTTP handles POST requests to create a new deployment target
func (c *CreateDeploymentTargetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx, span := telemetry.NewSpan(r.Context(), "serve-create-deployment-target")
defer span.End()

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

cluster, clusterOk := 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 := &CreateDeploymentTargetRequest{}
request := &types.CreateDeploymentTargetRequest{}
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))
Expand All @@ -69,14 +56,24 @@ func (c *CreateDeploymentTargetHandler) ServeHTTP(w http.ResponseWriter, r *http
return
}

clusterId := request.ClusterId
if clusterOk {
clusterId = cluster.ID
}
if clusterId == 0 {
err := telemetry.Error(ctx, span, nil, "cluster id is required")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}

name := request.Name
if name == "" {
name = request.Selector
}

createReq := connect.NewRequest(&porterv1.CreateDeploymentTargetRequest{
ProjectId: int64(project.ID),
ClusterId: int64(cluster.ID),
ClusterId: int64(clusterId),
Name: name,
Namespace: name,
IsPreview: request.Preview,
Expand All @@ -99,7 +96,9 @@ func (c *CreateDeploymentTargetHandler) ServeHTTP(w http.ResponseWriter, r *http
return
}

res := &CreateDeploymentTargetResponse{
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "deployment-target-id", Value: ccpResp.Msg.DeploymentTargetId})

res := &types.CreateDeploymentTargetResponse{
DeploymentTargetID: ccpResp.Msg.DeploymentTargetId,
}

Expand Down
38 changes: 21 additions & 17 deletions api/server/handlers/deployment_target/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,44 +28,48 @@ func NewListDeploymentTargetsHandler(
}
}

// 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)

telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "cluster-provided", Value: cluster != nil})

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{}
request := &types.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
var deploymentTargets []*models.DeploymentTarget
var err error

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

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

Expand Down
58 changes: 58 additions & 0 deletions api/server/router/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package router
import (
"fmt"

"github.com/porter-dev/porter/api/server/handlers/deployment_target"

"github.com/go-chi/chi/v5"
apiContract "github.com/porter-dev/porter/api/server/handlers/api_contract"
"github.com/porter-dev/porter/api/server/handlers/api_token"
Expand Down Expand Up @@ -1740,5 +1742,61 @@ func getProjectRoutes(
Router: r,
})

// GET /api/projects/{project_id}/targets -> deployment_target.ListDeploymentTargetHandler
listDeploymentTargetEndpoint := factory.NewAPIEndpoint(
&types.APIRequestMetadata{
Verb: types.APIVerbGet,
Method: types.HTTPVerbGet,
Path: &types.Path{
Parent: basePath,
RelativePath: fmt.Sprintf("%s/targets", relPath),
},
Scopes: []types.PermissionScope{
types.UserScope,
types.ProjectScope,
},
},
)

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

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

// GET /api/projects/{project_id}/targets -> deployment_target.ListDeploymentTargetHandler
createDeploymentTargetEndpoint := factory.NewAPIEndpoint(
&types.APIRequestMetadata{
Verb: types.APIVerbCreate,
Method: types.HTTPVerbPost,
Path: &types.Path{
Parent: basePath,
RelativePath: fmt.Sprintf("%s/targets", relPath),
},
Scopes: []types.PermissionScope{
types.UserScope,
types.ProjectScope,
},
},
)

createDeploymentTargetHandler := deployment_target.NewCreateDeploymentTargetHandler(
config,
factory.GetDecoderValidator(),
factory.GetResultWriter(),
)

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

return routes, newPath
}
25 changes: 25 additions & 0 deletions api/types/deployment_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,28 @@ type DeploymentTarget struct {
CreatedAtUTC time.Time `json:"created_at"`
UpdatedAtUTC time.Time `json:"updated_at"`
}

// CreateDeploymentTargetRequest is the request object for the /deployment-targets POST endpoint
type CreateDeploymentTargetRequest struct {
// Deprecated: use name instead
Selector string `json:"selector"`
Name string `json:"name,omitempty"`
Preview bool `json:"preview"`
// required if using the project-scoped endpoint
ClusterId uint `json:"cluster_id"`
}

// CreateDeploymentTargetResponse is the response object for the /deployment-targets POST endpoint
type CreateDeploymentTargetResponse struct {
DeploymentTargetID string `json:"deployment_target_id"`
}

// 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 []DeploymentTarget `json:"deployment_targets"`
}
1 change: 1 addition & 0 deletions cli/cmd/commands/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func RegisterCommands() (*cobra.Command, error) {
rootCmd.AddCommand(registerCommand_Run(cliConf))
rootCmd.AddCommand(registerCommand_Server(cliConf))
rootCmd.AddCommand(registerCommand_Stack(cliConf))
rootCmd.AddCommand(registerCommand_Target(cliConf))
rootCmd.AddCommand(registerCommand_Update(cliConf))
rootCmd.AddCommand(registerCommand_Version(cliConf))
rootCmd.AddCommand(registerCommand_Env(cliConf))
Expand Down
Loading

0 comments on commit 52808da

Please sign in to comment.