Skip to content

Commit

Permalink
CID-2774: Send error logs to backend
Browse files Browse the repository at this point in the history
  • Loading branch information
mohamedlajmileanix committed Sep 3, 2024
1 parent afcd120 commit a37dbbf
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package net.leanix.githubagent.controllers.advice

import net.leanix.githubagent.exceptions.InvalidEventSignatureException
import net.leanix.githubagent.exceptions.WebhookSecretNotSetException
import net.leanix.githubagent.services.SyncLogService
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.http.HttpStatus
Expand All @@ -10,22 +11,28 @@ import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler

@ControllerAdvice
class GlobalExceptionHandler {
class GlobalExceptionHandler(
private val syncLogService: SyncLogService
) {

val exceptionLogger: Logger = LoggerFactory.getLogger(GlobalExceptionHandler::class.java)

@ExceptionHandler(InvalidEventSignatureException::class)
fun handleInvalidEventSignatureException(exception: InvalidEventSignatureException): ProblemDetail {
val problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.UNAUTHORIZED, "Invalid event signature")
val detail = "Received event with an invalid signature"
val problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.UNAUTHORIZED, detail)
problemDetail.title = exception.message
exceptionLogger.warn(exception.message)
syncLogService.sendErrorLog(detail)
return problemDetail
}

@ExceptionHandler(WebhookSecretNotSetException::class)
fun handleWebhookSecretNotSetException(exception: WebhookSecretNotSetException): ProblemDetail {
val problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, "Webhook secret not set")
val detail = "Unable to process GitHub event. Webhook secret not set"
val problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, detail)
problemDetail.title = exception.message
syncLogService.sendErrorLog(detail)
return problemDetail
}
}
23 changes: 23 additions & 0 deletions src/main/kotlin/net/leanix/githubagent/dto/SyncLogDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package net.leanix.githubagent.dto

import java.util.UUID

data class SyncLogDto(
val runId: UUID?,
val trigger: Trigger,
val logLevel: LogLevel,
val message: String
)

enum class Trigger {
START_FULL_SYNC,
FINISH_FULL_SYNC,
GENERIC
}

