diff --git a/ledger/ledger-on-sql/BUILD.bazel b/ledger/ledger-on-sql/BUILD.bazel index 7c94b7ff3c..4e838785ef 100644 --- a/ledger/ledger-on-sql/BUILD.bazel +++ b/ledger/ledger-on-sql/BUILD.bazel @@ -216,6 +216,7 @@ da_scala_test_suite( "//ledger/participant-state/kvutils", "//ledger/participant-state/kvutils:kvutils-tests-lib", "//libs-scala/contextualized-logging", + "//libs-scala/flyway-testing", "//libs-scala/postgresql-testing", "//libs-scala/resources", "@maven//:com_typesafe_akka_akka_actor_2_12", diff --git a/ledger/ledger-on-sql/hash-migrations.sh b/ledger/ledger-on-sql/hash-migrations.sh index 42d135d27e..fb3ff89a98 100755 --- a/ledger/ledger-on-sql/hash-migrations.sh +++ b/ledger/ledger-on-sql/hash-migrations.sh @@ -3,11 +3,10 @@ # Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -set -e -set -u +set -eou pipefail +shopt -s globstar DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" -for file in "$DIR"/src/main/resources/com/daml/ledger/on/sql/migrations/**/*.sql; do - shasum -a 256 "$file" | awk '{ print $1 }' > "$file.sha256" -done +"$DIR"/../../libs-scala/flyway-testing/hash-migrations.sh \ + "$DIR"/src/main/resources/com/daml/ledger/on/sql/migrations/**/*.sql diff --git a/ledger/ledger-on-sql/src/test/suite/scala/com/daml/ledger/on/sql/ImmutableMigrationsSpec.scala b/ledger/ledger-on-sql/src/test/suite/scala/com/daml/ledger/on/sql/ImmutableMigrationsSpec.scala index 5b471e364d..e586876ec1 100644 --- a/ledger/ledger-on-sql/src/test/suite/scala/com/daml/ledger/on/sql/ImmutableMigrationsSpec.scala +++ b/ledger/ledger-on-sql/src/test/suite/scala/com/daml/ledger/on/sql/ImmutableMigrationsSpec.scala @@ -3,76 +3,10 @@ package com.daml.ledger.on.sql -import java.io.{BufferedReader, FileNotFoundException} -import java.math.BigInteger -import java.nio.charset.Charset -import java.security.MessageDigest -import java.util +import com.daml.flyway.AbstractImmutableMigrationsSpec -import com.daml.ledger.on.sql.ImmutableMigrationsSpec._ -import org.flywaydb.core.Flyway -import org.flywaydb.core.api.configuration.FluentConfiguration -import org.flywaydb.core.internal.resource.LoadableResource -import org.flywaydb.core.internal.scanner.{LocationScannerCache, ResourceNameCache, Scanner} -import org.scalatest.Matchers._ -import org.scalatest.WordSpec - -import scala.collection.JavaConverters._ - -class ImmutableMigrationsSpec extends WordSpec { - "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 >= 3 - - 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.""", - ) - } - } - } -} - -object ImmutableMigrationsSpec { - private val migrationsResourcePath = "com/daml/ledger/on/sql/migrations" - private val hashMigrationsScriptPath = "ledger/ledger-on-sql/hash-migrations.sh" - - private def flywayScanner(configuration: FluentConfiguration) = - new Scanner( - classOf[Object], - util.Arrays.asList(configuration.getLocations: _*), - getClass.getClassLoader, - configuration.getEncoding, - new ResourceNameCache, - new LocationScannerCache, - ) - - 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 = MessageDigest.getInstance("SHA-256") - 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)) - } +class ImmutableMigrationsSpec extends AbstractImmutableMigrationsSpec { + protected override val migrationsResourcePath = "com/daml/ledger/on/sql/migrations" + protected override val migrationsMinSize = 3 + protected override val hashMigrationsScriptPath = "ledger/ledger-on-sql/hash-migrations.sh" } diff --git a/libs-scala/flyway-testing/BUILD.bazel b/libs-scala/flyway-testing/BUILD.bazel new file mode 100644 index 0000000000..519a9ac8e3 --- /dev/null +++ b/libs-scala/flyway-testing/BUILD.bazel @@ -0,0 +1,16 @@ +# Copyright (c) 2020 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"]), + tags = ["maven_coordinates=com.daml:flyway-testing:__VERSION__"], + visibility = ["//visibility:public"], + deps = [ + "@maven//:org_flywaydb_flyway_core", + "@maven//:org_scalactic_scalactic_2_12", + "@maven//:org_scalatest_scalatest_2_12", + ], +) diff --git a/libs-scala/flyway-testing/hash-migrations.sh b/libs-scala/flyway-testing/hash-migrations.sh new file mode 100755 index 0000000000..0001a31fb8 --- /dev/null +++ b/libs-scala/flyway-testing/hash-migrations.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# Copyright (c) 2020 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 diff --git a/libs-scala/flyway-testing/src/main/scala/flyway/AbstractImmutableMigrationsSpec.scala b/libs-scala/flyway-testing/src/main/scala/flyway/AbstractImmutableMigrationsSpec.scala new file mode 100644 index 0000000000..e5f2f5dd60 --- /dev/null +++ b/libs-scala/flyway-testing/src/main/scala/flyway/AbstractImmutableMigrationsSpec.scala @@ -0,0 +1,76 @@ +// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.daml.flyway + +import java.io.{BufferedReader, FileNotFoundException} +import java.math.BigInteger +import java.nio.charset.Charset +import java.security.MessageDigest +import java.util + +import org.flywaydb.core.Flyway +import org.flywaydb.core.api.configuration.FluentConfiguration +import org.flywaydb.core.internal.resource.LoadableResource +import org.flywaydb.core.internal.scanner.{LocationScannerCache, ResourceNameCache, Scanner} +import org.scalatest.Matchers._ +import org.scalatest.WordSpec + +import scala.collection.JavaConverters._ + +abstract class AbstractImmutableMigrationsSpec extends WordSpec { + 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, + new ResourceNameCache, + new LocationScannerCache, + ) + + 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 = MessageDigest.getInstance("SHA-256") + 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.""", + ) + } + } + } +} diff --git a/triggers/service/BUILD.bazel b/triggers/service/BUILD.bazel index cec5685925..1a37340625 100644 --- a/triggers/service/BUILD.bazel +++ b/triggers/service/BUILD.bazel @@ -115,6 +115,7 @@ da_scala_test( "//ledger/participant-state", "//ledger/sandbox-classic", "//ledger/sandbox-common", + "//libs-scala/flyway-testing", "//libs-scala/ports", "//libs-scala/postgresql-testing", "//libs-scala/resources", @@ -127,6 +128,7 @@ da_scala_test( "@maven//:com_typesafe_akka_akka_parsing_2_12", "@maven//:eu_rekawek_toxiproxy_toxiproxy_java_2_1_3", "@maven//:io_spray_spray_json_2_12", + "@maven//:org_flywaydb_flyway_core", "@maven//:org_scalatest_scalatest_2_12", "@maven//:org_scalaz_scalaz_core_2_12", ], diff --git a/triggers/service/hash-migrations.sh b/triggers/service/hash-migrations.sh new file mode 100755 index 0000000000..1363decbcc --- /dev/null +++ b/triggers/service/hash-migrations.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -eou pipefail +shopt -s globstar + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" + +"$DIR"/../../libs-scala/flyway-testing/hash-migrations.sh \ + "$DIR"/src/main/resources/com/daml/lf/engine/trigger/db/migration/**/*.sql diff --git a/triggers/service/src/main/resources/com/daml/lf/engine/trigger/db/migration/postgres/V1__Init.sha256 b/triggers/service/src/main/resources/com/daml/lf/engine/trigger/db/migration/postgres/V1__Init.sha256 deleted file mode 100644 index 6f1f9c844a..0000000000 --- a/triggers/service/src/main/resources/com/daml/lf/engine/trigger/db/migration/postgres/V1__Init.sha256 +++ /dev/null @@ -1 +0,0 @@ -cbac336e1d253707f6ef4dd866b7ed107be3dd908906c446202f5dc7f8594c48 diff --git a/triggers/service/src/main/resources/com/daml/lf/engine/trigger/db/migration/postgres/V1__Init.sql.sha256 b/triggers/service/src/main/resources/com/daml/lf/engine/trigger/db/migration/postgres/V1__Init.sql.sha256 new file mode 100644 index 0000000000..78f21a8b22 --- /dev/null +++ b/triggers/service/src/main/resources/com/daml/lf/engine/trigger/db/migration/postgres/V1__Init.sql.sha256 @@ -0,0 +1 @@ +9e3bad4ec8947cea838aaabaffeaa81c0084fffd3f1f930caa9d43309c9bb7fa diff --git a/triggers/service/src/main/resources/com/daml/lf/engine/trigger/db/migration/postgres/V2__Add_access_token.sha256 b/triggers/service/src/main/resources/com/daml/lf/engine/trigger/db/migration/postgres/V2__Add_access_token.sha256 deleted file mode 100644 index 500c866c0b..0000000000 --- a/triggers/service/src/main/resources/com/daml/lf/engine/trigger/db/migration/postgres/V2__Add_access_token.sha256 +++ /dev/null @@ -1 +0,0 @@ -6762d275f249330869111253545121673c09faf31fb032b78654106380d167fa diff --git a/triggers/service/src/main/resources/com/daml/lf/engine/trigger/db/migration/postgres/V2__Add_access_token.sql.sha256 b/triggers/service/src/main/resources/com/daml/lf/engine/trigger/db/migration/postgres/V2__Add_access_token.sql.sha256 new file mode 100644 index 0000000000..c834d04439 --- /dev/null +++ b/triggers/service/src/main/resources/com/daml/lf/engine/trigger/db/migration/postgres/V2__Add_access_token.sql.sha256 @@ -0,0 +1 @@ +774600d1fcdab326edc840421da21d42ed2022d15fdbdb10e319364a2a1de7ba diff --git a/triggers/service/src/test/scala/com/digitalasset/daml/lf/engine/trigger/ImmutableMigrationsSpec.scala b/triggers/service/src/test/scala/com/digitalasset/daml/lf/engine/trigger/ImmutableMigrationsSpec.scala new file mode 100644 index 0000000000..33624c176c --- /dev/null +++ b/triggers/service/src/test/scala/com/digitalasset/daml/lf/engine/trigger/ImmutableMigrationsSpec.scala @@ -0,0 +1,12 @@ +// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.daml.lf.engine.trigger + +import com.daml.flyway.AbstractImmutableMigrationsSpec + +class ImmutableMigrationsSpec extends AbstractImmutableMigrationsSpec { + protected override val migrationsResourcePath = "com/daml/lf/engine/trigger/db/migration/postgres" + protected override val migrationsMinSize = 2 + protected override val hashMigrationsScriptPath = "trigger/service/hash-migrations.sh" +}