From 373e0bebdc0959da327344fd8f1b556704c3d300 Mon Sep 17 00:00:00 2001 From: YanJin Date: Wed, 18 Mar 2026 16:17:45 +0100 Subject: [PATCH 01/10] MK8S-197 - Add PrometheusAuthProvider --- ui/src/FederableApp.tsx | 13 ++++++----- ui/src/containers/PrometheusAuthProvider.tsx | 23 ++++++++++++++++++++ ui/src/services/ApiClient.ts | 2 +- ui/src/services/prometheus/api.ts | 6 +++++ 4 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 ui/src/containers/PrometheusAuthProvider.tsx diff --git a/ui/src/FederableApp.tsx b/ui/src/FederableApp.tsx index 8f898033c2..85556010e8 100644 --- a/ui/src/FederableApp.tsx +++ b/ui/src/FederableApp.tsx @@ -6,6 +6,7 @@ import { applyMiddleware, compose, createStore, Store } from 'redux'; import createSagaMiddleware from 'redux-saga'; import 'regenerator-runtime/runtime'; import App from './containers/App'; +import PrometheusAuthProvider from './containers/PrometheusAuthProvider'; import { authErrorAction } from './ducks/app/authError'; import { setApiConfigAction } from './ducks/config'; import { setHistory as setReduxHistory } from './ducks/history'; @@ -125,11 +126,13 @@ export default function FederableApp(props: FederatedAppProps) { > - - - - - + + + + + + + diff --git a/ui/src/containers/PrometheusAuthProvider.tsx b/ui/src/containers/PrometheusAuthProvider.tsx new file mode 100644 index 0000000000..62b8ccc920 --- /dev/null +++ b/ui/src/containers/PrometheusAuthProvider.tsx @@ -0,0 +1,23 @@ +import { ReactNode, useEffect, useState } from 'react'; +import { useAuth } from './PrivateRoute'; +import { setHeaders } from '../services/prometheus/api'; + +export default function PrometheusAuthProvider({ + children, +}: { + children: ReactNode; +}) { + const { userData } = useAuth(); + const [ready, setReady] = useState(false); + + useEffect(() => { + if (userData?.token) { + setHeaders({ Authorization: `Bearer ${userData.token}` }); + setReady(true); + } + }, [userData?.token]); + + if (!ready) return null; + + return <>{children}; +} diff --git a/ui/src/services/ApiClient.ts b/ui/src/services/ApiClient.ts index ba3c317ced..f96ba8ded6 100644 --- a/ui/src/services/ApiClient.ts +++ b/ui/src/services/ApiClient.ts @@ -12,7 +12,7 @@ class ApiClient { setHeaders = (headers) => { // @ts-expect-error - FIXME when you are working on it - this.headers = headers; + this.headers = { ...this.headers, ...headers }; }; async get(endpoint, params = {}, opts = {}) { diff --git a/ui/src/services/prometheus/api.ts b/ui/src/services/prometheus/api.ts index 21bb237fd2..28b628be31 100644 --- a/ui/src/services/prometheus/api.ts +++ b/ui/src/services/prometheus/api.ts @@ -51,6 +51,12 @@ export function initialize(apiUrl: string) { prometheusApiClient = new ApiClient({ apiUrl }); } +export function setHeaders(headers: Record) { + if (prometheusApiClient) { + prometheusApiClient.setHeaders(headers); + } +} + export function getAlerts() { if (prometheusApiClient) { return prometheusApiClient.get('/api/v1/alerts'); From a01f236a86105da2a44207bfc4c7ef6879fc1dc5 Mon Sep 17 00:00:00 2001 From: YanJin Date: Thu, 19 Mar 2026 15:00:46 +0100 Subject: [PATCH 02/10] MK8S-197 - Fix the type issue of ApiClient --- ui/src/services/ApiClient.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/ui/src/services/ApiClient.ts b/ui/src/services/ApiClient.ts index f96ba8ded6..069aa5861a 100644 --- a/ui/src/services/ApiClient.ts +++ b/ui/src/services/ApiClient.ts @@ -1,17 +1,23 @@ import axios from 'axios'; class ApiClient { - constructor({ apiUrl, headers = {} }) { - // @ts-expect-error - FIXME when you are working on it + headers: Record; + settings: { baseURL: string }; + + constructor({ + apiUrl, + headers = {}, + }: { + apiUrl: string; + headers?: Record; + }) { this.headers = headers; - // @ts-expect-error - FIXME when you are working on it this.settings = { baseURL: apiUrl, }; } - setHeaders = (headers) => { - // @ts-expect-error - FIXME when you are working on it + setHeaders = (headers: Record) => { this.headers = { ...this.headers, ...headers }; }; @@ -72,12 +78,10 @@ class ApiClient { try { const response = await axios({ method, - // @ts-expect-error - FIXME when you are working on it headers: { ...this.headers, ...headers }, params, url: endpoint, data: payload, - // @ts-expect-error - FIXME when you are working on it ...this.settings, }); return response.data; From 27d8a16e3bd192d6c78e529710207e25fdc64c5f Mon Sep 17 00:00:00 2001 From: YanJin Date: Thu, 19 Mar 2026 17:06:09 +0100 Subject: [PATCH 03/10] MK8S-197 - Pass access token to getAlerts in Shell-UI --- shell-ui/src/alerts/AlertProvider.tsx | 20 +++++++++++++------- shell-ui/src/alerts/services/alertManager.ts | 6 ++++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/shell-ui/src/alerts/AlertProvider.tsx b/shell-ui/src/alerts/AlertProvider.tsx index fb05e85c5b..4a160d62ec 100644 --- a/shell-ui/src/alerts/AlertProvider.tsx +++ b/shell-ui/src/alerts/AlertProvider.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { useQuery } from 'react-query'; import { Loader } from '@scality/core-ui/dist/components/loader/Loader.component'; +import { useAuth } from '../auth/AuthProvider'; import { getAlerts } from './services/alertManager'; import { AlertContext } from './alertContext'; /** @@ -18,13 +19,18 @@ export default function AlertProvider({ alertManagerUrl: string; children: React.ReactNode; }) { - const query = useQuery('activeAlerts', () => getAlerts(alertManagerUrl), { - // refetch the alerts every 10 seconds - refetchInterval: 30000, - // TODO manage this refresh interval globally - // avoid stucking at the hard loading state before alertmanager is ready - initialData: [], - }); + const { userData } = useAuth(); + const query = useQuery( + ['activeAlerts', userData?.token], + () => getAlerts(alertManagerUrl, userData?.token), + { + // refetch the alerts every 30 seconds + refetchInterval: 30000, + // TODO manage this refresh interval globally + // avoid stucking at the hard loading state before alertmanager is ready + initialData: [], + }, + ); return ( {query.status === 'loading' && ( diff --git a/shell-ui/src/alerts/services/alertManager.ts b/shell-ui/src/alerts/services/alertManager.ts index 2f1d928b70..9b6ca5fcb8 100644 --- a/shell-ui/src/alerts/services/alertManager.ts +++ b/shell-ui/src/alerts/services/alertManager.ts @@ -29,8 +29,10 @@ export type AlertLabels = { selectors?: string[]; [labelName: string]: string; }; -export function getAlerts(alertManagerUrl: string) { - return fetch(alertManagerUrl + '/api/v2/alerts') +export function getAlerts(alertManagerUrl: string, token?: string) { + return fetch(alertManagerUrl + '/api/v2/alerts', { + headers: token ? { Authorization: `Bearer ${token}` } : {}, + }) .then((r) => { if (r.ok) { return r.json(); From df413a9bd080d5a7432789445ff3a2fba72fff5a Mon Sep 17 00:00:00 2001 From: YanJin Date: Mon, 23 Mar 2026 15:45:23 +0100 Subject: [PATCH 04/10] MK8S-197 - Fix the bug to display UI regardless of loading alerts successfully --- ui/src/containers/VolumePageRSP.tsx | 2 +- ui/src/hooks.tsx | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/ui/src/containers/VolumePageRSP.tsx b/ui/src/containers/VolumePageRSP.tsx index 2af2e2be97..3d60a7692a 100644 --- a/ui/src/containers/VolumePageRSP.tsx +++ b/ui/src/containers/VolumePageRSP.tsx @@ -54,7 +54,7 @@ export const VolumePageRSP = (props) => { const alertsVolume = useAlerts({ persistentvolumeclaim: PVCName, }); - const alertlist = alertsVolume && alertsVolume.alerts; + const alertlist = alertsVolume?.alerts ?? []; const criticalAlerts = alertlist.filter( (alert) => alert.severity === 'critical', ); diff --git a/ui/src/hooks.tsx b/ui/src/hooks.tsx index 4800688c14..1e0683cdf6 100644 --- a/ui/src/hooks.tsx +++ b/ui/src/hooks.tsx @@ -121,13 +121,9 @@ export const useVolumesWithAlerts = (nodeName?: string) => { // @ts-expect-error - FIXME when you are working on it getVolumeListData(state, null, nodeName), ); - //This forces alerts to have been fetched at least once (watchdog alert should be present) - // before rendering volume list - // TODO enhance this using useAlerts status - if (!alerts || alerts.length === 0) return []; // @ts-expect-error - FIXME when you are working on it const volumeListWithStatus = volumeListData.map((volume) => { - const volumeAlerts = filterAlerts(alerts, { + const volumeAlerts = filterAlerts(alerts || [], { persistentvolumeclaim: volume.persistentvolumeclaim, }); // For the unbound volume, the health status should be none. From 2343fea4e3847022ada7ee0adbcddb2047361ba1 Mon Sep 17 00:00:00 2001 From: YanJin Date: Mon, 23 Mar 2026 16:35:59 +0100 Subject: [PATCH 05/10] MK8S-197 - Add banner in case of failed to retrieve alerts --- ui/src/components/DashboardAlerts.test.tsx | 18 +++++++ ui/src/components/DashboardAlerts.tsx | 58 +++++++++++++++------- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/ui/src/components/DashboardAlerts.test.tsx b/ui/src/components/DashboardAlerts.test.tsx index fca56d9fd1..f7f38ee4fe 100644 --- a/ui/src/components/DashboardAlerts.test.tsx +++ b/ui/src/components/DashboardAlerts.test.tsx @@ -99,6 +99,24 @@ describe('the dashboard alerts sub-panel', () => { expect(queryByTestId('critical-alert-badge')).not.toBeInTheDocument(); expect(queryByTestId('view-all-link')).not.toBeInTheDocument(); }); + test('should display a warning banner when alerts fail to fetch', () => { + (useAlertLibrary as any).mockImplementation(() => ({ + getPlatformAlertSelectors: () => ({ + alertname: ['ClusterAtRisk', 'ClusterDegraded'], + }), + })); + (useAlerts as any).mockImplementation(() => ({ + alerts: [], + error: new Error('Alert manager responded with 401'), + })); + const { getByText } = render(); + expect( + getByText('Monitoring information unavailable'), + ).toBeInTheDocument(); + expect( + getByText('Some data can not be well retrieved'), + ).toBeInTheDocument(); + }); test('should redirect to alert page with View All link', () => { (useAlerts as any).mockImplementation(() => ({ alerts: alerts, diff --git a/ui/src/components/DashboardAlerts.tsx b/ui/src/components/DashboardAlerts.tsx index 534d3d2562..a81a5e9d2d 100644 --- a/ui/src/components/DashboardAlerts.tsx +++ b/ui/src/components/DashboardAlerts.tsx @@ -1,4 +1,4 @@ -import { spacing, Text, TextBadge } from '@scality/core-ui'; +import { Banner, Icon, spacing, Text, TextBadge } from '@scality/core-ui'; import { Box } from '@scality/core-ui/dist/next'; import { useMemo } from 'react'; import { useIntl } from 'react-intl'; @@ -61,25 +61,45 @@ const DashboardAlerts = () => { const totalAlerts = criticalAlerts.length + warningAlerts.length; return ( -
- - {intl.formatMessage({ - id: 'platform_active_alerts', - })} - - +
+
+ + {intl.formatMessage({ + id: 'platform_active_alerts', + })} + + + {totalAlerts === 0 && ( +
+ + {intl.formatMessage({ + id: 'no_active_alerts', + })} + +
+ )} +
+ {alerts.error && ( +
+ } + title={intl.formatMessage({ + id: 'monitoring_information_unavailable', + })} + > + {intl.formatMessage({ + id: 'some_data_not_retrieved', + })} + +
+ )}
- {totalAlerts === 0 ? ( - - {intl.formatMessage({ - id: 'no_active_alerts', - })} - - ) : ( + {totalAlerts === 0 ? null : (
From 99ecc6cc2f3f4a036cbb728dd50c57d1e0c00dd5 Mon Sep 17 00:00:00 2001 From: YanJin Date: Mon, 23 Mar 2026 16:56:28 +0100 Subject: [PATCH 06/10] MK8S-197 - Display the alert status in Alert page --- ui/src/containers/AlertPage.tsx | 35 ++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/ui/src/containers/AlertPage.tsx b/ui/src/containers/AlertPage.tsx index 8c44391280..a3b811e14a 100644 --- a/ui/src/containers/AlertPage.tsx +++ b/ui/src/containers/AlertPage.tsx @@ -20,6 +20,8 @@ import { STATUS_CRITICAL, STATUS_HEALTH, STATUS_WARNING } from '../constants'; import { useUserAccessRight } from '../hooks'; import { compareHealth } from '../services/utils'; import { useAlerts } from './AlertProvider'; +import type { Alert } from '../services/alertUtils'; +import type { QueryStatus } from 'react-query'; import { useBasenameRelativeNavigate } from '@scality/module-federation'; const AlertPageHeaderContainer = styled.div` @@ -94,8 +96,8 @@ const getAlertStatus = (numbersOfCritical, numbersOfWarning) => numbersOfCritical > 0 ? STATUS_CRITICAL : numbersOfWarning > 0 - ? STATUS_WARNING - : STATUS_HEALTH; + ? STATUS_WARNING + : STATUS_HEALTH; function AlertPageHeader({ activeAlerts, @@ -118,7 +120,7 @@ function AlertPageHeader({ <AlertStatusIcon> <StatusWrapper status={alertStatus}> - <StatusIcon status={alertStatus} name="Alert" entity='Alerts' /> + <StatusIcon status={alertStatus} name="Alert" entity="Alerts" /> </StatusWrapper> </AlertStatusIcon> <> @@ -163,9 +165,14 @@ function AlertPageHeader({ ); } +type ActiveAlertTabProps = { + columns: Record<string, unknown>[]; + data: Alert[]; + status: QueryStatus; +}; + const ActiveAlertTab = React.memo( - // @ts-expect-error - FIXME when you are working on it - ({ columns, data }) => { + ({ columns, data, status }: ActiveAlertTabProps) => { const sortTypes = React.useMemo(() => { return { severity: (row1, row2) => { @@ -197,6 +204,7 @@ const ActiveAlertTab = React.memo( data={data} defaultSortingKey={DEFAULT_SORTING_KEY} sortTypes={sortTypes} + status={status} entityName={{ en: { singular: 'active alert', @@ -219,10 +227,12 @@ const ActiveAlertTab = React.memo( </Table> ); }, - (a, b) => { - // compare the alert only on id and severity - // @ts-expect-error - FIXME when you are working on it - return isEqual(a.columns, b.columns) && isEqualAlert(a.data, b.data); + (prevProps: ActiveAlertTabProps, nextProps: ActiveAlertTabProps) => { + return ( + isEqual(prevProps.columns, nextProps.columns) && + isEqualAlert(prevProps.data, nextProps.data) && + prevProps.status === nextProps.status + ); }, ); export default function AlertPage() { @@ -295,8 +305,11 @@ export default function AlertPage() { /> </AppContainer.OverallSummary> <AppContainer.MainContent> - {/* @ts-expect-error - FIXME when you are working on it */} - <ActiveAlertTab data={leafAlerts} columns={columns} /> + <ActiveAlertTab + data={leafAlerts} + columns={columns} + status={alerts.status} + /> </AppContainer.MainContent> </AppContainer> ); From c15b1d02016f2f36f766afb1427146ac14df06f6 Mon Sep 17 00:00:00 2001 From: YanJin <yanjin.cheng@scality.com> Date: Tue, 24 Mar 2026 17:34:25 +0100 Subject: [PATCH 07/10] MK8S-197 - Always pass token to alertmanager --- shell-ui/src/alerts/AlertProvider.tsx | 7 +++---- shell-ui/src/alerts/alertHooks.test.tsx | 10 ++++++++++ shell-ui/src/alerts/services/alertManager.ts | 4 ++-- ui/src/components/DashboardAlerts.tsx | 4 ++-- ui/src/containers/PrometheusAuthProvider.tsx | 19 ++++++++++++------- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/shell-ui/src/alerts/AlertProvider.tsx b/shell-ui/src/alerts/AlertProvider.tsx index 4a160d62ec..1f013f635e 100644 --- a/shell-ui/src/alerts/AlertProvider.tsx +++ b/shell-ui/src/alerts/AlertProvider.tsx @@ -19,12 +19,11 @@ export default function AlertProvider({ alertManagerUrl: string; children: React.ReactNode; }) { - const { userData } = useAuth(); + const { getToken } = useAuth(); const query = useQuery( - ['activeAlerts', userData?.token], - () => getAlerts(alertManagerUrl, userData?.token), + ['activeAlerts'], + async () => getAlerts(alertManagerUrl, await getToken()), { - // refetch the alerts every 30 seconds refetchInterval: 30000, // TODO manage this refresh interval globally // avoid stucking at the hard loading state before alertmanager is ready diff --git a/shell-ui/src/alerts/alertHooks.test.tsx b/shell-ui/src/alerts/alertHooks.test.tsx index 45f81611a4..e5ac68fd23 100644 --- a/shell-ui/src/alerts/alertHooks.test.tsx +++ b/shell-ui/src/alerts/alertHooks.test.tsx @@ -7,6 +7,16 @@ import AlertProvider from './AlertProvider'; import { useHighestSeverityAlerts } from './alertHooks'; import { afterAll, beforeAll, jest } from '@jest/globals'; import { QueryClientProvider } from '../QueryClientProvider'; + +jest.mock('../auth/AuthProvider', () => ({ + useAuth: () => ({ + userData: { + token: 'test-token', + }, + getToken: () => Promise.resolve('test-token'), + }), +})); + const testService = 'http://10.0.0.1/api/alertmanager'; const VOLUME_DEGRADED_ALERT = { diff --git a/shell-ui/src/alerts/services/alertManager.ts b/shell-ui/src/alerts/services/alertManager.ts index 9b6ca5fcb8..83de6d9680 100644 --- a/shell-ui/src/alerts/services/alertManager.ts +++ b/shell-ui/src/alerts/services/alertManager.ts @@ -29,9 +29,9 @@ export type AlertLabels = { selectors?: string[]; [labelName: string]: string; }; -export function getAlerts(alertManagerUrl: string, token?: string) { +export function getAlerts(alertManagerUrl: string, token: string) { return fetch(alertManagerUrl + '/api/v2/alerts', { - headers: token ? { Authorization: `Bearer ${token}` } : {}, + headers: { Authorization: `Bearer ${token}` }, }) .then((r) => { if (r.ok) { diff --git a/ui/src/components/DashboardAlerts.tsx b/ui/src/components/DashboardAlerts.tsx index a81a5e9d2d..50d82e3fcb 100644 --- a/ui/src/components/DashboardAlerts.tsx +++ b/ui/src/components/DashboardAlerts.tsx @@ -61,7 +61,7 @@ const DashboardAlerts = () => { const totalAlerts = criticalAlerts.length + warningAlerts.length; return ( <AlertsContainer> - <div style={{ display: 'flex', alignItems: 'center', gap: spacing.r8 }}> + <Box display="flex" alignItems="center" gap={spacing.r8}> <div> <Text isEmphazed> {intl.formatMessage({ @@ -98,7 +98,7 @@ const DashboardAlerts = () => { </Banner> </div> )} - </div> + </Box> {totalAlerts === 0 ? null : ( <Box pr={24}> <BadgesContainer> diff --git a/ui/src/containers/PrometheusAuthProvider.tsx b/ui/src/containers/PrometheusAuthProvider.tsx index 62b8ccc920..92e3dfd541 100644 --- a/ui/src/containers/PrometheusAuthProvider.tsx +++ b/ui/src/containers/PrometheusAuthProvider.tsx @@ -1,23 +1,28 @@ -import { ReactNode, useEffect, useState } from 'react'; +import { ReactNode, useEffect } from 'react'; import { useAuth } from './PrivateRoute'; import { setHeaders } from '../services/prometheus/api'; +// Temporary solution: The Prometheus API client is initialized with the URL in the saga +// (setApiConfig), but the auth token is not available at that point. Rather than modifying +// the saga (which will be removed soon), this provider sets the auth header on the shared +// Prometheus client once the token is available, and blocks rendering until it's ready. +// TODO: Remove this provider once the saga is removed. + export default function PrometheusAuthProvider({ children, }: { children: ReactNode; }) { const { userData } = useAuth(); - const [ready, setReady] = useState(false); + const token = userData?.token; useEffect(() => { - if (userData?.token) { - setHeaders({ Authorization: `Bearer ${userData.token}` }); - setReady(true); + if (token) { + setHeaders({ Authorization: `Bearer ${token}` }); } - }, [userData?.token]); + }, [token]); - if (!ready) return null; + if (!token) return null; return <>{children}</>; } From f7ff2bc5bdcf3cc412cd199f73b41854b1593a45 Mon Sep 17 00:00:00 2001 From: YanJin <yanjin.cheng@scality.com> Date: Fri, 27 Mar 2026 11:14:08 +0100 Subject: [PATCH 08/10] MK8S-197 - Remove checkActiveAlertProvider which is not used anymore --- shell-ui/src/alerts/services/alertManager.ts | 19 ++----------------- ui/src/services/alertmanager/api.ts | 14 -------------- 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/shell-ui/src/alerts/services/alertManager.ts b/shell-ui/src/alerts/services/alertManager.ts index 83de6d9680..01e80ce157 100644 --- a/shell-ui/src/alerts/services/alertManager.ts +++ b/shell-ui/src/alerts/services/alertManager.ts @@ -2,8 +2,6 @@ import { removeWarningAlerts, formatActiveAlerts, sortAlerts, - STATUS_CRITICAL, - STATUS_HEALTH, } from './alertUtils'; export type PrometheusAlert = { annotations: Record<string, string>; @@ -29,9 +27,9 @@ export type AlertLabels = { selectors?: string[]; [labelName: string]: string; }; -export function getAlerts(alertManagerUrl: string, token: string) { +export function getAlerts(alertManagerUrl: string, token?: string) { return fetch(alertManagerUrl + '/api/v2/alerts', { - headers: { Authorization: `Bearer ${token}` }, + headers: token ? { Authorization: `Bearer ${token}` } : {}, }) .then((r) => { if (r.ok) { @@ -52,16 +50,3 @@ export function getAlerts(alertManagerUrl: string, token: string) { return sortAlerts(removeWarningAlerts(formatActiveAlerts(result))); }); } -export const checkActiveAlertProvider = (): Promise<{ - status: 'healthy' | 'critical'; -}> => { - // depends on Watchdog to see the if Alertmanager is up - // @ts-expect-error - FIXME when you are working on it - return getAlerts().then((result) => { - const watchdog = result.find( - (alert) => alert.labels.alertname === 'Watchdog', - ); - if (watchdog) return STATUS_HEALTH; - else return STATUS_CRITICAL; - }); -}; diff --git a/ui/src/services/alertmanager/api.ts b/ui/src/services/alertmanager/api.ts index 6538f35dc5..77bc551e31 100644 --- a/ui/src/services/alertmanager/api.ts +++ b/ui/src/services/alertmanager/api.ts @@ -1,5 +1,4 @@ import ApiClient from '../ApiClient'; -import { STATUS_CRITICAL, STATUS_HEALTH } from '../../constants'; import { removeWarningAlerts, formatActiveAlerts, @@ -52,16 +51,3 @@ export function getAlerts() { return sortAlerts(removeWarningAlerts(formatActiveAlerts(result))); }); } -export const checkActiveAlertProvider = (): Promise<{ - status: 'healthy' | 'critical'; -}> => { - // depends on Watchdog to see the if Alertmanager is up - // @ts-expect-error - FIXME when you are working on it - return getAlerts().then((result) => { - const watchdog = result.find( - (alert) => alert.labels.alertname === 'Watchdog', - ); - if (watchdog) return STATUS_HEALTH; - else return STATUS_CRITICAL; - }); -}; From 63f0757175150ccfdb6312f8b812a2531b6ccd33 Mon Sep 17 00:00:00 2001 From: YanJin <yanjin.cheng@scality.com> Date: Fri, 27 Mar 2026 11:33:44 +0100 Subject: [PATCH 09/10] MK8S-197 - Remove saga init dead code --- ui/src/components/NodePartitionTable.test.tsx | 3 -- ui/src/ducks/config.ts | 2 -- ui/src/services/alertmanager/api.ts | 31 ------------------- 3 files changed, 36 deletions(-) diff --git a/ui/src/components/NodePartitionTable.test.tsx b/ui/src/components/NodePartitionTable.test.tsx index b6a3d2c4b0..34cf32a70a 100644 --- a/ui/src/components/NodePartitionTable.test.tsx +++ b/ui/src/components/NodePartitionTable.test.tsx @@ -4,7 +4,6 @@ import { rest } from 'msw'; import NodePartitionTable from './NodePartitionTable'; import { render, FAKE_CONTROL_PLANE_IP } from './__TEST__/util'; import { initialize as initializeProm } from '../services/prometheus/api'; -import { initialize as initializeAM } from '../services/alertmanager/api'; import { initialize as initializeLoki } from '../services/loki/api'; import { mockOffsetSize } from '../tests/mocks/util'; import { useAlerts } from '../containers/AlertProvider'; @@ -338,7 +337,6 @@ describe('the system partition table', () => { }); // Setup initializeProm(`http://${FAKE_CONTROL_PLANE_IP}:8443/api/prometheus`); - initializeAM(`http://${FAKE_CONTROL_PLANE_IP}:8443/api/alertmanager`); initializeLoki(`http://${FAKE_CONTROL_PLANE_IP}:8443/api/loki`); render(<NodePartitionTable instanceIP={'192.168.1.29'} />); expect( @@ -361,7 +359,6 @@ describe('the system partition table', () => { test('handles server error', async () => { // S initializeProm(`http://${FAKE_CONTROL_PLANE_IP}:8443/api/prometheus`); - initializeAM(`http://${FAKE_CONTROL_PLANE_IP}:8443/api/alertmanager`); initializeLoki(`http://${FAKE_CONTROL_PLANE_IP}:8443/api/loki`); // override the default route with error status server.use( diff --git a/ui/src/ducks/config.ts b/ui/src/ducks/config.ts index 7444463ec0..1482caf032 100644 --- a/ui/src/ducks/config.ts +++ b/ui/src/ducks/config.ts @@ -6,7 +6,6 @@ import * as Api from '../services/api'; import * as ApiK8s from '../services/k8s/api'; import * as ApiSalt from '../services/salt/api'; import * as ApiPrometheus from '../services/prometheus/api'; -import * as ApiAlertmanager from '../services/alertmanager/api'; import * as ApiLoki from '../services/loki/api'; import { EN_LANG } from '../constants'; import { authenticateSaltApi } from './login'; @@ -165,7 +164,6 @@ function* setApiConfig({ }): Generator<Effect, void, Result<Config>> { yield call(ApiSalt.initialize, config.url_salt); yield call(ApiPrometheus.initialize, config.url_prometheus); - yield call(ApiAlertmanager.initialize, config.url_alertmanager); yield call(ApiLoki.initialize, config.url_loki); yield put(setConfigStatusAction('success')); } diff --git a/ui/src/services/alertmanager/api.ts b/ui/src/services/alertmanager/api.ts index 77bc551e31..8308e9e5ea 100644 --- a/ui/src/services/alertmanager/api.ts +++ b/ui/src/services/alertmanager/api.ts @@ -1,15 +1,3 @@ -import ApiClient from '../ApiClient'; -import { - removeWarningAlerts, - formatActiveAlerts, - sortAlerts, -} from '../alertUtils'; -let alertmanagerApiClient: ApiClient | null | undefined = null; -export function initialize(apiUrl: string) { - alertmanagerApiClient = new ApiClient({ - apiUrl, - }); -} export type PrometheusAlert = { annotations: Record<string, string>; receivers: { @@ -32,22 +20,3 @@ export type AlertLabels = { selectors?: string[]; [labelName: string]: string; }; -export function getAlerts() { - if (!alertmanagerApiClient) { - throw new Error('alertmanagerApiClient should be defined'); - } - - return alertmanagerApiClient - .get('/api/v2/alerts') - .then((resolve) => { - if (resolve.error) { - throw resolve.error; - } - - return resolve; - }) - .then((result) => { - // format the alerts then remove the warning and finally sort the alerts. - return sortAlerts(removeWarningAlerts(formatActiveAlerts(result))); - }); -} From 8aa0b4d06aea3164080c4a84a7d9b248305773c4 Mon Sep 17 00:00:00 2001 From: YanJin <yanjin.cheng@scality.com> Date: Fri, 27 Mar 2026 12:03:17 +0100 Subject: [PATCH 10/10] MK8S-197 - Fix minor issue --- ui/src/containers/PrometheusAuthProvider.tsx | 4 ++-- ui/src/services/prometheus/api.ts | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ui/src/containers/PrometheusAuthProvider.tsx b/ui/src/containers/PrometheusAuthProvider.tsx index 92e3dfd541..4203e777b3 100644 --- a/ui/src/containers/PrometheusAuthProvider.tsx +++ b/ui/src/containers/PrometheusAuthProvider.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useEffect } from 'react'; +import { ReactNode, useLayoutEffect } from 'react'; import { useAuth } from './PrivateRoute'; import { setHeaders } from '../services/prometheus/api'; @@ -16,7 +16,7 @@ export default function PrometheusAuthProvider({ const { userData } = useAuth(); const token = userData?.token; - useEffect(() => { + useLayoutEffect(() => { if (token) { setHeaders({ Authorization: `Bearer ${token}` }); } diff --git a/ui/src/services/prometheus/api.ts b/ui/src/services/prometheus/api.ts index 28b628be31..d998a861e3 100644 --- a/ui/src/services/prometheus/api.ts +++ b/ui/src/services/prometheus/api.ts @@ -54,6 +54,10 @@ export function initialize(apiUrl: string) { export function setHeaders(headers: Record<string, string>) { if (prometheusApiClient) { prometheusApiClient.setHeaders(headers); + } else { + console.warn( + 'setHeaders called before prometheusApiClient was initialized', + ); } }