Skip to content

Commit f2c8b63

Browse files
authored
backport of commit e2f09cb
1 parent 609ad64 commit f2c8b63

File tree

3 files changed

+78
-49
lines changed

3 files changed

+78
-49
lines changed

builtin/logical/database/path_config_connection.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"fmt"
1010
"net/url"
1111
"sort"
12-
"strings"
1312

1413
"github.com/fatih/structs"
1514
"github.com/hashicorp/go-uuid"
@@ -173,6 +172,7 @@ func (b *databaseBackend) reloadPlugin() framework.OperationFunc {
173172
return nil, err
174173
}
175174
reloaded := []string{}
175+
reloadFailed := []string{}
176176
for _, connName := range connNames {
177177
entry, err := req.Storage.Get(ctx, fmt.Sprintf("config/%s", connName))
178178
if err != nil {
@@ -188,15 +188,14 @@ func (b *databaseBackend) reloadPlugin() framework.OperationFunc {
188188
}
189189
if config.PluginName == pluginName {
190190
if err := b.reloadConnection(ctx, req.Storage, connName); err != nil {
191-
var successfullyReloaded string
192-
if len(reloaded) > 0 {
193-
successfullyReloaded = fmt.Sprintf("successfully reloaded %d connection(s): %s; ",
194-
len(reloaded),
195-
strings.Join(reloaded, ", "))
196-
}
197-
return nil, fmt.Errorf("%sfailed to reload connection %q: %w", successfullyReloaded, connName, err)
191+
b.Logger().Error("failed to reload connection", "name", connName, "error", err)
192+
b.dbEvent(ctx, "reload-connection-fail", req.Path, "", false, "name", connName)
193+
reloadFailed = append(reloadFailed, connName)
194+
} else {
195+
b.Logger().Debug("reloaded connection", "name", connName)
196+
b.dbEvent(ctx, "reload-connection", req.Path, "", true, "name", connName)
197+
reloaded = append(reloaded, connName)
198198
}
199-
reloaded = append(reloaded, connName)
200199
}
201200
}
202201

@@ -207,10 +206,12 @@ func (b *databaseBackend) reloadPlugin() framework.OperationFunc {
207206
},
208207
}
209208

210-
if len(reloaded) == 0 {
211-
resp.AddWarning(fmt.Sprintf("no connections were found with plugin_name %q", pluginName))
209+
if len(reloaded) > 0 {
210+
b.dbEvent(ctx, "reload", req.Path, "", true, "plugin_name", pluginName)
211+
} else if len(reloaded) == 0 && len(reloadFailed) == 0 {
212+
b.Logger().Debug("no connections were found", "plugin_name", pluginName)
212213
}
213-
b.dbEvent(ctx, "reload", req.Path, "", true, "plugin_name", pluginName)
214+
214215
return resp, nil
215216
}
216217
}

