Add health check
This commit is contained in:
parent
9467db2382
commit
681fd635b3
@ -32,6 +32,8 @@ RUN strip -p --strip-unneeded /myjdk/lib/server/libjvm.so
|
|||||||
|
|
||||||
FROM alpine
|
FROM alpine
|
||||||
|
|
||||||
|
RUN apk add --no-cache curl
|
||||||
|
|
||||||
ENV APPLICATION_USER simplenotes
|
ENV APPLICATION_USER simplenotes
|
||||||
RUN adduser -D -g '' $APPLICATION_USER
|
RUN adduser -D -g '' $APPLICATION_USER
|
||||||
|
|
||||||
|
|||||||
12
app/src/main/kotlin/controllers/HealthCheckController.kt
Normal file
12
app/src/main/kotlin/controllers/HealthCheckController.kt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package be.simplenotes.app.controllers
|
||||||
|
|
||||||
|
import be.simplenotes.persistance.DbHealthCheck
|
||||||
|
import org.http4k.core.Request
|
||||||
|
import org.http4k.core.Response
|
||||||
|
import org.http4k.core.Status.Companion.OK
|
||||||
|
import org.http4k.core.Status.Companion.SERVICE_UNAVAILABLE
|
||||||
|
|
||||||
|
class HealthCheckController(private val dbHealthCheck: DbHealthCheck) {
|
||||||
|
fun healthCheck(request: Request) =
|
||||||
|
if (dbHealthCheck.isOk()) Response(OK) else Response(SERVICE_UNAVAILABLE)
|
||||||
|
}
|
||||||
@ -1,9 +1,6 @@
|
|||||||
package be.simplenotes.app.modules
|
package be.simplenotes.app.modules
|
||||||
|
|
||||||
import be.simplenotes.app.controllers.BaseController
|
import be.simplenotes.app.controllers.*
|
||||||
import be.simplenotes.app.controllers.NoteController
|
|
||||||
import be.simplenotes.app.controllers.SettingsController
|
|
||||||
import be.simplenotes.app.controllers.UserController
|
|
||||||
import be.simplenotes.app.views.BaseView
|
import be.simplenotes.app.views.BaseView
|
||||||
import be.simplenotes.app.views.NoteView
|
import be.simplenotes.app.views.NoteView
|
||||||
import be.simplenotes.app.views.SettingView
|
import be.simplenotes.app.views.SettingView
|
||||||
@ -16,6 +13,7 @@ val userModule = module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val baseModule = module {
|
val baseModule = module {
|
||||||
|
single { HealthCheckController(get()) }
|
||||||
single { BaseController(get()) }
|
single { BaseController(get()) }
|
||||||
single { BaseView(get()) }
|
single { BaseView(get()) }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,7 @@ val serverModule = module {
|
|||||||
get(),
|
get(),
|
||||||
get(),
|
get(),
|
||||||
get(),
|
get(),
|
||||||
|
get(),
|
||||||
requiredAuth = get(AuthType.Required.qualifier),
|
requiredAuth = get(AuthType.Required.qualifier),
|
||||||
optionalAuth = get(AuthType.Optional.qualifier),
|
optionalAuth = get(AuthType.Optional.qualifier),
|
||||||
apiAuth = get(named("apiAuthFilter")),
|
apiAuth = get(named("apiAuthFilter")),
|
||||||
|
|||||||
@ -2,10 +2,7 @@ package be.simplenotes.app.routes
|
|||||||
|
|
||||||
import be.simplenotes.app.api.ApiNoteController
|
import be.simplenotes.app.api.ApiNoteController
|
||||||
import be.simplenotes.app.api.ApiUserController
|
import be.simplenotes.app.api.ApiUserController
|
||||||
import be.simplenotes.app.controllers.BaseController
|
import be.simplenotes.app.controllers.*
|
||||||
import be.simplenotes.app.controllers.NoteController
|
|
||||||
import be.simplenotes.app.controllers.SettingsController
|
|
||||||
import be.simplenotes.app.controllers.UserController
|
|
||||||
import be.simplenotes.app.filters.*
|
import be.simplenotes.app.filters.*
|
||||||
import be.simplenotes.domain.security.JwtPayload
|
import be.simplenotes.domain.security.JwtPayload
|
||||||
import org.http4k.core.*
|
import org.http4k.core.*
|
||||||
@ -22,6 +19,7 @@ class Router(
|
|||||||
private val settingsController: SettingsController,
|
private val settingsController: SettingsController,
|
||||||
private val apiUserController: ApiUserController,
|
private val apiUserController: ApiUserController,
|
||||||
private val apiNoteController: ApiNoteController,
|
private val apiNoteController: ApiNoteController,
|
||||||
|
private val healthCheckController: HealthCheckController,
|
||||||
private val requiredAuth: Filter,
|
private val requiredAuth: Filter,
|
||||||
private val optionalAuth: Filter,
|
private val optionalAuth: Filter,
|
||||||
private val apiAuth: Filter,
|
private val apiAuth: Filter,
|
||||||
@ -31,7 +29,11 @@ class Router(
|
|||||||
) {
|
) {
|
||||||
operator fun invoke(): RoutingHttpHandler {
|
operator fun invoke(): RoutingHttpHandler {
|
||||||
|
|
||||||
val basicRoutes = ImmutableFilter.then(static(Classpath("/static"), "woff2" to ContentType("font/woff2")))
|
val basicRoutes =
|
||||||
|
routes(
|
||||||
|
"/health" bind GET to healthCheckController::healthCheck,
|
||||||
|
ImmutableFilter.then(static(Classpath("/static"), "woff2" to ContentType("font/woff2")))
|
||||||
|
)
|
||||||
|
|
||||||
val publicRoutes = routes(
|
val publicRoutes = routes(
|
||||||
"/" bind GET public baseController::index,
|
"/" bind GET public baseController::index,
|
||||||
|
|||||||
@ -11,7 +11,11 @@ simplenotes.be {
|
|||||||
import strict-transport
|
import strict-transport
|
||||||
header -Server
|
header -Server
|
||||||
|
|
||||||
reverse_proxy http://localhost:8080
|
reverse_proxy http://localhost:8080 {
|
||||||
|
health_path /health
|
||||||
|
health_interval 5s
|
||||||
|
health_timeout 200ms
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dev.simplenotes.be {
|
dev.simplenotes.be {
|
||||||
|
|||||||
@ -19,8 +19,10 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- notes-db-volume:/var/lib/mysql
|
- notes-db-volume:/var/lib/mysql
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
|
test: "mysql --protocol=tcp -u simplenotes -p$MYSQL_PASSWORD -e 'show databases'"
|
||||||
timeout: 10s
|
interval: 5s
|
||||||
|
timeout: 1s
|
||||||
|
start_period: 2s
|
||||||
retries: 10
|
retries: 10
|
||||||
|
|
||||||
simplenotes:
|
simplenotes:
|
||||||
@ -39,6 +41,12 @@ services:
|
|||||||
# - PASSWORD
|
# - PASSWORD
|
||||||
ports:
|
ports:
|
||||||
- 127.0.0.1:8080:8080
|
- 127.0.0.1:8080:8080
|
||||||
|
healthcheck:
|
||||||
|
test: "curl --fail -s http://localhost:8080/health"
|
||||||
|
interval: 5s
|
||||||
|
timeout: 1s
|
||||||
|
start_period: 2s
|
||||||
|
retries: 3
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|||||||
28
persistance/src/main/kotlin/HealthCheck.kt
Normal file
28
persistance/src/main/kotlin/HealthCheck.kt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package be.simplenotes.persistance
|
||||||
|
|
||||||
|
import be.simplenotes.persistance.utils.DbType
|
||||||
|
import be.simplenotes.persistance.utils.type
|
||||||
|
import be.simplenotes.shared.config.DataSourceConfig
|
||||||
|
import me.liuwj.ktorm.database.Database
|
||||||
|
import me.liuwj.ktorm.database.asIterable
|
||||||
|
import java.sql.SQLTransientException
|
||||||
|
|
||||||
|
interface DbHealthCheck {
|
||||||
|
fun isOk(): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DbHealthCheckImpl(
|
||||||
|
private val db: Database,
|
||||||
|
private val dataSourceConfig: DataSourceConfig,
|
||||||
|
) : DbHealthCheck {
|
||||||
|
override fun isOk() = if (dataSourceConfig.type() == DbType.H2) true
|
||||||
|
else try {
|
||||||
|
db.useConnection { connection ->
|
||||||
|
connection.prepareStatement("""SHOW DATABASES""").use {
|
||||||
|
it.executeQuery().asIterable().map { it.getString(1) }
|
||||||
|
}
|
||||||
|
}.any { it in dataSourceConfig.jdbcUrl }
|
||||||
|
} catch (e: SQLTransientException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,18 +1,24 @@
|
|||||||
package be.simplenotes.persistance
|
package be.simplenotes.persistance
|
||||||
|
|
||||||
|
import be.simplenotes.persistance.utils.DbType
|
||||||
|
import be.simplenotes.persistance.utils.type
|
||||||
import be.simplenotes.shared.config.DataSourceConfig
|
import be.simplenotes.shared.config.DataSourceConfig
|
||||||
import org.flywaydb.core.Flyway
|
import org.flywaydb.core.Flyway
|
||||||
import javax.sql.DataSource
|
import javax.sql.DataSource
|
||||||
|
|
||||||
|
interface DbMigrations {
|
||||||
|
fun migrate()
|
||||||
|
}
|
||||||
|
|
||||||
internal class DbMigrationsImpl(
|
internal class DbMigrationsImpl(
|
||||||
private val dataSource: DataSource,
|
private val dataSource: DataSource,
|
||||||
private val dataSourceConfig: DataSourceConfig
|
private val dataSourceConfig: DataSourceConfig,
|
||||||
) : DbMigrations {
|
) : DbMigrations {
|
||||||
override fun migrate() {
|
override fun migrate() {
|
||||||
|
|
||||||
val migrationDir = when {
|
val migrationDir = when (dataSourceConfig.type()) {
|
||||||
dataSourceConfig.jdbcUrl.contains("mariadb") -> "db/migration/mariadb"
|
DbType.H2 -> "db/migration/other"
|
||||||
else -> "db/migration/other"
|
DbType.MariaDb -> "db/migration/mariadb"
|
||||||
}
|
}
|
||||||
|
|
||||||
Flyway.configure()
|
Flyway.configure()
|
||||||
@ -13,10 +13,6 @@ import org.koin.dsl.module
|
|||||||
import org.koin.dsl.onClose
|
import org.koin.dsl.onClose
|
||||||
import javax.sql.DataSource
|
import javax.sql.DataSource
|
||||||
|
|
||||||
interface DbMigrations {
|
|
||||||
fun migrate()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hikariDataSource(conf: DataSourceConfig): HikariDataSource {
|
private fun hikariDataSource(conf: DataSourceConfig): HikariDataSource {
|
||||||
val hikariConfig = HikariConfig().also {
|
val hikariConfig = HikariConfig().also {
|
||||||
it.jdbcUrl = conf.jdbcUrl
|
it.jdbcUrl = conf.jdbcUrl
|
||||||
@ -41,4 +37,5 @@ val persistanceModule = module {
|
|||||||
get<DbMigrations>().migrate()
|
get<DbMigrations>().migrate()
|
||||||
Database.connect(get<DataSource>())
|
Database.connect(get<DataSource>())
|
||||||
}
|
}
|
||||||
|
single<DbHealthCheck> { DbHealthCheckImpl(get(), get()) }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
package be.simplenotes.persistance.utils
|
||||||
|
|
||||||
|
import be.simplenotes.shared.config.DataSourceConfig
|
||||||
|
|
||||||
|
enum class DbType { H2, MariaDb }
|
||||||
|
|
||||||
|
fun DataSourceConfig.type(): DbType = if (jdbcUrl.contains("mariadb")) DbType.MariaDb
|
||||||
|
else DbType.H2
|
||||||
Loading…
x
Reference in New Issue
Block a user