enum class LogLevel {
OK,
WARNING,
INFO,
ERROR
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import net.leanix.githubagent.client.GitHubClient
import net.leanix.githubagent.config.GitHubEnterpriseProperties
import net.leanix.githubagent.dto.Installation
import net.leanix.githubagent.exceptions.FailedToCreateJWTException
import net.leanix.githubagent.exceptions.GitHubAppInsufficientPermissionsException
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.slf4j.LoggerFactory
import org.springframework.core.io.ResourceLoader
Expand All @@ -28,6 +29,7 @@ class GitHubAuthenticationService(
private val resourceLoader: ResourceLoader,
private val gitHubEnterpriseService: GitHubEnterpriseService,
private val gitHubClient: GitHubClient,
private val syncLogService: SyncLogService,
) {

companion object {
Expand Down Expand Up @@ -58,6 +60,11 @@ class GitHubAuthenticationService(
cachingService.set("jwtToken", jwt.getOrThrow(), JWT_EXPIRATION_DURATION)
}.onFailure {
logger.error("Failed to generate/validate JWT token", it)
if (it is GitHubAppInsufficientPermissionsException) {
syncLogService.sendErrorLog(it.message.toString())
} else {
syncLogService.sendErrorLog("Failed to generate/validate JWT token")
}
if (it is InvalidKeySpecException) {
throw IllegalArgumentException("The provided private key is not in a valid PKCS8 format.", it)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ class GitHubScanningService(
private val cachingService: CachingService,
private val webSocketService: WebSocketService,
private val gitHubGraphQLService: GitHubGraphQLService,
private val gitHubAuthenticationService: GitHubAuthenticationService
private val gitHubAuthenticationService: GitHubAuthenticationService,
private val syncLogService: SyncLogService
) {

private val logger = LoggerFactory.getLogger(GitHubScanningService::class.java)
Expand All @@ -30,8 +31,10 @@ class GitHubScanningService(
fetchAndSendRepositoriesData(installation)
}
}.onFailure {
val message = "Error while scanning GitHub resources"
syncLogService.sendErrorLog(message)
cachingService.remove("runId")
logger.error("Error while scanning GitHub resources")
logger.error(message)
throw it
}
}
Expand Down
33 changes: 33 additions & 0 deletions src/main/kotlin/net/leanix/githubagent/services/SyncLogService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package net.leanix.githubagent.services

import net.leanix.githubagent.dto.LogLevel
import net.leanix.githubagent.dto.SyncLogDto
import net.leanix.githubagent.dto.Trigger
import net.leanix.githubagent.shared.LOGS_TOPIC
import org.springframework.stereotype.Service
import java.util.UUID

@Service
class SyncLogService(
private val webSocketService: WebSocketService,
private val cachingService: CachingService
) {
fun sendErrorLog(message: String) {
sendSyncLog(message, LOGS_TOPIC, null, LogLevel.ERROR)
}

fun sendSyncLog(message: String, topic: String, trigger: Trigger?, logLevel: LogLevel) {
val runId = cachingService.get("runId") as UUID
val syncLogDto = SyncLogDto(
runId = runId,
trigger = trigger ?: Trigger.GENERIC,
logLevel = logLevel,
message = message
)
webSocketService.sendMessage(constructTopic(topic), syncLogDto)
}

private fun constructTopic(topic: String): String {
return "${cachingService.get("runId")}/$topic"
}
}
1 change: 1 addition & 0 deletions src/main/kotlin/net/leanix/githubagent/shared/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package net.leanix.githubagent.shared

const val TOPIC_PREFIX = "/app/ghe/"
const val AGENT_METADATA_TOPIC = "agent"
const val LOGS_TOPIC = "logs"

enum class ManifestFileName(val fileName: String) {
YAML("leanix.yaml"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import com.ninjasquad.springmockk.MockkBean
import io.mockk.every
import net.leanix.githubagent.exceptions.WebhookSecretNotSetException
import net.leanix.githubagent.services.GitHubWebhookHandler
import net.leanix.githubagent.services.SyncLogService
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
Expand All @@ -20,6 +22,14 @@ class GitHubWebhookControllerTest {
@MockkBean
private lateinit var gitHubWebhookHandler: GitHubWebhookHandler

@MockkBean
private lateinit var syncLogService: SyncLogService

@BeforeEach
fun setUp() {
every { syncLogService.sendErrorLog(any()) } returns Unit
}

@Test
fun `should return 202 if webhook event is processed successfully`() {
val eventType = "PUSH"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import net.leanix.githubagent.client.GitHubClient
import net.leanix.githubagent.config.GitHubEnterpriseProperties
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.springframework.core.io.ClassPathResource
Expand All @@ -18,14 +19,21 @@ class GitHubAuthenticationServiceTest {
private val resourceLoader = mockk<ResourceLoader>()
private val gitHubEnterpriseService = mockk<GitHubEnterpriseService>()
private val gitHubClient = mockk<GitHubClient>()
private val syncLogService = mockk<SyncLogService>()
private val githubAuthenticationService = GitHubAuthenticationService(
cachingService,
githubEnterpriseProperties,
resourceLoader,
gitHubEnterpriseService,
gitHubClient
gitHubClient,
syncLogService
)

@BeforeEach
fun setUp() {
every { syncLogService.sendErrorLog(any()) } returns Unit
}

@Test
fun `generateJwtToken with valid data should not throw exception`() {
every { cachingService.get(any()) } returns "dummy-value"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ class GitHubScanningServiceTest {
private val webSocketService = mockk<WebSocketService>(relaxUnitFun = true)
private val gitHubGraphQLService = mockk<GitHubGraphQLService>()
private val gitHubAuthenticationService = mockk<GitHubAuthenticationService>()
private val syncLogService = mockk<SyncLogService>()
private val gitHubScanningService = GitHubScanningService(
gitHubClient,
cachingService,
webSocketService,
gitHubGraphQLService,
gitHubAuthenticationService
gitHubAuthenticationService,
syncLogService
)
private val runId = UUID.randomUUID()

Expand All @@ -50,6 +52,7 @@ class GitHubScanningServiceTest {
)
every { cachingService.remove(any()) } returns Unit
every { gitHubAuthenticationService.generateAndCacheInstallationTokens(any(), any()) } returns Unit
every { syncLogService.sendErrorLog(any()) } returns Unit
}

@Test
Expand Down

0 comments on commit a37dbbf

Please sign in to comment.