From 63d61699b299d9b970f41a30b0c798e75b619395 Mon Sep 17 00:00:00 2001 From: KirillPamPam Date: Wed, 5 Nov 2025 13:06:05 +0400 Subject: [PATCH] Update health chains (#740) --- .../dshackle/config/HealthConfig.kt | 27 +++- .../dshackle/config/HealthConfigReader.kt | 6 +- .../reload/HealthReloadConfigProcessor.kt | 25 ++++ .../config/reload/ReloadConfigProcessor.kt | 131 +++++++++++++++++ .../config/reload/ReloadConfigService.kt | 12 ++ .../config/reload/ReloadConfigSetup.kt | 132 ++---------------- .../dshackle/monitoring/HealthCheckSetup.kt | 2 +- .../monitoring/HealthCheckSetupSpec.groovy | 50 +++---- .../reload/HealthReloadConfigProcessorTest.kt | 93 ++++++++++++ .../config/reload/ReloadConfigTest.kt | 23 ++- .../configs/health-changed-params.yaml | 7 + .../resources/configs/health-changed.yaml | 11 ++ .../resources/configs/health-initial.yaml | 7 + 13 files changed, 372 insertions(+), 154 deletions(-) create mode 100644 src/main/kotlin/io/emeraldpay/dshackle/config/reload/HealthReloadConfigProcessor.kt create mode 100644 src/main/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigProcessor.kt create mode 100644 src/test/kotlin/io/emeraldpay/dshackle/config/reload/HealthReloadConfigProcessorTest.kt create mode 100644 src/test/resources/configs/health-changed-params.yaml create mode 100644 src/test/resources/configs/health-changed.yaml create mode 100644 src/test/resources/configs/health-initial.yaml diff --git a/src/main/kotlin/io/emeraldpay/dshackle/config/HealthConfig.kt b/src/main/kotlin/io/emeraldpay/dshackle/config/HealthConfig.kt index b077234f..f2ebfa02 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/config/HealthConfig.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/config/HealthConfig.kt @@ -16,8 +16,13 @@ package io.emeraldpay.dshackle.config import io.emeraldpay.dshackle.Chain +import java.util.concurrent.atomic.AtomicReference -class HealthConfig { +class HealthConfig() { + + constructor(newChains: Map) : this() { + updateChains(newChains) + } companion object { fun default(): HealthConfig { @@ -28,14 +33,28 @@ class HealthConfig { var port: Int = 8082 var host: String = "127.0.0.1" var path: String = "/health" - val chains = HashMap() + private val chains = AtomicReference>(HashMap()) fun isEnabled(): Boolean { - return chains.isNotEmpty() + return chains.get().isNotEmpty() } + fun loadChains(): Map = chains.get() + fun configs(): Collection { - return chains.values + return chains.get().values + } + + fun containsChain(chain: Chain): Boolean { + return chains.get().containsKey(chain) + } + + fun config(chain: Chain): ChainConfig? { + return chains.get()[chain] + } + + fun updateChains(newChains: Map) { + chains.set(newChains) } data class ChainConfig( diff --git a/src/main/kotlin/io/emeraldpay/dshackle/config/HealthConfigReader.kt b/src/main/kotlin/io/emeraldpay/dshackle/config/HealthConfigReader.kt index 26fec9d0..afb5aba7 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/config/HealthConfigReader.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/config/HealthConfigReader.kt @@ -54,6 +54,7 @@ class HealthConfigReader : YamlConfigReader() { if (input == null) { return } + val configs = HashMap() input.value.forEach { conf -> val chain = getValueAsString(conf, "chain") ?.let { Global.chainById(it) } @@ -64,11 +65,12 @@ class HealthConfigReader : YamlConfigReader() { if (chain == Chain.UNSPECIFIED) { log.warn("Using UNSPECIFIED blockchain for Health Check. Always fails") } - if (healthConfig.chains.containsKey(chain)) { + if (healthConfig.containsChain(chain)) { log.warn("Duplicate Health Check config for $chain. Replace previous with new") } val minAvailable = getValueAsInt(conf, "min-available") ?: 1 - healthConfig.chains[chain] = HealthConfig.ChainConfig(chain, minAvailable.coerceAtLeast(0)) + configs[chain] = HealthConfig.ChainConfig(chain, minAvailable.coerceAtLeast(0)) } + healthConfig.updateChains(configs) } } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/config/reload/HealthReloadConfigProcessor.kt b/src/main/kotlin/io/emeraldpay/dshackle/config/reload/HealthReloadConfigProcessor.kt new file mode 100644 index 00000000..3c466b60 --- /dev/null +++ b/src/main/kotlin/io/emeraldpay/dshackle/config/reload/HealthReloadConfigProcessor.kt @@ -0,0 +1,25 @@ +package io.emeraldpay.dshackle.config.reload + +import org.springframework.stereotype.Component + +@Component +class HealthReloadConfigProcessor( + private val reloadConfigService: ReloadConfigService, +) : ReloadConfigProcessor { + override fun reload(): Boolean { + val currentCfg = reloadConfigService.currentHealthConfig() + val newCfg = reloadConfigService.readHealthConfig() + + if (currentCfg.configs().toSet() == newCfg.configs().toSet()) { + return false + } + + reloadConfigService.updateHealthChains(newCfg.loadChains()) + + return true + } + + override fun configType(): String { + return "health config" + } +} diff --git a/src/main/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigProcessor.kt b/src/main/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigProcessor.kt new file mode 100644 index 00000000..c886d447 --- /dev/null +++ b/src/main/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigProcessor.kt @@ -0,0 +1,131 @@ +package io.emeraldpay.dshackle.config.reload + +import io.emeraldpay.dshackle.Chain +import io.emeraldpay.dshackle.Global.Companion.chainById +import io.emeraldpay.dshackle.config.UpstreamsConfig +import io.emeraldpay.dshackle.foundation.ChainOptions +import org.springframework.stereotype.Component +import java.util.stream.Collectors + +interface ReloadConfigProcessor { + fun reload(): Boolean + fun configType(): String +} + +@Component +class UpstreamConfigReloadConfigProcessor( + private val reloadConfigService: ReloadConfigService, + private val reloadConfigUpstreamService: ReloadConfigUpstreamService, +) : ReloadConfigProcessor { + override fun reload(): Boolean { + val newUpstreamsConfig = reloadConfigService.readUpstreamsConfig() + val currentUpstreamsConfig = reloadConfigService.currentUpstreamsConfig() + + if (newUpstreamsConfig == currentUpstreamsConfig) { + return false + } + + val chainsToReload = analyzeDefaultOptions( + currentUpstreamsConfig.defaultOptions, + newUpstreamsConfig.defaultOptions, + ) + val upstreamsAnalyzeData = analyzeUpstreams( + currentUpstreamsConfig.upstreams, + newUpstreamsConfig.upstreams, + ) + + val upstreamsToRemove = upstreamsAnalyzeData.removed + .filterNot { chainsToReload.contains(it.second) } + .toSet() + val upstreamsToAdd = upstreamsAnalyzeData.added + + reloadConfigService.updateUpstreamsConfig(newUpstreamsConfig) + + reloadConfigUpstreamService.reloadUpstreams(chainsToReload, upstreamsToRemove, upstreamsToAdd, newUpstreamsConfig) + + return true + } + + override fun configType(): String { + return "upstream config" + } + + private fun analyzeUpstreams( + currentUpstreams: List>, + newUpstreams: List>, + ): UpstreamAnalyzeData { + if (currentUpstreams == newUpstreams) { + return UpstreamAnalyzeData() + } + val reloaded = mutableSetOf>() + val removed = mutableSetOf>() + val currentUpstreamsMap = currentUpstreams.associateBy { it.id!! to chainById(it.chain) } + val newUpstreamsMap = newUpstreams.associateBy { it.id!! to chainById(it.chain) } + + currentUpstreamsMap.forEach { + val newUpstream = newUpstreamsMap[it.key] + if (newUpstream == null) { + removed.add(it.key) + } else if (newUpstream != it.value) { + reloaded.add(it.key) + } + } + + val added = newUpstreamsMap + .minus(currentUpstreamsMap.keys) + .mapTo(mutableSetOf()) { it.key } + .plus(reloaded) + + return UpstreamAnalyzeData(added, removed.plus(reloaded)) + } + + private fun analyzeDefaultOptions( + currentDefaultOptions: List, + newDefaultOptions: List, + ): Set { + val chainsToReload = mutableSetOf() + + val currentOptions = getChainOptions(currentDefaultOptions) + val newOptions = getChainOptions(newDefaultOptions) + + if (currentOptions == newOptions) { + return emptySet() + } + + val removed = mutableSetOf() + + currentOptions.forEach { + val newChainOption = newOptions[it.key] + if (newChainOption == null) { + removed.add(chainById(it.key)) + } else if (newChainOption != it.value) { + chainsToReload.add(chainById(it.key)) + } + } + + val added = newOptions.minus(currentOptions.keys).map { chainById(it.key) } + + return chainsToReload.plus(added).plus(removed) + } + + private fun getChainOptions( + defaultOptions: List, + ): Map> { + return defaultOptions.stream() + .flatMap { options -> options.chains?.stream()?.map { it to options.options } } + .collect( + Collectors.groupingBy( + { it.first }, + Collectors.mapping( + { it.second }, + Collectors.toUnmodifiableList(), + ), + ), + ) + } + + private data class UpstreamAnalyzeData( + val added: Set> = emptySet(), + val removed: Set> = emptySet(), + ) +} diff --git a/src/main/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigService.kt b/src/main/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigService.kt index 76135cb1..0d5817ff 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigService.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigService.kt @@ -1,7 +1,10 @@ package io.emeraldpay.dshackle.config.reload +import io.emeraldpay.dshackle.Chain import io.emeraldpay.dshackle.Config import io.emeraldpay.dshackle.FileResolver +import io.emeraldpay.dshackle.config.HealthConfig +import io.emeraldpay.dshackle.config.HealthConfigReader import io.emeraldpay.dshackle.config.MainConfig import io.emeraldpay.dshackle.config.UpstreamsConfig import io.emeraldpay.dshackle.config.UpstreamsConfigReader @@ -17,12 +20,21 @@ class ReloadConfigService( ) { private val optionsReader = ChainOptionsReader() private val upstreamsConfigReader = UpstreamsConfigReader(fileResolver, optionsReader) + private val healthConfigReader = HealthConfigReader() fun readUpstreamsConfig() = upstreamsConfigReader.read(config.getConfigPath().inputStream())!! fun currentUpstreamsConfig() = mainConfig.initialConfig!! + fun currentHealthConfig() = mainConfig.health + + fun readHealthConfig() = healthConfigReader.read(config.getConfigPath().inputStream())!! + fun updateUpstreamsConfig(newConfig: UpstreamsConfig) { mainConfig.upstreams = newConfig } + + fun updateHealthChains(newChains: Map) { + mainConfig.health.updateChains(newChains) + } } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigSetup.kt b/src/main/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigSetup.kt index ecece93c..6eac64b6 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigSetup.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigSetup.kt @@ -1,20 +1,14 @@ package io.emeraldpay.dshackle.config.reload -import io.emeraldpay.dshackle.Chain -import io.emeraldpay.dshackle.Global.Companion.chainById -import io.emeraldpay.dshackle.config.UpstreamsConfig -import io.emeraldpay.dshackle.foundation.ChainOptions import org.slf4j.LoggerFactory import org.springframework.stereotype.Component import sun.misc.Signal import sun.misc.SignalHandler import java.util.concurrent.locks.ReentrantLock -import java.util.stream.Collectors @Component class ReloadConfigSetup( - private val reloadConfigService: ReloadConfigService, - private val reloadConfigUpstreamService: ReloadConfigUpstreamService, + private val processors: List, ) : SignalHandler { companion object { @@ -43,124 +37,24 @@ class ReloadConfigSetup( try { log.info("Reloading config...") - if (reloadConfig()) { - log.info("Config is reloaded") - } else { - log.info("There is nothing to reload, config is the same") + if (processors.isEmpty()) { + log.warn("No reload config processors") + return + } + + processors.forEach { + if (it.reload()) { + log.info("{} is reloaded", it.configType()) + } else { + log.info("There is nothing to reload, {} is the same", it.configType()) + } } } finally { + log.info("Reloading config has been completed") reloadLock.unlock() } } else { log.warn("Reloading is in progress") } } - - private fun reloadConfig(): Boolean { - val newUpstreamsConfig = reloadConfigService.readUpstreamsConfig() - val currentUpstreamsConfig = reloadConfigService.currentUpstreamsConfig() - - if (newUpstreamsConfig == currentUpstreamsConfig) { - return false - } - - val chainsToReload = analyzeDefaultOptions( - currentUpstreamsConfig.defaultOptions, - newUpstreamsConfig.defaultOptions, - ) - val upstreamsAnalyzeData = analyzeUpstreams( - currentUpstreamsConfig.upstreams, - newUpstreamsConfig.upstreams, - ) - - val upstreamsToRemove = upstreamsAnalyzeData.removed - .filterNot { chainsToReload.contains(it.second) } - .toSet() - val upstreamsToAdd = upstreamsAnalyzeData.added - - reloadConfigService.updateUpstreamsConfig(newUpstreamsConfig) - - reloadConfigUpstreamService.reloadUpstreams(chainsToReload, upstreamsToRemove, upstreamsToAdd, newUpstreamsConfig) - - return true - } - - private fun analyzeUpstreams( - currentUpstreams: List>, - newUpstreams: List>, - ): UpstreamAnalyzeData { - if (currentUpstreams == newUpstreams) { - return UpstreamAnalyzeData() - } - val reloaded = mutableSetOf>() - val removed = mutableSetOf>() - val currentUpstreamsMap = currentUpstreams.associateBy { it.id!! to chainById(it.chain) } - val newUpstreamsMap = newUpstreams.associateBy { it.id!! to chainById(it.chain) } - - currentUpstreamsMap.forEach { - val newUpstream = newUpstreamsMap[it.key] - if (newUpstream == null) { - removed.add(it.key) - } else if (newUpstream != it.value) { - reloaded.add(it.key) - } - } - - val added = newUpstreamsMap - .minus(currentUpstreamsMap.keys) - .mapTo(mutableSetOf()) { it.key } - .plus(reloaded) - - return UpstreamAnalyzeData(added, removed.plus(reloaded)) - } - - private fun analyzeDefaultOptions( - currentDefaultOptions: List, - newDefaultOptions: List, - ): Set { - val chainsToReload = mutableSetOf() - - val currentOptions = getChainOptions(currentDefaultOptions) - val newOptions = getChainOptions(newDefaultOptions) - - if (currentOptions == newOptions) { - return emptySet() - } - - val removed = mutableSetOf() - - currentOptions.forEach { - val newChainOption = newOptions[it.key] - if (newChainOption == null) { - removed.add(chainById(it.key)) - } else if (newChainOption != it.value) { - chainsToReload.add(chainById(it.key)) - } - } - - val added = newOptions.minus(currentOptions.keys).map { chainById(it.key) } - - return chainsToReload.plus(added).plus(removed) - } - - private fun getChainOptions( - defaultOptions: List, - ): Map> { - return defaultOptions.stream() - .flatMap { options -> options.chains?.stream()?.map { it to options.options } } - .collect( - Collectors.groupingBy( - { it.first }, - Collectors.mapping( - { it.second }, - Collectors.toUnmodifiableList(), - ), - ), - ) - } - - private data class UpstreamAnalyzeData( - val added: Set> = emptySet(), - val removed: Set> = emptySet(), - ) } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/monitoring/HealthCheckSetup.kt b/src/main/kotlin/io/emeraldpay/dshackle/monitoring/HealthCheckSetup.kt index 30a7d3b8..66632d3d 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/monitoring/HealthCheckSetup.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/monitoring/HealthCheckSetup.kt @@ -102,7 +102,7 @@ class HealthCheckSetup( val details = chains.flatMap { chain -> var chainUnavailable = false val up = multistreamHolder.getUpstream(chain) - val required = healthConfig.chains[chain] + val required = healthConfig.config(chain) if (!up.isAvailable()) { if (required != null) { anyUnavailable = true diff --git a/src/test/groovy/io/emeraldpay/dshackle/monitoring/HealthCheckSetupSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/monitoring/HealthCheckSetupSpec.groovy index ac5a5f92..d09f038c 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/monitoring/HealthCheckSetupSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/monitoring/HealthCheckSetupSpec.groovy @@ -26,11 +26,11 @@ class HealthCheckSetupSpec extends Specification { def "OK when meets availability - 1"() { setup: - def config = new HealthConfig().tap { - it.chains[Chain.ETHEREUM__MAINNET] = new HealthConfig.ChainConfig( - Chain.ETHEREUM__MAINNET, 1 - ) - } + def config = new HealthConfig( + Collections.singletonMap( + Chain.ETHEREUM__MAINNET, new HealthConfig.ChainConfig(Chain.ETHEREUM__MAINNET, 1) + ) + ) def up1 = Mock(Upstream) def ethereumUpstreams = Mock(Multistream) def multistream = Mock(MultistreamHolder) @@ -50,11 +50,11 @@ class HealthCheckSetupSpec extends Specification { def "OK when meets availability - 1 - bitcoin"() { setup: - def config = new HealthConfig().tap { - it.chains[Chain.BITCOIN__MAINNET] = new HealthConfig.ChainConfig( - Chain.BITCOIN__MAINNET, 1 - ) - } + def config = new HealthConfig( + Collections.singletonMap( + Chain.BITCOIN__MAINNET, new HealthConfig.ChainConfig(Chain.BITCOIN__MAINNET, 1) + ) + ) def up1 = Mock(Upstream) def bitcoinUpstreams = Mock(Multistream) def multistream = Mock(MultistreamHolder) @@ -74,11 +74,11 @@ class HealthCheckSetupSpec extends Specification { def "OK when meets availability - 2/3"() { setup: - def config = new HealthConfig().tap { - it.chains[Chain.ETHEREUM__MAINNET] = new HealthConfig.ChainConfig( - Chain.ETHEREUM__MAINNET, 2 - ) - } + def config = new HealthConfig( + Collections.singletonMap( + Chain.ETHEREUM__MAINNET, new HealthConfig.ChainConfig(Chain.ETHEREUM__MAINNET, 1) + ) + ) def up1 = Mock(Upstream) def up2 = Mock(Upstream) def up3 = Mock(Upstream) @@ -102,11 +102,11 @@ class HealthCheckSetupSpec extends Specification { def "OK when doesn't meet availability - 2/3"() { setup: - def config = new HealthConfig().tap { - it.chains[Chain.ETHEREUM__MAINNET] = new HealthConfig.ChainConfig( - Chain.ETHEREUM__MAINNET, 2 - ) - } + def config = new HealthConfig( + Collections.singletonMap( + Chain.ETHEREUM__MAINNET, new HealthConfig.ChainConfig(Chain.ETHEREUM__MAINNET, 2) + ) + ) def up1 = Mock(Upstream) def up2 = Mock(Upstream) def up3 = Mock(Upstream) @@ -130,11 +130,11 @@ class HealthCheckSetupSpec extends Specification { def "OK when meets availability - 2/3 - detailed"() { setup: - def config = new HealthConfig().tap { - it.chains[Chain.ETHEREUM__MAINNET] = new HealthConfig.ChainConfig( - Chain.ETHEREUM__MAINNET, 2 - ) - } + def config = new HealthConfig( + Collections.singletonMap( + Chain.ETHEREUM__MAINNET, new HealthConfig.ChainConfig(Chain.ETHEREUM__MAINNET, 1) + ) + ) def up1 = Mock(Upstream) def up2 = Mock(Upstream) def up3 = Mock(Upstream) diff --git a/src/test/kotlin/io/emeraldpay/dshackle/config/reload/HealthReloadConfigProcessorTest.kt b/src/test/kotlin/io/emeraldpay/dshackle/config/reload/HealthReloadConfigProcessorTest.kt new file mode 100644 index 00000000..5566c8dc --- /dev/null +++ b/src/test/kotlin/io/emeraldpay/dshackle/config/reload/HealthReloadConfigProcessorTest.kt @@ -0,0 +1,93 @@ +package io.emeraldpay.dshackle.config.reload + +import io.emeraldpay.dshackle.Chain +import io.emeraldpay.dshackle.Config +import io.emeraldpay.dshackle.FileResolver +import io.emeraldpay.dshackle.config.HealthConfig +import io.emeraldpay.dshackle.config.HealthConfigReader +import io.emeraldpay.dshackle.config.MainConfig +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import org.springframework.util.ResourceUtils +import java.io.File + +class HealthReloadConfigProcessorTest { + private val fileResolver = FileResolver(File("")) + private val mainConfig = MainConfig() + + private val config = mock() + private val reloadConfigService = ReloadConfigService(config, fileResolver, mainConfig) + private val processor = HealthReloadConfigProcessor(reloadConfigService) + private val healthConfigReader = HealthConfigReader() + + @BeforeEach + fun setupTests() { + mainConfig.health = HealthConfig.default() + } + + @Test + fun `get health processor config type`() { + assertThat(processor.configType()).isEqualTo("health config") + } + + @Test + fun `cant reload config if they are equal`() { + val newConfigFile = ResourceUtils.getFile("classpath:configs/health-initial.yaml") + whenever(config.getConfigPath()).thenReturn(newConfigFile) + + val initialConfigIs = ResourceUtils.getFile("classpath:configs/health-initial.yaml").inputStream() + val initialConfig = healthConfigReader.read(initialConfigIs)!! + + mainConfig.health = initialConfig + + val result = processor.reload() + + assertThat(result).isFalse + } + + @Test + fun `cant reload config if they everything is different but not blockchain list`() { + val newConfigFile = ResourceUtils.getFile("classpath:configs/health-changed-params.yaml") + whenever(config.getConfigPath()).thenReturn(newConfigFile) + + val initialConfigIs = ResourceUtils.getFile("classpath:configs/health-initial.yaml").inputStream() + val initialConfig = healthConfigReader.read(initialConfigIs)!! + + mainConfig.health = initialConfig + + val result = processor.reload() + + assertThat(result).isFalse + } + + @Test + fun `reload health config`() { + val newConfigFile = ResourceUtils.getFile("classpath:configs/health-changed.yaml") + whenever(config.getConfigPath()).thenReturn(newConfigFile) + + val initialConfigIs = ResourceUtils.getFile("classpath:configs/health-initial.yaml").inputStream() + val initialConfig = healthConfigReader.read(initialConfigIs)!! + + mainConfig.health = initialConfig + assertThat(mainConfig.health.configs().toSet()).isEqualTo( + setOf( + HealthConfig.ChainConfig(Chain.BSC__MAINNET, 0), + ), + ) + + val reloaded = processor.reload() + val newChains = mainConfig.health.configs() + + assertThat(reloaded).isTrue + assertThat(newChains.toSet()).isEqualTo( + setOf( + HealthConfig.ChainConfig(Chain.ARBITRUM__MAINNET, 5), + HealthConfig.ChainConfig(Chain.OPTIMISM__MAINNET, 1), + HealthConfig.ChainConfig(Chain.BSC__MAINNET, 1), + ), + ) + } +} diff --git a/src/test/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigTest.kt b/src/test/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigTest.kt index 69c6f40a..f825d736 100644 --- a/src/test/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigTest.kt +++ b/src/test/kotlin/io/emeraldpay/dshackle/config/reload/ReloadConfigTest.kt @@ -53,6 +53,20 @@ class ReloadConfigTest { mainConfig.upstreams = null } + @Test + fun `reload config and use all processors`() { + val firstProcessor = mock() + val secondProcessor = mock() + val reloadConfig = ReloadConfigSetup(listOf(firstProcessor, secondProcessor)) + + reloadConfig.handle(Signal("HUP")) + + verify(firstProcessor).reload() + verify(firstProcessor).configType() + verify(secondProcessor).reload() + verify(secondProcessor).configType() + } + @Test fun `reload upstreams changes`() { val up1 = upstream("local1") @@ -76,7 +90,8 @@ class ReloadConfigTest { currentMultistreamHolder, configuredUpstreams, ) - val reloadConfig = ReloadConfigSetup(reloadConfigService, reloadConfigUpstreamService) + val upstreamCfgReloadProcessor = UpstreamConfigReloadConfigProcessor(reloadConfigService, reloadConfigUpstreamService) + val reloadConfig = ReloadConfigSetup(listOf(upstreamCfgReloadProcessor)) val initialConfigIs = ResourceUtils.getFile("classpath:configs/upstreams-initial.yaml").inputStream() val initialConfig = upstreamsConfigReader.read(initialConfigIs)!! @@ -127,7 +142,8 @@ class ReloadConfigTest { currentMultistreamHolder, configuredUpstreams, ) - val reloadConfig = ReloadConfigSetup(reloadConfigService, reloadConfigUpstreamService) + val upstreamCfgReloadProcessor = UpstreamConfigReloadConfigProcessor(reloadConfigService, reloadConfigUpstreamService) + val reloadConfig = ReloadConfigSetup(listOf(upstreamCfgReloadProcessor)) val initialConfigIs = ResourceUtils.getFile("classpath:configs/upstreams-initial.yaml").inputStream() val initialConfig = upstreamsConfigReader.read(initialConfigIs)!! val newConfig = upstreamsConfigReader.read(newConfigFile.inputStream())!! @@ -157,7 +173,8 @@ class ReloadConfigTest { val reloadConfigUpstreamService = mock() - val reloadConfig = ReloadConfigSetup(reloadConfigService, reloadConfigUpstreamService) + val upstreamCfgReloadProcessor = UpstreamConfigReloadConfigProcessor(reloadConfigService, reloadConfigUpstreamService) + val reloadConfig = ReloadConfigSetup(listOf(upstreamCfgReloadProcessor)) whenever(config.getConfigPath()).thenReturn(initialConfigFile) diff --git a/src/test/resources/configs/health-changed-params.yaml b/src/test/resources/configs/health-changed-params.yaml new file mode 100644 index 00000000..f76fa42b --- /dev/null +++ b/src/test/resources/configs/health-changed-params.yaml @@ -0,0 +1,7 @@ +health: + port: 8081 + host: 1.0.0.0 + path: /healtha + blockchains: + - chain: bsc + min-available: 0 \ No newline at end of file diff --git a/src/test/resources/configs/health-changed.yaml b/src/test/resources/configs/health-changed.yaml new file mode 100644 index 00000000..9a21a924 --- /dev/null +++ b/src/test/resources/configs/health-changed.yaml @@ -0,0 +1,11 @@ +health: + port: 8082 + host: 0.0.0.0 + path: /health + blockchains: + - chain: arbitrum + min-available: 5 + - chain: bsc + min-available: 1 + - chain: optimism + min-available: 1 \ No newline at end of file diff --git a/src/test/resources/configs/health-initial.yaml b/src/test/resources/configs/health-initial.yaml new file mode 100644 index 00000000..b08aa1df --- /dev/null +++ b/src/test/resources/configs/health-initial.yaml @@ -0,0 +1,7 @@ +health: + port: 8082 + host: 0.0.0.0 + path: /health + blockchains: + - chain: bsc + min-available: 0 \ No newline at end of file