testcontainers / testcontainers-java

Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
https://testcontainers.org
MIT License
7.97k stars 1.64k forks source link

Could not find a valid Docker environment when using Gradle Shared Build Service (`ClassNotFoundException` when loading strategies) #9050

Open kiview opened 1 month ago

kiview commented 1 month ago

Discussed in https://github.com/testcontainers/testcontainers-java/discussions/9034

Originally posted by **debapgithub** July 30, 2024 Hi, I am new to testcontainers and setting up for a gradle service where I wanted to achieve the following in sequence. When gradle build command will run : _`./gradlew clean build`_ 1. MySql Testcontainer should start 2. DB migrations should apply against the testcontainer DB 3. **generateJooq** task should execute against the testcontainer DB 4. Run the integration tests against testcontainer 5. At the end of the build process, testcotnainer should stop automatically. I have tried starting MySql testcontainer as a gradle task and I was able to execute the following tasks properly except one issue due to which I have decided to switch to another approach where I can start the MySql testcontainer as a gradle Shared service so that it will automaticllay manage the testcontainer lifecycle. Here is the code which will start the testcontainer as a gradle shared service and it worked fine as well. But next day when I tried running the gradle command, I started getting the below error. **Error** ``` * What went wrong: Execution failed for task ':startMySQLContainer'. > Failed to create service 'mysqlContainerService'. > Could not create an instance of type MySQLContainerService. > Could not find a valid Docker environment. Please see logs and check configuration * Try: > Run with --stacktrace option to get the stack trace. > Run with --info or --debug option to get more log output. > Run with --scan to get full insights. > Get more help at https://help.gradle.org. ``` **Gradle Code** ``` import org.testcontainers.containers.MySQLContainer; import org.gradle.api.services.BuildService import org.gradle.api.services.BuildServiceParameters buildscript { repositories { maven { name repoName url repoUrl credentials() { username = repoUsername password = repoPassword } } } dependencies { classpath "org.testcontainers:testcontainers:${dependencyManagement.importedProperties['testcontainers.version']}" classpath "org.testcontainers:mysql:${dependencyManagement.importedProperties['testcontainers.version']}" classpath "com.mysql:mysql-connector-j:${dependencyManagement.importedProperties['mysql.version']}" } } // Here we register service for providing our database during the build. gradle.sharedServices.registerIfAbsent('mysqlContainerService', MySQLContainerService) { spec -> spec.parameters.databaseSchemaName.set(project.ext.databaseSchemaName) } /** * Build service for providing database container. */ abstract class MySQLContainerService implements BuildService, AutoCloseable { interface Params extends BuildServiceParameters { Property getDatabaseSchemaName() } private final MySQLContainer mysqlContainer; @javax.inject.Inject MySQLContainerService(Params params) { // Services are initialized lazily, on first request to them, so we start container immediately. long startTime = System.currentTimeMillis() println("DB Schema Name: ${params.getDatabaseSchemaName().get()}") mysqlContainer = new MySQLContainer(MySQLContainer.IMAGE).withDatabaseName(params.getDatabaseSchemaName().get()) mysqlContainer.start() long duration = System.currentTimeMillis() - startTime println("MySQL Testcontainer Started successfully with URL: ${mysqlContainer.getJdbcUrl()}. Total time taken: ${duration} ms") } String getJdbcUrl() { return mysqlContainer.getJdbcUrl() } String getUsername() { return mysqlContainer.getUsername() } String getPassword() { return mysqlContainer.getPassword() } String getDriverClassName() { return mysqlContainer.getDriverClassName() } @Override void close() { // Ensure to stop container in the end if (mysqlContainer != null) { try { mysqlContainer.stop() println("MySQL container stopped successfully.") } catch (Exception e) { println("Failed to stop MySQL container: ${e.message}") } } else { println("MySQL container is already null (was not started).") } } } tasks.register('startMySQLContainer') { doLast { gradle.sharedServices.getRegistrations().getByName('mysqlContainerService') } } // Ensure the integrationTest uses the same container and stops it after tasks.named('integrationTest').configure { dependsOn 'startMySQLContainer' doFirst { def service = gradle.sharedServices.getRegistrations().getByName('mysqlContainerService').service.get() systemProperty 'spring.datasource.url', service.getJdbcUrl() systemProperty 'spring.datasource.username', service.getUsername() systemProperty 'spring.datasource.password', service.getPassword() systemProperty 'spring.datasource.driver-class-name', service.getDriverClassName() } doLast { systemProperties.remove('spring.datasource.url') systemProperties.remove('spring.datasource.username') systemProperties.remove('spring.datasource.password') systemProperties.remove('spring.datasource.driver-class-name') } } ``` Not sure whats going wrong and how it worked first time when I did this code. Thanks in advance!! -Deba
kiview commented 1 month ago

The error

Can't instantiate a strategy from org.testcontainers.dockerclient.UnixSocketClientProviderStrategy (ClassNotFoundException). This probably means that cached configuration refers to a client provider class that is not available in this version of Testcontainers. Other strategies will be tried instead.
Could not find a valid Docker environment. Please check configuration. Attempted configurations were:
As no valid configuration was found, execution cannot continue.
See https://java.testcontainers.org/on_failure.html for more details.

indicates issues with the class loader in the context of a Gradle Share Service and it having access to the classes within the Testcontainers dependency.

debapgithub commented 1 month ago

@kiview I have created a discussion topic for this issue in Gradle GH.

https://discuss.gradle.org/t/could-not-find-a-valid-docker-environment-when-using-gradle-shared-build-service-classnotfoundexception-when-loading-strategies/49031