1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

auto-detect prometheus installation

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen 2020-05-05 18:07:55 +03:00
parent 6412d73a5a
commit e23c8cfb6b
7 changed files with 131 additions and 54 deletions

View File

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

View File

@ -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<string> {
const apiClient = this.kc.makeApiClient(CoreV1Api)
const providers = providerId ? prometheusProviders.filter((p, _) => p.id == providerId) : prometheusProviders
const prometheusPromises: Promise<PrometheusService>[] = providers.map(async (provider: PrometheusProvider): Promise<PrometheusService> => {
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) {

View File

@ -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<PrometheusService> {
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
}
}
}
}

View File

@ -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<PrometheusService> {
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 {
}
}
}
}
}

View File

@ -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<PrometheusService> {
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 {
}
}
}
}
}

View File

@ -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<PrometheusService>;
}
export type PrometheusProviderList = {
@ -73,4 +87,4 @@ export class PrometheusProviderRegistry {
static getProviders(): PrometheusProvider[] {
return Object.values(this.prometheusProviders)
}
}
}

View File

@ -20,17 +20,7 @@
<div class="cluster-settings-section">
<b>Prometheus</b>
<p>Use pre-installed Prometheus service for metrics. Please refer to the <a href="https://github.com/lensapp/lens/blob/master/troubleshooting/custom-prometheus.md">guide</a> for possible configuration changes.</p>
<b-form-group
label="Prometheus service address."
description="A path to an existing Prometheus installation (<namespace>/<service>:<port>)."
>
<b-form-input
v-model="prometheusPath"
placeholder="lens-metrics/prometheus:80"
id="input-prometheuspath"
@blur="onPrometheusSave"
/>
</b-form-group>
<b-form-group
label="Prometheus installation method."
description="What query format is used to fetch metrics from Prometheus"
@ -41,6 +31,18 @@
@change="onPrometheusProviderSave"
/>
</b-form-group>
<b-form-group
label="Prometheus service address."
description="A path to an existing Prometheus installation (<namespace>/<service>:<port>)."
v-if="canEditPrometheusPath"
>
<b-form-input
v-model="prometheusPath"
placeholder="lens-metrics/prometheus:80"
id="input-prometheuspath"
@blur="onPrometheusSave"
/>
</b-form-group>
</div>
</div>
<div class="col-12">
@ -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) {