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

Add setting for filtering empty containers on Prometheus queries

Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
This commit is contained in:
Juho Heikka 2023-05-25 13:59:29 +03:00
parent dd62e034b7
commit f1974afac4
4 changed files with 46 additions and 6 deletions

View File

@ -112,6 +112,10 @@ export interface ClusterPreferences extends ClusterPrometheusPreferences {
defaultNamespace?: string; defaultNamespace?: string;
} }
interface QueryFilterOptions {
hideEmptyContainers: boolean;
}
/** /**
* A cluster's prometheus settings (a subset of cluster settings) * A cluster's prometheus settings (a subset of cluster settings)
*/ */
@ -125,6 +129,7 @@ export interface ClusterPrometheusPreferences {
prometheusProvider?: { prometheusProvider?: {
type: string; type: string;
}; };
prometheusQueryOptions?: QueryFilterOptions;
} }
/** /**

View File

@ -9,6 +9,9 @@ import { getInjectable } from "@ogre-tools/injectable";
export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => ( export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => (
(opts, queryName) => { (opts, queryName) => {
const emptyContainerAndImageFilter = opts.hideEmptyContainers === "true" ? `container!="", image!="",` : "";
const emptyContainerFilter = opts.hideEmptyContainers === "true" ? `container!="",` : "";
switch(opts.category) { switch(opts.category) {
case "cluster": case "cluster":
switch (queryName) { switch (queryName) {
@ -71,19 +74,19 @@ export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string
case "pods": case "pods":
switch (queryName) { switch (queryName) {
case "cpuUsage": case "cpuUsage":
return `sum(rate(container_cpu_usage_seconds_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`; return `sum(rate(container_cpu_usage_seconds_total{${emptyContainerAndImageFilter} pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
case "cpuRequests": case "cpuRequests":
return `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}", resource="cpu", namespace="${opts.namespace}"}) by (${opts.selector})`; return `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}", resource="cpu", namespace="${opts.namespace}"}) by (${opts.selector})`;
case "cpuLimits": case "cpuLimits":
return `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}", resource="cpu", namespace="${opts.namespace}"}) by (${opts.selector})`; return `sum(kube_pod_container_resource_limits{${emptyContainerAndImageFilter} pod=~"${opts.pods}", resource="cpu", namespace="${opts.namespace}"}) by (${opts.selector})`;
case "memoryUsage": case "memoryUsage":
return `sum(container_memory_working_set_bytes{pod=~"${opts.pods}", namespace="${opts.namespace}"}) by (${opts.selector})`; return `sum(container_memory_working_set_bytes{pod=~"${opts.pods}", namespace="${opts.namespace}"}) by (${opts.selector})`;
case "memoryRequests": case "memoryRequests":
return `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}", resource="memory", namespace="${opts.namespace}"}) by (${opts.selector})`; return `sum(kube_pod_container_resource_requests{${emptyContainerFilter} pod=~"${opts.pods}", resource="memory", namespace="${opts.namespace}"}) by (${opts.selector})`;
case "memoryLimits": case "memoryLimits":
return `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}", resource="memory", namespace="${opts.namespace}"}) by (${opts.selector})`; return `sum(kube_pod_container_resource_limits{${emptyContainerFilter} pod=~"${opts.pods}", resource="memory", namespace="${opts.namespace}"}) by (${opts.selector})`;
case "fsUsage": case "fsUsage":
return `sum(container_fs_usage_bytes{pod=~"${opts.pods}", namespace="${opts.namespace}"}) by (${opts.selector})`; return `sum(container_fs_usage_bytes{${emptyContainerFilter} pod=~"${opts.pods}", namespace="${opts.namespace}"}) by (${opts.selector})`;
case "fsWrites": case "fsWrites":
return `sum(rate(container_fs_writes_bytes_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`; return `sum(rate(container_fs_writes_bytes_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
case "fsReads": case "fsReads":

View File

@ -97,9 +97,13 @@ const addMetricsRouteInjectable = getRouteInjectable({
if (isObject(payload)) { if (isObject(payload)) {
const data = payload as Record<string, Record<string, string>>; const data = payload as Record<string, Record<string, string>>;
const queryFilterPreferences: Record<string, string> = cluster.preferences.prometheusQueryOptions ?
Object.fromEntries(Object.entries(cluster.preferences.prometheusQueryOptions).map(([k, v]) => [k, String(v)]))
: {};
const queries = object.entries(data) const queries = object.entries(data)
.map(([queryName, queryOpts]) => ( .map(([queryName, queryOpts]) => (
provider.getQuery(queryOpts, queryName) provider.getQuery({ ...queryOpts, ...queryFilterPreferences }, queryName)
)); ));
const result = await loadMetrics(queries, cluster, prometheusPath, queryParams); const result = await loadMetrics(queries, cluster, prometheusPath, queryParams);

View File

@ -16,6 +16,9 @@ import type { MetricProviderInfo, RequestMetricsProviders } from "../../../commo
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import requestMetricsProvidersInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-providers.injectable"; import requestMetricsProvidersInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-providers.injectable";
import productNameInjectable from "../../../common/vars/product-name.injectable"; import productNameInjectable from "../../../common/vars/product-name.injectable";
import { Checkbox } from "../checkbox";
import Gutter from "../gutter/gutter";
export interface ClusterPrometheusSettingProps { export interface ClusterPrometheusSettingProps {
cluster: Cluster; cluster: Cluster;
@ -123,6 +126,9 @@ class NonInjectedClusterPrometheusSetting extends React.Component<ClusterPrometh
}; };
render() { render() {
const providerType = this.props.cluster.preferences.prometheusProvider?.type;
const showQueryFilters = providerType === "operator" || !providerType;
return ( return (
<> <>
<section> <section>
@ -147,6 +153,28 @@ class NonInjectedClusterPrometheusSetting extends React.Component<ClusterPrometh
) )
} }
</section> </section>
{
showQueryFilters && (
<>
<Gutter />
<section>
<SubTitle title="Prometheus queries" />
<Checkbox
label="Filter empty containers on pod metrics"
value={this.props.cluster.preferences.prometheusQueryOptions?.hideEmptyContainers ?? false}
onChange={v => {
this.props.cluster.preferences.prometheusQueryOptions = {
hideEmptyContainers: v,
};
}}
/>
<small className="hint">
In certain metric setups, pod metrics may be observed as double values. This filter can be helpful in ensuring accurate metric are shown.
</small>
</section>
</>
)
}
{this.canEditPrometheusPath && ( {this.canEditPrometheusPath && (
<> <>
<hr /> <hr />