Skip to content

Commit

Permalink
Clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
atduarte committed Nov 23, 2021
1 parent 8ee5515 commit bb701cc
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 91 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,7 @@ components/ws-scheduler/ws-scheduler
components/ws-proxy/ws-proxy

# Logs
components/server/*.log
components/server/*.log

# IntelliJ
.idea/
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
package io.gitpod.ide.jetbrains.backend.listeners

import com.intellij.openapi.application.ApplicationActivationListener
import io.gitpod.ide.jetbrains.backend.services.HeartbeatService
import com.intellij.openapi.wm.IdeFrame
import com.intellij.openapi.components.service
import com.intellij.openapi.wm.IdeFrame
import io.gitpod.ide.jetbrains.backend.services.HeartbeatService

class MyApplicationActivationListener : ApplicationActivationListener {
override fun applicationActivated(ideFrame: IdeFrame) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License-AGPL.txt in the project root for license information.

package io.gitpod.ide.jetbrains.backend.services

import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.intellij.openapi.diagnostic.logger
import io.gitpod.ide.jetbrains.backend.utils.Retrier.retry
import io.ktor.client.HttpClient
import io.ktor.client.features.HttpTimeout
import io.ktor.client.features.json.JacksonSerializer
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.request.get
import java.io.IOException

object ControllerStatusService {
private val logger = logger<ControllerStatusService>()

private const val PORT = 63342
private val cwmToken = System.getenv("CWM_HOST_STATUS_OVER_HTTP_TOKEN")

private val client: HttpClient by lazy {
HttpClient {
install(HttpTimeout) {
@Suppress("MagicNumber")
requestTimeoutMillis = 2000
}
install(JsonFeature) {
serializer = JacksonSerializer()
}
}
}

data class ControllerStatus(val connected: Boolean, val secondsSinceLastActivity: Int)

/**
* @throws IOException
*/
suspend fun fetch(): ControllerStatus =
@Suppress("MagicNumber")
retry(3, logger) {
@Suppress("TooGenericExceptionCaught") // Unsure what exceptions Ktor might throw
val response: Response = try {
client.get("http://localhost:$PORT/codeWithMe/unattendedHostStatus?token=$cwmToken")
} catch (e: Exception) {
throw IOException("Failed to retrieve controller status.", e)
}

if (response.projects.isEmpty()) {
throw IOException("Failed to fetch controller status as project list is empty.")
}

ControllerStatus(
response.projects[0].controllerConnected,
response.projects[0].secondsSinceLastControllerActivity
)
}

