1package get 2 3import ( 4 "bytes" 5 "encoding/base64" 6 "flag" 7 "fmt" 8 "io" 9 "text/tabwriter" 10 11 "github.com/hashicorp/consul/api" 12 "github.com/hashicorp/consul/command/flags" 13 "github.com/mitchellh/cli" 14) 15 16func New(ui cli.Ui) *cmd { 17 c := &cmd{UI: ui} 18 c.init() 19 return c 20} 21 22type cmd struct { 23 UI cli.Ui 24 flags *flag.FlagSet 25 http *flags.HTTPFlags 26 help string 27 base64encode bool 28 detailed bool 29 keys bool 30 recurse bool 31 separator string 32} 33 34func (c *cmd) init() { 35 c.flags = flag.NewFlagSet("", flag.ContinueOnError) 36 c.flags.BoolVar(&c.base64encode, "base64", false, 37 "Base64 encode the value. The default value is false.") 38 c.flags.BoolVar(&c.detailed, "detailed", false, 39 "Provide additional metadata about the key in addition to the value such "+ 40 "as the ModifyIndex and any flags that may have been set on the key. "+ 41 "The default value is false.") 42 c.flags.BoolVar(&c.keys, "keys", false, 43 "List keys which start with the given prefix, but not their values. "+ 44 "This is especially useful if you only need the key names themselves. "+ 45 "This option is commonly combined with the -separator option. The default "+ 46 "value is false.") 47 c.flags.BoolVar(&c.recurse, "recurse", false, 48 "Recursively look at all keys prefixed with the given path. The default "+ 49 "value is false.") 50 c.flags.StringVar(&c.separator, "separator", "/", 51 "String to use as a separator between keys. The default value is \"/\", "+ 52 "but this option is only taken into account when paired with the -keys flag.") 53 54 c.http = &flags.HTTPFlags{} 55 flags.Merge(c.flags, c.http.ClientFlags()) 56 flags.Merge(c.flags, c.http.ServerFlags()) 57 flags.Merge(c.flags, c.http.NamespaceFlags()) 58 c.help = flags.Usage(help, c.flags) 59} 60 61func (c *cmd) Run(args []string) int { 62 if err := c.flags.Parse(args); err != nil { 63 return 1 64 } 65 66 key := "" 67 68 // Check for arg validation 69 args = c.flags.Args() 70 switch len(args) { 71 case 0: 72 key = "" 73 case 1: 74 key = args[0] 75 default: 76 c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args))) 77 return 1 78 } 79 80 // This is just a "nice" thing to do. Since pairs cannot start with a /, but 81 // users will likely put "/" or "/foo", lets go ahead and strip that for them 82 // here. 83 if len(key) > 0 && key[0] == '/' { 84 key = key[1:] 85 } 86 87 // If the key is empty and we are not doing a recursive or key-based lookup, 88 // this is an error. 89 if key == "" && !(c.recurse || c.keys) { 90 c.UI.Error("Error! Missing KEY argument") 91 return 1 92 } 93 94 // Create and test the HTTP client 95 client, err := c.http.APIClient() 96 if err != nil { 97 c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err)) 98 return 1 99 } 100 101 switch { 102 case c.keys: 103 keys, _, err := client.KV().Keys(key, c.separator, &api.QueryOptions{ 104 AllowStale: c.http.Stale(), 105 }) 106 if err != nil { 107 c.UI.Error(fmt.Sprintf("Error querying Consul agent: %s", err)) 108 return 1 109 } 110 111 for _, k := range keys { 112 c.UI.Info(string(k)) 113 } 114 115 return 0 116 case c.recurse: 117 pairs, _, err := client.KV().List(key, &api.QueryOptions{ 118 AllowStale: c.http.Stale(), 119 }) 120 if err != nil { 121 c.UI.Error(fmt.Sprintf("Error querying Consul agent: %s", err)) 122 return 1 123 } 124 125 for i, pair := range pairs { 126 if c.detailed { 127 var b bytes.Buffer 128 if err := prettyKVPair(&b, pair, c.base64encode); err != nil { 129 c.UI.Error(fmt.Sprintf("Error rendering KV pair: %s", err)) 130 return 1 131 } 132 133 c.UI.Info(b.String()) 134 135 if i < len(pairs)-1 { 136 c.UI.Info("") 137 } 138 } else { 139 if c.base64encode { 140 c.UI.Info(fmt.Sprintf("%s:%s", pair.Key, base64.StdEncoding.EncodeToString(pair.Value))) 141 } else { 142 c.UI.Info(fmt.Sprintf("%s:%s", pair.Key, pair.Value)) 143 } 144 } 145 } 146 147 return 0 148 default: 149 pair, _, err := client.KV().Get(key, &api.QueryOptions{ 150 AllowStale: c.http.Stale(), 151 }) 152 if err != nil { 153 c.UI.Error(fmt.Sprintf("Error querying Consul agent: %s", err)) 154 return 1 155 } 156 157 if pair == nil { 158 c.UI.Error(fmt.Sprintf("Error! No key exists at: %s", key)) 159 return 1 160 } 161 162 if c.detailed { 163 var b bytes.Buffer 164 if err := prettyKVPair(&b, pair, c.base64encode); err != nil { 165 c.UI.Error(fmt.Sprintf("Error rendering KV pair: %s", err)) 166 return 1 167 } 168 169 c.UI.Info(b.String()) 170 return 0 171 } 172 173 if c.base64encode { 174 c.UI.Info(base64.StdEncoding.EncodeToString(pair.Value)) 175 } else { 176 c.UI.Info(string(pair.Value)) 177 } 178 return 0 179 } 180} 181 182func (c *cmd) Synopsis() string { 183 return synopsis 184} 185 186func (c *cmd) Help() string { 187 return c.help 188} 189 190func prettyKVPair(w io.Writer, pair *api.KVPair, base64EncodeValue bool) error { 191 tw := tabwriter.NewWriter(w, 0, 2, 6, ' ', 0) 192 fmt.Fprintf(tw, "CreateIndex\t%d\n", pair.CreateIndex) 193 fmt.Fprintf(tw, "Flags\t%d\n", pair.Flags) 194 fmt.Fprintf(tw, "Key\t%s\n", pair.Key) 195 fmt.Fprintf(tw, "LockIndex\t%d\n", pair.LockIndex) 196 fmt.Fprintf(tw, "ModifyIndex\t%d\n", pair.ModifyIndex) 197 if pair.Session == "" { 198 fmt.Fprint(tw, "Session\t-\n") 199 } else { 200 fmt.Fprintf(tw, "Session\t%s\n", pair.Session) 201 } 202 if pair.Namespace != "" { 203 fmt.Fprintf(tw, "Namespace\t%s\n", pair.Namespace) 204 } 205 if base64EncodeValue { 206 fmt.Fprintf(tw, "Value\t%s", base64.StdEncoding.EncodeToString(pair.Value)) 207 } else { 208 fmt.Fprintf(tw, "Value\t%s", pair.Value) 209 } 210 return tw.Flush() 211} 212 213const synopsis = "Retrieves or lists data from the KV store" 214const help = ` 215Usage: consul kv get [options] [KEY_OR_PREFIX] 216 217 Retrieves the value from Consul's key-value store at the given key name. If no 218 key exists with that name, an error is returned. If a key exists with that 219 name but has no data, nothing is returned. If the name or prefix is omitted, 220 it defaults to "" which is the root of the key-value store. 221 222 To retrieve the value for the key named "foo" in the key-value store: 223 224 $ consul kv get foo 225 226 This will return the original, raw value stored in Consul. To view detailed 227 information about the key, specify the "-detailed" flag. This will output all 228 known metadata about the key including ModifyIndex and any user-supplied 229 flags: 230 231 $ consul kv get -detailed foo 232 233 To treat the path as a prefix and list all keys which start with the given 234 prefix, specify the "-recurse" flag: 235 236 $ consul kv get -recurse foo 237 238 This will return all key-value pairs. To just list the keys which start with 239 the specified prefix, use the "-keys" option instead: 240 241 $ consul kv get -keys foo 242 243 For a full list of options and examples, please see the Consul documentation. 244` 245