add bulk deletion support to ark backup delete

Signed-off-by: Steve Kriss <steve@heptio.com>
pull/745/head
Steve Kriss 2018-08-07 16:07:01 -07:00
parent 1f7a4a1665
commit 5acccaa739
5 changed files with 159 additions and 37 deletions

View File

@ -31,7 +31,7 @@ Work with backups
### SEE ALSO ### SEE ALSO
* [ark](ark.md) - Back up and restore Kubernetes cluster resources. * [ark](ark.md) - Back up and restore Kubernetes cluster resources.
* [ark backup create](ark_backup_create.md) - Create a backup * [ark backup create](ark_backup_create.md) - Create a backup
* [ark backup delete](ark_backup_delete.md) - Delete a backup * [ark backup delete](ark_backup_delete.md) - Delete backups
* [ark backup describe](ark_backup_describe.md) - Describe backups * [ark backup describe](ark_backup_describe.md) - Describe backups
* [ark backup download](ark_backup_download.md) - Download a backup * [ark backup download](ark_backup_download.md) - Download a backup
* [ark backup get](ark_backup_get.md) - Get backups * [ark backup get](ark_backup_get.md) - Get backups

View File

@ -1,21 +1,43 @@
## ark backup delete ## ark backup delete
Delete a backup Delete backups
### Synopsis ### Synopsis
Delete a backup Delete backups
``` ```
ark backup delete NAME [flags] ark backup delete [NAMES] [flags]
```
### Examples
```
# delete a backup named "backup-1"
ark backup delete backup-1
# delete a backup named "backup-1" without prompting for confirmation
ark backup delete backup-1 --confirm
# delete backups named "backup-1" and "backup-2"
ark backup delete backup-1 backup-2
# delete all backups triggered by schedule "schedule-1"
ark backup delete --selector ark-schedule=schedule-1
# delete all backups
ark backup delete --all
``` ```
### Options ### Options
``` ```
--confirm Confirm deletion --all Delete all backups
-h, --help help for delete --confirm Confirm deletion
-h, --help help for delete
-l, --selector labelSelector Delete all backups matching this label selector (default <none>)
``` ```
### Options inherited from parent commands ### Options inherited from parent commands

View File

@ -30,7 +30,7 @@ Delete ark resources
### SEE ALSO ### SEE ALSO
* [ark](ark.md) - Back up and restore Kubernetes cluster resources. * [ark](ark.md) - Back up and restore Kubernetes cluster resources.
* [ark delete backup](ark_delete_backup.md) - Delete a backup * [ark delete backup](ark_delete_backup.md) - Delete backups
* [ark delete restore](ark_delete_restore.md) - Delete a restore * [ark delete restore](ark_delete_restore.md) - Delete a restore
* [ark delete schedule](ark_delete_schedule.md) - Delete a schedule * [ark delete schedule](ark_delete_schedule.md) - Delete a schedule

View File

@ -1,21 +1,43 @@
## ark delete backup ## ark delete backup
Delete a backup Delete backups
### Synopsis ### Synopsis
Delete a backup Delete backups
``` ```
ark delete backup NAME [flags] ark delete backup [NAMES] [flags]
```
### Examples
```
# delete a backup named "backup-1"
ark backup delete backup-1
# delete a backup named "backup-1" without prompting for confirmation
ark backup delete backup-1 --confirm
# delete backups named "backup-1" and "backup-2"
ark backup delete backup-1 backup-2
# delete all backups triggered by schedule "schedule-1"
ark backup delete --selector ark-schedule=schedule-1
# delete all backups
ark backup delete --all
``` ```
### Options ### Options
``` ```
--confirm Confirm deletion --all Delete all backups
-h, --help help for backup --confirm Confirm deletion
-h, --help help for backup
-l, --selector labelSelector Delete all backups matching this label selector (default <none>)
``` ```
### Options inherited from parent commands ### Options inherited from parent commands

View File

