diff --git a/docs/cli-reference/ark_backup.md b/docs/cli-reference/ark_backup.md index 78c37bbf0..ce979e372 100644 --- a/docs/cli-reference/ark_backup.md +++ b/docs/cli-reference/ark_backup.md @@ -31,7 +31,7 @@ Work with backups ### SEE ALSO * [ark](ark.md) - Back up and restore Kubernetes cluster resources. * [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 download](ark_backup_download.md) - Download a backup * [ark backup get](ark_backup_get.md) - Get backups diff --git a/docs/cli-reference/ark_backup_delete.md b/docs/cli-reference/ark_backup_delete.md index 7866f02f3..5e24a10e1 100644 --- a/docs/cli-reference/ark_backup_delete.md +++ b/docs/cli-reference/ark_backup_delete.md @@ -1,21 +1,43 @@ ## ark backup delete -Delete a backup +Delete backups ### 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 ``` - --confirm Confirm deletion - -h, --help help for delete + --all Delete all backups + --confirm Confirm deletion + -h, --help help for delete + -l, --selector labelSelector Delete all backups matching this label selector (default ) ``` ### Options inherited from parent commands diff --git a/docs/cli-reference/ark_delete.md b/docs/cli-reference/ark_delete.md index 7016169ce..a9693d20a 100644 --- a/docs/cli-reference/ark_delete.md +++ b/docs/cli-reference/ark_delete.md @@ -30,7 +30,7 @@ Delete ark resources ### SEE ALSO * [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 schedule](ark_delete_schedule.md) - Delete a schedule diff --git a/docs/cli-reference/ark_delete_backup.md b/docs/cli-reference/ark_delete_backup.md index 3dceaf041..d370004b0 100644 --- a/docs/cli-reference/ark_delete_backup.md +++ b/docs/cli-reference/ark_delete_backup.md @@ -1,21 +1,43 @@ ## ark delete backup -Delete a backup +Delete backups ### 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 ``` - --confirm Confirm deletion - -h, --help help for backup + --all Delete all backups + --confirm Confirm deletion + -h, --help help for backup + -l, --selector labelSelector Delete all backups matching this label selector (default ) ``` ### Options inherited from parent commands diff --git a/pkg/cmd/cli/backup/delete.go b/pkg/cmd/cli/backup/delete.go index cc9bee64c..22f71df09 100644 --- a/pkg/cmd/cli/backup/delete.go +++ b/pkg/cmd/cli/backup/delete.go @@ -18,20 +18,24 @@ package backup import ( "bufio" - "errors" "fmt" "os" "strings" - "github.com/heptio/ark/pkg/apis/ark/v1" - "github.com/heptio/ark/pkg/backup" - clientset "github.com/heptio/ark/pkg/generated/clientset/versioned" + "github.com/pkg/errors" "github.com/spf13/cobra" "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/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. @@ -39,9 +43,23 @@ func NewDeleteCommand(f client.Factory, use string) *cobra.Command { o := &DeleteOptions{} c := &cobra.Command{ - Use: fmt.Sprintf("%s NAME", use), - Short: "Delete a backup", - Args: cobra.ExactArgs(1), + Use: fmt.Sprintf("%s [NAMES]", use), + Short: "Delete backups", + 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) { cmd.CheckError(o.Complete(f, args)) 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. type DeleteOptions struct { - Name string - Confirm bool + Names []string + All bool + Selector flag.LabelSelector + Confirm bool client clientset.Interface namespace string - backup *v1.Backup } // BindFlags binds options for this command to flags. func (o *DeleteOptions) BindFlags(flags *pflag.FlagSet) { 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. func (o *DeleteOptions) Complete(f client.Factory, args []string) error { - o.Name = args[0] - o.namespace = f.Namespace() client, err := f.Client() @@ -81,11 +100,7 @@ func (o *DeleteOptions) Complete(f client.Factory, args []string) error { } o.client = client - backup, err := o.client.ArkV1().Backups(f.Namespace()).Get(o.Name, metav1.GetOptions{}) - if err != nil { - return err - } - o.backup = backup + o.Names = args 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") } - if o.backup == nil { - return errors.New("backup is not set; unable to proceed") + var ( + 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 } +// 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. func (o *DeleteOptions) Run() error { if !o.Confirm && !getConfirmation() { @@ -110,14 +146,56 @@ func (o *DeleteOptions) Run() error { 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 { - return err + // get the list of backups to delete + 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) - return nil + if len(backups) == 0 { + 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 {