Skip to content

Commit a2d7757

Browse files
committed
Links per alert
1 parent 0e8bdb3 commit a2d7757

File tree

14 files changed

+178
-149
lines changed

14 files changed

+178
-149
lines changed

pkg/handler/alertingmock/alerting_mock.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"math/rand/v2"
77
"net/http"
8+
"net/url"
89
"strings"
910

1011
"github.com/prometheus/common/model"
@@ -98,7 +99,7 @@ func createAlerts(probability float64, name string, threshold, upperBound int, t
9899
return alerts, ruleState
99100
}
100101

101-
func createRule(probability float64, name, severity string, threshold, upperBound int, bynetobs bool, nsLbl, nodeLbl []string) AlertingRule {
102+
func createRule(probability float64, name, severity, extraFilter string, threshold, upperBound int, bynetobs bool, nsLbl, nodeLbl []string) AlertingRule {
102103
labels := model.LabelSet{
103104
"severity": model.LabelValue(severity),
104105
}
@@ -125,12 +126,19 @@ func createRule(probability float64, name, severity string, threshold, upperBoun
125126
}
126127
jsonNodeLbl = fmt.Sprintf(`"nodeLabels":[%s],`, strings.Join(quotedLbl, ","))
127128
}
129+
searchURL := "https://duckduckgo.com/?q=" + url.PathEscape(name)
130+
var extraFilterJSON string
131+
if extraFilter != "" {
132+
extraFilterJSON = fmt.Sprintf(`,"trafficLinkFilter":"%s"`, extraFilter)
133+
}
128134
annotations["netobserv_io_network_health"] = model.LabelValue(fmt.Sprintf(
129-
`{%s%s"threshold":"%d","upperBound":"%d","unit":"%%"}`,
135+
`{%s%s"threshold":"%d","upperBound":"%d","unit":"%%","links":[{"name":"Search the web", "url": "%s"}]%s}`,
130136
jsonNsLbl,
131137
jsonNodeLbl,
132138
threshold,
133139
upperBound,
140+
searchURL,
141+
extraFilterJSON,
134142
))
135143
ruleLabels := labels.Clone()
136144
ruleLabels["prometheus"] = "openshift-monitoring/k8s"
@@ -160,13 +168,13 @@ func createRule(probability float64, name, severity string, threshold, upperBoun
160168
func GetRules() func(w http.ResponseWriter, r *http.Request) {
161169
return func(w http.ResponseWriter, _ *http.Request) {
162170
alertingRules := []AlertingRule{
163-
createRule(0.4, "Packet delivery failed", "info", 5, 100, true, []string{"SrcK8S_Namespace", "DstK8S_Namespace"}, []string{}),
164-
createRule(0.3, "You have reached your hourly rate limit", "info", 5, 100, true, []string{"SrcK8S_Namespace", "DstK8S_Namespace"}, []string{}),
165-
createRule(0.1, "It's always DNS", "warning", 15, 100, true, []string{"SrcK8S_Namespace", "DstK8S_Namespace"}, []string{}),
166-
createRule(0.1, "We're under attack", "warning", 20, 100, true, []string{}, []string{}),
167-
createRule(0.1, "Sh*t - Famous last words", "critical", 5, 100, true, []string{}, []string{"SrcK8S_Hostname", "DstK8S_Hostname"}),
168-
createRule(0.3, "FromIngress", "info", 10, 100, false, []string{"exported_namespace"}, []string{}),
169-
createRule(0.3, "Degraded latency", "info", 100, 1000, true, []string{"SrcK8S_Namespace", "DstK8S_Namespace"}, []string{}),
171+
createRule(0.4, "Packet delivery failed", "info", "", 5, 100, true, []string{"SrcK8S_Namespace", "DstK8S_Namespace"}, []string{}),
172+
createRule(0.3, "You have reached your hourly rate limit", "info", "", 5, 100, true, []string{"SrcK8S_Namespace", "DstK8S_Namespace"}, []string{}),
173+
createRule(0.1, "It's always DNS", "warning", `dns_flag_response_code!=\"\"`, 15, 100, true, []string{"SrcK8S_Namespace", "DstK8S_Namespace"}, []string{}),
174+
createRule(0.1, "We're under attack", "warning", "", 20, 100, true, []string{}, []string{}),
175+
createRule(0.1, "Sh*t - Famous last words", "critical", "", 5, 100, true, []string{}, []string{"SrcK8S_Hostname", "DstK8S_Hostname"}),
176+
createRule(0.3, "FromIngress", "info", "", 10, 100, false, []string{"exported_namespace"}, []string{}),
177+
createRule(0.3, "Degraded latency", "info", "", 100, 1000, true, []string{"SrcK8S_Namespace", "DstK8S_Namespace"}, []string{}),
170178
}
171179
res := map[string]any{
172180
"status": "success",

web/locales/en/plugin__netobserv-plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@
240240
"Severity": "Severity",
241241
"Labels": "Labels",
242242
"Description": "Description",
243+
"Navigate to network traffic": "Navigate to network traffic",
243244
"Global": "Global",
244245
"critical issues": "critical issues",
245246
"warnings": "warnings",
@@ -248,7 +249,6 @@
248249
"silenced issues": "silenced issues",
249250
"Score": "Score",
250251
"Weight": "Weight",
251-
"View in Network Traffic": "View in Network Traffic",
252252
"No violations found": "No violations found",
253253
"Global rule violations": "Global rule violations",
254254
"No rules found, health cannot be determined": "No rules found, health cannot be determined",

web/src/components/health/__tests__/helper.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AlertStates } from '@openshift-console/dynamic-plugin-sdk';
2-
import { AlertWithRuleName, ByResource, computeAlertScore, computeScore } from '../helper';
2+
import { AlertWithRuleName, ByResource, computeAlertScore, computeScore } from '../health-helper';
33

44
const mockAlert = (
55
name: string,
@@ -14,7 +14,7 @@ const mockAlert = (
1414
state: state as AlertStates,
1515
annotations: {},
1616
ruleID: '',
17-
metadata: { thresholdF: threshold, threshold: '', upperBoundF: 100, upperBound: '', unit: '%' },
17+
metadata: { thresholdF: threshold, threshold: '', upperBoundF: 100, upperBound: '', unit: '%', links: [] },
1818
value: value
1919
};
2020
};

web/src/components/health/alert-details.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Label, Text, TextContent, TextVariants } from '@patternfly/react-core';
44
import { useTranslation } from 'react-i18next';
55
import { Link } from 'react-router-dom';
66
import { valueFormat } from '../../utils/format';
7-
import { AlertWithRuleName, getAlertFilteredLabels, getAlertLink, getHealthMetadata } from './helper';
7+
import { AlertWithRuleName, getAlertFilteredLabels, getAlertLink } from './health-helper';
88

99
export interface AlertDetailsProps {
1010
resourceName: string;
@@ -13,8 +13,6 @@ export interface AlertDetailsProps {
1313

1414
export const AlertDetails: React.FC<AlertDetailsProps> = ({ resourceName, alert }) => {
1515
const { t } = useTranslation('plugin__netobserv-plugin');
16-
17-
const md = getHealthMetadata(alert.annotations);
1816
const labels = getAlertFilteredLabels(alert, resourceName);
1917

2018
return (
@@ -39,7 +37,7 @@ export const AlertDetails: React.FC<AlertDetailsProps> = ({ resourceName, alert
3937
<AlertDetailsValue title={t('Value')}>
4038
<>
4139
{valueFormat(alert.value as number, 2)}
42-
{md?.threshold && ' > ' + md.threshold + ' ' + md.unit}
40+
{alert.metadata.threshold && ' > ' + alert.metadata.threshold + ' ' + alert.metadata.unit}
4341
</>
4442
</AlertDetailsValue>
4543
<AlertDetailsValue title={t('Description')}>{alert.annotations['description']}</AlertDetailsValue>
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { ActionsColumn, Td, Tr } from '@patternfly/react-table';
2+
import * as React from 'react';
3+
4+
import { Label, Tooltip } from '@patternfly/react-core';
5+
import { useTranslation } from 'react-i18next';
6+
import { valueFormat } from '../../utils/format';
7+
import { HealthColorSquare } from './health-color-square';
8+
import { AlertWithRuleName, getAlertFilteredLabels, getAlertLink, getTrafficLink } from './health-helper';
9+
10+
export interface AlertRowProps {
11+
kind: string;
12+
resourceName: string;
13+
alert: AlertWithRuleName;
14+
wide: boolean;
15+
}
16+
17+
export const AlertRow: React.FC<AlertRowProps> = ({ kind, resourceName, alert, wide }) => {
18+
const { t } = useTranslation('plugin__netobserv-plugin');
19+
20+
const labels = getAlertFilteredLabels(alert, resourceName);
21+
const links = [
22+
{
23+
name: t('Navigate to alert details'),
24+
url: getAlertLink(alert)
25+
},
26+
{
27+
name: t('Navigate to network traffic'),
28+
url: getTrafficLink(kind, resourceName, alert)
29+
},
30+
...alert.metadata.links
31+
];
32+
33+
return (
34+
<Tr>
35+
{wide && (
36+
<Td>
37+
<AlertSummaryCell alert={alert} showTooltip={false} />
38+
</Td>
39+
)}
40+
<Td noPadding={!wide}>{alert.state}</Td>
41+
<Td>{alert.labels.severity}</Td>
42+
<Td>
43+
{labels.length === 0
44+
? t('None')
45+
: labels.map(kv => (
46+
<Label key={kv[0]}>
47+
{kv[0]}={kv[1]}
48+
</Label>
49+
))}
50+
</Td>
51+
<Td>
52+
{valueFormat(alert.value as number, 2)}
53+
{alert.metadata.threshold && ' > ' + alert.metadata.threshold + ' ' + alert.metadata.unit}
54+
</Td>
55+
{wide && <Td>{alert.annotations['description']}</Td>}
56+
<Td noPadding>
57+
<ActionsColumn
58+
isDisabled={links.length === 0}
59+
items={links.map(l => {
60+
return {
61+
title: <a href={l.url}>{l.name}</a>
62+
};
63+
})}
64+
/>
65+
</Td>
66+
</Tr>
67+
);
68+
};
69+
70+
export const AlertSummaryCell: React.FC<{ alert: AlertWithRuleName; showTooltip: boolean }> = ({
71+
alert,
72+
showTooltip
73+
}) => {
74+
return (
75+
<>
76+
<HealthColorSquare alert={alert} />
77+
{showTooltip ? (
78+
<Tooltip content={alert.annotations['description']}>
79+
<span>{alert.annotations['summary']}</span>
80+
</Tooltip>
81+
) : (
82+
<>{alert.annotations['summary']}</>
83+
)}
84+
</>
85+
);
86+
};

web/src/components/health/health-card.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { BellIcon, ExclamationCircleIcon, ExclamationTriangleIcon, InfoAltIcon }
1414
import * as React from 'react';
1515
import { useTranslation } from 'react-i18next';
1616
import { valueFormat } from '../../utils/format';
17-
import { ByResource } from './helper';
17+
import { ByResource } from './health-helper';
1818

1919
export interface HealthCardProps {
2020
stats: ByResource;

web/src/components/health/health-color-square.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22
import { useTranslation } from 'react-i18next';
33
import { valueFormat } from '../../utils/format';
4-
import { AlertWithRuleName, computeAlertScore, computeExcessRatioStatusWeighted } from './helper';
4+
import { AlertWithRuleName, computeAlertScore, computeExcessRatioStatusWeighted } from './health-helper';
55

66
import { Tooltip } from '@patternfly/react-core';
77
import './health-color-square.css';

web/src/components/health/health-drawer-container.tsx

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,13 @@ import {
55
DrawerContentBody,
66
DrawerHead,
77
DrawerPanelContent,
8-
Dropdown,
9-
DropdownItem,
10-
DropdownList,
11-
MenuToggle,
12-
MenuToggleElement,
138
Text,
149
TextContent,
1510
TextVariants
1611
} from '@patternfly/react-core';
17-
import { EllipsisVIcon } from '@patternfly/react-icons';
1812
import * as React from 'react';
19-
import { useTranslation } from 'react-i18next';
2013
import { HealthGallery } from './health-gallery';
21-
import { ByResource } from './helper';
14+
import { ByResource } from './health-helper';
2215
import { RuleDetails } from './rule-details';
2316

2417
export interface HealthDrawerContainerProps {
@@ -29,9 +22,7 @@ export interface HealthDrawerContainerProps {
2922
}
3023

3124
export const HealthDrawerContainer: React.FC<HealthDrawerContainerProps> = ({ title, stats, kind, isDark }) => {
32-
const { t } = useTranslation('plugin__netobserv-plugin');
3325
const [selectedResource, setSelectedResource] = React.useState<ByResource | undefined>(undefined);
34-
const [isKebabOpen, setKebabOpen] = React.useState(false);
3526
const drawerRef = React.useRef<HTMLDivElement>(null);
3627

3728
const onExpand = () => {
@@ -49,16 +40,6 @@ export const HealthDrawerContainer: React.FC<HealthDrawerContainerProps> = ({ ti
4940
// eslint-disable-next-line react-hooks/exhaustive-deps
5041
}, [stats]);
5142

52-
const filter = encodeURIComponent(
53-
`${kind === 'Namespace' ? 'src_namespace' : 'src_node'}="${selectedResource?.name}"`
54-
);
55-
const kebabLinks = [
56-
{
57-
to: `/netflow-traffic?filters=${filter}&bnf=true`,
58-
text: t('View in Network Traffic')
59-
}
60-
];
61-
6243
return (
6344
<>
6445
<TextContent>
@@ -78,38 +59,13 @@ export const HealthDrawerContainer: React.FC<HealthDrawerContainerProps> = ({ ti
7859
{selectedResource !== undefined && (
7960
<>
8061
<ResourceLink inline={true} kind={kind} name={selectedResource.name} />
81-
<Dropdown
82-
isOpen={isKebabOpen}
83-
onSelect={() => setKebabOpen(false)}
84-
onOpenChange={(isOpen: boolean) => setKebabOpen(isOpen)}
85-
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
86-
<MenuToggle
87-
ref={toggleRef}
88-
variant="plain"
89-
onClick={() => setKebabOpen(!isKebabOpen)}
90-
isExpanded={isKebabOpen}
91-
>
92-
<EllipsisVIcon />
93-
</MenuToggle>
94-
)}
95-
>
96-
<DropdownList>
97-
{kebabLinks.map((l, i) => {
98-
return (
99-
<DropdownItem key={'link_' + i} value={i} to={l.to}>
100-
{l.text}
101-
</DropdownItem>
102-
);
103-
})}
104-
</DropdownList>
105-
</Dropdown>
10662
</>
10763
)}
10864
</span>
10965
</DrawerHead>
11066
{selectedResource && (
11167
<div className="health-gallery-drawer-content">
112-
<RuleDetails info={selectedResource} header={false} />
68+
<RuleDetails kind={kind} info={selectedResource} wide={false} />
11369
</div>
11470
)}
11571
</DrawerPanelContent>

web/src/components/health/health-gallery.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as _ from 'lodash';
44
import * as React from 'react';
55
import { useTranslation } from 'react-i18next';
66
import { HealthCard } from './health-card';
7-
import { ByResource } from './helper';
7+
import { ByResource } from './health-helper';
88

99
export interface HealthGalleryProps {
1010
stats: ByResource[];

web/src/components/health/health-global.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { CheckCircleIcon } from '@patternfly/react-icons';
1313
import * as React from 'react';
1414
import { useTranslation } from 'react-i18next';
1515
import { HealthCard } from './health-card';
16-
import { ByResource, getAllAlerts } from './helper';
16+
import { ByResource, getAllAlerts } from './health-helper';
1717
import { RuleDetails } from './rule-details';
1818

1919
export interface HealthGlobalProps {
@@ -43,7 +43,7 @@ export const HealthGlobal: React.FC<HealthGlobalProps> = ({ info, isDark }) => {
4343
<HealthCard isDark={isDark} stats={info} isSelected={false} />
4444
</GridItem>
4545
<GridItem span={9}>
46-
<RuleDetails info={info} header={true} />
46+
<RuleDetails kind={'Global'} info={info} wide={true} />
4747
</GridItem>
4848
</Grid>
4949
)}

0 commit comments

Comments
 (0)