|
9 | 9 | "fmt"
|
10 | 10 | "net/url"
|
11 | 11 | "sort"
|
| 12 | + "strings" |
12 | 13 |
|
13 | 14 | "github.com/fatih/structs"
|
14 | 15 | "github.com/hashicorp/go-uuid"
|
@@ -94,17 +95,108 @@ func (b *databaseBackend) pathConnectionReset() framework.OperationFunc {
|
94 | 95 | return logical.ErrorResponse(respErrEmptyName), nil
|
95 | 96 | }
|
96 | 97 |
|
97 |
| - // Close plugin and delete the entry in the connections cache. |
98 |
| - if err := b.ClearConnection(name); err != nil { |
| 98 | + if err := b.reloadConnection(ctx, req.Storage, name); err != nil { |
99 | 99 | return nil, err
|
100 | 100 | }
|
101 | 101 |
|
102 |
| - // Execute plugin again, we don't need the object so throw away. |
103 |
| - if _, err := b.GetConnection(ctx, req.Storage, name); err != nil { |
| 102 | + return nil, nil |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +func (b *databaseBackend) reloadConnection(ctx context.Context, storage logical.Storage, name string) error { |
| 107 | + // Close plugin and delete the entry in the connections cache. |
| 108 | + if err := b.ClearConnection(name); err != nil { |
| 109 | + return err |
| 110 | + } |
| 111 | + |
| 112 | + // Execute plugin again, we don't need the object so throw away. |
| 113 | + if _, err := b.GetConnection(ctx, storage, name); err != nil { |
| 114 | + return err |
| 115 | + } |
| 116 | + |
| 117 | + return nil |
| 118 | +} |
| 119 | + |
| 120 | +// pathReloadPlugin reloads all connections using a named plugin. |
| 121 | +func pathReloadPlugin(b *databaseBackend) *framework.Path { |
| 122 | + return &framework.Path{ |
| 123 | + Pattern: fmt.Sprintf("reload/%s", framework.GenericNameRegex("plugin_name")), |
| 124 | + |
| 125 | + DisplayAttrs: &framework.DisplayAttributes{ |
| 126 | + OperationPrefix: operationPrefixDatabase, |
| 127 | + OperationVerb: "reload", |
| 128 | + OperationSuffix: "plugin", |
| 129 | + }, |
| 130 | + |
| 131 | + Fields: map[string]*framework.FieldSchema{ |
| 132 | + "plugin_name": { |
| 133 | + Type: framework.TypeString, |
| 134 | + Description: "Name of the database plugin", |
| 135 | + }, |
| 136 | + }, |
| 137 | + |
| 138 | + Callbacks: map[logical.Operation]framework.OperationFunc{ |
| 139 | + logical.UpdateOperation: b.reloadPlugin(), |
| 140 | + }, |
| 141 | + |
| 142 | + HelpSynopsis: pathReloadPluginHelpSyn, |
| 143 | + HelpDescription: pathReloadPluginHelpDesc, |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +// reloadPlugin reloads all instances of a named plugin by closing the existing |
| 148 | +// instances and creating new ones. |
| 149 | +func (b *databaseBackend) reloadPlugin() framework.OperationFunc { |
| 150 | + return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { |
| 151 | + pluginName := data.Get("plugin_name").(string) |
| 152 | + if pluginName == "" { |
| 153 | + return logical.ErrorResponse(respErrEmptyPluginName), nil |
| 154 | + } |
| 155 | + |
| 156 | + connNames, err := req.Storage.List(ctx, "config/") |
| 157 | + if err != nil { |
104 | 158 | return nil, err
|
105 | 159 | }
|
| 160 | + reloaded := []string{} |
| 161 | + for _, connName := range connNames { |
| 162 | + entry, err := req.Storage.Get(ctx, fmt.Sprintf("config/%s", connName)) |
| 163 | + if err != nil { |
| 164 | + return nil, fmt.Errorf("failed to read connection configuration: %w", err) |
| 165 | + } |
| 166 | + if entry == nil { |
| 167 | + continue |
| 168 | + } |
106 | 169 |
|
107 |
| - return nil, nil |
| 170 | + var config DatabaseConfig |
| 171 | + if err := entry.DecodeJSON(&config); err != nil { |
| 172 | + return nil, err |
| 173 | + } |
| 174 | + if config.PluginName == pluginName { |
| 175 | + if err := b.reloadConnection(ctx, req.Storage, connName); err != nil { |
| 176 | + var successfullyReloaded string |
| 177 | + if len(reloaded) > 0 { |
| 178 | + successfullyReloaded = fmt.Sprintf("successfully reloaded %d connection(s): %s; ", |
| 179 | + len(reloaded), |
| 180 | + strings.Join(reloaded, ", ")) |
| 181 | + } |
| 182 | + return nil, fmt.Errorf("%sfailed to reload connection %q: %w", successfullyReloaded, connName, err) |
| 183 | + } |
| 184 | + reloaded = append(reloaded, connName) |
| 185 | + } |
| 186 | + } |
| 187 | + |
| 188 | + resp := &logical.Response{ |
| 189 | + Data: map[string]interface{}{ |
| 190 | + "connections": reloaded, |
| 191 | + "count": len(reloaded), |
| 192 | + }, |
| 193 | + } |
| 194 | + |
| 195 | + if len(reloaded) == 0 { |
| 196 | + resp.AddWarning(fmt.Sprintf("no connections were found with plugin_name %q", pluginName)) |
| 197 | + } |
| 198 | + |
| 199 | + return resp, nil |
108 | 200 | }
|
109 | 201 | }
|
110 | 202 |
|
@@ -551,3 +643,12 @@ const pathResetConnectionHelpDesc = `
|
551 | 643 | This path resets the database connection by closing the existing database plugin
|
552 | 644 | instance and running a new one.
|
553 | 645 | `
|
| 646 | + |
| 647 | +const pathReloadPluginHelpSyn = ` |
| 648 | +Reloads all connections using a named database plugin. |
| 649 | +` |
| 650 | + |
| 651 | +const pathReloadPluginHelpDesc = ` |
| 652 | +This path resets each database connection using a named plugin by closing each |
| 653 | +existing database plugin instance and running a new one. |
| 654 | +` |
0 commit comments