Skip to content

Commit 0247ad4

Browse files
Add periodic topology snapshot trigger (#3295)
* Add trigger for periodic topology snapshots (#3188) Signed-off-by: Julien Tinguely <julien.tinguely@digitalasset.com>
1 parent c0f722e commit 0247ad4

File tree

30 files changed

+684
-50
lines changed

30 files changed

+684
-50
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# This is deliberately lower than ConsoleCommandTimeout.defaultRequestTimeout
22
pekko.http.server.request-timeout = 38 seconds
33
pekko.http.server.parsing.error-handler="org.lfdecentralizedtrust.splice.http.PekkoHttpParsingErrorHandler$"
4-
pekko.http.server.parsing.ignore-illegal-header-for = ["user-agent"]
4+
pekko.http.server.parsing.ignore-illegal-header-for = ["user-agent", "etag"]
5+
pekko.http.client.parsing.ignore-illegal-header-for = ["etag"]

apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/PeriodicBackupIntegrationTest.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import org.lfdecentralizedtrust.splice.config.{
44
BackupDumpConfig,
55
ConfigTransforms,
66
GcpBucketConfig,
7+
IdentityDumpTest,
78
PeriodicBackupDumpConfig,
89
}
910
import org.lfdecentralizedtrust.splice.identities.NodeIdentitiesDump
@@ -98,7 +99,7 @@ final class DirectoryPeriodicBackupIntegrationTest
9899
final class GcpBucketPeriodicBackupIntegrationTest
99100
extends PeriodicBackupIntegrationTestBase[BackupDumpConfig.Gcp] {
100101
override def backupDumpLocation =
101-
BackupDumpConfig.Gcp(GcpBucketConfig.inferForTesting, None)
102+
BackupDumpConfig.Gcp(GcpBucketConfig.inferForTesting(IdentityDumpTest), None)
102103
val bucket = new GcpBucket(backupDumpLocation.bucket, loggerFactory)
103104
override def readDump(filename: String) = {
104105
bucket.readStringFromBucket(Paths.get(filename))
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package org.lfdecentralizedtrust.splice.integration.tests
2+
3+
import com.digitalasset.canton.config.NonNegativeFiniteDuration
4+
import com.digitalasset.canton.logging.SuppressionRule
5+
import com.google.cloud.storage.Blob
6+
import org.lfdecentralizedtrust.splice.config.*
7+
import org.lfdecentralizedtrust.splice.integration.EnvironmentDefinition
8+
import org.lfdecentralizedtrust.splice.integration.tests.SpliceTests.IntegrationTest
9+
import org.lfdecentralizedtrust.splice.util.GcpBucket
10+
import org.slf4j.event.Level
11+
12+
import java.nio.charset.StandardCharsets
13+
import java.time.{ZoneOffset, ZonedDateTime}
14+
import scala.concurrent.duration.DurationInt
15+
16+
class PeriodicTopologySnapshotIntegrationTest[T <: BackupDumpConfig] extends IntegrationTest {
17+
18+
private val bucket = new GcpBucket(topologySnapshotLocation.bucket, loggerFactory)
19+
20+
private val topologySnapshotInterval: NonNegativeFiniteDuration =
21+
NonNegativeFiniteDuration.ofMinutes(10)
22+
23+
private def topologySnapshotConfig: PeriodicBackupDumpConfig =
24+
PeriodicBackupDumpConfig(topologySnapshotLocation, topologySnapshotInterval)
25+
26+
private def topologySnapshotLocation: BackupDumpConfig.Gcp =
27+
BackupDumpConfig.Gcp(GcpBucketConfig.inferForTesting(TopologySnapshotTest), None)
28+
29+
private def listDump(utcDate: String): Seq[Blob] =
30+
bucket.list(startOffset = s"topology_snapshot_$utcDate", endOffset = "")
31+
32+
override def environmentDefinition: SpliceEnvironmentDefinition =
33+
EnvironmentDefinition
34+
.simpleTopology1Sv(this.getClass.getSimpleName)
35+
.addConfigTransformsToFront((_, conf) =>
36+
ConfigTransforms.updateAllSvAppConfigs((_, c) =>
37+
c.copy(topologySnapshotConfig = Some(topologySnapshotConfig))
38+
)(conf)
39+
)
40+
.withManualStart
41+
42+
"sv1" should {
43+
"produces a topology snapshot in the background" in { implicit env =>
44+
val utcDate = ZonedDateTime.now(ZoneOffset.UTC).toLocalDate.toString
45+
clue("topology snapshot is being produced")(
46+
loggerFactory.assertEventuallyLogsSeq(SuppressionRule.Level(Level.INFO))(
47+
timeUntilSuccess = 40.seconds,
48+
within = {
49+
initDsoWithSv1Only()
50+
},
51+
assertion = logEntries => {
52+
forAtLeast(
53+
1,
54+
logEntries,
55+
)(logEntry =>
56+
logEntry.message should (include(s"Took a new topology snapshot on $utcDate") or
57+
include("Today's topology snapshot already exists."))
58+
)
59+
},
60+
)
61+
)
62+
63+
clue("the 3 files created from the topology snapshot exist in gcp.")({
64+
val dumps = listDump(utcDate).filter(_.getSize > 0L)
65+
dumps.size shouldBe 3
66+
new String(
67+
dumps
68+
.find(_.getName.endsWith("metadata"))
69+
.getOrElse(throw new RuntimeException("Metadata file not found."))
70+
.getContent(),
71+
StandardCharsets.UTF_8,
72+
) should include("SEQ::sv1") // the sequencerId change through ci runs
73+
dumps.exists(_.getName.endsWith("authorized")) shouldBe true
74+
dumps.exists(_.getName.endsWith("onboarding-state")) shouldBe true
75+
})
76+
}
77+
}
78+
}

apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/runbook/NonDsoNonDevNetPreflightIntegrationTest.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package org.lfdecentralizedtrust.splice.integration.tests.runbook
22

3+
import org.lfdecentralizedtrust.splice.config.IdentityDump
34
import org.lfdecentralizedtrust.splice.integration.EnvironmentDefinition
45
import org.lfdecentralizedtrust.splice.integration.tests.SpliceTests.{
5-
SpliceTestConsoleEnvironment,
66
IntegrationTestWithSharedEnvironment,
7+
SpliceTestConsoleEnvironment,
78
}
89
import org.lfdecentralizedtrust.splice.util.DataExportTestUtil
910

@@ -41,11 +42,11 @@ final class NonDsoNonDevNetPreflightIntegrationTest
4142
}
4243

4344
"Check that there is a recent participant identities backup on GCP for validator1" in { _ =>
44-
testRecentParticipantIdentitiesDump("validator1")
45+
testRecentParticipantIdentitiesDump("validator1", IdentityDump)
4546
}
4647

4748
"Check that there is a recent participant identities backup on GCP for splitwell validator" in {
4849
_ =>
49-
testRecentParticipantIdentitiesDump("splitwell")
50+
testRecentParticipantIdentitiesDump("splitwell", IdentityDump)
5051
}
5152
}

apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/runbook/SvNonDevNetPreflightintegrationTest.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package org.lfdecentralizedtrust.splice.integration.tests.runbook
22

3+
import org.lfdecentralizedtrust.splice.config.IdentityDump
34
import org.lfdecentralizedtrust.splice.integration.EnvironmentDefinition
45
import org.lfdecentralizedtrust.splice.integration.tests.SpliceTests.SpliceTestConsoleEnvironment
56
import org.lfdecentralizedtrust.splice.integration.tests.FrontendIntegrationTestWithSharedEnvironment
67
import org.lfdecentralizedtrust.splice.util.DataExportTestUtil
7-
88
import org.lfdecentralizedtrust.splice.util.FrontendLoginUtil
9+
910
import scala.util.Try
1011

1112
abstract class SvNonDevNetPreflightIntegrationTestBase
@@ -113,7 +114,7 @@ abstract class SvNonDevNetPreflightIntegrationTestBase
113114
}
114115

115116
"Check that there is a recent participant identities backup on GCP" in { _ =>
116-
testRecentParticipantIdentitiesDump(svNamespace)
117+
testRecentParticipantIdentitiesDump(svNamespace, IdentityDump)
117118
}
118119
}
119120

