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

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

package org.podval.tools.cloudrun

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.{YAMLFactory, YAMLGenerator}
import com.google.auth.oauth2.ServiceAccountCredentials
import com.google.auth.http.HttpCredentialsAdapter
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport
import com.google.api.client.googleapis.services.AbstractGoogleClientRequest
import com.google.api.client.json.{JsonFactory, JsonObjectParser}
import com.google.api.client.json.gson.GsonFactory
import com.google.api.services.run.v1.{CloudRunScopes, CloudRun => GoogleCloudRun}
import com.google.api.services.run.v1.model.{Configuration, Revision, Route, Service, Status}
import org.podval.tools.cloudrun.ServiceExtender.Operations
import org.slf4j.Logger
import java.io.{ByteArrayInputStream, InputStream}
import java.nio.charset.Charset
import scala.jdk.CollectionConverters.IterableHasAsScala
import scala.io.Source

// Note: see https://github.com/googleapis/google-cloud-java
final class CloudRun(
  serviceAccountKey: String,
  val region: String,
  val log: Logger
) {
  // Note: see https://github.com/googleapis/google-auth-library-java#google-auth-library-oauth2-http
  // (what is ServiceAccountJwtAccessCredentials.fromStream(keyStream) for?)
  private val credentials: ServiceAccountCredentials = ServiceAccountCredentials
    .fromStream(CloudRun.string2stream(serviceAccountKey))
    .createScoped(CloudRunScopes.all)
    .asInstanceOf[ServiceAccountCredentials]

  private val client: GoogleCloudRun = new GoogleCloudRun.Builder(
    GoogleNetHttpTransport.newTrustedTransport,
    CloudRun.jsonFactory,
    // Note: see https://github.com/googleapis/google-auth-library-java#using-credentials-with-google-http-client
    new HttpCredentialsAdapter(credentials)
  )
    .setRootUrl(s"https://$region-run.googleapis.com")
    .setApplicationName(CloudRun.applicationName)
    .build()

  def projectId: String = credentials.getProjectId

  private def namespace: String = "namespaces/" + projectId

  def listServices: List[Service] =
    getList(_.services().list(namespace))(_.getItems)

  def createService(service: Service): Service =
    get(_.services().create(namespace, service))

  def getService(serviceName: String): Service =
    get(_.services().get(s"$namespace/services/$serviceName"))

  def replaceService(service: Service): Service =
    get(_.services().replaceService(s"$namespace/services/${service.name}", service))

  def deleteService(serviceName: String): Status =
    get(_.services().delete(s"$namespace/services/$serviceName"))

  def listRevisions: List[Revision] =
    getList(_.revisions().list(namespace))(_.getItems)

  def getRevision(revisionName: String): Revision =
    get(_.revisions().get(s"$namespace/revisions/$revisionName"))

  def deleteRevision(revisionName: String): Status =
    get(_.revisions().delete(s"$namespace/revisions/$revisionName"))

  def listRoutes: List[Route] =
    getList(_.routes().list(namespace))(_.getItems)

  def getRoute(routeName: String): Route =
    get(_.routes().get(s"$namespace/routes/$routeName"))

  def listConfigurations: List[Configuration] =
    getList(_.configurations().list(namespace))(_.getItems)

  def getConfiguration(configurationName: String): Configuration =
    get(_.configurations().get(s"$namespace/configurations/$configurationName"))

  private def getList[R, T](
    request: GoogleCloudRun#Namespaces => AbstractGoogleClientRequest[R])(
    items: R => java.util.List[T]
  ): List[T] =
    items(get(request)).asScala.toList

  private def get[T](request: GoogleCloudRun#Namespaces => AbstractGoogleClientRequest[T]): T =
    request(client.namespaces()).execute()
}

object CloudRun {

  val applicationName: String = getClass.getPackage.getName

  val applicationVersion: String = Option(getClass.getPackage.getImplementationVersion).getOrElse("unknown version")

  private def jsonFactory: JsonFactory = GsonFactory.getDefaultInstance

  // Note: when parsing Service YAML, objectMapper.readValue(inputStream, classOf[Service]) throws
  //   java.lang.IllegalArgumentException:
  //   Can not set com.google.api.services.run.v1.model.ObjectMeta field
  //   com.google.api.services.run.v1.model.Service.metadata to java.util.LinkedHashMap
  // so I convert YAML into a JSON string and then parse it using Google's parser:
  def yamlFile2[T](clazz: Class[T], yamlFilePath: String): T = {
    val json: String = yamlObjectMapper
      .readTree(file2string(yamlFilePath))
      .toString

    new JsonObjectParser(jsonFactory).parseAndClose(
      string2stream(json),
      utf8,
      clazz
    )
  }

  def json2yaml(value: AnyRef): String = yamlObjectMapper.writeValueAsString(value)

  private def yamlObjectMapper: ObjectMapper = {
    val yamlFactory: YAMLFactory = new YAMLFactory
    yamlFactory.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER) // suppress leading "---"
    new ObjectMapper(yamlFactory)
  }

  def file2string(path: String): String = source2string(Source.fromFile(path))

  // def stream2string(stream: InputStream): String = source2string(Source.fromInputStream(stream)(new Codec(utf8)))

  private def source2string(source: Source): String = {
    val result: String = source.getLines().mkString("\n")
    source.close()
    result
  }

  private def string2stream(string: String): InputStream = new ByteArrayInputStream(string.getBytes(utf8))

  private def utf8: Charset = Charset.forName("UTF-8")
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy