Keep Mongo operations online across Vault credential rotation

This commit is contained in:
Stephan D committed 2026-09-12 22:19:36 +02:00
1 parent 80d6917aa7
commit 23f7b78c3b
30 files changed
+1290 -202

No files matched your search

+64 -62
View File
@@ -3,15 +3,18 @@ package mongo
import (
"context"
"fmt"
sharedmodel "git.sendico.io/tech/sxchnge/shared/model"
"net"
"os"
"strings"
"time"
mutil "git.sendico.io/tech/sxchnge/shared/config"
"git.sendico.io/tech/sxchnge/shared/database/internal/transaction"
"git.sendico.io/tech/sxchnge/shared/database/source"
"git.sendico.io/tech/sxchnge/shared/database/transaction"
dbvault "git.sendico.io/tech/sxchnge/shared/database/vault"
"git.sendico.io/tech/sxchnge/shared/logging"
sharedmodel "git.sendico.io/tech/sxchnge/shared/model"
"github.com/go-viper/mapstructure/v2"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/readpref"
@@ -20,28 +23,29 @@ import (
// Config represents configuration
type Config struct {
Port *string `mapstructure:"port"`
PortEnv *string `mapstructure:"port_env"`
User *string `mapstructure:"user"`
UserEnv *string `mapstructure:"user_env"`
PasswordEnv string `mapstructure:"password_env"`
Database *string `mapstructure:"database"`
DatabaseEnv *string `mapstructure:"database_env"`
Host *string `mapstructure:"host"`
HostEnv *string `mapstructure:"host_env"`
Hosts []string `mapstructure:"hosts,omitempty"`
HostsEnvPrefix *string `mapstructure:"hosts_env_prefix,omitempty"`
HostsEnvPrefixEnv *string `mapstructure:"hosts_env_prefix_env,omitempty"`
PortsEnvPrefix *string `mapstructure:"ports_env_prefix,omitempty"`
PortsEnvPrefixEnv *string `mapstructure:"ports_env_prefix_env,omitempty"`
URI *string `mapstructure:"uri,omitempty"`
URIEnv *string `mapstructure:"uri_env,omitempty"`
AuthSource *string `mapstructure:"auth_source,omitempty"`
AuthSourceEnv *string `mapstructure:"auth_source_env,omitempty"`
AuthMechanism *string `mapstructure:"auth_mechanism,omitempty"`
AuthMechanismEnv *string `mapstructure:"auth_mechanism_env,omitempty"`
ReplicaSet *string `mapstructure:"replica_set,omitempty"`
ReplicaSetEnv *string `mapstructure:"replica_set_env,omitempty"`
Vault *dbvault.Config `mapstructure:"vault"`
Port *string `mapstructure:"port"`
PortEnv *string `mapstructure:"port_env"`
User *string `mapstructure:"user"`
UserEnv *string `mapstructure:"user_env"`
PasswordEnv string `mapstructure:"password_env"`
Database *string `mapstructure:"database"`
DatabaseEnv *string `mapstructure:"database_env"`
Host *string `mapstructure:"host"`
HostEnv *string `mapstructure:"host_env"`
Hosts []string `mapstructure:"hosts,omitempty"`
HostsEnvPrefix *string `mapstructure:"hosts_env_prefix,omitempty"`
HostsEnvPrefixEnv *string `mapstructure:"hosts_env_prefix_env,omitempty"`
PortsEnvPrefix *string `mapstructure:"ports_env_prefix,omitempty"`
PortsEnvPrefixEnv *string `mapstructure:"ports_env_prefix_env,omitempty"`
URI *string `mapstructure:"uri,omitempty"`
URIEnv *string `mapstructure:"uri_env,omitempty"`
AuthSource *string `mapstructure:"auth_source,omitempty"`
AuthSourceEnv *string `mapstructure:"auth_source_env,omitempty"`
AuthMechanism *string `mapstructure:"auth_mechanism,omitempty"`
AuthMechanismEnv *string `mapstructure:"auth_mechanism_env,omitempty"`
ReplicaSet *string `mapstructure:"replica_set,omitempty"`
ReplicaSetEnv *string `mapstructure:"replica_set_env,omitempty"`
}
type DBSettings struct {
@@ -119,37 +123,42 @@ func dialMongo(logger mlogger.Logger, dbSettings *DBSettings) (*mongo.Client, er
return client, nil
}
func ConnectClient(logger mlogger.Logger, settings sharedmodel.SettingsT) (*mongo.Client, *Config, *DBSettings, error) {
config, dbSettings, err := decodeConfig(logger, settings)
if err != nil {
return nil, nil, nil, err
}
client, err := dialMongo(logger, dbSettings)
if err != nil {
return nil, nil, nil, err
}
return client, config, dbSettings, nil
}
// DB represents the structure of the database
type DB struct {
logger mlogger.Logger
config *DBSettings
client *mongo.Client
}
func (db *DB) db() *mongo.Database {
return db.client.Database(db.config.Database)
}
func (db *DB) MongoDatabase() *mongo.Database {
return db.db()
logger mlogger.Logger
database source.Source
close func(context.Context) error
}
func (db *DB) MongoDatabase() source.Source { return db.database }
func (db *DB) TransactionFactory() transaction.Factory {
return transactionimp.CreateFactory(db.client)
return transactionimp.CreateSourceFactory(db.database)
}
// OpenSource owns either a fixed connection or a rotating Vault-backed source.
func OpenSource(logger mlogger.Logger, settings sharedmodel.SettingsT) (source.Source, func(context.Context) error, error) {
config, dbSettings, err := decodeConfig(logger, settings)
if err != nil {
return nil, nil, err
}
if config.Vault != nil {
issuer, err := dbvault.New(*config.Vault, dbSettings.URI)
if err != nil {
return nil, nil, err
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
manager, err := source.New(ctx, logger.Named("credentials"), dbSettings.Database, issuer)
if err != nil {
return nil, nil, err
}
return manager, manager.Close, nil
}
client, err := dialMongo(logger, dbSettings)
if err != nil {
return nil, nil, err
}
return source.Fixed(client.Database(dbSettings.Database)), client.Disconnect, nil
}
func collectReplicaHosts(configuredHosts []string, replicaSet, defaultPort, hostPrefix, portPrefix string) []string {
@@ -217,24 +226,17 @@ func collectReplicaHosts(configuredHosts []string, replicaSet, defaultPort, host
}
func (db *DB) CloseConnection() {
if err := db.client.Disconnect(context.Background()); err != nil {
db.logger.Warn("Failed to close connection", zap.Error(err))
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if err := db.close(ctx); err != nil {
db.logger.Warn("Failed to drain database connection", zap.Error(err))
}
db.logger.Info("Database connection closed")
}
// NewConnection creates a new database connection
func NewConnection(logger mlogger.Logger, settings sharedmodel.SettingsT) (*DB, error) {
client, _, dbSettings, err := ConnectClient(logger, settings)
database, close, err := OpenSource(logger, settings)
if err != nil {
return nil, err
}
db := &DB{
logger: logger.Named("db"),
config: dbSettings,
client: client,
}
return db, nil
return &DB{logger: logger, database: database, close: close}, nil
}