Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for project forced deletion #900

Merged
merged 11 commits into from
May 30, 2024
15 changes: 15 additions & 0 deletions client/incus_projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,18 @@ func (r *ProtocolIncus) DeleteProject(name string) error {

return nil
}

// DeleteProjectForce deletes a project and everything inside of it.
func (r *ProtocolIncus) DeleteProjectForce(name string) error {
if !r.HasExtension("projects_force_delete") {
return fmt.Errorf("The server is missing the required \"projects_force_delete\" API extension")
}

// Send the request
_, _, err := r.query("DELETE", fmt.Sprintf("/projects/%s?force=1", url.PathEscape(name)), nil, "")
if err != nil {
return err
}

return nil
}
1 change: 1 addition & 0 deletions client/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ type InstanceServer interface {
UpdateProject(name string, project api.ProjectPut, ETag string) (err error)
RenameProject(name string, project api.ProjectPost) (op Operation, err error)
DeleteProject(name string) (err error)
DeleteProjectForce(name string) (err error)

// Storage pool functions ("storage" API extension)
GetStoragePoolNames() (names []string, err error)
Expand Down
38 changes: 34 additions & 4 deletions cmd/incus/project.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bufio"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -185,6 +186,8 @@ func (c *cmdProjectCreate) Run(cmd *cobra.Command, args []string) error {
type cmdProjectDelete struct {
global *cmdGlobal
project *cmdProject

flagForce bool
}

func (c *cmdProjectDelete) Command() *cobra.Command {
Expand All @@ -195,6 +198,7 @@ func (c *cmdProjectDelete) Command() *cobra.Command {
cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
`Delete projects`))

cmd.Flags().BoolVarP(&c.flagForce, "force", "f", false, i18n.G("Force delete the project and everything it contains."))
cmd.RunE = c.Run

cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
Expand All @@ -208,6 +212,20 @@ func (c *cmdProjectDelete) Command() *cobra.Command {
return cmd
}

func (c *cmdProjectDelete) promptConfirmation(name string) error {
reader := bufio.NewReader(os.Stdin)

fmt.Printf(i18n.G("Remove %s and everything it contains (instances, images, volumes, networks, ...) (yes/no): "), name)
input, _ := reader.ReadString('\n')
input = strings.TrimSuffix(input, "\n")

if !slices.Contains([]string{i18n.G("yes")}, strings.ToLower(input)) {
return fmt.Errorf(i18n.G("User aborted delete operation"))
}

return nil
}

func (c *cmdProjectDelete) Run(cmd *cobra.Command, args []string) error {
// Quick checks.
exit, err := c.global.CheckArgs(cmd, args, 1, 1)
Expand All @@ -232,10 +250,22 @@ func (c *cmdProjectDelete) Run(cmd *cobra.Command, args []string) error {
return fmt.Errorf(i18n.G("Missing project name"))
}

// Delete the project
err = resource.server.DeleteProject(resource.name)
if err != nil {
return err
// Delete the project, server is unable to find the project here.
if c.flagForce {
err := c.promptConfirmation(resource.name)
if err != nil {
return err
}

err = resource.server.DeleteProjectForce(resource.name)
if err != nil {
return err
}
} else {
err = resource.server.DeleteProject(resource.name)
if err != nil {
return err
}
}

if !c.global.flagQuiet {
Expand Down
Loading
Loading