Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 2.x] Implemented cross-cluster monitors. #872

Merged
merged 1 commit into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading