[Monitoring] Support for unlinked deployments (#28278)
* Unlinked deployment working for beats * Use better constant * Show N/A for license * Rename to Unlinked Cluster * Use callout to mention unlinked cluster * PR feedback * Use fragment * Speed up the query by using terminate_after * Handle failures more defensively * Remove unnecessary msearch * PR feedback * PR feedback and a bit of light refactor * Updated text * Add api integration tests * Localize call out * Update loc pattern * Fix improper i18n.translate usage * Revert "Fix improper i18n.translate usage" This reverts commit0e2e7608c3
. * Revert "Update loc pattern" This reverts commitcc99fe8a8a
. * Ensure the unlinked deployment cluster counts as a valid cluster * Sometimes, you miss the smallest things * Ensure the unlinked cluster is supported, in that users can click the link and load it * Update tests * PR feedback. Simplifying the flag supported code and adding more tests * Update naming * Rename to Standalone Cluster * Remove unnecessary file * Move logic for setting isSupported to exclusively in flag supported clusters code, update tests
This commit is contained in:
parent
50ec75f800
commit
9f37c1fd0a
|
@ -150,3 +150,5 @@ export const DEBOUNCE_FAST_MS = 10; // roughly how long it takes to render a fra
|
||||||
* Configuration key for setting the email address used for cluster alert notifications.
|
* Configuration key for setting the email address used for cluster alert notifications.
|
||||||
*/
|
*/
|
||||||
export const CLUSTER_ALERTS_ADDRESS_CONFIG_KEY = 'cluster_alerts.email_notifications.email_address';
|
export const CLUSTER_ALERTS_ADDRESS_CONFIG_KEY = 'cluster_alerts.email_notifications.email_address';
|
||||||
|
|
||||||
|
export const STANDALONE_CLUSTER_CLUSTER_UUID = '__standalone_cluster__';
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { Listing } from './listing';
|
|
@ -3,20 +3,20 @@
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
import React, { Fragment, Component } from 'react';
|
||||||
import React, { Fragment } from 'react';
|
import chrome from 'ui/chrome';
|
||||||
import { render } from 'react-dom';
|
|
||||||
import { capitalize, partial } from 'lodash';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import numeral from '@elastic/numeral';
|
import numeral from '@elastic/numeral';
|
||||||
import { uiModules } from 'ui/modules';
|
import { capitalize, partial } from 'lodash';
|
||||||
import chrome from 'ui/chrome';
|
|
||||||
import {
|
import {
|
||||||
EuiHealth,
|
EuiHealth,
|
||||||
EuiLink,
|
EuiLink,
|
||||||
EuiPage,
|
EuiPage,
|
||||||
EuiPageBody,
|
EuiPageBody,
|
||||||
EuiPageContent,
|
EuiPageContent,
|
||||||
|
EuiCallOut,
|
||||||
|
EuiSpacer,
|
||||||
|
EuiIcon
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import { toastNotifications } from 'ui/notify';
|
import { toastNotifications } from 'ui/notify';
|
||||||
import { EuiMonitoringTable } from 'plugins/monitoring/components/table';
|
import { EuiMonitoringTable } from 'plugins/monitoring/components/table';
|
||||||
|
@ -24,11 +24,14 @@ import { Tooltip } from 'plugins/monitoring/components/tooltip';
|
||||||
import { AlertsIndicator } from 'plugins/monitoring/components/cluster/listing/alerts_indicator';
|
import { AlertsIndicator } from 'plugins/monitoring/components/cluster/listing/alerts_indicator';
|
||||||
import { I18nProvider, FormattedMessage } from '@kbn/i18n/react';
|
import { I18nProvider, FormattedMessage } from '@kbn/i18n/react';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../../common/constants';
|
||||||
|
|
||||||
const IsClusterSupported = ({ isSupported, children }) => {
|
const IsClusterSupported = ({ isSupported, children }) => {
|
||||||
return isSupported ? children : '-';
|
return isSupported ? children : '-';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const STANDALONE_CLUSTER_STORAGE_KEY = 'viewedStandaloneCluster';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This checks if alerts feature is supported via monitoring cluster
|
* This checks if alerts feature is supported via monitoring cluster
|
||||||
* license. If the alerts feature is not supported because the prod cluster
|
* license. If the alerts feature is not supported because the prod cluster
|
||||||
|
@ -195,6 +198,17 @@ const getColumns = (
|
||||||
sortable: true,
|
sortable: true,
|
||||||
render: (licenseType, cluster) => {
|
render: (licenseType, cluster) => {
|
||||||
const license = cluster.license;
|
const license = cluster.license;
|
||||||
|
|
||||||
|
if (!licenseType) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="monTableCell__clusterCellLiscense">
|
||||||
|
N/A
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (license) {
|
if (license) {
|
||||||
const licenseExpiry = () => {
|
const licenseExpiry = () => {
|
||||||
if (license.expiry_date_in_millis < moment().valueOf()) {
|
if (license.expiry_date_in_millis < moment().valueOf()) {
|
||||||
|
@ -342,72 +356,112 @@ const handleClickInvalidLicense = (scope, clusterName) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const uiModule = uiModules.get('monitoring/directives', []);
|
export class Listing extends Component {
|
||||||
uiModule.directive('monitoringClusterListing', ($injector) => {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
restrict: 'E',
|
this.state = {
|
||||||
scope: {
|
[STANDALONE_CLUSTER_STORAGE_KEY]: false,
|
||||||
clusters: '=',
|
};
|
||||||
sorting: '=',
|
}
|
||||||
filterText: '=',
|
|
||||||
paginationSettings: '=pagination',
|
|
||||||
onTableChange: '=',
|
|
||||||
},
|
|
||||||
link(scope, $el) {
|
|
||||||
const globalState = $injector.get('globalState');
|
|
||||||
const kbnUrl = $injector.get('kbnUrl');
|
|
||||||
const showLicenseExpiration = $injector.get('showLicenseExpiration');
|
|
||||||
|
|
||||||
const _changeCluster = partial(changeCluster, scope, globalState, kbnUrl);
|
|
||||||
const _handleClickIncompatibleLicense = partial(handleClickIncompatibleLicense, scope);
|
|
||||||
const _handleClickInvalidLicense = partial(handleClickInvalidLicense, scope);
|
|
||||||
|
|
||||||
const { sorting, pagination, onTableChange } = scope;
|
|
||||||
|
|
||||||
scope.$watch('clusters', (clusters = []) => {
|
|
||||||
const clusterTable = (
|
|
||||||
<I18nProvider>
|
|
||||||
<EuiPage>
|
|
||||||
<EuiPageBody>
|
|
||||||
<EuiPageContent>
|
|
||||||
<EuiMonitoringTable
|
|
||||||
className="clusterTable"
|
|
||||||
rows={clusters}
|
|
||||||
columns={getColumns(
|
|
||||||
showLicenseExpiration,
|
|
||||||
_changeCluster,
|
|
||||||
_handleClickIncompatibleLicense,
|
|
||||||
_handleClickInvalidLicense
|
|
||||||
)}
|
|
||||||
rowProps={item => {
|
|
||||||
return {
|
|
||||||
'data-test-subj': `clusterRow_${item.cluster_uuid}`
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
sorting={{
|
|
||||||
...sorting,
|
|
||||||
sort: {
|
|
||||||
...sorting.sort,
|
|
||||||
field: 'cluster_name'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
pagination={pagination}
|
|
||||||
search={{
|
|
||||||
box: {
|
|
||||||
incremental: true,
|
|
||||||
placeholder: scope.filterText
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onTableChange={onTableChange}
|
|
||||||
/>
|
|
||||||
</EuiPageContent>
|
|
||||||
</EuiPageBody>
|
|
||||||
</EuiPage>
|
|
||||||
</I18nProvider>
|
|
||||||
);
|
|
||||||
render(clusterTable, $el[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
renderStandaloneClusterCallout(changeCluster, storage) {
|
||||||
|
if (storage.get(STANDALONE_CLUSTER_STORAGE_KEY)) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
});
|
return (
|
||||||
|
<div>
|
||||||
|
<EuiCallOut
|
||||||
|
color="warning"
|
||||||
|
title={i18n.translate('xpack.monitoring.cluster.listing.standaloneClusterCallOutTitle', {
|
||||||
|
defaultMessage: 'It looks like you have instances that aren\'t connected to an Elasticsearch cluster.'
|
||||||
|
})}
|
||||||
|
iconType="link"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<EuiLink
|
||||||
|
onClick={() => changeCluster(STANDALONE_CLUSTER_CLUSTER_UUID)}
|
||||||
|
data-test-subj="standaloneClusterLink"
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.monitoring.cluster.listing.standaloneClusterCallOutLink"
|
||||||
|
defaultMessage="View these instances."
|
||||||
|
/>
|
||||||
|
</EuiLink>
|
||||||
|
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.monitoring.cluster.listing.standaloneClusterCallOutText"
|
||||||
|
defaultMessage="Or, click Standalone Cluster in the table below"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<EuiLink onClick={() => {
|
||||||
|
storage.set(STANDALONE_CLUSTER_STORAGE_KEY, true);
|
||||||
|
this.setState({ [STANDALONE_CLUSTER_STORAGE_KEY]: true });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EuiIcon type="cross"/>
|
||||||
|
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.monitoring.cluster.listing.standaloneClusterCallOutDismiss"
|
||||||
|
defaultMessage="Dismiss"
|
||||||
|
/>
|
||||||
|
</EuiLink>
|
||||||
|
</p>
|
||||||
|
</EuiCallOut>
|
||||||
|
<EuiSpacer/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { angular, clusters, sorting, pagination, onTableChange } = this.props;
|
||||||
|
|
||||||
|
const _changeCluster = partial(changeCluster, angular.scope, angular.globalState, angular.kbnUrl);
|
||||||
|
const _handleClickIncompatibleLicense = partial(handleClickIncompatibleLicense, angular.scope);
|
||||||
|
const _handleClickInvalidLicense = partial(handleClickInvalidLicense, angular.scope);
|
||||||
|
const hasStandaloneCluster = !!clusters.find(cluster => cluster.cluster_uuid === STANDALONE_CLUSTER_CLUSTER_UUID);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<I18nProvider>
|
||||||
|
<EuiPage>
|
||||||
|
<EuiPageBody>
|
||||||
|
<EuiPageContent>
|
||||||
|
{hasStandaloneCluster ? this.renderStandaloneClusterCallout(_changeCluster, angular.storage) : null}
|
||||||
|
<EuiMonitoringTable
|
||||||
|
className="clusterTable"
|
||||||
|
rows={clusters}
|
||||||
|
columns={getColumns(
|
||||||
|
angular.showLicenseExpiration,
|
||||||
|
_changeCluster,
|
||||||
|
_handleClickIncompatibleLicense,
|
||||||
|
_handleClickInvalidLicense
|
||||||
|
)}
|
||||||
|
rowProps={item => {
|
||||||
|
return {
|
||||||
|
'data-test-subj': `clusterRow_${item.cluster_uuid}`
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
sorting={{
|
||||||
|
...sorting,
|
||||||
|
sort: {
|
||||||
|
...sorting.sort,
|
||||||
|
field: 'cluster_name'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
pagination={pagination}
|
||||||
|
search={{
|
||||||
|
box: {
|
||||||
|
incremental: true,
|
||||||
|
placeholder: angular.scope.filterText
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onTableChange={onTableChange}
|
||||||
|
/>
|
||||||
|
</EuiPageContent>
|
||||||
|
</EuiPageBody>
|
||||||
|
</EuiPage>
|
||||||
|
</I18nProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { ElasticsearchPanel } from './elasticsearch_panel';
|
import { ElasticsearchPanel } from './elasticsearch_panel';
|
||||||
import { KibanaPanel } from './kibana_panel';
|
import { KibanaPanel } from './kibana_panel';
|
||||||
import { LogstashPanel } from './logstash_panel';
|
import { LogstashPanel } from './logstash_panel';
|
||||||
|
@ -13,23 +13,32 @@ import { BeatsPanel } from './beats_panel';
|
||||||
|
|
||||||
import { EuiPage, EuiPageBody } from '@elastic/eui';
|
import { EuiPage, EuiPageBody } from '@elastic/eui';
|
||||||
import { ApmPanel } from './apm_panel';
|
import { ApmPanel } from './apm_panel';
|
||||||
|
import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../../common/constants';
|
||||||
|
|
||||||
export function Overview(props) {
|
export function Overview(props) {
|
||||||
|
const isFromStandaloneCluster = props.cluster.cluster_uuid === STANDALONE_CLUSTER_CLUSTER_UUID;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiPage>
|
<EuiPage>
|
||||||
<EuiPageBody>
|
<EuiPageBody>
|
||||||
<AlertsPanel alerts={props.cluster.alerts} changeUrl={props.changeUrl} />
|
<AlertsPanel alerts={props.cluster.alerts} changeUrl={props.changeUrl} />
|
||||||
|
|
||||||
<ElasticsearchPanel
|
{ !isFromStandaloneCluster ?
|
||||||
{...props.cluster.elasticsearch}
|
(
|
||||||
version={props.cluster.version}
|
<Fragment>
|
||||||
ml={props.cluster.ml}
|
<ElasticsearchPanel
|
||||||
changeUrl={props.changeUrl}
|
{...props.cluster.elasticsearch}
|
||||||
license={props.cluster.license}
|
version={props.cluster.version}
|
||||||
showLicenseExpiration={props.showLicenseExpiration}
|
ml={props.cluster.ml}
|
||||||
/>
|
changeUrl={props.changeUrl}
|
||||||
|
license={props.cluster.license}
|
||||||
<KibanaPanel {...props.cluster.kibana} changeUrl={props.changeUrl} />
|
showLicenseExpiration={props.showLicenseExpiration}
|
||||||
|
/>
|
||||||
|
<KibanaPanel {...props.cluster.kibana} changeUrl={props.changeUrl} />
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
<LogstashPanel {...props.cluster.logstash} changeUrl={props.changeUrl} />
|
<LogstashPanel {...props.cluster.logstash} changeUrl={props.changeUrl} />
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
import './main';
|
import './main';
|
||||||
import './chart';
|
import './chart';
|
||||||
import './sparkline';
|
import './sparkline';
|
||||||
import './cluster/listing';
|
|
||||||
import './elasticsearch/cluster_status';
|
import './elasticsearch/cluster_status';
|
||||||
import './elasticsearch/index_summary';
|
import './elasticsearch/index_summary';
|
||||||
import './elasticsearch/node_summary';
|
import './elasticsearch/node_summary';
|
||||||
|
|
|
@ -7,6 +7,18 @@
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
|
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
|
||||||
import { timefilter } from 'ui/timefilter';
|
import { timefilter } from 'ui/timefilter';
|
||||||
|
import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../common/constants';
|
||||||
|
|
||||||
|
function formatClusters(clusters) {
|
||||||
|
return clusters.map(formatCluster);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCluster(cluster) {
|
||||||
|
if (cluster.cluster_uuid === STANDALONE_CLUSTER_CLUSTER_UUID) {
|
||||||
|
cluster.cluster_name = 'Standalone Cluster';
|
||||||
|
}
|
||||||
|
return cluster;
|
||||||
|
}
|
||||||
|
|
||||||
const uiModule = uiModules.get('monitoring/clusters');
|
const uiModule = uiModules.get('monitoring/clusters');
|
||||||
uiModule.service('monitoringClusters', ($injector) => {
|
uiModule.service('monitoringClusters', ($injector) => {
|
||||||
|
@ -30,9 +42,9 @@ uiModule.service('monitoringClusters', ($injector) => {
|
||||||
.then(response => response.data)
|
.then(response => response.data)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (clusterUuid) {
|
if (clusterUuid) {
|
||||||
return data[0]; // return single cluster
|
return formatCluster(data[0]); // return single cluster
|
||||||
}
|
}
|
||||||
return data; // return set of clusters
|
return formatClusters(data); // return set of clusters
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
const Private = $injector.get('Private');
|
const Private = $injector.get('Private');
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
<monitoring-main name="listing">
|
<monitoring-main name="listing">
|
||||||
<monitoring-cluster-listing
|
<div id="monitoringClusterListingApp"></div>
|
||||||
pagination-settings="clusters.pagination"
|
|
||||||
sorting="clusters.sorting"
|
|
||||||
on-table-change="clusters.onTableChange"
|
|
||||||
clusters="clusters.data"
|
|
||||||
></monitoring-cluster-listing>
|
|
||||||
</monitoring-main>
|
</monitoring-main>
|
||||||
|
|
|
@ -4,10 +4,13 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
import uiRoutes from 'ui/routes';
|
import uiRoutes from 'ui/routes';
|
||||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||||
import { MonitoringViewBaseEuiTableController } from '../../';
|
import { MonitoringViewBaseEuiTableController } from '../../';
|
||||||
|
import { I18nProvider } from '@kbn/i18n/react';
|
||||||
import template from './index.html';
|
import template from './index.html';
|
||||||
|
import { Listing } from '../../../components/cluster/listing';
|
||||||
|
|
||||||
const getPageData = $injector => {
|
const getPageData = $injector => {
|
||||||
const monitoringClusters = $injector.get('monitoringClusters');
|
const monitoringClusters = $injector.get('monitoringClusters');
|
||||||
|
@ -42,11 +45,37 @@ uiRoutes.when('/home', {
|
||||||
storageKey: 'clusters',
|
storageKey: 'clusters',
|
||||||
getPageData,
|
getPageData,
|
||||||
$scope,
|
$scope,
|
||||||
$injector
|
$injector,
|
||||||
|
reactNodeId: 'monitoringClusterListingApp'
|
||||||
});
|
});
|
||||||
|
|
||||||
const $route = $injector.get('$route');
|
const $route = $injector.get('$route');
|
||||||
|
const kbnUrl = $injector.get('kbnUrl');
|
||||||
|
const globalState = $injector.get('globalState');
|
||||||
|
const storage = $injector.get('localStorage');
|
||||||
|
const showLicenseExpiration = $injector.get('showLicenseExpiration');
|
||||||
this.data = $route.current.locals.clusters;
|
this.data = $route.current.locals.clusters;
|
||||||
|
|
||||||
|
|
||||||
|
$scope.$watch(() => this.data, data => {
|
||||||
|
this.renderReact(
|
||||||
|
<I18nProvider>
|
||||||
|
<Listing
|
||||||
|
clusters={data}
|
||||||
|
angular={{
|
||||||
|
scope: $scope,
|
||||||
|
globalState,
|
||||||
|
kbnUrl,
|
||||||
|
storage,
|
||||||
|
showLicenseExpiration
|
||||||
|
}}
|
||||||
|
sorting={this.sorting}
|
||||||
|
pagination={this.pagination}
|
||||||
|
onTableChange={this.onTableChange}
|
||||||
|
/>
|
||||||
|
</I18nProvider>
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -35,6 +35,7 @@ const mockReq = (log, queryResult = {}) => {
|
||||||
};
|
};
|
||||||
const goldLicense = () => ({ license: { type: 'gold' } });
|
const goldLicense = () => ({ license: { type: 'gold' } });
|
||||||
const basicLicense = () => ({ license: { type: 'basic' } });
|
const basicLicense = () => ({ license: { type: 'basic' } });
|
||||||
|
const standaloneCluster = () => ({ cluster_uuid: '__standalone_cluster__' });
|
||||||
|
|
||||||
describe('Flag Supported Clusters', () => {
|
describe('Flag Supported Clusters', () => {
|
||||||
describe('With multiple clusters in the monitoring data', () => {
|
describe('With multiple clusters in the monitoring data', () => {
|
||||||
|
@ -141,6 +142,118 @@ describe('Flag Supported Clusters', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('involving an standalone cluster', () => {
|
||||||
|
it('should ignore the standalone cluster in calculating supported basic clusters', () => {
|
||||||
|
const logStub = sinon.stub();
|
||||||
|
const req = mockReq(logStub, {
|
||||||
|
hits: {
|
||||||
|
hits: [ { _source: { cluster_uuid: 'supported_cluster_uuid' } } ]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const kbnIndices = [];
|
||||||
|
const clusters = [
|
||||||
|
{ cluster_uuid: 'supported_cluster_uuid', ...basicLicense() },
|
||||||
|
{ cluster_uuid: 'unsupported_cluster_uuid', ...basicLicense() },
|
||||||
|
{ ...standaloneCluster() }
|
||||||
|
];
|
||||||
|
|
||||||
|
return flagSupportedClusters(req, kbnIndices)(clusters)
|
||||||
|
.then(resultClusters => {
|
||||||
|
expect(resultClusters).to.eql([
|
||||||
|
{
|
||||||
|
cluster_uuid: 'supported_cluster_uuid',
|
||||||
|
isSupported: true,
|
||||||
|
...basicLicense()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cluster_uuid: 'unsupported_cluster_uuid',
|
||||||
|
...basicLicense()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...standaloneCluster(),
|
||||||
|
isSupported: true,
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
sinon.assert.calledWith(
|
||||||
|
logStub,
|
||||||
|
['debug', 'monitoring-ui', 'supported-clusters'],
|
||||||
|
'Found basic license admin cluster UUID for Monitoring UI support: supported_cluster_uuid.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore the standalone cluster in calculating supported mixed license clusters', () => {
|
||||||
|
const logStub = sinon.stub();
|
||||||
|
const req = mockReq(logStub);
|
||||||
|
const kbnIndices = [];
|
||||||
|
const clusters = [
|
||||||
|
{ cluster_uuid: 'supported_cluster_uuid', ...goldLicense() },
|
||||||
|
{ cluster_uuid: 'unsupported_cluster_uuid', ...basicLicense() },
|
||||||
|
{ ...standaloneCluster() }
|
||||||
|
];
|
||||||
|
|
||||||
|
return flagSupportedClusters(req, kbnIndices)(clusters)
|
||||||
|
.then(resultClusters => {
|
||||||
|
expect(resultClusters).to.eql([
|
||||||
|
{
|
||||||
|
cluster_uuid: 'supported_cluster_uuid',
|
||||||
|
isSupported: true,
|
||||||
|
...goldLicense()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cluster_uuid: 'unsupported_cluster_uuid',
|
||||||
|
...basicLicense()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...standaloneCluster(),
|
||||||
|
isSupported: true,
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
sinon.assert.calledWith(
|
||||||
|
logStub,
|
||||||
|
['debug', 'monitoring-ui', 'supported-clusters'],
|
||||||
|
'Found some basic license clusters in monitoring data. Only non-basic will be supported.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore the standalone cluster in calculating supported non-basic clusters', () => {
|
||||||
|
const logStub = sinon.stub();
|
||||||
|
const req = mockReq(logStub);
|
||||||
|
const kbnIndices = [];
|
||||||
|
const clusters = [
|
||||||
|
{ cluster_uuid: 'supported_cluster_uuid_1', ...goldLicense() },
|
||||||
|
{ cluster_uuid: 'supported_cluster_uuid_2', ...goldLicense() },
|
||||||
|
{ ...standaloneCluster() }
|
||||||
|
];
|
||||||
|
|
||||||
|
return flagSupportedClusters(req, kbnIndices)(clusters)
|
||||||
|
.then(resultClusters => {
|
||||||
|
expect(resultClusters).to.eql([
|
||||||
|
{
|
||||||
|
cluster_uuid: 'supported_cluster_uuid_1',
|
||||||
|
isSupported: true,
|
||||||
|
...goldLicense()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cluster_uuid: 'supported_cluster_uuid_2',
|
||||||
|
isSupported: true,
|
||||||
|
...goldLicense()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...standaloneCluster(),
|
||||||
|
isSupported: true,
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
sinon.assert.calledWith(
|
||||||
|
logStub,
|
||||||
|
['debug', 'monitoring-ui', 'supported-clusters'],
|
||||||
|
'Found all non-basic cluster licenses. All clusters will be supported.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('With single cluster in the monitoring data', () => {
|
describe('With single cluster in the monitoring data', () => {
|
||||||
|
@ -198,5 +311,24 @@ describe('Flag Supported Clusters', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('involving an standalone cluster', () => {
|
||||||
|
it('should ensure it is supported', () => {
|
||||||
|
const req = mockReq(logStub);
|
||||||
|
const kbnIndices = [];
|
||||||
|
const clusters = [{ ...standaloneCluster() }];
|
||||||
|
return flagSupportedClusters(req, kbnIndices)(clusters)
|
||||||
|
.then(result => {
|
||||||
|
expect(result).to.eql([
|
||||||
|
{ ...standaloneCluster(), isSupported: true, }
|
||||||
|
]);
|
||||||
|
sinon.assert.calledWith(
|
||||||
|
logStub,
|
||||||
|
['debug', 'monitoring-ui', 'supported-clusters'],
|
||||||
|
'Found single cluster in monitoring data.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import { get, set, find } from 'lodash';
|
import { get, set, find } from 'lodash';
|
||||||
import { checkParam } from '../error_missing_required';
|
import { checkParam } from '../error_missing_required';
|
||||||
import { createTypeFilter } from '../create_query';
|
import { createTypeFilter } from '../create_query';
|
||||||
import { LOGGING_TAG } from '../../../common/constants';
|
import { LOGGING_TAG, STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../common/constants';
|
||||||
|
|
||||||
async function findSupportedBasicLicenseCluster(req, clusters, kbnIndexPattern, kibanaUuid, serverLog) {
|
async function findSupportedBasicLicenseCluster(req, clusters, kbnIndexPattern, kibanaUuid, serverLog) {
|
||||||
checkParam(kbnIndexPattern, 'kbnIndexPattern in cluster/findSupportedBasicLicenseCluster');
|
checkParam(kbnIndexPattern, 'kbnIndexPattern in cluster/findSupportedBasicLicenseCluster');
|
||||||
|
@ -51,11 +51,12 @@ async function findSupportedBasicLicenseCluster(req, clusters, kbnIndexPattern,
|
||||||
* Flag clusters as supported, which means their monitoring data can be seen in the UI.
|
* Flag clusters as supported, which means their monitoring data can be seen in the UI.
|
||||||
*
|
*
|
||||||
* Flagging a Basic licensed cluster as supported when it is part of a multi-cluster environment:
|
* Flagging a Basic licensed cluster as supported when it is part of a multi-cluster environment:
|
||||||
* 1. Detect if there are multiple clusters
|
* 1. Detect if there any standalone clusters and ignore those for these calculations as they are auto supported
|
||||||
* 2. Detect if all of the different cluster licenses are basic
|
* 2. Detect if there are multiple linked clusters
|
||||||
* 3. Make a query to the monitored kibana data to find the "supported" cluster
|
* 3. Detect if all of the different linked cluster licenses are basic
|
||||||
* UUID, which is the cluster associated with *this* Kibana instance.
|
* 4. Make a query to the monitored kibana data to find the "supported" linked cluster
|
||||||
* 4. Flag the cluster object with an `isSupported` boolean
|
* UUID, which is the linked cluster associated with *this* Kibana instance.
|
||||||
|
* 5. Flag the linked cluster object with an `isSupported` boolean
|
||||||
*
|
*
|
||||||
* Non-Basic license clusters and any cluster in a single-cluster environment
|
* Non-Basic license clusters and any cluster in a single-cluster environment
|
||||||
* are also flagged as supported in this method.
|
* are also flagged as supported in this method.
|
||||||
|
@ -75,8 +76,17 @@ export function flagSupportedClusters(req, kbnIndexPattern) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return async function (clusters) {
|
return async function (clusters) {
|
||||||
// if multi cluster
|
// Standalone clusters are automatically supported in the UI so ignore those for
|
||||||
if (clusters.length > 1) {
|
// our calculations here
|
||||||
|
let linkedClusterCount = 0;
|
||||||
|
for (const cluster of clusters) {
|
||||||
|
if (cluster.cluster_uuid === STANDALONE_CLUSTER_CLUSTER_UUID) {
|
||||||
|
cluster.isSupported = true;
|
||||||
|
} else {
|
||||||
|
linkedClusterCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (linkedClusterCount > 1) {
|
||||||
const basicLicenseCount = clusters.reduce((accumCount, cluster) => {
|
const basicLicenseCount = clusters.reduce((accumCount, cluster) => {
|
||||||
if (cluster.license && cluster.license.type === 'basic') {
|
if (cluster.license && cluster.license.type === 'basic') {
|
||||||
accumCount++;
|
accumCount++;
|
||||||
|
@ -90,8 +100,8 @@ export function flagSupportedClusters(req, kbnIndexPattern) {
|
||||||
return flagAllSupported(clusters);
|
return flagAllSupported(clusters);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if all basic licenses
|
// if all linked are basic licenses
|
||||||
if (clusters.length === basicLicenseCount) {
|
if (linkedClusterCount === basicLicenseCount) {
|
||||||
const kibanaUuid = config.get('server.uuid');
|
const kibanaUuid = config.get('server.uuid');
|
||||||
return await findSupportedBasicLicenseCluster(req, clusters, kbnIndexPattern, kibanaUuid, serverLog);
|
return await findSupportedBasicLicenseCluster(req, clusters, kbnIndexPattern, kibanaUuid, serverLog);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,10 @@ import { alertsClustersAggregation } from '../../cluster_alerts/alerts_clusters_
|
||||||
import { alertsClusterSearch } from '../../cluster_alerts/alerts_cluster_search';
|
import { alertsClusterSearch } from '../../cluster_alerts/alerts_cluster_search';
|
||||||
import { checkLicense as checkLicenseForAlerts } from '../../cluster_alerts/check_license';
|
import { checkLicense as checkLicenseForAlerts } from '../../cluster_alerts/check_license';
|
||||||
import { getClustersSummary } from './get_clusters_summary';
|
import { getClustersSummary } from './get_clusters_summary';
|
||||||
import { CLUSTER_ALERTS_SEARCH_SIZE } from '../../../common/constants';
|
import { CLUSTER_ALERTS_SEARCH_SIZE, STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../common/constants';
|
||||||
import { getApmsForClusters } from '../apm/get_apms_for_clusters';
|
import { getApmsForClusters } from '../apm/get_apms_for_clusters';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { standaloneClusterDefinition, hasStandaloneClusters } from '../standalone_clusters';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all clusters or the cluster associated with {@code clusterUuid} when it is defined.
|
* Get all clusters or the cluster associated with {@code clusterUuid} when it is defined.
|
||||||
|
@ -34,8 +35,29 @@ export async function getClustersFromRequest(req, indexPatterns, { clusterUuid,
|
||||||
alertsIndex
|
alertsIndex
|
||||||
} = indexPatterns;
|
} = indexPatterns;
|
||||||
|
|
||||||
// get clusters with stats and cluster state
|
const isStandaloneCluster = clusterUuid === STANDALONE_CLUSTER_CLUSTER_UUID;
|
||||||
let clusters = await getClustersStats(req, esIndexPattern, clusterUuid);
|
|
||||||
|
let clusters = [];
|
||||||
|
|
||||||
|
if (isStandaloneCluster) {
|
||||||
|
clusters.push(standaloneClusterDefinition);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// get clusters with stats and cluster state
|
||||||
|
clusters = await getClustersStats(req, esIndexPattern, clusterUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!clusterUuid && !isStandaloneCluster) {
|
||||||
|
const indexPatternsToCheckForNonClusters = [
|
||||||
|
lsIndexPattern,
|
||||||
|
beatsIndexPattern,
|
||||||
|
apmIndexPattern
|
||||||
|
];
|
||||||
|
|
||||||
|
if (await hasStandaloneClusters(req, indexPatternsToCheckForNonClusters)) {
|
||||||
|
clusters.push(standaloneClusterDefinition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: this handling logic should be two different functions
|
// TODO: this handling logic should be two different functions
|
||||||
if (clusterUuid) { // if is defined, get specific cluster (no need for license checking)
|
if (clusterUuid) { // if is defined, get specific cluster (no need for license checking)
|
||||||
|
@ -63,7 +85,7 @@ export async function getClustersFromRequest(req, indexPatterns, { clusterUuid,
|
||||||
if (alerts) {
|
if (alerts) {
|
||||||
cluster.alerts = alerts;
|
cluster.alerts = alerts;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!isStandaloneCluster) {
|
||||||
// get all clusters
|
// get all clusters
|
||||||
if (!clusters || clusters.length === 0) {
|
if (!clusters || clusters.length === 0) {
|
||||||
// we do NOT throw 404 here so that the no-data page can use this to check for data
|
// we do NOT throw 404 here so that the no-data page can use this to check for data
|
||||||
|
@ -89,7 +111,7 @@ export async function getClustersFromRequest(req, indexPatterns, { clusterUuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
// add kibana data
|
// add kibana data
|
||||||
const kibanas = await getKibanasForClusters(req, kbnIndexPattern, clusters);
|
const kibanas = isStandaloneCluster ? [] : await getKibanasForClusters(req, kbnIndexPattern, clusters);
|
||||||
// add the kibana data to each cluster
|
// add the kibana data to each cluster
|
||||||
kibanas.forEach(kibana => {
|
kibanas.forEach(kibana => {
|
||||||
const clusterIndex = findIndex(clusters, { cluster_uuid: kibana.clusterUuid });
|
const clusterIndex = findIndex(clusters, { cluster_uuid: kibana.clusterUuid });
|
||||||
|
|
|
@ -22,7 +22,7 @@ export function getClustersSummary(clusters, kibanaUuid) {
|
||||||
apm,
|
apm,
|
||||||
alerts,
|
alerts,
|
||||||
ccs,
|
ccs,
|
||||||
cluster_settings: clusterSettings
|
cluster_settings: clusterSettings,
|
||||||
} = cluster;
|
} = cluster;
|
||||||
|
|
||||||
const clusterName = get(clusterSettings, 'cluster.metadata.display_name', cluster.cluster_name);
|
const clusterName = get(clusterSettings, 'cluster.metadata.display_name', cluster.cluster_name);
|
||||||
|
@ -73,11 +73,11 @@ export function getClustersSummary(clusters, kibanaUuid) {
|
||||||
beats,
|
beats,
|
||||||
apm,
|
apm,
|
||||||
alerts,
|
alerts,
|
||||||
isPrimary: kibana.uuids.includes(kibanaUuid),
|
isPrimary: kibana ? kibana.uuids.includes(kibanaUuid) : false,
|
||||||
status: calculateOverallStatus([
|
status: calculateOverallStatus([
|
||||||
status,
|
status,
|
||||||
kibana && kibana.status || null
|
kibana && kibana.status || null
|
||||||
])
|
]),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
import { defaults, get } from 'lodash';
|
import { defaults, get } from 'lodash';
|
||||||
import { MissingRequiredError } from './error_missing_required';
|
import { MissingRequiredError } from './error_missing_required';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import { standaloneClusterFilter } from './standalone_clusters';
|
||||||
|
import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../common/constants';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Builds a type filter syntax that supports backwards compatibility to read
|
* Builds a type filter syntax that supports backwards compatibility to read
|
||||||
|
@ -46,13 +48,15 @@ export function createQuery(options) {
|
||||||
options = defaults(options, { filters: [] });
|
options = defaults(options, { filters: [] });
|
||||||
const { type, clusterUuid, uuid, start, end, filters } = options;
|
const { type, clusterUuid, uuid, start, end, filters } = options;
|
||||||
|
|
||||||
|
const isFromStandaloneCluster = clusterUuid === STANDALONE_CLUSTER_CLUSTER_UUID;
|
||||||
|
|
||||||
let typeFilter;
|
let typeFilter;
|
||||||
if (type) {
|
if (type) {
|
||||||
typeFilter = createTypeFilter(type);
|
typeFilter = createTypeFilter(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
let clusterUuidFilter;
|
let clusterUuidFilter;
|
||||||
if (clusterUuid) {
|
if (clusterUuid && !isFromStandaloneCluster) {
|
||||||
clusterUuidFilter = { term: { 'cluster_uuid': clusterUuid } };
|
clusterUuidFilter = { term: { 'cluster_uuid': clusterUuid } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,9 +93,15 @@ export function createQuery(options) {
|
||||||
combinedFilters.push(timeRangeFilter);
|
combinedFilters.push(timeRangeFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
if (isFromStandaloneCluster) {
|
||||||
|
combinedFilters.push(standaloneClusterFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = {
|
||||||
bool: {
|
bool: {
|
||||||
filter: combinedFilters.filter(Boolean)
|
filter: combinedFilters.filter(Boolean)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return query;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
import moment from 'moment';
|
||||||
|
import { get } from 'lodash';
|
||||||
|
import { standaloneClusterFilter } from './';
|
||||||
|
|
||||||
|
export async function hasStandaloneClusters(req, indexPatterns) {
|
||||||
|
const indexPatternList = indexPatterns.reduce((list, patterns) => {
|
||||||
|
list.push(...patterns.split(','));
|
||||||
|
return list;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const filters = [standaloneClusterFilter];
|
||||||
|
// Not every page will contain a time range so check for that
|
||||||
|
if (req.payload.timeRange) {
|
||||||
|
const start = req.payload.timeRange.min;
|
||||||
|
const end = req.payload.timeRange.max;
|
||||||
|
|
||||||
|
const timeRangeFilter = {
|
||||||
|
range: {
|
||||||
|
timestamp: {
|
||||||
|
format: 'epoch_millis'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (start) {
|
||||||
|
timeRangeFilter.range.timestamp.gte = moment.utc(start).valueOf();
|
||||||
|
}
|
||||||
|
if (end) {
|
||||||
|
timeRangeFilter.range.timestamp.lte = moment.utc(end).valueOf();
|
||||||
|
}
|
||||||
|
filters.push(timeRangeFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
index: indexPatternList,
|
||||||
|
body: {
|
||||||
|
size: 0,
|
||||||
|
terminate_after: 1,
|
||||||
|
query: {
|
||||||
|
bool: {
|
||||||
|
filter: filters,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring');
|
||||||
|
const response = await callWithRequest(req, 'search', params);
|
||||||
|
if (response && response.hits) {
|
||||||
|
return get(response, 'hits.total.value', 0) > 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { hasStandaloneClusters } from './has_standalone_clusters';
|
||||||
|
export { standaloneClusterDefinition } from './standalone_cluster_definition';
|
||||||
|
export { standaloneClusterFilter } from './standalone_cluster_query_filter';
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../common/constants';
|
||||||
|
|
||||||
|
export const standaloneClusterDefinition = {
|
||||||
|
cluster_uuid: STANDALONE_CLUSTER_CLUSTER_UUID,
|
||||||
|
license: {},
|
||||||
|
cluster_state: {},
|
||||||
|
cluster_stats: {
|
||||||
|
nodes: {
|
||||||
|
jvm: {},
|
||||||
|
count: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const standaloneClusterFilter = {
|
||||||
|
bool: {
|
||||||
|
should: [
|
||||||
|
{
|
||||||
|
term: {
|
||||||
|
cluster_uuid: {
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
bool: {
|
||||||
|
must_not: [
|
||||||
|
{
|
||||||
|
exists: {
|
||||||
|
field: 'cluster_uuid'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
|
@ -18,7 +18,7 @@ export {
|
||||||
} from './beats';
|
} from './beats';
|
||||||
export {
|
export {
|
||||||
clusterRoute,
|
clusterRoute,
|
||||||
clustersRoute
|
clustersRoute,
|
||||||
} from './cluster';
|
} from './cluster';
|
||||||
export {
|
export {
|
||||||
esIndexRoute,
|
esIndexRoute,
|
||||||
|
|
|
@ -14,5 +14,6 @@ export default function ({ loadTestFile }) {
|
||||||
loadTestFile(require.resolve('./kibana'));
|
loadTestFile(require.resolve('./kibana'));
|
||||||
loadTestFile(require.resolve('./logstash'));
|
loadTestFile(require.resolve('./logstash'));
|
||||||
loadTestFile(require.resolve('./common'));
|
loadTestFile(require.resolve('./common'));
|
||||||
|
loadTestFile(require.resolve('./standalone_cluster'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import expect from 'expect.js';
|
||||||
|
import clusterFixture from './fixtures/cluster';
|
||||||
|
|
||||||
|
export default function ({ getService }) {
|
||||||
|
const supertest = getService('supertest');
|
||||||
|
const esArchiver = getService('esArchiver');
|
||||||
|
|
||||||
|
describe('cluster', () => {
|
||||||
|
const archive = 'monitoring/standalone_cluster';
|
||||||
|
const timeRange = {
|
||||||
|
min: '2019-01-15T19:00:49.104Z',
|
||||||
|
max: '2019-01-15T19:59:49.104Z'
|
||||||
|
};
|
||||||
|
|
||||||
|
before('load archive', () => {
|
||||||
|
return esArchiver.load(archive);
|
||||||
|
});
|
||||||
|
|
||||||
|
after('unload archive', () => {
|
||||||
|
return esArchiver.unload(archive);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get cluster data', async () => {
|
||||||
|
const { body } = await supertest
|
||||||
|
.post('/api/monitoring/v1/clusters/__standalone_cluster__')
|
||||||
|
.set('kbn-xsrf', 'xxx')
|
||||||
|
.send({ timeRange })
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(body).to.eql(clusterFixture);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import expect from 'expect.js';
|
||||||
|
import clustersFixture from './fixtures/clusters';
|
||||||
|
|
||||||
|
export default function ({ getService }) {
|
||||||
|
const supertest = getService('supertest');
|
||||||
|
const esArchiver = getService('esArchiver');
|
||||||
|
|
||||||
|
describe('clusters', () => {
|
||||||
|
const archive = 'monitoring/standalone_cluster';
|
||||||
|
const timeRange = {
|
||||||
|
min: '2019-01-15T19:00:49.104Z',
|
||||||
|
max: '2019-01-15T19:59:49.104Z'
|
||||||
|
};
|
||||||
|
|
||||||
|
before('load archive', () => {
|
||||||
|
return esArchiver.load(archive);
|
||||||
|
});
|
||||||
|
|
||||||
|
after('unload archive', () => {
|
||||||
|
return esArchiver.unload(archive);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the cluster listing', async () => {
|
||||||
|
const { body } = await supertest
|
||||||
|
.post('/api/monitoring/v1/clusters')
|
||||||
|
.set('kbn-xsrf', 'xxx')
|
||||||
|
.send({ timeRange })
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(body).to.eql(clustersFixture);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
[{"isSupported":true,"cluster_uuid":"__standalone_cluster__","license":{},"elasticsearch":{"cluster_stats":{"indices":{},"nodes":{"count":{},"jvm":{}}}},"logstash":{},"kibana":{"status":null,"requests_total":0,"concurrent_connections":0,"response_time_max":0,"memory_size":0,"memory_limit":0,"count":0},"beats":{"totalEvents":6963,"bytesSent":6283358,"beats":{"total":1,"types":[{"type":"Packetbeat","count":1}]}},"apm":{"totalEvents":0,"memRss":0,"memTotal":0,"apms":{"total":0}},"alerts":{"message":"Cluster Alerts are not displayed because the [production] cluster's license could not be determined."},"isPrimary":false}]
|
|
@ -0,0 +1 @@
|
||||||
|
[{"cluster_uuid":"BsqrVriJSu21Q-MkOr6vTA","cluster_name":"monitoring","version":"7.0.0","license":{"status":"active","type":"basic"},"elasticsearch":{"cluster_stats":{"indices":{"count":5,"docs":{"count":7814,"deleted":169},"shards":{"total":7,"primaries":7,"replication":0,"index":{"shards":{"min":1,"max":3,"avg":1.4},"primaries":{"min":1,"max":3,"avg":1.4},"replication":{"min":0,"max":0,"avg":0}}},"store":{"size_in_bytes":9230231}},"nodes":{"fs":{"total_in_bytes":499963174912,"free_in_bytes":83429146624,"available_in_bytes":70893522944},"count":{"total":1},"jvm":{"max_uptime_in_millis":190074,"mem":{"heap_used_in_bytes":114044640,"heap_max_in_bytes":1038876672}}},"status":"yellow"}},"logstash":{"node_count":0,"events_in_total":0,"events_out_total":0,"avg_memory":0,"avg_memory_used":0,"max_uptime":0,"pipeline_count":0,"queue_types":{"memory":0,"persisted":0},"versions":[]},"kibana":{"status":"green","requests_total":3,"concurrent_connections":2,"response_time_max":58,"memory_size":255426560,"memory_limit":8564343808,"count":1},"beats":{"totalEvents":0,"bytesSent":0,"beats":{"total":0,"types":[]}},"apm":{"totalEvents":0,"memRss":0,"memTotal":0,"apms":{"total":0}},"alerts":{"alertsMeta":{"enabled":true},"clusterMeta":{"enabled":false,"message":"Cluster [monitoring] license type [basic] does not support Cluster Alerts"}},"isPrimary":true,"isSupported": true,"status":"yellow"},{"isSupported":true,"cluster_uuid":"__standalone_cluster__","license":{},"elasticsearch":{"cluster_stats":{"indices":{},"nodes":{"count":{},"jvm":{}}}},"logstash":{"node_count":0,"events_in_total":0,"events_out_total":0,"avg_memory":0,"avg_memory_used":0,"max_uptime":0,"pipeline_count":0,"queue_types":{"memory":0,"persisted":0},"versions":[]},"kibana":{"status":null,"requests_total":0,"concurrent_connections":0,"response_time_max":0,"memory_size":0,"memory_limit":0,"count":0},"beats":{"totalEvents":6963,"bytesSent":6283358,"beats":{"total":1,"types":[{"type":"Packetbeat","count":1}]}},"apm":{"totalEvents":0,"memRss":0,"memTotal":0,"apms":{"total":0}},"alerts":{"alertsMeta":{"enabled":true},"clusterMeta":{"enabled":false,"message":"Cluster [] license type [undefined] does not support Cluster Alerts"}},"isPrimary":false}]
|
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function ({ loadTestFile }) {
|
||||||
|
describe('Standalone Cluster', () => {
|
||||||
|
loadTestFile(require.resolve('./clusters'));
|
||||||
|
loadTestFile(require.resolve('./cluster'));
|
||||||
|
});
|
||||||
|
}
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue