最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - Error when trying to test MongoDB via testcontainers (This host is unknown ) - Stack Overflow

programmeradmin3浏览0评论

I have a service on Spring boot where a custom configuration is set up that works with MongoDB's ReplicaSet

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.1'
    id 'io.spring.dependency-management' version '1.1.4'
}


java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

....
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(MongoPropertiesCustom.class)
@EnableReactiveMongoRepositories(basePackageClasses = {SubscriptionRepository.class})
public class MongoConfig extends AbstractReactiveMongoConfiguration {

    private final MongoPropertiesCustom mongoPropertiesCustom;

    @Override
    protected String getDatabaseName() {
        return mongoPropertiesCustom.getDatabase();
    }

    @Override
    public MongoClient reactiveMongoClient() {
        return MongoClients.create(mongoPropertiesCustom.buildUri());
    }

    @Bean
    public ReactiveMongoTemplate reactiveMongoTemplate() {
        return new ReactiveMongoTemplate(reactiveMongoClient(), getDatabaseName());
    }
    @Bean
    public CommandLineRunner initializeIndexes(ReactiveMongoTemplate mongoTemplate) {
        return args -> {
            
            mongoTemplate.indexOps(SubscriptionDocument.class)
                    .ensureIndex(new Index().on("id", Sort.Direction.ASC).unique())
                    .subscribe();
            mongoTemplate.indexOps(ProcessingError.class)
                    .ensureIndex(new Index().on("timestamp", Sort.Direction.DESC))
                    .subscribe();
        };
    }

    @Bean
    public ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory factory) {
        return new ReactiveMongoTransactionManager(factory);
    }
}

@ConfigurationProperties(prefix = "mongodb")
@Data
public class MongoPropertiesCustom {

    private String host = "";
    private int port = 0;
    private String database = "";
    private String username;
    private String password;
    private String authSource = "";
    private String replicaSet;

    public String buildUri() {
        return String.format("mongodb://%s:%s@%s:%d/%s?authSource=%s&replicaSet=%s",
                username, password, host, port, database, authSource, replicaSet);
    }
}

@Component
@RequiredArgsConstructor
@Slf4j
public class SubscriptionRepository {

    private final ReactiveMongoTemplate mongoTemplate;
    private final ReactiveMongoTransactionManager transactionManager;

    public Mono<SubscriptionDocument> save(SubscriptionDocument subscription) {
       
        TransactionalOperator transactionalOperator = TransactionalOperator.create(transactionManager);

        return mongoTemplate.save(subscription)
                .doOnSuccess(saved -> log.debug("Saved subscription: {}", saved))
                .doOnError(error -> log.error("Error saving subscription: {}", error.getMessage()))
                
                .as(transactionalOperator::transactional);
    }
............

tests

@SpringBootTest(classes = {
        MongoConfig.class,
        SubscriptionRepository.class
})
@ActiveProfiles("test")
@Testcontainers
class SubscriptionRepositoryTest {
    private static final Logger log = LoggerFactory.getLogger(SubscriptionRepositoryTest.class);

      @Container
    static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:6.0")
            .withCommand(
                    "--replSet", "rs0",
                    "--bind_ip_all",
                    "--setParameter", "transactionLifetimeLimitSeconds=30"
            );

    @Autowired
    private SubscriptionRepository subscriptionRepository;

    @DynamicPropertySource
    static void setProperties(DynamicPropertyRegistry registry) {
        mongoDBContainer.start();

        String containerIpAddress = mongoDBContainer.getHost();
        Integer mappedPort = mongoDBContainer.getFirstMappedPort();

        log.info("MongoDB container started at {}:{}", containerIpAddress, mappedPort);

        initializeReplicaSet(mongoDBContainer);

        registry.add("mongodb.host", () -> containerIpAddress);
        registry.add("mongodb.port", () -> mappedPort);
        registry.add("mongodb.database", () -> "test");
        registry.add("mongodb.username", () -> "root");
        registry.add("mongodb.password", () -> "root");
        registry.add("mongodb.authSource", () -> "admin");
        registry.add("mongodb.replicaSet", () -> "rs0");
    }