apps/app/src/test/scala/org/lfdecentralizedtrust/splice/util/DataExportTestUtil.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package org.lfdecentralizedtrust.splice.util
22

3-
import org.lfdecentralizedtrust.splice.config.GcpBucketConfig
3+
import org.lfdecentralizedtrust.splice.config.{BucketName, GcpBucketConfig}
44
import org.lfdecentralizedtrust.splice.identities.{NodeIdentitiesDump, NodeIdentitiesStore}
55
import org.lfdecentralizedtrust.splice.integration.tests.SpliceTests.TestCommon
66
import com.digitalasset.canton.topology.ParticipantId
@@ -15,8 +15,9 @@ trait DataExportTestUtil extends TestCommon {
1515
namespace: String,
1616
getFileName: Instant => Path,
1717
decode: String => Either[String, A],
18+
bucketName: BucketName,
1819
) = {
19-
val bucket = new GcpBucket(GcpBucketConfig.inferForCluster, loggerFactory)
20+
val bucket = new GcpBucket(GcpBucketConfig.inferForCluster(bucketName), loggerFactory)
2021
import java.time.Instant
2122
import java.time.temporal.ChronoUnit
2223
val cluster = sys.env("GCP_CLUSTER_BASENAME")
@@ -33,10 +34,11 @@ trait DataExportTestUtil extends TestCommon {
3334
}
3435
}
3536

