Skip to content

Commit

Permalink
CID-1812 Implements heartbeat after getting assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
alfredo-mfaria committed Aug 15, 2023
1 parent a984238 commit 8c3210c
Show file tree
Hide file tree
Showing 22 changed files with 334 additions and 3 deletions.
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.cloud:spring-cloud-starter-openfeign")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.cloud:spring-cloud-starter-contract-stub-runner")
testImplementation("org.springframework.security:spring-security-test")
testImplementation("org.awaitility:awaitility-kotlin:4.2.0")
}

dependencyManagement {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package net.leanix.vsm.gitlab.broker

import net.leanix.vsm.gitlab.broker.shared.properties.GitLabOnPremProperties
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.runApplication
import org.springframework.cloud.openfeign.EnableFeignClients
import org.springframework.scheduling.annotation.EnableScheduling

@EnableScheduling
@EnableFeignClients
@SpringBootApplication
@EnableConfigurationProperties(GitLabOnPremProperties::class)
class GitlabBrokerApplication

fun main() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package net.leanix.vsm.gitlab.broker.connector.adapter.feign

import net.leanix.vsm.gitlab.broker.connector.domain.AssignmentProvider
import net.leanix.vsm.gitlab.broker.connector.domain.GitLabAssignment
import org.springframework.stereotype.Component

@Component
class FeignAssignmentProvider(private val vsmClient: VsmClient) : AssignmentProvider {
override fun getAssignments(): Result<List<GitLabAssignment>> {
return kotlin.runCatching {
vsmClient.getAssignments()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.leanix.vsm.gitlab.broker.connector.adapter.feign

import net.leanix.vsm.gitlab.broker.connector.domain.GitLabAssignment
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
import org.springframework.web.bind.annotation.RequestParam

@FeignClient(
name = "vsmClient",
url = "\${leanix.vsm.events-broker.base-url}",
configuration = [MtmFeignClientConfiguration::class]
)
interface VsmClient {

@GetMapping("/gitlab-on-prem/assignments")
fun getAssignments(): List<GitLabAssignment>

@GetMapping("/gitlab-on-prem/health/heartbeat")
fun heartbeat(@RequestParam("runId") runId: String): String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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 org.slf4j.LoggerFactory
import org.springframework.stereotype.Service

@Service
class AssignmentService(
private val assignmentProvider: AssignmentProvider
) {

private val logger = LoggerFactory.getLogger(AssignmentService::class.java)

fun getAssignments(): List<GitLabAssignment> {
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()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package net.leanix.vsm.gitlab.broker.connector.domain

interface AssignmentProvider {
fun getAssignments(): Result<List<GitLabAssignment>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package net.leanix.vsm.gitlab.broker.connector.domain

import java.util.UUID

data class GitLabAssignment(
val runId: UUID,
val workspaceId: UUID,
val configurationId: UUID,
val connectorConfiguration: GitLabConfiguration
)

data class GitLabConfiguration(
val orgName: String,
)
Original file line number Diff line number Diff line change
@@ -1,17 +1,45 @@
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
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner
import org.springframework.stereotype.Component

@Component
class InitialStateRunner : ApplicationRunner {
class InitialStateRunner(
private val assignmentService: AssignmentService,
) : ApplicationRunner {

private val logger: Logger = LoggerFactory.getLogger(InitialStateRunner::class.java)

override fun run(args: ApplicationArguments?) {
logger.info("Started get initial state")
logger.info("Started to get initial state")
runCatching {
getAssignments()?.forEach { assignment ->
logger.info(
"Received assignment for ${assignment.connectorConfiguration.orgName} " +
"with configuration id: ${assignment.configurationId} and with run id: ${assignment.runId}"
)
}
}.onSuccess {
logger.info("Cached ${AssignmentsCache.getAll().size} assignments")
}.onFailure { e ->
logger.error("Failed to get initial state", e)
}
}

private fun getAssignments(): List<GitLabAssignment>? {
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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ class ShutdownService {

@PreDestroy
fun onDestroy() {
logger.info("Shutting down github broker")
logger.info("Shutting down gitlab on-prem")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package net.leanix.vsm.gitlab.broker.connector.scheduler

import net.leanix.vsm.gitlab.broker.connector.adapter.feign.VsmClient
import net.leanix.vsm.gitlab.broker.shared.cache.AssignmentsCache
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component

@Component
class HeartbeatScheduler(
private val vsmClient: VsmClient
) {

@Scheduled(fixedRate = 60000) // 1 minute
fun heartbeat() {
AssignmentsCache.getAll().values.forEach { assigment ->
println("Sending heartbeat for runId: ${assigment.runId}")
vsmClient.heartbeat(assigment.runId.toString())
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package net.leanix.vsm.gitlab.broker.shared

object Constants {

const val GITLAB_ENTERPRISE_CONNECTOR = "gitlab-enterprise-connector"
const val API_USER = "apitoken"
const val GITLAB_ON_PREM_VERSION_HEADER = "X-LX-VsmGitLABBroker-Version"
const val EVENT_TYPE_HEADER = "X-LX-CanopyItem-EventType"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.leanix.vsm.gitlab.broker.shared.auth.adapter.feign

import net.leanix.vsm.gitlab.broker.shared.auth.adapter.feign.data.JwtTokenResponse
import org.springframework.cloud.openfeign.FeignClient
import org.springframework.http.HttpHeaders.AUTHORIZATION
import org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestHeader

@FeignClient(
name = "authentication",
url = "\${leanix.vsm.auth.access-token-uri}"
)
fun interface AuthClient {

@PostMapping(value = ["/oauth2/token"], consumes = [APPLICATION_FORM_URLENCODED_VALUE])
fun getToken(
@RequestHeader(name = AUTHORIZATION) authorization: String,
@RequestBody body: String
): JwtTokenResponse
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package net.leanix.vsm.gitlab.broker.shared.auth.adapter.feign.config

import feign.RequestInterceptor
import net.leanix.vsm.gitlab.broker.shared.Constants.GITLAB_ON_PREM_VERSION_HEADER
import net.leanix.vsm.gitlab.broker.shared.auth.application.GetBearerToken
import net.leanix.vsm.gitlab.broker.shared.properties.GradleProperties.Companion.GITLAB_ENTERPRISE_VERSION
import org.springframework.context.annotation.Bean
import org.springframework.http.HttpHeaders.AUTHORIZATION

class MtmFeignClientConfiguration(private val getBearerToken: GetBearerToken) {

@Bean
fun requestInterceptor(): RequestInterceptor {
return RequestInterceptor {
it.header(GITLAB_ON_PREM_VERSION_HEADER, GITLAB_ENTERPRISE_VERSION)
it.header(AUTHORIZATION, "Bearer ${getBearerToken()}")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package net.leanix.vsm.gitlab.broker.shared.auth.adapter.feign.data

import com.fasterxml.jackson.annotation.JsonProperty

data class JwtTokenResponse(
val scope: String,
val expired: Boolean,
@JsonProperty("access_token")
val accessToken: String,
@JsonProperty("token_type")
val tokenType: String,
@JsonProperty("expired_in")
val expiredIn: Int
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package net.leanix.vsm.gitlab.broker.shared.auth.application

import net.leanix.vsm.gitlab.broker.shared.Constants.API_USER
import net.leanix.vsm.gitlab.broker.shared.auth.adapter.feign.AuthClient
import net.leanix.vsm.gitlab.broker.shared.properties.GitLabOnPremProperties
import org.springframework.stereotype.Service
import java.util.*

@Service
class GetBearerToken(
private val authClient: AuthClient,
private val vsmProperties: GitLabOnPremProperties
) {

operator fun invoke(): String {
return authClient.getToken(
authorization = getBasicAuthHeader(),
body = "grant_type=client_credentials"
).accessToken
}

private fun getBasicAuthHeader(): String =
"Basic " + Base64.getEncoder().encodeToString(
"$API_USER:${vsmProperties.apiUserToken}".toByteArray()
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package net.leanix.vsm.gitlab.broker.shared.cache

import net.leanix.vsm.gitlab.broker.connector.domain.GitLabAssignment

object AssignmentsCache {

private val assigmentCache: MutableMap<String, GitLabAssignment> = mutableMapOf()

fun addAll(newAssignments: List<GitLabAssignment>) {
newAssignments.forEach { assignment -> assigmentCache[assignment.connectorConfiguration.orgName] = assignment }
}

fun get(key: String): GitLabAssignment? {
return assigmentCache[key]
}

fun getAll(): Map<String, GitLabAssignment> {
return assigmentCache
}

fun deleteAll() {
assigmentCache.clear()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package net.leanix.vsm.gitlab.broker.shared.properties

import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties(prefix = "leanix.vsm.connector")
data class GitLabOnPremProperties(
val apiUserToken: String
)
12 changes: 12 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
leanix:
base-url: https://${LEANIX_DOMAIN}/services
vsm:
connector:
api-user-token: ${LEANIX_TECHNICAL_USER_TOKEN}
events-broker:
# base-url: ${leanix.vsm.base-url}/vsm-events-broker
base-url: http://localhost:8083
auth:
access-token-uri: ${leanix.base-url}/mtm/v1

server:
port: 8082
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package net.leanix.vsm.gitlab.broker.connector.runner

import com.github.tomakehurst.wiremock.client.WireMock
import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor
import com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo
import org.awaitility.kotlin.await
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock

@SpringBootTest(properties = ["application.runner.enabled=true"])
@AutoConfigureWireMock(port = 6666)
class InitialStateRunnerTest {

@Test
fun `it should get the assignments`() {
await.untilAsserted {
WireMock.verify(
1,
getRequestedFor(
urlEqualTo(
"/gitlab-on-prem/assignments"
)
)
)
}
}
}
9 changes: 9 additions & 0 deletions src/test/resources/application.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
leanix:
base-url: http://localhost:${wiremock.server.port:6666}/services
vsm:
connector:
api-user-token: ${LEANIX_TECHNICAL_USER_TOKEN}
events-broker:
base-url: http://localhost:${wiremock.server.port:6666}
auth:
access-token-uri: ${leanix.base-url}/mtm/v1
13 changes: 13 additions & 0 deletions src/test/resources/mappings/authenticate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"request": {
"url": "/services/mtm/v1/oauth2/token",
"method": "POST"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": "{\"scope\": \"\",\"expired\": false,\"access_token\": \"dummyToken\",\"token_type\": \"bearer\",\"expires_in\": 3599}"
}
}
Loading

0 comments on commit 8c3210c

Please sign in to comment.