Skip to content

Commit

Permalink
add support for bulk deletion to ark schedule delete
Browse files Browse the repository at this point in the history
refactor and move DeleteOptions struct and methods

unexport fields not used outside the package in DeleteOptions struct

refactor BindFlags() to work with name of command

fix constructor

Signed-off-by: Shubheksha Jalan <jshubheksha@gmail.com>
  • Loading branch information
shubheksha committed Oct 5, 2018
1 parent 889b220 commit 66bcbc0
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 192 deletions.
69 changes: 7 additions & 62 deletions pkg/cmd/cli/backup/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (

"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
Expand All @@ -32,13 +31,11 @@ import (
"github.com/heptio/ark/pkg/client"
"github.com/heptio/ark/pkg/cmd"
"github.com/heptio/ark/pkg/cmd/cli"
"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.
func NewDeleteCommand(f client.Factory, use string) *cobra.Command {
o := &DeleteOptions{}
o := cli.NewDeleteOptions("backup")

c := &cobra.Command{
Use: fmt.Sprintf("%s [NAMES]", use),
Expand All @@ -60,8 +57,8 @@ func NewDeleteCommand(f client.Factory, use string) *cobra.Command {
`,
Run: func(c *cobra.Command, args []string) {
cmd.CheckError(o.Complete(f, args))
cmd.CheckError(o.Validate(c, args, f))
cmd.CheckError(o.Run())
cmd.CheckError(o.Validate(c, f, args))
cmd.CheckError(Run(o))
},
}

Expand All @@ -70,60 +67,8 @@ func NewDeleteCommand(f client.Factory, use string) *cobra.Command {
return c
}

// DeleteOptions contains parameters for deleting a backup.
type DeleteOptions struct {
Names []string
All bool
Selector flag.LabelSelector
Confirm bool

client clientset.Interface
namespace string
}

// 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.namespace = f.Namespace()

client, err := f.Client()
if err != nil {
return err
}
o.client = client

o.Names = args

return nil
}

// Validate ensures all of the parameters have been filled in correctly.
func (o *DeleteOptions) Validate(c *cobra.Command, args []string, f client.Factory) error {
if o.client == nil {
return errors.New("Ark client is not set; unable to proceed")
}

var (
hasNames = len(o.Names) > 0
hasAll = o.All
hasSelector = o.Selector.LabelSelector != nil
)

if !cli.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
}

// Run performs the delete backup operation.
func (o *DeleteOptions) Run() error {
func Run(o *cli.DeleteOptions) error {
if !o.Confirm && !cli.GetConfirmation() {
// Don't do anything unless we get confirmation
return nil
Expand All @@ -138,7 +83,7 @@ func (o *DeleteOptions) Run() error {
switch {
case len(o.Names) > 0:
for _, name := range o.Names {
backup, err := o.client.ArkV1().Backups(o.namespace).Get(name, metav1.GetOptions{})
backup, err := o.Client.ArkV1().Backups(o.Namespace).Get(name, metav1.GetOptions{})
if err != nil {
errs = append(errs, errors.WithStack(err))
continue
Expand All @@ -152,7 +97,7 @@ func (o *DeleteOptions) Run() error {
selector = o.Selector.String()
}

res, err := o.client.ArkV1().Backups(o.namespace).List(metav1.ListOptions{LabelSelector: selector})
res, err := o.Client.ArkV1().Backups(o.Namespace).List(metav1.ListOptions{LabelSelector: selector})
if err != nil {
return errors.WithStack(err)
}
Expand All @@ -170,7 +115,7 @@ func (o *DeleteOptions) Run() error {
for _, b := range backups {
deleteRequest := backup.NewDeleteBackupRequest(b.Name, string(b.UID))

if _, err := o.client.ArkV1().DeleteBackupRequests(o.namespace).Create(deleteRequest); err != nil {
if _, err := o.Client.ArkV1().DeleteBackupRequests(o.Namespace).Create(deleteRequest); err != nil {
errs = append(errs, err)
continue
}
Expand Down
64 changes: 0 additions & 64 deletions pkg/cmd/cli/common.go

This file was deleted.

125 changes: 125 additions & 0 deletions pkg/cmd/cli/delete_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
Copyright 2018 the Heptio Ark contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cli

import (
"bufio"
"errors"
"fmt"
"os"
"strings"

"github.com/heptio/ark/pkg/client"
"github.com/heptio/ark/pkg/cmd/util/flag"
clientset "github.com/heptio/ark/pkg/generated/clientset/versioned"

"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

// DeleteOptions contains parameters used for deleting a restore.
type DeleteOptions struct {
Names []string
all bool
Selector flag.LabelSelector
Confirm bool
Client clientset.Interface
Namespace string
singularTypeName string
}

func NewDeleteOptions(singularTypeName string) *DeleteOptions {
o := &DeleteOptions{}
o.singularTypeName = singularTypeName
return o
}

// Complete fills in the correct values for all the options.
func (o *DeleteOptions) Complete(f client.Factory, args []string) error {
o.Namespace = f.Namespace()
client, err := f.Client()
if err != nil {
return err
}
o.Client = client
o.Names = args
return nil
}

// Validate validates the fields of the DeleteOptions struct.
func (o *DeleteOptions) Validate(c *cobra.Command, f client.Factory, args []string) error {
if o.Client == nil {
return errors.New("Ark client 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 restore name(s), the --all flag, or the --selector flag")
}

return nil
}

// 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 "+o.singularTypeName+"s")
flags.VarP(&o.Selector, "selector", "l", "Delete all "+o.singularTypeName+"s matching this label selector")
}

// GetConfirmation ensures that the user confirms the action before proceeding.
func GetConfirmation() bool {
reader := bufio.NewReader(os.Stdin)

for {
fmt.Printf("Are you sure you want to continue (Y/N)? ")

confirmation, err := reader.ReadString('\n')
if err != nil {
fmt.Fprintf(os.Stderr, "error reading user input: %v\n", err)
return false
}
confirmation = strings.TrimSpace(confirmation)
if len(confirmation) != 1 {
continue
}

switch strings.ToLower(confirmation) {
case "y":
return true
case "n":
return false
}
}
}

// 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
}
Loading

0 comments on commit 66bcbc0

Please sign in to comment.