mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: hide "silence" button for external AM setups (#62133)
This commit is contained in:
parent
a190e03133
commit
26866953c1
@ -148,8 +148,15 @@ func (srv ConfigSrv) RouteGetAlertingStatus(c *contextmodel.ReqContext) response
|
|||||||
sendsAlertsTo = cfg.SendAlertsTo
|
sendsAlertsTo = cfg.SendAlertsTo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle errors
|
||||||
|
externalAlertManagers, err := srv.externalAlertmanagers(c.Req.Context(), c.OrgID)
|
||||||
|
if err != nil {
|
||||||
|
return ErrResp(http.StatusInternalServerError, err, "")
|
||||||
|
}
|
||||||
|
|
||||||
resp := apimodels.AlertingStatus{
|
resp := apimodels.AlertingStatus{
|
||||||
AlertmanagersChoice: apimodels.AlertmanagersChoice(sendsAlertsTo.String()),
|
AlertmanagersChoice: apimodels.AlertmanagersChoice(sendsAlertsTo.String()),
|
||||||
|
NumExternalAlertmanagers: len(externalAlertManagers),
|
||||||
}
|
}
|
||||||
return response.JSON(http.StatusOK, resp)
|
return response.JSON(http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
|
@ -93,4 +93,5 @@ type GettableAlertmanagers struct {
|
|||||||
// swagger:model
|
// swagger:model
|
||||||
type AlertingStatus struct {
|
type AlertingStatus struct {
|
||||||
AlertmanagersChoice AlertmanagersChoice `json:"alertmanagersChoice"`
|
AlertmanagersChoice AlertmanagersChoice `json:"alertmanagersChoice"`
|
||||||
|
NumExternalAlertmanagers int `json:"numExternalAlertmanagers"`
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ import { getFiltersFromUrlParams } from './utils/misc';
|
|||||||
import { initialAsyncRequestState } from './utils/redux';
|
import { initialAsyncRequestState } from './utils/redux';
|
||||||
|
|
||||||
const AlertGroups = () => {
|
const AlertGroups = () => {
|
||||||
const { useGetAlertmanagerChoiceQuery } = alertmanagerApi;
|
const { useGetAlertmanagerChoiceStatusQuery } = alertmanagerApi;
|
||||||
|
|
||||||
const alertManagers = useAlertManagersByPermission('instance');
|
const alertManagers = useAlertManagersByPermission('instance');
|
||||||
const [alertManagerSourceName] = useAlertManagerSourceName(alertManagers);
|
const [alertManagerSourceName] = useAlertManagerSourceName(alertManagers);
|
||||||
@ -34,7 +34,7 @@ const AlertGroups = () => {
|
|||||||
const { groupBy = [] } = getFiltersFromUrlParams(queryParams);
|
const { groupBy = [] } = getFiltersFromUrlParams(queryParams);
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
const { currentData: alertmanagerChoice } = useGetAlertmanagerChoiceQuery();
|
const { currentData: amConfigStatus } = useGetAlertmanagerChoiceStatusQuery();
|
||||||
|
|
||||||
const alertGroups = useUnifiedAlertingSelector((state) => state.amAlertGroups);
|
const alertGroups = useUnifiedAlertingSelector((state) => state.amAlertGroups);
|
||||||
const {
|
const {
|
||||||
@ -47,7 +47,8 @@ const AlertGroups = () => {
|
|||||||
const filteredAlertGroups = useFilteredAmGroups(groupedAlerts);
|
const filteredAlertGroups = useFilteredAmGroups(groupedAlerts);
|
||||||
|
|
||||||
const grafanaAmDeliveryDisabled =
|
const grafanaAmDeliveryDisabled =
|
||||||
alertManagerSourceName === GRAFANA_RULES_SOURCE_NAME && alertmanagerChoice === AlertmanagerChoice.External;
|
alertManagerSourceName === GRAFANA_RULES_SOURCE_NAME &&
|
||||||
|
amConfigStatus?.alertmanagersChoice === AlertmanagerChoice.External;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function fetchNotifications() {
|
function fetchNotifications() {
|
||||||
|
@ -7,7 +7,6 @@ import { useDispatch } from 'app/types';
|
|||||||
|
|
||||||
import { useCleanup } from '../../../core/hooks/useCleanup';
|
import { useCleanup } from '../../../core/hooks/useCleanup';
|
||||||
|
|
||||||
import { alertmanagerApi } from './api/alertmanagerApi';
|
|
||||||
import { AlertManagerPicker } from './components/AlertManagerPicker';
|
import { AlertManagerPicker } from './components/AlertManagerPicker';
|
||||||
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
|
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
|
||||||
import { GrafanaAlertmanagerDeliveryWarning } from './components/GrafanaAlertmanagerDeliveryWarning';
|
import { GrafanaAlertmanagerDeliveryWarning } from './components/GrafanaAlertmanagerDeliveryWarning';
|
||||||
@ -29,12 +28,11 @@ import { initialAsyncRequestState } from './utils/redux';
|
|||||||
|
|
||||||
const AmRoutes = () => {
|
const AmRoutes = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { useGetAlertmanagerChoiceQuery } = alertmanagerApi;
|
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const [isRootRouteEditMode, setIsRootRouteEditMode] = useState(false);
|
const [isRootRouteEditMode, setIsRootRouteEditMode] = useState(false);
|
||||||
const alertManagers = useAlertManagersByPermission('notification');
|
const alertManagers = useAlertManagersByPermission('notification');
|
||||||
const [alertManagerSourceName, setAlertManagerSourceName] = useAlertManagerSourceName(alertManagers);
|
const [alertManagerSourceName, setAlertManagerSourceName] = useAlertManagerSourceName(alertManagers);
|
||||||
const { currentData: alertmanagerChoice } = useGetAlertmanagerChoiceQuery();
|
|
||||||
|
|
||||||
const amConfigs = useUnifiedAlertingSelector((state) => state.amConfigs);
|
const amConfigs = useUnifiedAlertingSelector((state) => state.amConfigs);
|
||||||
|
|
||||||
@ -130,10 +128,7 @@ const AmRoutes = () => {
|
|||||||
{resultError.message || 'Unknown error.'}
|
{resultError.message || 'Unknown error.'}
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
<GrafanaAlertmanagerDeliveryWarning
|
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={alertManagerSourceName} />
|
||||||
currentAlertmanager={alertManagerSourceName}
|
|
||||||
alertmanagerChoice={alertmanagerChoice}
|
|
||||||
/>
|
|
||||||
{isProvisioned && <ProvisioningAlert resource={ProvisionedResource.RootNotificationPolicy} />}
|
{isProvisioned && <ProvisioningAlert resource={ProvisionedResource.RootNotificationPolicy} />}
|
||||||
{resultLoading && <LoadingPlaceholder text="Loading Alertmanager config..." />}
|
{resultLoading && <LoadingPlaceholder text="Loading Alertmanager config..." />}
|
||||||
{result && !resultLoading && !resultError && (
|
{result && !resultLoading && !resultError && (
|
||||||
|
@ -24,6 +24,7 @@ import 'whatwg-fetch';
|
|||||||
|
|
||||||
import Receivers from './Receivers';
|
import Receivers from './Receivers';
|
||||||
import { fetchAlertManagerConfig, fetchStatus, testReceivers, updateAlertManagerConfig } from './api/alertmanager';
|
import { fetchAlertManagerConfig, fetchStatus, testReceivers, updateAlertManagerConfig } from './api/alertmanager';
|
||||||
|
import { AlertmanagersChoiceResponse } from './api/alertmanagerApi';
|
||||||
import { discoverAlertmanagerFeatures } from './api/buildInfo';
|
import { discoverAlertmanagerFeatures } from './api/buildInfo';
|
||||||
import { fetchNotifiers } from './api/grafana';
|
import { fetchNotifiers } from './api/grafana';
|
||||||
import * as receiversApi from './api/receiversApi';
|
import * as receiversApi from './api/receiversApi';
|
||||||
@ -63,6 +64,11 @@ const mocks = {
|
|||||||
contextSrv: jest.mocked(contextSrv),
|
contextSrv: jest.mocked(contextSrv),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const alertmanagerChoiceMockedResponse: AlertmanagersChoiceResponse = {
|
||||||
|
alertmanagersChoice: AlertmanagerChoice.Internal,
|
||||||
|
numExternalAlertmanagers: 0,
|
||||||
|
};
|
||||||
|
|
||||||
const renderReceivers = (alertManagerSourceName?: string) => {
|
const renderReceivers = (alertManagerSourceName?: string) => {
|
||||||
const store = configureStore();
|
const store = configureStore();
|
||||||
|
|
||||||
@ -185,7 +191,7 @@ describe('Receivers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Template and receiver tables are rendered, alertmanager can be selected, no notification errors', async () => {
|
it('Template and receiver tables are rendered, alertmanager can be selected, no notification errors', async () => {
|
||||||
mockAlertmanagerChoiceResponse(server, { alertmanagersChoice: AlertmanagerChoice.All });
|
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
|
||||||
mocks.api.fetchConfig.mockImplementation((name) =>
|
mocks.api.fetchConfig.mockImplementation((name) =>
|
||||||
Promise.resolve(name === GRAFANA_RULES_SOURCE_NAME ? someGrafanaAlertManagerConfig : someCloudAlertManagerConfig)
|
Promise.resolve(name === GRAFANA_RULES_SOURCE_NAME ? someGrafanaAlertManagerConfig : someCloudAlertManagerConfig)
|
||||||
);
|
);
|
||||||
@ -230,7 +236,7 @@ describe('Receivers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Grafana receiver can be tested', async () => {
|
it('Grafana receiver can be tested', async () => {
|
||||||
mockAlertmanagerChoiceResponse(server, { alertmanagersChoice: AlertmanagerChoice.All });
|
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
|
||||||
|
|
||||||
mocks.api.fetchConfig.mockResolvedValue(someGrafanaAlertManagerConfig);
|
mocks.api.fetchConfig.mockResolvedValue(someGrafanaAlertManagerConfig);
|
||||||
|
|
||||||
@ -288,7 +294,7 @@ describe('Receivers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Grafana receiver can be created', async () => {
|
it('Grafana receiver can be created', async () => {
|
||||||
mockAlertmanagerChoiceResponse(server, { alertmanagersChoice: AlertmanagerChoice.All });
|
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
|
||||||
|
|
||||||
mocks.api.fetchConfig.mockResolvedValue(someGrafanaAlertManagerConfig);
|
mocks.api.fetchConfig.mockResolvedValue(someGrafanaAlertManagerConfig);
|
||||||
mocks.api.updateConfig.mockResolvedValue();
|
mocks.api.updateConfig.mockResolvedValue();
|
||||||
@ -352,7 +358,7 @@ describe('Receivers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Hides create contact point button for users without permission', () => {
|
it('Hides create contact point button for users without permission', () => {
|
||||||
mockAlertmanagerChoiceResponse(server, { alertmanagersChoice: AlertmanagerChoice.All });
|
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
|
||||||
|
|
||||||
mocks.api.fetchConfig.mockResolvedValue(someGrafanaAlertManagerConfig);
|
mocks.api.fetchConfig.mockResolvedValue(someGrafanaAlertManagerConfig);
|
||||||
mocks.api.updateConfig.mockResolvedValue();
|
mocks.api.updateConfig.mockResolvedValue();
|
||||||
@ -368,7 +374,7 @@ describe('Receivers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Cloud alertmanager receiver can be edited', async () => {
|
it('Cloud alertmanager receiver can be edited', async () => {
|
||||||
mockAlertmanagerChoiceResponse(server, { alertmanagersChoice: AlertmanagerChoice.All });
|
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
|
||||||
|
|
||||||
mocks.api.fetchConfig.mockResolvedValue(someCloudAlertManagerConfig);
|
mocks.api.fetchConfig.mockResolvedValue(someCloudAlertManagerConfig);
|
||||||
mocks.api.updateConfig.mockResolvedValue();
|
mocks.api.updateConfig.mockResolvedValue();
|
||||||
@ -464,7 +470,7 @@ describe('Receivers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Prometheus Alertmanager receiver cannot be edited', async () => {
|
it('Prometheus Alertmanager receiver cannot be edited', async () => {
|
||||||
mockAlertmanagerChoiceResponse(server, { alertmanagersChoice: AlertmanagerChoice.All });
|
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
|
||||||
|
|
||||||
mocks.api.fetchStatus.mockResolvedValue({
|
mocks.api.fetchStatus.mockResolvedValue({
|
||||||
...someCloudAlertManagerStatus,
|
...someCloudAlertManagerStatus,
|
||||||
@ -503,7 +509,7 @@ describe('Receivers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Loads config from status endpoint if there is no user config', async () => {
|
it('Loads config from status endpoint if there is no user config', async () => {
|
||||||
mockAlertmanagerChoiceResponse(server, { alertmanagersChoice: AlertmanagerChoice.All });
|
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
|
||||||
// loading an empty config with make it fetch config from status endpoint
|
// loading an empty config with make it fetch config from status endpoint
|
||||||
mocks.api.fetchConfig.mockResolvedValue({
|
mocks.api.fetchConfig.mockResolvedValue({
|
||||||
template_files: {},
|
template_files: {},
|
||||||
@ -525,7 +531,7 @@ describe('Receivers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Shows an empty config when config returns an error and the AM supports lazy config initialization', async () => {
|
it('Shows an empty config when config returns an error and the AM supports lazy config initialization', async () => {
|
||||||
mockAlertmanagerChoiceResponse(server, { alertmanagersChoice: AlertmanagerChoice.All });
|
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
|
||||||
|
|
||||||
mocks.api.discoverAlertmanagerFeatures.mockResolvedValue({ lazyConfigInit: true });
|
mocks.api.discoverAlertmanagerFeatures.mockResolvedValue({ lazyConfigInit: true });
|
||||||
mocks.api.fetchConfig.mockRejectedValue({ message: 'alertmanager storage object not found' });
|
mocks.api.fetchConfig.mockRejectedValue({ message: 'alertmanager storage object not found' });
|
||||||
@ -542,7 +548,7 @@ describe('Receivers', () => {
|
|||||||
|
|
||||||
describe('Contact points state', () => {
|
describe('Contact points state', () => {
|
||||||
it('Should render error notifications when there are some points state ', async () => {
|
it('Should render error notifications when there are some points state ', async () => {
|
||||||
mockAlertmanagerChoiceResponse(server, { alertmanagersChoice: AlertmanagerChoice.All });
|
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
|
||||||
mocks.api.fetchConfig.mockResolvedValue(someGrafanaAlertManagerConfig);
|
mocks.api.fetchConfig.mockResolvedValue(someGrafanaAlertManagerConfig);
|
||||||
mocks.api.updateConfig.mockResolvedValue();
|
mocks.api.updateConfig.mockResolvedValue();
|
||||||
|
|
||||||
@ -614,7 +620,7 @@ describe('Receivers', () => {
|
|||||||
expect(byText('OK').getAll(criticalDetailTable)).toHaveLength(2);
|
expect(byText('OK').getAll(criticalDetailTable)).toHaveLength(2);
|
||||||
});
|
});
|
||||||
it('Should render no attempt message when there are some points state with null lastNotifyAttempt, and "-" in null values', async () => {
|
it('Should render no attempt message when there are some points state with null lastNotifyAttempt, and "-" in null values', async () => {
|
||||||
mockAlertmanagerChoiceResponse(server, { alertmanagersChoice: AlertmanagerChoice.All });
|
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
|
||||||
mocks.api.fetchConfig.mockResolvedValue(someGrafanaAlertManagerConfig);
|
mocks.api.fetchConfig.mockResolvedValue(someGrafanaAlertManagerConfig);
|
||||||
mocks.api.updateConfig.mockResolvedValue();
|
mocks.api.updateConfig.mockResolvedValue();
|
||||||
|
|
||||||
@ -691,7 +697,7 @@ describe('Receivers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should not render error notifications when fetching contact points state raises 404 error ', async () => {
|
it('Should not render error notifications when fetching contact points state raises 404 error ', async () => {
|
||||||
mockAlertmanagerChoiceResponse(server, { alertmanagersChoice: AlertmanagerChoice.All });
|
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
|
||||||
mocks.api.fetchConfig.mockResolvedValue(someGrafanaAlertManagerConfig);
|
mocks.api.fetchConfig.mockResolvedValue(someGrafanaAlertManagerConfig);
|
||||||
mocks.api.updateConfig.mockResolvedValue();
|
mocks.api.updateConfig.mockResolvedValue();
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import { useDispatch } from 'app/types';
|
|||||||
|
|
||||||
import { ContactPointsState } from '../../../types';
|
import { ContactPointsState } from '../../../types';
|
||||||
|
|
||||||
import { alertmanagerApi } from './api/alertmanagerApi';
|
|
||||||
import { useGetContactPointsState } from './api/receiversApi';
|
import { useGetContactPointsState } from './api/receiversApi';
|
||||||
import { AlertManagerPicker } from './components/AlertManagerPicker';
|
import { AlertManagerPicker } from './components/AlertManagerPicker';
|
||||||
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
|
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
|
||||||
@ -55,8 +54,6 @@ function NotificationError({ errorCount }: NotificationErrorProps) {
|
|||||||
type PageType = 'receivers' | 'templates' | 'global-config';
|
type PageType = 'receivers' | 'templates' | 'global-config';
|
||||||
|
|
||||||
const Receivers = () => {
|
const Receivers = () => {
|
||||||
const { useGetAlertmanagerChoiceQuery } = alertmanagerApi;
|
|
||||||
|
|
||||||
const alertManagers = useAlertManagersByPermission('notification');
|
const alertManagers = useAlertManagersByPermission('notification');
|
||||||
const [alertManagerSourceName, setAlertManagerSourceName] = useAlertManagerSourceName(alertManagers);
|
const [alertManagerSourceName, setAlertManagerSourceName] = useAlertManagerSourceName(alertManagers);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@ -97,8 +94,6 @@ const Receivers = () => {
|
|||||||
const contactPointsState: ContactPointsState = useGetContactPointsState(alertManagerSourceName ?? '');
|
const contactPointsState: ContactPointsState = useGetContactPointsState(alertManagerSourceName ?? '');
|
||||||
const integrationsErrorCount = contactPointsState?.errorCount ?? 0;
|
const integrationsErrorCount = contactPointsState?.errorCount ?? 0;
|
||||||
|
|
||||||
const { data: alertmanagerChoice } = useGetAlertmanagerChoiceQuery();
|
|
||||||
|
|
||||||
const disableAmSelect = !isRoot;
|
const disableAmSelect = !isRoot;
|
||||||
|
|
||||||
let pageNav = getPageNavigationModel(type, id);
|
let pageNav = getPageNavigationModel(type, id);
|
||||||
@ -131,10 +126,7 @@ const Receivers = () => {
|
|||||||
{error.message || 'Unknown error.'}
|
{error.message || 'Unknown error.'}
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
<GrafanaAlertmanagerDeliveryWarning
|
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={alertManagerSourceName} />
|
||||||
alertmanagerChoice={alertmanagerChoice}
|
|
||||||
currentAlertmanager={alertManagerSourceName}
|
|
||||||
/>
|
|
||||||
{loading && !config && <LoadingPlaceholder text="loading configuration..." />}
|
{loading && !config && <LoadingPlaceholder text="loading configuration..." />}
|
||||||
{config && !error && (
|
{config && !error && (
|
||||||
<Switch>
|
<Switch>
|
||||||
|
@ -5,7 +5,6 @@ import { Alert, withErrorBoundary } from '@grafana/ui';
|
|||||||
import { Silence } from 'app/plugins/datasource/alertmanager/types';
|
import { Silence } from 'app/plugins/datasource/alertmanager/types';
|
||||||
import { useDispatch } from 'app/types';
|
import { useDispatch } from 'app/types';
|
||||||
|
|
||||||
import { alertmanagerApi } from './api/alertmanagerApi';
|
|
||||||
import { featureDiscoveryApi } from './api/featureDiscoveryApi';
|
import { featureDiscoveryApi } from './api/featureDiscoveryApi';
|
||||||
import { AlertManagerPicker } from './components/AlertManagerPicker';
|
import { AlertManagerPicker } from './components/AlertManagerPicker';
|
||||||
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
|
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
|
||||||
@ -26,7 +25,6 @@ const Silences = () => {
|
|||||||
const [alertManagerSourceName, setAlertManagerSourceName] = useAlertManagerSourceName(alertManagers);
|
const [alertManagerSourceName, setAlertManagerSourceName] = useAlertManagerSourceName(alertManagers);
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { useGetAlertmanagerChoiceQuery } = alertmanagerApi;
|
|
||||||
const silences = useUnifiedAlertingSelector((state) => state.silences);
|
const silences = useUnifiedAlertingSelector((state) => state.silences);
|
||||||
const alertsRequests = useUnifiedAlertingSelector((state) => state.amAlerts);
|
const alertsRequests = useUnifiedAlertingSelector((state) => state.amAlerts);
|
||||||
const alertsRequest = alertManagerSourceName
|
const alertsRequest = alertManagerSourceName
|
||||||
@ -42,8 +40,6 @@ const Silences = () => {
|
|||||||
{ skip: !alertManagerSourceName }
|
{ skip: !alertManagerSourceName }
|
||||||
);
|
);
|
||||||
|
|
||||||
const { currentData: alertmanagerChoice } = useGetAlertmanagerChoiceQuery();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function fetchAll() {
|
function fetchAll() {
|
||||||
if (alertManagerSourceName) {
|
if (alertManagerSourceName) {
|
||||||
@ -84,10 +80,7 @@ const Silences = () => {
|
|||||||
onChange={setAlertManagerSourceName}
|
onChange={setAlertManagerSourceName}
|
||||||
dataSources={alertManagers}
|
dataSources={alertManagers}
|
||||||
/>
|
/>
|
||||||
<GrafanaAlertmanagerDeliveryWarning
|
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={alertManagerSourceName} />
|
||||||
currentAlertmanager={alertManagerSourceName}
|
|
||||||
alertmanagerChoice={alertmanagerChoice}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{mimirLazyInitError && (
|
{mimirLazyInitError && (
|
||||||
<Alert title="The selected Alertmanager has no configuration" severity="warning">
|
<Alert title="The selected Alertmanager has no configuration" severity="warning">
|
||||||
|
@ -9,14 +9,14 @@ import { alertingApi } from './alertingApi';
|
|||||||
|
|
||||||
export interface AlertmanagersChoiceResponse {
|
export interface AlertmanagersChoiceResponse {
|
||||||
alertmanagersChoice: AlertmanagerChoice;
|
alertmanagersChoice: AlertmanagerChoice;
|
||||||
|
numExternalAlertmanagers: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const alertmanagerApi = alertingApi.injectEndpoints({
|
export const alertmanagerApi = alertingApi.injectEndpoints({
|
||||||
endpoints: (build) => ({
|
endpoints: (build) => ({
|
||||||
getAlertmanagerChoice: build.query<AlertmanagerChoice, void>({
|
getAlertmanagerChoiceStatus: build.query<AlertmanagersChoiceResponse, void>({
|
||||||
query: () => ({ url: '/api/v1/ngalert' }),
|
query: () => ({ url: '/api/v1/ngalert' }),
|
||||||
providesTags: ['AlertmanagerChoice'],
|
providesTags: ['AlertmanagerChoice'],
|
||||||
transformResponse: (response: AlertmanagersChoiceResponse) => response.alertmanagersChoice,
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getExternalAlertmanagerConfig: build.query<ExternalAlertmanagerConfig, void>({
|
getExternalAlertmanagerConfig: build.query<ExternalAlertmanagerConfig, void>({
|
||||||
|
@ -1,47 +1,100 @@
|
|||||||
import { render } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { setupServer } from 'msw/node';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
|
import 'whatwg-fetch';
|
||||||
|
|
||||||
|
import { setBackendSrv } from '@grafana/runtime';
|
||||||
|
import { backendSrv } from 'app/core/services/backend_srv';
|
||||||
|
import { configureStore } from 'app/store/configureStore';
|
||||||
|
|
||||||
import { AlertmanagerChoice } from '../../../../plugins/datasource/alertmanager/types';
|
import { AlertmanagerChoice } from '../../../../plugins/datasource/alertmanager/types';
|
||||||
|
import { mockAlertmanagerChoiceResponse } from '../mocks/alertmanagerApi';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||||
|
|
||||||
import { GrafanaAlertmanagerDeliveryWarning } from './GrafanaAlertmanagerDeliveryWarning';
|
import { GrafanaAlertmanagerDeliveryWarning } from './GrafanaAlertmanagerDeliveryWarning';
|
||||||
|
|
||||||
describe('GrafanaAlertmanagerDeliveryWarning', () => {
|
describe('GrafanaAlertmanagerDeliveryWarning', () => {
|
||||||
describe('When AlertmanagerChoice set to External', () => {
|
const server = setupServer();
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
setBackendSrv(backendSrv);
|
||||||
|
server.listen({ onUnhandledRequest: 'error' });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
server.resetHandlers();
|
||||||
|
});
|
||||||
|
|
||||||
it('Should not render when the datasource is not Grafana', () => {
|
it('Should not render when the datasource is not Grafana', () => {
|
||||||
const { container } = render(
|
mockAlertmanagerChoiceResponse(server, {
|
||||||
<GrafanaAlertmanagerDeliveryWarning
|
alertmanagersChoice: AlertmanagerChoice.External,
|
||||||
currentAlertmanager="custom-alertmanager"
|
numExternalAlertmanagers: 0,
|
||||||
alertmanagerChoice={AlertmanagerChoice.External}
|
});
|
||||||
/>
|
|
||||||
|
const { container } = renderWithStore(
|
||||||
|
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager="custom-alertmanager" />
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(container).toBeEmptyDOMElement();
|
expect(container).toBeEmptyDOMElement();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should render warning when the datasource is Grafana', () => {
|
it('Should render warning when the datasource is Grafana and using external AM', async () => {
|
||||||
const { container } = render(
|
mockAlertmanagerChoiceResponse(server, {
|
||||||
<GrafanaAlertmanagerDeliveryWarning
|
alertmanagersChoice: AlertmanagerChoice.External,
|
||||||
currentAlertmanager={GRAFANA_RULES_SOURCE_NAME}
|
numExternalAlertmanagers: 1,
|
||||||
alertmanagerChoice={AlertmanagerChoice.External}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(container).toHaveTextContent('Grafana alerts are not delivered to Grafana Alertmanager');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([AlertmanagerChoice.All, AlertmanagerChoice.Internal])(
|
renderWithStore(<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={GRAFANA_RULES_SOURCE_NAME} />);
|
||||||
'Should not render when datasource is Grafana and Alertmanager choice is %s',
|
|
||||||
(choice) => {
|
expect(await screen.findByText('Grafana alerts are not delivered to Grafana Alertmanager')).toBeVisible();
|
||||||
const { container } = render(
|
});
|
||||||
<GrafanaAlertmanagerDeliveryWarning
|
|
||||||
currentAlertmanager={GRAFANA_RULES_SOURCE_NAME}
|
it('Should render warning when the datasource is Grafana and using All AM', async () => {
|
||||||
alertmanagerChoice={choice}
|
mockAlertmanagerChoiceResponse(server, {
|
||||||
/>
|
alertmanagersChoice: AlertmanagerChoice.All,
|
||||||
|
numExternalAlertmanagers: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
renderWithStore(<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={GRAFANA_RULES_SOURCE_NAME} />);
|
||||||
|
|
||||||
|
expect(await screen.findByText('You have additional Alertmanagers to configure')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should render no warning when choice is Internal', async () => {
|
||||||
|
mockAlertmanagerChoiceResponse(server, {
|
||||||
|
alertmanagersChoice: AlertmanagerChoice.Internal,
|
||||||
|
numExternalAlertmanagers: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { container } = renderWithStore(
|
||||||
|
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={GRAFANA_RULES_SOURCE_NAME} />
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(container).toBeEmptyDOMElement();
|
expect(container).toBeEmptyDOMElement();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should render no warning when choice is All but no active AM instances', async () => {
|
||||||
|
mockAlertmanagerChoiceResponse(server, {
|
||||||
|
alertmanagersChoice: AlertmanagerChoice.All,
|
||||||
|
numExternalAlertmanagers: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { container } = renderWithStore(
|
||||||
|
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={GRAFANA_RULES_SOURCE_NAME} />
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(container).toBeEmptyDOMElement();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderWithStore(element: JSX.Element) {
|
||||||
|
const store = configureStore();
|
||||||
|
|
||||||
|
return render(<Provider store={store}>{element}</Provider>);
|
||||||
}
|
}
|
||||||
);
|
|
||||||
});
|
|
||||||
|
@ -5,39 +5,60 @@ import { GrafanaTheme2 } from '@grafana/data/src';
|
|||||||
import { Alert, useStyles2 } from '@grafana/ui/src';
|
import { Alert, useStyles2 } from '@grafana/ui/src';
|
||||||
|
|
||||||
import { AlertmanagerChoice } from '../../../../plugins/datasource/alertmanager/types';
|
import { AlertmanagerChoice } from '../../../../plugins/datasource/alertmanager/types';
|
||||||
|
import { alertmanagerApi } from '../api/alertmanagerApi';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||||
|
|
||||||
interface GrafanaAlertmanagerDeliveryWarningProps {
|
interface GrafanaAlertmanagerDeliveryWarningProps {
|
||||||
alertmanagerChoice?: AlertmanagerChoice;
|
|
||||||
currentAlertmanager: string;
|
currentAlertmanager: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GrafanaAlertmanagerDeliveryWarning({
|
export function GrafanaAlertmanagerDeliveryWarning({ currentAlertmanager }: GrafanaAlertmanagerDeliveryWarningProps) {
|
||||||
alertmanagerChoice,
|
|
||||||
currentAlertmanager,
|
|
||||||
}: GrafanaAlertmanagerDeliveryWarningProps) {
|
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
if (currentAlertmanager !== GRAFANA_RULES_SOURCE_NAME) {
|
const { useGetAlertmanagerChoiceStatusQuery } = alertmanagerApi;
|
||||||
|
const { currentData: amChoiceStatus } = useGetAlertmanagerChoiceStatusQuery();
|
||||||
|
|
||||||
|
const viewingInternalAM = currentAlertmanager === GRAFANA_RULES_SOURCE_NAME;
|
||||||
|
|
||||||
|
const interactsWithExternalAMs =
|
||||||
|
amChoiceStatus?.alertmanagersChoice &&
|
||||||
|
[AlertmanagerChoice.External, AlertmanagerChoice.All].includes(amChoiceStatus?.alertmanagersChoice);
|
||||||
|
|
||||||
|
if (!interactsWithExternalAMs || !viewingInternalAM) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alertmanagerChoice !== AlertmanagerChoice.External) {
|
const hasActiveExternalAMs = amChoiceStatus.numExternalAlertmanagers > 0;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (amChoiceStatus.alertmanagersChoice === AlertmanagerChoice.External) {
|
||||||
return (
|
return (
|
||||||
<Alert title="Grafana alerts are not delivered to Grafana Alertmanager">
|
<Alert title="Grafana alerts are not delivered to Grafana Alertmanager">
|
||||||
Grafana is configured to send alerts to external Alertmanagers only. Changing Grafana Alertmanager configuration
|
Grafana is configured to send alerts to external Alertmanagers only. Changing Grafana Alertmanager configuration
|
||||||
will not affect delivery of your alerts!
|
will not affect delivery of your alerts.
|
||||||
<div className={styles.adminHint}>
|
<div className={styles.adminHint}>
|
||||||
You can change the configuration on the Alerting Admin page. If you do not have access, contact your
|
To change your Alertmanager setup, go to the Alerting Admin page. If you do not have access, contact your
|
||||||
Administrator
|
Administrator.
|
||||||
</div>
|
</div>
|
||||||
</Alert>
|
</Alert>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (amChoiceStatus.alertmanagersChoice === AlertmanagerChoice.All && hasActiveExternalAMs) {
|
||||||
|
return (
|
||||||
|
<Alert title="You have additional Alertmanagers to configure" severity="warning">
|
||||||
|
Ensure you make configuration changes in the correct Alertmanagers; both internal and external. Changing one
|
||||||
|
will not affect the others.
|
||||||
|
<div className={styles.adminHint}>
|
||||||
|
To change your Alertmanager setup, go to the Alerting Admin page. If you do not have access, contact your
|
||||||
|
Administrator.
|
||||||
|
</div>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => ({
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
adminHint: css`
|
adminHint: css`
|
||||||
font-size: ${theme.typography.bodySmall.fontSize};
|
font-size: ${theme.typography.bodySmall.fontSize};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { render, screen, waitFor } from '@testing-library/react';
|
import { render, screen, waitFor } from '@testing-library/react';
|
||||||
|
import { setupServer } from 'msw/node';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
@ -7,12 +8,15 @@ import { byRole } from 'testing-library-selector';
|
|||||||
import { setBackendSrv } from '@grafana/runtime';
|
import { setBackendSrv } from '@grafana/runtime';
|
||||||
import { backendSrv } from 'app/core/services/backend_srv';
|
import { backendSrv } from 'app/core/services/backend_srv';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
|
import { AlertmanagerChoice } from 'app/plugins/datasource/alertmanager/types';
|
||||||
import { configureStore } from 'app/store/configureStore';
|
import { configureStore } from 'app/store/configureStore';
|
||||||
import { AccessControlAction } from 'app/types';
|
import { AccessControlAction } from 'app/types';
|
||||||
import { CombinedRule } from 'app/types/unified-alerting';
|
import { CombinedRule } from 'app/types/unified-alerting';
|
||||||
|
|
||||||
|
import { AlertmanagersChoiceResponse } from '../../api/alertmanagerApi';
|
||||||
import { useIsRuleEditable } from '../../hooks/useIsRuleEditable';
|
import { useIsRuleEditable } from '../../hooks/useIsRuleEditable';
|
||||||
import { getCloudRule, getGrafanaRule } from '../../mocks';
|
import { getCloudRule, getGrafanaRule } from '../../mocks';
|
||||||
|
import { mockAlertmanagerChoiceResponse } from '../../mocks/alertmanagerApi';
|
||||||
|
|
||||||
import { RuleDetails } from './RuleDetails';
|
import { RuleDetails } from './RuleDetails';
|
||||||
|
|
||||||
@ -32,11 +36,27 @@ const ui = {
|
|||||||
|
|
||||||
jest.spyOn(contextSrv, 'accessControlEnabled').mockReturnValue(true);
|
jest.spyOn(contextSrv, 'accessControlEnabled').mockReturnValue(true);
|
||||||
|
|
||||||
|
const server = setupServer();
|
||||||
|
|
||||||
|
const alertmanagerChoiceMockedResponse: AlertmanagersChoiceResponse = {
|
||||||
|
alertmanagersChoice: AlertmanagerChoice.Internal,
|
||||||
|
numExternalAlertmanagers: 0,
|
||||||
|
};
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
setBackendSrv(backendSrv);
|
setBackendSrv(backendSrv);
|
||||||
|
server.listen({ onUnhandledRequest: 'error' });
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
server.resetHandlers();
|
||||||
|
});
|
||||||
|
|
||||||
describe('RuleDetails RBAC', () => {
|
describe('RuleDetails RBAC', () => {
|
||||||
describe('Grafana rules action buttons in details', () => {
|
describe('Grafana rules action buttons in details', () => {
|
||||||
const grafanaRule = getGrafanaRule({ name: 'Grafana' });
|
const grafanaRule = getGrafanaRule({ name: 'Grafana' });
|
||||||
@ -68,6 +88,7 @@ describe('RuleDetails RBAC', () => {
|
|||||||
it('Should not render Silence button for users wihout the instance create permission', async () => {
|
it('Should not render Silence button for users wihout the instance create permission', async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(false);
|
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(false);
|
||||||
|
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
renderRuleDetails(grafanaRule);
|
renderRuleDetails(grafanaRule);
|
||||||
@ -78,6 +99,8 @@ describe('RuleDetails RBAC', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should render Silence button for users with the instance create permissions', async () => {
|
it('Should render Silence button for users with the instance create permissions', async () => {
|
||||||
|
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
|
||||||
|
|
||||||
// Arrange
|
// Arrange
|
||||||
jest
|
jest
|
||||||
.spyOn(contextSrv, 'hasPermission')
|
.spyOn(contextSrv, 'hasPermission')
|
||||||
@ -91,6 +114,7 @@ describe('RuleDetails RBAC', () => {
|
|||||||
await waitFor(() => screen.queryByRole('button', { name: 'Declare incident' }));
|
await waitFor(() => screen.queryByRole('button', { name: 'Declare incident' }));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Cloud rules action buttons', () => {
|
describe('Cloud rules action buttons', () => {
|
||||||
const cloudRule = getCloudRule({ name: 'Cloud' });
|
const cloudRule = getCloudRule({ name: 'Cloud' });
|
||||||
|
|
||||||
|
@ -7,10 +7,12 @@ import { config } from '@grafana/runtime';
|
|||||||
import { Button, ClipboardButton, ConfirmModal, HorizontalGroup, LinkButton, useStyles2 } from '@grafana/ui';
|
import { Button, ClipboardButton, ConfirmModal, HorizontalGroup, LinkButton, useStyles2 } from '@grafana/ui';
|
||||||
import { useAppNotification } from 'app/core/copy/appNotification';
|
import { useAppNotification } from 'app/core/copy/appNotification';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
|
import { AlertmanagerChoice } from 'app/plugins/datasource/alertmanager/types';
|
||||||
import { AccessControlAction, useDispatch } from 'app/types';
|
import { AccessControlAction, useDispatch } from 'app/types';
|
||||||
import { CombinedRule, RulesSource } from 'app/types/unified-alerting';
|
import { CombinedRule, RulesSource } from 'app/types/unified-alerting';
|
||||||
import { PromAlertingRuleState } from 'app/types/unified-alerting-dto';
|
import { PromAlertingRuleState } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
|
import { alertmanagerApi } from '../../api/alertmanagerApi';
|
||||||
import { useIsRuleEditable } from '../../hooks/useIsRuleEditable';
|
import { useIsRuleEditable } from '../../hooks/useIsRuleEditable';
|
||||||
import { useStateHistoryModal } from '../../hooks/useStateHistoryModal';
|
import { useStateHistoryModal } from '../../hooks/useStateHistoryModal';
|
||||||
import { deleteRuleAction } from '../../state/actions';
|
import { deleteRuleAction } from '../../state/actions';
|
||||||
@ -84,6 +86,7 @@ export const RuleDetailsActionButtons: FC<Props> = ({ rule, rulesSource, isViewM
|
|||||||
const rulesPermissions = getRulesPermissions(rulesSourceName);
|
const rulesPermissions = getRulesPermissions(rulesSourceName);
|
||||||
const hasCreateRulePermission = contextSrv.hasPermission(rulesPermissions.create);
|
const hasCreateRulePermission = contextSrv.hasPermission(rulesPermissions.create);
|
||||||
const { isEditable, isRemovable } = useIsRuleEditable(rulesSourceName, rulerRule);
|
const { isEditable, isRemovable } = useIsRuleEditable(rulesSourceName, rulerRule);
|
||||||
|
const canSilence = useCanSilence(rule);
|
||||||
|
|
||||||
const returnTo = location.pathname + location.search;
|
const returnTo = location.pathname + location.search;
|
||||||
// explore does not support grafana rule queries atm
|
// explore does not support grafana rule queries atm
|
||||||
@ -149,7 +152,7 @@ export const RuleDetailsActionButtons: FC<Props> = ({ rule, rulesSource, isViewM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alertmanagerSourceName && contextSrv.hasAccess(AccessControlAction.AlertingInstanceCreate, contextSrv.isEditor)) {
|
if (canSilence && alertmanagerSourceName) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<LinkButton
|
<LinkButton
|
||||||
size="sm"
|
size="sm"
|
||||||
@ -263,6 +266,31 @@ export const RuleDetailsActionButtons: FC<Props> = ({ rule, rulesSource, isViewM
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We don't want to show the silence button if either
|
||||||
|
* 1. the user has no permissions to create silences
|
||||||
|
* 2. the admin has configured to only send instances to external AMs
|
||||||
|
*/
|
||||||
|
function useCanSilence(rule: CombinedRule) {
|
||||||
|
const isGrafanaManagedRule = isGrafanaRulerRule(rule.rulerRule);
|
||||||
|
|
||||||
|
const { useGetAlertmanagerChoiceStatusQuery } = alertmanagerApi;
|
||||||
|
const { currentData: amConfigStatus, isLoading } = useGetAlertmanagerChoiceStatusQuery(undefined, {
|
||||||
|
skip: !isGrafanaManagedRule,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isGrafanaManagedRule || isLoading) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasPermissions = contextSrv.hasAccess(AccessControlAction.AlertingInstanceCreate, contextSrv.isEditor);
|
||||||
|
|
||||||
|
const interactsOnlyWithExternalAMs = amConfigStatus?.alertmanagersChoice === AlertmanagerChoice.External;
|
||||||
|
const interactsWithAll = amConfigStatus?.alertmanagersChoice === AlertmanagerChoice.All;
|
||||||
|
|
||||||
|
return hasPermissions && (!interactsOnlyWithExternalAMs || interactsWithAll);
|
||||||
|
}
|
||||||
|
|
||||||
export const getStyles = (theme: GrafanaTheme2) => ({
|
export const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
wrapper: css`
|
wrapper: css`
|
||||||
padding: ${theme.spacing(2)} 0;
|
padding: ${theme.spacing(2)} 0;
|
||||||
|
@ -64,12 +64,12 @@ export const MatchedSilencedRules = () => {
|
|||||||
{matchers.every((matcher) => !matcher.value && !matcher.name) ? (
|
{matchers.every((matcher) => !matcher.value && !matcher.name) ? (
|
||||||
<span>Add a valid matcher to see affected alerts</span>
|
<span>Add a valid matcher to see affected alerts</span>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<DynamicTable
|
||||||
<DynamicTable items={matchedAlertRules.slice(0, 5) ?? []} isExpandable={false} cols={columns} />
|
items={matchedAlertRules}
|
||||||
{matchedAlertRules.length > 5 && (
|
isExpandable={false}
|
||||||
<div className={styles.moreMatches}>and {matchedAlertRules.length - 5} more</div>
|
cols={columns}
|
||||||
)}
|
pagination={{ itemsPerPage: 5 }}
|
||||||
</>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -92,7 +92,7 @@ function useColumns(): MatchedRulesTableColumnProps[] {
|
|||||||
renderCell: function renderName({ data: { matchedInstance } }) {
|
renderCell: function renderName({ data: { matchedInstance } }) {
|
||||||
return <AlertLabels labels={matchedInstance.labels} />;
|
return <AlertLabels labels={matchedInstance.labels} />;
|
||||||
},
|
},
|
||||||
size: '250px',
|
size: 'auto',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'created',
|
id: 'created',
|
||||||
@ -106,7 +106,7 @@ function useColumns(): MatchedRulesTableColumnProps[] {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
size: '400px',
|
size: '180px',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,25 @@ export const mockPromAlertingRule = (partial: Partial<AlertingRule> = {}): Alert
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const mockGrafanaRulerRule = (partial: Partial<RulerGrafanaRuleDTO> = {}): RulerGrafanaRuleDTO => {
|
||||||
|
return {
|
||||||
|
for: '',
|
||||||
|
annotations: {},
|
||||||
|
labels: {},
|
||||||
|
grafana_alert: {
|
||||||
|
...partial,
|
||||||
|
uid: '',
|
||||||
|
title: 'my rule',
|
||||||
|
namespace_uid: '',
|
||||||
|
namespace_id: 0,
|
||||||
|
condition: '',
|
||||||
|
no_data_state: GrafanaAlertStateDecision.NoData,
|
||||||
|
exec_err_state: GrafanaAlertStateDecision.Error,
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const mockPromRecordingRule = (partial: Partial<RecordingRule> = {}): RecordingRule => {
|
export const mockPromRecordingRule = (partial: Partial<RecordingRule> = {}): RecordingRule => {
|
||||||
return {
|
return {
|
||||||
type: PromRuleType.Recording,
|
type: PromRuleType.Recording,
|
||||||
@ -570,6 +589,7 @@ export function getGrafanaRule(override?: Partial<CombinedRule>) {
|
|||||||
name: 'Grafana',
|
name: 'Grafana',
|
||||||
rulesSource: 'grafana',
|
rulesSource: 'grafana',
|
||||||
},
|
},
|
||||||
|
rulerRule: mockGrafanaRulerRule(),
|
||||||
...override,
|
...override,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user