    private static void initializeReplicaSet(MongoDBContainer container) {
        try {

            log.info("Starting replica set initialization...");
            Thread.sleep(1000);

            String containerIpAddress = container.getHost();
            Integer mappedPort = container.getFirstMappedPort();

            log.info("Using host: {}, port: {}", containerIpAddress, mappedPort);

            String rsConfig = String.format(
                    "rs.initiate({" +
                            "_id: 'rs0'," +
                            "members: [{" +
                            "_id: 0," +
                            "host: '%s:%d'," +
                            "priority: 1" +
                            "}]," +
                            "settings: {" +
                            "electionTimeoutMillis: 2000" +
                          
                            "}" +
                            "})",
                    containerIpAddress, mappedPort
            );

            log.info("Initializing replica set with config: {}", rsConfig);

            org.testcontainers.containers.Container.ExecResult initResult = container.execInContainer(
                    "mongosh",
                    "--eval",
                    rsConfig
            );
            log.info("Initialization result - stdout: {}", initResult.getStdout());

          
            org.testcontainers.containers.Container.ExecResult statusResult = container.execInContainer(
                    "mongosh",
                    "--eval",
                    "rs.status()"
            );
            log.info("Replica set status: {}", statusResult.getStdout());
            Thread.sleep(2000);

            ConnectionString connectionString = new ConnectionString(
                    String.format("mongodb://%s:%d/?directConnection=true",
                            containerIpAddress, mappedPort)
            );

            try (MongoClient testClient = MongoClients.create(connectionString)) {
                Mono.from(testClient.getDatabase("admin")
                                .runCommand(new Document("ping", 1)))
                        .block(Duration.ofSeconds(10));
                log.info("Replica set initialized successfully");

                String createUserCommand = "db.getSiblingDB('admin').createUser({" +
                        "user: 'root'," +
                        "pwd: 'root'," +
                        "roles: ['root']" +
                        "})";

                org.testcontainers.containers.Container.ExecResult createUserResult = container.execInContainer(
                        "mongosh",
                        "--eval",
                        createUserCommand
                );
                log.info("Create user result: {}", createUserResult.getStdout());

                String checkUserCommand = "db.getSiblingDB('admin').getUser('root')";
                org.testcontainers.containers.Container.ExecResult checkUserResult = container.execInContainer(
                        "mongosh",
                        "--eval",
                        checkUserCommand
                );
                log.info("User check result: {}", checkUserResult.getStdout());

                String authTestCommand = "db.getSiblingDB('admin').auth('root', 'root')";
                org.testcontainers.containers.Container.ExecResult authTestResult = container.execInContainer(
                        "mongosh",
                        "--eval",
                        authTestCommand
                );
                log.info("Auth test result: {}", authTestResult.getStdout());
            }

        } catch (Exception e) {
            log.error("Failed to initialize replica set", e);
            throw new RuntimeException("Failed to initialize replica set", e);
        }
    }


    @BeforeEach
    void setUp() {
        log.info("Cleaning up database before test");
        subscriptionRepository.dropAllCollections()
                .doOnSuccess(v -> log.info("Database cleaned successfully"))
                .doOnError(e -> log.error("Error cleaning database", e))
                .block();
    }

.....

After when are runned test ....

2025-01-18T20:43:08.861+03:00 INFO 2560 --- [consumer] [-localhost:3316] org.mongodb.driver.cluster : Server localhost:3316 is no longer a member of the replica set. Removing from client view of cluster. 2025-01-18T20:43:08.862+03:00 INFO 2560 --- [consumer] [-localhost:3316] org.mongodb.driver.cluster : Discovered replica set primary localhost:3316 with max election id 7fffffff0000000000000001 and max set version 1 2025-01-18T20:43:09.233+03:00 INFO 2560 --- [consumer] [ Test worker] c.repository.SubscriptionRepositoryTest : Started SubscriptionRepositoryTest in 8.815 seconds (process running for 14.105) 2025-01-18T20:43:09.317+03:00 INFO 2560 --- [consumer] [ Test worker] org.mongodb.driver.cluster : No server chosen by WritableServerSelector from cluster description ClusterDescription{type=REPLICA_SET, connectionMode=MULTIPLE, serverDescriptions=[ServerDescription{address=localhost:3316, type=UNKNOWN, state=CONNECTING}]}. Waiting for 30000 ms before timing out 2025-01-18T20:43:09.324+03:00 INFO 2560 --- [consumer] [ Test worker] org.mongodb.driver.cluster : No server chosen by WritableServerSelector from cluster description ClusterDescription{type=REPLICA_SET, connectionMode=MULTIPLE, serverDescriptions=[ServerDescription{address=localhost:3316, type=UNKNOWN, state=CONNECTING}]}. Waiting for 30000 ms before timing out 2025-01-18T20:43:10.541+03:00 INFO 2560 --- [consumer] [ Test worker] c.repository.SubscriptionRepositoryTest : Cleaning up database before test 2025-01-18T20:43:10.638+03:00 INFO 2560 --- [consumer] [ Test worker] org.mongodb.driver.cluster : No server chosen by ReadPreferenceServerSelector{readPreference=primary} from cluster description ClusterDescription{type=REPLICA_SET, connectionMode=MULTIPLE, serverDescriptions=[ServerDescription{address=localhost:3316, type=UNKNOWN, state=CONNECTING}]}. Waiting for 30000 ms before timing out 2025-01-18T20:43:11.151+03:00 INFO 2560 --- [consumer] [3de90b922:27017] org.mongodb.driver.cluster : Exception in monitor thread while connecting to server 3c43de90b922:27017

com.mongodb.MongoSocketException: This host is unknown (3c43de90b922) at com.mongodb.ServerAddress.getSocketAddresses(ServerAddress.java:221) ~[mongodb-driver-core-4.11.1.jar:na]

Does anyone have any ideas why the initialization of the replica set is successful, as are the checks, then the connectionString for ReactiveMongoTemplate is also successfully formed, but at some point in time the external address is not accessed, that is, MongoDB inside the container uses its internal hostname (22e0b261103c), but this hostname is not available outside the container?

I have a service on Spring boot where a custom configuration is set up that works with MongoDB's ReplicaSet

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.1'
    id 'io.spring.dependency-management' version '1.1.4'
}


java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

....
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(MongoPropertiesCustom.class)
@EnableReactiveMongoRepositories(basePackageClasses = {SubscriptionRepository.class})
public class MongoConfig extends AbstractReactiveMongoConfiguration {

