Skip to content

Commit

Permalink
Implemented cross-cluster monitors. (#871) (#872)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
7 people authored Feb 6, 2024
1 parent 607b2d7 commit a66a331
Show file tree
Hide file tree
Showing 45 changed files with 1,313 additions and 168 deletions.
16 changes: 8 additions & 8 deletions cypress/integration/cluster_metrics_monitor_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const SAMPLE_DESTINATION = 'sample_destination';

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

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

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

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

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

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

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

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

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

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

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

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

// Confirm there are 0 triggers defined
cy.contains('Triggers (0)', { timeout: 20000 });
cy.contains('Triggers (0)', { timeout: 30000 });
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ exports[`AddAlertingMonitor renders 1`] = `
"associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"clusterNames": Array [],
"cronExpression": "0 */1 * * *",
"daily": 0,
"description": "",
Expand Down Expand Up @@ -60,6 +61,7 @@ exports[`AddAlertingMonitor renders 1`] = `
"timezone": Array [],
"uri": Object {
"api_type": "",
"clusters": Array [],
"path": "",
"path_params": "",
"url": "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { getTime } from '../../../../pages/MonitorDetails/components/MonitorOver
import { PLUGIN_NAME } from '../../../../../utils/constants';
import {
ALERT_STATE,
DEFAULT_EMPTY_DATA,
MONITOR_GROUP_BY,
MONITOR_INPUT_DETECTOR_ID,
MONITOR_TYPE,
Expand All @@ -35,8 +36,6 @@ import { UNITS_OF_TIME } from '../../../../pages/CreateMonitor/components/Monito
import { DEFAULT_WHERE_EXPRESSION_TEXT } from '../../../../pages/CreateMonitor/components/MonitorExpressions/expressions/utils/whereHelpers';
import { acknowledgeAlerts, backendErrorNotification } from '../../../../utils/helpers';
import {
displayAcknowledgedAlertsToast,
filterActiveAlerts,
getQueryObjectFromState,
getURLQueryParams,
insertGroupByColumn,
Expand All @@ -54,6 +53,11 @@ import {
TABLE_TAB_IDS,
} from '../../../../pages/Dashboard/components/FindingsDashboard/findingsUtils';
import FindingsDashboard from '../../../../pages/Dashboard/containers/FindingsDashboard';
import { CLUSTER_METRICS_CROSS_CLUSTER_ALERT_TABLE_COLUMN } from '../../../../pages/CreateMonitor/components/ClusterMetricsMonitor/utils/clusterMetricsMonitorConstants';
import {
getDataSources,
getLocalClusterName,
} from '../../../../pages/CreateMonitor/components/CrossClusterConfigurations/utils/helpers';

export const DEFAULT_NUM_FLYOUT_ROWS = 10;

Expand All @@ -73,6 +77,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
alerts: [],
alertState: alertState,
loading: true,
localClusterName: undefined,
monitor: monitor,
monitorIds: [monitor_id],
monitorType: monitorType,
Expand Down Expand Up @@ -103,6 +108,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
alertState,
monitorIds
);
this.getLocalClusterName();
}

componentDidUpdate(prevProps, prevState) {
Expand Down Expand Up @@ -141,7 +147,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
getMultipleGraphConditions = (trigger) => {
let conditions = _.get(trigger, 'condition.script.source');
if (_.isEmpty(conditions)) {
return '-';
return DEFAULT_EMPTY_DATA;
} else {
conditions = conditions.replaceAll(' && ', '&AND&');
conditions = conditions.replaceAll(' || ', '&OR&');
Expand All @@ -150,6 +156,12 @@ export default class AlertsDashboardFlyoutComponent extends Component {
}
};

getLocalClusterName = async () => {
this.setState({
localClusterName: await getLocalClusterName(this.props.httpClient),
});
};

getSeverityText = (severity) => {
return _.get(_.find(SEVERITY_OPTIONS, { value: severity }), 'text');
};
Expand Down Expand Up @@ -319,6 +331,10 @@ export default class AlertsDashboardFlyoutComponent extends Component {
case MONITOR_TYPE.BUCKET_LEVEL:
columns = insertGroupByColumn(groupBy);
break;
case MONITOR_TYPE.CLUSTER_METRICS:
columns = _.cloneDeep(queryColumns);
columns.push(CLUSTER_METRICS_CROSS_CLUSTER_ALERT_TABLE_COLUMN);
break;
case MONITOR_TYPE.DOC_LEVEL:
columns = _.cloneDeep(queryColumns);
columns.splice(
Expand Down Expand Up @@ -495,7 +511,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
triggerID,
trigger_name,
} = this.props;
const { loading, monitor, monitorType, tabContent } = this.state;
const { loading, localClusterName, monitor, monitorType, tabContent } = this.state;
const searchType = _.get(monitor, 'ui_metadata.search.searchType', SEARCH_TYPE.GRAPH);
const triggerType = this.getTriggerType(monitorType);

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

let displayMultipleConditions;
switch (monitorType) {
Expand All @@ -526,7 +542,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
const filters =
monitorType === MONITOR_TYPE.BUCKET_LEVEL && searchType === SEARCH_TYPE.GRAPH
? this.getBucketLevelGraphFilter(trigger)
: '-';
: DEFAULT_EMPTY_DATA;

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

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

const dataSources = getDataSources(monitor, localClusterName).join('\n');
return (
<div>
<EuiFlexGroup>
Expand All @@ -566,7 +583,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
<EuiFlexItem>
<EuiText size={'m'} data-test-subj={`alertsDashboardFlyout_severity_${trigger_name}`}>
<strong>Severity</strong>
<p>{this.getSeverityText(severity) || severity || '-'}</p>
<p>{this.getSeverityText(severity) || severity || DEFAULT_EMPTY_DATA}</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down Expand Up @@ -599,6 +616,12 @@ export default class AlertsDashboardFlyoutComponent extends Component {
</p>
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiText size={'m'}>
<strong>Monitor data sources</strong>
<p style={{ whiteSpace: 'pre-wrap' }}>{dataSources}</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>

<EuiHorizontalRule margin={'xxl'} />
Expand Down Expand Up @@ -651,7 +674,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
? 'Loading groups...'
: !_.isEmpty(groupBy)
? _.join(_.orderBy(groupBy), ', ')
: '-'}
: DEFAULT_EMPTY_DATA}
</p>
</EuiText>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,24 @@ exports[`AlertsDashboardFlyoutComponent renders 1`] = `
</p>
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiText
size="m"
>
<strong>
Monitor data sources
</strong>
<p
style={
Object {
"whiteSpace": "pre-wrap",
}
}
>
-
</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule
margin="xxl"
Expand Down
105 changes: 105 additions & 0 deletions public/components/Flyout/flyouts/dataSources.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { EuiBasicTable, EuiFlexGroup, EuiButtonIcon, EuiTitle, EuiFlexItem } from '@elastic/eui';
import { MONITOR_TYPE } from '../../../utils/constants';

export const DATA_SOURCES_FLYOUT_TYPE = 'dataSources';

const dataSources = ({
closeFlyout = () => {},
dataSources = [],
localClusterName = '',
monitorType = MONITOR_TYPE.QUERY_LEVEL,
}) => {
const columns = [
{
field: 'cluster',
name: 'Data connection',
sortable: true,
truncateText: true,
},
];
switch (monitorType) {
case MONITOR_TYPE.CLUSTER_METRICS:
// Cluster metrics monitors do not use indexes as data sources; excluding that column.
break;
default:
columns.push({
field: 'index',
name: 'Index',
sortable: true,
truncateText: true,
});
}

const indexItems = dataSources.map((dataSource = '', int) => {
const item = { id: int };
switch (monitorType) {
case MONITOR_TYPE.CLUSTER_METRICS:
item.cluster =
dataSource === localClusterName ? `${dataSource} (Local)` : `${dataSource} (Remote)`;
break;
default:
const shouldSplit = dataSource.includes(':');
const splitIndex = dataSource.split(':');
let clusterName = shouldSplit ? splitIndex[0] : localClusterName;
clusterName =
clusterName === localClusterName ? `${clusterName} (Local)` : `${clusterName} (Remote)`;
const indexName = shouldSplit ? splitIndex[1] : dataSource;
item.cluster = clusterName;
item.index = indexName;
}
return item;
});

return {
flyoutProps: {
'aria-labelledby': 'dataSourcesFlyout',
size: 'm',
hideCloseButton: true,
'data-test-subj': `dataSourcesFlyout`,
},
headerProps: { hasBorder: true },
header: (
<EuiFlexGroup justifyContent="flexStart" alignItems="center">
<EuiFlexItem className={'eui-textTruncate'}>
<EuiTitle
className={'eui-textTruncate'}
size={'m'}
data-test-subj={'dataSourcesFlyout_header'}
>
<h3>{`Data sources`}</h3>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
data-test-subj={'dataSourcesFlyout_closeButton'}
iconType={'cross'}
display={'empty'}
iconSize={'m'}
onClick={closeFlyout}
/>
</EuiFlexItem>
</EuiFlexGroup>
),
footerProps: { style: { backgroundColor: '#F5F7FA' } },
body: (
<EuiBasicTable
items={indexItems}
itemId={(item) => item.id}
columns={columns}
pagination={true}
isSelectable={false}
hasActions={false}
noItemsMessage={'No data sources configured for this monitor.'}
data-test-subj={'dataSourcesFlyout_table'}
/>
),
};
};

export default dataSources;
2 changes: 2 additions & 0 deletions public/components/Flyout/flyouts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import message from './message';
import messageFrequency from './messageFrequency';
import triggerCondition from './triggerCondition';
import alertsDashboard from './alertsDashboard';
import dataSources from './dataSources';

const Flyouts = {
messageFrequency,
message,
triggerCondition,
alertsDashboard,
dataSources,
};

export default Flyouts;
Loading

0 comments on commit a66a331

Please sign in to comment.