Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add a workaround for testcontainers/testcontainers-java#6441 #152

Merged
merged 3 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
package dev.monosoul.jooq.artifact

import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.read.ListAppender
import org.slf4j.LoggerFactory
import org.testcontainers.containers.BindMode.READ_ONLY
import org.testcontainers.containers.GenericContainer
import org.testcontainers.containers.SelinuxContext.SHARED
import org.testcontainers.containers.output.Slf4jLogConsumer
import org.testcontainers.containers.output.ToStringConsumer
import org.testcontainers.containers.startupcheck.IndefiniteWaitOneShotStartupCheckStrategy

class GradleContainer : GenericContainer<GradleContainer>("gradle:jdk17-alpine") {
class GradleContainer(
dockerSocketPath: String = "/var/run/docker.sock",
) : GenericContainer<GradleContainer>("gradle:jdk17-alpine") {

private val listAppender = ListAppender<ILoggingEvent>().also { it.start() }
private val logger = (LoggerFactory.getLogger("GradleContainer[$dockerImageName]") as Logger).also {
it.addAppender(listAppender)
}
val output: List<String> get() = ArrayList(listAppender.list).map { it.formattedMessage }
private val toStringLogConsumer = ToStringConsumer()
val output: String get() = toStringLogConsumer.toUtf8String()
val projectPath = "/home/gradle/project"

init {
withLogConsumer(
Slf4jLogConsumer(logger)
Slf4jLogConsumer(LoggerFactory.getLogger("GradleContainer[$dockerImageName]"))
)
withLogConsumer(toStringLogConsumer)
addFsBind("build/local-repository", "$projectPath/local-repository")
addFsBind("/var/run/docker.sock", "/var/run/docker.sock")
addFsBind("/var/run/docker.sock", dockerSocketPath)
withWorkingDirectory(projectPath)

withStartupCheckStrategy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ class JooqCodegenLoggingLevelsTest {
gradleContainer.stop()
}.isSuccess()

val output = gradleContainer.output.joinToString("\n")
that(output).apply {
that(gradleContainer.output).apply {
not().contains("Database version is older than what dialect POSTGRES supports")
}
}
Expand All @@ -54,8 +53,7 @@ class JooqCodegenLoggingLevelsTest {
gradleContainer.stop()
}.isSuccess()

val output = gradleContainer.output.joinToString("\n")
that(output).apply {
that(gradleContainer.output).apply {
contains("Database version is older than what dialect POSTGRES supports")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ class PluginWorksArtifactTest {
gradleContainer.stop()
}.isSuccess()

val output = gradleContainer.output.joinToString("\n")
that(output).contains("BUILD SUCCESSFUL in ")
that(gradleContainer.output).contains("BUILD SUCCESSFUL in ")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package dev.monosoul.jooq.artifact

import org.junit.jupiter.api.Test
import org.testcontainers.utility.MountableFile.forClasspathResource
import strikt.api.expect
import strikt.assertions.contains
import strikt.assertions.isSuccess

class WorksAfterFailingToFindDockerArtifactTest {

@Test
fun `should be possible to generate jooq classes even after it failed on first attempt`() {
// given
val gradleContainer = GradleContainer(dockerSocketPath = "/var/run/docker-alt.sock").apply {
withEnv("TESTCONTAINERS_RYUK_DISABLED", "true")
withCopyToContainer(forClasspathResource("/testproject"), projectPath)
withCopyToContainer(forClasspathResource("/gradle_run.sh"), "/gradle_run.sh")
withCommand("/gradle_run.sh")
}

// when & then
expect {
catching {
gradleContainer.start()
gradleContainer.stop()
}.isSuccess()

that(gradleContainer.output).contains("BUILD SUCCESSFUL in ")
}
}
}
8 changes: 8 additions & 0 deletions artifact-tests/src/test/resources/gradle_run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/sh

gradle classes --info --stacktrace

export DOCKER_HOST=unix:///var/run/docker-alt.sock
export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker-alt.sock

gradle classes --info --stacktrace
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,26 @@ import org.slf4j.LoggerFactory
import org.testcontainers.containers.JdbcDatabaseContainer
import org.testcontainers.containers.output.Slf4jLogConsumer
import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy
import org.testcontainers.dockerclient.DockerClientProviderStrategy
import org.testcontainers.utility.DockerImageName
import java.sql.Driver
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
import kotlin.reflect.KCallable
import kotlin.reflect.full.declaredMembers
import kotlin.reflect.jvm.isAccessible

class GenericDatabaseContainer(
private val image: Image,
private val database: Database.Internal,
private val jdbcAwareClassLoader: ClassLoader,
) : JdbcDatabaseContainer<GenericDatabaseContainer>(DockerImageName.parse(image.name)) {
private val image: Image,
private val database: Database.Internal,
private val jdbcAwareClassLoader: ClassLoader,
) : JdbcDatabaseContainer<GenericDatabaseContainer>(
image.let {
failFastAlways.set(false)
DockerImageName.parse(it.name)
}
) {

private val driverLoadLock = ReentrantLock()
private var driver: Driver? = null
Expand Down Expand Up @@ -67,4 +77,18 @@ class GenericDatabaseContainer(
else -> throw e
}
}

private companion object {
/**
* Workaround for https://github.com/testcontainers/testcontainers-java/issues/6441
*/
val failFastAlways = DockerClientProviderStrategy::class.declaredMembers
.single { it.name == "FAIL_FAST_ALWAYS" }
.apply { isAccessible = true }
.let {
@Suppress("UNCHECKED_CAST")
it as KCallable<AtomicBoolean>
}
.call()
}
}
Loading