mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-04 20:06:27 +02:00
Merge remote-tracking branch 'remotes/from/ce/main'
This commit is contained in:
commit
08503e7dd0
@ -152,30 +152,61 @@ globals {
|
||||
protocol = "tcp"
|
||||
}
|
||||
}
|
||||
// Ports that we'll open up for ingress in the security group for all external integration target machines.
|
||||
integration_host_ports = {
|
||||
ldap : {
|
||||
description = "LDAP"
|
||||
port = 389
|
||||
protocol = "tcp"
|
||||
// Database configurations for Vault database secrets engine testing
|
||||
database_configs = {
|
||||
postgres : {
|
||||
port = 5432
|
||||
version = "16-alpine"
|
||||
username = "postgres"
|
||||
password = "secret"
|
||||
database = "postgres"
|
||||
description = "PostgreSQL Server"
|
||||
},
|
||||
ldaps : {
|
||||
description = "LDAPS"
|
||||
port = 636
|
||||
protocol = "tcp"
|
||||
mongodb : {
|
||||
port = 27017
|
||||
version = "7.0"
|
||||
username = "admin"
|
||||
password = "secret"
|
||||
database = "admin"
|
||||
description = "MongoDB Server"
|
||||
},
|
||||
mysql : {
|
||||
description = "MySQL Server"
|
||||
port = 3306
|
||||
protocol = "tcp"
|
||||
},
|
||||
kmip : {
|
||||
description = "KMIP Server"
|
||||
port = 5696
|
||||
protocol = "tcp"
|
||||
},
|
||||
version = "8.0"
|
||||
username = "root"
|
||||
password = "secret"
|
||||
database = "mysql"
|
||||
description = "MySQL Server"
|
||||
}
|
||||
}
|
||||
|
||||
// Ports that we'll open up for ingress in the security group for all external integration target machines.
|
||||
integration_host_ports = merge(
|
||||
{
|
||||
ldap : {
|
||||
description = "LDAP"
|
||||
port = 389
|
||||
protocol = "tcp"
|
||||
},
|
||||
ldaps : {
|
||||
description = "LDAPS"
|
||||
port = 636
|
||||
protocol = "tcp"
|
||||
},
|
||||
kmip : {
|
||||
description = "KMIP Server"
|
||||
port = 5696
|
||||
protocol = "tcp"
|
||||
}
|
||||
},
|
||||
// Add database ports dynamically from database_configs
|
||||
{ for db_name, db_config in global.database_configs : db_name => {
|
||||
description = db_config.description
|
||||
port = db_config.port
|
||||
protocol = "tcp"
|
||||
} }
|
||||
)
|
||||
|
||||
// Combine all ports into a single map
|
||||
ports = merge(
|
||||
global.vault_cluster_ports,
|
||||
|
||||
@ -234,10 +234,11 @@ scenario "plugin" {
|
||||
}
|
||||
|
||||
variables {
|
||||
hosts = step.create_plugin_integration_target.hosts
|
||||
ip_version = matrix.ip_version
|
||||
packages = concat(global.packages, global.distro_packages["ubuntu"]["24.04"], ["podman", "podman-docker"])
|
||||
ports = global.integration_host_ports
|
||||
hosts = step.create_plugin_integration_target.hosts
|
||||
ip_version = matrix.ip_version
|
||||
packages = concat(global.packages, global.distro_packages["ubuntu"]["24.04"], ["podman", "podman-docker"])
|
||||
ports = global.integration_host_ports
|
||||
database_configs = global.database_configs
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
80
enos/modules/database_container/main.tf
Normal file
80
enos/modules/database_container/main.tf
Normal file
@ -0,0 +1,80 @@
|
||||
# Copyright IBM Corp. 2016, 2026
|
||||
# SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
terraform {
|
||||
required_providers {
|
||||
enos = {
|
||||
source = "registry.terraform.io/hashicorp-forge/enos"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
# Database-specific configurations
|
||||
database_configs = {
|
||||
postgres = {
|
||||
image_template = "docker.io/postgres:${var.db_version}"
|
||||
env_vars = {
|
||||
POSTGRES_USER = var.username
|
||||
POSTGRES_PASSWORD = var.password
|
||||
POSTGRES_DB = var.database
|
||||
}
|
||||
}
|
||||
mongodb = {
|
||||
image_template = "docker.io/mongo:${var.db_version}"
|
||||
env_vars = {
|
||||
MONGO_INITDB_ROOT_USERNAME = var.username
|
||||
MONGO_INITDB_ROOT_PASSWORD = var.password
|
||||
MONGO_INITDB_DATABASE = var.database
|
||||
}
|
||||
}
|
||||
mysql = {
|
||||
image_template = "docker.io/mysql:${var.db_version}"
|
||||
env_vars = {
|
||||
MYSQL_ROOT_PASSWORD = var.password
|
||||
MYSQL_USER = var.username
|
||||
MYSQL_PASSWORD = var.password
|
||||
MYSQL_DATABASE = var.database
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config = local.database_configs[var.database_type]
|
||||
image = local.config.image_template
|
||||
env_vars_map = local.config.env_vars
|
||||
env_vars = join(",", [for k, v in local.env_vars_map : "${k}=${v}"])
|
||||
}
|
||||
|
||||
# Creating Database Server using generic container script
|
||||
resource "enos_remote_exec" "create_database" {
|
||||
depends_on = [var.depends_on_modules]
|
||||
|
||||
scripts = [abspath("${path.module}/../../modules/set_up_external_integration_target/scripts/start-container.sh")]
|
||||
|
||||
environment = {
|
||||
CONTAINER_IMAGE = local.image
|
||||
CONTAINER_NAME = "${var.database_type}-${var.instance_name}"
|
||||
CONTAINER_PORTS = var.port
|
||||
CONTAINER_ENVS = local.env_vars
|
||||
}
|
||||
|
||||
transport = {
|
||||
ssh = {
|
||||
host = var.host.public_ip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Outputs
|
||||
output "config" {
|
||||
description = "Database configuration details"
|
||||
value = {
|
||||
type = var.database_type
|
||||
username = var.username
|
||||
password = var.password
|
||||
database = var.database
|
||||
version = var.db_version
|
||||
port = var.port
|
||||
host = var.host
|
||||
}
|
||||
}
|
||||
58
enos/modules/database_container/variables.tf
Normal file
58
enos/modules/database_container/variables.tf
Normal file
@ -0,0 +1,58 @@
|
||||
# Copyright IBM Corp. 2016, 2026
|
||||
# SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
variable "database_type" {
|
||||
description = "Type of database to create (postgres, mongodb, mysql)"
|
||||
type = string
|
||||
validation {
|
||||
condition = contains(["postgres", "mongodb", "mysql"], var.database_type)
|
||||
error_message = "database_type must be one of: postgres, mongodb, mysql"
|
||||
}
|
||||
}
|
||||
|
||||
variable "db_version" {
|
||||
description = "Database version to use"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "username" {
|
||||
description = "Database username"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "password" {
|
||||
description = "Database password"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "database" {
|
||||
description = "Database name"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "port" {
|
||||
description = "Database port"
|
||||
type = number
|
||||
}
|
||||
|
||||
variable "host" {
|
||||
description = "Host configuration with public_ip"
|
||||
type = object({
|
||||
public_ip = string
|
||||
private_ip = string
|
||||
ipv6 = optional(string)
|
||||
})
|
||||
}
|
||||
|
||||
variable "instance_name" {
|
||||
description = "Unique instance name for the container (defaults to 'default')"
|
||||
type = string
|
||||
default = "default"
|
||||
}
|
||||
|
||||
variable "depends_on_modules" {
|
||||
description = "List of modules this depends on"
|
||||
type = list(any)
|
||||
default = []
|
||||
}
|
||||
@ -29,14 +29,24 @@ locals {
|
||||
port = var.ports.mysql.port
|
||||
host = var.hosts[0]
|
||||
}
|
||||
# Database configurations are now pulled from var.database_configs
|
||||
database_servers = {
|
||||
for db_name, db_config in var.database_configs : db_name => merge(db_config, {
|
||||
host = var.hosts[0]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
# Outputs
|
||||
output "state" {
|
||||
value = {
|
||||
ldap = local.ldap_server
|
||||
kmip = local.kmip_client
|
||||
}
|
||||
value = merge(
|
||||
{
|
||||
ldap = local.ldap_server
|
||||
kmip = local.kmip_client
|
||||
},
|
||||
# Add database servers dynamically
|
||||
{ for db_name, db_server in local.database_servers : db_name => db_server }
|
||||
)
|
||||
}
|
||||
|
||||
# We run install_packages before we install Vault because for some combinations of
|
||||
@ -125,3 +135,19 @@ resource "enos_remote_exec" "create_kmip" {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Creating Database Servers using generic database_container module
|
||||
module "database_servers" {
|
||||
for_each = var.database_configs
|
||||
source = "../database_container"
|
||||
|
||||
database_type = each.key
|
||||
db_version = each.value.version
|
||||
username = each.value.username
|
||||
password = each.value.password
|
||||
database = each.value.database
|
||||
port = each.value.port
|
||||
host = var.hosts[0]
|
||||
instance_name = "default"
|
||||
depends_on_modules = [module.install_packages]
|
||||
}
|
||||
|
||||
@ -41,3 +41,17 @@ variable "ports" {
|
||||
description = string
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
variable "database_configs" {
|
||||
description = "Database configurations for setting up database servers"
|
||||
type = map(object({
|
||||
port = number
|
||||
version = string
|
||||
username = string
|
||||
password = string
|
||||
database = string
|
||||
description = string
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
@ -0,0 +1,145 @@
|
||||
// Copyright IBM Corp. 2025, 2026
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package mongodb
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/sdk/helper/testcluster/blackbox"
|
||||
)
|
||||
|
||||
// TestMongoDBConnectionConfigCRUDWorkflows runs all MongoDB connection
|
||||
// config CRUD workflow tests.
|
||||
func TestMongoDBConnectionConfigCRUDWorkflows(t *testing.T) {
|
||||
t.Run("CreateBasic", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := blackbox.New(t)
|
||||
testMongoDBConnectionConfigCreateBasic(t, v)
|
||||
})
|
||||
|
||||
t.Run("ListReturnsNamesOnly", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := blackbox.New(t)
|
||||
testMongoDBConnectionConfigListReturnsNamesOnly(t, v)
|
||||
})
|
||||
|
||||
t.Run("ReadRedactsSensitiveFields", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := blackbox.New(t)
|
||||
testMongoDBConnectionConfigReadRedactsSensitiveFields(t, v)
|
||||
})
|
||||
|
||||
t.Run("ResetConnection", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := blackbox.New(t)
|
||||
testMongoDBConnectionConfigResetConnection(t, v)
|
||||
})
|
||||
|
||||
t.Run("DeleteConnection", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := blackbox.New(t)
|
||||
testMongoDBConnectionConfigDeleteConnection(t, v)
|
||||
})
|
||||
}
|
||||
|
||||
// TestMongoDBConnectionConfigValidationWorkflows runs all MongoDB
|
||||
// connection config validation workflow tests.
|
||||
func TestMongoDBConnectionConfigValidationWorkflows(t *testing.T) {
|
||||
t.Run("VerifyConnectionValid", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := blackbox.New(t)
|
||||
testMongoDBConnectionConfigVerifyConnectionValid(t, v)
|
||||
})
|
||||
|
||||
t.Run("VerifyConnectionInvalid", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := blackbox.New(t)
|
||||
testMongoDBConnectionConfigVerifyConnectionInvalid(t, v)
|
||||
})
|
||||
}
|
||||
|
||||
// testMongoDBConnectionConfigCreateBasic verifies that configuring a
|
||||
// MongoDB database connection succeeds at database/config/{name}.
|
||||
func testMongoDBConnectionConfigCreateBasic(t *testing.T, v *blackbox.Session) {
|
||||
t.Skip("Test implementation pending - MongoDB framework setup complete")
|
||||
|
||||
// TODO: Implement test following this pattern:
|
||||
// 1. requireVaultEnv(t)
|
||||
// 2. cleanup, connURL := PrepareTestContainer(t)
|
||||
// 3. defer cleanup()
|
||||
// 4. mount := fmt.Sprintf("database-%s", sanitize(t.Name()))
|
||||
// 5. v.MustEnableSecretsEngine(mount, &api.MountInput{Type: "database"})
|
||||
// 6. v.MustWrite(mount+"/config/my-mongodb-db", mongoConnectionConfigPayload(...))
|
||||
// 7. config := v.MustReadRequired(mount + "/config/my-mongodb-db")
|
||||
// 8. v.AssertSecret(config).Data().HasKey("plugin_name", "mongodb-database-plugin")
|
||||
}
|
||||
|
||||
// testMongoDBConnectionConfigListReturnsNamesOnly verifies LIST
|
||||
// /database/config returns configured connection names only, without
|
||||
// sensitive config details.
|
||||
func testMongoDBConnectionConfigListReturnsNamesOnly(t *testing.T, v *blackbox.Session) {
|
||||
t.Skip("Test implementation pending - MongoDB framework setup complete")
|
||||
|
||||
// TODO: Implement test to verify:
|
||||
// 1. Create multiple MongoDB connections
|
||||
// 2. List connections
|
||||
// 3. Verify only names are returned, no sensitive data
|
||||
}
|
||||
|
||||
// testMongoDBConnectionConfigReadRedactsSensitiveFields verifies that reading
|
||||
// a MongoDB connection config returns sanitized connection details.
|
||||
func testMongoDBConnectionConfigReadRedactsSensitiveFields(t *testing.T, v *blackbox.Session) {
|
||||
t.Skip("Test implementation pending - MongoDB framework setup complete")
|
||||
|
||||
// TODO: Implement test to verify:
|
||||
// 1. Create MongoDB connection with credentials
|
||||
// 2. Read connection config
|
||||
// 3. Verify password and other sensitive fields are redacted
|
||||
}
|
||||
|
||||
// testMongoDBConnectionConfigVerifyConnectionValid verifies that
|
||||
// configuration succeeds with verify_connection=true when credentials are valid.
|
||||
func testMongoDBConnectionConfigVerifyConnectionValid(t *testing.T, v *blackbox.Session) {
|
||||
t.Skip("Test implementation pending - MongoDB framework setup complete")
|
||||
|
||||
// TODO: Implement test to verify:
|
||||
// 1. Create MongoDB connection with valid credentials and verify_connection=true
|
||||
// 2. Verify connection succeeds
|
||||
}
|
||||
|
||||
// testMongoDBConnectionConfigVerifyConnectionInvalid verifies that
|
||||
// configuration fails with verify_connection=true when credentials are invalid.
|
||||
func testMongoDBConnectionConfigVerifyConnectionInvalid(t *testing.T, v *blackbox.Session) {
|
||||
t.Skip("Test implementation pending - MongoDB framework setup complete")
|
||||
|
||||
// TODO: Implement test to verify:
|
||||
// 1. Create MongoDB connection with invalid credentials and verify_connection=true
|
||||
// 2. Verify connection fails with appropriate error
|
||||
}
|
||||
|
||||
// testMongoDBConnectionConfigResetConnection verifies that resetting a
|
||||
// MongoDB database connection succeeds and preserves the stored connection
|
||||
// configuration.
|
||||
func testMongoDBConnectionConfigResetConnection(t *testing.T, v *blackbox.Session) {
|
||||
t.Skip("Test implementation pending - MongoDB framework setup complete")
|
||||
|
||||
// TODO: Implement test to verify:
|
||||
// 1. Create MongoDB connection
|
||||
// 2. Reset connection
|
||||
// 3. Verify config is preserved
|
||||
}
|
||||
|
||||
// testMongoDBConnectionConfigDeleteConnection verifies that deleting a
|
||||
// MongoDB database connection removes it, prevents new credential generation,
|
||||
// and remains idempotent when deleted again.
|
||||
func testMongoDBConnectionConfigDeleteConnection(t *testing.T, v *blackbox.Session) {
|
||||
t.Skip("Test implementation pending - MongoDB framework setup complete")
|
||||
|
||||
// TODO: Implement test to verify:
|
||||
// 1. Create MongoDB connection and role
|
||||
// 2. Generate credentials
|
||||
// 3. Delete connection
|
||||
// 4. Verify connection is gone and credentials can't be generated
|
||||
// 5. Delete again to verify idempotency
|
||||
}
|
||||
@ -0,0 +1,266 @@
|
||||
// Copyright IBM Corp. 2025, 2026
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package mongodb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/sdk/helper/docker"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultMongoImage = "docker.mirror.hashicorp.services/mongo"
|
||||
defaultMongoVersion = "7.0"
|
||||
defaultMongoUser = "admin"
|
||||
defaultMongoPass = "secret"
|
||||
)
|
||||
|
||||
// defaultRunOpts returns default Docker run options for MongoDB container
|
||||
// Uses test name to ensure unique container names for parallel execution
|
||||
func defaultRunOpts(t *testing.T) docker.RunOptions {
|
||||
return docker.RunOptions{
|
||||
ContainerName: fmt.Sprintf("mongodb-%s", sanitize(t.Name())),
|
||||
ImageRepo: defaultMongoImage,
|
||||
ImageTag: defaultMongoVersion,
|
||||
Env: []string{
|
||||
"MONGO_INITDB_ROOT_USERNAME=" + defaultMongoUser,
|
||||
"MONGO_INITDB_ROOT_PASSWORD=" + defaultMongoPass,
|
||||
"MONGO_INITDB_DATABASE=admin",
|
||||
},
|
||||
Ports: []string{"27017/tcp"},
|
||||
DoNotAutoRemove: false,
|
||||
OmitLogTimestamps: true,
|
||||
LogConsumer: func(s string) {
|
||||
if t.Failed() {
|
||||
t.Logf("container logs: %s", s)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// requireVaultEnv skips the test if required Vault environment variables are not set
|
||||
func requireVaultEnv(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
if os.Getenv("VAULT_ADDR") == "" || os.Getenv("VAULT_TOKEN") == "" {
|
||||
t.Skip("skipping blackbox test: VAULT_ADDR and VAULT_TOKEN are required")
|
||||
}
|
||||
}
|
||||
|
||||
// sanitize converts test name to a valid container name
|
||||
// Removes special characters and converts to lowercase
|
||||
func sanitize(name string) string {
|
||||
name = strings.ToLower(name)
|
||||
name = strings.ReplaceAll(name, "/", "-")
|
||||
name = strings.ReplaceAll(name, "_", "-")
|
||||
name = strings.ReplaceAll(name, " ", "-")
|
||||
// Remove any remaining special characters
|
||||
var result strings.Builder
|
||||
for _, r := range name {
|
||||
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' {
|
||||
result.WriteRune(r)
|
||||
}
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// PrepareTestContainer starts a MongoDB container for testing
|
||||
// Returns cleanup function and connection URL
|
||||
// If MONGO_URL environment variable is set, uses that instead of starting a container
|
||||
func PrepareTestContainer(t *testing.T) (func(), string) {
|
||||
_, cleanup, connURL, _ := prepareTestContainer(t, defaultRunOpts(t), defaultMongoPass, true, false)
|
||||
return cleanup, connURL
|
||||
}
|
||||
|
||||
// prepareTestContainer is the internal function that handles container setup
|
||||
// Supports both Docker container creation and external MongoDB via environment variable
|
||||
func prepareTestContainer(
|
||||
t *testing.T,
|
||||
runOpts docker.RunOptions,
|
||||
password string,
|
||||
addSuffix bool,
|
||||
forceLocalAddr bool,
|
||||
) (*docker.Runner, func(), string, string) {
|
||||
requireVaultEnv(t)
|
||||
|
||||
// Check for external MongoDB URL
|
||||
if os.Getenv("MONGO_URL") != "" {
|
||||
envMongoURL := os.Getenv("MONGO_URL")
|
||||
|
||||
// Create unique database for this test
|
||||
dbName := fmt.Sprintf("test_%s_%d", sanitize(t.Name()), time.Now().Unix())
|
||||
testURL := replaceDatabase(envMongoURL, dbName)
|
||||
|
||||
// Create the database
|
||||
if err := createDatabase(t, envMongoURL, dbName); err != nil {
|
||||
t.Fatalf("Failed to create test database: %v", err)
|
||||
}
|
||||
|
||||
cleanup := func() {
|
||||
dropDatabase(t, envMongoURL, dbName)
|
||||
}
|
||||
|
||||
return nil, cleanup, testURL, ""
|
||||
}
|
||||
|
||||
// Start Docker container
|
||||
runner, err := docker.NewServiceRunner(runOpts)
|
||||
if err != nil {
|
||||
errStr := strings.ToLower(err.Error())
|
||||
if strings.Contains(errStr, "docker") &&
|
||||
(strings.Contains(errStr, "daemon") || strings.Contains(errStr, "connect")) {
|
||||
t.Skipf("skipping blackbox test: docker not available: %v", err)
|
||||
}
|
||||
t.Fatalf("Could not start docker MongoDB: %s", err)
|
||||
}
|
||||
|
||||
svc, containerID, err := runner.StartNewService(
|
||||
context.Background(),
|
||||
addSuffix,
|
||||
forceLocalAddr,
|
||||
connectMongoDB(password),
|
||||
)
|
||||
if err != nil {
|
||||
errStr := strings.ToLower(err.Error())
|
||||
if strings.Contains(errStr, "docker") &&
|
||||
(strings.Contains(errStr, "daemon") || strings.Contains(errStr, "connect")) {
|
||||
t.Skipf("skipping blackbox test: docker not available: %v", err)
|
||||
}
|
||||
t.Fatalf("Could not start docker MongoDB: %s", err)
|
||||
}
|
||||
|
||||
connURL := svc.Config.URL().String()
|
||||
|
||||
// Create unique database for this test
|
||||
dbName := fmt.Sprintf("test_%s_%d", sanitize(t.Name()), time.Now().Unix())
|
||||
testURL := replaceDatabase(connURL, dbName)
|
||||
|
||||
// Create the database
|
||||
if err := createDatabase(t, connURL, dbName); err != nil {
|
||||
svc.Cleanup()
|
||||
t.Fatalf("Failed to create test database: %v", err)
|
||||
}
|
||||
|
||||
cleanup := func() {
|
||||
dropDatabase(t, connURL, dbName)
|
||||
svc.Cleanup()
|
||||
}
|
||||
|
||||
return runner, cleanup, testURL, containerID
|
||||
}
|
||||
|
||||
// connectMongoDB returns a ServiceAdapter that connects to MongoDB
|
||||
// Includes retry logic with 30-second timeout for container startup
|
||||
func connectMongoDB(password string) docker.ServiceAdapter {
|
||||
return func(ctx context.Context, host string, port int) (docker.ServiceConfig, error) {
|
||||
u := url.URL{
|
||||
Scheme: "mongodb",
|
||||
User: url.UserPassword(defaultMongoUser, password),
|
||||
Host: fmt.Sprintf("%s:%d", host, port),
|
||||
Path: "/admin",
|
||||
}
|
||||
|
||||
// Retry connection with timeout
|
||||
deadline := time.Now().Add(30 * time.Second)
|
||||
var lastErr error
|
||||
|
||||
for time.Now().Before(deadline) {
|
||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(u.String()))
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// Ping to verify connection
|
||||
if err = client.Ping(ctx, nil); err != nil {
|
||||
client.Disconnect(ctx)
|
||||
lastErr = err
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// Connection successful
|
||||
client.Disconnect(ctx)
|
||||
return docker.NewServiceURL(u), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("mongodb not ready after 30s: %w", lastErr)
|
||||
}
|
||||
}
|
||||
|
||||
// replaceDatabase replaces the database name in a MongoDB connection URL
|
||||
func replaceDatabase(connURL, dbName string) string {
|
||||
u, err := url.Parse(connURL)
|
||||
if err != nil {
|
||||
return connURL
|
||||
}
|
||||
|
||||
u.Path = "/" + dbName
|
||||
return u.String()
|
||||
}
|
||||
|
||||
// createDatabase creates a new database in MongoDB
|
||||
// MongoDB creates databases lazily, so we create a collection to ensure it exists
|
||||
func createDatabase(t *testing.T, connURL, dbName string) error {
|
||||
t.Helper()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(connURL))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to MongoDB: %w", err)
|
||||
}
|
||||
defer client.Disconnect(ctx)
|
||||
|
||||
// Create a collection to ensure database exists
|
||||
db := client.Database(dbName)
|
||||
if err := db.CreateCollection(ctx, "_init"); err != nil {
|
||||
return fmt.Errorf("failed to create database: %w", err)
|
||||
}
|
||||
|
||||
t.Logf("Created test database: %s", dbName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// dropDatabase drops a database from MongoDB
|
||||
func dropDatabase(t *testing.T, connURL, dbName string) {
|
||||
t.Helper()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(connURL))
|
||||
if err != nil {
|
||||
t.Logf("Warning: failed to connect for cleanup: %v", err)
|
||||
return
|
||||
}
|
||||
defer client.Disconnect(ctx)
|
||||
|
||||
if err := client.Database(dbName).Drop(ctx); err != nil {
|
||||
t.Logf("Warning: failed to drop database %s: %v", dbName, err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("Dropped test database: %s", dbName)
|
||||
}
|
||||
|
||||
// mongoConnectionConfigPayload returns a standard MongoDB connection configuration payload
|
||||
func mongoConnectionConfigPayload(connURL, allowedRoles string, verifyConnection bool) map[string]any {
|
||||
return map[string]any{
|
||||
"plugin_name": "mongodb-database-plugin",
|
||||
"connection_url": connURL,
|
||||
"allowed_roles": allowedRoles,
|
||||
"verify_connection": verifyConnection,
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user