New database plugin API to reload by plugin name (#24472)

This commit is contained in:
Tom Proctor
2023-12-13 10:23:34 +00:00
committed by GitHub
parent 486df81934
commit dc5c3e8d97
5 changed files with 208 additions and 18 deletions

View File

@@ -9,6 +9,7 @@ import (
"fmt"
"net/url"
"sort"
"strings"
"github.com/fatih/structs"
"github.com/hashicorp/go-uuid"
@@ -94,13 +95,7 @@ func (b *databaseBackend) pathConnectionReset() framework.OperationFunc {
return logical.ErrorResponse(respErrEmptyName), nil
}
// Close plugin and delete the entry in the connections cache.
if err := b.ClearConnection(name); err != nil {
return nil, err
}
// Execute plugin again, we don't need the object so throw away.
if _, err := b.GetConnection(ctx, req.Storage, name); err != nil {
if err := b.reloadConnection(ctx, req.Storage, name); err != nil {
return nil, err
}
@@ -108,6 +103,103 @@ func (b *databaseBackend) pathConnectionReset() framework.OperationFunc {
}
}
func (b *databaseBackend) reloadConnection(ctx context.Context, storage logical.Storage, name string) error {
// Close plugin and delete the entry in the connections cache.
if err := b.ClearConnection(name); err != nil {
return err
}
// Execute plugin again, we don't need the object so throw away.
if _, err := b.GetConnection(ctx, storage, name); err != nil {
return err
}
return nil
}
// pathReloadPlugin reloads all connections using a named plugin.
func pathReloadPlugin(b *databaseBackend) *framework.Path {
return &framework.Path{
Pattern: fmt.Sprintf("reload/%s", framework.GenericNameRegex("plugin_name")),
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: operationPrefixDatabase,
OperationVerb: "reload",
OperationSuffix: "plugin",
},
Fields: map[string]*framework.FieldSchema{
"plugin_name": {
Type: framework.TypeString,
Description: "Name of the database plugin",
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: b.reloadPlugin(),
},
HelpSynopsis: pathReloadPluginHelpSyn,
HelpDescription: pathReloadPluginHelpDesc,
}
}
// reloadPlugin reloads all instances of a named plugin by closing the existing
// instances and creating new ones.
func (b *databaseBackend) reloadPlugin() framework.OperationFunc {
return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
pluginName := data.Get("plugin_name").(string)
if pluginName == "" {
return logical.ErrorResponse(respErrEmptyPluginName), nil
}
connNames, err := req.Storage.List(ctx, "config/")
if err != nil {
return nil, err
}
reloaded := []string{}
for _, connName := range connNames {
entry, err := req.Storage.Get(ctx, fmt.Sprintf("config/%s", connName))
if err != nil {
return nil, fmt.Errorf("failed to read connection configuration: %w", err)
}
if entry == nil {
continue
}
var config DatabaseConfig
if err := entry.DecodeJSON(&config); err != nil {
return nil, err
}
if config.PluginName == pluginName {
if err := b.reloadConnection(ctx, req.Storage, connName); err != nil {
var successfullyReloaded string
if len(reloaded) > 0 {
successfullyReloaded = fmt.Sprintf("successfully reloaded %d connection(s): %s; ",
len(reloaded),
strings.Join(reloaded, ", "))
}
return nil, fmt.Errorf("%sfailed to reload connection %q: %w", successfullyReloaded, connName, err)
}
reloaded = append(reloaded, connName)
}
}
resp := &logical.Response{
Data: map[string]interface{}{
"connections": reloaded,
"count": len(reloaded),
},
}
if len(reloaded) == 0 {
resp.AddWarning(fmt.Sprintf("no connections were found with plugin_name %q", pluginName))
}
return resp, nil
}
}
// pathConfigurePluginConnection returns a configured framework.Path setup to
// operate on plugins.
func pathConfigurePluginConnection(b *databaseBackend) *framework.Path {
@@ -551,3 +643,12 @@ const pathResetConnectionHelpDesc = `
This path resets the database connection by closing the existing database plugin
instance and running a new one.
`
const pathReloadPluginHelpSyn = `
Reloads all connections using a named database plugin.
`
const pathReloadPluginHelpDesc = `
This path resets each database connection using a named plugin by closing each
existing database plugin instance and running a new one.
`