1package dependency
2
3import (
4	"fmt"
5	"log"
6	"net/url"
7	"regexp"
8	"strings"
9
10	"github.com/pkg/errors"
11)
12
13var (
14	// Ensure implements
15	_ Dependency = (*KVKeysQuery)(nil)
16
17	// KVKeysQueryRe is the regular expression to use.
18	KVKeysQueryRe = regexp.MustCompile(`\A` + prefixRe + dcRe + `\z`)
19)
20
21// KVKeysQuery queries the KV store for a single key.
22type KVKeysQuery struct {
23	stopCh chan struct{}
24
25	dc     string
26	prefix string
27}
28
29// NewKVKeysQuery parses a string into a dependency.
30func NewKVKeysQuery(s string) (*KVKeysQuery, error) {
31	if s != "" && !KVKeysQueryRe.MatchString(s) {
32		return nil, fmt.Errorf("kv.keys: invalid format: %q", s)
33	}
34
35	m := regexpMatch(KVKeysQueryRe, s)
36	return &KVKeysQuery{
37		stopCh: make(chan struct{}, 1),
38		dc:     m["dc"],
39		prefix: m["prefix"],
40	}, nil
41}
42
43// Fetch queries the Consul API defined by the given client.
44func (d *KVKeysQuery) Fetch(clients *ClientSet, opts *QueryOptions) (interface{}, *ResponseMetadata, error) {
45	select {
46	case <-d.stopCh:
47		return nil, nil, ErrStopped
48	default:
49	}
50
51	opts = opts.Merge(&QueryOptions{
52		Datacenter: d.dc,
53	})
54
55	log.Printf("[TRACE] %s: GET %s", d, &url.URL{
56		Path:     "/v1/kv/" + d.prefix,
57		RawQuery: opts.String(),
58	})
59
60	list, qm, err := clients.Consul().KV().Keys(d.prefix, "", opts.ToConsulOpts())
61	if err != nil {
62		return nil, nil, errors.Wrap(err, d.String())
63	}
64
65	keys := make([]string, len(list))
66	for i, v := range list {
67		v = strings.TrimPrefix(v, d.prefix)
68		v = strings.TrimLeft(v, "/")
69		keys[i] = v
70	}
71
72	log.Printf("[TRACE] %s: returned %d results", d, len(list))
73
74	rm := &ResponseMetadata{
75		LastIndex:   qm.LastIndex,
76		LastContact: qm.LastContact,
77	}
78
79	return keys, rm, nil
80}
81
82// CanShare returns a boolean if this dependency is shareable.
83func (d *KVKeysQuery) CanShare() bool {
84	return true
85}
86
87// String returns the human-friendly version of this dependency.
88func (d *KVKeysQuery) String() string {
89	prefix := d.prefix
90	if d.dc != "" {
91		prefix = prefix + "@" + d.dc
92	}
93	return fmt.Sprintf("kv.keys(%s)", prefix)
94}
95
96// Stop halts the dependency's fetch function.
97func (d *KVKeysQuery) Stop() {
98	close(d.stopCh)
99}
100
101// Type returns the type of this dependency.
102func (d *KVKeysQuery) Type() Type {
103	return TypeConsul
104}
105