Skip to content

Commit a66a331

Browse files
opensearch-trigger-bot[bot]github-actions[bot]lezzagopeterzhuamazonamsiglan
authored
Implemented cross-cluster monitors. (#871) (#872)
* Support any channel types from Notification (#743) * Support any channel types from Notification * Remove unused CHANNEL_TYPES constant * Return empty array if failed to get features --------- * Drafted 2.11 release notes. (#764) * Drafted 2.11 release notes. * Drafted 2.11 release notes. --------- * Removed "last updated by" sections from the UI. (#767) * Removed "last updated by" section from the UI as the SearchMonitor API can no longer return that info. * Updated cypress workflow. --------- * Onboard Jenkins prod docker image to github actions (#789) * Onboard Jenkins prod docker image to github actions * Small typos * Add back workflows * Restore macos/windows --------- * Bumped babel version. (#821) * Bumped babel version. Adjusted babel config. Refactored unit test that started failing after bump. * Updated yarn file. --------- * Fix fetching of channels for composite monitors (#820) * fixed incorrect use of this.props * resolved dependency conflict * updated babel config --------- * Added 2.11.1 release notes. (#828) * Amended 2.11.0 release notes. * Added 2.11.1 release notes. * Added 2.11.1 release notes. * Added 2.11.1 release notes. * Added 2.11.1 release notes. --------- * Fixed bucket monitor groupBy/aggregation display bug. (#827) * Fixed a bug that was causing groupBy/aggregation fields from displaying in various areas of the UI. Related issues: 816, 817, 818. * Fixed trigger context object bug in issue 791. * Capitalized bucket column titles, and moved bucket columns to the end of the column array. * Added wait steps to reduce test flakiness. * Added wait step to reduce test flakiness. Adjusted test monitor trigger condition to always triggers on a healthy clusters. * Removed unused imports. * fixed bucket level monitor flaky cypress test --------- * Issue #671 fix trigger name validation (#794) * Remove integtest.sh since it is not being used (#849) * do not create Message component on every text change (#854) * Implemented server API call to feature backend API. * Implemented remote cluster support for creating/editing query, bucket, and cluster metrics monitors. * Implemented warning model when monitor execution time exceeds a certain value. * Updated alert details flyout to show remote cluster info. Updated monitor details page to show data sources. * Updated unit tests. * Added experimental banner. * Updated snapshots. * Edited text on the experimental banner. * Moved getSettings call to hide Data source panel for cluster metrics monitors when remote monitoring is disabled. * Updated snapshots. * Increased cypress test timeout. --------- (cherry picked from commit fb82368) Signed-off-by: Ashish Agrawal <[email protected]> Signed-off-by: AWSHurneyt <[email protected]> Signed-off-by: Peter Zhu <[email protected]> Signed-off-by: Amardeepsingh Siglani <[email protected]> Signed-off-by: Chenxi Wang <[email protected]> Signed-off-by: Derek Ho <[email protected]> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Ashish Agrawal <[email protected]> Co-authored-by: Peter Zhu <[email protected]> Co-authored-by: Amardeepsingh Siglani <[email protected]> Co-authored-by: Chenxi Wang <[email protected]> Co-authored-by: Derek Ho <[email protected]>
1 parent 607b2d7 commit a66a331

File tree

45 files changed

+1313
-168
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1313
-168
lines changed

cypress/integration/cluster_metrics_monitor_spec.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const SAMPLE_DESTINATION = 'sample_destination';
1515

1616
const addClusterMetricsTrigger = (triggerName, triggerIndex, actionName, isEdit, source) => {
1717
// Click 'Add trigger' button
18-
cy.contains('Add trigger', { timeout: 20000 }).click({ force: true });
18+
cy.contains('Add trigger', { timeout: 30000 }).click({ force: true });
1919

2020
if (isEdit === true) {
2121
// TODO: Passing button props in EUI accordion was added in newer versions (31.7.0+).
@@ -37,7 +37,7 @@ const addClusterMetricsTrigger = (triggerName, triggerIndex, actionName, isEdit,
3737
force: true,
3838
parseSpecialCharSequences: false,
3939
delay: 5,
40-
timeout: 20000,
40+
timeout: 30000,
4141
})
4242
.trigger('blur', { force: true });
4343
});
@@ -76,7 +76,7 @@ describe('ClusterMetricsMonitor', () => {
7676
cy.visit(`${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/monitors`);
7777

7878
// Common text to wait for to confirm page loaded, give up to 20 seconds for initial load
79-
cy.contains('Create monitor', { timeout: 20000 });
79+
cy.contains('Create monitor', { timeout: 30000 });
8080
});
8181

8282
describe('can be created', () => {
@@ -87,7 +87,7 @@ describe('ClusterMetricsMonitor', () => {
8787

8888
it('for the Cluster Health API', () => {
8989
// Go to create monitor page
90-
cy.contains('Create monitor', { timeout: 20000 }).click({ force: true });
90+
cy.contains('Create monitor', { timeout: 30000 }).click({ force: true });
9191

9292
// Select ClusterMetrics radio card
9393
cy.get('[data-test-subj="clusterMetricsMonitorRadioCard"]').click({ force: true });
@@ -141,7 +141,7 @@ describe('ClusterMetricsMonitor', () => {
141141

142142
it('for the Nodes Stats API', () => {
143143
// Go to create monitor page
144-
cy.contains('Create monitor', { timeout: 20000 }).click({ force: true });
144+
cy.contains('Create monitor', { timeout: 30000 }).click({ force: true });
145145

146146
// Select ClusterMetrics radio card
147147
cy.get('[data-test-subj="clusterMetricsMonitorRadioCard"]').click({ force: true });
@@ -202,7 +202,7 @@ describe('ClusterMetricsMonitor', () => {
202202

203203
it('for the CAT Snapshots API', () => {
204204
// Go to create monitor page
205-
cy.contains('Create monitor', { timeout: 20000 }).click({ force: true });
205+
cy.contains('Create monitor', { timeout: 30000 }).click({ force: true });
206206

207207
// Select ClusterMetrics radio card
208208
cy.get('[data-test-subj="clusterMetricsMonitorRadioCard"]').click({ force: true });
@@ -228,7 +228,7 @@ describe('ClusterMetricsMonitor', () => {
228228
// Begin monitor creation
229229

230230
// Go to create monitor page
231-
cy.contains('Create monitor', { timeout: 20000 }).click({ force: true });
231+
cy.contains('Create monitor', { timeout: 30000 }).click({ force: true });
232232

233233
// Select ClusterMetrics radio card
234234
cy.get('[data-test-subj="clusterMetricsMonitorRadioCard"]').click({ force: true });
@@ -335,7 +335,7 @@ describe('ClusterMetricsMonitor', () => {
335335
cy.get('[data-test-subj="clusterMetricsApiTypeComboBox"]').contains('Cluster settings');
336336

337337
// Confirm there are 0 triggers defined
338-
cy.contains('Triggers (0)', { timeout: 20000 });
338+
cy.contains('Triggers (0)', { timeout: 30000 });
339339
});
340340
});
341341
});

public/components/FeatureAnywhereContextMenu/AddAlertingMonitor/__snapshots__/AddAlertingMonitor.test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ exports[`AddAlertingMonitor renders 1`] = `
1919
"associatedMonitorsList": Array [],
2020
"bucketUnitOfTime": "h",
2121
"bucketValue": 1,
22+
"clusterNames": Array [],
2223
"cronExpression": "0 */1 * * *",
2324
"daily": 0,
2425
"description": "",
@@ -60,6 +61,7 @@ exports[`AddAlertingMonitor renders 1`] = `
6061
"timezone": Array [],
6162
"uri": Object {
6263
"api_type": "",
64+
"clusters": Array [],
6365
"path": "",
6466
"path_params": "",
6567
"url": "",

public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { getTime } from '../../../../pages/MonitorDetails/components/MonitorOver
2424
import { PLUGIN_NAME } from '../../../../../utils/constants';
2525
import {
2626
ALERT_STATE,
27+
DEFAULT_EMPTY_DATA,
2728
MONITOR_GROUP_BY,
2829
MONITOR_INPUT_DETECTOR_ID,
2930
MONITOR_TYPE,
@@ -35,8 +36,6 @@ import { UNITS_OF_TIME } from '../../../../pages/CreateMonitor/components/Monito
3536
import { DEFAULT_WHERE_EXPRESSION_TEXT } from '../../../../pages/CreateMonitor/components/MonitorExpressions/expressions/utils/whereHelpers';
3637
import { acknowledgeAlerts, backendErrorNotification } from '../../../../utils/helpers';
3738
import {
38-
displayAcknowledgedAlertsToast,
39-
filterActiveAlerts,
4039
getQueryObjectFromState,
4140
getURLQueryParams,
4241
insertGroupByColumn,
@@ -54,6 +53,11 @@ import {
5453
TABLE_TAB_IDS,
5554
} from '../../../../pages/Dashboard/components/FindingsDashboard/findingsUtils';
5655
import FindingsDashboard from '../../../../pages/Dashboard/containers/FindingsDashboard';
56+
import { CLUSTER_METRICS_CROSS_CLUSTER_ALERT_TABLE_COLUMN } from '../../../../pages/CreateMonitor/components/ClusterMetricsMonitor/utils/clusterMetricsMonitorConstants';
57+
import {
58+
getDataSources,
59+
getLocalClusterName,
60+
} from '../../../../pages/CreateMonitor/components/CrossClusterConfigurations/utils/helpers';
5761

5862
export const DEFAULT_NUM_FLYOUT_ROWS = 10;
5963

@@ -73,6 +77,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
7377
alerts: [],
7478
alertState: alertState,
7579
loading: true,
80+
localClusterName: undefined,
7681
monitor: monitor,
7782
monitorIds: [monitor_id],
7883
monitorType: monitorType,
@@ -103,6 +108,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
103108
alertState,
104109
monitorIds
105110
);
111+
this.getLocalClusterName();
106112
}
107113

108114
componentDidUpdate(prevProps, prevState) {
@@ -141,7 +147,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
141147
getMultipleGraphConditions = (trigger) => {
142148
let conditions = _.get(trigger, 'condition.script.source');
143149
if (_.isEmpty(conditions)) {
144-
return '-';
150+
return DEFAULT_EMPTY_DATA;
145151
} else {
146152
conditions = conditions.replaceAll(' && ', '&AND&');
147153
conditions = conditions.replaceAll(' || ', '&OR&');
@@ -150,6 +156,12 @@ export default class AlertsDashboardFlyoutComponent extends Component {
150156
}
151157
};
152158

159+
getLocalClusterName = async () => {
160+
this.setState({
161+
localClusterName: await getLocalClusterName(this.props.httpClient),
162+
});
163+
};
164+
153165
getSeverityText = (severity) => {
154166
return _.get(_.find(SEVERITY_OPTIONS, { value: severity }), 'text');
155167
};
@@ -319,6 +331,10 @@ export default class AlertsDashboardFlyoutComponent extends Component {
319331
case MONITOR_TYPE.BUCKET_LEVEL:
320332
columns = insertGroupByColumn(groupBy);
321333
break;
334+
case MONITOR_TYPE.CLUSTER_METRICS:
335+
columns = _.cloneDeep(queryColumns);
336+
columns.push(CLUSTER_METRICS_CROSS_CLUSTER_ALERT_TABLE_COLUMN);
337+
break;
322338
case MONITOR_TYPE.DOC_LEVEL:
323339
columns = _.cloneDeep(queryColumns);
324340
columns.splice(
@@ -495,7 +511,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
495511
triggerID,
496512
trigger_name,
497513
} = this.props;
498-
const { loading, monitor, monitorType, tabContent } = this.state;
514+
const { loading, localClusterName, monitor, monitorType, tabContent } = this.state;
499515
const searchType = _.get(monitor, 'ui_metadata.search.searchType', SEARCH_TYPE.GRAPH);
500516
const triggerType = this.getTriggerType(monitorType);
501517

@@ -511,7 +527,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
511527
searchType === SEARCH_TYPE.GRAPH &&
512528
(monitorType === MONITOR_TYPE.BUCKET_LEVEL || monitorType === MONITOR_TYPE.DOC_LEVEL)
513529
? this.getMultipleGraphConditions(trigger)
514-
: _.get(trigger, 'condition.script.source', '-');
530+
: _.get(trigger, 'condition.script.source', DEFAULT_EMPTY_DATA);
515531

516532
let displayMultipleConditions;
517533
switch (monitorType) {
@@ -526,7 +542,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
526542
const filters =
527543
monitorType === MONITOR_TYPE.BUCKET_LEVEL && searchType === SEARCH_TYPE.GRAPH
528544
? this.getBucketLevelGraphFilter(trigger)
529-
: '-';
545+
: DEFAULT_EMPTY_DATA;
530546

531547
const bucketValue = _.get(monitor, 'ui_metadata.search.bucketValue');
532548
let bucketUnitOfTime = _.get(monitor, 'ui_metadata.search.bucketUnitOfTime');
@@ -536,7 +552,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
536552
const timeRangeForLast =
537553
bucketValue !== undefined && !_.isEmpty(bucketUnitOfTime)
538554
? `${bucketValue} ${bucketUnitOfTime}`
539-
: '-';
555+
: DEFAULT_EMPTY_DATA;
540556

541557
let displayTableTabs;
542558
switch (monitorType) {
@@ -551,6 +567,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
551567
monitorType === MONITOR_TYPE.COMPOSITE_LEVEL ? '?type=workflow' : ''
552568
}`;
553569

570+
const dataSources = getDataSources(monitor, localClusterName).join('\n');
554571
return (
555572
<div>
556573
<EuiFlexGroup>
@@ -566,7 +583,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
566583
<EuiFlexItem>
567584
<EuiText size={'m'} data-test-subj={`alertsDashboardFlyout_severity_${trigger_name}`}>
568585
<strong>Severity</strong>
569-
<p>{this.getSeverityText(severity) || severity || '-'}</p>
586+
<p>{this.getSeverityText(severity) || severity || DEFAULT_EMPTY_DATA}</p>
570587
</EuiText>
571588
</EuiFlexItem>
572589
</EuiFlexGroup>
@@ -599,6 +616,12 @@ export default class AlertsDashboardFlyoutComponent extends Component {
599616
</p>
600617
</EuiText>
601618
</EuiFlexItem>
619+
<EuiFlexItem>
620+
<EuiText size={'m'}>
621+
<strong>Monitor data sources</strong>
622+
<p style={{ whiteSpace: 'pre-wrap' }}>{dataSources}</p>
623+
</EuiText>
624+
</EuiFlexItem>
602625
</EuiFlexGroup>
603626

604627
<EuiHorizontalRule margin={'xxl'} />
@@ -651,7 +674,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
651674
? 'Loading groups...'
652675
: !_.isEmpty(groupBy)
653676
? _.join(_.orderBy(groupBy), ', ')
654-
: '-'}
677+
: DEFAULT_EMPTY_DATA}
655678
</p>
656679
</EuiText>
657680
</EuiFlexItem>

public/components/Flyout/flyouts/components/__snapshots__/AlertsDashboardFlyoutComponent.test.js.snap

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,24 @@ exports[`AlertsDashboardFlyoutComponent renders 1`] = `
7676
</p>
7777
</EuiText>
7878
</EuiFlexItem>
79+
<EuiFlexItem>
80+
<EuiText
81+
size="m"
82+
>
83+
<strong>
84+
Monitor data sources
85+
</strong>
86+
<p
87+
style={
88+
Object {
89+
"whiteSpace": "pre-wrap",
90+
}
91+
}
92+
>
93+
-
94+
</p>
95+
</EuiText>
96+
</EuiFlexItem>
7997
</EuiFlexGroup>
8098
<EuiHorizontalRule
8199
margin="xxl"
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import { EuiBasicTable, EuiFlexGroup, EuiButtonIcon, EuiTitle, EuiFlexItem } from '@elastic/eui';
8+
import { MONITOR_TYPE } from '../../../utils/constants';
9+
10+
export const DATA_SOURCES_FLYOUT_TYPE = 'dataSources';
11+
12+
const dataSources = ({
13+
closeFlyout = () => {},
14+
dataSources = [],
15+
localClusterName = '',
16+
monitorType = MONITOR_TYPE.QUERY_LEVEL,
17+
}) => {
18+
const columns = [
19+
{
20+
field: 'cluster',
21+
name: 'Data connection',
22+
sortable: true,
23+
truncateText: true,
24+
},
25+
];
26+
switch (monitorType) {
27+
case MONITOR_TYPE.CLUSTER_METRICS:
28+
// Cluster metrics monitors do not use indexes as data sources; excluding that column.
29+
break;
30+
default:
31+
columns.push({
32+
field: 'index',
33+
name: 'Index',
34+
sortable: true,
35+
truncateText: true,
36+
});
37+
}
38+
39+
const indexItems = dataSources.map((dataSource = '', int) => {
40+
const item = { id: int };
41+
switch (monitorType) {
42+
case MONITOR_TYPE.CLUSTER_METRICS:
43+
item.cluster =
44+
dataSource === localClusterName ? `${dataSource} (Local)` : `${dataSource} (Remote)`;
45+
break;
46+
default:
47+
const shouldSplit = dataSource.includes(':');
48+
const splitIndex = dataSource.split(':');
49+
let clusterName = shouldSplit ? splitIndex[0] : localClusterName;
50+
clusterName =
51+
clusterName === localClusterName ? `${clusterName} (Local)` : `${clusterName} (Remote)`;
52+
const indexName = shouldSplit ? splitIndex[1] : dataSource;
53+
item.cluster = clusterName;
54+
item.index = indexName;
55+
}
56+
return item;
57+
});
58+
59+
return {
60+
flyoutProps: {
61+
'aria-labelledby': 'dataSourcesFlyout',
62+
size: 'm',
63+
hideCloseButton: true,
64+
'data-test-subj': `dataSourcesFlyout`,
65+
},
66+
headerProps: { hasBorder: true },
67+
header: (
68+
<EuiFlexGroup justifyContent="flexStart" alignItems="center">
69+
<EuiFlexItem className={'eui-textTruncate'}>
70+
<EuiTitle
71+
className={'eui-textTruncate'}
72+
size={'m'}
73+
data-test-subj={'dataSourcesFlyout_header'}
74+
>
75+
<h3>{`Data sources`}</h3>
76+
</EuiTitle>
77+
</EuiFlexItem>
78+
<EuiFlexItem grow={false}>
79+
<EuiButtonIcon
80+
data-test-subj={'dataSourcesFlyout_closeButton'}
81+
iconType={'cross'}
82+
display={'empty'}
83+
iconSize={'m'}
84+
onClick={closeFlyout}
85+
/>
86+
</EuiFlexItem>
87+
</EuiFlexGroup>
88+
),
89+
footerProps: { style: { backgroundColor: '#F5F7FA' } },
90+
body: (
91+
<EuiBasicTable
92+
items={indexItems}
93+
itemId={(item) => item.id}
94+
columns={columns}
95+
pagination={true}
96+
isSelectable={false}
97+
hasActions={false}
98+
noItemsMessage={'No data sources configured for this monitor.'}
99+
data-test-subj={'dataSourcesFlyout_table'}
100+
/>
101+
),
102+
};
103+
};
104+
105+
export default dataSources;

public/components/Flyout/flyouts/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import message from './message';
77
import messageFrequency from './messageFrequency';
88
import triggerCondition from './triggerCondition';
99
import alertsDashboard from './alertsDashboard';
10+
import dataSources from './dataSources';
1011

1112
const Flyouts = {
1213
messageFrequency,
1314
message,
1415
triggerCondition,
1516
alertsDashboard,
17+
dataSources,
1618
};
1719

1820
export default Flyouts;

0 commit comments

Comments
 (0)