Răsfoiți Sursa

Delete target from CLI (#4652)

ianedwards 2 ani în urmă
părinte
comite
094315d684

+ 13 - 0
api/client/deployment_target.go

@@ -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,
+	)
+}

+ 9 - 18
api/server/handlers/deployment_target/delete.go

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

+ 29 - 0
api/server/router/deployment_target.go

@@ -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{

+ 0 - 29
api/server/router/deployment_target_legacy.go

@@ -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{

+ 70 - 0
cli/cmd/commands/target.go

@@ -1,10 +1,12 @@
 package commands
 
 import (
+	"bufio"
 	"context"
 	"fmt"
 	"os"
 	"sort"
+	"strings"
 	"text/tabwriter"
 
 	"github.com/fatih/color"
@@ -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
 }
 
@@ -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 "✓"

+ 0 - 1
dashboard/src/main/home/app-dashboard/apps/Apps.tsx

@@ -226,7 +226,6 @@ const Apps: React.FC = () => {
         {},
         {
           project_id: currentProject.id,
-          cluster_id: currentCluster.id,
           deployment_target_id: currentDeploymentTarget.id,
         }
       );

+ 2 - 3
dashboard/src/shared/api.tsx

@@ -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<