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.contextName = kc.currentContext
this.url = this.contextHandler.url this.url = this.contextHandler.url
this.apiUrl = kc.getCurrentCluster().server this.apiUrl = kc.getCurrentCluster().server
await this.contextHandler.init()
} }
public stopServer() { public stopServer() {
@ -120,7 +119,7 @@ export class Cluster implements ClusterInfo {
public async refreshCluster() { public async refreshCluster() {
clusterStore.reloadCluster(this) clusterStore.reloadCluster(this)
this.contextHandler.setClusterPreferences(this.preferences) await this.contextHandler.setClusterPreferences(this.preferences)
const connectionStatus = await this.getConnectionStatus() const connectionStatus = await this.getConnectionStatus()
if (connectionStatus == ClusterStatus.AccessGranted) { 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 { readFileSync } from "fs"
import * as http from "http" import * as http from "http"
import { ServerOptions } from "http-proxy" import { ServerOptions } from "http-proxy"
@ -7,6 +7,8 @@ import logger from "./logger"
import { getFreePort } from "./port" import { getFreePort } from "./port"
import { KubeAuthProxy } from "./kube-auth-proxy" import { KubeAuthProxy } from "./kube-auth-proxy"
import { Cluster, ClusterPreferences } from "./cluster" import { Cluster, ClusterPreferences } from "./cluster"
import { prometheusProviders } from "../common/prometheus-providers"
import { PrometheusService, PrometheusProvider } from "./prometheus/provider-registry"
export class ContextHandler { export class ContextHandler {
public contextName: string public contextName: string
@ -56,7 +58,6 @@ export class ContextHandler {
this.defaultNamespace = kc.getContextObject(kc.currentContext).namespace this.defaultNamespace = kc.getContextObject(kc.currentContext).namespace
this.url = `http://${this.id}.localhost:${cluster.port}/` this.url = `http://${this.id}.localhost:${cluster.port}/`
this.kubernetesApi = `http://127.0.0.1:${cluster.port}/${this.id}` this.kubernetesApi = `http://127.0.0.1:${cluster.port}/${this.id}`
this.setClusterPreferences(cluster.preferences)
this.kc.clusters = [ this.kc.clusters = [
{ {
name: kc.getCurrentCluster().name, name: kc.getCurrentCluster().name,
@ -64,14 +65,16 @@ export class ContextHandler {
skipTLSVerify: true skipTLSVerify: true
} }
] ]
this.setClusterPreferences(cluster.preferences)
} }
public setClusterPreferences(clusterPreferences?: ClusterPreferences) { public async setClusterPreferences(clusterPreferences?: ClusterPreferences) {
if (clusterPreferences && clusterPreferences.prometheus) { if (clusterPreferences && clusterPreferences.prometheus) {
const prom = clusterPreferences.prometheus const prom = clusterPreferences.prometheus
this.prometheusPath = `${prom.namespace}/services/${prom.service}:${prom.port}` this.prometheusPath = `${prom.namespace}/services/${prom.service}:${prom.port}`
} else { } 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) { if(clusterPreferences && clusterPreferences.clusterName) {
this.clusterName = clusterPreferences.clusterName; this.clusterName = clusterPreferences.clusterName;
@ -80,26 +83,22 @@ export class ContextHandler {
} }
} }
public getPrometheusPath() { protected async resolvePrometheusPath(providerId: string): Promise<string> {
return this.prometheusPath 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() { public getPrometheusPath() {
const currentCluster = this.kc.getCurrentCluster() return this.prometheusPath
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 async getApiTarget(isWatchRequest = false) { public async getApiTarget(isWatchRequest = false) {

View File

@ -1,10 +1,29 @@
import { PrometheusLens } from "./lens" import { PrometheusLens } from "./lens"
import { CoreV1Api } from "@kubernetes/client-node"
import { PrometheusService } from "./provider-registry";
import logger from "../logger"
export class PrometheusHelm extends PrometheusLens { export class PrometheusHelm extends PrometheusLens {
constructor() { id = "helm"
super() name = "helm"
this.id = "helm" rateAccuracy = "5m"
this.name = "Helm"
this.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 { export class PrometheusLens implements PrometheusProvider {
id = "lens" id = "lens"
name = "Lens" name = "Lens"
rateAccuracy = "1m" 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) { switch(opts.category) {
case 'cluster': case 'cluster':
return { 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 { export class PrometheusOperator implements PrometheusProvider {
rateAccuracy = "1m" rateAccuracy = "1m"
id = "operator" id = "operator"
name = "Prometheus 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) { switch(opts.category) {
case 'cluster': case 'cluster':
return { 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 = { export type PrometheusClusterQuery = {
memoryUsage: string; memoryUsage: string;
memoryRequests: string; memoryRequests: string;
@ -48,8 +50,20 @@ export type PrometheusQueryOpts = {
[key: string]: string | any; [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 { 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 = { export type PrometheusProviderList = {
@ -73,4 +87,4 @@ export class PrometheusProviderRegistry {
static getProviders(): PrometheusProvider[] { static getProviders(): PrometheusProvider[] {
return Object.values(this.prometheusProviders) return Object.values(this.prometheusProviders)
} }
} }

View File

@ -20,17 +20,7 @@
<div class="cluster-settings-section"> <div class="cluster-settings-section">
<b>Prometheus</b> <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> <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 <b-form-group
label="Prometheus installation method." label="Prometheus installation method."
description="What query format is used to fetch metrics from Prometheus" description="What query format is used to fetch metrics from Prometheus"
@ -41,6 +31,18 @@
@change="onPrometheusProviderSave" @change="onPrometheusProviderSave"
/> />
</b-form-group> </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> </div>
<div class="col-12"> <div class="col-12">
@ -84,13 +86,25 @@ export default {
}, },
prometheusPath: "", prometheusPath: "",
prometheusProvider: "", 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() { mounted: async function() {
this.prometheusProviders = prometheusProviders.map((provider) => {
return { text: provider.name, value: provider.id }
})
this.updateValues() this.updateValues()
}, },
methods: { methods: {
@ -104,7 +118,7 @@ export default {
if (this.cluster.preferences.prometheusProvider) { if (this.cluster.preferences.prometheusProvider) {
this.prometheusProvider = this.cluster.preferences.prometheusProvider.type this.prometheusProvider = this.cluster.preferences.prometheusProvider.type
} else { } else {
this.prometheusProvider = "lens" this.prometheusProvider = ""
} }
}, },
parsePrometheusPath: function(path) { parsePrometheusPath: function(path) {