Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prometheus: Add settings to prometheus plugin #87

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84,499 changes: 36,852 additions & 47,647 deletions prometheus/package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions prometheus/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "prometheus",
"name": "@headlamp-k8s/prometheus",
Copy link
Contributor

@illume illume Oct 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will affect how this is installed.

How to handle this? At minimum a note in the release notes (or in the README?) should warn people to remove their old prometheus plugin, what the old one is called and what the new folder name is called.

This could also affect settings/permissions for running plugins.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the app/ bundling will need to be changed to get the new plugin name too?

"version": "0.2.0",
"description": "Your Headlamp plugin",
"description": "Prometheus plugin for Headlamp",
"scripts": {
"start": "headlamp-plugin start",
"build": "headlamp-plugin build",
Expand Down Expand Up @@ -30,7 +30,7 @@
]
},
"devDependencies": {
"@kinvolk/headlamp-plugin": "^0.8.0-alpha.13"
"@kinvolk/headlamp-plugin": "^0.9.2"
},
"dependencies": {
"recharts": "^2.7.3",
Expand Down
36 changes: 28 additions & 8 deletions prometheus/src/VisibilityButton.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,55 @@
import { Icon } from '@iconify/react';

Check warning on line 1 in prometheus/src/VisibilityButton.tsx

View workflow job for this annotation

GitHub Actions / build (18.x, ./prometheus)

Run autofix to sort these imports!
import { useCluster } from '@kinvolk/headlamp-plugin/lib/k8s';
import { KubeObject } from '@kinvolk/headlamp-plugin/lib/K8s/cluster';
import { Tooltip } from '@mui/material';
import ToggleButton from '@mui/material/ToggleButton';
import React from 'react';
import { ChartEnabledKinds, usePluginSettings } from './util';
import React, { useEffect, useState } from 'react';
import { ChartEnabledKinds } from './util';
import { getConfigStore, disableMetrics, enableMetrics } from './util';

export interface VisibilityButtonProps {
resource?: KubeObject;
}

export default function VisibilityButton(props: VisibilityButtonProps) {
const { resource } = props;
const pluginSettings = usePluginSettings();
const cluster = useCluster();
const [isEnabled, setIsEnabled] = useState(false);
const configStore = getConfigStore();
const useClusterConfig = configStore.useConfig();
const clusterConfig = useClusterConfig();

useEffect(() => {
if (clusterConfig?.[cluster]?.isMetricsEnabled !== isEnabled) {
setIsEnabled(clusterConfig?.[cluster]?.isMetricsEnabled || false);
}
}, [clusterConfig]);

const [description, icon] = React.useMemo(() => {
if (pluginSettings.isVisible) {
if (isEnabled) {
return ['Hide Prometheus metrics', 'mdi:chart-box-outline'];
}
return ['Show Prometheus metrics', 'mdi:chart-box'];
}, [pluginSettings]);
}, [isEnabled]);

if (!ChartEnabledKinds.includes(resource?.jsonData?.kind)) {
return null;
}

const handleToggle = () => {
if (isEnabled) {
disableMetrics(cluster);
} else {
enableMetrics(cluster);
}
};

return (
<Tooltip title={description}>
<ToggleButton
aria-label={'description'}
onClick={() => pluginSettings.setIsVisible(visible => !visible)}
selected={pluginSettings.isVisible}
aria-label={description}
onClick={handleToggle}
selected={isEnabled}
size="small"
>
<Icon icon={icon} width="24px" />
Expand Down
1 change: 1 addition & 0 deletions prometheus/src/chart.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ const Template: Story<ChartProps> = () => {
dataProcessor: dataProcessor,
},
]}
interval="10m"
autoRefresh={false}
xAxisProps={XTickProps}
yAxisProps={YTickProps}
Expand Down
54 changes: 48 additions & 6 deletions prometheus/src/chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,49 @@ import { Box, useTheme } from '@mui/material';
import { useEffect, useState } from 'react';
import { Area, AreaChart, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';

/**
* Calculates the time range based on the given interval.
* @param {string} interval - The time interval (e.g., '10m', '1h', '24h', 'week').
* @returns {Object} An object containing the 'from' timestamp, 'to' timestamp, and 'step' in seconds.
*/
export function getTimeRange(interval: string): { from: number; to: number; step: number } {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there somewhere this is specified? A URL to some docs or code perhaps?

const now = Math.floor(Date.now() / 1000);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function could use some tests.

const day = 86400; // seconds in a day

switch (interval) {
case '10m':
return { from: now - 600, to: now, step: 15 }; // 15 seconds step
case '30m':
return { from: now - 1800, to: now, step: 30 }; // 30 seconds step
case '1h':
return { from: now - 3600, to: now, step: 60 }; // 1 minute step
case '3h':
return { from: now - 10800, to: now, step: 180 }; // 3 minutes step
case '6h':
return { from: now - 21600, to: now, step: 360 }; // 6 minutes step
case '12h':
return { from: now - 43200, to: now, step: 720 }; // 12 minutes step
case '24h':
return { from: now - day, to: now, step: 300 }; // 5 minutes step
case '48h':
return { from: now - 2 * day, to: now, step: 600 }; // 10 minutes step
case 'today':
return { from: now - (now % day), to: now, step: 300 }; // 5 minutes step
case 'yesterday':
return { from: now - (now % day) - day, to: now - (now % day), step: 300 }; // 5 minutes step
case 'week':
return { from: now - 7 * day, to: now, step: 3600 }; // 1 hour step
case 'lastweek':
return { from: now - 14 * day, to: now - 7 * day, step: 3600 }; // 1 hour step
case '7d':
return { from: now - 7 * day, to: now, step: 3600 }; // 1 hour step
case '14d':
return { from: now - 14 * day, to: now, step: 7200 }; // 2 hours step
default:
return { from: now - 600, to: now, step: 15 }; // Default to 10 minutes with 15 seconds step
}
}

export interface ChartProps {
plots: Array<{
query: string;
Expand All @@ -12,6 +55,7 @@ export interface ChartProps {
dataProcessor: (data: any) => any[];
}>;
fetchMetrics: (query: object) => Promise<any>;
interval: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please document these fields?

prometheusPrefix: string;
autoRefresh: boolean;
xAxisProps: {
Expand Down Expand Up @@ -40,9 +84,7 @@ export function Chart(props: ChartProps) {
plots: Array<{ query: string; name: string; dataProcessor: (data: any) => any }>,
firstLoad: boolean = false
) => {
const currentTime = Date.now() / 1000;

const tenMinutesBefore = currentTime - 10 * 60;
const { from, to, step } = getTimeRange(props.interval);

const fetchedMetrics: {
[key: string]: {
Expand All @@ -60,9 +102,9 @@ export function Chart(props: ChartProps) {
response = await fetchMetrics({
prefix: props.prometheusPrefix,
query: plot.query,
from: tenMinutesBefore,
to: currentTime,
step: 2,
from: from,
to: to,
step: step,
});
} catch (e) {
fetchedMetrics[plot.name] = { data: [], state: ChartState.ERROR };
Expand Down
Loading
Loading