Skip to content

Commit

Permalink
Delete target from CLI (#4652)
Browse files Browse the repository at this point in the history
  • Loading branch information
ianedwards authored May 16, 2024
1 parent 71615a8 commit 094315d
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 51 deletions.
13 changes: 13 additions & 0 deletions api/client/deployment_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,16 @@ func (c *Client) ListDeploymentTargets(

return resp, err
}

// DeleteDeploymentTarget deletes a deployment target in a project
func (c *Client) DeleteDeploymentTarget(
ctx context.Context,
projectId uint,
deploymentTargetName string,
) error {
return c.deleteRequest(
fmt.Sprintf("/projects/%d/targets/%s", projectId, deploymentTargetName),
nil,
nil,
)
}
27 changes: 9 additions & 18 deletions api/server/handlers/deployment_target/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ 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/requestutils"
"github.com/porter-dev/porter/api/types"
"github.com/porter-dev/porter/internal/models"
"github.com/porter-dev/porter/internal/telemetry"
)

// DeleteDeploymentTargetHandler is the handler for DELETE /api/projects/{project_id}/clusters/{cluster_id}/deployment-targets/{deployment_target_id}
// DeleteDeploymentTargetHandler is the handler for DELETE /api/projects/{project_id}/targets/{deployment_target_identifier}
type DeleteDeploymentTargetHandler struct {
handlers.PorterHandlerReadWriter
authz.KubernetesAgentGetter
Expand All @@ -34,28 +33,20 @@ func NewDeleteDeploymentTargetHandler(
}
}

// ServeHTTP deletes the deployment target from the cluster
// ServeHTTP deletes the deployment target from the project
func (c *DeleteDeploymentTargetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx, span := telemetry.NewSpan(r.Context(), "server-delete-deployment-target-by-id")
ctx, span := telemetry.NewSpan(r.Context(), "serve-delete-deployment-target")
defer span.End()

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

deploymentTargetID, reqErr := requestutils.GetURLParamString(r, types.URLParamDeploymentTargetID)
if reqErr != nil {
err := telemetry.Error(ctx, span, reqErr, "error parsing deployment target id")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}
if deploymentTargetID == "" {
err := telemetry.Error(ctx, span, nil, "deployment target id cannot be empty")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}
deploymentTarget, _ := ctx.Value(types.DeploymentTargetScope).(types.DeploymentTarget)

deleteReq := connect.NewRequest(&porterv1.DeleteDeploymentTargetRequest{
ProjectId: int64(project.ID),
DeploymentTargetId: deploymentTargetID,
ProjectId: int64(project.ID),
DeploymentTargetIdentifier: &porterv1.DeploymentTargetIdentifier{
Id: deploymentTarget.ID.String(),
Name: deploymentTarget.Name,
},
})

_, err := c.Config().ClusterControlPlaneClient.DeleteDeploymentTarget(ctx, deleteReq)
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 @@ -89,6 +89,35 @@ func getDeploymentTargetRoutes(
Router: r,
})

// DELETE /api/projects/{project_id}/targets/{deployment_target_identifier} -> deployment_target.DeleteDeploymentTargetHandler
deleteDeploymentTargetEndpoint := factory.NewAPIEndpoint(
&types.APIRequestMetadata{
Verb: types.APIVerbDelete,
Method: types.HTTPVerbDelete,
Path: &types.Path{
Parent: basePath,
RelativePath: relPath,
},
Scopes: []types.PermissionScope{
types.UserScope,
types.ProjectScope,
types.DeploymentTargetScope,
},
},
)

deleteDeploymentTargetHandler := deployment_target.NewDeleteDeploymentTargetHandler(
config,
factory.GetDecoderValidator(),
factory.GetResultWriter(),
)

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

