-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Combined Database Backend with Plugins #2200
Changes from 151 commits
3d77a9a
ad17d11
bfbb104
cee3dc9
5e2cffc
e442917
fa8da4c
4d33509
354233f
c823ad0
1d23bbb
01300e0
78fdc2a
73200db
cd68899
00359cd
d4ea6c1
3766ab1
b63147b
72a878b
a0d207e
c111b02
143166b
a6ae4bd
5b05f62
3890f19
2ef1cbf
a878791
4043f53
404596e
ff6749b
2fdb342
2d6f36d
1be8136
9aaec25
73e553a
cab491f
a1b7246
e870e39
ca026c6
b2c4555
d93378b
6de5cfa
0c562fa
947fd66
8ef78f0
1d3d3b7
2b08521
ac519ab
b54e1cd
1faa5fc
8e3cb50
df944f2
73a2cdf
f6b45bd
485b331
8f88452
8a2e29c
0da69cf
8e77bd9
9ae5a2a
3c1c388
73f66f8
64efc50
f54c4de
de36d61
da4d9a8
8f75c30
8c264c6
0e08279
cb844b5
1bc0243
c9dc7b8
f2401c0
03e2bcb
4c75326
33d66f3
b20c177
07f3f4f
be50cba
ea41734
1f6bf29
370dd2d
8b7fa73
afc5be1
d9ce189
62cae4a
f1fa617
a3f6580
9abc31e
3ceb7b6
c5d5abe
2faa08d
f4ef3df
707e6ca
4cda9ea
4c306bd
7e3f5e6
4315e68
f6b96cc
194695f
1971d65
57f78c4
630962b
6741811
22612ad
58b0bbd
e187576
6131bdd
37aacba
d8dbfc6
dc9740d
cb13786
6b05047
f92d686
2e2d382
230a36c
47df4ac
766b909
6684e5c
445a0e3
f3e7ad7
b87f8a1
6ca436c
66630f6
d68f283
885398e
31541b7
6ddfe9a
7f92c5f
d300c23
1df8ec9
6d4f1aa
30a02ed
6e7696b
fe86f06
dc5979e
d230446
60753dc
2be2e4c
85967cb
78b27fa
799cd3c
311acb3
f424a9a
c381b00
657826d
5b8ce92
3ca266b
3fcf1ad
a3619c4
2af2b85
9e28b03
c825362
55f1f51
886f873
17bea65
fcd4f90
2e82e00
65b7bba
3f7ea0d
c48b7fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package database | ||
|
||
import ( | ||
"fmt" | ||
"net/rpc" | ||
"strings" | ||
"sync" | ||
|
||
log "github.com/mgutz/logxi/v1" | ||
|
||
"github.com/hashicorp/vault/builtin/logical/database/dbplugin" | ||
"github.com/hashicorp/vault/logical" | ||
"github.com/hashicorp/vault/logical/framework" | ||
) | ||
|
||
const databaseConfigPath = "database/config/" | ||
|
||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) { | ||
return Backend(conf).Setup(conf) | ||
} | ||
|
||
func Backend(conf *logical.BackendConfig) *databaseBackend { | ||
var b databaseBackend | ||
b.Backend = &framework.Backend{ | ||
Help: strings.TrimSpace(backendHelp), | ||
|
||
Paths: []*framework.Path{ | ||
pathConfigurePluginConnection(&b), | ||
pathListRoles(&b), | ||
pathRoles(&b), | ||
pathCredsCreate(&b), | ||
pathResetConnection(&b), | ||
}, | ||
|
||
Secrets: []*framework.Secret{ | ||
secretCreds(&b), | ||
}, | ||
|
||
Clean: b.closeAllDBs, | ||
|
||
Invalidate: b.invalidate, | ||
} | ||
|
||
b.logger = conf.Logger | ||
b.connections = make(map[string]dbplugin.Database) | ||
return &b | ||
} | ||
|
||
type databaseBackend struct { | ||
connections map[string]dbplugin.Database | ||
logger log.Logger | ||
|
||
*framework.Backend | ||
sync.RWMutex | ||
} | ||
|
||
// resetAllDBs closes all connections from all database types | ||
func (b *databaseBackend) closeAllDBs() { | ||
b.Lock() | ||
defer b.Unlock() | ||
|
||
for _, db := range b.connections { | ||
db.Close() | ||
} | ||
|
||
b.connections = make(map[string]dbplugin.Database) | ||
} | ||
|
||
// This function is used to retrieve a database object either from the cached | ||
// connection map. The caller of this function needs to hold the backend's read | ||
// lock. | ||
func (b *databaseBackend) getDBObj(name string) (dbplugin.Database, bool) { | ||
db, ok := b.connections[name] | ||
return db, ok | ||
} | ||
|
||
// This function creates a new db object from the stored configuration and | ||
// caches it in the connections map. The caller of this function needs to hold | ||
// the backend's write lock | ||
func (b *databaseBackend) createDBObj(s logical.Storage, name string) (dbplugin.Database, error) { | ||
db, ok := b.connections[name] | ||
if ok { | ||
return db, nil | ||
} | ||
|
||
config, err := b.DatabaseConfig(s, name) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
db, err = dbplugin.PluginFactory(config.PluginName, b.System(), b.logger) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
err = db.Initialize(config.ConnectionDetails, true) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
b.connections[name] = db | ||
|
||
return db, nil | ||
} | ||
|
||
func (b *databaseBackend) DatabaseConfig(s logical.Storage, name string) (*DatabaseConfig, error) { | ||
entry, err := s.Get(fmt.Sprintf("config/%s", name)) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to read connection configuration with name: %s", name) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should |
||
} | ||
if entry == nil { | ||
return nil, fmt.Errorf("failed to find entry for connection with name: %s", name) | ||
} | ||
|
||
var config DatabaseConfig | ||
if err := entry.DecodeJSON(&config); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &config, nil | ||
} | ||
|
||
func (b *databaseBackend) Role(s logical.Storage, n string) (*roleEntry, error) { | ||
entry, err := s.Get("role/" + n) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you update |
||
if err != nil { | ||
return nil, err | ||
} | ||
if entry == nil { | ||
return nil, nil | ||
} | ||
|
||
var result roleEntry | ||
if err := entry.DecodeJSON(&result); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &result, nil | ||
} | ||
|
||
func (b *databaseBackend) invalidate(key string) { | ||
b.Lock() | ||
defer b.Unlock() | ||
|
||
switch { | ||
case strings.HasPrefix(key, databaseConfigPath): | ||
name := strings.TrimPrefix(key, databaseConfigPath) | ||
b.clearConnection(name) | ||
} | ||
} | ||
|
||
// clearConnection closes the database connection and | ||
// removes it from the b.connections map. | ||
func (b *databaseBackend) clearConnection(name string) { | ||
db, ok := b.connections[name] | ||
if ok { | ||
db.Close() | ||
delete(b.connections, name) | ||
} | ||
} | ||
|
||
func (b *databaseBackend) closeIfShutdown(name string, err error) { | ||
// Plugin has shutdown, close it so next call can reconnect. | ||
if err == rpc.ErrShutdown { | ||
b.Lock() | ||
b.clearConnection(name) | ||
b.Unlock() | ||
} | ||
} | ||
|
||
const backendHelp = ` | ||
The database backend supports using many different databases | ||
as secret backends, including but not limited to: | ||
cassandra, msslq, mysql, postgres | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s/msslq/mssql/ |
||
|
||
After mounting this backend, configure it using the endpoints within | ||
the "database/config/" path. | ||
` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know we don't lint but s/resetAllDBs/closeAllDBs