github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/backups/remove.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package backups
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/cmd"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/gnuflag"
    12  	"github.com/juju/utils/set"
    13  
    14  	"github.com/juju/juju/apiserver/params"
    15  	jujucmd "github.com/juju/juju/cmd"
    16  	"github.com/juju/juju/cmd/modelcmd"
    17  )
    18  
    19  const removeDoc = `
    20  remove-backup removes a backup from remote storage.
    21  `
    22  
    23  // NewRemoveCommand returns a command used to remove a
    24  // backup from remote storage.
    25  func NewRemoveCommand() cmd.Command {
    26  	return modelcmd.Wrap(&removeCommand{})
    27  }
    28  
    29  type removeCommand struct {
    30  	CommandBase
    31  	// ID refers to the backup to be removed.
    32  	ID         string
    33  	KeepLatest bool
    34  }
    35  
    36  // Info implements Command.Info.
    37  func (c *removeCommand) Info() *cmd.Info {
    38  	return jujucmd.Info(&cmd.Info{
    39  		Name:    "remove-backup",
    40  		Args:    "[--keep-latest|<ID>]",
    41  		Purpose: "Remove the specified backup from remote storage.",
    42  		Doc:     removeDoc,
    43  	})
    44  }
    45  
    46  // SetFlags implements Command.SetFlags.
    47  func (c *removeCommand) SetFlags(f *gnuflag.FlagSet) {
    48  	c.CommandBase.SetFlags(f)
    49  	f.BoolVar(&c.KeepLatest, "keep-latest", false,
    50  		"Remove all backups on remote storage except for the latest.")
    51  }
    52  
    53  // Init implements Command.Init.
    54  func (c *removeCommand) Init(args []string) error {
    55  	switch {
    56  	case len(args) == 0 && !c.KeepLatest:
    57  		return errors.New("missing ID or --keep-latest option")
    58  	case len(args) != 0:
    59  		id, args := args[0], args[1:]
    60  		if err := cmd.CheckEmpty(args); err != nil {
    61  			return errors.Trace(err)
    62  		}
    63  		c.ID = id
    64  	case c.KeepLatest:
    65  	default:
    66  		return errors.New("unknown error parsing arguments")
    67  	}
    68  	return nil
    69  }
    70  
    71  // Run implements Command.Run.
    72  func (c *removeCommand) Run(ctx *cmd.Context) error {
    73  	if c.Log != nil {
    74  		if err := c.Log.Start(ctx); err != nil {
    75  			return err
    76  		}
    77  	}
    78  
    79  	client, apiVersion, err := c.NewGetAPI()
    80  	if err != nil {
    81  		return errors.Trace(err)
    82  	}
    83  	defer client.Close()
    84  
    85  	if apiVersion < 2 && c.KeepLatest {
    86  		return errors.New("--keep-latest is not supported by this controller")
    87  	}
    88  	//ctx.Infof("apiversion %d", apiVersion)
    89  
    90  	ids := []string{}
    91  	var keep string
    92  	if c.KeepLatest {
    93  		list, err := client.List()
    94  		if err != nil {
    95  			return errors.Trace(err)
    96  		}
    97  
    98  		ids, keep, err = parseList(list.List)
    99  		switch {
   100  		case err != nil:
   101  			return errors.Trace(err)
   102  		case len(ids) > 0:
   103  			break
   104  		case keep != "":
   105  			ctx.Warningf("no backups to remove, %v most current", keep)
   106  			return nil
   107  		default:
   108  			ctx.Warningf("no backups to remove")
   109  			return nil
   110  		}
   111  	} else {
   112  		ids = append(ids, c.ID)
   113  	}
   114  	//ctx.Infof("%s; %+v", keep, ids)
   115  
   116  	results, err := client.Remove(ids...)
   117  	if err != nil {
   118  		return errors.Trace(err)
   119  	}
   120  
   121  	for i, err := range results {
   122  		if err.Error != nil {
   123  			// Some errors do not provide enough info, let's try to fix that here.
   124  			err.Error.Message = fmt.Sprintf("failed to remove %v: %s", ids[i], err.Error.Message)
   125  			continue
   126  		}
   127  		ctx.Infof("successfully removed: %v\n", ids[i])
   128  	}
   129  	if c.KeepLatest {
   130  		ctx.Infof("kept: %v", keep)
   131  	}
   132  	return errors.Trace(params.ErrorResults{results}.Combine())
   133  }
   134  
   135  // parseList returns a list of IDs to be removed and the one ID to be kept.
   136  // Keep the latest ID based on Started.
   137  func parseList(list []params.BackupsMetadataResult) ([]string, string, error) {
   138  	if len(list) == 0 {
   139  		return nil, "", nil
   140  	}
   141  	latest := list[0]
   142  	retList := set.NewStrings()
   143  	// Start looking for a new latest with the 2nd item in the slice.
   144  	for _, entry := range list[1:] {
   145  		if entry.Started.After(latest.Started) {
   146  			// Found a new latest, add the old one to the set
   147  			retList.Add(latest.ID)
   148  			latest = entry
   149  			continue
   150  		}
   151  		// Not the latest, add to the set
   152  		retList.Add(entry.ID)
   153  	}
   154  	return retList.SortedValues(), latest.ID, nil
   155  }