From d97047b354274797d8c41371722b8c53a68dfcef Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Mon, 20 Apr 2020 15:48:18 +0200 Subject: [PATCH 01/34] fix naming in the pipelines --- .github/workflows/cd.yml | 10 +++++----- .github/workflows/release.yml | 20 ++++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index bc94506..a87f6fc 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set envs + - name: Set Release Version # use latest tag as release version run: echo ::set-env name=RELEASE_VERSION::${GITHUB_SHA} @@ -55,15 +55,15 @@ jobs: token: ${{ secrets.RUBICON_GIT_TOKEN }} # Setup gcloud CLI - - uses: GoogleCloudPlatform/github-actions/setup-gcloud@master + - name: Setup Google Cloud CLI + uses: GoogleCloudPlatform/github-actions/setup-gcloud@master with: version: '286.0.0' service_account_email: ${{ secrets.GKE_SA_EMAIL }} service_account_key: ${{ secrets.GKE_SA_KEY }} project_id: ${{ secrets.GKE_PROJECT }} - # Configure Docker to use the gcloud command-line tool as a credential - # helper for authentication + # Configure Docker to use the gcloud command-line tool - name: Configure Docker Google cloud run: |- gcloud --quiet auth configure-docker @@ -77,7 +77,7 @@ jobs: gcloud container clusters get-credentials "$GKE_CLUSTER" --zone "$GKE_ZONE" # K8s is set up, deploy the app - - name: Deploy + - name: Deploy the Service run: |- kubectl delete pod -l name=poll -n staging kubectl describe pod -l name=poll -n staging diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b4d998f..cefcc62 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,8 +8,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - name: Set envs + - uses: actions/checkout@v2 + + - name: Set Release Version # use latest tag as release version run: echo ::set-env name=RELEASE_VERSION::${GITHUB_REF:10} @@ -41,7 +42,7 @@ jobs: token: ${{ secrets.RUBICON_GIT_TOKEN }} # Update version to the one that was just built - - name: Change version + - name: Change Version in Rubicon env: RELEASE_VERSION: ${{ env.RELEASE_VERSION }} run: |- @@ -50,7 +51,8 @@ jobs: rm poll.yaml.bak # Setup gcloud CLI - - uses: GoogleCloudPlatform/github-actions/setup-gcloud@master + - name: Setup Google Cloud CLI + uses: GoogleCloudPlatform/github-actions/setup-gcloud@master with: version: '286.0.0' service_account_email: ${{ secrets.GKE_SA_EMAIL }} @@ -71,12 +73,12 @@ jobs: gcloud container clusters get-credentials "$GKE_CLUSTER" --zone "$GKE_ZONE" # K8s is set up, deploy the app - - name: Deploy + - name: Deploy the Service run: |- kubectl apply -f rubicon/prod/services/poll/poll.yaml # Commit all data to Rubicon and open PR - - name: Create Pull Request + - name: Create Rubicon Pull Request uses: peter-evans/create-pull-request@v2 env: RELEASE_VERSION: ${{ env.RELEASE_VERSION }} @@ -85,8 +87,10 @@ jobs: branch: poll-bot-release token: ${{ secrets.RUBICON_GIT_TOKEN }} title: "Poll Bot Release" - commit-message: "Poll Bot version bump" - body: "Poll Bot Release" + commit-message: "Poll bot version bump" + body: | + This is automatic version bump. + Made by [Poll Bot pipeline](https://github.com/wireapp/poll-bot/blob/master/.github/workflows/release.yml). # Send webhook to Wire using Slack Bot - name: Webhook to Wire From 653fe7adf1fbca4eb0dc231dcd6ea96e63cda958 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Mon, 20 Apr 2020 15:50:06 +0200 Subject: [PATCH 02/34] naming --- .github/workflows/cd.yml | 1 + .github/workflows/release.yml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index a87f6fc..f0bd776 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -7,6 +7,7 @@ on: jobs: publish: + name: Build and publish docker image runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cefcc62..bd149e5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,7 +5,8 @@ on: types: published jobs: - build: + deploy: + name: Build and deploy service runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 From 67a25f740e42d370fec3a981908512d0b6490395 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Mon, 20 Apr 2020 15:58:20 +0200 Subject: [PATCH 03/34] pipelines badges --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eb31987..fabc808 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Wire Poll Bot [![GitHub version](https://badge.fury.io/gh/wireapp%2Fpoll-bot.svg)](https://badge.fury.io/gh/wireapp%2Fpoll-bot) -![CI/CD](https://github.com/wireapp/poll-bot/workflows/CI/CD/badge.svg) +![CI](https://github.com/wireapp/poll-bot/workflows/CI/badge.svg) +![CD](https://github.com/wireapp/poll-bot/workflows/CD/badge.svg) ![Release Pipeline](https://github.com/wireapp/poll-bot/workflows/Release%20Pipeline/badge.svg) [Wire](https://wire.com/) bot for the polls. From e228fc450799098f7ed22c4d092543d99ddc782a Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 11:32:47 +0200 Subject: [PATCH 04/34] remove obsolete sockets --- .../wire/bots/polls/routing/HealthRoute.kt | 31 ---------- .../wire/bots/polls/utils/JsonExtensions.kt | 57 ------------------ .../bots/polls/websockets/PollWebSocket.kt | 21 ------- .../bots/polls/websockets/WebSocketBase.kt | 48 --------------- .../bots/polls/websockets/WebSocketConfig.kt | 19 ------ .../polls/websockets/WebSocketJsonReceiver.kt | 59 ------------------- .../websockets/WebsocketRegistrations.kt | 20 ------- 7 files changed, 255 deletions(-) delete mode 100644 src/main/kotlin/com/wire/bots/polls/routing/HealthRoute.kt delete mode 100644 src/main/kotlin/com/wire/bots/polls/utils/JsonExtensions.kt delete mode 100644 src/main/kotlin/com/wire/bots/polls/websockets/PollWebSocket.kt delete mode 100644 src/main/kotlin/com/wire/bots/polls/websockets/WebSocketBase.kt delete mode 100644 src/main/kotlin/com/wire/bots/polls/websockets/WebSocketConfig.kt delete mode 100644 src/main/kotlin/com/wire/bots/polls/websockets/WebSocketJsonReceiver.kt delete mode 100644 src/main/kotlin/com/wire/bots/polls/websockets/WebsocketRegistrations.kt diff --git a/src/main/kotlin/com/wire/bots/polls/routing/HealthRoute.kt b/src/main/kotlin/com/wire/bots/polls/routing/HealthRoute.kt deleted file mode 100644 index b96da0a..0000000 --- a/src/main/kotlin/com/wire/bots/polls/routing/HealthRoute.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.wire.bots.polls.routing - -import com.wire.bots.polls.dao.DatabaseSetup -import io.ktor.application.call -import io.ktor.http.HttpStatusCode -import io.ktor.response.respond -import io.ktor.routing.Routing -import io.ktor.routing.get - -/** - * Health indication endpoints. - */ -fun Routing.healthStatus() { - /** - * Responds only 200 for ingres. - */ - get("/status") { - call.respond(HttpStatusCode.OK) - } - - /** - * More complex API for indication of all resources. - */ - get("/status/health") { - if (DatabaseSetup.isConnected()) { - call.respond("healthy") - } else { - call.respond(HttpStatusCode.ServiceUnavailable, "DB connection is not working") - } - } -} diff --git a/src/main/kotlin/com/wire/bots/polls/utils/JsonExtensions.kt b/src/main/kotlin/com/wire/bots/polls/utils/JsonExtensions.kt deleted file mode 100644 index 5480284..0000000 --- a/src/main/kotlin/com/wire/bots/polls/utils/JsonExtensions.kt +++ /dev/null @@ -1,57 +0,0 @@ -@file:Suppress("unused") // might be handy in the future - -package com.wire.bots.polls.utils - -import ai.blindspot.ktoolz.extensions.newLine -import com.fasterxml.jackson.databind.DeserializationFeature -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import mu.KLogging - -/** - * Logger for this file. - */ -@PublishedApi -internal val jsonLogger = KLogging().logger("JsonExtensions") - -/** - * Standard [ObjectMapper] configured in a way the platform operates. - */ -fun jacksonMapper(): ObjectMapper = jacksonObjectMapper().apply { - configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false) - configure(DeserializationFeature.ACCEPT_FLOAT_AS_INT, false) -} - -/** - * Tries to create instance of T from provided [json], null is returned when it is not possible to parse it. - */ -inline fun parseJson(json: String): T? = - runCatching { jacksonMapper().readValue(json) } - .onFailure { jsonLogger.warn(it) { "Exception raised during JSON parsing:$newLine$json" } } - .getOrNull() - -/** - * Tries to create instance of T from provided [json], null is returned when it is not possible to parse it. - */ -inline fun parseJson(json: ByteArray): T? = - runCatching { jacksonMapper().readValue(json) } - .onFailure { jsonLogger.warn(it) { "Exception raised during JSON parsing:$newLine$json" } } - .getOrNull() - -/** - * Serializes given object to string. - */ -fun createJson(value: T): String = jacksonMapper().writeValueAsString(value) - -/** - * Serializes given object to byte array. - */ -fun createJsonBytes(value: T): ByteArray = jacksonMapper().writeValueAsBytes(value) - -/** - * Pretty print a json. - */ -fun prettyPrintJson(json: String): String = with(jacksonMapper()) { - writerWithDefaultPrettyPrinter().writeValueAsString(readValue(json)) -} diff --git a/src/main/kotlin/com/wire/bots/polls/websockets/PollWebSocket.kt b/src/main/kotlin/com/wire/bots/polls/websockets/PollWebSocket.kt deleted file mode 100644 index 0dac846..0000000 --- a/src/main/kotlin/com/wire/bots/polls/websockets/PollWebSocket.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.wire.bots.polls.websockets - -import com.wire.bots.polls.dto.roman.Message -import com.wire.bots.polls.services.MessagesHandlingService -import io.ktor.client.HttpClient -import mu.KLogging - -/** - * Class which contains logic for receiving JSON formatted request from the given [WebSocketConfig]. - */ -class PollWebSocket(private val client: HttpClient, private val config: WebSocketConfig, private val handler: MessagesHandlingService) { - - private companion object : KLogging() - - /** - * Starts listening on the web socket. - */ - suspend fun run() = client.createJsonWebSocketReceiver(config) { _, message -> - handler.handle(message) - }.subscribe() -} diff --git a/src/main/kotlin/com/wire/bots/polls/websockets/WebSocketBase.kt b/src/main/kotlin/com/wire/bots/polls/websockets/WebSocketBase.kt deleted file mode 100644 index 6b493ec..0000000 --- a/src/main/kotlin/com/wire/bots/polls/websockets/WebSocketBase.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.wire.bots.polls.websockets - -import io.ktor.client.HttpClient -import io.ktor.client.features.websocket.DefaultClientWebSocketSession -import io.ktor.client.features.websocket.ws -import io.ktor.http.DEFAULT_PORT -import io.ktor.http.cio.websocket.Frame -import mu.KLogging - -/** - * Base class for web socket connections. - */ -abstract class WebSocketBase(private val client: HttpClient, private val config: WebSocketConfig) { - - private companion object : KLogging() - - /** - * Opens web socket and starts receiving connections. [keepAlive] determines whether the app should try to reconnect when the - * connection is closed. - */ - tailrec suspend fun subscribe(keepAlive: Boolean = true) { - // TODO websocket reconnect - use better solution than dummy while true - runCatching { - client.ws( - host = config.host, - port = config.port ?: DEFAULT_PORT, - path = config.path - ) { - for (frame in incoming) { - logger.debug { "WS frame received." } - runCatching { onFrameReceived(frame) } - .onFailure { logger.error(it) { "Exception occurred during handling onFrameReceived." } } - - } - logger.info { "Closing the socket" } - } - }.onFailure { - logger.error(it) { "Exception occurred while receiving web sockets. Keep Alive - $keepAlive" } - } - // use tail recursion if the bot should keep the connection opened - if (keepAlive) subscribe(keepAlive) - } - - /** - * Method called when frame is received. - */ - abstract suspend fun DefaultClientWebSocketSession.onFrameReceived(frame: Frame) -} diff --git a/src/main/kotlin/com/wire/bots/polls/websockets/WebSocketConfig.kt b/src/main/kotlin/com/wire/bots/polls/websockets/WebSocketConfig.kt deleted file mode 100644 index 255eb49..0000000 --- a/src/main/kotlin/com/wire/bots/polls/websockets/WebSocketConfig.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.wire.bots.polls.websockets - -/** - * Configuration for [PollWebSocket] class. - */ -data class WebSocketConfig( - /** - * Host address - ie. proxy.services.zinfra.io - */ - val host: String, - /** - * Path to socket - ie. /await/ws - */ - val path: String, - /** - * Port, if null, default is used. - */ - val port: Int? = null -) diff --git a/src/main/kotlin/com/wire/bots/polls/websockets/WebSocketJsonReceiver.kt b/src/main/kotlin/com/wire/bots/polls/websockets/WebSocketJsonReceiver.kt deleted file mode 100644 index f841040..0000000 --- a/src/main/kotlin/com/wire/bots/polls/websockets/WebSocketJsonReceiver.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.wire.bots.polls.websockets - -import ai.blindspot.ktoolz.extensions.whenNull -import com.wire.bots.polls.utils.jacksonMapper -import io.ktor.client.HttpClient -import io.ktor.client.features.websocket.DefaultClientWebSocketSession -import io.ktor.http.cio.websocket.Frame -import io.ktor.http.cio.websocket.readText -import mu.KLogging -import kotlin.reflect.KClass - -/** - * Class which uses JSON parser for [Frame.Text]. - */ -open class WebSocketJsonReceiver( - client: HttpClient, - config: WebSocketConfig, - private val clazz: KClass, - private val onJsonReceived: (suspend (DefaultClientWebSocketSession, T) -> Unit)? -) : WebSocketBase(client, config) { - - private companion object : KLogging() - - /** - * Default implementation of on received which parses the JSON from [frame]. - * - * Note that there is no error handling and parse exceptions will be propagated. - */ - override suspend fun DefaultClientWebSocketSession.onFrameReceived(frame: Frame) { - when (frame) { - is Frame.Text -> { - logger.debug { "Text frame received." } - val text = frame.readText() - // TODO remove this when going to prod as it prints users data to the log - logger.info { "Received text:\n$text" } - - @Suppress("BlockingMethodInNonBlockingContext") // because sadly jackson does not have async read - jacksonMapper().readValue(text, clazz.java) - .whenNull { logger.error { "It was not possible to parse incoming message!" } } - ?.let { onJsonReceived(it) } - } - else -> logger.debug { "Received non-text frame, not processing it." } - } - } - - /** - * Default implementation for the handling of received JSONs. [onJsonReceived] is used when provided. - */ - open suspend fun DefaultClientWebSocketSession.onJsonReceived(payload: T) = - onJsonReceived?.invoke(this, payload) ?: logger.warn { "No action specified, skipping." } -} - -/** - * Create instance of [WebSocketJsonReceiver] with [onJsonReceived] for handling the JSONs. - */ -inline fun HttpClient.createJsonWebSocketReceiver( - config: WebSocketConfig, - noinline onJsonReceived: suspend (DefaultClientWebSocketSession, T) -> Unit -) = WebSocketJsonReceiver(this, config, T::class, onJsonReceived) diff --git a/src/main/kotlin/com/wire/bots/polls/websockets/WebsocketRegistrations.kt b/src/main/kotlin/com/wire/bots/polls/websockets/WebsocketRegistrations.kt deleted file mode 100644 index 7a9042d..0000000 --- a/src/main/kotlin/com/wire/bots/polls/websockets/WebsocketRegistrations.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.wire.bots.polls.websockets - -import io.ktor.application.Application -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.kodein.di.generic.instance -import org.kodein.di.ktor.kodein - -/** - * Start listening the preconfigured web sockets. - */ -fun Application.subscribeToWebSockets() { - val k by kodein() - - GlobalScope.launch(Dispatchers.IO) { - val pollWebSocket by k.instance() - pollWebSocket.run() - } -} From b57197edeaa10af0951525070b2f2e3c8cbf1c44 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 11:33:11 +0200 Subject: [PATCH 05/34] introduce prometheus --- .github/workflows/ci.yml | 6 +- Dockerfile | 4 +- build.gradle.kts | 15 ++++- .../kotlin/com/wire/bots/polls/PollBot.kt | 2 - .../wire/bots/polls/routing/MessagesRoute.kt | 25 ++++--- .../com/wire/bots/polls/routing/Routing.kt | 24 ++----- .../wire/bots/polls/routing/ServiceRoutes.kt | 62 ++++++++++++++++++ .../bots/polls/services/ProxySenderService.kt | 5 +- .../setup/ConfigurationDependencyInjection.kt | 27 +------- .../bots/polls/setup/DependencyInjection.kt | 44 ++++--------- .../bots/polls/setup/EnvConfigVariables.kt | 20 ------ .../bots/polls/setup/ExceptionHandling.kt | 35 ++++++++++ .../com/wire/bots/polls/setup/HttpDebug.kt | 50 ++++++++++++-- .../com/wire/bots/polls/setup/KodeinSetup.kt | 2 - .../wire/bots/polls/setup/KtorInstallation.kt | 65 +++++++++---------- .../com/wire/bots/polls/utils/Extensions.kt | 13 ++++ .../bots/polls/utils/PrometheusExtensions.kt | 49 ++++++++++++++ 17 files changed, 286 insertions(+), 162 deletions(-) create mode 100644 src/main/kotlin/com/wire/bots/polls/routing/ServiceRoutes.kt create mode 100644 src/main/kotlin/com/wire/bots/polls/setup/ExceptionHandling.kt create mode 100644 src/main/kotlin/com/wire/bots/polls/utils/Extensions.kt create mode 100644 src/main/kotlin/com/wire/bots/polls/utils/PrometheusExtensions.kt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 133843e..3ac268c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,10 @@ name: CI -on: [pull_request] +on: + push: + branches-ignore: + - master + pull_request: jobs: check: diff --git a/Dockerfile b/Dockerfile index e2a5b47..5417e6d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,11 +27,11 @@ ENV APP_ROOT /app WORKDIR $APP_ROOT # Obtain built from the base -COPY --from=build /src/build/distributions/polls*.tar $APP_ROOT/ +COPY --from=build /src/build/distributions/app.tar $APP_ROOT/ # Extract executables RUN mkdir $APP_ROOT/run -RUN tar -xvf polls*.tar --strip-components=1 -C $APP_ROOT/run +RUN tar -xvf app.tar --strip-components=1 -C $APP_ROOT/run # create version file ARG release_version=development diff --git a/build.gradle.kts b/build.gradle.kts index 4c801b9..7779edc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,10 +34,13 @@ dependencies { // Ktor client dependencies implementation("io.ktor", "ktor-client-json", ktorVersion) implementation("io.ktor", "ktor-client-jackson", ktorVersion) - implementation("io.ktor", "ktor-client-websockets", ktorVersion) - implementation("io.ktor", "ktor-client-cio", ktorVersion) + implementation("io.ktor", "ktor-client-apache", ktorVersion) implementation("io.ktor", "ktor-client-logging-jvm", ktorVersion) + // Prometheus metrics + implementation("io.ktor", "ktor-metrics-micrometer", ktorVersion) + implementation("io.micrometer", "micrometer-registry-prometheus", "1.4.1") + // logging implementation("io.github.microutils", "kotlin-logging", "1.7.9") implementation("ch.qos.logback", "logback-classic", "1.2.3") @@ -69,6 +72,14 @@ tasks { kotlinOptions.jvmTarget = "1.8" } + distTar { + archiveFileName.set("app.tar") + } + + withType { + useJUnitPlatform() + } + register("fatJar") { manifest { attributes["Main-Class"] = mainClass diff --git a/src/main/kotlin/com/wire/bots/polls/PollBot.kt b/src/main/kotlin/com/wire/bots/polls/PollBot.kt index f0ac374..c982309 100644 --- a/src/main/kotlin/com/wire/bots/polls/PollBot.kt +++ b/src/main/kotlin/com/wire/bots/polls/PollBot.kt @@ -4,9 +4,7 @@ import com.wire.bots.polls.setup.init import io.ktor.application.Application import io.ktor.server.engine.embeddedServer import io.ktor.server.netty.Netty -import io.ktor.util.KtorExperimentalAPI -@KtorExperimentalAPI fun main() { embeddedServer(Netty, 8080, module = Application::init).start() } diff --git a/src/main/kotlin/com/wire/bots/polls/routing/MessagesRoute.kt b/src/main/kotlin/com/wire/bots/polls/routing/MessagesRoute.kt index 258a83b..6d5d61c 100644 --- a/src/main/kotlin/com/wire/bots/polls/routing/MessagesRoute.kt +++ b/src/main/kotlin/com/wire/bots/polls/routing/MessagesRoute.kt @@ -9,39 +9,38 @@ import io.ktor.request.receive import io.ktor.response.respond import io.ktor.routing.Routing import io.ktor.routing.post -import mu.KLogger +import org.kodein.di.LazyKodein import org.kodein.di.generic.instance -import org.kodein.di.ktor.kodein /** * Messages API. */ -fun Routing.messages() { - val k by kodein() - - val logger by k.instance("routing-logger") +fun Routing.messages(k: LazyKodein) { val handler by k.instance() val authService by k.instance() + /** + * API for receiving messages from Roman. + */ post("/messages") { - logger.debug { "POST /messages" } + routingLogger.debug { "POST /messages" } // verify whether request contain correct auth header if (authService.isTokenValid { call.request.headers }) { - logger.info { "Token is valid." } + routingLogger.debug { "Token is valid." } // bot responds either with 200 or with 400 runCatching { - logger.info { "Parsing an message." } + routingLogger.debug { "Parsing an message." } val message = call.receive() - logger.info { "Message parsed." } + routingLogger.debug { "Message parsed." } handler.handle(message) - logger.info { "Responding OK" } + routingLogger.debug { "Responding OK" } call.respond(HttpStatusCode.OK) }.onFailure { - logger.error(it) { "Exception occurred during the request handling!" } + routingLogger.error(it) { "Exception occurred during the request handling!" } call.respond(HttpStatusCode.BadRequest, "Bot did not understand the message.") } } else { - logger.info { "Token is invalid." } + routingLogger.warn { "Token is invalid." } call.respond(HttpStatusCode.Unauthorized, "Please provide Authorization header.") } } diff --git a/src/main/kotlin/com/wire/bots/polls/routing/Routing.kt b/src/main/kotlin/com/wire/bots/polls/routing/Routing.kt index 74fe6f9..56e7ed7 100644 --- a/src/main/kotlin/com/wire/bots/polls/routing/Routing.kt +++ b/src/main/kotlin/com/wire/bots/polls/routing/Routing.kt @@ -1,31 +1,17 @@ package com.wire.bots.polls.routing -import ai.blindspot.ktoolz.extensions.newLine -import io.ktor.application.call -import io.ktor.content.TextContent -import io.ktor.http.ContentType -import io.ktor.response.respond +import com.wire.bots.polls.utils.createLogger import io.ktor.routing.Routing -import io.ktor.routing.get -import org.kodein.di.generic.instance import org.kodein.di.ktor.kodein +internal val routingLogger by lazy { createLogger("RoutingLogger") } + /** * Register routes to the KTor. */ fun Routing.registerRoutes() { - val k by kodein() - val version by k.instance("version") - - get("/") { - call.respond("This is the Wire Poll Bot running version \"$version\".") - } - - get("/version") { - call.respond(TextContent("{\"version\": \"$version\"}$newLine", ContentType.Application.Json)) - } - healthStatus() - messages() + serviceRoutes(k) + messages(k) } diff --git a/src/main/kotlin/com/wire/bots/polls/routing/ServiceRoutes.kt b/src/main/kotlin/com/wire/bots/polls/routing/ServiceRoutes.kt new file mode 100644 index 0000000..518c84a --- /dev/null +++ b/src/main/kotlin/com/wire/bots/polls/routing/ServiceRoutes.kt @@ -0,0 +1,62 @@ +package com.wire.bots.polls.routing + +import com.wire.bots.polls.dao.DatabaseSetup +import io.ktor.application.call +import io.ktor.http.HttpStatusCode +import io.ktor.response.respond +import io.ktor.response.respondTextWriter +import io.ktor.routing.Routing +import io.ktor.routing.get +import io.micrometer.prometheus.PrometheusMeterRegistry +import org.kodein.di.LazyKodein +import org.kodein.di.generic.instance + +/** + * Registers prometheus data. + */ +fun Routing.serviceRoutes(k: LazyKodein) { + val version by k.instance("version") + val registry by k.instance() + + /** + * Information about service. + */ + get("/") { + call.respond("Server running version: \"$version\".") + } + + /** + * Send data about version. + */ + get("/version") { + call.respond(mapOf("version" to version)) + } + + /** + * Responds only 200 for ingres. + */ + get("/status") { + call.respond(HttpStatusCode.OK) + } + + /** + * More complex API for indication of all resources. + */ + get("/status/health") { + if (DatabaseSetup.isConnected()) { + call.respond(mapOf("health" to "healthy")) + } else { + call.respond(HttpStatusCode.ServiceUnavailable, "DB connection is not working") + } + } + + /** + * Prometheus endpoint. + */ + get("/prometheus") { + call.respondTextWriter(status = HttpStatusCode.OK) { + @Suppress("BlockingMethodInNonBlockingContext") // sadly this is synchronous API + registry.scrape(this) + } + } +} diff --git a/src/main/kotlin/com/wire/bots/polls/services/ProxySenderService.kt b/src/main/kotlin/com/wire/bots/polls/services/ProxySenderService.kt index c90d222..746f0b5 100644 --- a/src/main/kotlin/com/wire/bots/polls/services/ProxySenderService.kt +++ b/src/main/kotlin/com/wire/bots/polls/services/ProxySenderService.kt @@ -1,8 +1,9 @@ package com.wire.bots.polls.services +import ai.blindspot.ktoolz.extensions.createJson import com.wire.bots.polls.dto.bot.BotMessage import com.wire.bots.polls.dto.roman.Response -import com.wire.bots.polls.utils.createJson +import com.wire.bots.polls.utils.appendPath import io.ktor.client.HttpClient import io.ktor.client.call.receive import io.ktor.client.request.header @@ -25,7 +26,7 @@ class ProxySenderService(private val client: HttpClient, config: ProxyConfigurat const val conversationPath = "/conversation" } - private val conversationEndpoint = config.baseUrl + conversationPath + private val conversationEndpoint = config.baseUrl appendPath conversationPath /** * Send given message with provided token. diff --git a/src/main/kotlin/com/wire/bots/polls/setup/ConfigurationDependencyInjection.kt b/src/main/kotlin/com/wire/bots/polls/setup/ConfigurationDependencyInjection.kt index 8de4bd0..a564441 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/ConfigurationDependencyInjection.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/ConfigurationDependencyInjection.kt @@ -4,24 +4,18 @@ import ai.blindspot.ktoolz.extensions.getEnv import ai.blindspot.ktoolz.extensions.whenNull import com.wire.bots.polls.dto.conf.DatabaseConfiguration import com.wire.bots.polls.services.ProxyConfiguration -import com.wire.bots.polls.setup.EnvConfigVariables.APP_KEY import com.wire.bots.polls.setup.EnvConfigVariables.DB_PASSWORD import com.wire.bots.polls.setup.EnvConfigVariables.DB_URL import com.wire.bots.polls.setup.EnvConfigVariables.DB_USER import com.wire.bots.polls.setup.EnvConfigVariables.PROXY_DOMAIN -import com.wire.bots.polls.setup.EnvConfigVariables.PROXY_WS_HOST -import com.wire.bots.polls.setup.EnvConfigVariables.PROXY_WS_PATH import com.wire.bots.polls.setup.EnvConfigVariables.SERVICE_TOKEN -import com.wire.bots.polls.setup.EnvConfigVariables.USE_WEB_SOCKETS -import com.wire.bots.polls.websockets.WebSocketConfig -import mu.KLogging +import com.wire.bots.polls.utils.createLogger import org.kodein.di.Kodein.MainBuilder import org.kodein.di.generic.bind -import org.kodein.di.generic.instance import org.kodein.di.generic.singleton import java.io.File -private val logger = KLogging().logger("EnvironmentLoaderLogger") +private val logger = createLogger("EnvironmentLoaderLogger") private fun getEnvOrLogDefault(env: String, defaultValue: String) = getEnv(env).whenNull { logger.warn { "Env variable $env not set! Using default value - $defaultValue" } @@ -55,27 +49,10 @@ fun MainBuilder.bindConfiguration() { getEnvOrLogDefault(SERVICE_TOKEN, "local-token") } - bind("app-key-websocket") with singleton { - getEnvOrLogDefault(APP_KEY, "") - } - bind("version") with singleton { loadVersion("development") } - bind("use-websocket") with singleton { - getEnvOrLogDefault(USE_WEB_SOCKETS, "false").toBoolean() - } - - bind() with singleton { - val appKey = instance("app-key-websocket") - - val host = getEnvOrLogDefault(PROXY_WS_HOST, "proxy.services.zinfra.io") - val path = getEnvOrLogDefault(PROXY_WS_PATH, "/await") - - WebSocketConfig(host = host, path = "$path/$appKey") - } - bind() with singleton { ProxyConfiguration(getEnvOrLogDefault(PROXY_DOMAIN, "http://proxy.services.zinfra.io")) } diff --git a/src/main/kotlin/com/wire/bots/polls/setup/DependencyInjection.kt b/src/main/kotlin/com/wire/bots/polls/setup/DependencyInjection.kt index b240c31..766a8eb 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/DependencyInjection.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/DependencyInjection.kt @@ -11,16 +11,9 @@ import com.wire.bots.polls.services.PollService import com.wire.bots.polls.services.ProxySenderService import com.wire.bots.polls.services.StatsFormattingService import com.wire.bots.polls.services.UserCommunicationService -import com.wire.bots.polls.websockets.PollWebSocket import io.ktor.client.HttpClient -import io.ktor.client.engine.cio.CIO -import io.ktor.client.features.json.JacksonSerializer -import io.ktor.client.features.json.JsonFeature -import io.ktor.client.features.logging.LogLevel -import io.ktor.client.features.logging.Logger -import io.ktor.client.features.logging.Logging -import io.ktor.client.features.websocket.WebSockets -import io.ktor.util.KtorExperimentalAPI +import io.micrometer.prometheus.PrometheusConfig +import io.micrometer.prometheus.PrometheusMeterRegistry import mu.KLogger import mu.KLogging import org.kodein.di.Kodein.MainBuilder @@ -28,34 +21,11 @@ import org.kodein.di.generic.bind import org.kodein.di.generic.instance import org.kodein.di.generic.singleton -@KtorExperimentalAPI fun MainBuilder.configureContainer() { bind() with singleton { PollValidation() } - bind() with singleton { - HttpClient(CIO) { - install(WebSockets) - - install(JsonFeature) { - serializer = JacksonSerializer() - } - - install(Logging) { - this.level - logger = Logger.DEBUG - level = LogLevel.ALL - } - } - } - - bind() with singleton { - PollWebSocket( - client = instance(), - config = instance(), - handler = instance() - ) - } + bind() with singleton { createHttpClient(instance()) } bind() with singleton { ProxySenderService( @@ -64,6 +34,14 @@ fun MainBuilder.configureContainer() { ) } + bind() with singleton { + PrometheusMeterRegistry(PrometheusConfig.DEFAULT).apply { + with(this.config()) { + commonTags("application", "poll-bot") + } + } + } + bind() with singleton { InputParser() } bind() with singleton { PollFactory(instance(), instance()) } diff --git a/src/main/kotlin/com/wire/bots/polls/setup/EnvConfigVariables.kt b/src/main/kotlin/com/wire/bots/polls/setup/EnvConfigVariables.kt index 5a8ee7e..f9bee7d 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/EnvConfigVariables.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/EnvConfigVariables.kt @@ -27,26 +27,6 @@ object EnvConfigVariables { */ const val SERVICE_TOKEN = "SERVICE_TOKEN" - /** - * Key for connecting to the web socket of the proxy. - */ - const val APP_KEY = "APP_KEY" - - /** - * Determines whether to use web sockets for connection to proxy or not eg. true - */ - const val USE_WEB_SOCKETS = "USE_WEB_SOCKETS" - - /** - * Host name for the connection to web socket eg."proxy.services.zinfra.io" - */ - const val PROXY_WS_HOST = "PROXY_WS_HOST" - - /** - * Path to web socket at proxy eg. "/await" - */ - const val PROXY_WS_PATH = "PROXY_WS_PATH" - /** * Domain used for sending the messages from the bot to proxy eg. "https://proxy.services.zinfra.io" */ diff --git a/src/main/kotlin/com/wire/bots/polls/setup/ExceptionHandling.kt b/src/main/kotlin/com/wire/bots/polls/setup/ExceptionHandling.kt new file mode 100644 index 0000000..bebd6ec --- /dev/null +++ b/src/main/kotlin/com/wire/bots/polls/setup/ExceptionHandling.kt @@ -0,0 +1,35 @@ +package com.wire.bots.polls.setup + +import com.wire.bots.polls.utils.countException +import com.wire.bots.polls.utils.createLogger +import io.ktor.application.Application +import io.ktor.application.ApplicationCall +import io.ktor.application.call +import io.ktor.application.install +import io.ktor.features.StatusPages +import io.ktor.http.HttpStatusCode +import io.ktor.response.respond +import io.micrometer.prometheus.PrometheusMeterRegistry +import org.kodein.di.LazyKodein +import org.kodein.di.generic.instance + +private val logger = createLogger("ExceptionHandler") + +/** + * Registers exception handling. + */ +fun Application.registerExceptionHandlers(k: LazyKodein) { + val registry by k.instance() + + install(StatusPages) { + exception { cause -> + logger.error(cause) { "Exception occurred in the application: ${cause.message}" } + call.errorResponse(HttpStatusCode.InternalServerError, cause.message) + registry.countException(cause) + } + } +} + +suspend inline fun ApplicationCall.errorResponse(statusCode: HttpStatusCode, message: String?) { + respond(status = statusCode, message = mapOf("message" to (message ?: "No details specified"))) +} diff --git a/src/main/kotlin/com/wire/bots/polls/setup/HttpDebug.kt b/src/main/kotlin/com/wire/bots/polls/setup/HttpDebug.kt index ad99ca5..9f22376 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/HttpDebug.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/HttpDebug.kt @@ -1,17 +1,55 @@ package com.wire.bots.polls.setup +import com.wire.bots.polls.utils.createLogger +import com.wire.bots.polls.utils.httpCall import io.ktor.client.HttpClient +import io.ktor.client.engine.apache.Apache +import io.ktor.client.features.json.JacksonSerializer +import io.ktor.client.features.json.JsonFeature +import io.ktor.client.features.logging.LogLevel import io.ktor.client.features.logging.Logger -import org.slf4j.LoggerFactory +import io.ktor.client.features.logging.Logging +import io.ktor.client.features.observer.ResponseObserver +import io.micrometer.core.instrument.MeterRegistry /** - * Debug logger for HTTP requests. + * Prepares HTTP Client with given keystore. */ -val Logger.Companion.DEBUG: Logger - get() = object : Logger { - private val delegate = LoggerFactory.getLogger(HttpClient::class.java)!! +fun createHttpClient(meterRegistry: MeterRegistry) = + HttpClient(Apache) { + install(JsonFeature) { + serializer = JacksonSerializer() + } + + install(ResponseObserver) { + onResponse { + meterRegistry.httpCall(it) + } + } + + install(Logging) { + logger = Logger.TRACE + level = LogLevel.ALL + } + } + +/** + * Debug logger for HTTP Requests. + */ +private val Logger.Companion.DEBUG: Logger + get() = object : Logger, org.slf4j.Logger by createLogger("HttpCallsLogging") { override fun log(message: String) { - delegate.debug(message) + debug(message) } } + +/** + * Trace logger for HTTP Requests. + */ +private val Logger.Companion.TRACE: Logger + get() = object : Logger, org.slf4j.Logger by createLogger("HttpCallsLogging") { + override fun log(message: String) { + trace(message) + } + } diff --git a/src/main/kotlin/com/wire/bots/polls/setup/KodeinSetup.kt b/src/main/kotlin/com/wire/bots/polls/setup/KodeinSetup.kt index a98b7d8..4cad3fa 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/KodeinSetup.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/KodeinSetup.kt @@ -1,13 +1,11 @@ package com.wire.bots.polls.setup import io.ktor.application.Application -import io.ktor.util.KtorExperimentalAPI import org.kodein.di.ktor.kodein /** * Inits and sets up DI container. */ -@KtorExperimentalAPI fun Application.setupKodein() { kodein { bindConfiguration() diff --git a/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt b/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt index 95470d5..29cbe05 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt @@ -4,91 +4,83 @@ import com.fasterxml.jackson.databind.SerializationFeature import com.wire.bots.polls.dao.DatabaseSetup import com.wire.bots.polls.dto.conf.DatabaseConfiguration import com.wire.bots.polls.routing.registerRoutes -import com.wire.bots.polls.websockets.subscribeToWebSockets +import com.wire.bots.polls.utils.createLogger import io.ktor.application.Application import io.ktor.application.install import io.ktor.features.CallLogging import io.ktor.features.ContentNegotiation import io.ktor.features.DefaultHeaders -import io.ktor.http.cio.websocket.pingPeriod -import io.ktor.http.cio.websocket.timeout import io.ktor.jackson.jackson +import io.ktor.metrics.micrometer.MicrometerMetrics import io.ktor.routing.routing -import io.ktor.util.KtorExperimentalAPI -import io.ktor.websocket.WebSockets -import mu.KLogger +import io.micrometer.core.instrument.distribution.DistributionStatisticConfig +import io.micrometer.prometheus.PrometheusMeterRegistry import org.flywaydb.core.Flyway import org.kodein.di.LazyKodein import org.kodein.di.generic.instance import org.kodein.di.ktor.kodein +import org.slf4j.event.Level import java.text.DateFormat -import java.time.Duration +private val installationLogger = createLogger("ApplicationSetup") + /** * Loads the application. */ -@KtorExperimentalAPI fun Application.init() { setupKodein() // now kodein is running and can be used val k by kodein() - val logger by k.instance("install-logger") - logger.debug { "DI container started." } + installationLogger.debug { "DI container started." } // connect to the database - connectDatabase(logger, k) + connectDatabase(k) // configure Ktor - installFrameworks() + installFrameworks(k) // register routing routing { registerRoutes() } - - // determine whether should bot connect to the proxy web socket - val useWebSockets by k.instance("use-websocket") - if (useWebSockets) { - subscribeToWebSockets() - } } /** * Connect bot to the database. */ -fun connectDatabase(logger: KLogger, k: LazyKodein) { - logger.info { "Connecting to the DB" } +fun connectDatabase(k: LazyKodein) { + installationLogger.info { "Connecting to the DB" } val dbConfig by k.instance() DatabaseSetup.connect(dbConfig) if (DatabaseSetup.isConnected()) { - logger.info { "DB connected." } - migrateDatabase(logger, dbConfig) + installationLogger.info { "DB connected." } + migrateDatabase(dbConfig) } else { // TODO verify handling, maybe exit the App? - logger.error { "It was not possible to connect to db database! The application will start but it won't work." } + installationLogger.error { "It was not possible to connect to db database! The application will start but it won't work." } } } /** * Migrate database using flyway. */ -fun migrateDatabase(logger: KLogger, dbConfig: DatabaseConfiguration) { - logger.info { "Migrating database." } +fun migrateDatabase(dbConfig: DatabaseConfiguration) { + installationLogger.info { "Migrating database." } val migrationsCount = Flyway .configure() .dataSource(dbConfig.url, dbConfig.userName, dbConfig.password) .load() .migrate() - logger.info { if (migrationsCount == 0) "No migrations necessary." else "Applied $migrationsCount migrations." } + installationLogger.info { if (migrationsCount == 0) "No migrations necessary." else "Applied $migrationsCount migrations." } } /** * Configure Ktor and install necessary extensions. */ -fun Application.installFrameworks() { +fun Application.installFrameworks(k: LazyKodein) { install(ContentNegotiation) { jackson { // enable pretty print for JSONs @@ -98,15 +90,18 @@ fun Application.installFrameworks() { } install(DefaultHeaders) - install(CallLogging) + install(CallLogging) { + level = Level.TRACE + logger = createLogger("HttpCallLogger") + } - install(WebSockets) { - // enable ping - to keep the connection alive - pingPeriod = Duration.ofSeconds(30) - timeout = Duration.ofSeconds(15) - // disabled (max value) - the connection will be closed if surpassed this length. - maxFrameSize = Long.MAX_VALUE - masking = false + val prometheusRegistry by k.instance() + install(MicrometerMetrics) { + registry = prometheusRegistry + distributionStatisticConfig = DistributionStatisticConfig.Builder() + .percentilesHistogram(true) + .build() } + registerExceptionHandlers(k) } diff --git a/src/main/kotlin/com/wire/bots/polls/utils/Extensions.kt b/src/main/kotlin/com/wire/bots/polls/utils/Extensions.kt new file mode 100644 index 0000000..cd9f880 --- /dev/null +++ b/src/main/kotlin/com/wire/bots/polls/utils/Extensions.kt @@ -0,0 +1,13 @@ +package com.wire.bots.polls.utils + +import mu.KLogging + +/** + * Creates URL from [this] as base and [path] as path + */ +infix fun String.appendPath(path: String) = "${dropLastWhile { it == '/' }}/${path.dropWhile { it == '/' }}" + +/** + * Creates logger with given name. + */ +fun createLogger(name: String) = KLogging().logger("com.wire.$name") diff --git a/src/main/kotlin/com/wire/bots/polls/utils/PrometheusExtensions.kt b/src/main/kotlin/com/wire/bots/polls/utils/PrometheusExtensions.kt new file mode 100644 index 0000000..2db37cb --- /dev/null +++ b/src/main/kotlin/com/wire/bots/polls/utils/PrometheusExtensions.kt @@ -0,0 +1,49 @@ +package com.wire.bots.polls.utils + +import io.ktor.client.statement.HttpResponse +import io.ktor.client.statement.request +import io.micrometer.core.instrument.MeterRegistry +import io.micrometer.core.instrument.Tag +import java.util.concurrent.TimeUnit + +/** + * Registers exception in the prometheus metrics. + */ +fun MeterRegistry.countException(exception: Throwable, additionalTags: Map = emptyMap()) { + val baseTags = mapOf( + "type" to exception.javaClass.name, + "message" to (exception.message ?: "No message.") + ) + val tags = (baseTags + additionalTags).toTags() + counter("exceptions", tags).increment() +} + +/** + * Register http call. + */ +fun MeterRegistry.httpCall(response: HttpResponse) { + val startTime = response.requestTime.timestamp + val endTime = response.responseTime.timestamp + + val duration = endTime - startTime + val tags = mapOf( + "code" to response.status.value.toString(), + "url" to response.request.url.toString() + ).toTags() + + timer("http_calls", tags).record(duration, TimeUnit.MILLISECONDS) +} + +/** + * Convert map to the logging tags. + */ +private fun Map.toTags() = + map { (key, value) -> Tag(key, value) } + +/** + * Because original implementation is not handy. + */ +private data class Tag(private val k: String, private val v: String) : Tag { + override fun getKey(): String = k + override fun getValue(): String = v +} From a8178107338eace841a14204569773ded8daa365 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 12:46:24 +0200 Subject: [PATCH 06/34] add slightly better exception handling --- .../com/wire/bots/polls/routing/MessagesRoute.kt | 11 ++++++----- .../bots/polls/services/MessagesHandlingService.kt | 4 ++-- .../wire/bots/polls/services/ProxySenderService.kt | 4 ++-- .../com/wire/bots/polls/setup/DependencyInjection.kt | 6 +++--- .../com/wire/bots/polls/setup/KtorInstallation.kt | 1 + .../polls/setup/{ => errors}/ExceptionHandling.kt | 8 +++++++- .../com/wire/bots/polls/setup/errors/Exceptions.kt | 9 +++++++++ 7 files changed, 30 insertions(+), 13 deletions(-) rename src/main/kotlin/com/wire/bots/polls/setup/{ => errors}/ExceptionHandling.kt (77%) create mode 100644 src/main/kotlin/com/wire/bots/polls/setup/errors/Exceptions.kt diff --git a/src/main/kotlin/com/wire/bots/polls/routing/MessagesRoute.kt b/src/main/kotlin/com/wire/bots/polls/routing/MessagesRoute.kt index 6d5d61c..3d4404f 100644 --- a/src/main/kotlin/com/wire/bots/polls/routing/MessagesRoute.kt +++ b/src/main/kotlin/com/wire/bots/polls/routing/MessagesRoute.kt @@ -30,14 +30,15 @@ fun Routing.messages(k: LazyKodein) { // bot responds either with 200 or with 400 runCatching { routingLogger.debug { "Parsing an message." } - val message = call.receive() - routingLogger.debug { "Message parsed." } - handler.handle(message) - routingLogger.debug { "Responding OK" } - call.respond(HttpStatusCode.OK) + call.receive() }.onFailure { routingLogger.error(it) { "Exception occurred during the request handling!" } call.respond(HttpStatusCode.BadRequest, "Bot did not understand the message.") + }.onSuccess { + routingLogger.debug { "Message parsed." } + handler.handle(it) + routingLogger.debug { "Responding OK" } + call.respond(HttpStatusCode.OK) } } else { routingLogger.warn { "Token is invalid." } diff --git a/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt b/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt index dff4c67..5d618f6 100644 --- a/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt +++ b/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt @@ -69,14 +69,14 @@ class MessagesHandlingService( } }.onFailure { logger.error(it) { "Exception during handling the message: $message with token $token." } - }.getOrNull() ?: false + }.getOrThrow() } private suspend fun handleText(token: String, message: Message): Boolean { var handled = true fun ignore(reason: () -> String) { - logger.info(reason) + logger.debug(reason) handled = false } diff --git a/src/main/kotlin/com/wire/bots/polls/services/ProxySenderService.kt b/src/main/kotlin/com/wire/bots/polls/services/ProxySenderService.kt index 746f0b5..40d5e3c 100644 --- a/src/main/kotlin/com/wire/bots/polls/services/ProxySenderService.kt +++ b/src/main/kotlin/com/wire/bots/polls/services/ProxySenderService.kt @@ -3,6 +3,7 @@ package com.wire.bots.polls.services import ai.blindspot.ktoolz.extensions.createJson import com.wire.bots.polls.dto.bot.BotMessage import com.wire.bots.polls.dto.roman.Response +import com.wire.bots.polls.setup.errors.RomanUnavailableException import com.wire.bots.polls.utils.appendPath import io.ktor.client.HttpClient import io.ktor.client.call.receive @@ -48,8 +49,7 @@ class ProxySenderService(private val client: HttpClient, config: ProxyConfigurat } else -> { val body = it.readText(Charset.defaultCharset()) - logger.error { "Error in communication with proxy. Status: ${it.status}, body: $body." } - null + throw RomanUnavailableException(it.status, body) } } } diff --git a/src/main/kotlin/com/wire/bots/polls/setup/DependencyInjection.kt b/src/main/kotlin/com/wire/bots/polls/setup/DependencyInjection.kt index 766a8eb..de1467b 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/DependencyInjection.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/DependencyInjection.kt @@ -11,11 +11,11 @@ import com.wire.bots.polls.services.PollService import com.wire.bots.polls.services.ProxySenderService import com.wire.bots.polls.services.StatsFormattingService import com.wire.bots.polls.services.UserCommunicationService +import com.wire.bots.polls.utils.createLogger import io.ktor.client.HttpClient import io.micrometer.prometheus.PrometheusConfig import io.micrometer.prometheus.PrometheusMeterRegistry import mu.KLogger -import mu.KLogging import org.kodein.di.Kodein.MainBuilder import org.kodein.di.generic.bind import org.kodein.di.generic.instance @@ -62,6 +62,6 @@ fun MainBuilder.configureContainer() { bind() with singleton { StatsFormattingService(instance()) } - bind("routing-logger") with singleton { KLogging().logger("Routing") } - bind("install-logger") with singleton { KLogging().logger("KtorStartup") } + bind("routing-logger") with singleton { createLogger("Routing") } + bind("install-logger") with singleton { createLogger("KtorStartup") } } diff --git a/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt b/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt index 29cbe05..5b0fad7 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.SerializationFeature import com.wire.bots.polls.dao.DatabaseSetup import com.wire.bots.polls.dto.conf.DatabaseConfiguration import com.wire.bots.polls.routing.registerRoutes +import com.wire.bots.polls.setup.errors.registerExceptionHandlers import com.wire.bots.polls.utils.createLogger import io.ktor.application.Application import io.ktor.application.install diff --git a/src/main/kotlin/com/wire/bots/polls/setup/ExceptionHandling.kt b/src/main/kotlin/com/wire/bots/polls/setup/errors/ExceptionHandling.kt similarity index 77% rename from src/main/kotlin/com/wire/bots/polls/setup/ExceptionHandling.kt rename to src/main/kotlin/com/wire/bots/polls/setup/errors/ExceptionHandling.kt index bebd6ec..bf17568 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/ExceptionHandling.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/errors/ExceptionHandling.kt @@ -1,4 +1,4 @@ -package com.wire.bots.polls.setup +package com.wire.bots.polls.setup.errors import com.wire.bots.polls.utils.countException import com.wire.bots.polls.utils.createLogger @@ -27,6 +27,12 @@ fun Application.registerExceptionHandlers(k: LazyKodein) { call.errorResponse(HttpStatusCode.InternalServerError, cause.message) registry.countException(cause) } + + exception { cause -> + logger.error { "Error in communication with Roman. Status: ${cause.status}, body: ${cause.body}." } + call.errorResponse(HttpStatusCode.ServiceUnavailable, cause.message) + registry.countException(cause) + } } } diff --git a/src/main/kotlin/com/wire/bots/polls/setup/errors/Exceptions.kt b/src/main/kotlin/com/wire/bots/polls/setup/errors/Exceptions.kt new file mode 100644 index 0000000..ec3d261 --- /dev/null +++ b/src/main/kotlin/com/wire/bots/polls/setup/errors/Exceptions.kt @@ -0,0 +1,9 @@ +package com.wire.bots.polls.setup.errors + +import io.ktor.http.HttpStatusCode + +data class RomanUnavailableException( + val status: HttpStatusCode, + val body: String, + override val message: String = "Error in communication with Roman." +) : Exception(message) From 6443d1b232fc7bd08c71b2e825515311b0f61ba1 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 12:51:03 +0200 Subject: [PATCH 07/34] delete obsolete stuff --- README.md | 17 +---------------- .../polls/setup/{HttpDebug.kt => HttpClient.kt} | 2 +- 2 files changed, 2 insertions(+), 17 deletions(-) rename src/main/kotlin/com/wire/bots/polls/setup/{HttpDebug.kt => HttpClient.kt} (97%) diff --git a/README.md b/README.md index fabc808..4e313d0 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Basic usage ## Dev Stack * HTTP Server - [Ktor](https://ktor.io/) -* HTTP Client - [CIO](https://ktor.io/clients/http-client/engines.html) under [Ktor](https://ktor.io/) +* HTTP Client - [Apache](https://ktor.io/clients/http-client/engines.html) under [Ktor](https://ktor.io/) * Dependency Injection - [Kodein](https://github.com/Kodein-Framework/Kodein-DI) * Build system - [Gradle](https://gradle.org/) * Communication with [Wire](https://wire.com/) - [Roman](https://github.com/dkovacevic/roman) @@ -72,18 +72,6 @@ Configuration is currently being loaded from the environment variables. * Key for connecting to the web socket of the proxy. */ const val APP_KEY = "APP_KEY" - /** - * Determines whether to use web sockets for connection to proxy or not eg. true - */ - const val USE_WEB_SOCKETS = "USE_WEB_SOCKETS" - /** - * Host name for the connection to web socket eg."proxy.services.zinfra.io" - */ - const val PROXY_WS_HOST = "PROXY_WS_HOST" - /** - * Path to web socket at proxy eg. "/await" - */ - const val PROXY_WS_PATH = "PROXY_WS_PATH" /** * Domain used for sending the messages from the bot to proxy eg. "https://proxy.services.zinfra.io" */ @@ -107,8 +95,5 @@ DB_PASSWORD= DB_URL= SERVICE_TOKEN= APP_KEY= -USE_WEB_SOCKETS= -PROXY_WS_HOST= -PROXY_WS_PATH= PROXY_DOMAIN= ``` diff --git a/src/main/kotlin/com/wire/bots/polls/setup/HttpDebug.kt b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt similarity index 97% rename from src/main/kotlin/com/wire/bots/polls/setup/HttpDebug.kt rename to src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt index 9f22376..b77dbff 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/HttpDebug.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt @@ -13,7 +13,7 @@ import io.ktor.client.features.observer.ResponseObserver import io.micrometer.core.instrument.MeterRegistry /** - * Prepares HTTP Client with given keystore. + * Prepares HTTP Client. */ fun createHttpClient(meterRegistry: MeterRegistry) = HttpClient(Apache) { From b3d0e874e74de65906c2da81bb3bed538b8f244c Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 13:24:56 +0200 Subject: [PATCH 08/34] add more logs as /stats is not send --- .../kotlin/com/wire/bots/polls/services/PollService.kt | 8 +++++++- .../com/wire/bots/polls/setup/errors/ExceptionHandling.kt | 2 +- src/main/resources/logback.xml | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/wire/bots/polls/services/PollService.kt b/src/main/kotlin/com/wire/bots/polls/services/PollService.kt index f89298c..395fa6e 100644 --- a/src/main/kotlin/com/wire/bots/polls/services/PollService.kt +++ b/src/main/kotlin/com/wire/bots/polls/services/PollService.kt @@ -114,10 +114,14 @@ class PollService( * Sends statistics about the poll to the proxy. */ suspend fun sendStats(token: String, pollId: String, conversationMembers: Int? = null) { + logger.debug { "Sending stats for poll $pollId" } val conversationMembersCount = conversationMembers ?: conversationService.getNumberOfConversationMembers(token) .whenNull { logger.warn { "It was not possible to determine number of conversation members!" } } - val stats = statsFormattingService.formatStats(pollId, conversationMembersCount) ?: return + logger.debug { "Conversation members: $conversationMembersCount" } + val stats = statsFormattingService.formatStats(pollId, conversationMembersCount) + .whenNull { logger.warn { "It was not possible to format stats for poll $pollId" } } ?: return + GlobalScope.launch { proxySenderService.send(token, stats) } } @@ -125,6 +129,8 @@ class PollService( * Sends stats for latest poll. */ suspend fun sendStatsForLatest(token: String, botId: String) { + logger.debug { "Sending latest stats for bot $botId" } + val latest = repository.getLatestForBot(botId).whenNull { logger.info { "No polls found for bot $botId" } } ?: return diff --git a/src/main/kotlin/com/wire/bots/polls/setup/errors/ExceptionHandling.kt b/src/main/kotlin/com/wire/bots/polls/setup/errors/ExceptionHandling.kt index bf17568..5e66868 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/errors/ExceptionHandling.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/errors/ExceptionHandling.kt @@ -29,7 +29,7 @@ fun Application.registerExceptionHandlers(k: LazyKodein) { } exception { cause -> - logger.error { "Error in communication with Roman. Status: ${cause.status}, body: ${cause.body}." } + logger.error(cause) { "Error in communication with Roman. Status: ${cause.status}, body: ${cause.body}." } call.errorResponse(HttpStatusCode.ServiceUnavailable, cause.message) registry.countException(cause) } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 4eb4c30..ecf20a2 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -12,5 +12,5 @@ - + From 76835ae35eee2cc328b32dd6f6f23c4115a9da5b Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 13:25:43 +0200 Subject: [PATCH 09/34] add trace of message --- .../com/wire/bots/polls/services/MessagesHandlingService.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt b/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt index 5d618f6..70e0c4f 100644 --- a/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt +++ b/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt @@ -14,6 +14,7 @@ class MessagesHandlingService( suspend fun handle(message: Message) { logger.debug { "Handling message." } + logger.trace { message } val handled = when (message.type) { "conversation.bot_request" -> false.also { logger.info { "Bot was added to conversation." } } From 3af15f3624b4916b49ce151e95b5fe3a975311c7 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 13:28:55 +0200 Subject: [PATCH 10/34] name metrics endpoint with standard /metrics --- src/main/kotlin/com/wire/bots/polls/routing/ServiceRoutes.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/wire/bots/polls/routing/ServiceRoutes.kt b/src/main/kotlin/com/wire/bots/polls/routing/ServiceRoutes.kt index 518c84a..63c1ff2 100644 --- a/src/main/kotlin/com/wire/bots/polls/routing/ServiceRoutes.kt +++ b/src/main/kotlin/com/wire/bots/polls/routing/ServiceRoutes.kt @@ -53,7 +53,7 @@ fun Routing.serviceRoutes(k: LazyKodein) { /** * Prometheus endpoint. */ - get("/prometheus") { + get("/metrics") { call.respondTextWriter(status = HttpStatusCode.OK) { @Suppress("BlockingMethodInNonBlockingContext") // sadly this is synchronous API registry.scrape(this) From 43a709c7750ec9d8b78305372789303c99a56e1b Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 14:11:22 +0200 Subject: [PATCH 11/34] add more tracing stuff to find out what is happening --- .../polls/services/ConversationService.kt | 23 ++++++++++++------- .../bots/polls/services/ProxySenderService.kt | 4 ++-- .../bots/polls/utils/PrometheusExtensions.kt | 6 ++--- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/com/wire/bots/polls/services/ConversationService.kt b/src/main/kotlin/com/wire/bots/polls/services/ConversationService.kt index a1e0add..2c34f23 100644 --- a/src/main/kotlin/com/wire/bots/polls/services/ConversationService.kt +++ b/src/main/kotlin/com/wire/bots/polls/services/ConversationService.kt @@ -21,12 +21,19 @@ class ConversationService(private val client: HttpClient, config: ProxyConfigura /** * Returns the number of members of conversation. */ - suspend fun getNumberOfConversationMembers(token: String): Int? { - val conversationInformation = client.get { - url(endpoint) - header("Authorization", "Bearer $token") - } - - return conversationInformation.members?.filter { it.service == null }?.size - } + suspend fun getNumberOfConversationMembers(token: String): Int? = + runCatching { + client.get { + url(endpoint) + header("Authorization", "Bearer $token") + } + }.onFailure { + logger.error(it) { "It was not possible to fetch conversation information!" } + }.onSuccess { + logger.debug { "Successfully got conversation information." } + logger.trace { it } + }.getOrNull() + ?.members + ?.filter { it.service == null } + ?.size } diff --git a/src/main/kotlin/com/wire/bots/polls/services/ProxySenderService.kt b/src/main/kotlin/com/wire/bots/polls/services/ProxySenderService.kt index 40d5e3c..746f0b5 100644 --- a/src/main/kotlin/com/wire/bots/polls/services/ProxySenderService.kt +++ b/src/main/kotlin/com/wire/bots/polls/services/ProxySenderService.kt @@ -3,7 +3,6 @@ package com.wire.bots.polls.services import ai.blindspot.ktoolz.extensions.createJson import com.wire.bots.polls.dto.bot.BotMessage import com.wire.bots.polls.dto.roman.Response -import com.wire.bots.polls.setup.errors.RomanUnavailableException import com.wire.bots.polls.utils.appendPath import io.ktor.client.HttpClient import io.ktor.client.call.receive @@ -49,7 +48,8 @@ class ProxySenderService(private val client: HttpClient, config: ProxyConfigurat } else -> { val body = it.readText(Charset.defaultCharset()) - throw RomanUnavailableException(it.status, body) + logger.error { "Error in communication with proxy. Status: ${it.status}, body: $body." } + null } } } diff --git a/src/main/kotlin/com/wire/bots/polls/utils/PrometheusExtensions.kt b/src/main/kotlin/com/wire/bots/polls/utils/PrometheusExtensions.kt index 2db37cb..d39fa6f 100644 --- a/src/main/kotlin/com/wire/bots/polls/utils/PrometheusExtensions.kt +++ b/src/main/kotlin/com/wire/bots/polls/utils/PrometheusExtensions.kt @@ -27,10 +27,10 @@ fun MeterRegistry.httpCall(response: HttpResponse) { val duration = endTime - startTime val tags = mapOf( - "code" to response.status.value.toString(), - "url" to response.request.url.toString() + "method" to response.request.method.value, + "url" to response.request.url.toString(), + "response_code" to response.status.value.toString() ).toTags() - timer("http_calls", tags).record(duration, TimeUnit.MILLISECONDS) } From 2ded6b44a02754a34dca32a96fdf788565fabfcb Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 14:12:43 +0200 Subject: [PATCH 12/34] update pipeline, send information about deployment to stagging --- .github/workflows/cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index f0bd776..53bdb94 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -88,8 +88,8 @@ jobs: uses: 8398a7/action-slack@v2 with: status: ${{ job.status }} - author_name: Poll Bot Deployment to Kubernetes + author_name: Poll Bot Deployment to Stagging env: SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_CI }} # Send message only if previous step failed - if: failure() + if: always() From a0c4d62dd617cb1b5bc58607b58adc2a5ae04c12 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 14:21:17 +0200 Subject: [PATCH 13/34] more tracing --- .../kotlin/com/wire/bots/polls/setup/HttpClient.kt | 12 +++++++++++- .../com/wire/bots/polls/setup/KtorInstallation.kt | 7 +++++-- .../wire/bots/polls/utils/PrometheusExtensions.kt | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt index b77dbff..4d32641 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt @@ -12,6 +12,8 @@ import io.ktor.client.features.logging.Logging import io.ktor.client.features.observer.ResponseObserver import io.micrometer.core.instrument.MeterRegistry +private val httpClientLogger = createLogger("ObserverLogger") + /** * Prepares HTTP Client. */ @@ -23,7 +25,15 @@ fun createHttpClient(meterRegistry: MeterRegistry) = install(ResponseObserver) { onResponse { - meterRegistry.httpCall(it) + httpClientLogger.trace { "Response received" } + runCatching { + httpClientLogger.trace { "Sending to registry" } + meterRegistry.httpCall(it) + }.onSuccess { + httpClientLogger.trace { "Registered" } + }.onFailure { + httpClientLogger.error(it) { "Problem while storing data in registry." } + } } } diff --git a/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt b/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt index 5b0fad7..dc59079 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt @@ -96,6 +96,11 @@ fun Application.installFrameworks(k: LazyKodein) { logger = createLogger("HttpCallLogger") } + configurePrometheus(k) + registerExceptionHandlers(k) +} + +fun Application.configurePrometheus(k: LazyKodein) { val prometheusRegistry by k.instance() install(MicrometerMetrics) { registry = prometheusRegistry @@ -103,6 +108,4 @@ fun Application.installFrameworks(k: LazyKodein) { .percentilesHistogram(true) .build() } - - registerExceptionHandlers(k) } diff --git a/src/main/kotlin/com/wire/bots/polls/utils/PrometheusExtensions.kt b/src/main/kotlin/com/wire/bots/polls/utils/PrometheusExtensions.kt index d39fa6f..6cbe45a 100644 --- a/src/main/kotlin/com/wire/bots/polls/utils/PrometheusExtensions.kt +++ b/src/main/kotlin/com/wire/bots/polls/utils/PrometheusExtensions.kt @@ -31,6 +31,7 @@ fun MeterRegistry.httpCall(response: HttpResponse) { "url" to response.request.url.toString(), "response_code" to response.status.value.toString() ).toTags() + timer("http_calls", tags).record(duration, TimeUnit.MILLISECONDS) } From b84483bcae430f21c8f45d1102d6ed878cb53ff1 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 14:23:07 +0200 Subject: [PATCH 14/34] set timeout to 30 seconds --- src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt index 4d32641..38f3c27 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt @@ -4,6 +4,7 @@ import com.wire.bots.polls.utils.createLogger import com.wire.bots.polls.utils.httpCall import io.ktor.client.HttpClient import io.ktor.client.engine.apache.Apache +import io.ktor.client.features.HttpTimeout import io.ktor.client.features.json.JacksonSerializer import io.ktor.client.features.json.JsonFeature import io.ktor.client.features.logging.LogLevel @@ -19,6 +20,11 @@ private val httpClientLogger = createLogger("ObserverLogger") */ fun createHttpClient(meterRegistry: MeterRegistry) = HttpClient(Apache) { + install(HttpTimeout) { + // timeout config for 30 seconds + requestTimeoutMillis = 30000 + } + install(JsonFeature) { serializer = JacksonSerializer() } From 9529d9cf5e7b99a52173a122da0f9fd832fdf915 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 14:54:14 +0200 Subject: [PATCH 15/34] try to use CIO instead of apache if it helps --- build.gradle.kts | 1 + .../kotlin/com/wire/bots/polls/setup/HttpClient.kt | 12 ++++-------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7779edc..54ea4b2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { implementation("io.ktor", "ktor-client-json", ktorVersion) implementation("io.ktor", "ktor-client-jackson", ktorVersion) implementation("io.ktor", "ktor-client-apache", ktorVersion) + implementation("io.ktor", "ktor-client-cio", ktorVersion) implementation("io.ktor", "ktor-client-logging-jvm", ktorVersion) // Prometheus metrics diff --git a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt index 38f3c27..b41fd2a 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt @@ -1,10 +1,11 @@ +@file:Suppress("EXPERIMENTAL_API_USAGE") + package com.wire.bots.polls.setup import com.wire.bots.polls.utils.createLogger import com.wire.bots.polls.utils.httpCall import io.ktor.client.HttpClient -import io.ktor.client.engine.apache.Apache -import io.ktor.client.features.HttpTimeout +import io.ktor.client.engine.cio.CIO import io.ktor.client.features.json.JacksonSerializer import io.ktor.client.features.json.JsonFeature import io.ktor.client.features.logging.LogLevel @@ -19,12 +20,7 @@ private val httpClientLogger = createLogger("ObserverLogger") * Prepares HTTP Client. */ fun createHttpClient(meterRegistry: MeterRegistry) = - HttpClient(Apache) { - install(HttpTimeout) { - // timeout config for 30 seconds - requestTimeoutMillis = 30000 - } - + HttpClient(CIO) { install(JsonFeature) { serializer = JacksonSerializer() } From 7402d268341d063e26bd8771f6862868662c8991 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 15:04:10 +0200 Subject: [PATCH 16/34] more granular things --- .../wire/bots/polls/services/ConversationService.kt | 12 ++++++++++-- .../kotlin/com/wire/bots/polls/setup/HttpClient.kt | 6 ++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/com/wire/bots/polls/services/ConversationService.kt b/src/main/kotlin/com/wire/bots/polls/services/ConversationService.kt index 2c34f23..07f1d64 100644 --- a/src/main/kotlin/com/wire/bots/polls/services/ConversationService.kt +++ b/src/main/kotlin/com/wire/bots/polls/services/ConversationService.kt @@ -1,10 +1,13 @@ package com.wire.bots.polls.services +import ai.blindspot.ktoolz.extensions.parseJson import com.wire.bots.polls.dto.roman.ConversationInformation import io.ktor.client.HttpClient +import io.ktor.client.call.receive import io.ktor.client.request.get import io.ktor.client.request.header import io.ktor.client.request.url +import io.ktor.client.statement.HttpStatement import mu.KLogging /** @@ -23,10 +26,15 @@ class ConversationService(private val client: HttpClient, config: ProxyConfigura */ suspend fun getNumberOfConversationMembers(token: String): Int? = runCatching { - client.get { + val response = client.get { url(endpoint) header("Authorization", "Bearer $token") - } + }.execute() + logger.trace { "Executed" } + + val payload = response.receive() + logger.trace { payload } + parseJson(payload) }.onFailure { logger.error(it) { "It was not possible to fetch conversation information!" } }.onSuccess { diff --git a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt index b41fd2a..4d32641 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt @@ -1,11 +1,9 @@ -@file:Suppress("EXPERIMENTAL_API_USAGE") - package com.wire.bots.polls.setup import com.wire.bots.polls.utils.createLogger import com.wire.bots.polls.utils.httpCall import io.ktor.client.HttpClient -import io.ktor.client.engine.cio.CIO +import io.ktor.client.engine.apache.Apache import io.ktor.client.features.json.JacksonSerializer import io.ktor.client.features.json.JsonFeature import io.ktor.client.features.logging.LogLevel @@ -20,7 +18,7 @@ private val httpClientLogger = createLogger("ObserverLogger") * Prepares HTTP Client. */ fun createHttpClient(meterRegistry: MeterRegistry) = - HttpClient(CIO) { + HttpClient(Apache) { install(JsonFeature) { serializer = JacksonSerializer() } From cc08c94ebc8b9f86d83c47d2b1da6fdb3a4a6306 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 15:23:02 +0200 Subject: [PATCH 17/34] publish from stagging branch --- .github/workflows/cd.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 53bdb94..361a0e6 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - stagging jobs: publish: @@ -26,11 +27,11 @@ jobs: # pass release_version build_args: release_version=${{ env.RELEASE_VERSION }} # tag the image with latest git tag - tag_with_ref: true + tags: [latest] # add labels based on the build - see https://github.com/opencontainers/image-spec/blob/master/annotations.md add_git_labels: true - # push only if this is master - push: ${{ startsWith(github.ref, 'refs/heads/master') }} + # push + push: true # Send webhook to Wire using Slack Bot - name: Webhook to Wire From ba60ee65b650cadd5adbb6473a0234140d745caa Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 15:25:26 +0200 Subject: [PATCH 18/34] fix deployment tag --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 361a0e6..30eefee 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -27,7 +27,7 @@ jobs: # pass release_version build_args: release_version=${{ env.RELEASE_VERSION }} # tag the image with latest git tag - tags: [latest] + tags: latest # add labels based on the build - see https://github.com/opencontainers/image-spec/blob/master/annotations.md add_git_labels: true # push From 88fedd8f4afc3f77299e7cef605f63358cd73ad1 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 15:29:53 +0200 Subject: [PATCH 19/34] use correct tags when the docker image is built --- .github/workflows/cd.yml | 2 +- .github/workflows/ci.yml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 30eefee..97f736a 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -27,7 +27,7 @@ jobs: # pass release_version build_args: release_version=${{ env.RELEASE_VERSION }} # tag the image with latest git tag - tags: latest + tag_with_ref: true # add labels based on the build - see https://github.com/opencontainers/image-spec/blob/master/annotations.md add_git_labels: true # push diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ac268c..a1cb687 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,8 @@ on: push: branches-ignore: - master + - stagging + pull_request: jobs: From 8e5f5b000b4ca9eb62612e0b0d1ec37ff610265c Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 15:33:50 +0200 Subject: [PATCH 20/34] fix branch name --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 97f736a..a66dc98 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -4,7 +4,7 @@ on: push: branches: - master - - stagging + - staging jobs: publish: From 91636358165335ffbec5b634a12fb2303813939f Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 15:34:27 +0200 Subject: [PATCH 21/34] fix branch name vol2 --- .github/workflows/cd.yml | 2 +- .github/workflows/ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index a66dc98..05f695d 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -89,7 +89,7 @@ jobs: uses: 8398a7/action-slack@v2 with: status: ${{ job.status }} - author_name: Poll Bot Deployment to Stagging + author_name: Poll Bot Deployment to Staging env: SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_CI }} # Send message only if previous step failed diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1cb687..e226c36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: push: branches-ignore: - master - - stagging + - staging pull_request: From a31344d8a731ac151f1f7f46b87541b005bc854b Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 15:41:21 +0200 Subject: [PATCH 22/34] disable meter registry --- .../com/wire/bots/polls/setup/HttpClient.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt index 4d32641..d2ef2bf 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt @@ -26,14 +26,14 @@ fun createHttpClient(meterRegistry: MeterRegistry) = install(ResponseObserver) { onResponse { httpClientLogger.trace { "Response received" } - runCatching { - httpClientLogger.trace { "Sending to registry" } - meterRegistry.httpCall(it) - }.onSuccess { - httpClientLogger.trace { "Registered" } - }.onFailure { - httpClientLogger.error(it) { "Problem while storing data in registry." } - } +// runCatching { +// httpClientLogger.trace { "Sending to registry" } +// meterRegistry.httpCall(it) +// }.onSuccess { +// httpClientLogger.trace { "Registered" } +// }.onFailure { +// httpClientLogger.error(it) { "Problem while storing data in registry." } +// } } } From 86024d24fe3cabfecce28e619fe4be3f92a28d4a Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 15:57:43 +0200 Subject: [PATCH 23/34] do not use observer --- .../kotlin/com/wire/bots/polls/setup/HttpClient.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt index d2ef2bf..3a3f1ab 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt @@ -23,9 +23,9 @@ fun createHttpClient(meterRegistry: MeterRegistry) = serializer = JacksonSerializer() } - install(ResponseObserver) { - onResponse { - httpClientLogger.trace { "Response received" } +// install(ResponseObserver) { +// onResponse { +// httpClientLogger.trace { "Response received" } // runCatching { // httpClientLogger.trace { "Sending to registry" } // meterRegistry.httpCall(it) @@ -34,8 +34,8 @@ fun createHttpClient(meterRegistry: MeterRegistry) = // }.onFailure { // httpClientLogger.error(it) { "Problem while storing data in registry." } // } - } - } +// } +// } install(Logging) { logger = Logger.TRACE From fe9b10c8f24523c4ac8d3d9a4d02ee0a2ee8bf10 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Tue, 21 Apr 2020 16:03:43 +0200 Subject: [PATCH 24/34] enable obserer with just log --- .../kotlin/com/wire/bots/polls/setup/HttpClient.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt index 3a3f1ab..d2ef2bf 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt @@ -23,9 +23,9 @@ fun createHttpClient(meterRegistry: MeterRegistry) = serializer = JacksonSerializer() } -// install(ResponseObserver) { -// onResponse { -// httpClientLogger.trace { "Response received" } + install(ResponseObserver) { + onResponse { + httpClientLogger.trace { "Response received" } // runCatching { // httpClientLogger.trace { "Sending to registry" } // meterRegistry.httpCall(it) @@ -34,8 +34,8 @@ fun createHttpClient(meterRegistry: MeterRegistry) = // }.onFailure { // httpClientLogger.error(it) { "Problem while storing data in registry." } // } -// } -// } + } + } install(Logging) { logger = Logger.TRACE From 329717c2207ec94a48956962ebabb4a74b7135d4 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Thu, 23 Apr 2020 09:32:57 +0200 Subject: [PATCH 25/34] try to use jetty as http client provider --- build.gradle.kts | 2 +- src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 54ea4b2..340e3e2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,7 +35,7 @@ dependencies { implementation("io.ktor", "ktor-client-json", ktorVersion) implementation("io.ktor", "ktor-client-jackson", ktorVersion) implementation("io.ktor", "ktor-client-apache", ktorVersion) - implementation("io.ktor", "ktor-client-cio", ktorVersion) + implementation("io.ktor", "ktor-client-jetty", ktorVersion) implementation("io.ktor", "ktor-client-logging-jvm", ktorVersion) // Prometheus metrics diff --git a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt index d2ef2bf..cbddf3c 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt @@ -4,6 +4,7 @@ import com.wire.bots.polls.utils.createLogger import com.wire.bots.polls.utils.httpCall import io.ktor.client.HttpClient import io.ktor.client.engine.apache.Apache +import io.ktor.client.engine.jetty.Jetty import io.ktor.client.features.json.JacksonSerializer import io.ktor.client.features.json.JsonFeature import io.ktor.client.features.logging.LogLevel @@ -18,7 +19,7 @@ private val httpClientLogger = createLogger("ObserverLogger") * Prepares HTTP Client. */ fun createHttpClient(meterRegistry: MeterRegistry) = - HttpClient(Apache) { + HttpClient(Jetty) { install(JsonFeature) { serializer = JacksonSerializer() } From f7d48ea506fc18e937d3e98e13f2decc94381695 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Thu, 23 Apr 2020 09:43:55 +0200 Subject: [PATCH 26/34] try to use okhttp --- build.gradle.kts | 2 +- src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt | 6 ++---- .../kotlin/com/wire/bots/polls/setup/KtorInstallation.kt | 3 +++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 340e3e2..8af0897 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,7 +35,7 @@ dependencies { implementation("io.ktor", "ktor-client-json", ktorVersion) implementation("io.ktor", "ktor-client-jackson", ktorVersion) implementation("io.ktor", "ktor-client-apache", ktorVersion) - implementation("io.ktor", "ktor-client-jetty", ktorVersion) + implementation("io.ktor", "ktor-client-okhttp", ktorVersion) implementation("io.ktor", "ktor-client-logging-jvm", ktorVersion) // Prometheus metrics diff --git a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt index cbddf3c..9290a96 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt @@ -1,10 +1,8 @@ package com.wire.bots.polls.setup import com.wire.bots.polls.utils.createLogger -import com.wire.bots.polls.utils.httpCall import io.ktor.client.HttpClient -import io.ktor.client.engine.apache.Apache -import io.ktor.client.engine.jetty.Jetty +import io.ktor.client.engine.okhttp.OkHttp import io.ktor.client.features.json.JacksonSerializer import io.ktor.client.features.json.JsonFeature import io.ktor.client.features.logging.LogLevel @@ -19,7 +17,7 @@ private val httpClientLogger = createLogger("ObserverLogger") * Prepares HTTP Client. */ fun createHttpClient(meterRegistry: MeterRegistry) = - HttpClient(Jetty) { + HttpClient(OkHttp) { install(JsonFeature) { serializer = JacksonSerializer() } diff --git a/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt b/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt index dc59079..fad8c04 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt @@ -13,6 +13,7 @@ import io.ktor.features.ContentNegotiation import io.ktor.features.DefaultHeaders import io.ktor.jackson.jackson import io.ktor.metrics.micrometer.MicrometerMetrics +import io.ktor.request.path import io.ktor.routing.routing import io.micrometer.core.instrument.distribution.DistributionStatisticConfig import io.micrometer.prometheus.PrometheusMeterRegistry @@ -94,6 +95,8 @@ fun Application.installFrameworks(k: LazyKodein) { install(CallLogging) { level = Level.TRACE logger = createLogger("HttpCallLogger") + + filter { call -> call.request.path().startsWith("/messages") } } configurePrometheus(k) From 0742c417e9b15715a90f9b9c401c675cdd69992a Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Thu, 23 Apr 2020 09:51:47 +0200 Subject: [PATCH 27/34] disable observer --- .../com/wire/bots/polls/setup/HttpClient.kt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt index 9290a96..a22b6c2 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt @@ -2,13 +2,12 @@ package com.wire.bots.polls.setup import com.wire.bots.polls.utils.createLogger import io.ktor.client.HttpClient -import io.ktor.client.engine.okhttp.OkHttp +import io.ktor.client.engine.apache.Apache import io.ktor.client.features.json.JacksonSerializer import io.ktor.client.features.json.JsonFeature import io.ktor.client.features.logging.LogLevel import io.ktor.client.features.logging.Logger import io.ktor.client.features.logging.Logging -import io.ktor.client.features.observer.ResponseObserver import io.micrometer.core.instrument.MeterRegistry private val httpClientLogger = createLogger("ObserverLogger") @@ -17,14 +16,14 @@ private val httpClientLogger = createLogger("ObserverLogger") * Prepares HTTP Client. */ fun createHttpClient(meterRegistry: MeterRegistry) = - HttpClient(OkHttp) { + HttpClient(Apache) { install(JsonFeature) { serializer = JacksonSerializer() } - install(ResponseObserver) { - onResponse { - httpClientLogger.trace { "Response received" } +// install(ResponseObserver) { +// onResponse { +// httpClientLogger.trace { "Response received" } // runCatching { // httpClientLogger.trace { "Sending to registry" } // meterRegistry.httpCall(it) @@ -33,8 +32,8 @@ fun createHttpClient(meterRegistry: MeterRegistry) = // }.onFailure { // httpClientLogger.error(it) { "Problem while storing data in registry." } // } - } - } +// } +// } install(Logging) { logger = Logger.TRACE From 52220df11c63a35a67b1470577925b7774053cc9 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Thu, 23 Apr 2020 10:17:45 +0200 Subject: [PATCH 28/34] add observer but do not isntall it --- .../polls/services/MessagesHandlingService.kt | 3 +- .../com/wire/bots/polls/setup/HttpClient.kt | 29 +++++++++---------- .../wire/bots/polls/setup/KtorInstallation.kt | 5 +++- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt b/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt index 70e0c4f..a364c15 100644 --- a/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt +++ b/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt @@ -3,6 +3,7 @@ package com.wire.bots.polls.services import com.wire.bots.polls.dto.PollAction import com.wire.bots.polls.dto.UsersInput import com.wire.bots.polls.dto.roman.Message +import io.ktor.features.BadRequestException import mu.KLogging class MessagesHandlingService( @@ -83,7 +84,7 @@ class MessagesHandlingService( with(message) { when { - userId == null -> throw IllegalArgumentException("UserId must be set for text messages.") + userId == null -> throw BadRequestException("UserId must be set for text messages.") // it is a reply on something refMessageId != null && text != null -> when { // request for stats diff --git a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt index a22b6c2..aa459b4 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt @@ -1,6 +1,7 @@ package com.wire.bots.polls.setup import com.wire.bots.polls.utils.createLogger +import com.wire.bots.polls.utils.httpCall import io.ktor.client.HttpClient import io.ktor.client.engine.apache.Apache import io.ktor.client.features.json.JacksonSerializer @@ -8,9 +9,9 @@ import io.ktor.client.features.json.JsonFeature import io.ktor.client.features.logging.LogLevel import io.ktor.client.features.logging.Logger import io.ktor.client.features.logging.Logging +import io.ktor.client.features.observer.ResponseObserver import io.micrometer.core.instrument.MeterRegistry -private val httpClientLogger = createLogger("ObserverLogger") /** * Prepares HTTP Client. @@ -21,19 +22,15 @@ fun createHttpClient(meterRegistry: MeterRegistry) = serializer = JacksonSerializer() } -// install(ResponseObserver) { -// onResponse { -// httpClientLogger.trace { "Response received" } -// runCatching { -// httpClientLogger.trace { "Sending to registry" } -// meterRegistry.httpCall(it) -// }.onSuccess { -// httpClientLogger.trace { "Registered" } -// }.onFailure { -// httpClientLogger.error(it) { "Problem while storing data in registry." } -// } -// } -// } + // TODO check https://github.com/ktorio/ktor/issues/1813 + @Suppress("ConstantConditionIf") // temporary disabled until https://github.com/ktorio/ktor/issues/1813 is resolved + if (false) { + install(ResponseObserver) { + onResponse { + meterRegistry.httpCall(it) + } + } + } install(Logging) { logger = Logger.TRACE @@ -45,7 +42,7 @@ fun createHttpClient(meterRegistry: MeterRegistry) = * Debug logger for HTTP Requests. */ private val Logger.Companion.DEBUG: Logger - get() = object : Logger, org.slf4j.Logger by createLogger("HttpCallsLogging") { + get() = object : Logger, org.slf4j.Logger by createLogger("DebugHttpClient") { override fun log(message: String) { debug(message) } @@ -56,7 +53,7 @@ private val Logger.Companion.DEBUG: Logger * Trace logger for HTTP Requests. */ private val Logger.Companion.TRACE: Logger - get() = object : Logger, org.slf4j.Logger by createLogger("HttpCallsLogging") { + get() = object : Logger, org.slf4j.Logger by createLogger("TraceHttpClient") { override fun log(message: String) { trace(message) } diff --git a/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt b/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt index fad8c04..8593383 100644 --- a/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt +++ b/src/main/kotlin/com/wire/bots/polls/setup/KtorInstallation.kt @@ -94,7 +94,7 @@ fun Application.installFrameworks(k: LazyKodein) { install(DefaultHeaders) install(CallLogging) { level = Level.TRACE - logger = createLogger("HttpCallLogger") + logger = createLogger("EndpointLogger") filter { call -> call.request.path().startsWith("/messages") } } @@ -103,6 +103,9 @@ fun Application.installFrameworks(k: LazyKodein) { registerExceptionHandlers(k) } +/** + * Install prometheus. + */ fun Application.configurePrometheus(k: LazyKodein) { val prometheusRegistry by k.instance() install(MicrometerMetrics) { From ca92cb7410210b7083c2e19273118b32a087d559 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Thu, 23 Apr 2020 15:05:55 +0200 Subject: [PATCH 29/34] redesign the pipelines to match new branch strategy --- .github/workflows/ci.yml | 10 ++-- .github/workflows/master.yml | 48 +++++++++++++++++++ .github/workflows/{release.yml => prod.yml} | 36 ++++++++------ .github/workflows/{cd.yml => staging.yml} | 43 +++++------------ .../polls/services/ConversationService.kt | 16 ++----- src/main/resources/logback.xml | 2 +- 6 files changed, 91 insertions(+), 64 deletions(-) create mode 100644 .github/workflows/master.yml rename .github/workflows/{release.yml => prod.yml} (72%) rename .github/workflows/{cd.yml => staging.yml} (70%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e226c36..bb7c99c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,12 +42,8 @@ jobs: uses: docker/build-push-action@v1 with: # login to repo - repository: lukaswire/polls - # tag the image with latest git tag - tag_with_ref: true - # add labels based on the build - see https://github.com/opencontainers/image-spec/blob/master/annotations.md - add_git_labels: true - # push only if this is master + repository: wire/ci-test-image + # do not push image push: false # Send webhook to Wire using Slack Bot @@ -55,7 +51,7 @@ jobs: uses: 8398a7/action-slack@v2 with: status: ${{ job.status }} - author_name: Poll Bot Docker CI pipeline + author_name: Docker CI pipeline env: SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_CI }} # Send message only if previous step failed diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml new file mode 100644 index 0000000..2a538a6 --- /dev/null +++ b/.github/workflows/master.yml @@ -0,0 +1,48 @@ +name: Docker Latest build + +on: + push: + branches: + - master + +env: + DOCKER_IMAGE: lukaswire/polls + +jobs: + publish: + name: Build and publish docker image + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set Release Version + # use latest tag as release version + run: echo ::set-env name=RELEASE_VERSION::${GITHUB_SHA} + + - name: Build docker image, push + uses: docker/build-push-action@v1 + with: + # login to repo + repository: ${{ env.DOCKER_IMAGE }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + # pass release_version + build_args: release_version=${{ env.RELEASE_VERSION }} + # tag the image with name of the branch - latest as this is master + tag_with_ref: true + # add labels based on the build - see https://github.com/opencontainers/image-spec/blob/master/annotations.md + add_git_labels: true + # push + push: true + + # Send webhook to Wire using Slack Bot + - name: Webhook to Wire + uses: 8398a7/action-slack@v2 + with: + status: ${{ job.status }} + author_name: ${{ env.DOCKER_IMAGE }} master branch build + env: + SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_CI }} + # Send message only if previous step failed + if: failure() + diff --git a/.github/workflows/release.yml b/.github/workflows/prod.yml similarity index 72% rename from .github/workflows/release.yml rename to .github/workflows/prod.yml index bd149e5..a9060c8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/prod.yml @@ -4,6 +4,10 @@ on: release: types: published +env: + DOCKER_IMAGE: lukaswire/polls + RUBICON_SERVICE: poll + jobs: deploy: name: Build and deploy service @@ -19,7 +23,7 @@ jobs: uses: docker/build-push-action@v1 with: # login to repo - repository: lukaswire/polls + repository: ${{ env.DOCKER_IMAGE }} username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} # pass release_version @@ -45,11 +49,18 @@ jobs: # Update version to the one that was just built - name: Change Version in Rubicon env: - RELEASE_VERSION: ${{ env.RELEASE_VERSION }} + IMAGE: ${{ env.DOCKER_IMAGE }} + SERVICE: ${{ env.RUBICON_SERVICE }} + VERSION: ${{ env.RELEASE_VERSION }} run: |- - cd rubicon/prod/services/poll - sed -i".bak" "s/image: lukaswire\/polls.*/image: lukaswire\/polls:$RELEASE_VERSION/g" poll.yaml - rm poll.yaml.bak + # go to directory with configuration + cd "rubicon/prod/services/$SERVICE" + # escape literals for the sed + export SED_PREPARED=$(echo $IMAGE | awk '{ gsub("/", "\\/", $1); print $1 }') + # update final yaml + sed -i".bak" "s/image: $SED_PREPARED.*/image: $SED_PREPARED:$VERSION/g" "$SERVICE.yaml" + # delete bakup file + rm "$SERVICE.yaml.bak" # Setup gcloud CLI - name: Setup Google Cloud CLI @@ -76,29 +87,26 @@ jobs: # K8s is set up, deploy the app - name: Deploy the Service run: |- - kubectl apply -f rubicon/prod/services/poll/poll.yaml + kubectl apply -f "rubicon/prod/services/$SERVICE/$SERVICE.yaml" # Commit all data to Rubicon and open PR - name: Create Rubicon Pull Request uses: peter-evans/create-pull-request@v2 - env: - RELEASE_VERSION: ${{ env.RELEASE_VERSION }} with: path: rubicon - branch: poll-bot-release + branch: ${{ env.RUBICON_SERVICE }}-release token: ${{ secrets.RUBICON_GIT_TOKEN }} - title: "Poll Bot Release" - commit-message: "Poll bot version bump" + title: ${{ env.RUBICON_SERVICE }} release ${{ env.RELEASE_VERSION }} + commit-message: ${{ env.RUBICON_SERVICE }} version bump to ${{ env.RELEASE_VERSION }} body: | - This is automatic version bump. - Made by [Poll Bot pipeline](https://github.com/wireapp/poll-bot/blob/master/.github/workflows/release.yml). + This is automatic version bump from the pipeline. # Send webhook to Wire using Slack Bot - name: Webhook to Wire uses: 8398a7/action-slack@v2 with: status: ${{ job.status }} - author_name: Poll Bot Release Pipeline + author_name: ${{ env.RUBICON_SERVICE }} release pipeline env: SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_RELEASE }} # Notify every release diff --git a/.github/workflows/cd.yml b/.github/workflows/staging.yml similarity index 70% rename from .github/workflows/cd.yml rename to .github/workflows/staging.yml index 05f695d..bd0b986 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/staging.yml @@ -1,11 +1,14 @@ -name: CD +name: Staging Deployment on: push: branches: - - master - staging +env: + DOCKER_IMAGE: lukaswire/polls + RUBICON_SERVICE: poll + jobs: publish: name: Build and publish docker image @@ -21,41 +24,18 @@ jobs: uses: docker/build-push-action@v1 with: # login to repo - repository: lukaswire/polls + repository: ${{ env.DOCKER_IMAGE }} username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} # pass release_version build_args: release_version=${{ env.RELEASE_VERSION }} - # tag the image with latest git tag + # tag the image with name of the branch - staging tag_with_ref: true # add labels based on the build - see https://github.com/opencontainers/image-spec/blob/master/annotations.md add_git_labels: true # push push: true - # Send webhook to Wire using Slack Bot - - name: Webhook to Wire - uses: 8398a7/action-slack@v2 - with: - status: ${{ job.status }} - author_name: Poll Bot CD pipeline - env: - SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_CI }} - # Send message only if previous step failed - if: failure() - - - deploy: - name: Deploy to K8S - needs: publish - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - repository: zinfra/rubicon - ref: develop - token: ${{ secrets.RUBICON_GIT_TOKEN }} - # Setup gcloud CLI - name: Setup Google Cloud CLI uses: GoogleCloudPlatform/github-actions/setup-gcloud@master @@ -80,17 +60,20 @@ jobs: # K8s is set up, deploy the app - name: Deploy the Service + env: + SERVICE: ${{ env.RUBICON_SERVICE }} run: |- - kubectl delete pod -l name=poll -n staging - kubectl describe pod -l name=poll -n staging + kubectl delete pod -l name=$SERVICE -n staging + kubectl describe pod -l name=$SERVICE -n staging # Send webhook to Wire using Slack Bot - name: Webhook to Wire uses: 8398a7/action-slack@v2 with: status: ${{ job.status }} - author_name: Poll Bot Deployment to Staging + author_name: ${{ env.RUBICON_SERVICE }} staging pipeline env: SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_CI }} # Send message only if previous step failed if: always() + diff --git a/src/main/kotlin/com/wire/bots/polls/services/ConversationService.kt b/src/main/kotlin/com/wire/bots/polls/services/ConversationService.kt index 07f1d64..43076ff 100644 --- a/src/main/kotlin/com/wire/bots/polls/services/ConversationService.kt +++ b/src/main/kotlin/com/wire/bots/polls/services/ConversationService.kt @@ -1,13 +1,11 @@ package com.wire.bots.polls.services -import ai.blindspot.ktoolz.extensions.parseJson import com.wire.bots.polls.dto.roman.ConversationInformation +import com.wire.bots.polls.utils.appendPath import io.ktor.client.HttpClient -import io.ktor.client.call.receive import io.ktor.client.request.get import io.ktor.client.request.header import io.ktor.client.request.url -import io.ktor.client.statement.HttpStatement import mu.KLogging /** @@ -19,27 +17,21 @@ class ConversationService(private val client: HttpClient, config: ProxyConfigura const val conversationPath = "/conversation" } - private val endpoint = config.baseUrl + conversationPath + private val endpoint = config.baseUrl appendPath conversationPath /** * Returns the number of members of conversation. */ suspend fun getNumberOfConversationMembers(token: String): Int? = runCatching { - val response = client.get { + client.get { url(endpoint) header("Authorization", "Bearer $token") - }.execute() - logger.trace { "Executed" } - - val payload = response.receive() - logger.trace { payload } - parseJson(payload) + } }.onFailure { logger.error(it) { "It was not possible to fetch conversation information!" } }.onSuccess { logger.debug { "Successfully got conversation information." } - logger.trace { it } }.getOrNull() ?.members ?.filter { it.service == null } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index ecf20a2..4eb4c30 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -12,5 +12,5 @@ - + From 0ba3bdc30d5d3ed6e1d023ac670f26ff8cf93917 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Thu, 23 Apr 2020 15:23:21 +0200 Subject: [PATCH 30/34] change flow name and use coherent logging --- .github/workflows/staging.yml | 2 +- .../com/wire/bots/polls/services/MessagesHandlingService.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index bd0b986..7954de6 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -11,7 +11,7 @@ env: jobs: publish: - name: Build and publish docker image + name: Deploy to staging runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt b/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt index a364c15..3a393cb 100644 --- a/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt +++ b/src/main/kotlin/com/wire/bots/polls/services/MessagesHandlingService.kt @@ -15,11 +15,11 @@ class MessagesHandlingService( suspend fun handle(message: Message) { logger.debug { "Handling message." } - logger.trace { message } + logger.trace { "Message: $message" } val handled = when (message.type) { - "conversation.bot_request" -> false.also { logger.info { "Bot was added to conversation." } } - "conversation.bot_removed" -> false.also { logger.info { "Bot was removed from the conversation." } } + "conversation.bot_request" -> false.also { logger.debug { "Bot was added to conversation." } } + "conversation.bot_removed" -> false.also { logger.debug { "Bot was removed from the conversation." } } else -> { logger.debug { "Handling type: ${message.type}" } when { From f3aa5f8e2814b92f9f79fbcccb2e2b89429c3758 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Thu, 23 Apr 2020 15:56:32 +0200 Subject: [PATCH 31/34] cleanup --- build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 8af0897..7779edc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,7 +35,6 @@ dependencies { implementation("io.ktor", "ktor-client-json", ktorVersion) implementation("io.ktor", "ktor-client-jackson", ktorVersion) implementation("io.ktor", "ktor-client-apache", ktorVersion) - implementation("io.ktor", "ktor-client-okhttp", ktorVersion) implementation("io.ktor", "ktor-client-logging-jvm", ktorVersion) // Prometheus metrics From 022b349e7a3e936d0bcbc29589333c00d09a9c7b Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Thu, 23 Apr 2020 16:16:16 +0200 Subject: [PATCH 32/34] use common service name --- .github/workflows/prod.yml | 12 ++++++------ .github/workflows/staging.yml | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index a9060c8..85532d0 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -6,7 +6,7 @@ on: env: DOCKER_IMAGE: lukaswire/polls - RUBICON_SERVICE: poll + SERVICE_NAME: poll jobs: deploy: @@ -50,7 +50,7 @@ jobs: - name: Change Version in Rubicon env: IMAGE: ${{ env.DOCKER_IMAGE }} - SERVICE: ${{ env.RUBICON_SERVICE }} + SERVICE: ${{ env.SERVICE_NAME }} VERSION: ${{ env.RELEASE_VERSION }} run: |- # go to directory with configuration @@ -94,10 +94,10 @@ jobs: uses: peter-evans/create-pull-request@v2 with: path: rubicon - branch: ${{ env.RUBICON_SERVICE }}-release + branch: ${{ env.SERVICE_NAME }}-release token: ${{ secrets.RUBICON_GIT_TOKEN }} - title: ${{ env.RUBICON_SERVICE }} release ${{ env.RELEASE_VERSION }} - commit-message: ${{ env.RUBICON_SERVICE }} version bump to ${{ env.RELEASE_VERSION }} + title: ${{ env.SERVICE_NAME }} release ${{ env.RELEASE_VERSION }} + commit-message: ${{ env.SERVICE_NAME }} version bump to ${{ env.RELEASE_VERSION }} body: | This is automatic version bump from the pipeline. @@ -106,7 +106,7 @@ jobs: uses: 8398a7/action-slack@v2 with: status: ${{ job.status }} - author_name: ${{ env.RUBICON_SERVICE }} release pipeline + author_name: ${{ env.SERVICE_NAME }} release pipeline env: SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_RELEASE }} # Notify every release diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 7954de6..1a8d941 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -7,7 +7,7 @@ on: env: DOCKER_IMAGE: lukaswire/polls - RUBICON_SERVICE: poll + SERVICE_NAME: poll jobs: publish: @@ -61,7 +61,7 @@ jobs: # K8s is set up, deploy the app - name: Deploy the Service env: - SERVICE: ${{ env.RUBICON_SERVICE }} + SERVICE: ${{ env.SERVICE_NAME }} run: |- kubectl delete pod -l name=$SERVICE -n staging kubectl describe pod -l name=$SERVICE -n staging @@ -71,7 +71,7 @@ jobs: uses: 8398a7/action-slack@v2 with: status: ${{ job.status }} - author_name: ${{ env.RUBICON_SERVICE }} staging pipeline + author_name: ${{ env.SERVICE_NAME }} staging pipeline env: SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_CI }} # Send message only if previous step failed From c087d73590aee4f42ab9dd51f45a45886607ff4d Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Fri, 24 Apr 2020 10:07:49 +0200 Subject: [PATCH 33/34] add labels for the created PR to rubicon --- .github/workflows/prod.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index 85532d0..12dcda2 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -96,6 +96,7 @@ jobs: path: rubicon branch: ${{ env.SERVICE_NAME }}-release token: ${{ secrets.RUBICON_GIT_TOKEN }} + labels: version-bump, automerge title: ${{ env.SERVICE_NAME }} release ${{ env.RELEASE_VERSION }} commit-message: ${{ env.SERVICE_NAME }} version bump to ${{ env.RELEASE_VERSION }} body: | From a2aca2b4f11f5f5a85daa77c4aa05f480a5fc984 Mon Sep 17 00:00:00 2001 From: Lukas Forst Date: Thu, 30 Apr 2020 10:48:00 +0200 Subject: [PATCH 34/34] deploy bot to GCR --- .github/workflows/master.yml | 12 +++++++----- .github/workflows/prod.yml | 33 +++++++++++++++++++-------------- .github/workflows/staging.yml | 25 ++++++++++++++----------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 2a538a6..f1c83bd 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -6,7 +6,7 @@ on: - master env: - DOCKER_IMAGE: lukaswire/polls + DOCKER_IMAGE: wire-bot/poll jobs: publish: @@ -22,10 +22,13 @@ jobs: - name: Build docker image, push uses: docker/build-push-action@v1 with: - # login to repo + # set docker image repository: ${{ env.DOCKER_IMAGE }} - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} + # use GCR repository + registry: eu.gcr.io + # see https://github.com/marketplace/actions/docker-build-push#google-container-registry-gcr + username: _json_key + password: ${{ secrets.GCR_ACCESS_JSON }} # pass release_version build_args: release_version=${{ env.RELEASE_VERSION }} # tag the image with name of the branch - latest as this is master @@ -45,4 +48,3 @@ jobs: SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_CI }} # Send message only if previous step failed if: failure() - diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index 12dcda2..fb443af 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -5,7 +5,7 @@ on: types: published env: - DOCKER_IMAGE: lukaswire/polls + DOCKER_IMAGE: wire-bot/poll SERVICE_NAME: poll jobs: @@ -22,10 +22,13 @@ jobs: - name: Build and publish docker image uses: docker/build-push-action@v1 with: - # login to repo + # set docker image repository: ${{ env.DOCKER_IMAGE }} - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} + # use GCR repository + registry: eu.gcr.io + # see https://github.com/marketplace/actions/docker-build-push#google-container-registry-gcr + username: _json_key + password: ${{ secrets.GCR_ACCESS_JSON }} # pass release_version build_args: release_version=${{ env.RELEASE_VERSION }} # tag the image with latest git tag @@ -52,11 +55,11 @@ jobs: IMAGE: ${{ env.DOCKER_IMAGE }} SERVICE: ${{ env.SERVICE_NAME }} VERSION: ${{ env.RELEASE_VERSION }} - run: |- + run: | # go to directory with configuration cd "rubicon/prod/services/$SERVICE" - # escape literals for the sed - export SED_PREPARED=$(echo $IMAGE | awk '{ gsub("/", "\\/", $1); print $1 }') + # escape literals for the sed and set output with GCR + export SED_PREPARED=$(echo $IMAGE | awk '{ gsub("/", "\\/", $1); print "eu.gcr.io\\/"$1 }') # update final yaml sed -i".bak" "s/image: $SED_PREPARED.*/image: $SED_PREPARED:$VERSION/g" "$SERVICE.yaml" # delete bakup file @@ -67,26 +70,28 @@ jobs: uses: GoogleCloudPlatform/github-actions/setup-gcloud@master with: version: '286.0.0' - service_account_email: ${{ secrets.GKE_SA_EMAIL }} + service_account_email: kubernetes-deployment-agent@wire-bot.iam.gserviceaccount.com service_account_key: ${{ secrets.GKE_SA_KEY }} - project_id: ${{ secrets.GKE_PROJECT }} + project_id: wire-bot # Configure Docker to use the gcloud command-line tool - name: Configure Docker Google cloud - run: |- + run: | gcloud --quiet auth configure-docker # Get the GKE credentials so we can deploy to the cluster - name: Obtain k8s credentials env: - GKE_CLUSTER: ${{ secrets.GKE_CLUSTER }} - GKE_ZONE: ${{ secrets.GKE_ZONE }} - run: |- + GKE_CLUSTER: anayotto + GKE_ZONE: europe-west1-c + run: | gcloud container clusters get-credentials "$GKE_CLUSTER" --zone "$GKE_ZONE" # K8s is set up, deploy the app - name: Deploy the Service - run: |- + env: + SERVICE: ${{ env.SERVICE_NAME }} + run: | kubectl apply -f "rubicon/prod/services/$SERVICE/$SERVICE.yaml" # Commit all data to Rubicon and open PR diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 1a8d941..61f000b 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -6,7 +6,7 @@ on: - staging env: - DOCKER_IMAGE: lukaswire/polls + DOCKER_IMAGE: wire-bot/poll SERVICE_NAME: poll jobs: @@ -23,10 +23,13 @@ jobs: - name: Build docker image, push uses: docker/build-push-action@v1 with: - # login to repo + # set docker image repository: ${{ env.DOCKER_IMAGE }} - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} + # use GCR repository + registry: eu.gcr.io + # see https://github.com/marketplace/actions/docker-build-push#google-container-registry-gcr + username: _json_key + password: ${{ secrets.GCR_ACCESS_JSON }} # pass release_version build_args: release_version=${{ env.RELEASE_VERSION }} # tag the image with name of the branch - staging @@ -41,28 +44,28 @@ jobs: uses: GoogleCloudPlatform/github-actions/setup-gcloud@master with: version: '286.0.0' - service_account_email: ${{ secrets.GKE_SA_EMAIL }} + service_account_email: kubernetes-deployment-agent@wire-bot.iam.gserviceaccount.com service_account_key: ${{ secrets.GKE_SA_KEY }} - project_id: ${{ secrets.GKE_PROJECT }} + project_id: wire-bot # Configure Docker to use the gcloud command-line tool - name: Configure Docker Google cloud - run: |- + run: | gcloud --quiet auth configure-docker # Get the GKE credentials so we can deploy to the cluster - name: Obtain k8s credentials env: - GKE_CLUSTER: ${{ secrets.GKE_CLUSTER }} - GKE_ZONE: ${{ secrets.GKE_ZONE }} - run: |- + GKE_CLUSTER: anayotto + GKE_ZONE: europe-west1-c + run: | gcloud container clusters get-credentials "$GKE_CLUSTER" --zone "$GKE_ZONE" # K8s is set up, deploy the app - name: Deploy the Service env: SERVICE: ${{ env.SERVICE_NAME }} - run: |- + run: | kubectl delete pod -l name=$SERVICE -n staging kubectl describe pod -l name=$SERVICE -n staging