36-
def testRecentParticipantIdentitiesDump(namespace: String) =
37+
def testRecentParticipantIdentitiesDump(namespace: String, bucketName: BucketName) =
3738
testRecentDump(
3839
namespace,
3940
NodeIdentitiesStore.dumpFilename(_),
4041
NodeIdentitiesDump.fromJsonString(ParticipantId.tryFromProtoPrimitive, _),
42+
bucketName,
4143
)
4244
}

apps/common/src/main/scala/org/lfdecentralizedtrust/splice/config/GcpConfig.scala

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,36 @@ final case class GcpBucketConfig(
4848
def description: String = s"GCP bucket $bucketName in project $projectId"
4949
}
5050

51+
sealed trait BucketName {
52+
def value: String
53+
}
54+
55+
case object IdentityDumpTest extends BucketName {
56+
val value: String = "da-splice-identity-dumps"
57+
}
58+
59+
case object TopologySnapshotTest extends BucketName {
60+
val value: String = "da-splice-topology-snapshots"
61+
}
62+
63+
case object IdentityDump extends BucketName {
64+
val value: String = "da-cn-data-dumps"
65+
}
66+
67+
case object TopologySnapshot extends BucketName {
68+
val value: String = "da-cn-topology-snapshots"
69+
}
70+
5171
object GcpBucketConfig {
52-
def inferForTesting: GcpBucketConfig =
72+
def inferForTesting(testBucketName: BucketName): GcpBucketConfig =
5373
infer(
5474
"GCP_DATA_EXPORT_INTEGRATION_TEST_SERVICE_ACCOUNT_CREDENTIALS",
5575
"da-cn-splice",
56-
"da-splice-identity-dumps",
76+
testBucketName.value,
5777
)
5878

59-
def inferForCluster: GcpBucketConfig =
60-
infer("GCP_DATA_DUMP_BUCKET_SERVICE_ACCOUNT_CREDENTIALS", "da-cn-devnet", "da-cn-data-dumps")
79+
def inferForCluster(bucketName: BucketName): GcpBucketConfig =
80+
infer("GCP_DATA_DUMP_BUCKET_SERVICE_ACCOUNT_CREDENTIALS", "da-cn-devnet", bucketName.value)
6181

6282
def inferForBundles: GcpBucketConfig =
6383
infer(

0 commit comments

Comments
 (0)