1package dependency
2
3import (
4	"io/ioutil"
5	"log"
6	"os"
7	"strings"
8	"time"
9
10	"github.com/pkg/errors"
11)
12
13var (
14	// Ensure implements
15	_ Dependency = (*VaultAgentTokenQuery)(nil)
16)
17
18const (
19	// VaultAgentTokenSleepTime is the amount of time to sleep between queries, since
20	// the fsnotify library is not compatible with solaris and other OSes yet.
21	VaultAgentTokenSleepTime = 15 * time.Second
22)
23
24// VaultAgentTokenQuery is the dependency to Vault Agent token
25type VaultAgentTokenQuery struct {
26	stopCh chan struct{}
27	path   string
28	stat   os.FileInfo
29}
30
31// NewVaultAgentTokenQuery creates a new dependency.
32func NewVaultAgentTokenQuery(path string) (*VaultAgentTokenQuery, error) {
33	return &VaultAgentTokenQuery{
34		stopCh: make(chan struct{}, 1),
35		path:   path,
36	}, nil
37}
38
39// Fetch retrieves this dependency and returns the result or any errors that
40// occur in the process.
41func (d *VaultAgentTokenQuery) Fetch(clients *ClientSet, opts *QueryOptions) (interface{}, *ResponseMetadata, error) {
42	log.Printf("[TRACE] %s: READ %s", d, d.path)
43
44	select {
45	case <-d.stopCh:
46		log.Printf("[TRACE] %s: stopped", d)
47		return "", nil, ErrStopped
48	case r := <-d.watch(d.stat):
49		if r.err != nil {
50			return "", nil, errors.Wrap(r.err, d.String())
51		}
52
53		log.Printf("[TRACE] %s: reported change", d)
54
55		token, err := ioutil.ReadFile(d.path)
56		if err != nil {
57			return "", nil, errors.Wrap(err, d.String())
58		}
59
60		d.stat = r.stat
61		clients.Vault().SetToken(strings.TrimSpace(string(token)))
62	}
63
64	return respWithMetadata("")
65}
66
67// CanShare returns if this dependency is sharable.
68func (d *VaultAgentTokenQuery) CanShare() bool {
69	return false
70}
71
72// Stop halts the dependency's fetch function.
73func (d *VaultAgentTokenQuery) Stop() {
74	close(d.stopCh)
75}
76
77// String returns the human-friendly version of this dependency.
78func (d *VaultAgentTokenQuery) String() string {
79	return "vault-agent.token"
80}
81
82// Type returns the type of this dependency.
83func (d *VaultAgentTokenQuery) Type() Type {
84	return TypeVault
85}
86
87// watch watches the file for changes
88func (d *VaultAgentTokenQuery) watch(lastStat os.FileInfo) <-chan *watchResult {
89	ch := make(chan *watchResult, 1)
90
91	go func(lastStat os.FileInfo) {
92		for {
93			stat, err := os.Stat(d.path)
94			if err != nil {
95				select {
96				case <-d.stopCh:
97					return
98				case ch <- &watchResult{err: err}:
99					return
100				}
101			}
102
103			changed := lastStat == nil ||
104				lastStat.Size() != stat.Size() ||
105				lastStat.ModTime() != stat.ModTime()
106
107			if changed {
108				select {
109				case <-d.stopCh:
110					return
111				case ch <- &watchResult{stat: stat}:
112					return
113				}
114			}
115
116			time.Sleep(VaultAgentTokenSleepTime)
117		}
118	}(lastStat)
119
120	return ch
121}
122