Skip to content

Commit

Permalink
feat(bpdm): added connection to dependencies in readiness check for b…
Browse files Browse the repository at this point in the history
…pdm services
  • Loading branch information
SujitMBRDI committed Dec 19, 2024
1 parent f4cc099 commit e37a5d0
Show file tree
Hide file tree
Showing 18 changed files with 362 additions and 42 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ For changes to the BPDM Helm charts please consult the [changelog](charts/bpdm/C
- BPDM Pool: Post endpoint to fetch the BPNL/S/A based on the requested identifiers.([#1052](https://github.com/eclipse-tractusx/bpdm/issues/1052))
- BPDM Gate & Orchestrator: Enhance the error handling mechanism for the orchestrator and gate components by extending the list of available error codes.([#1003](https://github.com/eclipse-tractusx/bpdm/pull/1003#pullrequestreview-2477395867))
- BPDM System Test: Tester module which performs automated end-to-end tests on an existing golden record process.([#1070](https://github.com/eclipse-tractusx/bpdm/pull/1070))
- Apps : Enhanced dependency readiness checks with a scheduler to verify connections to required services every 30 seconds and during startup for Pool, Cleaning Service Dummy, and Gate service. ([#1161](https://github.com/eclipse-tractusx/bpdm/issues/1161))

### Changed

Expand Down
2 changes: 1 addition & 1 deletion DEPENDENCIES
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-core/10.1.33, Apache-2.0
maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-el/10.1.33, Apache-2.0, approved, #6997
maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.33, Apache-2.0, approved, #7920
maven/mavencentral/org.apiguardian/apiguardian-api/1.1.2, Apache-2.0, approved, #17641
maven/mavencentral/org.aspectj/aspectjweaver/1.9.22.1, Apache-2.0 AND BSD-3-Clause AND EPL-1.0 AND BSD-3-Clause AND Apache-1.1, approved, #15252
maven/mavencentral/org.aspectj/aspectjweaver/1.9.22.1, EPL-1.0, approved, tools.aspectj
maven/mavencentral/org.assertj/assertj-core/3.24.2, Apache-2.0, approved, #6161
maven/mavencentral/org.awaitility/awaitility/4.2.2, Apache-2.0, approved, #14178
maven/mavencentral/org.checkerframework/checker-qual/3.42.0, MIT, approved, clearlydefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties(prefix = PREFIX)
class CleaningServiceConfigProperties (
val step: TaskStep
val step: TaskStep,
val dependencyCheck: DependencyCheckConfig
){
companion object{
const val PREFIX = "bpdm.golden-record-process"
}

data class DependencyCheckConfig(
val cron: String = "-"
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*******************************************************************************
* Copyright (c) 2021,2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/

package org.eclipse.tractusx.bpdm.cleaning.config

import jakarta.annotation.PostConstruct
import mu.KotlinLogging
import org.eclipse.tractusx.bpdm.cleaning.service.DependencyHealthService
import org.springframework.context.annotation.Configuration
import org.springframework.scheduling.TaskScheduler
import org.springframework.scheduling.support.CronTrigger

@Configuration
class DependencyHealthScheduler(
private val dependencyHealthService: DependencyHealthService,
private val taskScheduler: TaskScheduler,
private val configProperties: CleaningServiceConfigProperties
) {

private val logger = KotlinLogging.logger { }

@PostConstruct
fun scheduleHealthChecks() {
taskScheduler.scheduleIfEnabled(
{ performHealthCheck() },
configProperties.dependencyCheck.cron
)
}

private fun performHealthCheck() {
val healthStatus = dependencyHealthService.checkAllDependencies()
val unhealthyDependencies = healthStatus.filter { it.value == "Down" }

if (unhealthyDependencies.isNotEmpty()) {
logger.error("Dependencies not ready: ${unhealthyDependencies.map { "${it.key}: ${it.value}" }.joinToString(", ")}")
} else {
logger.info("All dependencies are healthy: ${healthStatus.map { "${it.key}: ${it.value}" }.joinToString(", ")}")
}
}

private fun TaskScheduler.scheduleIfEnabled(task: Runnable, cronExpression: String) {
if (cronExpression != "-") {
schedule(task, CronTrigger(cronExpression))
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*******************************************************************************
* Copyright (c) 2021,2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/

package org.eclipse.tractusx.bpdm.cleaning.service

import org.eclipse.tractusx.bpdm.cleaning.util.OrchestratorHealthIndicator
import org.springframework.boot.actuate.health.Status
import org.springframework.stereotype.Service

@Service
class DependencyHealthService(
private val orchestratorHealthIndicator: OrchestratorHealthIndicator
) {

fun checkAllDependencies(): Map<String, String> {
val orchestratorHealth = if (orchestratorHealthIndicator.health().status == Status.UP) "Healthy" else "Down"

return mapOf(
"Orchestrator Service" to orchestratorHealth
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ class OrchestratorHealthIndicator(
override fun health(): Health {

val orchestratorHealthUrl = "${orchestratorConfigProperties.baseUrl}/actuator/health"

return try {
/*
Todo: Create new separate REST api end point for orchestrator service which will enable check on health readiness in authenticated way.
*/
val response = restTemplate.getForEntity(orchestratorHealthUrl, String::class.java)
if (response.statusCode.is2xxSuccessful) {
Health.up().withDetail("Orchestrator Service", "Available").build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ bpdm:
client-secret: "**********"
golden-record-process:
step: CleanAndSync
dependencyCheck:
# How often the golden record connection dependencies should be checked for being healthy
cron: '*/30 * * * * *'

cleaningService:
# When and how often the cleaning service should poll for golden record tasks in the orchestrator
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*******************************************************************************
* Copyright (c) 2021,2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/

package org.eclipse.tractusx.bpdm.gate.config

import jakarta.annotation.PostConstruct
import mu.KotlinLogging
import org.eclipse.tractusx.bpdm.gate.service.DependencyHealthService
import org.springframework.context.annotation.Configuration
import org.springframework.scheduling.TaskScheduler
import org.springframework.scheduling.support.CronTrigger

@Configuration
class DependencyHealthScheduler(
private val dependencyHealthService: DependencyHealthService,
private val taskScheduler: TaskScheduler,
private val configProperties: GoldenRecordTaskConfigProperties
) {

private val logger = KotlinLogging.logger { }

@PostConstruct
fun scheduleHealthChecks() {
taskScheduler.scheduleIfEnabled(
{ performHealthCheck() },
configProperties.dependencyCheck.cron
)
}

private fun performHealthCheck() {
val healthStatus = dependencyHealthService.checkAllDependencies()
val unhealthyDependencies = healthStatus.filter { it.value == "Down" }

if (unhealthyDependencies.isNotEmpty()) {
logger.error("Dependencies not ready: ${unhealthyDependencies.map { "${it.key}: ${it.value}" }.joinToString(", ")}")
} else {
logger.info("All dependencies are healthy: ${healthStatus.map { "${it.key}: ${it.value}" }.joinToString(", ")}")
}
}

private fun TaskScheduler.scheduleIfEnabled(task: Runnable, cronExpression: String) {
if (cronExpression != "-") {
schedule(task, CronTrigger(cronExpression))
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties
data class GoldenRecordTaskConfigProperties(
val creation: CreationProperties = CreationProperties(),
val check: TaskProcessProperties = TaskProcessProperties(),
val healthCheck: TaskProcessProperties = TaskProcessProperties()
val healthCheck: TaskProcessProperties = TaskProcessProperties(),
val dependencyCheck: TaskProcessProperties = TaskProcessProperties()
) {
data class CreationProperties(
val fromSharingMember: CreationTaskProperties = CreationTaskProperties(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2021,2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/

package org.eclipse.tractusx.bpdm.gate.service

import org.eclipse.tractusx.bpdm.gate.util.OrchestratorHealthIndicator
import org.eclipse.tractusx.bpdm.gate.util.PoolHealthIndicator
import org.springframework.boot.actuate.health.Status
import org.springframework.stereotype.Service

@Service
class DependencyHealthService(
private val poolHealthIndicator: PoolHealthIndicator,
private val orchestratorHealthIndicator: OrchestratorHealthIndicator
) {

fun checkAllDependencies(): Map<String, String> {
val poolHealth = if (poolHealthIndicator.health().status == Status.UP) "Healthy" else "Down"
val orchestratorHealth = if (orchestratorHealthIndicator.health().status == Status.UP) "Healthy" else "Down"

return mapOf(
"Pool Service" to poolHealth,
"Orchestrator Service" to orchestratorHealth
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,29 @@

package org.eclipse.tractusx.bpdm.gate.util

import org.springframework.boot.actuate.health.Status
import mu.KotlinLogging
import org.eclipse.tractusx.bpdm.gate.service.DependencyHealthService
import org.springframework.boot.context.event.ApplicationReadyEvent
import org.springframework.context.ApplicationListener
import org.springframework.stereotype.Component

@Component
class GateServiceStartupListener(
private val poolHealthIndicator: PoolHealthIndicator,
private val orchestratorHealthIndicator: OrchestratorHealthIndicator
private val dependencyHealthService: DependencyHealthService
) : ApplicationListener<ApplicationReadyEvent> {

private val logger = KotlinLogging.logger { }

override fun onApplicationEvent(event: ApplicationReadyEvent) {
val poolHealth = poolHealthIndicator.health().status
val orchestratorHealth = orchestratorHealthIndicator.health().status
val healthStatus = dependencyHealthService.checkAllDependencies()
val unhealthyDependencies = healthStatus.filter { it.value == "Down" }

if (poolHealth != Status.UP || orchestratorHealth != Status.UP) {
throw IllegalStateException("Dependencies not ready: Pool: $poolHealth, Orchestrator: $orchestratorHealth")
if (unhealthyDependencies.isNotEmpty()) {
logger.error("Startup failed. Dependencies not ready: ${unhealthyDependencies.map { "${it.key}: ${it.value}" }.joinToString(", ")}")
throw IllegalStateException("Dependencies not ready: ${unhealthyDependencies.map { "${it.key}: ${it.value}" }.joinToString(", ")}")
} else {
logger.info("All dependencies are healthy on startup: ${healthStatus.map { "${it.key}: ${it.value}" }.joinToString(", ")}")
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,28 @@

package org.eclipse.tractusx.bpdm.gate.util

import org.eclipse.tractusx.bpdm.gate.config.OrchestratorClientConfigurationProperties
import org.eclipse.tractusx.bpdm.common.dto.PaginationRequest
import org.eclipse.tractusx.orchestrator.api.client.OrchestrationApiClient
import org.springframework.boot.actuate.health.Health
import org.springframework.boot.actuate.health.HealthIndicator
import org.springframework.stereotype.Component
import org.springframework.web.client.RestTemplate
import java.time.Instant

@Component("orchestratorHealth")
class OrchestratorHealthIndicator(
private val restTemplate: RestTemplate,
private val orchestratorClientProperties: OrchestratorClientConfigurationProperties
private val orchestrationClient: OrchestrationApiClient
) : HealthIndicator {

override fun health(): Health {

val orchestratorHealthUrl = "${orchestratorClientProperties.baseUrl}/actuator/health"
return try {
val response = restTemplate.getForEntity(orchestratorHealthUrl, String::class.java)
if (response.statusCode.is2xxSuccessful) {
/*
We can directly use actuator heath response from Orchestrator service but that will not be an authenticated way.
So, included get finished task events request to achieve the same for now and in future we can create separate REST api endpoint which will provide
health of the service with readiness in authenticated way.
*/
val response = orchestrationClient.finishedTaskEvents.getEvents(Instant.now(), PaginationRequest(page = 0, size = 1))
if (response.contentSize >= 0) {
Health.up().withDetail("Orchestrator Service", "Available").build()
} else {
Health.down().withDetail("Orchestrator Service", "Unreachable").build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,28 @@

package org.eclipse.tractusx.bpdm.gate.util

import org.eclipse.tractusx.bpdm.gate.config.PoolClientConfigurationProperties
import org.eclipse.tractusx.bpdm.common.dto.PaginationRequest
import org.eclipse.tractusx.bpdm.pool.api.client.PoolApiClient
import org.eclipse.tractusx.bpdm.pool.api.model.request.ChangelogSearchRequest
import org.springframework.boot.actuate.health.Health
import org.springframework.boot.actuate.health.HealthIndicator
import org.springframework.stereotype.Component
import org.springframework.web.client.RestTemplate

@Component("poolHealth")
class PoolHealthIndicator(
private val restTemplate: RestTemplate,
private val poolClientProperties: PoolClientConfigurationProperties
private val poolClient: PoolApiClient,
) : HealthIndicator {

override fun health(): Health {

val poolHealthUrl = "${poolClientProperties.baseUrl}/actuator/health"
return try {
val response = restTemplate.getForEntity(poolHealthUrl, String::class.java)
if (response.statusCode.is2xxSuccessful) {
/*
We can directly use actuator heath response from Pool service but that will not be an authenticated way.
So, included changelog request to achieve the same for now and in future we can create separate REST api endpoint which will provide
health of the service with readiness in authenticated way.
*/
val response = poolClient.changelogs.getChangelogEntries(ChangelogSearchRequest(), PaginationRequest(page = 0, size = 1))
if (response.contentSize >= 0) {
Health.up().withDetail("Pool Service", "Available").build()
} else {
Health.down().withDetail("Pool Service", "Unreachable").build()
Expand Down
3 changes: 3 additions & 0 deletions bpdm-gate/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ bpdm:
batchSize: 1000
# How often the golden record tasks should be checked for being healthy
cron: '0 0 0 * * *'
dependencyCheck:
# How often the golden record connection dependencies should be checked for being healthy
cron: '*/30 * * * * *'

# Connection to the pool and orchestrator
client:
Expand Down
Loading

0 comments on commit e37a5d0

Please sign in to comment.