add bulk deletion support to ark backup delete
Signed-off-by: Steve Kriss <steve@heptio.com>pull/745/head
parent
1f7a4a1665
commit
5acccaa739
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue