Delete projects that are no longer used by any releasable component (#18180)

* remove pureconfig-utils

* remove db-utils

* remove doobie slf4j

* remove flyway testing

* remove grpc-reverse-proxy

* remove grpc-server-reflection-client

* remove oracle-testing

* update release artifacts
This commit is contained in:
mziolekda 2024-01-18 09:37:45 +01:00 committed by GitHub
parent c6cf5b787b
commit 47c9baa28c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 0 additions and 1676 deletions

View File

@ -1,62 +0,0 @@
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
load(
"//bazel_tools:scala.bzl",
"da_scala_library",
"da_scala_test",
"lf_scalacopts",
)
da_scala_library(
name = "pureconfig-utils",
srcs = glob(["src/main/scala/**/*.scala"]),
scala_deps = [
"@maven//:com_chuusai_shapeless",
"@maven//:com_github_pureconfig_pureconfig_core",
"@maven//:com_github_pureconfig_pureconfig_generic",
"@maven//:org_apache_pekko_pekko_http_core",
"@maven//:com_typesafe_scala_logging_scala_logging",
"@maven//:com_github_scopt_scopt",
"@maven//:org_scalaz_scalaz_core",
"@maven//:org_parboiled_parboiled",
],
scalacopts = lf_scalacopts,
visibility = [
"//visibility:public",
],
runtime_deps = [
"@maven//:ch_qos_logback_logback_classic",
],
deps = [
"//ledger/ledger-api-common",
"//libs-scala/db-utils",
"//libs-scala/jwt",
"//observability/metrics",
"@maven//:com_auth0_java_jwt",
"@maven//:com_typesafe_config",
"@maven//:io_netty_netty_handler",
],
)
da_scala_test(
name = "tests",
size = "medium",
srcs = glob(["src/test/scala/**/*.scala"]),
scala_deps = [
"@maven//:com_chuusai_shapeless",
"@maven//:com_github_pureconfig_pureconfig_core",
"@maven//:com_github_pureconfig_pureconfig_generic",
"@maven//:org_scalatest_scalatest_core",
"@maven//:org_scalatest_scalatest_matchers_core",
"@maven//:org_scalatest_scalatest_shouldmatchers",
"@maven//:org_scalatest_scalatest_wordspec",
],
scalacopts = lf_scalacopts,
deps = [
":pureconfig-utils",
"//libs-scala/jwt",
"//observability/metrics",
"@maven//:org_scalatest_scalatest_compatible",
],
)

View File

@ -1,148 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.pureconfigutils
import org.apache.pekko.http.scaladsl.model.Uri
import com.auth0.jwt.algorithms.Algorithm
import com.daml.dbutils.JdbcConfig
import com.daml.jwt.{ECDSAVerifier, HMAC256Verifier, JwksVerifier, JwtVerifierBase, RSA256Verifier}
import com.daml.ledger.api.tls.TlsConfiguration
import com.daml.platform.services.time.TimeProviderType
import io.netty.handler.ssl.ClientAuth
import pureconfig.error.{CannotConvert, ConvertFailure, FailureReason}
import pureconfig.{ConfigObjectCursor, ConfigReader, ConvertHelpers}
import pureconfig.generic.semiauto.deriveReader
import com.daml.jwt.{Error => JwtError}
import scalaz.\/
import scalaz.syntax.std.option._
import java.nio.file.Path
import java.io.File
import com.daml.metrics.api.reporters.MetricsReporter
final case class HttpServerConfig(
address: String,
port: Int,
portFile: Option[Path] = None,
https: Option[HttpsConfig] = None,
)
final case class HttpsConfig(
certChainFile: File,
privateKeyFile: File,
trustCollectionFile: Option[File] = None,
) {
def tlsConfiguration: TlsConfiguration =
TlsConfiguration(
enabled = true,
certChainFile = Some(certChainFile),
privateKeyFile = Some(privateKeyFile),
trustCollectionFile = trustCollectionFile,
clientAuth = ClientAuth.NONE,
)
}
final case class LedgerTlsConfig(
enabled: Boolean = false,
certChainFile: Option[File] = None,
privateKeyFile: Option[File] = None,
trustCollectionFile: Option[File] = None,
) {
def tlsConfiguration: TlsConfiguration =
TlsConfiguration(enabled, certChainFile, privateKeyFile, trustCollectionFile)
}
final case class LedgerApiConfig(
address: String,
port: Int,
tls: LedgerTlsConfig = LedgerTlsConfig(),
)
object TokenVerifierConfig {
private val knownTokenVerifiers: Map[String, String => JwtError \/ JwtVerifierBase] =
Map(
"rs256-crt" -> (RSA256Verifier.fromCrtFile(_)),
"es256-crt" -> (ECDSAVerifier
.fromCrtFile(_, Algorithm.ECDSA256(_, null))),
"es512-crt" -> (ECDSAVerifier
.fromCrtFile(_, Algorithm.ECDSA512(_, null))),
"rs256-jwks" -> (valueStr =>
\/.attempt(JwksVerifier(valueStr))(e => JwtError(Symbol("RS256"), e.getMessage))
),
)
private val unsafeTokenVerifier: (String, String => JwtError \/ JwtVerifierBase) =
"hs256-unsafe" -> (HMAC256Verifier(_))
def extractByType(
typeStr: String,
valueStr: String,
objectCursor: ConfigObjectCursor,
): ConfigReader.Result[JwtVerifierBase] = {
def convertFailure(msg: String) = {
ConfigReader.Result.fail(
ConvertFailure(
CannotConvert(typeStr, "JwtVerifier", msg),
objectCursor,
)
)
}
(knownTokenVerifiers + unsafeTokenVerifier)
.get(typeStr)
.cata(
{ conv =>
conv(valueStr).fold(
err => convertFailure(s"Failed to create $typeStr verifier: $err"),
(Right(_)),
)
},
convertFailure(s"value not one of ${knownTokenVerifiers.keys.mkString(", ")}"),
)
}
}
object SharedConfigReaders {
def catchConvertError[A, B](f: String => Either[String, B])(implicit
B: reflect.ClassTag[B]
): String => Either[FailureReason, B] =
s => f(s).left.map(CannotConvert(s, B.toString, _))
implicit val tokenVerifierCfgRead: ConfigReader[JwtVerifierBase] =
ConfigReader.fromCursor { cur =>
for {
objCur <- cur.asObjectCursor
typeCur <- objCur.atKey("type")
typeStr <- typeCur.asString
valueCur <- objCur.atKey("uri")
valueStr <- valueCur.asString
ident <- TokenVerifierConfig.extractByType(typeStr, valueStr, objCur)
} yield ident
}
implicit val uriCfgReader: ConfigReader[Uri] =
ConfigReader.fromString[Uri](ConvertHelpers.catchReadError(s => Uri(s)))
implicit val timeProviderTypeCfgReader: ConfigReader[TimeProviderType] = {
ConfigReader.fromString[TimeProviderType](catchConvertError { s =>
s.toLowerCase() match {
case "static" => Right(TimeProviderType.Static)
case "wall-clock" => Right(TimeProviderType.WallClock)
case _ => Left("not one of 'static' or 'wall-clock'")
}
})
}
implicit val metricReporterReader: ConfigReader[MetricsReporter] = {
ConfigReader.fromString[MetricsReporter](ConvertHelpers.catchReadError { s =>
MetricsReporter.parseMetricsReporter(s.toLowerCase())
})
}
implicit val jdbcCfgReader: ConfigReader[JdbcConfig] = deriveReader[JdbcConfig]
implicit val httpsCfgReader: ConfigReader[HttpsConfig] = deriveReader[HttpsConfig]
implicit val httpServerCfgReader: ConfigReader[HttpServerConfig] =
deriveReader[HttpServerConfig]
implicit val ledgerTlsCfgReader: ConfigReader[LedgerTlsConfig] =
deriveReader[LedgerTlsConfig]
implicit val ledgerApiConfReader: ConfigReader[LedgerApiConfig] =
deriveReader[LedgerApiConfig]
}

View File

@ -1,110 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.pureconfigutils
import com.daml.jwt.JwtVerifierBase
import com.daml.metrics.{HistogramDefinition, MetricsConfig}
import org.scalatest.Inside.inside
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AsyncWordSpec
import pureconfig.error.{ConfigReaderFailures, ConvertFailure}
import pureconfig.generic.semiauto.deriveReader
import pureconfig.{ConfigReader, ConfigSource}
import java.nio.file.Paths
import com.daml.metrics.api.reporters.MetricsReporter
import scala.concurrent.duration._
class SharedConfigReadersTest extends AsyncWordSpec with Matchers {
import SharedConfigReaders._
case class SampleServiceConfig(
server: HttpServerConfig,
ledgerApi: LedgerApiConfig,
metrics: MetricsConfig,
)
implicit val serviceConfigReader: ConfigReader[SampleServiceConfig] =
deriveReader[SampleServiceConfig]
case class DummyConfig(tokenVerifier: JwtVerifierBase)
implicit val dummyCfgReader: ConfigReader[DummyConfig] = deriveReader[DummyConfig]
"should be able to parse a sample config with shared config objects" in {
val conf = """
|{
| server {
| address = "127.0.0.1"
| port = 8890
| port-file = "port-file"
| }
| ledger-api {
| address = "127.0.0.1"
| port = 8098
| }
| metrics {
| reporter = "console"
| reporting-interval = 10s
| histograms = [
| {
| name-regex = "test"
| bucket-boundaries = [ 2.1 ]
| }
| ]
| }
|}
|""".stripMargin
val expectedConf = SampleServiceConfig(
HttpServerConfig("127.0.0.1", 8890, Some(Paths.get("port-file"))),
LedgerApiConfig("127.0.0.1", 8098),
MetricsConfig(
MetricsReporter.Console,
10.seconds,
histograms = Seq(
HistogramDefinition(
"test",
Seq(2.1d),
)
),
),
)
ConfigSource.string(conf).load[SampleServiceConfig] shouldBe Right(expectedConf)
}
"should be able to parse minimal required metrics config" in {
val conf = """
|{
| reporter = "console"
| reporting-interval = 10s
|}
|""".stripMargin
val expectedMetricsConfig = MetricsConfig(
MetricsReporter.Console,
10.seconds,
histograms = Seq.empty,
)
ConfigSource.string(conf).load[MetricsConfig] shouldBe Right(expectedMetricsConfig)
}
"should fail on loading unknown tokenVerifiers" in {
val conf = """
|{
| token-verifier {
| type = "foo"
| uri = "bar"
| }
|}
|""".stripMargin
val cfg = ConfigSource.string(conf).load[DummyConfig]
inside(cfg) { case Left(ConfigReaderFailures(ex)) =>
ex shouldBe a[ConvertFailure]
}
}
}

View File

@ -1,62 +0,0 @@
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
load(
"//bazel_tools:scala.bzl",
"da_scala_library",
"da_scala_test",
"lf_scalacopts",
)
da_scala_library(
name = "db-utils",
srcs = glob(["src/main/scala/**/*.scala"]),
scala_deps = [
"@maven//:com_github_scopt_scopt",
"@maven//:org_scalaz_scalaz_core",
"@maven//:org_tpolecat_doobie_core",
"@maven//:org_typelevel_cats_core",
"@maven//:org_typelevel_cats_effect",
"@maven//:org_typelevel_cats_kernel",
"@maven//:com_typesafe_scala_logging_scala_logging",
],
scala_runtime_deps = [
"@maven//:org_tpolecat_doobie_postgres",
],
scalacopts = lf_scalacopts,
tags = ["maven_coordinates=com.daml:db-utils:__VERSION__"],
visibility = [
"//visibility:public",
],
runtime_deps = [
"@maven//:ch_qos_logback_logback_classic",
],
deps = [
"//libs-scala/scala-utils",
"@maven//:com_zaxxer_HikariCP",
"@maven//:io_dropwizard_metrics_metrics_core",
],
)
da_scala_test(
name = "tests",
size = "medium",
srcs = glob(["src/test/scala/**/*.scala"]),
scala_deps = [
"@maven//:org_scalatest_scalatest_core",
"@maven//:org_scalatest_scalatest_matchers_core",
"@maven//:org_scalatest_scalatest_shouldmatchers",
"@maven//:org_scalatest_scalatest_wordspec",
"@maven//:org_scalaz_scalaz_core",
"@maven//:org_tpolecat_doobie_core",
"@maven//:org_typelevel_cats_core",
"@maven//:org_typelevel_cats_effect",
"@maven//:org_typelevel_cats_kernel",
],
scalacopts = lf_scalacopts,
deps = [
":db-utils",
"//libs-scala/scala-utils",
"@maven//:org_scalatest_scalatest_compatible",
],
)

View File

@ -1,78 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.dbutils
import cats.effect.{Blocker, ContextShift, IO}
import com.codahale.metrics.MetricRegistry
import com.zaxxer.hikari.{HikariConfig, HikariDataSource}
import doobie._
import java.io.Closeable
import java.util.concurrent.Executors.newWorkStealingPool
import javax.sql.DataSource
import scala.concurrent.ExecutionContext
object Connection {
type T = Transactor.Aux[IO, Unit]
def connect(cfg: JdbcConfig)(implicit
cs: ContextShift[IO]
): T =
Transactor
.fromDriverManager[IO](cfg.driver, cfg.url, cfg.user, cfg.password)(
IO.ioConcurrentEffect(cs),
cs,
)
}
object ConnectionPool {
type PoolSize = Int
object PoolSize {
val Integration = 2
val Production = 8
}
type T = Transactor.Aux[IO, _ <: DataSource with Closeable]
def connect(
c: JdbcConfig,
metricRegistry: Option[MetricRegistry] = None,
)(implicit
ec: ExecutionContext,
cs: ContextShift[IO],
): (DataSource with Closeable, T) = {
val ds = dataSource(c, metricRegistry)
(
ds,
Transactor
.fromDataSource[IO](
ds,
connectEC = ec,
blocker = Blocker liftExecutorService newWorkStealingPool(c.poolSize),
)(IO.ioConcurrentEffect(cs), cs),
)
}
private[this] def dataSource(
jc: JdbcConfig,
metricRegistry: Option[MetricRegistry],
) = {
import jc._
val c = new HikariConfig
c.setJdbcUrl(url)
c.setUsername(user)
c.setPassword(password)
c.setMinimumIdle(jc.minIdle)
c.setConnectionTimeout(jc.connectionTimeout.toMillis)
c.setMaximumPoolSize(poolSize)
c.setIdleTimeout(jc.idleTimeout.toMillis)
metricRegistry match {
case Some(mr) => c.setMetricRegistry(mr)
case None =>
}
new HikariDataSource(c)
}
}

View File

@ -1,196 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.dbutils
import ConnectionPool.PoolSize
import com.typesafe.scalalogging.StrictLogging
import scalaz.std.either._
import scalaz.std.option._
import scalaz.syntax.std.option._
import scalaz.syntax.traverse._
import scalaz.{Show, StateT, \/}
import java.io.File
import scala.concurrent.duration._
import scala.util.Try
object DBConfig {
final case class JdbcConfigDefaults(
supportedJdbcDrivers: Set[String],
defaultDriver: Option[String] = None,
)
}
final case class JdbcConfig(
driver: String,
url: String,
user: String,
password: String,
poolSize: Int,
minIdle: Int = JdbcConfig.MinIdle,
connectionTimeout: FiniteDuration = JdbcConfig.ConnectionTimeout,
idleTimeout: FiniteDuration = JdbcConfig.IdleTimeout,
tablePrefix: String = "",
)
abstract class ConfigCompanion[A, ReadCtx](name: String) {
import com.daml.scalautil.ExceptionOps._
protected val indent: String = List.fill(8)(" ").mkString
// If we don't DRY our keys, we will definitely forget to remove one. We're
// less likely to make a mistake in backend-specific conf if redundant data
// isn't there. -SC
type Fields[Z] = StateT[({ type l[a] = Either[String, a] })#l, Map[String, String], Z]
protected[this] def create(implicit
readCtx: ReadCtx
): Fields[A]
implicit final def `read instance`(implicit ctx: ReadCtx): scopt.Read[A] =
scopt.Read.reads { s =>
val x = implicitly[scopt.Read[Map[String, String]]].reads(s)
create.eval(x).fold(e => throw new IllegalArgumentException(e), identity)
}
protected def requiredField(k: String): Fields[String] =
StateT { m =>
m.get(k)
.filter(_.nonEmpty)
.map((m - k, _))
.toRight(s"Invalid $name, must contain '$k' field")
}
protected def optionalStringField(
k: String
): Fields[Option[String]] =
StateT { m => Right((m - k, m get k)) }
protected def optionalBooleanField(
k: String
): Fields[Option[Boolean]] =
optionalStringField(k).flatMap(ov => StateT liftM (ov traverse parseBoolean(k)).toEither)
protected def optionalIntField(k: String): Fields[Option[Int]] =
optionalStringField(k).flatMap(ov => StateT liftM (ov traverse parseInt(k)).toEither)
protected def optionalLongField(k: String): Fields[Option[Long]] =
optionalStringField(k).flatMap(ov => StateT liftM (ov traverse parseLong(k)).toEither)
import scalaz.syntax.std.string._
protected def parseBoolean(k: String)(v: String): String \/ Boolean =
v.parseBoolean.leftMap(e => s"$k=$v must be a boolean value: ${e.description}").disjunction
protected def parseLong(k: String)(v: String): String \/ Long =
v.parseLong.leftMap(e => s"$k=$v must be a int value: ${e.description}").disjunction
protected def parseInt(k: String)(v: String): String \/ Int =
v.parseInt.leftMap(e => s"$k=$v must be an int value: ${e.description}").disjunction
protected def requiredDirectoryField(k: String): Fields[File] =
requiredField(k).flatMap(s => StateT liftM directory(s))
protected def directory(s: String): Either[String, File] =
Try(new File(s).getAbsoluteFile).toEither.left
.map(e => e.description)
.flatMap { d =>
if (d.isDirectory) Right(d)
else Left(s"Directory does not exist: ${d.getAbsolutePath}")
}
}
object JdbcConfig
extends ConfigCompanion[JdbcConfig, DBConfig.JdbcConfigDefaults]("JdbcConfig")
with StrictLogging {
final val MinIdle = 8
final val IdleTimeout = 10000.millis // minimum according to log, defaults to 600s
final val ConnectionTimeout = 5000.millis
@scala.deprecated("do I need this?", since = "SC")
implicit val showInstance: Show[JdbcConfig] =
Show.shows(a =>
s"JdbcConfig(driver=${a.driver}, url=${a.url}, user=${a.user}, poolSize=${a.poolSize}, " +
s"minIdle=${a.minIdle}, connectionTimeout=${a.connectionTimeout}, idleTimeout=${a.idleTimeout}"
)
def help(otherOptions: String = "")(implicit jcd: DBConfig.JdbcConfigDefaults): String =
"Contains comma-separated key-value pairs. Where:\n" +
s"${indent}driver -- JDBC driver class name, ${jcd.supportedJdbcDrivers.mkString(", ")} supported right now,\n" +
s"${indent}url -- JDBC connection URL,\n" +
s"${indent}user -- database user name,\n" +
s"${indent}password -- database user password,\n" +
s"${indent}tablePrefix -- prefix for table names to avoid collisions, empty by default,\n" +
s"${indent}poolSize -- int value, specifies the max pool size for the database connection pool.\n" +
s"${indent}minIdle -- int value, specifies the min idle connections for database connection pool.\n" +
s"${indent}connectionTimeout -- long value, specifies the connection timeout for database connection pool.\n" +
s"${indent}idleTimeout -- long value, specifies the idle timeout for the database connection pool.\n" +
otherOptions +
s"${indent}Example: " + helpString(
"org.postgresql.Driver",
"jdbc:postgresql://localhost:5432/test?&ssl=true",
"postgres",
"password",
"table_prefix_",
PoolSize.Production.toString,
MinIdle.toString,
ConnectionTimeout.toString,
IdleTimeout.toString,
)
private[daml] def create(x: Map[String, String])(implicit
readCtx: DBConfig.JdbcConfigDefaults
): Either[String, JdbcConfig] =
create.eval(x)
override def create(implicit
readCtx: DBConfig.JdbcConfigDefaults
): Fields[JdbcConfig] = for {
driver <-
readCtx.defaultDriver.cata(
dd => optionalStringField("driver").map(_ getOrElse dd),
requiredField("driver"),
)
_ <- StateT liftM Either.cond(
readCtx.supportedJdbcDrivers(driver),
(),
s"$driver unsupported. Supported drivers: ${readCtx.supportedJdbcDrivers.mkString(", ")}",
)
url <- requiredField("url")
user <- requiredField("user")
password <- requiredField("password")
tablePrefix <- optionalStringField("tablePrefix").map(_ getOrElse "")
maxPoolSize <- optionalIntField("poolSize").map(_ getOrElse PoolSize.Production)
minIdle <- optionalIntField("minIdle").map(_ getOrElse MinIdle)
connTimeout <- optionalLongField("connectionTimeout")
.map(x => x.map(_.millis) getOrElse ConnectionTimeout)
idleTimeout <- optionalLongField("idleTimeout")
.map(x => x.map(_.millis) getOrElse IdleTimeout)
} yield JdbcConfig(
driver = driver,
url = url,
user = user,
password = password,
tablePrefix = tablePrefix,
poolSize = maxPoolSize,
minIdle = minIdle,
connectionTimeout = connTimeout,
idleTimeout = idleTimeout,
)
private def helpString(
driver: String,
url: String,
user: String,
password: String,
tablePrefix: String,
poolSize: String,
minIdle: String,
connectionTimeout: String,
idleTimeout: String,
): String =
s"""\"driver=$driver,url=$url,user=$user,password=$password,tablePrefix=$tablePrefix,poolSize=$poolSize,
|minIdle=$minIdle, connectionTimeout=$connectionTimeout,idleTimeout=$idleTimeout\"""".stripMargin
}

View File

@ -1,23 +0,0 @@
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
load(
"//bazel_tools:scala.bzl",
"da_scala_library",
"da_scala_test_suite",
)
da_scala_library(
name = "doobie-slf4j",
srcs = glob(["src/main/scala/**/*.scala"]),
scala_deps = [
"@maven//:org_tpolecat_doobie_core",
],
tags = ["maven_coordinates=com.daml:doobie-slf4j:__VERSION__"],
visibility = [
"//:__subpackages__",
],
deps = [
"@maven//:org_slf4j_slf4j_api",
],
)

View File

@ -1,43 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.doobie.logging
import org.slf4j.{Logger, LoggerFactory}
import doobie.util.log.{ExecFailure, LogHandler, ProcessingFailure, Success}
object Slf4jLogHandler {
def apply(clazz: Class[_]): LogHandler =
apply(LoggerFactory.getLogger(clazz))
def apply(logger: Logger): LogHandler =
LogHandler {
case Success(s, a, e1, e2) =>
logger.debug(s"""Successful Statement Execution:
|
| ${s.linesIterator.dropWhile(_.trim.isEmpty).mkString("\n ")}
|
| arguments = [${a.mkString(", ")}]
| elapsed = ${e1.toMillis.toString} ms exec + ${e2.toMillis.toString} ms processing (${(e1 + e2).toMillis.toString} ms total)
""".stripMargin)
case ProcessingFailure(s, a, e1, e2, t) =>
logger.error(s"""Failed Resultset Processing:
|
| ${s.linesIterator.dropWhile(_.trim.isEmpty).mkString("\n ")}
|
| arguments = [${a.mkString(", ")}]
| elapsed = ${e1.toMillis.toString} ms exec + ${e2.toMillis.toString} ms processing (failed) (${(e1 + e2).toMillis.toString} ms total)
| failure = ${t.getMessage}
""".stripMargin)
case ExecFailure(s, a, e1, t) =>
logger.error(s"""Failed Statement Execution:
|
| ${s.linesIterator.dropWhile(_.trim.isEmpty).mkString("\n ")}
|
| arguments = [${a.mkString(", ")}]
| elapsed = ${e1.toMillis.toString} ms exec (failed)
| failure = ${t.getMessage}
""".stripMargin)
}
}

View File

@ -1,23 +0,0 @@
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
load("//bazel_tools:scala.bzl", "da_scala_library")
da_scala_library(
name = "flyway-testing",
srcs = glob(["src/main/scala/**/*.scala"]),
scala_deps = [
"@maven//:org_scalactic_scalactic",
"@maven//:org_scalatest_scalatest_core",
"@maven//:org_scalatest_scalatest_matchers_core",
"@maven//:org_scalatest_scalatest_shouldmatchers",
"@maven//:org_scalatest_scalatest_wordspec",
],
tags = ["maven_coordinates=com.daml:flyway-testing:__VERSION__"],
visibility = ["//visibility:public"],
deps = [
"//libs-scala/crypto",
"@maven//:org_flywaydb_flyway_core",
"@maven//:org_scalatest_scalatest_compatible",
],
)

View File

@ -1,10 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
set -eou pipefail
for file in "$@"; do
shasum -a 256 "$file" | awk '{ print $1 }' > "$file.sha256"
done

View File

@ -1,81 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.flyway
import com.daml.crypto.MessageDigestPrototype
import org.flywaydb.core.Flyway
import org.flywaydb.core.api.configuration.FluentConfiguration
import org.flywaydb.core.api.resource.LoadableResource
import org.flywaydb.core.internal.scanner.{LocationScannerCache, ResourceNameCache, Scanner}
import org.scalatest.matchers.should.Matchers._
import org.scalatest.wordspec.AnyWordSpec
import java.io.{BufferedReader, FileNotFoundException}
import java.math.BigInteger
import java.nio.charset.Charset
import java.util
import scala.jdk.CollectionConverters._
abstract class AbstractImmutableMigrationsSpec extends AnyWordSpec {
protected def migrationsResourcePath: String
protected def migrationsMinSize: Int
protected def hashMigrationsScriptPath: String
private def flywayScanner(configuration: FluentConfiguration) =
new Scanner(
classOf[Object],
util.Arrays.asList(configuration.getLocations: _*),
getClass.getClassLoader,
configuration.getEncoding,
false,
false,
new ResourceNameCache,
new LocationScannerCache,
false,
)
private def readExpectedDigest(
digestFile: String,
resourceScanner: Scanner[_],
): String = {
val resource = Option(resourceScanner.getResource(digestFile))
.getOrElse(
throw new FileNotFoundException(
s"""\"$digestFile\" is missing. If you are introducing a new Flyway migration step, you need to create an SHA-256 digest file by running $hashMigrationsScriptPath."""
)
)
new BufferedReader(resource.read()).readLine()
}
private def computeCurrentDigest(resource: LoadableResource, encoding: Charset): String = {
val sha256 = MessageDigestPrototype.Sha256.newDigest
new BufferedReader(resource.read())
.lines()
.forEach(line => sha256.update((line + "\n").getBytes(encoding)))
val digest = sha256.digest()
String.format(s"%0${digest.length * 2}x", new BigInteger(1, digest))
}
"migration files" should {
"never change, according to their accompanying digest file" in {
val configuration = Flyway
.configure()
.locations(s"classpath:/$migrationsResourcePath")
val resourceScanner = flywayScanner(configuration)
val resources = resourceScanner.getResources("", ".sql").asScala.toSeq
resources.size should be >= migrationsMinSize
resources.foreach { resource =>
val migrationFile = resource.getRelativePath
val digestFile = migrationFile + ".sha256"
val expectedDigest = readExpectedDigest(digestFile, resourceScanner)
val currentDigest = computeCurrentDigest(resource, configuration.getEncoding)
assert(
currentDigest == expectedDigest,
s"""The contents of the migration file "$migrationFile" have changed! Migrations are immutable; you must not change their contents or their digest.""",
)
}
}
}
}

View File

@ -1,48 +0,0 @@
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
load(
"//bazel_tools:scala.bzl",
"da_scala_library",
"da_scala_test_suite",
)
da_scala_library(
name = "grpc-reverse-proxy",
srcs = glob(["src/main/scala/**/*.scala"]),
tags = ["maven_coordinates=com.daml:grpc-reverse-proxy:__VERSION__"],
visibility = [
"//:__subpackages__",
],
deps = [
"//libs-scala/grpc-server-reflection-client",
"//libs-scala/resources",
"//libs-scala/resources-grpc",
"@maven//:com_google_guava_guava",
"@maven//:io_grpc_grpc_api",
"@maven//:io_grpc_grpc_services",
"@maven//:io_grpc_grpc_stub",
"@maven//:io_netty_netty_common",
"@maven//:io_netty_netty_transport",
],
)
da_scala_test_suite(
name = "test",
srcs = glob(["src/test/scala/**/*.scala"]),
deps = [
":grpc-reverse-proxy",
"//libs-scala/grpc-test-utils",
"//libs-scala/grpc-utils",
"//libs-scala/resources",
"//libs-scala/resources-grpc",
"@maven//:com_google_protobuf_protobuf_java",
"@maven//:io_grpc_grpc_api",
"@maven//:io_grpc_grpc_core",
"@maven//:io_grpc_grpc_inprocess",
"@maven//:io_grpc_grpc_services",
"@maven//:io_grpc_grpc_stub",
"@maven//:io_netty_netty_common",
"@maven//:io_netty_netty_transport",
],
)

View File

@ -1,59 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.grpc
import io.grpc.MethodDescriptor.MethodType
import io.grpc.{CallOptions, Channel, ClientCall, MethodDescriptor, ServerMethodDefinition}
import io.grpc.stub.{ClientCalls, ServerCalls, StreamObserver}
private[grpc] object ForwardCall {
def apply[ReqT, RespT](
method: MethodDescriptor[ReqT, RespT],
backend: Channel,
options: CallOptions,
): ServerMethodDefinition[ReqT, RespT] = {
val forward = () => backend.newCall(method, options)
ServerMethodDefinition.create[ReqT, RespT](
method,
method.getType match {
case MethodType.UNARY =>
ServerCalls.asyncUnaryCall(new UnaryMethod(forward))
case MethodType.CLIENT_STREAMING =>
ServerCalls.asyncClientStreamingCall(new ClientStreamingMethod(forward))
case MethodType.SERVER_STREAMING =>
ServerCalls.asyncServerStreamingCall(new ServerStreamingMethod(forward))
case MethodType.BIDI_STREAMING =>
ServerCalls.asyncBidiStreamingCall(new BidiStreamMethod(forward))
case MethodType.UNKNOWN =>
sys.error(s"${method.getFullMethodName} has MethodType.UNKNOWN")
},
)
}
private final class UnaryMethod[ReqT, RespT](call: () => ClientCall[ReqT, RespT])
extends ServerCalls.UnaryMethod[ReqT, RespT] {
override def invoke(request: ReqT, responseObserver: StreamObserver[RespT]): Unit =
ClientCalls.asyncUnaryCall(call(), request, responseObserver)
}
private final class ClientStreamingMethod[ReqT, RespT](call: () => ClientCall[ReqT, RespT])
extends ServerCalls.ClientStreamingMethod[ReqT, RespT] {
override def invoke(responseObserver: StreamObserver[RespT]): StreamObserver[ReqT] =
ClientCalls.asyncClientStreamingCall(call(), responseObserver)
}
private final class ServerStreamingMethod[ReqT, RespT](call: () => ClientCall[ReqT, RespT])
extends ServerCalls.ServerStreamingMethod[ReqT, RespT] {
override def invoke(request: ReqT, responseObserver: StreamObserver[RespT]): Unit =
ClientCalls.asyncServerStreamingCall(call(), request, responseObserver)
}
private final class BidiStreamMethod[ReqT, RespT](call: () => ClientCall[ReqT, RespT])
extends ServerCalls.BidiStreamingMethod[ReqT, RespT] {
override def invoke(responseObserver: StreamObserver[RespT]): StreamObserver[ReqT] =
ClientCalls.asyncBidiStreamingCall(call(), responseObserver)
}
}

View File

@ -1,26 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.grpc
import java.io.{ByteArrayInputStream, InputStream}
import com.daml.grpc.reflection.ServiceDescriptorInfo
import com.google.common.io.ByteStreams
import io.grpc.{CallOptions, Channel, MethodDescriptor, ServerServiceDefinition}
private[grpc] object ForwardService {
def apply(backend: Channel, service: ServiceDescriptorInfo): ServerServiceDefinition =
service.methods
.map(_.toMethodDescriptor(ByteArrayMarshaller, ByteArrayMarshaller))
.map(ForwardCall(_, backend, CallOptions.DEFAULT))
.foldLeft(ServerServiceDefinition.builder(service.fullServiceName))(_ addMethod _)
.build()
private object ByteArrayMarshaller extends MethodDescriptor.Marshaller[Array[Byte]] {
override def parse(input: InputStream): Array[Byte] = ByteStreams.toByteArray(input)
override def stream(bytes: Array[Byte]): InputStream = new ByteArrayInputStream(bytes)
}
}

View File

@ -1,49 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.grpc
import com.daml.grpc.reflection.{ServerReflectionClient, ServiceDescriptorInfo}
import com.daml.resources.grpc.GrpcResourceOwnerFactories
import com.daml.resources.{AbstractResourceOwner, HasExecutionContext, ResourceOwnerFactories}
import io.grpc.{Channel, Server, ServerBuilder, ServerInterceptor, ServerInterceptors}
import io.grpc.reflection.v1alpha.ServerReflectionGrpc
import scala.concurrent.duration.DurationInt
object ReverseProxy {
private def proxyServices(
backend: Channel,
serverBuilder: ServerBuilder[_],
services: Set[ServiceDescriptorInfo],
interceptors: Map[String, Seq[ServerInterceptor]],
): Unit =
for (service <- services) {
serverBuilder.addService(
ServerInterceptors.interceptForward(
ForwardService(backend, service),
interceptors.getOrElse(service.fullServiceName, Seq.empty): _*
)
)
}
def owner[Context](
backend: Channel,
serverBuilder: ServerBuilder[_],
interceptors: Map[String, Seq[ServerInterceptor]],
)(implicit context: HasExecutionContext[Context]): AbstractResourceOwner[Context, Server] = {
val factory = new ResourceOwnerFactories[Context] with GrpcResourceOwnerFactories[Context] {
override protected implicit val hasExecutionContext: HasExecutionContext[Context] =
context
}
val stub = ServerReflectionGrpc.newStub(backend)
val client = new ServerReflectionClient(stub)
for {
services <- factory.forFuture(() => client.getAllServices())
_ = proxyServices(backend, serverBuilder, services, interceptors)
server <- factory.forServer(serverBuilder, shutdownTimeout = 5.seconds)
} yield server
}
}

View File

@ -1,10 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.grpc.interceptors
import io.grpc.ForwardingServerCall.SimpleForwardingServerCall
import io.grpc.ServerCall
final class ForwardingServerCall[ReqT, RespT](call: ServerCall[ReqT, RespT])
extends SimpleForwardingServerCall[ReqT, RespT](call) {}

View File

@ -1,15 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.grpc.interceptors
import io.grpc.ForwardingServerCallListener.SimpleForwardingServerCallListener
import io.grpc.{Metadata, ServerCall, ServerCallHandler}
abstract class ForwardingServerCallListener[ReqT, RespT](
call: ServerCall[ReqT, RespT],
headers: Metadata,
next: ServerCallHandler[ReqT, RespT],
) extends SimpleForwardingServerCallListener[ReqT](
next.startCall(new ForwardingServerCall(call), headers)
)

View File

@ -1,100 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.grpc
import java.util.concurrent.atomic.AtomicReference
import com.daml.grpc.interceptors.ForwardingServerCallListener
import com.daml.grpc.test.GrpcServer
import io.grpc.{Metadata, ServerCall, ServerCallHandler, ServerInterceptor, StatusRuntimeException}
import io.grpc.health.v1.{HealthCheckRequest, HealthGrpc}
import io.grpc.inprocess.{InProcessChannelBuilder, InProcessServerBuilder}
import org.scalatest.Inside
import org.scalatest.flatspec.AsyncFlatSpec
import org.scalatest.matchers.should.Matchers
final class ReverseProxySpec extends AsyncFlatSpec with Matchers with GrpcServer with Inside {
import ReverseProxySpec._
import Services._
behavior of "ReverseProxy.create"
it should "fail if the backend does not support reflection" in withServices(
Health.newInstance
) { backend =>
val proxyBuilder = InProcessServerBuilder.forName(InProcessServerBuilder.generateName())
ReverseProxy
.owner(backend, proxyBuilder, interceptors = Map.empty)
.use(_ => fail("The proxy started but the backend does not support reflection"))
.failed
.map(_ shouldBe a[StatusRuntimeException])
}
it should "expose the backend's own services" in withServices(
Health.newInstance,
Reflection.newInstance,
) { backend =>
val proxyName = InProcessServerBuilder.generateName()
val proxyBuilder = InProcessServerBuilder.forName(proxyName)
ReverseProxy.owner(backend, proxyBuilder, interceptors = Map.empty).use { _ =>
val proxy = InProcessChannelBuilder.forName(proxyName).build()
Health.getHealthStatus(backend) shouldEqual Health.getHealthStatus(proxy)
Reflection.listServices(backend) shouldEqual Reflection.listServices(proxy)
}
}
it should "correctly set up an interceptor" in withServices(
Health.newInstance,
Reflection.newInstance,
) { backend =>
val proxyName = InProcessServerBuilder.generateName()
val proxyBuilder = InProcessServerBuilder.forName(proxyName)
val recorder = new RecordingInterceptor
ReverseProxy
.owner(backend, proxyBuilder, Map(HealthGrpc.SERVICE_NAME -> Seq(recorder)))
.use { _ =>
val proxy = InProcessChannelBuilder.forName(proxyName).build()
Health.getHealthStatus(backend)
recorder.latestRequest() shouldBe empty
Health.getHealthStatus(proxy)
inside(recorder.latestRequest()) { case Some(request: Array[Byte]) =>
HealthCheckRequest.parseFrom(request)
succeed
}
}
}
}
object ReverseProxySpec {
final class Callback[ReqT, RespT](
call: ServerCall[ReqT, RespT],
headers: Metadata,
next: ServerCallHandler[ReqT, RespT],
callback: ReqT => Unit,
) extends ForwardingServerCallListener(call, headers, next) {
override def onMessage(message: ReqT): Unit = {
callback(message)
super.onMessage(message)
}
}
final class RecordingInterceptor extends ServerInterceptor {
private val latestRequestReference = new AtomicReference[Any]()
def latestRequest(): Option[Any] = Option(latestRequestReference.get)
override def interceptCall[ReqT, RespT](
call: ServerCall[ReqT, RespT],
headers: Metadata,
next: ServerCallHandler[ReqT, RespT],
): ServerCall.Listener[ReqT] = {
new Callback(call, headers, next, latestRequestReference.set)
}
}
}

View File

@ -1,37 +0,0 @@
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
load(
"//bazel_tools:scala.bzl",
"da_scala_library",
"da_scala_test_suite",
)
da_scala_library(
name = "grpc-server-reflection-client",
srcs = glob(["src/main/scala/**/*.scala"]),
tags = ["maven_coordinates=com.daml:grpc-server-reflection-client:__VERSION__"],
visibility = [
"//:__subpackages__",
],
deps = [
"@maven//:com_google_protobuf_protobuf_java",
"@maven//:io_grpc_grpc_api",
"@maven//:io_grpc_grpc_services",
"@maven//:io_grpc_grpc_stub",
],
)
da_scala_test_suite(
name = "test",
srcs = glob(["src/test/scala/**/*.scala"]),
deps = [
":grpc-server-reflection-client",
"//libs-scala/grpc-test-utils",
"@maven//:com_google_protobuf_protobuf_java",
"@maven//:io_grpc_grpc_api",
"@maven//:io_grpc_grpc_core",
"@maven//:io_grpc_grpc_services",
"@maven//:io_grpc_grpc_stub",
],
)

View File

@ -1,63 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.grpc.reflection
import com.google.protobuf.DescriptorProtos.MethodDescriptorProto
import io.grpc.MethodDescriptor
import io.grpc.MethodDescriptor.{MethodType, generateFullMethodName}
final case class MethodDescriptorInfo(
fullMethodName: String,
methodType: MethodType,
) {
def toMethodDescriptor[ReqT, RespT](
requestMarshaller: MethodDescriptor.Marshaller[ReqT],
responseMarshaller: MethodDescriptor.Marshaller[RespT],
): MethodDescriptor[ReqT, RespT] =
MethodDescriptor
.newBuilder(requestMarshaller, responseMarshaller)
.setType(methodType)
.setFullMethodName(fullMethodName)
.build()
}
object MethodDescriptorInfo {
def apply(fullServiceName: String, method: MethodDescriptorProto): MethodDescriptorInfo =
MethodDescriptorInfo(
methodName = generateFullMethodName(fullServiceName, method.getName),
clientStreaming = method.getClientStreaming,
serverStreaming = method.getServerStreaming,
)
def apply(method: MethodDescriptor[_, _]): MethodDescriptorInfo =
MethodDescriptorInfo(
fullMethodName = method.getFullMethodName,
methodType = method.getType,
)
def apply(
methodName: String,
clientStreaming: Boolean,
serverStreaming: Boolean,
): MethodDescriptorInfo =
MethodDescriptorInfo(
fullMethodName = methodName,
methodType = methodType(clientStreaming, serverStreaming),
)
private def methodType(clientStreaming: Boolean, serverStreaming: Boolean): MethodType =
if (clientStreaming && serverStreaming) {
MethodType.BIDI_STREAMING
} else if (clientStreaming) {
MethodType.CLIENT_STREAMING
} else if (serverStreaming) {
MethodType.SERVER_STREAMING
} else {
MethodType.UNARY
}
}

View File

@ -1,26 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.grpc.reflection
import io.grpc.reflection.v1alpha.ServerReflectionGrpc.ServerReflectionStub
import io.grpc.reflection.v1alpha.ServerReflectionRequest
import io.grpc.stub.StreamObserver
import scala.concurrent.Future
final class ServerReflectionClient(stub: ServerReflectionStub) {
def getAllServices(): Future[Set[ServiceDescriptorInfo]] = {
lazy val serviceDescriptorInfo: ServiceDescriptorInfoObserver =
new ServiceDescriptorInfoObserver(serverReflectionStream)
lazy val serverReflectionStream: StreamObserver[ServerReflectionRequest] =
stub.serverReflectionInfo(serviceDescriptorInfo)
serviceDescriptorInfo.result
}
}

View File

@ -1,16 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.grpc.reflection
import io.grpc.reflection.v1alpha.ServerReflectionRequest
private[reflection] object ServerReflectionRequests {
val ListServices: ServerReflectionRequest =
ServerReflectionRequest.newBuilder().setListServices("").build()
def fileContaining(symbol: String): ServerReflectionRequest =
ServerReflectionRequest.newBuilder().setFileContainingSymbol(symbol).build()
}

View File

@ -1,27 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.grpc.reflection
import com.google.protobuf.DescriptorProtos.MethodDescriptorProto
final case class ServiceDescriptorInfo(
fullServiceName: String,
methods: Set[MethodDescriptorInfo],
)
object ServiceDescriptorInfo {
def apply(
packageName: String,
serviceName: String,
methods: Iterable[MethodDescriptorProto],
): ServiceDescriptorInfo = {
val fullServiceName: String = s"$packageName.$serviceName"
ServiceDescriptorInfo(
fullServiceName = fullServiceName,
methods = methods.view.map(MethodDescriptorInfo(fullServiceName, _)).toSet,
)
}
}

View File

@ -1,68 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.grpc.reflection
import java.util.concurrent.atomic.AtomicInteger
import com.google.protobuf.DescriptorProtos.FileDescriptorProto
import io.grpc.Status
import io.grpc.reflection.v1alpha.{ServerReflectionRequest, ServerReflectionResponse}
import io.grpc.stub.StreamObserver
import scala.jdk.CollectionConverters._
import scala.concurrent.{Future, Promise}
import scala.util.Success
private[reflection] final class ServiceDescriptorInfoObserver(
serverReflectionStream: => StreamObserver[ServerReflectionRequest]
) extends StreamObserver[ServerReflectionResponse] {
private val builder = Set.newBuilder[ServiceDescriptorInfo]
private val promise = Promise[Set[ServiceDescriptorInfo]]()
private val servicesLeft = new AtomicInteger(0)
lazy val result: Future[Set[ServiceDescriptorInfo]] = {
serverReflectionStream.onNext(ServerReflectionRequests.ListServices)
promise.future
}
override def onNext(response: ServerReflectionResponse): Unit = {
if (response.hasListServicesResponse) {
servicesLeft.set(response.getListServicesResponse.getServiceCount)
for (service <- response.getListServicesResponse.getServiceList.asScala) {
serverReflectionStream.onNext(ServerReflectionRequests.fileContaining(service.getName))
}
} else if (response.hasFileDescriptorResponse) {
for (bytes <- response.getFileDescriptorResponse.getFileDescriptorProtoList.asScala) {
val fileDescriptorProto = FileDescriptorProto.parseFrom(bytes)
for (service <- fileDescriptorProto.getServiceList.asScala) {
builder +=
ServiceDescriptorInfo(
packageName = fileDescriptorProto.getPackage,
serviceName = service.getName,
methods = service.getMethodList.asScala,
)
}
}
if (servicesLeft.decrementAndGet() < 1) {
serverReflectionStream.onCompleted()
}
} else if (response.hasErrorResponse) {
val error = response.getErrorResponse
val throwable = Status
.fromCodeValue(error.getErrorCode)
.withDescription(error.getErrorMessage)
.asRuntimeException()
serverReflectionStream.onError(throwable)
}
}
override def onError(throwable: Throwable): Unit = {
val _ = promise.tryFailure(throwable)
}
override def onCompleted(): Unit = {
val _ = promise.tryComplete(Success(builder.result()))
}
}

View File

@ -1,55 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.grpc.reflection
import com.daml.grpc.test.GrpcServer
import io.grpc.StatusRuntimeException
import io.grpc.health.v1.HealthGrpc
import io.grpc.reflection.v1alpha.ServerReflectionGrpc
import org.scalatest.flatspec.AsyncFlatSpec
import org.scalatest.matchers.should.Matchers
final class ServerReflectionClientSpec extends AsyncFlatSpec with Matchers with GrpcServer {
import ServerReflectionClientSpec._
import Services._
behavior of "getAllServices"
it should "fail if reflection is not supported" in withServices(Health.newInstance) { channel =>
val stub = ServerReflectionGrpc.newStub(channel)
val client = new ServerReflectionClient(stub)
client.getAllServices().failed.map(_ shouldBe a[StatusRuntimeException])
}
it should "show all if reflection is supported" in withServices(
Health.newInstance,
Reflection.newInstance,
) { channel =>
val expected = Set(healthDescriptor, reflectionDescriptor)
val stub = ServerReflectionGrpc.newStub(channel)
val client = new ServerReflectionClient(stub)
client.getAllServices().map(_ should contain theSameElementsAs expected)
}
}
object ServerReflectionClientSpec {
private val healthDescriptor =
ServiceDescriptorInfo(
fullServiceName = HealthGrpc.SERVICE_NAME,
methods = Set(
MethodDescriptorInfo(HealthGrpc.getCheckMethod),
MethodDescriptorInfo(HealthGrpc.getWatchMethod),
),
)
private val reflectionDescriptor =
ServiceDescriptorInfo(
fullServiceName = ServerReflectionGrpc.SERVICE_NAME,
methods = Set(MethodDescriptorInfo(ServerReflectionGrpc.getServerReflectionInfoMethod)),
)
}

View File

@ -1,28 +0,0 @@
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
load(
"//bazel_tools:scala.bzl",
"da_scala_library",
"da_scala_test",
)
da_scala_library(
name = "oracle-testing",
srcs = glob(["src/main/scala/**/*.scala"]),
data = [
"//ci:oracle_image",
],
scala_deps = [
"@maven//:org_scalactic_scalactic",
"@maven//:org_scalatest_scalatest_core",
],
visibility = [
"//visibility:public",
],
deps = [
"//libs-scala/ports",
"@maven//:org_scalatest_scalatest_compatible",
"@maven//:org_slf4j_slf4j_api",
],
)

View File

@ -1,136 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.testing.oracle
import com.daml.ports._
import java.sql._
import org.slf4j.LoggerFactory
import scala.annotation.tailrec
import scala.util.{Failure, Random, Success, Try, Using}
object OracleAround {
case class RichOracleUser(
oracleUser: OracleUser,
jdbcUrlWithoutCredentials: String,
jdbcUrl: String,
drop: () => Unit,
) {
/** In CI we re-use the same Oracle instance for testing, so non-colliding DB-lock-ids need to be assigned
*
* @return A positive integer, which defines a unique / mutually exclusive range of usable lock ids: [seed, seed + 10)
*/
def lockIdSeed: Int = {
assert(oracleUser.id > 0, "Lock ID seeding is not supported, cannot ensure unique lock-ids")
assert(oracleUser.id < 10 * 1000 * 1000)
oracleUser.id * 10
}
}
case class OracleUser(name: String, id: Int) {
val pwd: String = "hunter2"
}
private val logger = LoggerFactory.getLogger(getClass)
def createNewUniqueRandomUser(): RichOracleUser = createRichOracleUser { stmt =>
val id = Random.nextInt(1000 * 1000) + 1
val user = OracleUser(s"U$id", id)
createUser(stmt, user.name, user.pwd)
logger.info(s"New unique random Oracle user created $user")
user
}
def createOrReuseUser(name: String): RichOracleUser = createRichOracleUser { stmt =>
val user = OracleUser(name.toUpperCase, -1)
if (!userExists(stmt, user.name)) {
createUser(stmt, user.name, user.pwd)
logger.info(s"User $name not found: new Oracle user created $user")
} else {
logger.info(s"User $name already created: re-using existing Oracle user $user")
}
user
}
private def userExists(stmt: Statement, name: String): Boolean = {
val res = stmt.executeQuery(
s"""SELECT count(*) AS user_count FROM all_users WHERE username='$name'"""
)
res.next()
res.getInt("user_count") > 0
}
private def createUser(stmt: Statement, name: String, pwd: String): Unit = {
stmt.execute(s"""create user $name identified by $pwd""")
stmt.execute(s"""grant connect, resource to $name""")
stmt.execute(
s"""grant create table, create materialized view, create view, create procedure, create sequence, create type to $name"""
)
stmt.execute(s"""alter user $name quota unlimited on users""")
// for DBMS_LOCK access
stmt.execute(s"""GRANT EXECUTE ON SYS.DBMS_LOCK TO $name""")
stmt.execute(s"""GRANT SELECT ON V_$$MYSTAT TO $name""")
stmt.execute(s"""GRANT SELECT ON V_$$LOCK TO $name""")
()
}
private def createRichOracleUser(createBody: Statement => OracleUser): RichOracleUser = {
val systemUser = sys.env("ORACLE_USERNAME")
val systemPwd = sys.env("ORACLE_PWD")
val host = sys.env.getOrElse("ORACLE_HOST", "localhost")
val port = Port(sys.env("ORACLE_PORT").toInt)
val jdbcUrlWithoutCredentials = s"jdbc:oracle:thin:@$host:$port/ORCLPDB1"
def withStmt[T](connectingUserName: String)(body: Statement => T): T =
Using.resource(
DriverManager.getConnection(
jdbcUrlWithoutCredentials,
connectingUserName,
systemPwd,
)
) { connection =>
connection.setAutoCommit(false)
val result = Using.resource(connection.createStatement())(body)
connection.commit()
result
}
@tailrec
def retry[T](times: Int, sleepMillisBeforeReTry: Long)(body: => T): T = Try(body) match {
case Success(t) => t
case Failure(_) if times > 0 =>
if (sleepMillisBeforeReTry > 0) Thread.sleep(sleepMillisBeforeReTry)
retry(times - 1, sleepMillisBeforeReTry)(body)
case Failure(t) => throw t
}
retry(20, 100) {
withStmt(
"sys as sysdba" // TODO this is needed for being able to grant the execute access for the sys.dbms_lock below. Consider making this configurable
) { stmt =>
logger.info("Trying to create Oracle user")
val oracleUser = createBody(stmt)
logger.info(s"Oracle user ready $oracleUser")
RichOracleUser(
oracleUser = oracleUser,
jdbcUrlWithoutCredentials = jdbcUrlWithoutCredentials,
jdbcUrl = s"jdbc:oracle:thin:${oracleUser.name}/${oracleUser.pwd}@$host:$port/ORCLPDB1",
drop = () => {
retry(10, 1000) {
logger.info(s"Trying to remove Oracle user ${oracleUser.name}")
withStmt(systemUser)(_.execute(s"""drop user ${oracleUser.name} cascade"""))
}
logger.info(s"Oracle user removed successfully ${oracleUser.name}")
()
},
)
}
}
}
}

View File

@ -1,21 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.testing.oracle
import org.scalatest.{BeforeAndAfterAll, Suite}
trait OracleAroundAll extends OracleAroundSuite with BeforeAndAfterAll {
self: Suite =>
override protected def beforeAll(): Unit = {
Class.forName("oracle.jdbc.OracleDriver")
createNewUser()
super.beforeAll()
}
override protected def afterAll(): Unit = {
super.afterAll()
dropUser()
}
}

View File

@ -1,29 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.testing.oracle
import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, Suite}
trait OracleAroundEach extends OracleAroundSuite with BeforeAndAfterAll with BeforeAndAfterEach {
self: Suite =>
override protected def beforeAll(): Unit = {
Class.forName("oracle.jdbc.OracleDriver")
super.beforeAll()
}
override protected def afterAll(): Unit = {
super.afterAll()
}
override protected def beforeEach(): Unit = {
createNewUser()
super.beforeEach()
}
override protected def afterEach(): Unit = {
super.afterEach()
dropUser()
}
}

View File

@ -1,24 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.testing.oracle
import com.daml.testing.oracle.OracleAround.RichOracleUser
import org.scalatest.Suite
trait OracleAroundSuite {
self: Suite =>
@volatile
private var user: RichOracleUser = _
def jdbcUrl: String = user.jdbcUrl
def lockIdSeed: Int = user.lockIdSeed
def oracleUserName: String = user.oracleUser.name
def oracleUserPwd: String = user.oracleUser.pwd
def oracleJdbcUrlWithoutCredentials: String = user.jdbcUrlWithoutCredentials
protected def createNewUser(): Unit = user = OracleAround.createNewUniqueRandomUser()
protected def dropUser(): Unit = user.drop()
}

View File

@ -171,8 +171,6 @@
type: jar-scala
- target: //libs-scala/crypto:crypto
type: jar-scala
- target: //libs-scala/doobie-slf4j:doobie-slf4j
type: jar-scala
- target: //libs-scala/grpc-test-utils:grpc-test-utils
type: jar-scala
- target: //libs-scala/grpc-utils:grpc-utils

View File

@ -42,7 +42,6 @@ write_scalatest_runpath(
],
runtime_deps = [
"//ledger/error:error-test-lib",
"//libs-scala/flyway-testing",
"//libs-scala/jwt",
"//libs-scala/scalatest-utils",
],