Add health check

This commit is contained in:
Hubert Van De Walle 2020-10-21 16:30:00 +02:00
parent 9467db2382
commit 681fd635b3
11 changed files with 86 additions and 20 deletions

View File

@ -32,6 +32,8 @@ RUN strip -p --strip-unneeded /myjdk/lib/server/libjvm.so
FROM alpine
RUN apk add --no-cache curl
ENV APPLICATION_USER simplenotes
RUN adduser -D -g '' $APPLICATION_USER

View 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)
}

View File

@ -1,9 +1,6 @@
package be.simplenotes.app.modules
import be.simplenotes.app.controllers.BaseController
import be.simplenotes.app.controllers.NoteController
import be.simplenotes.app.controllers.SettingsController
import be.simplenotes.app.controllers.UserController
import be.simplenotes.app.controllers.*
import be.simplenotes.app.views.BaseView
import be.simplenotes.app.views.NoteView
import be.simplenotes.app.views.SettingView
@ -16,6 +13,7 @@ val userModule = module {
}
val baseModule = module {
single { HealthCheckController(get()) }
single { BaseController(get()) }
single { BaseView(get()) }
}

View File

@ -45,6 +45,7 @@ val serverModule = module {
get(),
get(),
get(),
get(),
requiredAuth = get(AuthType.Required.qualifier),
optionalAuth = get(AuthType.Optional.qualifier),
apiAuth = get(named("apiAuthFilter")),

View File

@ -2,10 +2,7 @@ package be.simplenotes.app.routes
import be.simplenotes.app.api.ApiNoteController
import be.simplenotes.app.api.ApiUserController
import be.simplenotes.app.controllers.BaseController
import be.simplenotes.app.controllers.NoteController
import be.simplenotes.app.controllers.SettingsController
import be.simplenotes.app.controllers.UserController
import be.simplenotes.app.controllers.*
import be.simplenotes.app.filters.*
import be.simplenotes.domain.security.JwtPayload
import org.http4k.core.*
@ -22,6 +19,7 @@ class Router(
private val settingsController: SettingsController,
private val apiUserController: ApiUserController,
private val apiNoteController: ApiNoteController,
private val healthCheckController: HealthCheckController,
private val requiredAuth: Filter,
private val optionalAuth: Filter,
private val apiAuth: Filter,
@ -31,7 +29,11 @@ class Router(
) {
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(
"/" bind GET public baseController::index,

View File

@ -11,7 +11,11 @@ simplenotes.be {
import strict-transport
header -Server
reverse_proxy http://localhost:8080
reverse_proxy http://localhost:8080 {
health_path /health
health_interval 5s
health_timeout 200ms
}
}
dev.simplenotes.be {

View File

@ -19,8 +19,10 @@ services:
volumes:
- notes-db-volume:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
timeout: 10s
test: "mysql --protocol=tcp -u simplenotes -p$MYSQL_PASSWORD -e 'show databases'"
interval: 5s
timeout: 1s
start_period: 2s
retries: 10
simplenotes:
@ -39,6 +41,12 @@ services:
# - PASSWORD
ports:
- 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:
db:
condition: service_healthy

View 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
}
}

View File

@ -1,18 +1,24 @@
package be.simplenotes.persistance
import be.simplenotes.persistance.utils.DbType
import be.simplenotes.persistance.utils.type
import be.simplenotes.shared.config.DataSourceConfig
import org.flywaydb.core.Flyway
import javax.sql.DataSource
interface DbMigrations {
fun migrate()
}
internal class DbMigrationsImpl(
private val dataSource: DataSource,
private val dataSourceConfig: DataSourceConfig
private val dataSourceConfig: DataSourceConfig,
) : DbMigrations {
override fun migrate() {
val migrationDir = when {
dataSourceConfig.jdbcUrl.contains("mariadb") -> "db/migration/mariadb"
else -> "db/migration/other"
val migrationDir = when (dataSourceConfig.type()) {
DbType.H2 -> "db/migration/other"
DbType.MariaDb -> "db/migration/mariadb"
}
Flyway.configure()

View File

@ -13,10 +13,6 @@ import org.koin.dsl.module
import org.koin.dsl.onClose
import javax.sql.DataSource
interface DbMigrations {
fun migrate()
}
private fun hikariDataSource(conf: DataSourceConfig): HikariDataSource {
val hikariConfig = HikariConfig().also {
it.jdbcUrl = conf.jdbcUrl
@ -41,4 +37,5 @@ val persistanceModule = module {
get<DbMigrations>().migrate()
Database.connect(get<DataSource>())
}
single<DbHealthCheck> { DbHealthCheckImpl(get(), get()) }
}

View File

@ -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