API version service [KVL-541] (#7792)

This change exposes a new endpoint with information about the ledger API version.
The current approach is to read contents of ledger-api/VERSION and provide it as it is to the user.

CHANGELOG_BEGIN
- ledger API version endpoint
CHANGELOG_END
This commit is contained in:
Kamil Bożek 2020-10-27 17:47:44 +01:00 committed by GitHub
parent 1478669e2d
commit 9a2317cbdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 174 additions and 4 deletions

View File

@ -41,6 +41,7 @@ all cases, the Ledger API exposes the same services:
- Use the :ref:`package service <package-service>` to query the DAML packages deployed to the ledger.
- Use the :ref:`ledger identity service <ledger-identity-service>` to retrieve the Ledger ID of the ledger the application is connected to.
- Use the :ref:`ledger configuration service <ledger-configuration-service>` to retrieve some dynamic properties of the ledger, like maximum deduplication time for commands.
- Use the :ref:`version service <version-service>` to retrieve information about the Ledger API version.
- Testing services (on Sandbox only, *not* for production ledgers)
- Use the :ref:`time service <time-service>` to obtain the time as known by the ledger.

View File

@ -197,6 +197,15 @@ This configuration includes the maximum command deduplication time (see `Command
For full details, see :ref:`the proto documentation for the service <com.daml.ledger.api.v1.LedgerConfigurationService>`.
.. _version-service:
Version service
============================
Use the **version service** to retrieve information about the Ledger API version.
For full details, see :ref:`the proto documentation for the service <com.daml.ledger.api.v1.VersionService>`.
.. _ledger-api-testing-services:
Testing services

View File

@ -25,7 +25,7 @@ Application Portability and to some extent Network Upgradeability are achieved b
Specifically, if a DAML Application is built against Ledger API version X.Y.Z and a Participant Node exposes Ledger API version X.Y2.Z2, the application is guaranteed to work as long as Y2.Z2 >= Y.Z.
Currently, the Ledger API version is the same as the version of the Integration Components used in the Participant Node. This is mostly the case because there has been no need for the versions to diverge yet. This will likely change at the latest when one part of the ecosystem moves to version 2.X. integration components, DAML drivers, and Participant Nodes advertise the Ledger API version they expose.
Before SDK 1.7, the Ledger API version exposed by the Participant Node matches the SDK version. Currently, these versions can diverge. Integration Components, DAML Drivers, and Participant Nodes advertise the Ledger API version they expose.
As a concrete example, DAML for Postgres 1.4.0 has the Participant Node integrated, and exposes Ledger API version 1.4.0 and the DAML for VMware Blockchain 1.0 Participant Nodes expose Ledger API version 1.6.0. So any application that runs on DAML for Postgres 1.4.0 will also run on DAML for VMware Blockchain 1.0.

View File

@ -1,2 +1,12 @@
# Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
filegroup(
name = "api-version-files",
srcs = [
"VERSION",
],
visibility = [
"//visibility:public",
],
)

View File

@ -1 +1 @@
1.0.0
1.7.0

View File

@ -0,0 +1,31 @@
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
syntax = "proto3";
package com.daml.ledger.api.v1;
option java_outer_classname = "VersionServiceOuterClass";
option java_package = "com.daml.ledger.api.v1";
option csharp_namespace = "Com.Daml.Ledger.Api.V1";
// Allows clients to retrieve information about the ledger API version
service VersionService {
// Read the Ledger API version
rpc GetLedgerApiVersion (GetLedgerApiVersionRequest) returns (GetLedgerApiVersionResponse);
}
message GetLedgerApiVersionRequest {
// Must correspond to the ledger ID reported by the Ledger Identification Service.
// Must be a valid LedgerString (as described in ``value.proto``).
// Required
string ledger_id = 1;
}
message GetLedgerApiVersionResponse {
// The version of the ledger API
string version = 1;
}

View File

@ -64,6 +64,18 @@ final class LedgerClientIT
}
}
"get api version" in {
// semantic versioning regex as in: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
val semVerRegex =
"""^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"""
for {
client <- LedgerClient(channel, ClientConfiguration)
version <- client.versionClient.getApiVersion()
} yield {
version should fullyMatch regex semVerRegex
}
}
"shut down the channel when closed" in {
for {
client <- LedgerClient(channel, ClientConfiguration)

View File

@ -18,6 +18,7 @@ import com.daml.ledger.api.v1.command_submission_service.CommandSubmissionServic
import com.daml.ledger.api.v1.ledger_identity_service.LedgerIdentityServiceGrpc
import com.daml.ledger.api.v1.package_service.PackageServiceGrpc
import com.daml.ledger.api.v1.transaction_service.TransactionServiceGrpc
import com.daml.ledger.api.v1.version_service.VersionServiceGrpc
import com.daml.ledger.client.configuration.LedgerClientConfiguration
import com.daml.ledger.client.services.acs.ActiveContractSetClient
import com.daml.ledger.client.services.admin.{PackageManagementClient, PartyManagementClient}
@ -25,6 +26,7 @@ import com.daml.ledger.client.services.commands.{CommandClient, SynchronousComma
import com.daml.ledger.client.services.identity.LedgerIdentityClient
import com.daml.ledger.client.services.pkg.PackageClient
import com.daml.ledger.client.services.transactions.TransactionClient
import com.daml.ledger.client.services.version.VersionClient
import io.grpc.netty.NettyChannelBuilder
import io.grpc.stub.AbstractStub
import io.grpc.{Channel, ManagedChannel}
@ -71,6 +73,9 @@ final class LedgerClient private (
ledgerId,
LedgerClient.stub(TransactionServiceGrpc.stub(channel), config.token))
val versionClient: VersionClient =
new VersionClient(ledgerId, LedgerClient.stub(VersionServiceGrpc.stub(channel), config.token))
override def close(): Unit =
channel match {
case channel: ManagedChannel =>

View File

@ -0,0 +1,23 @@
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.ledger.client.services.version
import com.daml.dec.DirectExecutionContext
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.v1.version_service.GetLedgerApiVersionRequest
import com.daml.ledger.api.v1.version_service.VersionServiceGrpc.VersionServiceStub
import com.daml.ledger.client.LedgerClient
import scalaz.syntax.tag._
import scala.concurrent.Future
class VersionClient(ledgerId: LedgerId, service: VersionServiceStub) {
def getApiVersion(token: Option[String] = None): Future[String] =
LedgerClient
.stub(service, token)
.getLedgerApiVersion(new GetLedgerApiVersionRequest(ledgerId.unwrap))
.map(_.version)(DirectExecutionContext)
}

View File

@ -81,7 +81,9 @@ da_scala_library(
# Do not include logback.xml into the library: let the user
# of the sandbox-as-a-library decide how to log.
exclude = ["src/main/resources/logback.xml"],
),
) + [
"//ledger-api:api-version-files",
],
tags = ["maven_coordinates=com.daml:participant-integration-api:__VERSION__"],
visibility = [
"//visibility:public",

View File

@ -134,6 +134,9 @@ private[daml] object ApiServices {
val apiLedgerIdentityService =
ApiLedgerIdentityService.create(() => identityService.getLedgerId())
val apiVersionService =
ApiVersionService.create()
val apiPackageService = ApiPackageService.create(ledgerId, packagesService)
val apiConfigurationService =
@ -178,6 +181,7 @@ private[daml] object ApiServices {
new ActiveContractsServiceAuthorization(apiActiveContractsService, authorizer),
apiReflectionService,
apiHealthService,
apiVersionService,
)
}
@ -284,4 +288,5 @@ private[daml] object ApiServices {
}
}
}
}

View File

@ -0,0 +1,69 @@
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.platform.apiserver.services
import com.daml.ledger.api.v1.version_service.GetLedgerApiVersionRequest
import com.daml.ledger.api.v1.version_service.GetLedgerApiVersionResponse
import com.daml.ledger.api.v1.version_service.VersionServiceGrpc
import com.daml.ledger.api.v1.version_service.VersionServiceGrpc.VersionService
import com.daml.logging.ContextualizedLogger
import com.daml.logging.LoggingContext
import com.daml.platform.api.grpc.GrpcApiService
import io.grpc.ServerServiceDefinition
import io.grpc.Status
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.io.Source
import scala.util.Try
import scala.util.control.NonFatal
private[apiserver] final class ApiVersionService private (
implicit loggingContext: LoggingContext,
ec: ExecutionContext)
extends VersionService
with GrpcApiService {
private val logger = ContextualizedLogger.get(this.getClass)
private val versionFile: String = "ledger-api/VERSION"
private lazy val apiVersion: Try[String] = readVersion(versionFile)
override def getLedgerApiVersion(
request: GetLedgerApiVersionRequest): Future[GetLedgerApiVersionResponse] =
Future
.fromTry(apiVersion)
.map(GetLedgerApiVersionResponse(_))
.andThen(logger.logErrorsOnCall[GetLedgerApiVersionResponse])
.recoverWith {
case NonFatal(_) => internalError
}
private lazy val internalError: Future[Nothing] =
Future.failed(
Status.INTERNAL
.withDescription("Cannot read Ledger API version")
.asRuntimeException()
)
private def readVersion(versionFileName: String): Try[String] =
Try {
Source
.fromResource(versionFileName)
.getLines()
.toList
.head
}
override def bindService(): ServerServiceDefinition =
VersionServiceGrpc.bindService(this, ec)
override def close(): Unit = ()
}
private[apiserver] object ApiVersionService {
def create()(implicit loggingContext: LoggingContext, ec: ExecutionContext): ApiVersionService =
new ApiVersionService
}

View File

@ -30,10 +30,13 @@ final class ReflectionIT
"accessed" should {
"provide a list of exposed services" in {
val expectedServiceCount: Int = 16
for {
response <- execRequest(listServices)
} yield {
response.getListServicesResponse.getServiceCount shouldEqual 15
withClue("ServiceCount: ") {
response.getListServicesResponse.getServiceCount shouldEqual expectedServiceCount
}
}
}