harness.testContainer.ContainerBuilder.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of harness-test-container_3 Show documentation
Show all versions of harness-test-container_3 Show documentation
Miscellaneous libraries/utilities for Scala.
The newest version!
package harness.testContainer
import cats.syntax.option.*
import harness.zio.*
import java.util.UUID
import zio.*
import zio.internal.stacktracer.SourceLocation
abstract class ContainerBuilder[RIn, ROut](final val containerType: String) { self =>
protected type Internal
protected def makeInternal(metadata: ContainerBuilder.Metadata): RIO[RIn & Scope, Internal]
protected def makeContainers(internal: Internal, metadata: ContainerBuilder.Metadata): RIO[RIn & Scope, List[TestContainer]]
protected def makeEnv(internal: Internal, metadata: ContainerBuilder.Metadata): RIO[RIn & Scope, ZEnvironment[ROut]]
private def startContainer(container: TestContainer): RIO[Scope, Unit] = {
val runCommand = Sys.Command(
"docker",
List("run", "-d", "--name", container.name).some,
container.envVars.flatMap(e => List("-e", e.toString)).some,
container.ports.flatMap(p => List("-p", p.toString)).some,
container.labels.flatMap(l => List("-l", l.toString)).some,
List(s"${container.imageName}:${container.imageVersion}").some,
)
val runEffect: Task[Unit] =
Logger.log.detailed(s"Starting container '${container.name}'") *>
runCommand.execute0(outLevel = Logger.LogLevel.Detailed)
val closeEffect: Task[Unit] =
Logger.log.detailed(s"Stopping container '${container.name}'") *>
Sys.Command("docker", "stop", container.name).execute0(outLevel = Logger.LogLevel.Trace) *>
Sys.Command("docker", "rm", container.name).execute0(outLevel = Logger.LogLevel.Trace)
runEffect.withFinalizer { _ => closeEffect.orDie }
}
private def awaitContainers(containers: List[TestContainer], waitIncrement: Duration, waited: Duration, maxWait: Duration): Task[Unit] =
for {
_ <- ZIO.fail(new RuntimeException("Timed out waiting for containers")).when(waited >= maxWait)
awaiting <- ZIO.filterNot(containers) { _.healthCheck.orElseSucceed(false) }
_ <- ZIO.when(awaiting.nonEmpty) {
Logger.log.debug(s"Awaiting container(s): ${awaiting.map { c => s"'${c.name}'" }.mkString(", ")}") *>
Clock.sleep(waitIncrement).withClock(Clock.ClockLive) *>
awaitContainers(awaiting, waitIncrement, waited + waitIncrement, maxWait)
}
} yield ()
final def layer(implicit loc: SourceLocation): RLayer[RIn, ROut] =
ZLayer.scopedEnvironment {
val metadata = ContainerBuilder.Metadata(containerType, loc, UUID.randomUUID)
Logger.addContext("container-type" -> metadata.containerType, "layer-id" -> metadata.layerId) {
for {
_ <- Logger.log.detailed(s"Creating test-container layer for '$containerType'")
internal <- self.makeInternal(metadata)
containers <- self.makeContainers(internal, metadata)
_ <- ZIO.foreachDiscard(containers)(self.startContainer)
_ <- awaitContainers(containers, 100.millis, Duration.Zero, 5.seconds)
env <- makeEnv(internal, metadata)
} yield env
}
}
}
object ContainerBuilder {
final case class Metadata(containerType: String, loc: SourceLocation, layerId: UUID) {
def makeContainer(containerName: String, imageName: String, imageVersion: String)(healthCheck: Task[Boolean]): TestContainer =
TestContainer(
name = s"$containerType--$containerName--$layerId",
imageName = imageName,
imageVersion = imageVersion,
healthCheck = healthCheck,
envVars = Nil,
ports = Nil,
labels = List(
TestContainer.Var("HARNESS-TEST-CONTAINER--CONTAINER-TYPE", containerType),
TestContainer.Var("HARNESS-TEST-CONTAINER--SOURCE-PATH", loc.path),
TestContainer.Var("HARNESS-TEST-CONTAINER--SOURCE-LINE", loc.line.toString),
TestContainer.Var("HARNESS-TEST-CONTAINER--LAYER-ID", layerId.toString),
),
)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy