Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
- Display one external source per line when doing showing a security group
- chore: update crypto version #770
- chores: add git version/commit in user agent header #769
- Prompting for validation before deleting deployments and models (dedicated-inference)
- Ability to delete multiple deployments and models at once (dedicated-inference)

## 1.88.0

Expand Down
8 changes: 7 additions & 1 deletion cmd/aiservices/deployment/deployment_actions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,16 @@ func TestDeploymentDeleteScaleRevealLogs(t *testing.T) {
now := time.Now()
ts.deployments = []v3.ListDeploymentsResponseEntry{{ID: v3.UUID("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"), Name: "alpha", CreatedAT: now, UpdatedAT: now}}
// delete by name
del := &DeploymentDeleteCmd{CliCommandSettings: exocmd.DefaultCLICmdSettings(), Deployment: "alpha"}
del := &DeploymentDeleteCmd{CliCommandSettings: exocmd.DefaultCLICmdSettings(), Deployments: []string{"alpha"}, Force: true}
if err := del.CmdRun(nil, nil); err != nil {
t.Fatalf("delete: %v", err)
}
// delete multiple (add another deployment first)
ts.deployments = append(ts.deployments, v3.ListDeploymentsResponseEntry{ID: v3.UUID("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"), Name: "beta", CreatedAT: now, UpdatedAT: now})
delMultiple := &DeploymentDeleteCmd{CliCommandSettings: exocmd.DefaultCLICmdSettings(), Deployments: []string{"alpha", "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, Force: true}
if err := delMultiple.CmdRun(nil, nil); err != nil {
t.Fatalf("delete multiple: %v", err)
}
// scale by id
sc := &DeploymentScaleCmd{CliCommandSettings: exocmd.DefaultCLICmdSettings(), Deployment: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", Size: 3}
if err := sc.CmdRun(nil, nil); err != nil {
Expand Down
51 changes: 39 additions & 12 deletions cmd/aiservices/deployment/deployment_delete.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package deployment

import (
"context"
"fmt"
"os"

Expand All @@ -17,8 +16,9 @@ type DeploymentDeleteCmd struct {

_ bool `cli-cmd:"delete"`

Deployment string `cli-arg:"#" cli-usage:"ID or NAME"`
Zone v3.ZoneName `cli-short:"z" cli-usage:"zone"`
Deployments []string `cli-arg:"#" cli-usage:"NAME|ID..."`
Force bool `cli-short:"f" cli-usage:"don't prompt for confirmation"`
Zone v3.ZoneName `cli-short:"z" cli-usage:"zone"`
}

func (c *DeploymentDeleteCmd) CmdAliases() []string { return exocmd.GDeleteAlias }
Expand All @@ -37,24 +37,51 @@ func (c *DeploymentDeleteCmd) CmdRun(_ *cobra.Command, _ []string) error {
return err
}

// Resolve deployment ID using the SDK helper
// Resolve deployment IDs using the SDK helper
list, err := client.ListDeployments(ctx)
if err != nil {
return err
}
entry, err := list.FindListDeploymentsResponseEntry(c.Deployment)
if err != nil {
return err

deploymentsToDelete := []v3.UUID{}
for _, deploymentStr := range c.Deployments {
entry, err := list.FindListDeploymentsResponseEntry(deploymentStr)
if err != nil {
if !c.Force {
return err
}
fmt.Fprintf(os.Stderr, "warning: %s not found.\n", deploymentStr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just print error, eg. fmt.Fprintf(os.Stderr, "warning: %w\n", err)

continue
}

if !c.Force {
if !utils.AskQuestion(ctx, fmt.Sprintf("Are you sure you want to delete deployment %q?", deploymentStr)) {
return nil
}
}

deploymentsToDelete = append(deploymentsToDelete, entry.ID)
}
id := entry.ID

if err := utils.RunAsync(ctx, client, fmt.Sprintf("Deleting deployment %s...", c.Deployment), func(ctx context.Context, c *v3.Client) (*v3.Operation, error) {
return c.DeleteDeployment(ctx, id)
}); err != nil {
var fns []func() error
for _, id := range deploymentsToDelete {
fns = append(fns, func() error {
op, err := client.DeleteDeployment(ctx, id)
if err != nil {
return err
}
_, err = client.Wait(ctx, op, v3.OperationStateSuccess)
return err
})
}

err = utils.DecorateAsyncOperations("Deleting deployment(s)...", fns...)
if err != nil {
return err
}

if !globalstate.Quiet {
fmt.Fprintln(os.Stdout, "Deployment deleted.")
fmt.Fprintln(os.Stdout, "Deployment(s) deleted.")
}
return nil
}
Expand Down
47 changes: 37 additions & 10 deletions cmd/aiservices/model/model_delete.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package model

import (
"context"
"fmt"
"os"

Expand All @@ -17,8 +16,9 @@ type ModelDeleteCmd struct {

_ bool `cli-cmd:"delete"`

ID string `cli-arg:"#" cli-usage:"MODEL-ID (UUID)"`
Zone v3.ZoneName `cli-short:"z" cli-usage:"zone"`
IDs []string `cli-arg:"#" cli-usage:"MODEL-ID (UUID)..."`
Force bool `cli-short:"f" cli-usage:"don't prompt for confirmation"`
Zone v3.ZoneName `cli-short:"z" cli-usage:"zone"`
}

func (c *ModelDeleteCmd) CmdAliases() []string { return exocmd.GDeleteAlias }
Expand All @@ -35,18 +35,45 @@ func (c *ModelDeleteCmd) CmdRun(_ *cobra.Command, _ []string) error {
return err
}

id, err := v3.ParseUUID(c.ID)
if err != nil {
return fmt.Errorf("invalid model ID: %w", err)
modelsToDelete := []v3.UUID{}
for _, idStr := range c.IDs {
id, err := v3.ParseUUID(idStr)
if err != nil {
if !c.Force {
return fmt.Errorf("invalid model ID %q: %w", idStr, err)
}
fmt.Fprintf(os.Stderr, "warning: invalid model ID %q: %v\n", idStr, err)
continue
}

if !c.Force {
if !utils.AskQuestion(ctx, fmt.Sprintf("Are you sure you want to delete model %q?", idStr)) {
return nil
}
}

modelsToDelete = append(modelsToDelete, id)
}

if err := utils.RunAsync(ctx, client, fmt.Sprintf("Deleting model %s...", c.ID), func(ctx context.Context, c *v3.Client) (*v3.Operation, error) {
return c.DeleteModel(ctx, id)
}); err != nil {
var fns []func() error
for _, id := range modelsToDelete {
fns = append(fns, func() error {
op, err := client.DeleteModel(ctx, id)
if err != nil {
return err
}
_, err = client.Wait(ctx, op, v3.OperationStateSuccess)
return err
})
}

err = utils.DecorateAsyncOperations("Deleting model(s)...", fns...)
if err != nil {
return err
}

if !globalstate.Quiet {
fmt.Fprintln(os.Stdout, "Model deleted.")
fmt.Fprintln(os.Stdout, "Model(s) deleted.")
}
return nil
}
Expand Down
16 changes: 13 additions & 3 deletions cmd/aiservices/model/model_delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,24 @@ func TestModelDeleteInvalidUUIDAndSuccess(t *testing.T) {
}
globalstate.EgoscaleV3Client = client.WithEndpoint(v3.Endpoint(srv.URL))

// invalid UUID
cmd := &ModelDeleteCmd{CliCommandSettings: exocmd.DefaultCLICmdSettings(), ID: "not-a-uuid"}
// invalid UUID without force
cmd := &ModelDeleteCmd{CliCommandSettings: exocmd.DefaultCLICmdSettings(), IDs: []string{"not-a-uuid"}, Force: false}
if err := cmd.CmdRun(nil, nil); err == nil || !regexp.MustCompile(`invalid model ID`).MatchString(err.Error()) {
t.Fatalf("expected invalid uuid error, got %v", err)
}
// invalid UUID with force (should skip with warning, no error)
cmd = &ModelDeleteCmd{CliCommandSettings: exocmd.DefaultCLICmdSettings(), IDs: []string{"not-a-uuid"}, Force: true}
if err := cmd.CmdRun(nil, nil); err != nil {
t.Fatalf("expected no error with force flag, got %v", err)
}
// success
cmd = &ModelDeleteCmd{CliCommandSettings: exocmd.DefaultCLICmdSettings(), ID: "33333333-3333-3333-3333-333333333333"}
cmd = &ModelDeleteCmd{CliCommandSettings: exocmd.DefaultCLICmdSettings(), IDs: []string{"33333333-3333-3333-3333-333333333333"}, Force: true}
if err := cmd.CmdRun(nil, nil); err != nil {
t.Fatalf("model delete: %v", err)
}
// multiple models
cmd = &ModelDeleteCmd{CliCommandSettings: exocmd.DefaultCLICmdSettings(), IDs: []string{"33333333-3333-3333-3333-333333333333", "44444444-4444-4444-4444-444444444444"}, Force: true}
if err := cmd.CmdRun(nil, nil); err != nil {
t.Fatalf("model delete multiple: %v", err)
}
}