All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.podval.tools.cloudrun.StatusTracker.scala Maven / Gradle / Ivy

package org.podval.tools.cloudrun

import com.google.api.services.run.v1.model.GoogleCloudRunV1Condition
import org.slf4j.Logger
import scala.jdk.CollectionConverters.IterableHasAsScala

// inspired by
// https://github.com/twistedpair/google-cloud-sdk/blob/9d6a1cf6238702560b22944089355eff06b5c216/google-cloud-sdk/lib/googlecloudsdk/api_lib/util/waiter.py
final class StatusTracker(
  log: Logger,
  preStartSleepMs: Int = 500,
  sleepMs: Int = 500,
  stages: Seq[StatusTracker.Stage]
) {
  def track(): Unit = {
    if (preStartSleepMs > 0) Thread.sleep(preStartSleepMs)

    val threads: Seq[Thread] = for (stage <- stages)
      yield new Thread(() => new StageTracker(stage).track())

    for (thread <- threads) thread.start()
    for (thread <- threads) thread.join()
  }

  private final class StageTracker(stage: StatusTracker.Stage) {
    private var done: Boolean = false
    private var previous: Map[String, GoogleCloudRunV1Condition] = Map.empty

    def track(): Unit = while (!done) {
      val current: Map[String, GoogleCloudRunV1Condition] = stage.poll()

      val newMessages: Set[String] = StatusTracker.getNewMessages(previous, current)
        .map(message => stage.name + ": " + message)

      if (newMessages.nonEmpty) log.warn(newMessages.mkString("\n"))

      // TODO is it really done if Cloud Run is retrying *something* in 10 minutes?
      //val retry: Option[GoogleCloudRunV1Condition] = previous.get("Retry")
      //retry.foreach(retry => logger.warn(StatusTracker.toString(retry)))
      done = current.nonEmpty && current.values
        .filterNot(StatusTracker.isRetry)
        .forall(StatusTracker.isDone)

      previous = current

      if (!done) Thread.sleep(sleepMs)
    }
  }
}

object StatusTracker {

  final case class Stage(
    name: String,
    getConditions: () => java.util.List[GoogleCloudRunV1Condition]
  ) {
    def poll(): Map[String, GoogleCloudRunV1Condition] = Option(getConditions())
      .getOrElse(java.util.Collections.emptyList())
      .asScala
      .map(condition => condition.getType -> condition)
      .toMap
  }

  private def isRetry(condition: GoogleCloudRunV1Condition): Boolean = condition.getType   == "Retry"

  private def isDone (condition: GoogleCloudRunV1Condition): Boolean = condition.getStatus == "True"

  private def getNewMessages(
    previous: Map[String, GoogleCloudRunV1Condition],
    current: Map[String, GoogleCloudRunV1Condition]
  ): Set[String] = current.values
    .filterNot(isRetry)
    .filterNot(condition => previous.get(condition.getType).contains(condition))
    .filterNot(_.getMessage == null)
    .map(_.getMessage)
    .toSet

//  private def toString(condition: GoogleCloudRunV1Condition): String = {
//    val message: Option[String] = Option(condition.getMessage)
//    val reason: Option[String] = Option(condition.getReason)
//    val string: String =
//      message.map(message => s"[$message] ").getOrElse("") +
//      reason.map(reason => s"($reason)").getOrElse("")
//    s"${condition.getType}=${condition.getStatus} $string"
//  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy