whisklabs/docker-it-scala

Cannot use ready checker and log line receiver together

cb372 opened this issue · 3 comments

cb372 commented

Thanks for a great library!

I've found that I can use either withReadyChecker(DockerReadyChecker.LogLineContains("foo")) or withLogLineReceiver(LogLineReceiver(...)), but I can't use both at the same time. If I add the log line receiver then the ready checker never fires.

Is this expected?

Ideally I'd like to do the following:

  • use a ready checker to wait until the container outputs a certain line
  • use a log line receiver to write the container's output to a file for easy debugging after my tests complete

For you interest, I made a workaround for this problem creating a LogLineReciever that can also be used as readyChecker.

import com.whisk.docker.{DockerCommandExecutor, DockerContainerState, DockerReadyChecker, LogLineReceiver}
import grizzled.slf4j.Logging

import scala.concurrent.{ExecutionContext, Promise}

class BufferingLogLineReceiver(linesToKeep: Int = 100) extends LogLineReceiver(true, _ => {}) with Logging {
  import BufferingLogLineReceiver._
  private var buffer = Vector.empty[String]
  private var checkers: List[String => Unit] = List.empty

  override val f = (logLine: String) => {
    addToBuffer(logLine)
    callCheckers(logLine)
  }

  private def addToBuffer(content: String): Unit = {
    buffer = (buffer ++ content.split("\n")) takeRight linesToKeep
  }

  private def callCheckers(content: String): Unit = {
    checkers.foreach(_.apply(content))
  }

  def printAsError(): Unit = {
    if (buffer.length == linesToKeep) {
      error(s"${buffer.mkString("\n")}\n...}")
    } else {
      error(buffer.mkString("\n"))
    }
  }

  def logLineContains(partOfLine: String): DockerReadyChecker = {
    val checker = new SimpleContainsLineReadyCheck(partOfLine)
    checkers = checkers :+ checker.checkLine _
    //process already collected buffer
    buffer.foreach(checker.checkLine)
    checker
  }

}

object BufferingLogLineReceiver {

  private class SimpleContainsLineReadyCheck(partOfLine: String) extends DockerReadyChecker {
    val complete: Promise[Boolean] = Promise()

    override def apply(container: DockerContainerState)(implicit docker: DockerCommandExecutor, ec: ExecutionContext) = {
      complete.future
    }

    def checkLine(line: String): Unit = {
      if(line.toLowerCase.contains(partOfLine.toLowerCase)) {
        complete.trySuccess(true)
      }
    }
  }

}

Hope it helps you for now.

I think that LogLineReceiver is limited and the same time not properly working abstraction. I've just created #93 to address it in new version.

atais commented

@Vrolijkx

Defienietly easier to just log it during checking:

case class LogLineAndCheckContains(str: String) extends DockerReadyChecker {
  override def apply(container: DockerContainerState)(implicit docker: DockerCommandExecutor,
                                                      ec: ExecutionContext): Future[Boolean] = {
    for {
      id <- container.id
      _ <- docker.withLogStreamLinesRequirement(id, withErr = true) {
        s =>
          print(s)
          s.contains(str)
      }
    } yield {
      true
    }
  }
}