1package dependency 2 3import ( 4 "encoding/gob" 5 "fmt" 6 "log" 7 "net/url" 8 "regexp" 9 "strings" 10 11 "github.com/pkg/errors" 12) 13 14var ( 15 // Ensure implements 16 _ Dependency = (*KVListQuery)(nil) 17 18 // KVListQueryRe is the regular expression to use. 19 KVListQueryRe = regexp.MustCompile(`\A` + prefixRe + dcRe + `\z`) 20) 21 22func init() { 23 gob.Register([]*KeyPair{}) 24} 25 26// KeyPair is a simple Key-Value pair 27type KeyPair struct { 28 Path string 29 Key string 30 Value string 31 32 // Lesser-used, but still valuable keys from api.KV 33 CreateIndex uint64 34 ModifyIndex uint64 35 LockIndex uint64 36 Flags uint64 37 Session string 38} 39 40// KVListQuery queries the KV store for a single key. 41type KVListQuery struct { 42 stopCh chan struct{} 43 44 dc string 45 prefix string 46} 47 48// NewKVListQuery parses a string into a dependency. 49func NewKVListQuery(s string) (*KVListQuery, error) { 50 if s != "" && !KVListQueryRe.MatchString(s) { 51 return nil, fmt.Errorf("kv.list: invalid format: %q", s) 52 } 53 54 m := regexpMatch(KVListQueryRe, s) 55 return &KVListQuery{ 56 stopCh: make(chan struct{}, 1), 57 dc: m["dc"], 58 prefix: m["prefix"], 59 }, nil 60} 61 62// Fetch queries the Consul API defined by the given client. 63func (d *KVListQuery) Fetch(clients *ClientSet, opts *QueryOptions) (interface{}, *ResponseMetadata, error) { 64 select { 65 case <-d.stopCh: 66 return nil, nil, ErrStopped 67 default: 68 } 69 70 opts = opts.Merge(&QueryOptions{ 71 Datacenter: d.dc, 72 }) 73 74 log.Printf("[TRACE] %s: GET %s", d, &url.URL{ 75 Path: "/v1/kv/" + d.prefix, 76 RawQuery: opts.String(), 77 }) 78 79 list, qm, err := clients.Consul().KV().List(d.prefix, opts.ToConsulOpts()) 80 if err != nil { 81 return nil, nil, errors.Wrap(err, d.String()) 82 } 83 84 log.Printf("[TRACE] %s: returned %d pairs", d, len(list)) 85 86 pairs := make([]*KeyPair, 0, len(list)) 87 for _, pair := range list { 88 key := strings.TrimPrefix(pair.Key, d.prefix) 89 key = strings.TrimLeft(key, "/") 90 91 pairs = append(pairs, &KeyPair{ 92 Path: pair.Key, 93 Key: key, 94 Value: string(pair.Value), 95 CreateIndex: pair.CreateIndex, 96 ModifyIndex: pair.ModifyIndex, 97 LockIndex: pair.LockIndex, 98 Flags: pair.Flags, 99 Session: pair.Session, 100 }) 101 } 102 103 rm := &ResponseMetadata{ 104 LastIndex: qm.LastIndex, 105 LastContact: qm.LastContact, 106 } 107 108 return pairs, rm, nil 109} 110 111// CanShare returns a boolean if this dependency is shareable. 112func (d *KVListQuery) CanShare() bool { 113 return true 114} 115 116// String returns the human-friendly version of this dependency. 117func (d *KVListQuery) String() string { 118 prefix := d.prefix 119 if d.dc != "" { 120 prefix = prefix + "@" + d.dc 121 } 122 return fmt.Sprintf("kv.list(%s)", prefix) 123} 124 125// Stop halts the dependency's fetch function. 126func (d *KVListQuery) Stop() { 127 close(d.stopCh) 128} 129 130// Type returns the type of this dependency. 131func (d *KVListQuery) Type() Type { 132 return TypeConsul 133} 134