Alerting: improve provisioning indicator for notification policies (#71160)

This commit is contained in:
Gilles De Mey
2023-07-07 13:00:53 +02:00
committed by GitHub
parent 524f111ab3
commit 089305e399
4 changed files with 65 additions and 46 deletions

View File

@@ -16,7 +16,6 @@ import { alertmanagerApi } from './api/alertmanagerApi';
import { useGetContactPointsState } from './api/receiversApi'; import { useGetContactPointsState } from './api/receiversApi';
import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper'; import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper';
import { GrafanaAlertmanagerDeliveryWarning } from './components/GrafanaAlertmanagerDeliveryWarning'; import { GrafanaAlertmanagerDeliveryWarning } from './components/GrafanaAlertmanagerDeliveryWarning';
import { ProvisionedResource, ProvisioningAlert } from './components/Provisioning';
import { MuteTimingsTable } from './components/mute-timings/MuteTimingsTable'; import { MuteTimingsTable } from './components/mute-timings/MuteTimingsTable';
import { import {
computeInheritedTree, computeInheritedTree,
@@ -183,7 +182,7 @@ const AmRoutes = () => {
} }
const vanillaPrometheusAlertManager = isVanillaPrometheusAlertManagerDataSource(selectedAlertmanager); const vanillaPrometheusAlertManager = isVanillaPrometheusAlertManagerDataSource(selectedAlertmanager);
const readOnlyPolicies = vanillaPrometheusAlertManager || isProvisioned; const readOnlyPolicies = vanillaPrometheusAlertManager;
const readOnlyMuteTimings = vanillaPrometheusAlertManager; const readOnlyMuteTimings = vanillaPrometheusAlertManager;
const numberOfMuteTimings = result?.alertmanager_config.mute_time_intervals?.length ?? 0; const numberOfMuteTimings = result?.alertmanager_config.mute_time_intervals?.length ?? 0;
@@ -227,7 +226,6 @@ const AmRoutes = () => {
{policyTreeTabActive && ( {policyTreeTabActive && (
<> <>
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={selectedAlertmanager} /> <GrafanaAlertmanagerDeliveryWarning currentAlertmanager={selectedAlertmanager} />
{isProvisioned && <ProvisioningAlert resource={ProvisionedResource.RootNotificationPolicy} />}
<Stack direction="column" gap={1}> <Stack direction="column" gap={1}>
{rootRoute && ( {rootRoute && (
<NotificationPoliciesFilter <NotificationPoliciesFilter
@@ -244,6 +242,7 @@ const AmRoutes = () => {
alertGroups={alertGroups ?? []} alertGroups={alertGroups ?? []}
contactPointsState={contactPointsState.receivers} contactPointsState={contactPointsState.receivers}
readOnly={readOnlyPolicies} readOnly={readOnlyPolicies}
provisioned={isProvisioned}
alertManagerSourceName={selectedAlertmanager} alertManagerSourceName={selectedAlertmanager}
onAddPolicy={openAddModal} onAddPolicy={openAddModal}
onEditPolicy={openEditModal} onEditPolicy={openEditModal}

View File

@@ -3,13 +3,14 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { Stack } from '@grafana/experimental'; import { Stack } from '@grafana/experimental';
import { Badge, Button, Dropdown, Icon, Menu, Tooltip, useStyles2 } from '@grafana/ui'; import { Button, Dropdown, Icon, Menu, Tooltip, useStyles2 } from '@grafana/ui';
import { Span } from '@grafana/ui/src/unstable'; import { Span } from '@grafana/ui/src/unstable';
import ConditionalWrap from 'app/features/alerting/components/ConditionalWrap'; import ConditionalWrap from 'app/features/alerting/components/ConditionalWrap';
import { GrafanaNotifierType } from 'app/types/alerting'; import { GrafanaNotifierType } from 'app/types/alerting';
import { INTEGRATION_ICONS } from '../../types/contact-points'; import { INTEGRATION_ICONS } from '../../types/contact-points';
import { MetaText } from '../MetaText'; import { MetaText } from '../MetaText';
import { ProvisioningBadge } from '../Provisioning';
import { Spacer } from '../Spacer'; import { Spacer } from '../Spacer';
import { Strong } from '../Strong'; import { Strong } from '../Strong';
@@ -113,7 +114,7 @@ const ContactPointHeader = (props: ContactPointHeaderProps) => {
) : ( ) : (
<MetaText>is not used</MetaText> <MetaText>is not used</MetaText>
)} )}
{isProvisioned && <Badge color="purple" text="Provisioned" />} {isProvisioned && <ProvisioningBadge />}
<Spacer /> <Spacer />
<ConditionalWrap <ConditionalWrap
shouldWrap={isProvisioned} shouldWrap={isProvisioned}

View File

@@ -9,6 +9,7 @@ import { Stack } from '@grafana/experimental';
import { Badge, Button, Dropdown, getTagColorsFromName, Icon, Menu, Tooltip, useStyles2 } from '@grafana/ui'; import { Badge, Button, Dropdown, getTagColorsFromName, Icon, Menu, Tooltip, useStyles2 } from '@grafana/ui';
import { Span } from '@grafana/ui/src/unstable'; import { Span } from '@grafana/ui/src/unstable';
import { contextSrv } from 'app/core/core'; import { contextSrv } from 'app/core/core';
import ConditionalWrap from 'app/features/alerting/components/ConditionalWrap';
import { RouteWithID, Receiver, ObjectMatcher, AlertmanagerGroup } from 'app/plugins/datasource/alertmanager/types'; import { RouteWithID, Receiver, ObjectMatcher, AlertmanagerGroup } from 'app/plugins/datasource/alertmanager/types';
import { ReceiversState } from 'app/types'; import { ReceiversState } from 'app/types';
@@ -20,6 +21,7 @@ import { getInheritedProperties, InhertitableProperties } from '../../utils/noti
import { HoverCard } from '../HoverCard'; import { HoverCard } from '../HoverCard';
import { Label } from '../Label'; import { Label } from '../Label';
import { MetaText } from '../MetaText'; import { MetaText } from '../MetaText';
import { ProvisioningBadge } from '../Provisioning';
import { Spacer } from '../Spacer'; import { Spacer } from '../Spacer';
import { Strong } from '../Strong'; import { Strong } from '../Strong';
@@ -31,6 +33,7 @@ interface PolicyComponentProps {
alertGroups?: AlertmanagerGroup[]; alertGroups?: AlertmanagerGroup[];
contactPointsState?: ReceiversState; contactPointsState?: ReceiversState;
readOnly?: boolean; readOnly?: boolean;
provisioned?: boolean;
inheritedProperties?: Partial<InhertitableProperties>; inheritedProperties?: Partial<InhertitableProperties>;
routesMatchingFilters?: RouteWithID[]; routesMatchingFilters?: RouteWithID[];
// routeAlertGroupsMap?: Map<string, AlertmanagerGroup[]>; // routeAlertGroupsMap?: Map<string, AlertmanagerGroup[]>;
@@ -50,6 +53,7 @@ const Policy: FC<PolicyComponentProps> = ({
receivers = [], receivers = [],
contactPointsState, contactPointsState,
readOnly = false, readOnly = false,
provisioned = false,
alertGroups = [], alertGroups = [],
alertManagerSourceName, alertManagerSourceName,
currentRoute, currentRoute,
@@ -143,49 +147,57 @@ const Policy: FC<PolicyComponentProps> = ({
<Spacer /> <Spacer />
{/* TODO maybe we should move errors to the gutter instead? */} {/* TODO maybe we should move errors to the gutter instead? */}
{errors.length > 0 && <Errors errors={errors} />} {errors.length > 0 && <Errors errors={errors} />}
{!readOnly && ( {provisioned && <ProvisioningBadge />}
{readOnly ? null : (
<Stack direction="row" gap={0.5}> <Stack direction="row" gap={0.5}>
<Button <ConditionalWrap shouldWrap={provisioned} wrap={ProvisionedTooltip}>
variant="secondary"
icon="plus"
size="sm"
onClick={() => onAddPolicy(currentRoute)}
type="button"
>
New nested policy
</Button>
<Dropdown
overlay={
<Menu>
<Menu.Item
icon="edit"
disabled={!isEditable}
label="Edit"
onClick={() => onEditPolicy(currentRoute, isDefaultPolicy)}
/>
{isDeletable && (
<>
<Menu.Divider />
<Menu.Item
destructive
icon="trash-alt"
label="Delete"
onClick={() => onDeletePolicy(currentRoute)}
/>
</>
)}
</Menu>
}
>
<Button <Button
icon="ellipsis-h"
variant="secondary" variant="secondary"
icon="plus"
size="sm" size="sm"
onClick={() => onAddPolicy(currentRoute)}
disabled={provisioned}
type="button" type="button"
aria-label="more-actions" >
data-testid="more-actions" New nested policy
/> </Button>
</Dropdown> </ConditionalWrap>
<ConditionalWrap shouldWrap={provisioned} wrap={ProvisionedTooltip}>
<Dropdown
overlay={
<Menu>
<Menu.Item
icon="edit"
disabled={!isEditable}
label="Edit"
onClick={() => onEditPolicy(currentRoute, isDefaultPolicy)}
/>
{isDeletable && (
<>
<Menu.Divider />
<Menu.Item
destructive
icon="trash-alt"
label="Delete"
onClick={() => onDeletePolicy(currentRoute)}
/>
</>
)}
</Menu>
}
>
<Button
icon="ellipsis-h"
variant="secondary"
size="sm"
type="button"
aria-label="more-actions"
data-testid="more-actions"
disabled={provisioned}
/>
</Dropdown>
</ConditionalWrap>
</Stack> </Stack>
)} )}
</Stack> </Stack>
@@ -270,7 +282,7 @@ const Policy: FC<PolicyComponentProps> = ({
currentRoute={child} currentRoute={child}
receivers={receivers} receivers={receivers}
contactPointsState={contactPointsState} contactPointsState={contactPointsState}
readOnly={readOnly} readOnly={readOnly || provisioned}
inheritedProperties={childInheritedProperties} inheritedProperties={childInheritedProperties}
onAddPolicy={onAddPolicy} onAddPolicy={onAddPolicy}
onEditPolicy={onEditPolicy} onEditPolicy={onEditPolicy}
@@ -288,6 +300,12 @@ const Policy: FC<PolicyComponentProps> = ({
); );
}; };
const ProvisionedTooltip = (children: ReactNode) => (
<Tooltip content="Provisioned items cannot be edited in the UI" placement="top">
<span>{children}</span>
</Tooltip>
);
const Errors: FC<{ errors: React.ReactNode[] }> = ({ errors }) => ( const Errors: FC<{ errors: React.ReactNode[] }> = ({ errors }) => (
<HoverCard <HoverCard
arrow arrow

View File

@@ -5,7 +5,7 @@ import { useFormContext } from 'react-hook-form';
import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { GrafanaTheme2, SelectableValue } from '@grafana/data';
import { Stack } from '@grafana/experimental'; import { Stack } from '@grafana/experimental';
import { AsyncSelect, Badge, Field, InputControl, Label, useStyles2 } from '@grafana/ui'; import { AsyncSelect, Field, InputControl, Label, useStyles2 } from '@grafana/ui';
import { contextSrv } from 'app/core/core'; import { contextSrv } from 'app/core/core';
import { AccessControlAction, useDispatch } from 'app/types'; import { AccessControlAction, useDispatch } from 'app/types';
import { CombinedRuleGroup } from 'app/types/unified-alerting'; import { CombinedRuleGroup } from 'app/types/unified-alerting';
@@ -18,6 +18,7 @@ import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
import { MINUTE } from '../../utils/rule-form'; import { MINUTE } from '../../utils/rule-form';
import { isGrafanaRulerRule } from '../../utils/rules'; import { isGrafanaRulerRule } from '../../utils/rules';
import { InfoIcon } from '../InfoIcon'; import { InfoIcon } from '../InfoIcon';
import { ProvisioningBadge } from '../Provisioning';
import { Folder, RuleFolderPicker } from './RuleFolderPicker'; import { Folder, RuleFolderPicker } from './RuleFolderPicker';
import { checkForPathSeparator } from './util'; import { checkForPathSeparator } from './util';
@@ -174,7 +175,7 @@ export function FolderAndGroup() {
{option.isDisabled && ( {option.isDisabled && (
<> <>
{' '} {' '}
<Badge color="purple" text="Provisioned" /> <ProvisioningBadge />
</> </>
)} )}
</div> </div>