@JsonIgnoreProperties(ignoreUnknown = true)
private data class Response(
val appPid: Int,
val projects: List<Project>
) {
@JsonIgnoreProperties(ignoreUnknown = true)
data class Project(
val controllerConnected: Boolean,
val secondsSinceLastControllerActivity: Int
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,26 @@ import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.logger
import io.gitpod.gitpodprotocol.api.ConnectionHelper
import io.gitpod.gitpodprotocol.api.entities.SendHeartBeatOptions
import io.gitpod.ide.jetbrains.backend.services.ControllerStatusProvider.Companion.ControllerStatus
import io.gitpod.ide.jetbrains.backend.services.ControllerStatusService.ControllerStatus
import io.gitpod.ide.jetbrains.backend.utils.Retrier.retry
import kotlinx.coroutines.delay
import kotlinx.coroutines.future.await
import kotlinx.coroutines.runBlocking
import java.io.IOException
import java.util.concurrent.CompletableFuture
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
import kotlin.concurrent.thread
import kotlin.random.Random.Default.nextInt
import java.util.concurrent.CompletableFuture

@Service
class HeartbeatService() : Disposable {
class HeartbeatService : Disposable {
private val logger = logger<HeartbeatService>()
private val fetchInfo: suspend () -> SupervisorInfoService.Info = { SupervisorInfoService.fetch() }
private val controllerStatusProvider = ControllerStatusProvider()

@Suppress("MagicNumber")
private val intervalInSeconds = 30

private val client = AtomicReference<HeartbeatClient>()
private val heartbeatClient = AtomicReference<HeartbeatClient>()
private val status = AtomicReference(
ControllerStatus(
connected = false,
Expand All @@ -54,15 +53,27 @@ class HeartbeatService() : Disposable {

private suspend fun checkActivity(maxIntervalInSeconds: Int) {
logger.info("Checking activity")
val status = controllerStatusProvider.fetch()
val status = try {
ControllerStatusService.fetch()
} catch (e: IOException) {
logger.error(e.message, e)
return@checkActivity
}
val previousStatus = this.status.getAndSet(status)

if (status.connected != previousStatus.connected) {
return sendHeartbeat(wasClosed = !status.connected)
val wasClosed: Boolean? = when {
status.connected != previousStatus.connected -> !status.connected
status.connected && status.secondsSinceLastActivity <= maxIntervalInSeconds -> false
else -> null
}

if (status.connected && status.secondsSinceLastActivity <= maxIntervalInSeconds) {
return sendHeartbeat(wasClosed = false)
if (wasClosed != null) {
@Suppress("TooGenericExceptionCaught")
return try {
sendHeartbeat(wasClosed)
} catch (e: Exception) {
logger.error("Failed to send heartbeat with wasClosed=$wasClosed", e)
}
}
}

Expand All @@ -74,18 +85,18 @@ class HeartbeatService() : Disposable {
@Synchronized
private suspend fun sendHeartbeat(wasClosed: Boolean = false) {
retry(2, logger) {
if (client.get() == null) {
client.set(createHeartbeatClient())
if (heartbeatClient.get() == null) {
heartbeatClient.set(createHeartbeatClient())
}

@Suppress("TooGenericExceptionCaught") // Unsure what exceptions might be thrown
try {
client.get()!!(wasClosed).await()
heartbeatClient.get()!!(wasClosed).await()
logger.info("Heartbeat sent with wasClosed=$wasClosed")
} catch (e: Exception) {
// If connection fails for some reason,
// remove the reference to the existing server.
client.set(null)
heartbeatClient.set(null)
throw e
}
}
Expand All @@ -98,15 +109,17 @@ class HeartbeatService() : Disposable {
*/
private suspend fun createHeartbeatClient(): HeartbeatClient {
logger.info("Creating HeartbeatClient")
val info = fetchInfo()
val supervisorInfo = SupervisorInfoService.fetch()

val server = ConnectionHelper().connect(
"wss://${info.host.split("//").last()}/api/v1",
info.workspaceUrl,
info.authToken
"wss://${supervisorInfo.host.split("//").last()}/api/v1",
supervisorInfo.workspaceUrl,
supervisorInfo.authToken
).server()

return { wasClosed: Boolean -> server.sendHeartBeat(SendHeartBeatOptions(info.instanceId, wasClosed)) }
return { wasClosed: Boolean ->
server.sendHeartBeat(SendHeartBeatOptions(supervisorInfo.instanceId, wasClosed))
}
}

override fun dispose() = closed.set(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ package io.gitpod.ide.jetbrains.backend.services
import com.intellij.openapi.diagnostic.logger
import io.gitpod.ide.jetbrains.backend.utils.Retrier.retry
import io.gitpod.supervisor.api.Info
import io.gitpod.supervisor.api.Token.GetTokenRequest
import io.gitpod.supervisor.api.Info.WorkspaceInfoRequest
import io.gitpod.supervisor.api.TokenServiceGrpc
import io.gitpod.supervisor.api.InfoServiceGrpc
import io.gitpod.supervisor.api.Token.GetTokenRequest
import io.gitpod.supervisor.api.TokenServiceGrpc
import io.grpc.ManagedChannelBuilder
import kotlinx.coroutines.guava.asDeferred

Expand Down

0 comments on commit bb701cc

Please sign in to comment.