diff --git a/src/main/cluster.ts b/src/main/cluster.ts index b6323c0a0d..5d6fe11642 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -95,7 +95,6 @@ export class Cluster implements ClusterInfo { this.contextName = kc.currentContext this.url = this.contextHandler.url this.apiUrl = kc.getCurrentCluster().server - await this.contextHandler.init() } public stopServer() { @@ -120,7 +119,7 @@ export class Cluster implements ClusterInfo { public async refreshCluster() { clusterStore.reloadCluster(this) - this.contextHandler.setClusterPreferences(this.preferences) + await this.contextHandler.setClusterPreferences(this.preferences) const connectionStatus = await this.getConnectionStatus() if (connectionStatus == ClusterStatus.AccessGranted) { diff --git a/src/main/context-handler.ts b/src/main/context-handler.ts index 817b684d00..4c282a7636 100644 --- a/src/main/context-handler.ts +++ b/src/main/context-handler.ts @@ -1,4 +1,4 @@ -import { KubeConfig } from "@kubernetes/client-node" +import { KubeConfig, CoreV1Api } from "@kubernetes/client-node" import { readFileSync } from "fs" import * as http from "http" import { ServerOptions } from "http-proxy" @@ -7,6 +7,8 @@ import logger from "./logger" import { getFreePort } from "./port" import { KubeAuthProxy } from "./kube-auth-proxy" import { Cluster, ClusterPreferences } from "./cluster" +import { prometheusProviders } from "../common/prometheus-providers" +import { PrometheusService, PrometheusProvider } from "./prometheus/provider-registry" export class ContextHandler { public contextName: string @@ -56,7 +58,6 @@ export class ContextHandler { this.defaultNamespace = kc.getContextObject(kc.currentContext).namespace this.url = `http://${this.id}.localhost:${cluster.port}/` this.kubernetesApi = `http://127.0.0.1:${cluster.port}/${this.id}` - this.setClusterPreferences(cluster.preferences) this.kc.clusters = [ { name: kc.getCurrentCluster().name, @@ -64,14 +65,16 @@ export class ContextHandler { skipTLSVerify: true } ] + this.setClusterPreferences(cluster.preferences) } - public setClusterPreferences(clusterPreferences?: ClusterPreferences) { + public async setClusterPreferences(clusterPreferences?: ClusterPreferences) { if (clusterPreferences && clusterPreferences.prometheus) { const prom = clusterPreferences.prometheus this.prometheusPath = `${prom.namespace}/services/${prom.service}:${prom.port}` } else { - this.prometheusPath = "lens-metrics/services/prometheus:80" + const path = await this.resolvePrometheusPath(clusterPreferences.prometheusProvider?.type) + this.prometheusPath = path ? path : "lens-metrics/services/prometheus:80" } if(clusterPreferences && clusterPreferences.clusterName) { this.clusterName = clusterPreferences.clusterName; @@ -80,26 +83,22 @@ export class ContextHandler { } } - public getPrometheusPath() { - return this.prometheusPath + protected async resolvePrometheusPath(providerId: string): Promise { + const apiClient = this.kc.makeApiClient(CoreV1Api) + const providers = providerId ? prometheusProviders.filter((p, _) => p.id == providerId) : prometheusProviders + const prometheusPromises: Promise[] = providers.map(async (provider: PrometheusProvider): Promise => { + return await provider.getPrometheusService(apiClient) + }) + const resolvedPrometheusServices = await Promise.all(prometheusPromises) + const service = resolvedPrometheusServices.filter(n => n)[0] + console.log(service) + if (service) { + return `${service.namespace}/services/${service.service}:${service.port}` + } } - public async init() { - const currentCluster = this.kc.getCurrentCluster() - if (currentCluster.caFile) { - this.certData = readFileSync(currentCluster.caFile).toString() - } else if (currentCluster.caData) { - this.certData = Buffer.from(currentCluster.caData, "base64").toString("ascii") - } - const user = this.kc.getCurrentUser() - if (user.authProvider && user.authProvider.name === "oidc") { - const authConfig = user.authProvider.config - if (authConfig["idp-certificate-authority"]) { - this.authCertData = readFileSync(authConfig["idp-certificate-authority"]).toString() - } else if (authConfig["idp-certificate-authority-data"]) { - this.authCertData = Buffer.from(authConfig["idp-certificate-authority-data"], "base64").toString("ascii") - } - } + public getPrometheusPath() { + return this.prometheusPath } public async getApiTarget(isWatchRequest = false) { diff --git a/src/main/prometheus/helm.ts b/src/main/prometheus/helm.ts index c69df755d5..624a429017 100644 --- a/src/main/prometheus/helm.ts +++ b/src/main/prometheus/helm.ts @@ -1,10 +1,29 @@ import { PrometheusLens } from "./lens" +import { CoreV1Api } from "@kubernetes/client-node" +import { PrometheusService } from "./provider-registry"; +import logger from "../logger" export class PrometheusHelm extends PrometheusLens { - constructor() { - super() - this.id = "helm" - this.name = "Helm" - this.rateAccuracy = "5m" + id = "helm" + name = "helm" + rateAccuracy = "5m" + + public async getPrometheusService(client: CoreV1Api): Promise { + const labelSelector = "app=prometheus,component=server,heritage=Helm" + try { + const serviceList = await client.listServiceForAllNamespaces(false, "", null, labelSelector) + const service = serviceList.body.items[0] + if (!service) return + + return { + id: this.id, + namespace: service.metadata.namespace, + service: service.metadata.name, + port: service.spec.ports[0].port + } + } catch(error) { + logger.warn(`failed to list services: ${error.toString()}`) + return + } } -} \ No newline at end of file +} diff --git a/src/main/prometheus/lens.ts b/src/main/prometheus/lens.ts index f278000731..6b04385191 100644 --- a/src/main/prometheus/lens.ts +++ b/src/main/prometheus/lens.ts @@ -1,11 +1,21 @@ -import { PrometheusProvider, PrometheusQueryOpts, PrometheusClusterQuery, PrometheusNodeQuery, PrometheusPodQuery, PrometheusPvcQuery, PrometheusIngressQuery } from "./provider-registry"; +import { PrometheusProvider, PrometheusQueryOpts, PrometheusQuery, PrometheusService } from "./provider-registry"; +import { CoreV1Api } from "@kubernetes/client-node"; export class PrometheusLens implements PrometheusProvider { id = "lens" name = "Lens" rateAccuracy = "1m" - public getQueries(opts: PrometheusQueryOpts): PrometheusNodeQuery | PrometheusClusterQuery | PrometheusPodQuery | PrometheusPvcQuery | PrometheusIngressQuery { + public async getPrometheusService(client: CoreV1Api): Promise { + return { + id: this.id, + namespace: "lens-metrics", + service: "prometheus", + port: 80 + } + } + + public getQueries(opts: PrometheusQueryOpts): PrometheusQuery { switch(opts.category) { case 'cluster': return { @@ -63,4 +73,4 @@ export class PrometheusLens implements PrometheusProvider { } } } -} \ No newline at end of file +} diff --git a/src/main/prometheus/operator.ts b/src/main/prometheus/operator.ts index a44abbe72f..b899400558 100644 --- a/src/main/prometheus/operator.ts +++ b/src/main/prometheus/operator.ts @@ -1,11 +1,33 @@ -import { PrometheusProvider, PrometheusQueryOpts, PrometheusClusterQuery, PrometheusNodeQuery, PrometheusPodQuery, PrometheusPvcQuery, PrometheusIngressQuery } from "./provider-registry"; +import { PrometheusProvider, PrometheusQueryOpts, PrometheusQuery, PrometheusService } from "./provider-registry"; +import { CoreV1Api } from "@kubernetes/client-node"; +import logger from "../logger"; export class PrometheusOperator implements PrometheusProvider { rateAccuracy = "1m" id = "operator" name = "Prometheus Operator" - public getQueries(opts: PrometheusQueryOpts): PrometheusNodeQuery | PrometheusClusterQuery | PrometheusPodQuery | PrometheusPvcQuery | PrometheusIngressQuery { + public async getPrometheusService(client: CoreV1Api): Promise { + const labelSelector = "operated-prometheus==true" + try { + const serviceList = await client.listServiceForAllNamespaces(false, "", null, labelSelector) + const service = serviceList.body.items[0] + if (!service) return + + return { + id: this.id, + namespace: service.metadata.namespace, + service: service.metadata.name, + port: service.spec.ports[0].port + } + } catch(error) { + console.error(error) + logger.warn(`failed to list services: ${error.toString()}`) + return + } + } + + public getQueries(opts: PrometheusQueryOpts): PrometheusQuery { switch(opts.category) { case 'cluster': return { @@ -63,4 +85,4 @@ export class PrometheusOperator implements PrometheusProvider { } } } -} \ No newline at end of file +} diff --git a/src/main/prometheus/provider-registry.ts b/src/main/prometheus/provider-registry.ts index d02feb1ab5..c59b2ad8ea 100644 --- a/src/main/prometheus/provider-registry.ts +++ b/src/main/prometheus/provider-registry.ts @@ -1,3 +1,5 @@ +import { CoreV1Api } from "@kubernetes/client-node" + export type PrometheusClusterQuery = { memoryUsage: string; memoryRequests: string; @@ -48,8 +50,20 @@ export type PrometheusQueryOpts = { [key: string]: string | any; }; +export type PrometheusQuery = PrometheusNodeQuery | PrometheusClusterQuery | PrometheusPodQuery | PrometheusPvcQuery | PrometheusIngressQuery + +export type PrometheusService = { + id: string; + namespace: string; + service: string; + port: number; +} + export interface PrometheusProvider { - getQueries(opts: PrometheusQueryOpts): PrometheusNodeQuery | PrometheusClusterQuery | PrometheusPodQuery | PrometheusPvcQuery | PrometheusIngressQuery; + id: string; + name: string; + getQueries(opts: PrometheusQueryOpts): PrometheusQuery; + getPrometheusService(client: CoreV1Api): Promise; } export type PrometheusProviderList = { @@ -73,4 +87,4 @@ export class PrometheusProviderRegistry { static getProviders(): PrometheusProvider[] { return Object.values(this.prometheusProviders) } -} \ No newline at end of file +} diff --git a/src/renderer/components/ClusterSettings/Preferences/index.vue b/src/renderer/components/ClusterSettings/Preferences/index.vue index 108b6ad207..9def72d9d6 100644 --- a/src/renderer/components/ClusterSettings/Preferences/index.vue +++ b/src/renderer/components/ClusterSettings/Preferences/index.vue @@ -20,17 +20,7 @@
Prometheus

Use pre-installed Prometheus service for metrics. Please refer to the guide for possible configuration changes.

- - - + + + +
@@ -84,13 +86,25 @@ export default { }, prometheusPath: "", prometheusProvider: "", - prometheusProviders: [], + } + }, + computed: { + prometheusProviders: function() { + const providers = prometheusProviders.map((provider) => { + return { text: provider.name, value: provider.id } + }) + providers.unshift({text: "Auto detect", value: ""}) + + return providers; + }, + canEditPrometheusPath: function() { + if (this.prometheusProvider === "") return false + if (this.prometheusProvider === "lens") return false + + return true } }, mounted: async function() { - this.prometheusProviders = prometheusProviders.map((provider) => { - return { text: provider.name, value: provider.id } - }) this.updateValues() }, methods: { @@ -104,7 +118,7 @@ export default { if (this.cluster.preferences.prometheusProvider) { this.prometheusProvider = this.cluster.preferences.prometheusProvider.type } else { - this.prometheusProvider = "lens" + this.prometheusProvider = "" } }, parsePrometheusPath: function(path) {