docker-controller-scala

Actions Status: CI Maven Central Renovate License Tokei FOSSA Status

This library provides an easy and simple way to handle Docker Container or Docker Compose on ScalaTest, based on docker-java. The implementation of this library is thin, and if you know docker-java, your learning cost will be negligible.

M1 Macs also be supported.

Installation

Add the following to your sbt build (2.12.x, 2.13.x, 3.0.x):

val version = "..."

libraryDependencies += Seq(
  "com.github.j5ik2o" %% "docker-controller-scala-core" % version,
  "com.github.j5ik2o" %% "docker-controller-scala-scalatest" % version, // for scalatest
  // RDB
  "com.github.j5ik2o" %% "docker-controller-scala-mysql" % version, // optional
  "com.github.j5ik2o" %% "docker-controller-scala-postgresql" % version, // optional
  "com.github.j5ik2o" %% "docker-controller-scala-flyway" % version, // optional
  // NoSQL
  "com.github.j5ik2o" %% "docker-controller-scala-memcached" % version, // optional
  "com.github.j5ik2o" %% "docker-controller-scala-redis" % version, // optional
  "com.github.j5ik2o" %% "docker-controller-scala-elasticsearch" % version, // optional
  // Kafka
  "com.github.j5ik2o" %% "docker-controller-scala-zookeeper" % version, // optional
  "com.github.j5ik2o" %% "docker-controller-scala-kafka" % version, // optional
  // AWS Services
  "com.github.j5ik2o" %% "docker-controller-scala-dynamodb-local" % version, // optional
  "com.github.j5ik2o" %% "docker-controller-scala-minio" % version, // optional
  "com.github.j5ik2o" %% "docker-controller-scala-localstack" % version, // optional
  "com.github.j5ik2o" %% "docker-controller-scala-elasticmq" % version, // optional
)

In most cases, you can just select the scalatest module and the module you need.

libraryDependencies += Seq(
  "com.github.j5ik2o" %% "docker-controller-scala-scalatest" % version,
  "com.github.j5ik2o" %% "docker-controller-scala-mysql" % version,
)

Usage

DockerController that the thin wrapper for docker-java controls Docker Image and Docker Container for testing.

How to test with preset DockerController

The DockerController for the corresponding preset is as follows. Please see the corresponding **Spec for specific usage.

Use Flyway Migrate Command on MySQL/PostgreSQL

If you'd like to use flyway module, you can use docker-controller-scala-flyway.

libraryDependencies += Seq(
  "com.github.j5ik2o" %% "docker-controller-scala-scalatest" % version,
  "com.github.j5ik2o" %% "docker-controller-scala-mysql" % version,
  "com.github.j5ik2o" %% "docker-controller-scala-flyway" % version, // for flyway
)

Mix-in FlywaySpecSupport then, put the sql files in src/resources/flyway(src/resources/** can be set to any string.), run flywayContext.flyway.migrate() in afterStartContainers method.

How to test with DockerController your customized

To launch a docker container for testing

// In ScalaTest, please mix-in DockerControllerSpecSupport.
class NginxSpec extends AnyFreeSpec with DockerControllerSpecSupport {
  
  // choose whether to create and destroy containers per test class (ForAllTest) or per test (ForEachTest).
  override def createRemoveLifecycle: DockerContainerCreateRemoveLifecycle.Value =
    DockerContainerCreateRemoveLifecycle.ForEachTest

  // choose whether to start and stop containers per test class (ForAllTest) or per test (ForEachTest).
  override def startStopLifecycle: DockerContainerStartStopLifecycle.Value =
    DockerContainerStartStopLifecycle.ForEachTest
    
  val nginx: DockerController = DockerController(dockerClient)(
    imageName = "nginx",
    tag = Some("latest")
  ).configureCreateContainerCmd { cmd =>
    // if customize the container generation, please do the following.
    // In this example, a random host port is specified.
    val hostPort: Int              = temporaryServerPort()
    val containerPort: ExposedPort = ExposedPort.tcp(80)
    val portBinding: Ports         = new Ports()
    portBinding.bind(containerPort, Ports.Binding.bindPort(hostPort))
    logger.debug(s"hostPort = $hostPort, containerPort = $containerPort")
    cmd
      .withExposedPorts(containerPort)
      .withHostConfig(newHostConfig().withPortBindings(portBinding))
  }

  // Specify DockerControllers to be launched.
  override protected val dockerControllers: Vector[DockerController] = {
    Vector(nginx)
  }

  // Set the condition to wait for the container to be started.
  override protected val waitPredicatesSettings: Map[DockerController, WaitPredicateSetting] =
    Map(
      nginx -> WaitPredicateSetting(
        Duration.Inf,
        WaitPredicates.forLogMessageContained("Configuration complete; ready for start up")
      )
    )

  "nginx" - {
    "run-1" in {
      val hostPort = nginx.inspectContainer().getNetworkSettings.bindingHostPort(ExposedPort.tcp(80)).get
      val url      = new URL(s"http://$dockerHost:$hostPort")
      HttpRequestUtil.wget(url)
    }
    "run-2" in {
      val hostPort = nginx.inspectContainer().getNetworkSettings.bindingHostPort(ExposedPort.tcp(80)).get
      val url      = new URL(s"http://$dockerHost:$hostPort")
      HttpRequestUtil.wget(url)
    }
  }
}

How to use Docker Compose

  • Place the docker-compose.yml.ftl(ftl is Freemarker template) in src/test/resources. docker-compose.yml.ftl can be renamed to anything you want.
  • The variables in the ftl can be freely determined.
version: '3'
services:
  nginx:
    image: nginx
    ports:
      - ${nginxHostPort}:80
  • Use DockerComposeController, which is a subtype of DockerController. Other than this, it is the same as the test method above.
  • Pass the context containing the values of the variables to be used in the FTL to the constructor of DockerComposeController.
class NginxSpec extends AnyFreeSpec with DockerControllerSpecSupport {
// ...
  val buildDir: File                = ResourceUtil.getBuildDir(getClass)
  val dockerComposeWorkingDir: File = new File(buildDir, "docker-compose")
  val dockerController = DockerComposeController(dockerClient)(
    dockerComposeWorkingDir,
    "docker-compose.yml.ftl",
    Map("nginxHostPort" -> hostPort.toString)
  )

  override val dockerControllers: Vector[DockerController] = {
    Vector(dockerController)
  }
// ...
}     

License

FOSSA Status