diff --git a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodManager.kt b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodManager.kt index 16033e6d9352cf..46a51be5706f17 100644 --- a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodManager.kt +++ b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodManager.kt @@ -365,4 +365,31 @@ class GitpodManager : Disposable { } } } + + var resourceStatus: Status.ResourcesStatusResponse? = null + + private val metricsJob = GlobalScope.launch { + if (application.isHeadlessEnvironment) { + return@launch + } + val status = StatusServiceGrpc.newFutureStub(supervisorChannel) + while (isActive) { + try { + val f = status.resourcesStatus(Status.ResourcesStatuRequest.getDefaultInstance()) + resourceStatus = f.asDeferred().await() + } catch (t: Throwable) { + if (t is CancellationException) { + throw t + } + thisLogger().error("gitpod: failed to retrieve resource status: ", t) + } + delay(1000L) + } + } + init { + lifetime.onTerminationOrNow { + metricsJob.cancel() + } + } + } diff --git a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/latest/GitpodMetricControlProvider.kt b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/latest/GitpodMetricControlProvider.kt new file mode 100644 index 00000000000000..b70ca71f10fe76 --- /dev/null +++ b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/latest/GitpodMetricControlProvider.kt @@ -0,0 +1,71 @@ +// Copyright (c) 2022 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.jetbrains.remote.latest + +import com.jetbrains.ide.model.uiautomation.BeControl +import com.jetbrains.rd.ui.bedsl.dsl.VerticalGridBuilder +import com.jetbrains.rd.ui.bedsl.dsl.verticalGrid +import com.jetbrains.rd.util.lifetime.Lifetime +import com.jetbrains.rd.util.reactive.Property +import com.jetbrains.rdserver.diagnostics.BackendDiagnosticsService +import com.jetbrains.rdserver.unattendedHost.customization.controlCenter.performance.MetricControlProvider +import com.jetbrains.rdserver.unattendedHost.customization.controlCenter.performance.createProgressBar +import com.jetbrains.rdserver.unattendedHost.customization.controlCenter.performance.createProgressRow + +class GitpodMetricControlProvider : MetricControlProvider { + override val id: String = "gitpodMetricsControl" + override fun getControl(lifetime: Lifetime): BeControl { + return verticalGrid { + val backendDiagnosticsService = BackendDiagnosticsService.Companion.getInstance() + createCpuControl(this, backendDiagnosticsService, lifetime) + createMemoryControl(this, backendDiagnosticsService, lifetime) + } + } + + private fun createCpuControl(ctx: VerticalGridBuilder, backendDiagnosticsService: BackendDiagnosticsService, lifetime: Lifetime) { + val cpuUsed = backendDiagnosticsService.getMetric("gitpod_workspace_cpu_used") + val cpuTotal = backendDiagnosticsService.getMetric("gitpod_workspace_cpu_total") + val cpuPercentage = backendDiagnosticsService.getMetric("gitpod_workspace_cpu_percentage") + val cpuPercentageProperty = Property("$cpuPercentage %") + val label = "Workspace CPU" + val progressBar = createProgressBar(lifetime, cpuPercentage.valueProperty, cpuPercentageProperty) + val labelProperty = Property("") + + fun updateLabel() { + labelProperty.set("${cpuUsed}m / ${cpuTotal}m") + } + updateLabel() + cpuUsed.valueProperty.change.advise(lifetime) { + updateLabel() + } + cpuTotal.valueProperty.change.advise(lifetime) { + updateLabel() + } + createProgressRow(ctx, lifetime, label, cpuPercentage.statusProperty, labelProperty, cpuPercentageProperty, progressBar) + } + + private fun createMemoryControl(ctx: VerticalGridBuilder, backendDiagnosticsService: BackendDiagnosticsService, lifetime: Lifetime) { + val memoryUsed = backendDiagnosticsService.getMetric("gitpod_workspace_memory_used") + val memoryTotal = backendDiagnosticsService.getMetric("gitpod_workspace_memory_total") + val memoryPercentage = backendDiagnosticsService.getMetric("gitpod_workspace_memory_percentage") + val memoryPercentageProperty = Property("$memoryPercentage %") + val label = "Workspace Memory" + val progressBar = createProgressBar(lifetime, memoryPercentage.valueProperty, memoryPercentageProperty) + val labelProperty = Property("") + + fun updateLabel() { + labelProperty.set("${memoryUsed}GB / ${memoryTotal}GB") + } + updateLabel() + memoryUsed.valueProperty.change.advise(lifetime) { + updateLabel() + } + memoryTotal.valueProperty.change.advise(lifetime) { + updateLabel() + } + + createProgressRow(ctx, lifetime, label, memoryPercentage.statusProperty, labelProperty, memoryPercentageProperty, progressBar) + } +} diff --git a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/latest/GitpodMetricProvider.kt b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/latest/GitpodMetricProvider.kt new file mode 100644 index 00000000000000..d1f115681edbcd --- /dev/null +++ b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/latest/GitpodMetricProvider.kt @@ -0,0 +1,65 @@ +// Copyright (c) 2022 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.jetbrains.remote.latest + +import com.intellij.openapi.components.service +import com.jetbrains.rd.platform.codeWithMe.unattendedHost.metrics.Metric +import com.jetbrains.rd.platform.codeWithMe.unattendedHost.metrics.MetricType +import com.jetbrains.rd.platform.codeWithMe.unattendedHost.metrics.MetricsStatus +import com.jetbrains.rd.platform.codeWithMe.unattendedHost.metrics.providers.MetricProvider +import io.gitpod.jetbrains.remote.GitpodManager +import kotlin.math.roundToInt + +class GitpodMetricProvider: MetricProvider { + private val manager = service() + + override val id: String = "gitpodMetricsProvider" + override fun getMetrics(): Map { + val resourceStatus = manager.resourceStatus + + val cpuUsed = resourceStatus?.cpu?.used?.toDouble() ?: 0.0 + val cpuTotal = resourceStatus?.cpu?.limit?.toDouble() ?: 0.0 + val cpuPercentage = (cpuUsed / cpuTotal) * 100 + + // TODO: retrieve thresholds from supervisor once we implement this: https://github.com/gitpod-io/gitpod/issues/12075 + val cpuStatus = if (cpuPercentage >= 95) { + MetricsStatus.DANGER + } else if (cpuPercentage >= 80) { + MetricsStatus.WARNING + } else { + MetricsStatus.NORMAL + } + + val memoryUsed = convertBytesToGB(resourceStatus?.memory?.used ?: 0) + val memoryTotal = convertBytesToGB(resourceStatus?.memory?.limit ?: 0) + val memoryPercentage = (memoryUsed / memoryTotal) * 100 + + // TODO: retrieve thresholds from supervisor once we implement this: https://github.com/gitpod-io/gitpod/issues/12075 + val memoryStatus = if (memoryPercentage >= 95) { + MetricsStatus.DANGER + } else if (memoryPercentage >= 80) { + MetricsStatus.WARNING + } else { + MetricsStatus.NORMAL + } + + return mapOf( + "gitpod_workspace_cpu_used" to Metric(MetricType.PERFORMANCE, MetricsStatus.NORMAL, roundTo(cpuUsed, 0)), + "gitpod_workspace_cpu_total" to Metric(MetricType.PERFORMANCE, MetricsStatus.NORMAL, roundTo(cpuTotal, 0)), + "gitpod_workspace_cpu_percentage" to Metric(MetricType.PERFORMANCE, cpuStatus, (cpuPercentage * 1000.0).roundToInt() / 1000.0), + "gitpod_workspace_memory_used" to Metric(MetricType.PERFORMANCE, MetricsStatus.NORMAL, roundTo(memoryUsed, 2)), + "gitpod_workspace_memory_total" to Metric(MetricType.PERFORMANCE, MetricsStatus.NORMAL, roundTo(memoryTotal, 2)), + "gitpod_workspace_memory_percentage" to Metric(MetricType.PERFORMANCE, memoryStatus, (memoryPercentage * 1000.0).roundToInt() / 1000.0) + ) + } + + private fun convertBytesToGB(bytes: Long) : Double { + return bytes.div(1073741824.0) + } + + private fun roundTo(number: Double, decimals: Int) : String { + return String.format("%.${decimals}f", number) + } +} diff --git a/components/ide/jetbrains/backend-plugin/src/main/resources-latest/META-INF/extensions.xml b/components/ide/jetbrains/backend-plugin/src/main/resources-latest/META-INF/extensions.xml index c98241d241d8c9..47aa4aa265bfc4 100644 --- a/components/ide/jetbrains/backend-plugin/src/main/resources-latest/META-INF/extensions.xml +++ b/components/ide/jetbrains/backend-plugin/src/main/resources-latest/META-INF/extensions.xml @@ -7,5 +7,7 @@ + +