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

Postgres not ready #54

Closed
fokot opened this issue Sep 21, 2016 · 6 comments
Closed

Postgres not ready #54

fokot opened this issue Sep 21, 2016 · 6 comments

Comments

@fokot
Copy link
Contributor

fokot commented Sep 21, 2016

Hi, I'm using spotify docker client, official postgres image and docker checker from your example. Without that sleep slick is unable to connect to postgres.

class CatRepositorySlickTest extends Specification with DockerPostgresService with DockerTestKit {

  "should save things to db and read them" >> {  implicit ee: ExecutionEnv =>
    import scala.concurrent.duration._

    Await.result(isContainerReady(postgresContainer), 10.seconds)
    Thread.sleep(5000)

    <code working with postgres db>
 ....
}

Full code is here
https://github.com/fokot/docker-scala/blob/master/src/test/scala/fokot/docker/scala/service/CatRepositorySlickTest.scala

@viktortnk
Copy link
Contributor

Yep, I had the same issue with both Mysql and Postgres. The problem that is log entry "database system is ready to accept connections" (for mysql case) doesn't actually mean that you can connect straight away. So I've got an extended version:

trait HasMysqlDatabase extends DockerTestKit with DockerMysqlService with BeforeAndAfterEach {
  self: Suite =>

  var mysqldb: Database = _

  def bootstrapSchema: Option[DefaultMySQLDriver.DDL] = None

  private val mysqlInitPc = PatienceConfig(Span(5, Seconds), Span(10, Millis))

  private def initDb(port: Int) = {
    val dbConfig =
      s"""
         |main = {
         |  connectionPool = "HikariCP"
         |  dataSourceClass = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource"
         |  properties = {
         |    url = "jdbc:mysql://${dockerExecutor.host}:$port/$MysqlDatabase",
         |    user = "$MysqlUser"
         |    password = "$MysqlPassword"
         |  }
         |  numThreads = 2
         |}
      """.stripMargin
    val db = Database.forConfig("main", ConfigFactory.parseString(dbConfig))
    RetryUtils.looped({
      db.run(sql"select 1".as[Int])
    }, 5, FiniteDuration(500, TimeUnit.MILLISECONDS)).map(_ => db)
  }

  override def beforeAll(): Unit = {
    super.beforeAll()
    val port = mysqlContainer.getPorts().futureValue(mysqlInitPc).apply(MysqlAdvertisedPort)
    mysqldb = initDb(port).futureValue(mysqlInitPc)
  }

  override protected def beforeEach(): Unit = {
    super.beforeEach()
    bootstrapSchema.foreach { schema =>
      mysqldb.run(schema.create).futureValue(mysqlInitPc)
    }
  }


  override protected def afterEach(): Unit = {
    try {
      bootstrapSchema.foreach { schema =>
        mysqldb.run(schema.drop).futureValue(mysqlInitPc)
      }
    } finally {
      super.afterEach()
    }
  }

  override def afterAll(): Unit = {
    Try(mysqldb.close())
    super.afterAll()
  }
}

@viktortnk
Copy link
Contributor

I don't want to make the sample project to depend on Slick, but if somebody want to implement this with plain jdbc and send a PR, they are more than welcome

@fokot
Copy link
Contributor Author

fokot commented Sep 21, 2016

I created even simpler PostgresReadyChecker with pure java/scala libraries (only org.postgresql.Driver needs to be on path)

import java.sql.DriverManager

import com.whisk.docker.{DockerCommandExecutor, DockerContainerState, DockerReadyChecker}

import scala.concurrent.ExecutionContext
import scala.util.Try

class PostgresReadyChecker(dbname: String, username: String, password: String) extends DockerReadyChecker {

override def apply(container: DockerContainerState)(implicit docker: DockerCommandExecutor, ec: ExecutionContext) =
    container.getPorts().map(x => x.values.head).map(port =>
      Try {
        Class.forName("org.postgresql.Driver")
        val connection = DriverManager.getConnection(s"jdbc:postgresql://${docker.host}:$port/$dbname", username, password)
        if (connection != null) {
          connection.close()
          true
        } else {
          false
        }
      }.getOrElse(false)
    )

And then it can be used as loped checker

  val postgresContainer = DockerContainer("postgres:9.5")
    .withPorts((5432, Some(PostgresAdvertisedPort)))
    .withEnv(s"POSTGRES_USER=${db.getString("user")}", s"POSTGRES_PASSWORD=${db.getString("password")}")
    .withReadyChecker(
      new PostgresReadyChecker(DbName, db.getString("user"), db.getString("password")).looped(10, 1 second)
    )

Usually after 6-7 seconds it is started.

Code is here
https://github.com/fokot/docker-scala/blob/master/src/test/scala/fokot/docker/scala/service/PostgresReadyChecker.scala
and here
https://github.com/fokot/docker-scala/blob/master/src/test/scala/fokot/docker/scala/service/CatRepositorySlickTest.scala
I would do a PR but i don't know where (to which package) as I couldn't even find HasMysqlDatabase. So tell me and I can contribute :)

@viktortnk
Copy link
Contributor

Cool, thanks. Could you please adding ability to pass port (but keep default).
Otherwise this code can easily fail on multiple port mapping:

container.getPorts().map(x => x.values.head)

Make a PR to samples module please. You can put ReadyChecked into that file
https://github.com/whisklabs/docker-it-scala/blob/master/samples/src/main/scala/com/whisk/docker/DockerPostgresService.scala

@fokot
Copy link
Contributor Author

fokot commented Sep 22, 2016

I needed to expose port because otherwise java couldn't access postgres other than in another container with link...

@fokot fokot closed this as completed Sep 22, 2016
@fokot fokot reopened this Sep 22, 2016
@fokot
Copy link
Contributor Author

fokot commented Sep 22, 2016

@fokot fokot closed this as completed Sep 22, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants