1package command 2 3import ( 4 "fmt" 5 "strings" 6 7 "github.com/mitchellh/cli" 8 "github.com/posener/complete" 9) 10 11var _ cli.Command = (*KVUndeleteCommand)(nil) 12var _ cli.CommandAutocomplete = (*KVUndeleteCommand)(nil) 13 14type KVUndeleteCommand struct { 15 *BaseCommand 16 17 flagVersions []string 18} 19 20func (c *KVUndeleteCommand) Synopsis() string { 21 return "Undeletes versions in the KV store" 22} 23 24func (c *KVUndeleteCommand) Help() string { 25 helpText := ` 26Usage: vault kv undelete [options] KEY 27 28 Undeletes the data for the provided version and path in the key-value store. 29 This restores the data, allowing it to be returned on get requests. 30 31 To undelete version 3 of key "foo": 32 33 $ vault kv undelete -versions=3 secret/foo 34 35 Additional flags and more advanced use cases are detailed below. 36 37` + c.Flags().Help() 38 return strings.TrimSpace(helpText) 39} 40 41func (c *KVUndeleteCommand) Flags() *FlagSets { 42 set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat) 43 44 // Common Options 45 f := set.NewFlagSet("Common Options") 46 47 f.StringSliceVar(&StringSliceVar{ 48 Name: "versions", 49 Target: &c.flagVersions, 50 Default: nil, 51 Usage: `Specifies the version numbers to undelete.`, 52 }) 53 54 return set 55} 56 57func (c *KVUndeleteCommand) AutocompleteArgs() complete.Predictor { 58 return nil 59} 60 61func (c *KVUndeleteCommand) AutocompleteFlags() complete.Flags { 62 return c.Flags().Completions() 63} 64 65func (c *KVUndeleteCommand) Run(args []string) int { 66 f := c.Flags() 67 68 if err := f.Parse(args); err != nil { 69 c.UI.Error(err.Error()) 70 return 1 71 } 72 73 args = f.Args() 74 switch { 75 case len(args) < 1: 76 c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1, got %d)", len(args))) 77 return 1 78 case len(args) > 1: 79 c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args))) 80 return 1 81 } 82 83 if len(c.flagVersions) == 0 { 84 c.UI.Error("No versions provided, use the \"-versions\" flag to specify the version to undelete.") 85 return 1 86 } 87 88 client, err := c.Client() 89 if err != nil { 90 c.UI.Error(err.Error()) 91 return 2 92 } 93 94 path := sanitizePath(args[0]) 95 mountPath, v2, err := isKVv2(path, client) 96 if err != nil { 97 c.UI.Error(err.Error()) 98 return 2 99 } 100 if !v2 { 101 c.UI.Error("Undelete not supported on KV Version 1") 102 return 1 103 } 104 105 path = addPrefixToVKVPath(path, mountPath, "undelete") 106 data := map[string]interface{}{ 107 "versions": kvParseVersionsFlags(c.flagVersions), 108 } 109 110 secret, err := client.Logical().Write(path, data) 111 if err != nil { 112 c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err)) 113 if secret != nil { 114 OutputSecret(c.UI, secret) 115 } 116 return 2 117 } 118 if secret == nil { 119 // Don't output anything unless using the "table" format 120 if Format(c.UI) == "table" { 121 c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path)) 122 } 123 return 0 124 } 125 126 return OutputSecret(c.UI, secret) 127} 128