forked from hashicorp/vault
-
Notifications
You must be signed in to change notification settings - Fork 0
/
backend.go
145 lines (117 loc) · 3.02 KB
/
backend.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package rabbitmq
import (
"context"
"fmt"
"strings"
"sync"
"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
"github.com/michaelklishin/rabbit-hole"
)
// Factory creates and configures the backend
func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
b := Backend()
if err := b.Setup(ctx, conf); err != nil {
return nil, err
}
return b, nil
}
// Creates a new backend with all the paths and secrets belonging to it
func Backend() *backend {
var b backend
b.Backend = &framework.Backend{
Help: strings.TrimSpace(backendHelp),
PathsSpecial: &logical.Paths{
SealWrapStorage: []string{
"config/connection",
},
},
Paths: []*framework.Path{
pathConfigConnection(&b),
pathConfigLease(&b),
pathListRoles(&b),
pathCreds(&b),
pathRoles(&b),
},
Secrets: []*framework.Secret{
secretCreds(&b),
},
Clean: b.resetClient,
Invalidate: b.invalidate,
BackendType: logical.TypeLogical,
}
return &b
}
type backend struct {
*framework.Backend
client *rabbithole.Client
lock sync.RWMutex
}
// DB returns the database connection.
func (b *backend) Client(ctx context.Context, s logical.Storage) (*rabbithole.Client, error) {
b.lock.RLock()
// If we already have a client, return it
if b.client != nil {
b.lock.RUnlock()
return b.client, nil
}
b.lock.RUnlock()
// Otherwise, attempt to make connection
entry, err := s.Get(ctx, "config/connection")
if err != nil {
return nil, err
}
if entry == nil {
return nil, fmt.Errorf("configure the client connection with config/connection first")
}
var connConfig connectionConfig
if err := entry.DecodeJSON(&connConfig); err != nil {
return nil, err
}
b.lock.Lock()
defer b.lock.Unlock()
// If the client was creted during the lock switch, return it
if b.client != nil {
return b.client, nil
}
b.client, err = rabbithole.NewClient(connConfig.URI, connConfig.Username, connConfig.Password)
if err != nil {
return nil, err
}
// Use a default pooled transport so there would be no leaked file descriptors
b.client.SetTransport(cleanhttp.DefaultPooledTransport())
return b.client, nil
}
// resetClient forces a connection next time Client() is called.
func (b *backend) resetClient(_ context.Context) {
b.lock.Lock()
defer b.lock.Unlock()
b.client = nil
}
func (b *backend) invalidate(ctx context.Context, key string) {
switch key {
case "config/connection":
b.resetClient(ctx)
}
}
// Lease returns the lease information
func (b *backend) Lease(ctx context.Context, s logical.Storage) (*configLease, error) {
entry, err := s.Get(ctx, "config/lease")
if err != nil {
return nil, err
}
if entry == nil {
return nil, nil
}
var result configLease
if err := entry.DecodeJSON(&result); err != nil {
return nil, err
}
return &result, nil
}
const backendHelp = `
The RabbitMQ backend dynamically generates RabbitMQ users.
After mounting this backend, configure it using the endpoints within
the "config/" path.
`