@ -18,20 +18,24 @@ package backup
import ( import (
"bufio" "bufio"
"errors"
"fmt" "fmt"
"os" "os"
"strings" "strings"
"github.com/heptio/ark/pkg/apis/ark/v1" "github.com/pkg/errors"
"github.com/heptio/ark/pkg/backup"
clientset "github.com/heptio/ark/pkg/generated/clientset/versioned"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
kubeerrs "k8s.io/apimachinery/pkg/util/errors"
arkv1api "github.com/heptio/ark/pkg/apis/ark/v1"
"github.com/heptio/ark/pkg/backup"
"github.com/heptio/ark/pkg/client" "github.com/heptio/ark/pkg/client"
"github.com/heptio/ark/pkg/cmd" "github.com/heptio/ark/pkg/cmd"
"github.com/heptio/ark/pkg/cmd/util/flag"
clientset "github.com/heptio/ark/pkg/generated/clientset/versioned"
) )
// NewDeleteCommand creates a new command that deletes a backup. // NewDeleteCommand creates a new command that deletes a backup.
@ -39,9 +43,23 @@ func NewDeleteCommand(f client.Factory, use string) *cobra.Command {
o := &DeleteOptions{} o := &DeleteOptions{}
c := &cobra.Command{ c := &cobra.Command{
Use: fmt.Sprintf("%s NAME", use), Use: fmt.Sprintf("%s [NAMES]", use),
Short: "Delete a backup", Short: "Delete backups",
Args: cobra.ExactArgs(1), Example: ` # delete a backup named "backup-1"
ark backup delete backup-1
# delete a backup named "backup-1" without prompting for confirmation
ark backup delete backup-1 --confirm
# delete backups named "backup-1" and "backup-2"
ark backup delete backup-1 backup-2
# delete all backups triggered by schedule "schedule-1"
ark backup delete --selector ark-schedule=schedule-1
# delete all backups
ark backup delete --all
`,
Run: func(c *cobra.Command, args []string) { Run: func(c *cobra.Command, args []string) {
cmd.CheckError(o.Complete(f, args)) cmd.CheckError(o.Complete(f, args))
cmd.CheckError(o.Validate(c, args, f)) cmd.CheckError(o.Validate(c, args, f))
@ -56,23 +74,24 @@ func NewDeleteCommand(f client.Factory, use string) *cobra.Command {
// DeleteOptions contains parameters for deleting a backup. // DeleteOptions contains parameters for deleting a backup.
type DeleteOptions struct { type DeleteOptions struct {
Name string Names []string
Confirm bool All bool
Selector flag.LabelSelector
Confirm bool
client clientset.Interface client clientset.Interface
namespace string namespace string
backup *v1.Backup
} }
// BindFlags binds options for this command to flags. // BindFlags binds options for this command to flags.
func (o *DeleteOptions) BindFlags(flags *pflag.FlagSet) { func (o *DeleteOptions) BindFlags(flags *pflag.FlagSet) {
flags.BoolVar(&o.Confirm, "confirm", o.Confirm, "Confirm deletion") flags.BoolVar(&o.Confirm, "confirm", o.Confirm, "Confirm deletion")
flags.BoolVar(&o.All, "all", o.All, "Delete all backups")
flags.VarP(&o.Selector, "selector", "l", "Delete all backups matching this label selector")
} }
// Complete fills out the remainder of the parameters based on user input. // Complete fills out the remainder of the parameters based on user input.
func (o *DeleteOptions) Complete(f client.Factory, args []string) error { func (o *DeleteOptions) Complete(f client.Factory, args []string) error {
o.Name = args[0]
o.namespace = f.Namespace() o.namespace = f.Namespace()
client, err := f.Client() client, err := f.Client()
@ -81,11 +100,7 @@ func (o *DeleteOptions) Complete(f client.Factory, args []string) error {
} }
o.client = client o.client = client
backup, err := o.client.ArkV1().Backups(f.Namespace()).Get(o.Name, metav1.GetOptions{}) o.Names = args
if err != nil {
return err
}
o.backup = backup
return nil return nil
} }
@ -96,13 +111,34 @@ func (o *DeleteOptions) Validate(c *cobra.Command, args []string, f client.Facto
return errors.New("Ark client is not set; unable to proceed") return errors.New("Ark client is not set; unable to proceed")
} }
if o.backup == nil { var (
return errors.New("backup is not set; unable to proceed") hasNames = len(o.Names) > 0
hasAll = o.All
hasSelector = o.Selector.LabelSelector != nil
)
if !xor(hasNames, hasAll, hasSelector) {
return errors.New("you must specify exactly one of: specific backup name(s), the --all flag, or the --selector flag")
} }
return nil return nil
} }
// xor returns true if exactly one of the provided values is true,
// or false otherwise.
func xor(val bool, vals ...bool) bool {
res := val
for _, v := range vals {
if res && v {
return false
}
res = res || v
}
return res
}
// Run performs the delete backup operation. // Run performs the delete backup operation.
func (o *DeleteOptions) Run() error { func (o *DeleteOptions) Run() error {
if !o.Confirm && !getConfirmation() { if !o.Confirm && !getConfirmation() {
@ -110,14 +146,56 @@ func (o *DeleteOptions) Run() error {
return nil return nil
} }
deleteRequest := backup.NewDeleteBackupRequest(o.backup.Name, string(o.backup.UID)) var (
backups []*arkv1api.Backup
errs []error
)
if _, err := o.client.ArkV1().DeleteBackupRequests(o.namespace).Create(deleteRequest); err != nil { // get the list of backups to delete
return err switch {
case len(o.Names) > 0:
for _, name := range o.Names {
backup, err := o.client.ArkV1().Backups(o.namespace).Get(name, metav1.GetOptions{})
if err != nil {
errs = append(errs, errors.WithStack(err))
continue
}
backups = append(backups, backup)
}
default:
selector := labels.Everything().String()
if o.Selector.LabelSelector != nil {
selector = o.Selector.String()
}
res, err := o.client.ArkV1().Backups(o.namespace).List(metav1.ListOptions{LabelSelector: selector})
if err != nil {
return errors.WithStack(err)
}
for i := range res.Items {
backups = append(backups, &res.Items[i])
}
} }
fmt.Printf("Request to delete backup %q submitted successfully.\nThe backup will be fully deleted after all associated data (disk snapshots, backup files, restores) are removed.\n", o.backup.Name) if len(backups) == 0 {
return nil fmt.Println("No backups found")
return nil
}
// create a backup deletion request for each
for _, b := range backups {
deleteRequest := backup.NewDeleteBackupRequest(b.Name, string(b.UID))
if _, err := o.client.ArkV1().DeleteBackupRequests(o.namespace).Create(deleteRequest); err != nil {
errs = append(errs, err)
continue
}
fmt.Printf("Request to delete backup %q submitted successfully.\nThe backup will be fully deleted after all associated data (disk snapshots, backup files, restores) are removed.\n", b.Name)
}
return kubeerrs.NewAggregate(errs)
} }
func getConfirmation() bool { func getConfirmation() bool {