1package dependency
2
3import (
4	"fmt"
5	"log"
6	"net/url"
7	"regexp"
8
9	"github.com/pkg/errors"
10)
11
12var (
13	// Ensure implements
14	_ Dependency = (*KVGetQuery)(nil)
15
16	// KVGetQueryRe is the regular expression to use.
17	KVGetQueryRe = regexp.MustCompile(`\A` + keyRe + dcRe + `\z`)
18)
19
20// KVGetQuery queries the KV store for a single key.
21type KVGetQuery struct {
22	stopCh chan struct{}
23
24	dc    string
25	key   string
26	block bool
27}
28
29// NewKVGetQuery parses a string into a dependency.
30func NewKVGetQuery(s string) (*KVGetQuery, error) {
31	if s != "" && !KVGetQueryRe.MatchString(s) {
32		return nil, fmt.Errorf("kv.get: invalid format: %q", s)
33	}
34
35	m := regexpMatch(KVGetQueryRe, s)
36	return &KVGetQuery{
37		stopCh: make(chan struct{}, 1),
38		dc:     m["dc"],
39		key:    m["key"],
40	}, nil
41}
42
43// Fetch queries the Consul API defined by the given client.
44func (d *KVGetQuery) 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.key,
57		RawQuery: opts.String(),
58	})
59
60	pair, qm, err := clients.Consul().KV().Get(d.key, opts.ToConsulOpts())
61	if err != nil {
62		return nil, nil, errors.Wrap(err, d.String())
63	}
64
65	rm := &ResponseMetadata{
66		LastIndex:   qm.LastIndex,
67		LastContact: qm.LastContact,
68		Block:       d.block,
69	}
70
71	if pair == nil {
72		log.Printf("[TRACE] %s: returned nil", d)
73		return nil, rm, nil
74	}
75
76	value := string(pair.Value)
77	log.Printf("[TRACE] %s: returned %q", d, value)
78	return value, rm, nil
79}
80
81// EnableBlocking turns this into a blocking KV query.
82func (d *KVGetQuery) EnableBlocking() {
83	d.block = true
84}
85
86// CanShare returns a boolean if this dependency is shareable.
87func (d *KVGetQuery) CanShare() bool {
88	return true
89}
90
91// String returns the human-friendly version of this dependency.
92func (d *KVGetQuery) String() string {
93	key := d.key
94	if d.dc != "" {
95		key = key + "@" + d.dc
96	}
97
98	if d.block {
99		return fmt.Sprintf("kv.block(%s)", key)
100	}
101	return fmt.Sprintf("kv.get(%s)", key)
102}
103
104// Stop halts the dependency's fetch function.
105func (d *KVGetQuery) Stop() {
106	close(d.stopCh)
107}
108
109// Type returns the type of this dependency.
110func (d *KVGetQuery) Type() Type {
111	return TypeConsul
112}
113