changelog/29519.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
secrets/database: Fix a bug where a global database plugin reload exits if any of the database connections are not available
3+
```

vault/external_tests/plugin/external_plugin_test.go

Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -817,56 +817,81 @@ func TestExternalPlugin_DatabaseReload(t *testing.T) {
817817
t.Fatal(err)
818818
}
819819

820-
dbName := fmt.Sprintf("%s-%d", plugin.Name, 0)
821-
roleName := "test-role-" + dbName
822-
823-
cleanupContainer, connURL := postgreshelper.PrepareTestContainerWithVaultUser(t, context.Background())
824-
t.Cleanup(cleanupContainer)
820+
roleName := "test-role"
821+
822+
// create 4 databases to create connections for
823+
cleanupContainer0, connURL0 := postgreshelper.PrepareTestContainerWithVaultUser(t, context.Background())
824+
t.Cleanup(cleanupContainer0)
825+
cleanupContainer1, connURL1 := postgreshelper.PrepareTestContainerWithVaultUser(t, context.Background())
826+
t.Cleanup(cleanupContainer1)
827+
cleanupContainer2, connURL2 := postgreshelper.PrepareTestContainerWithVaultUser(t, context.Background())
828+
t.Cleanup(cleanupContainer2)
829+
830+
var roles []string
831+
// write the config and roles for the first 3 DBs
832+
for i, url := range []string{connURL0, connURL1, connURL2} {
833+
dbName := fmt.Sprintf("%s-%d", plugin.Name, i)
834+
_, err := client.Logical().Write("database/config/"+dbName, map[string]interface{}{
835+
"connection_url": url,
836+
"plugin_name": plugin.Name,
837+
"allowed_roles": []string{"*"},
838+
"username": "vaultadmin",
839+
"password": "vaultpass",
840+
})
841+
require.NoError(t, err)
825842

826-
_, err := client.Logical().Write("database/config/"+dbName, map[string]interface{}{
827-
"connection_url": connURL,
828-
"plugin_name": plugin.Name,
829-
"allowed_roles": []string{roleName},
830-
"username": "vaultadmin",
831-
"password": "vaultpass",
832-
})
833-
if err != nil {
834-
t.Fatal(err)
843+
r := fmt.Sprintf("%s-%d", roleName, i)
844+
roles = append(roles, r)
845+
_, err = client.Logical().Write("database/roles/"+r, map[string]interface{}{
846+
"db_name": dbName,
847+
"creation_statements": testRole,
848+
"max_ttl": "10m",
849+
})
850+
require.NoError(t, err)
835851
}
836852

837-
_, err = client.Logical().Write("database/roles/"+roleName, map[string]interface{}{
838-
"db_name": dbName,
839-
"creation_statements": testRole,
840-
"max_ttl": "10m",
853+
// the 4th db connection has a bad connURL on purpose, it should not fail
854+
// the plugin reload
855+
_, err := client.Logical().Write("database/config/"+plugin.Name+"-3", map[string]interface{}{
856+
"connection_url": "foobar",
857+
"verify_connection": false, // this db connection should not fail the plugin reload
858+
"plugin_name": plugin.Name,
859+
"allowed_roles": []string{"*"},
860+
"username": "vaultadmin",
861+
"password": "vaultpass",
841862
})
842-
if err != nil {
843-
t.Fatal(err)
844-
}
863+
require.NoError(t, err)
845864

846-
resp, err := client.Logical().Read("database/creds/" + roleName)
847-
if err != nil {
848-
t.Fatal(err)
849-
}
850-
if resp == nil {
851-
t.Fatal("read creds response is nil")
865+
for _, role := range roles {
866+
resp, err := client.Logical().Read("database/creds/" + role)
867+
require.NoError(t, err)
868+
if resp == nil {
869+
t.Fatal("read creds response is nil")
870+
}
852871
}
853872

854873
// Reload plugin
855-
if _, err := client.Sys().ReloadPlugin(&api.ReloadPluginInput{
874+
_, err = client.Sys().ReloadPlugin(&api.ReloadPluginInput{
856875
Plugin: plugin.Name,
857-
}); err != nil {
858-
t.Fatal(err)
859-
}
876+
})
877+
require.NoError(t, err)
860878

861879
// Generate credentials after reload
862-
resp, err = client.Logical().Read("database/creds/" + roleName)
863-
if err != nil {
864-
t.Fatal(err)
865-
}
866-
if resp == nil {
867-
t.Fatal("read creds response is nil")
880+
for _, role := range roles {
881+
resp, err := client.Logical().Read("database/creds/" + role)
882+
require.NoError(t, err)
883+
if resp == nil {
884+
t.Fatal("read creds response is nil")
885+
}
868886
}
869887

888+
// remove one postgres database so that the plugin reload will fail
889+
cleanupContainer1()
890+
_, err = client.Sys().ReloadPlugin(&api.ReloadPluginInput{
891+
Plugin: plugin.Name,
892+
})
893+
require.NoError(t, err)
894+
870895
if err := client.Sys().Unmount(plugin.Name); err != nil {
871896
t.Fatal(err)
872897
}

0 commit comments

Comments
 (0)