    private final MongoPropertiesCustom mongoPropertiesCustom;

    @Override
    protected String getDatabaseName() {
        return mongoPropertiesCustom.getDatabase();
    }

    @Override
    public MongoClient reactiveMongoClient() {
        return MongoClients.create(mongoPropertiesCustom.buildUri());
    }

    @Bean
    public ReactiveMongoTemplate reactiveMongoTemplate() {
        return new ReactiveMongoTemplate(reactiveMongoClient(), getDatabaseName());
    }
    @Bean
    public CommandLineRunner initializeIndexes(ReactiveMongoTemplate mongoTemplate) {
        return args -> {
            
            mongoTemplate.indexOps(SubscriptionDocument.class)
                    .ensureIndex(new Index().on("id", Sort.Direction.ASC).unique())
                    .subscribe();
            mongoTemplate.indexOps(ProcessingError.class)
                    .ensureIndex(new Index().on("timestamp", Sort.Direction.DESC))
                    .subscribe();
        };
    }

    @Bean
    public ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory factory) {
        return new ReactiveMongoTransactionManager(factory);
    }
}

@ConfigurationProperties(prefix = "mongodb")
@Data
public class MongoPropertiesCustom {

    private String host = "";
    private int port = 0;
    private String database = "";
    private String username;
    private String password;
    private String authSource = "";
    private String replicaSet;

    public String buildUri() {
        return String.format("mongodb://%s:%s@%s:%d/%s?authSource=%s&replicaSet=%s",
                username, password, host, port, database, authSource, replicaSet);
    }
}

@Component
@RequiredArgsConstructor
@Slf4j
public class SubscriptionRepository {

    private final ReactiveMongoTemplate mongoTemplate;
    private final ReactiveMongoTransactionManager transactionManager;

    public Mono<SubscriptionDocument> save(SubscriptionDocument subscription) {
       
        TransactionalOperator transactionalOperator = TransactionalOperator.create(transactionManager);

        return mongoTemplate.save(subscription)
                .doOnSuccess(saved -> log.debug("Saved subscription: {}", saved))
                .doOnError(error -> log.error("Error saving subscription: {}", error.getMessage()))
                
                .as(transactionalOperator::transactional);
    }
............

tests

@SpringBootTest(classes = {
        MongoConfig.class,
        SubscriptionRepository.class
})
@ActiveProfiles("test")
@Testcontainers
class SubscriptionRepositoryTest {
    private static final Logger log = LoggerFactory.getLogger(SubscriptionRepositoryTest.class);

      @Container
    static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:6.0")
            .withCommand(
                    "--replSet", "rs0",
                    "--bind_ip_all",
                    "--setParameter", "transactionLifetimeLimitSeconds=30"
            );

    @Autowired
    private SubscriptionRepository subscriptionRepository;