// GET /api/projects/{project_id}/targets/{deployment_target_identifier}/apps/{porter_app_name}/cloudsql -> porter_app.GetCloudSqlSecretHandler
getCloudSqlSecretEndpoint := factory.NewAPIEndpoint(
&types.APIRequestMetadata{
Expand Down
29 changes: 0 additions & 29 deletions api/server/router/deployment_target_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,35 +119,6 @@ func getLegacyDeploymentTargetRoutes(
Router: r,
})

// DELETE /api/projects/{project_id}/clusters/{cluster_id}/deployment-targets/{deployment_target_id} -> deployment_target.DeleteDeploymentTargetHandler
deleteDeploymentTargetEndpoint := factory.NewAPIEndpoint(
&types.APIRequestMetadata{
Verb: types.APIVerbDelete,
Method: types.HTTPVerbDelete,
Path: &types.Path{
Parent: basePath,
RelativePath: fmt.Sprintf("%s/{%s}", relPath, types.URLParamDeploymentTargetID),
},
Scopes: []types.PermissionScope{
types.UserScope,
types.ProjectScope,
types.ClusterScope,
},
},
)

deleteDeploymentTargetHandler := deployment_target.NewDeleteDeploymentTargetHandler(
config,
factory.GetDecoderValidator(),
factory.GetResultWriter(),
)

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

// GET /api/projects/{project_id}/clusters/{cluster_id}/deployment-targets/{deployment_target_id} -> deployment_target.GetDeploymentTargetHandler
getDeploymentTargetEndpoint := factory.NewAPIEndpoint(
&types.APIRequestMetadata{
Expand Down
70 changes: 70 additions & 0 deletions cli/cmd/commands/target.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package commands

import (
"bufio"
"context"
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"

"github.com/fatih/color"
Expand Down Expand Up @@ -61,6 +63,23 @@ If the --preview flag is set, only deployment targets for preview environments w
listTargetCmd.Flags().BoolVar(&includePreviews, "preview", false, "List preview environments")
targetCmd.AddCommand(listTargetCmd)

deleteTargetCmd := &cobra.Command{
Use: "delete",
Short: "Deletes a deployment target",
Long: `Deletes a deployment target in the project. Currently, this command only supports the deletion of preview environments.`,
Run: func(cmd *cobra.Command, args []string) {
err := checkLoginAndRunWithConfig(cmd, cliConf, args, deleteTarget)
if err != nil {
os.Exit(1)
}
},
}

deleteTargetCmd.Flags().StringVar(&targetName, "name", "", "Name of deployment target")
deleteTargetCmd.Flags().BoolP("force", "f", false, "Force deletion without confirmation")
deleteTargetCmd.MarkFlagRequired("name") // nolint:errcheck,gosec
targetCmd.AddCommand(deleteTargetCmd)

return targetCmd
}

Expand Down Expand Up @@ -126,6 +145,57 @@ func listTargets(ctx context.Context, user *types.GetAuthenticatedUserResponse,
return nil
}

func deleteTarget(ctx context.Context, _ *types.GetAuthenticatedUserResponse, client api.Client, cliConf config.CLIConfig, featureFlags config.FeatureFlags, cmd *cobra.Command, args []string) error {
name, err := cmd.Flags().GetString("name")
if err != nil {
return fmt.Errorf("error finding name flag: %w", err)
}
if name == "" {
return fmt.Errorf("name flag must be set")
}

force, err := cmd.Flags().GetBool("force")
if err != nil {
return fmt.Errorf("error finding force flag: %w", err)
}

var confirmed bool
if !force {
confirmed, err = confirmAction(fmt.Sprintf("Are you sure you want to delete target '%s'?", name))
if err != nil {
return fmt.Errorf("error confirming action: %w", err)
}
}
if !confirmed && !force {
color.New(color.FgYellow).Println("Deletion aborted") // nolint:errcheck,gosec
return nil
}

err = client.DeleteDeploymentTarget(ctx, cliConf.Project, name)
if err != nil {
return fmt.Errorf("error deleting target: %w", err)
}

color.New(color.FgGreen).Printf("Deleted target '%s'\n", name) // nolint:errcheck,gosec

return nil
}

func confirmAction(prompt string) (bool, error) {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("%s [Y/n]: ", prompt)

response, err := reader.ReadString('\n')
if err != nil {
return false, fmt.Errorf("error reading input: %w", err)
}

response = strings.TrimSpace(response)
confirmed := strings.ToLower(response) == "y" || response == ""

return confirmed, nil
}

func checkmark(b bool) string {
if b {
return "✓"
Expand Down
1 change: 0 additions & 1 deletion dashboard/src/main/home/app-dashboard/apps/Apps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ const Apps: React.FC = () => {
{},
{
project_id: currentProject.id,
cluster_id: currentCluster.id,
deployment_target_id: currentDeploymentTarget.id,
}
);
Expand Down
5 changes: 2 additions & 3 deletions dashboard/src/shared/api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -997,11 +997,10 @@ const deleteDeploymentTarget = baseApi<
{},
{
project_id: number;
cluster_id: number;
deployment_target_id: string;
}
>("DELETE", ({ project_id, cluster_id, deployment_target_id }) => {
return `/api/projects/${project_id}/clusters/${cluster_id}/deployment-targets/${deployment_target_id}`;
>("DELETE", ({ project_id, deployment_target_id }) => {
return `/api/projects/${project_id}/targets/${deployment_target_id}`;
});

const getBranchHead = baseApi<
Expand Down

0 comments on commit 094315d

Please sign in to comment.