Fix production health build identification
Mount the versioned health route instead of the legacy shadowing handler so deployments can verify the exact running image.
This commit is contained in:
@@ -108,6 +108,7 @@ import com.osglab.account.features.gateway.services.GatewayBearerIdentity
|
|||||||
import com.osglab.account.features.gateway.services.GatewayGrantService
|
import com.osglab.account.features.gateway.services.GatewayGrantService
|
||||||
import com.osglab.account.features.gateway.services.GatewayReconciliationService
|
import com.osglab.account.features.gateway.services.GatewayReconciliationService
|
||||||
import com.osglab.account.features.gateway.services.GatewayService
|
import com.osglab.account.features.gateway.services.GatewayService
|
||||||
|
import com.osglab.account.features.health.healthRoutes
|
||||||
import com.osglab.account.features.integrity.AppAttestCrypto
|
import com.osglab.account.features.integrity.AppAttestCrypto
|
||||||
import com.osglab.account.features.integrity.AppAttestRepository
|
import com.osglab.account.features.integrity.AppAttestRepository
|
||||||
import com.osglab.account.features.integrity.AppAttestService
|
import com.osglab.account.features.integrity.AppAttestService
|
||||||
@@ -152,8 +153,6 @@ import io.ktor.client.engine.cio.CIO
|
|||||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation as ClientContentNegotiation
|
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation as ClientContentNegotiation
|
||||||
import io.ktor.client.plugins.HttpTimeout
|
import io.ktor.client.plugins.HttpTimeout
|
||||||
import io.ktor.client.plugins.websocket.WebSockets as ClientWebSockets
|
import io.ktor.client.plugins.websocket.WebSockets as ClientWebSockets
|
||||||
import io.ktor.http.ContentType
|
|
||||||
import io.ktor.http.HttpStatusCode
|
|
||||||
import io.ktor.serialization.kotlinx.json.json
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
import io.ktor.server.application.Application
|
import io.ktor.server.application.Application
|
||||||
import io.ktor.server.application.ApplicationStopped
|
import io.ktor.server.application.ApplicationStopped
|
||||||
@@ -169,9 +168,6 @@ import io.ktor.server.plugins.ratelimit.RateLimitName
|
|||||||
import io.ktor.server.plugins.ratelimit.rateLimit
|
import io.ktor.server.plugins.ratelimit.rateLimit
|
||||||
import io.ktor.server.request.httpMethod
|
import io.ktor.server.request.httpMethod
|
||||||
import io.ktor.server.response.respond
|
import io.ktor.server.response.respond
|
||||||
import io.ktor.server.response.respondText
|
|
||||||
import io.ktor.server.routing.get
|
|
||||||
import io.ktor.server.routing.Route
|
|
||||||
import io.ktor.server.routing.routing
|
import io.ktor.server.routing.routing
|
||||||
import io.ktor.server.websocket.WebSockets
|
import io.ktor.server.websocket.WebSockets
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
@@ -379,22 +375,6 @@ fun Application.module() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.healthRoutes(databaseFactory: DatabaseFactory? = null) {
|
|
||||||
get("/health") { call.respondText("""{"status":"UP"}""", ContentType.Application.Json) }
|
|
||||||
get("/health/live") { call.respondText("""{"status":"UP"}""", ContentType.Application.Json) }
|
|
||||||
get("/health/ready") {
|
|
||||||
if (databaseFactory?.isReady() == true) {
|
|
||||||
call.respondText("""{"status":"UP"}""", ContentType.Application.Json)
|
|
||||||
} else {
|
|
||||||
call.respondText(
|
|
||||||
"""{"status":"DOWN"}""",
|
|
||||||
ContentType.Application.Json,
|
|
||||||
HttpStatusCode.ServiceUnavailable,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun accountServerModule(config: AppConfig): Module = module {
|
fun accountServerModule(config: AppConfig): Module = module {
|
||||||
single { config }
|
single { config }
|
||||||
single { DatabaseFactory(config.database) }
|
single { DatabaseFactory(config.database) }
|
||||||
|
|||||||
@@ -1,16 +1,23 @@
|
|||||||
package com.osglab.account
|
package com.osglab.account
|
||||||
|
|
||||||
import com.osglab.account.config.AppConfig
|
import com.osglab.account.config.AppConfig
|
||||||
|
import com.osglab.account.config.DatabaseFactory
|
||||||
import com.osglab.account.features.credits.repositories.BillingTransactionRunner
|
import com.osglab.account.features.credits.repositories.BillingTransactionRunner
|
||||||
import com.osglab.account.features.credits.repositories.BillingUnitOfWork
|
import com.osglab.account.features.credits.repositories.BillingUnitOfWork
|
||||||
import com.osglab.account.features.credits.services.CreditOperations
|
import com.osglab.account.features.credits.services.CreditOperations
|
||||||
import com.osglab.account.features.credits.services.CreditService
|
import com.osglab.account.features.credits.services.CreditService
|
||||||
|
import com.osglab.account.features.health.healthRoutes
|
||||||
import com.osglab.account.features.referrals.services.ReferralOperations
|
import com.osglab.account.features.referrals.services.ReferralOperations
|
||||||
import com.osglab.account.features.referrals.services.ReferralService
|
import com.osglab.account.features.referrals.services.ReferralService
|
||||||
|
import io.mockk.mockk
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.statement.bodyAsText
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
|
import io.ktor.server.application.install
|
||||||
import io.ktor.server.config.MapApplicationConfig
|
import io.ktor.server.config.MapApplicationConfig
|
||||||
|
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||||
import io.ktor.server.routing.routing
|
import io.ktor.server.routing.routing
|
||||||
import io.ktor.server.testing.testApplication
|
import io.ktor.server.testing.testApplication
|
||||||
import org.koin.dsl.koinApplication
|
import org.koin.dsl.koinApplication
|
||||||
@@ -21,11 +28,23 @@ import kotlin.test.Test
|
|||||||
class ApplicationTest {
|
class ApplicationTest {
|
||||||
@Test
|
@Test
|
||||||
fun `liveness endpoint remains independent of external services`() = testApplication {
|
fun `liveness endpoint remains independent of external services`() = testApplication {
|
||||||
|
val buildSha = "a".repeat(40)
|
||||||
application {
|
application {
|
||||||
routing { healthRoutes() }
|
install(ContentNegotiation) {
|
||||||
|
json()
|
||||||
|
}
|
||||||
|
routing {
|
||||||
|
healthRoutes(
|
||||||
|
databaseFactory = mockk<DatabaseFactory>(relaxed = true),
|
||||||
|
buildSha = buildSha,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.get("/health/live").status shouldBe HttpStatusCode.OK
|
client.get("/health/live").apply {
|
||||||
|
status shouldBe HttpStatusCode.OK
|
||||||
|
bodyAsText() shouldBe """{"status":"UP","buildSha":"$buildSha"}"""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user