    @DynamicPropertySource
    static void setProperties(DynamicPropertyRegistry registry) {
        mongoDBContainer.start();

        String containerIpAddress = mongoDBContainer.getHost();
        Integer mappedPort = mongoDBContainer.getFirstMappedPort();

        log.info("MongoDB container started at {}:{}", containerIpAddress, mappedPort);

        initializeReplicaSet(mongoDBContainer);

        registry.add("mongodb.host", () -> containerIpAddress);
        registry.add("mongodb.port", () -> mappedPort);
        registry.add("mongodb.database", () -> "test");
        registry.add("mongodb.username", () -> "root");
        registry.add("mongodb.password", () -> "root");
        registry.add("mongodb.authSource", () -> "admin");
        registry.add("mongodb.replicaSet", () -> "rs0");
    }

    private static void initializeReplicaSet(MongoDBContainer container) {
        try {

            log.info("Starting replica set initialization...");
            Thread.sleep(1000);

            String containerIpAddress = container.getHost();
            Integer mappedPort = container.getFirstMappedPort();

            log.info("Using host: {}, port: {}", containerIpAddress, mappedPort);

            String rsConfig = String.format(
                    "rs.initiate({" +
                            "_id: 'rs0'," +
                            "members: [{" +
                            "_id: 0," +
                            "host: '%s:%d'," +
                            "priority: 1" +
                            "}]," +
                            "settings: {" +
                            "electionTimeoutMillis: 2000" +
                          
                            "}" +
                            "})",
                    containerIpAddress, mappedPort
            );

            log.info("Initializing replica set with config: {}", rsConfig);

            org.testcontainers.containers.Container.ExecResult initResult = container.execInContainer(
                    "mongosh",
                    "--eval",
                    rsConfig
            );
            log.info("Initialization result - stdout: {}", initResult.getStdout());

          
            org.testcontainers.containers.Container.ExecResult statusResult = container.execInContainer(
                    "mongosh",
                    "--eval",
                    "rs.status()"
            );
            log.info("Replica set status: {}", statusResult.getStdout());
            Thread.sleep(2000);

            ConnectionString connectionString = new ConnectionString(
                    String.format("mongodb://%s:%d/?directConnection=true",
                            containerIpAddress, mappedPort)
            );

            try (MongoClient testClient = MongoClients.create(connectionString)) {
                Mono.from(testClient.getDatabase("admin")
                                .runCommand(new Document("ping", 1)))
                        .block(Duration.ofSeconds(10));
                log.info("Replica set initialized successfully");

                String createUserCommand = "db.getSiblingDB('admin').createUser({" +
                        "user: 'root'," +
                        "pwd: 'root'," +
                        "roles: ['root']" +
                        "})";

                org.testcontainers.containers.Container.ExecResult createUserResult = container.execInContainer(
                        "mongosh",
                        "--eval",
                        createUserCommand
                );
                log.info("Create user result: {}", createUserResult.getStdout());

                String checkUserCommand = "db.getSiblingDB('admin').getUser('root')";
                org.testcontainers.containers.Container.ExecResult checkUserResult = container.execInContainer(
                        "mongosh",
                        "--eval",
                        checkUserCommand
                );
                log.info("User check result: {}", checkUserResult.getStdout());

                String authTestCommand = "db.getSiblingDB('admin').auth('root', 'root')";
                org.testcontainers.containers.Container.ExecResult authTestResult = container.execInContainer(
                        "mongosh",
                        "--eval",
                        authTestCommand
                );
                log.info("Auth test result: {}", authTestResult.getStdout());
            }

        } catch (Exception e) {
            log.error("Failed to initialize replica set", e);
            throw new RuntimeException("Failed to initialize replica set", e);
        }
    }


