Skip to content

Commit

Permalink
Adds verified configuration and testing to issue digital-asset#14562
Browse files Browse the repository at this point in the history
* Adds verified configuration and testing to issue digital-asset#14562

CHANGELOG_BEGIN

* DAML Script Runner: DAR upload code in DAML script runner moved to integration tests.

CHANGELOG_END
  • Loading branch information
carlpulley-da authored Aug 10, 2022
1 parent e2d997a commit f65e7f5
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import com.daml.fs.Utils.deleteRecursively
import com.daml.grpc.adapter.{AkkaExecutionSequencerPool, ExecutionSequencerFactory}
import com.daml.ledger.api.refinements.ApiTypes.Party
import com.daml.ledger.api.tls.TlsConfiguration
import com.daml.lf.engine.script.{RunnerConfig, RunnerMain, ScriptConfig}
import com.daml.lf.engine.script.{RunnerCliConfig, RunnerMain, ScriptConfig}
import com.daml.ledger.client.configuration.{
CommandClientConfiguration,
LedgerClientChannelConfiguration,
Expand Down Expand Up @@ -100,7 +100,7 @@ object ExampleExportClient {
new AkkaExecutionSequencerPool("ScriptRunnerPool")(system)
implicit val ec: ExecutionContext = system.dispatcher

val config = RunnerConfig(
val config = RunnerCliConfig(
darPath = clientConfig.darPath,
scriptIdentifier = "ScriptExample:initializeFixed",
ledgerHost = Some("localhost"),
Expand Down
5 changes: 4 additions & 1 deletion daml-script/runner/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,17 @@ da_scala_library(
da_scala_test(
name = "tests",
srcs = glob(
["src/test/**/*.scala"],
["src/test/scala/**/*.scala"],
),
data = glob(["src/test/resources/**/*"]),
visibility = ["//visibility:public"],
deps = [
":script-runner-lib",
"//bazel_tools/runfiles:scala_runfiles",
"//daml-lf/data",
"//language-support/scala/bindings",
"//language-support/scala/bindings-akka",
"//ledger/ledger-api-common",
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import com.daml.ledger.api.refinements.ApiTypes.ApplicationId
import com.daml.ledger.api.tls.TlsConfiguration
import com.daml.lf.engine.script.ledgerinteraction.ScriptTimeMode

case class RunnerConfig(
case class RunnerCliConfig(
darPath: File,
scriptIdentifier: String,
ledgerHost: Option[String],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,80 +22,36 @@ import com.daml.lf.iface.reader.InterfaceReader
import com.daml.lf.language.Ast.Package
import com.daml.grpc.adapter.{AkkaExecutionSequencerPool, ExecutionSequencerFactory}
import com.daml.auth.TokenHolder
import com.daml.ledger.api.tls.TlsConfiguration
import com.daml.lf.data.NoCopy
import com.daml.lf.engine.script.ledgerinteraction.ScriptTimeMode
import com.daml.ledger.api.refinements.ApiTypes.ApplicationId

import java.io.File
import scala.util.Try

object RunnerMain {

def main(config: RunnerConfig): Unit = {
val dar: Dar[(PackageId, Package)] = DarDecoder.assertReadArchiveFromFile(config.darPath)
val scriptId: Identifier =
Identifier(dar.main._1, QualifiedName.assertFromString(config.scriptIdentifier))
def main(runnerConfig: RunnerCliConfig): Unit = {

implicit val system: ActorSystem = ActorSystem("ScriptRunner")
implicit val sequencer: ExecutionSequencerFactory =
new AkkaExecutionSequencerPool("ScriptRunnerPool")(system)
implicit val ec: ExecutionContext = system.dispatcher
implicit val materializer: Materializer = Materializer(system)

val inputValue = config.inputFile.map(file => {
val source = Source.fromFile(file)
val fileContent =
try {
source.mkString
} finally {
source.close()
}
fileContent.parseJson
})

val token = config.accessTokenFile.map(new TokenHolder(_)).flatMap(_.token)
val participantParams = config.participantConfig match {
case Some(file) => {
// We allow specifying --access-token-file/--application-id together with
// --participant-config and use the values as the default for
// all participants that do not specify an explicit token.
val source = Source.fromFile(file)
val fileContent =
try {
source.mkString
} finally {
source.close
}
val jsVal = fileContent.parseJson
import ParticipantsJsonProtocol._
jsVal
.convertTo[Participants[ApiParameters]]
.map(params =>
params.copy(
access_token = params.access_token.orElse(token),
application_id = params.application_id.orElse(config.applicationId),
)
)
}
case None =>
Participants(
default_participant = Some(
ApiParameters(
config.ledgerHost.get,
config.ledgerPort.get,
token,
config.applicationId,
)
),
participants = Map.empty,
party_participants = Map.empty,
)
}
val flow: Future[Unit] = for {
config <- Future.fromTry(RunnerConfig(runnerConfig))
clients <-
if (config.jsonApi) {
val ifaceDar = dar.map(pkg => InterfaceReader.readInterface(() => \/-(pkg))._2)
val ifaceDar = config.dar.map(pkg => InterfaceReader.readInterface(() => \/-(pkg))._2)
val envIface = EnvironmentInterface.fromReaderInterfaces(ifaceDar)
// TODO (#13973) resolve envIface, or not, depending on whether inherited choices are needed
Runner.jsonClients(participantParams, envIface)
Runner.jsonClients(config.participantParams, envIface)
} else {
Runner.connect(participantParams, config.tlsConfig, config.maxInboundMessageSize)
Runner.connect(config.participantParams, config.tlsConfig, config.maxInboundMessageSize)
}
result <- Runner.run(dar, scriptId, inputValue, clients, config.timeMode)
result <- Runner.run(config.dar, config.scriptId, config.inputValue, clients, config.timeMode)
_ <- Future {
config.outputFile.foreach { outputFile =>
val jsVal = LfValueCodec.apiValueToJsValue(result.toUnnormalizedValue)
Expand All @@ -109,12 +65,96 @@ object RunnerMain {
} yield ()

flow.onComplete(_ =>
if (config.jsonApi) {
if (runnerConfig.jsonApi) {
Http().shutdownAllConnectionPools().flatMap { case () => system.terminate() }
} else {
system.terminate()
}
)
Await.result(flow, Duration.Inf)
}

final case class RunnerConfig private (
dar: Dar[(PackageId, Package)],
scriptId: Identifier,
participantParams: Participants[ApiParameters],
timeMode: ScriptTimeMode,
inputValue: Option[JsValue],
outputFile: Option[File],
token: Option[String],
tlsConfig: TlsConfiguration,
jsonApi: Boolean,
maxInboundMessageSize: Int,
applicationId: Option[ApplicationId],
) extends NoCopy

object RunnerConfig {
private[script] def apply(config: RunnerCliConfig): Try[RunnerConfig] = Try {
val dar: Dar[(PackageId, Package)] = DarDecoder.assertReadArchiveFromFile(config.darPath)
val scriptId: Identifier =
Identifier(dar.main._1, QualifiedName.assertFromString(config.scriptIdentifier))
val token = config.accessTokenFile.map(new TokenHolder(_)).flatMap(_.token)
val inputValue: Option[JsValue] = config.inputFile.map(file => {
val source = Source.fromFile(file)
val fileContent =
try {
source.mkString
} finally {
source.close()
}
fileContent.parseJson
})
val participantParams: Participants[ApiParameters] = config.participantConfig match {
case Some(file) =>
// We allow specifying --access-token-file/--application-id together with
// --participant-config and use the values as the default for
// all participants that do not specify an explicit token.
val source = Source.fromFile(file)
val fileContent =
try {
source.mkString
} finally {
source.close
}
val jsVal = fileContent.parseJson
import ParticipantsJsonProtocol._
jsVal
.convertTo[Participants[ApiParameters]]
.map(params =>
params.copy(
access_token = params.access_token.orElse(token),
application_id = params.application_id.orElse(config.applicationId),
)
)

case None =>
Participants(
default_participant = Some(
ApiParameters(
config.ledgerHost.get,
config.ledgerPort.get,
token,
config.applicationId,
)
),
participants = Map.empty,
party_participants = Map.empty,
)
}

new RunnerConfig(
dar,
scriptId,
participantParams,
config.timeMode,
inputValue,
config.outputFile,
token,
config.tlsConfig,
config.jsonApi,
config.maxInboundMessageSize,
config.applicationId,
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object CliMode {

sealed abstract class ScriptCommand extends Product with Serializable
object ScriptCommand {
final case class RunOne(conf: RunnerConfig) extends ScriptCommand
final case class RunOne(conf: RunnerCliConfig) extends ScriptCommand
final case class RunAll(conf: TestConfig) extends ScriptCommand
}

Expand Down Expand Up @@ -51,7 +51,7 @@ case class ScriptConfig(
case CliMode.RunOne(id) =>
Right(
ScriptCommand.RunOne(
RunnerConfig(
RunnerCliConfig(
darPath = darPath,
ledgerHost = ledgerHost,
ledgerPort = ledgerPort,
Expand Down
Binary file added daml-script/runner/src/test/resources/dummy.dar
Binary file not shown.
5 changes: 5 additions & 0 deletions daml-script/runner/src/test/resources/participantConfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"default_participant": {"host": "localhost", "port": 6865},
"participants": {},
"party_participants": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.lf.engine.script

import akka.actor.ActorSystem
import com.daml.bazeltools.BazelRunfiles
import com.daml.ledger.api.tls.TlsConfiguration
import org.scalatest.Inspectors
import org.scalatest.freespec.AnyFreeSpec
import org.scalatest.matchers.should.Matchers

import java.nio.file.{Path, Paths}

class RunnerMainTest extends AnyFreeSpec with Matchers with Inspectors {

import RunnerMainTest._

implicit val system: ActorSystem = ActorSystem()
implicit val ec = system.dispatcher

"RunnerMain should not crash" - {
"with given configurations" in {
forAll(Seq(configLedgerParticipant, configNodeParticipants)) { clientConfig =>
RunnerMain.RunnerConfig(clientConfig) shouldBe Symbol("success")
}
}
}
}

object RunnerMainTest {
val localHost: String = "localhost"
val ledgerPort: Int = 8080
val participantPort: Int = 6865
val darFilePath: Path =
BazelRunfiles.rlocation(Paths.get("daml-script/runner/src/test/resources/dummy.dar"))
val participantConfigPath: Path =
BazelRunfiles.rlocation(
Paths.get("daml-script/runner/src/test/resources/participantConfig.json")
)
val configLedgerParticipant: RunnerCliConfig = RunnerCliConfig(
darPath = darFilePath.toFile,
scriptIdentifier = "Main:setup",
ledgerHost = Some(localHost),
ledgerPort = Some(ledgerPort),
participantConfig = None,
timeMode = ScriptConfig.DefaultTimeMode,
inputFile = None,
outputFile = None,
accessTokenFile = None,
tlsConfig = TlsConfiguration(enabled = false, None, None, None),
jsonApi = false,
maxInboundMessageSize = ScriptConfig.DefaultMaxInboundMessageSize,
applicationId = None,
)
val configNodeParticipants: RunnerCliConfig = RunnerCliConfig(
darPath = darFilePath.toFile,
scriptIdentifier = "Main:setup",
ledgerHost = None,
ledgerPort = None,
participantConfig = Some(participantConfigPath.toFile),
timeMode = ScriptConfig.DefaultTimeMode,
inputFile = None,
outputFile = None,
accessTokenFile = None,
tlsConfig = TlsConfiguration(enabled = false, None, None, None),
jsonApi = false,
maxInboundMessageSize = ScriptConfig.DefaultMaxInboundMessageSize,
applicationId = None,
)
}

0 comments on commit f65e7f5

Please sign in to comment.