diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/adapter/feign/VsmClient.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/adapter/feign/VsmClient.kt index 96d6136..29205de 100644 --- a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/adapter/feign/VsmClient.kt +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/adapter/feign/VsmClient.kt @@ -1,6 +1,7 @@ package net.leanix.vsm.gitlab.broker.connector.adapter.feign import net.leanix.vsm.gitlab.broker.connector.domain.GitLabAssignment +import net.leanix.vsm.gitlab.broker.connector.domain.GitLabHeartbeatResponse import net.leanix.vsm.gitlab.broker.shared.auth.adapter.feign.config.MtmFeignClientConfiguration import org.springframework.cloud.openfeign.FeignClient import org.springframework.web.bind.annotation.GetMapping @@ -17,6 +18,6 @@ interface VsmClient { @GetMapping("/gitlab-on-prem/assignments") fun getAssignments(): List - @PutMapping("/gitlab-on-prem/health/heartbeat") - fun heartbeat(@RequestParam("runId") runId: String): String + @PutMapping("/gitlab-on-prem/heartbeat") + fun heartbeat(@RequestParam("runId") runId: String): GitLabHeartbeatResponse } diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/applicaiton/AssignmentService.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/applicaiton/AssignmentService.kt index 2c32872..7d3d128 100644 --- a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/applicaiton/AssignmentService.kt +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/applicaiton/AssignmentService.kt @@ -2,6 +2,7 @@ package net.leanix.vsm.gitlab.broker.connector.applicaiton import net.leanix.vsm.gitlab.broker.connector.domain.AssignmentProvider import net.leanix.vsm.gitlab.broker.connector.domain.GitLabAssignment +import net.leanix.vsm.gitlab.broker.shared.cache.AssignmentsCache import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -12,11 +13,18 @@ class AssignmentService( private val logger = LoggerFactory.getLogger(AssignmentService::class.java) - fun getAssignments(): List { - return assignmentProvider.getAssignments().onFailure { - logger.error("Failed to retrieve assignment list: ", it) - }.onSuccess { - logger.info("Assignment list retrieved with success with ${it.size} assignments") - }.getOrThrow() + fun getAssignments(): List? { + kotlin.runCatching { + val assignments = assignmentProvider.getAssignments().onFailure { + logger.error("Failed to retrieve assignment list: ", it) + }.onSuccess { + logger.info("Assignment list retrieved with success with ${it.size} assignments") + }.getOrThrow() + + AssignmentsCache.deleteAll() + AssignmentsCache.addAll(assignments) + return assignments + } + return null } } diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/domain/GitLabHeartbeatResponse.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/domain/GitLabHeartbeatResponse.kt new file mode 100644 index 0000000..b65fc87 --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/domain/GitLabHeartbeatResponse.kt @@ -0,0 +1,6 @@ +package net.leanix.vsm.gitlab.broker.connector.domain + +data class GitLabHeartbeatResponse( + val status: String, + val newConfigAvailable: Boolean +) diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/runner/InitialStateRunner.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/runner/InitialStateRunner.kt index 2dd8bf8..dcd681a 100644 --- a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/runner/InitialStateRunner.kt +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/runner/InitialStateRunner.kt @@ -1,7 +1,6 @@ package net.leanix.vsm.gitlab.broker.connector.runner import net.leanix.vsm.gitlab.broker.connector.applicaiton.AssignmentService -import net.leanix.vsm.gitlab.broker.connector.domain.GitLabAssignment import net.leanix.vsm.gitlab.broker.shared.cache.AssignmentsCache import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -19,7 +18,7 @@ class InitialStateRunner( override fun run(args: ApplicationArguments?) { logger.info("Started to get initial state") runCatching { - getAssignments()?.forEach { assignment -> + assignmentService.getAssignments()?.forEach { assignment -> logger.info( "Received assignment for ${assignment.connectorConfiguration.orgName} " + "with configuration id: ${assignment.configurationId} and with run id: ${assignment.runId}" @@ -31,15 +30,4 @@ class InitialStateRunner( logger.error("Failed to get initial state", e) } } - - private fun getAssignments(): List? { - kotlin.runCatching { - val assignments = assignmentService.getAssignments() - AssignmentsCache.addAll(assignments) - return assignments - }.onFailure { - logger.error("Failed to get initial state. No assignment found for this workspace id") - } - return null - } } diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatScheduler.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatScheduler.kt index fa69632..068d214 100644 --- a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatScheduler.kt +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatScheduler.kt @@ -1,6 +1,7 @@ package net.leanix.vsm.gitlab.broker.connector.scheduler import net.leanix.vsm.gitlab.broker.connector.adapter.feign.VsmClient +import net.leanix.vsm.gitlab.broker.connector.applicaiton.AssignmentService import net.leanix.vsm.gitlab.broker.shared.cache.AssignmentsCache import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Scheduled @@ -8,16 +9,24 @@ import org.springframework.stereotype.Component @Component class HeartbeatScheduler( - private val vsmClient: VsmClient + private val vsmClient: VsmClient, + private val assignmentService: AssignmentService ) { private val logger = LoggerFactory.getLogger(HeartbeatScheduler::class.java) - @Scheduled(fixedRate = 300000) // 5 minute + @Scheduled(fixedRateString = "\${leanix.heartbeat.interval}") + @Suppress("ForbiddenComment") fun heartbeat() { - AssignmentsCache.getAll().values.forEach { assigment -> - logger.info("Sending heartbeat for runId: ${assigment.runId}") - vsmClient.heartbeat(assigment.runId.toString()) + AssignmentsCache.getAll().values.forEach { assignment -> + logger.info("Sending heartbeat for runId: ${assignment.runId}") + vsmClient.heartbeat(assignment.runId.toString()) + .takeIf { it.newConfigAvailable } + ?.also { + assignmentService.getAssignments() + // TODO: here we need to re-fetch everything for this config + // remove @Suppress from function definition + } } } } diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml new file mode 100644 index 0000000..4537d90 --- /dev/null +++ b/src/main/resources/application-local.yaml @@ -0,0 +1,12 @@ +leanix: + base-url: http://localhost:8080 + heartbeat: + interval: ${LEANIX_HEARTBEAT_INTERVAL:300000} # 5 minutes + vsm: + events-broker: + base-url: http://localhost:8080 + auth: + access-token-uri: https://test-app-1.leanix.net/services/mtm/v1 + +server: + port: 8082 diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 07a1dfc..decc64b 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,5 +1,7 @@ leanix: base-url: https://${LEANIX_DOMAIN}/services + heartbeat: + interval: ${LEANIX_HEARTBEAT_INTERVAL:300000} # 5 minutes vsm: connector: api-user-token: ${LEANIX_TECHNICAL_USER_TOKEN} diff --git a/src/test/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatSchedulerTest.kt b/src/test/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatSchedulerTest.kt new file mode 100644 index 0000000..d75226c --- /dev/null +++ b/src/test/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatSchedulerTest.kt @@ -0,0 +1,50 @@ +package net.leanix.vsm.gitlab.broker.connector.scheduler + +import net.leanix.vsm.gitlab.broker.connector.adapter.feign.VsmClient +import net.leanix.vsm.gitlab.broker.connector.applicaiton.AssignmentService +import net.leanix.vsm.gitlab.broker.connector.domain.GitLabAssignment +import net.leanix.vsm.gitlab.broker.connector.domain.GitLabConfiguration +import net.leanix.vsm.gitlab.broker.connector.domain.GitLabHeartbeatResponse +import net.leanix.vsm.gitlab.broker.shared.cache.AssignmentsCache +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoInteractions +import org.mockito.Mockito.`when` +import java.util.UUID +import java.util.UUID.randomUUID + +class HeartbeatSchedulerTest { + + private val vsmClient = mock(VsmClient::class.java) + private val assignmentService = mock(AssignmentService::class.java) + private val subject = HeartbeatScheduler(vsmClient, assignmentService) + private val runId: UUID = randomUUID() + + @BeforeEach + fun setupAssignmentCache() { + AssignmentsCache.deleteAll() + AssignmentsCache.addAll(listOf(getGitlabAssignment())) + } + + @Test + fun `should re-fetch assignments when new config available`() { + `when`(vsmClient.heartbeat(runId.toString())).thenReturn(GitLabHeartbeatResponse("OK", true)) + + subject.heartbeat() + + verify(assignmentService).getAssignments() + } + + @Test + fun `should not re-fetch assignments when no new config available`() { + `when`(vsmClient.heartbeat(runId.toString())).thenReturn(GitLabHeartbeatResponse("OK", false)) + + subject.heartbeat() + + verifyNoInteractions(assignmentService) + } + + private fun getGitlabAssignment() = GitLabAssignment(runId, randomUUID(), randomUUID(), GitLabConfiguration("")) +} diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml index dd5bf67..f3cb85c 100644 --- a/src/test/resources/application.yaml +++ b/src/test/resources/application.yaml @@ -1,5 +1,7 @@ leanix: base-url: http://localhost:${wiremock.server.port:6666}/services + heartbeat: + interval: ${LEANIX_HEARTBEAT_INTERVAL:300000} # 5 minutes vsm: connector: api-user-token: ${LEANIX_TECHNICAL_USER_TOKEN}