    @BeforeEach
    void setUp() {
        log.info("Cleaning up database before test");
        subscriptionRepository.dropAllCollections()
                .doOnSuccess(v -> log.info("Database cleaned successfully"))
                .doOnError(e -> log.error("Error cleaning database", e))
                .block();
    }

.....

After when are runned test ....

2025-01-18T20:43:08.861+03:00 INFO 2560 --- [consumer] [-localhost:3316] org.mongodb.driver.cluster : Server localhost:3316 is no longer a member of the replica set. Removing from client view of cluster. 2025-01-18T20:43:08.862+03:00 INFO 2560 --- [consumer] [-localhost:3316] org.mongodb.driver.cluster : Discovered replica set primary localhost:3316 with max election id 7fffffff0000000000000001 and max set version 1 2025-01-18T20:43:09.233+03:00 INFO 2560 --- [consumer] [ Test worker] c.repository.SubscriptionRepositoryTest : Started SubscriptionRepositoryTest in 8.815 seconds (process running for 14.105) 2025-01-18T20:43:09.317+03:00 INFO 2560 --- [consumer] [ Test worker] org.mongodb.driver.cluster : No server chosen by WritableServerSelector from cluster description ClusterDescription{type=REPLICA_SET, connectionMode=MULTIPLE, serverDescriptions=[ServerDescription{address=localhost:3316, type=UNKNOWN, state=CONNECTING}]}. Waiting for 30000 ms before timing out 2025-01-18T20:43:09.324+03:00 INFO 2560 --- [consumer] [ Test worker] org.mongodb.driver.cluster : No server chosen by WritableServerSelector from cluster description ClusterDescription{type=REPLICA_SET, connectionMode=MULTIPLE, serverDescriptions=[ServerDescription{address=localhost:3316, type=UNKNOWN, state=CONNECTING}]}. Waiting for 30000 ms before timing out 2025-01-18T20:43:10.541+03:00 INFO 2560 --- [consumer] [ Test worker] c.repository.SubscriptionRepositoryTest : Cleaning up database before test 2025-01-18T20:43:10.638+03:00 INFO 2560 --- [consumer] [ Test worker] org.mongodb.driver.cluster : No server chosen by ReadPreferenceServerSelector{readPreference=primary} from cluster description ClusterDescription{type=REPLICA_SET, connectionMode=MULTIPLE, serverDescriptions=[ServerDescription{address=localhost:3316, type=UNKNOWN, state=CONNECTING}]}. Waiting for 30000 ms before timing out 2025-01-18T20:43:11.151+03:00 INFO 2560 --- [consumer] [3de90b922:27017] org.mongodb.driver.cluster : Exception in monitor thread while connecting to server 3c43de90b922:27017

com.mongodb.MongoSocketException: This host is unknown (3c43de90b922) at com.mongodb.ServerAddress.getSocketAddresses(ServerAddress.java:221) ~[mongodb-driver-core-4.11.1.jar:na]

Does anyone have any ideas why the initialization of the replica set is successful, as are the checks, then the connectionString for ReactiveMongoTemplate is also successfully formed, but at some point in time the external address is not accessed, that is, MongoDB inside the container uses its internal hostname (22e0b261103c), but this hostname is not available outside the container?

Share Improve this question asked Jan 18 at 18:02 skyhoskyho 1,9037 gold badges30 silver badges75 bronze badges 2
  • just proposing another potential way of testing - via embedded mongo (like Flapdoodle for example). – ShaharT Commented Jan 18 at 23:21
  • Does not support all MongoDB features It may have differences in behavior from the real MongoDB. Limited version support. It is not suitable for testing specific MongoDB features. Therefore, it is not suitable for work projects. (testing in embedded mongodb..) – skyho Commented Jan 19 at 11:04
Add a comment  | 

1 Answer 1

Reset to default 0

Here is the solution I applied:

@ActiveProfiles("test")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        classes = DemoApplication.class)
@Testcontainers
@AutoConfigureWebTestClient
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class AccountControllerIntegrationTest {

    private static final Logger log = LoggerFactory.getLogger(AccountControllerIntegrationTest.class);

    @Autowired
    private WebTestClient webTestClient;

    @Autowired
    private AccountRepository accountRepository;

    @Container
    static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:6.0")
            .withCommand("--replSet", "rs0");

    static {
        mongoDBContainer.start();
    }

    @DynamicPropertySource
    static void setProperties(DynamicPropertyRegistry registry) {
        registry.add("mongodb.uri.set.prefix", () -> "mongodb://");
        registry.add("mongodb.uri.set.host-first", () -> "localhost");
        registry.add("mongodb.uri.set.port-first-instance", mongoDBContainer::getFirstMappedPort);
        registry.add("mongodb.uri.set.database", () -> "test");
        registry.add("mongodb.uri.set.username", () -> "root");
        registry.add("mongodb.uri.set.password", () -> "root");
        registry.add("mongodb.uri.set.authentication-database", () -> "admin");
        registry.add("mongodb.uri.set.maxPoolSize", () -> 100);
        registry.add("mongodb.uri.set.minPoolSize", () -> 5);
        registry.add("mongodb.uri.set.retryWrites", () -> true);
        registry.add("mongodb.uri.set.writeAcknowledgement", () -> "majority");
        registry.add("mongodb.uri.set.readPreference", () -> "primary");

        initReplicaSet();
    }

    /**
     * <p>Initialize MongoDB replica set for testing.</p>
     * <ul>
     *   <li>Creates root user with necessary privileges</li>
     *   <li>Verifies replica set readiness</li>
     *   <li>Uses active waiting with minimal timeout</li>
     * </ul>
     */
    private static void initReplicaSet() {
        String replicaSetUrl = mongoDBContainer.getReplicaSetUrl();
        log.info("Starting initReplicaSet with URL: {}", replicaSetUrl);

        try (MongoClient mongoClient = MongoClients.create(replicaSetUrl)) {
            waitForReplicaSetReady(mongoClient);
            createRootUser(mongoClient);
        } catch (Exception e) {
            log.error("Failed to initialize MongoDB: {}", e.getMessage());
            throw new RuntimeException("Failed to initialize MongoDB", e);
        }
    }

    /**
     * <p>Wait for replica set readiness with minimal timeout.</p>
     * <ul>
     *   <li>Checks replica set status</li>
     *   <li>Verifies primary node presence</li>
     *   <li>Uses short intervals between checks</li>
     *   <li>Fails fast on configuration issues</li>
     * </ul>
     *
     * @param mongoClient MongoDB client for status checking
     * @throws RuntimeException if replica set is not ready within allowed time
     */
    private static void waitForReplicaSetReady(MongoClient mongoClient) {
        long startTime = System.currentTimeMillis();
        long timeout = 2000;
        long interval = 100;

        while (System.currentTimeMillis() - startTime < timeout) {
            try {
                Document result = Mono.from(mongoClient.getDatabase("admin")
                                .runCommand(new Document("replSetGetStatus", 1)))
                        .block();

                Number myState = (Number) result.get("myState");

                if (myState != null && myState.intValue() == 1) {
                    log.info("Replica set is ready");
                    return;
                }

                log.debug("Waiting for replica set: {}", result.toJson());
                Thread.sleep(interval);
            } catch (Exception e) {
                log.debug("Waiting for replica set: {}", e.getMessage());
                try {
                    Thread.sleep(interval);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("Replica set readiness wait interrupted", ie);
                }
            }
        }

        throw new RuntimeException("MongoDB replica set not ready. Check container configuration.");
    }

    /**
     * <p>Create root user in MongoDB.</p>
     * <ul>
     *   <li>Creates user only if it doesn't exist</li>
     *   <li>Assigns all necessary privileges</li>
     *   <li>Verifies successful creation</li>
     * </ul>
     *
     * @param mongoClient MongoDB client for executing commands
     */
    private static void createRootUser(MongoClient mongoClient) {
        MongoDatabase adminDb = mongoClient.getDatabase("admin");
        Document createUserCommand = new Document("createUser", "root")
                .append("pwd", "root")
                .append("roles", Collections.singletonList(
                        new Document("role", "root")
                                .append("db", "admin")
                ));

        try {
            Mono.from(adminDb.runCommand(createUserCommand))
                    .doOnNext(result -> log.info("User creation result: {}", result.toJson()))
                    .doOnError(error -> log.error("User creation error: {}", error.getMessage()))
                    .block();
        } catch (MongoCommandException e) {
            if (e.getErrorCode() != 51003) { // Ignore only "User already exists"
                throw e;
            }
            log.info("User already exists, continuing...");
        }

        // Verify user creation
        Mono.from(adminDb.runCommand(new Document("usersInfo", "root")))
                .doOnNext(result -> log.info("User info: {}", result.toJson()))
                .doOnError(error -> log.error("Error getting user info: {}", error.getMessage()))
                .block();
    }
}

You need to pay attention to the order of the ReplicaSet and setProperties initialization script(DynamicPropertyRegistry registry).

Also note that the properties for your custom mongo configuration that you have defined in application.yml must be precisely defined in the setProperties() method. Since the values for your property class will be taken from here, the data from which will then be used in MongoConfig.

Since test container deploys ReplicaSet without an account (user\password) in the admin database, I had to create this account at the time of ReplicaSet initialization in testcontainers. Thus, this allowed us to create a test that fully protects the application configuration from changes in the future, as it fully simulates the operation of component initialization for custom configuration of MongoDB.

Of course, you can modify the configuration and remove various checks for checking the initialization of the replica and checking its functionality, and also reduce the timeout for waiting for the initialization of the replicaset.

发布评论

评论列表(0)

  1. 暂无评论