[Monitoring] Remove license check for alerting (#94874) (#95429)

* Removed license check for alerting

* Fixed tests and CR feedback

* Fixed test
# Conflicts:
#	x-pack/plugins/monitoring/server/cluster_alerts/alerts_cluster_search.js
#	x-pack/plugins/monitoring/server/cluster_alerts/alerts_clusters_aggregation.js
#	x-pack/plugins/monitoring/server/cluster_alerts/verify_monitoring_license.js
#	x-pack/plugins/monitoring/server/deprecations.ts
#	x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js
This commit is contained in:
igoristic 2021-03-25 14:17:34 -04:00 committed by GitHub
parent ab4b9d5610
commit e4cd33d9d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 10 additions and 1319 deletions

View file

@ -41,17 +41,14 @@ const IsClusterSupported = ({ isSupported, children }) => {
* completely
*/
const IsAlertsSupported = (props) => {
const { alertsMeta = { enabled: true }, clusterMeta = { enabled: true } } = props.cluster.alerts;
if (alertsMeta.enabled && clusterMeta.enabled) {
const { alertsMeta = { enabled: true } } = props.cluster.alerts;
if (alertsMeta.enabled) {
return <span>{props.children}</span>;
}
const message =
alertsMeta.message ||
clusterMeta.message ||
i18n.translate('xpack.monitoring.cluster.listing.unknownHealthMessage', {
defaultMessage: 'Unknown',
});
const message = i18n.translate('xpack.monitoring.cluster.listing.unknownHealthMessage', {
defaultMessage: 'Unknown',
});
return (
<EuiToolTip content={message} position="bottom">

View file

@ -1,34 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import sinon from 'sinon';
export function createStubs(mockQueryResult, featureStub) {
const callWithRequestStub = sinon.stub().returns(Promise.resolve(mockQueryResult));
const getClusterStub = sinon.stub().returns({ callWithRequest: callWithRequestStub });
const configStub = sinon.stub().returns({
get: sinon.stub().withArgs('xpack.monitoring.cluster_alerts.enabled').returns(true),
});
return {
callWithRequestStub,
mockReq: {
server: {
config: configStub,
plugins: {
monitoring: {
info: {
feature: featureStub,
},
},
elasticsearch: {
getCluster: getClusterStub,
},
},
},
},
};
}

View file

@ -1,227 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { get } from 'lodash';
import moment from 'moment';
import { verifyMonitoringLicense } from './verify_monitoring_license';
import { i18n } from '@kbn/i18n';
/**
* Retrieve any statically defined cluster alerts (not indexed) for the {@code cluster}.
*
* In the future, if we add other static cluster alerts, then we should probably just return an array.
* It may also make sense to put this into its own file in the future.
*
* @param {Object} cluster The cluster object containing the cluster's license.
* @return {Object} The alert to use for the cluster. {@code null} if none.
*/
export function staticAlertForCluster(cluster) {
const clusterNeedsTLSEnabled = get(cluster, 'license.cluster_needs_tls', false);
if (clusterNeedsTLSEnabled) {
const versionParts = get(cluster, 'version', '').split('.');
const version = versionParts.length > 1 ? `${versionParts[0]}.${versionParts[1]}` : 'current';
return {
metadata: {
severity: 0,
cluster_uuid: cluster.cluster_uuid,
link: `https://www.elastic.co/guide/en/x-pack/${version}/ssl-tls.html`,
},
update_timestamp: cluster.timestamp,
timestamp: get(cluster, 'license.issue_date', cluster.timestamp),
prefix: i18n.translate('xpack.monitoring.clusterAlerts.clusterNeedsTSLEnabledDescription', {
defaultMessage:
'Configuring TLS will be required to apply a Gold or Platinum license when security is enabled.',
}),
message: i18n.translate('xpack.monitoring.clusterAlerts.seeDocumentationDescription', {
defaultMessage: 'See documentation for details.',
}),
};
}
return null;
}
/**
* Append the static alert(s) for this {@code cluster}, limiting the response to {@code size} {@code alerts}.
*
* @param {Object} cluster The cluster object containing the cluster's license.
* @param {Array} alerts The existing cluster alerts.
* @param {Number} size The maximum size.
* @return {Array} The alerts array (modified or not).
*/
export function appendStaticAlerts(cluster, alerts, size) {
const staticAlert = staticAlertForCluster(cluster);
if (staticAlert) {
// we can put it over any resolved alert, or anything with a lower severity (which is currently none)
// the alerts array is pre-sorted from highest severity to lowest; unresolved alerts are at the bottom
const alertIndex = alerts.findIndex(
(alert) => alert.resolved_timestamp || alert.metadata.severity < staticAlert.metadata.severity
);
if (alertIndex !== -1) {
// we can put it in the place of this alert
alerts.splice(alertIndex, 0, staticAlert);
} else {
alerts.push(staticAlert);
}
// chop off the last item if necessary (when size is < alerts.length)
return alerts.slice(0, size);
}
return alerts;
}
/**
* Create a filter that should be used when no time range is supplied and thus only un-resolved cluster alerts should
* be returned.
*
* @return {Object} Query to restrict to un-resolved cluster alerts.
*/
export function createFilterForUnresolvedAlerts() {
return {
bool: {
must_not: {
exists: {
field: 'resolved_timestamp',
},
},
},
};
}
/**
* Create a filter that should be used when {@code options} has start or end times.
*
* This enables us to search for cluster alerts that have been resolved within the given time frame, while also
* grabbing any un-resolved cluster alerts.
*
* @param {Object} options The options for the cluster search.
* @return {Object} Query to restrict to un-resolved cluster alerts or cluster alerts resolved within the time range.
*/
export function createFilterForTime(options) {
const timeFilter = {};
if (options.start) {
timeFilter.gte = moment.utc(options.start).valueOf();
}
if (options.end) {
timeFilter.lte = moment.utc(options.end).valueOf();
}
return {
bool: {
should: [
{
range: {
resolved_timestamp: {
format: 'epoch_millis',
...timeFilter,
},
},
},
{
bool: {
must_not: {
exists: {
field: 'resolved_timestamp',
},
},
},
},
],
},
};
}
/**
* @param {Object} req Request object from the API route
* @param {String} cluster The cluster being checked
*/
export function alertsClusterSearch(req, alertsIndex, cluster, checkLicense, options = {}) {
const verification = verifyMonitoringLicense(req.server);
if (!verification.enabled) {
return Promise.resolve({ message: verification.message });
}
const license = get(cluster, 'license', {});
const prodLicenseInfo = checkLicense(license.type, license.status === 'active', 'production');
if (prodLicenseInfo.clusterAlerts.enabled) {
const config = req.server.config();
const size = options.size || config.get('monitoring.ui.max_bucket_size');
const params = {
index: alertsIndex,
ignoreUnavailable: true,
filterPath: 'hits.hits._source',
body: {
size,
query: {
bool: {
must: [
{
// This will cause anything un-resolved to be sorted above anything that is resolved
// From there, those items are sorted by their severity, then by their timestamp (age)
function_score: {
boost_mode: 'max',
functions: [
{
filter: {
bool: {
must_not: [
{
exists: {
field: 'resolved_timestamp',
},
},
],
},
},
weight: 2,
},
],
},
},
],
filter: [
{
term: { 'metadata.cluster_uuid': cluster.cluster_uuid },
},
],
},
},
sort: [
'_score',
{ 'metadata.severity': { order: 'desc' } },
{ timestamp: { order: 'asc' } },
],
},
};
if (options.start || options.end) {
params.body.query.bool.filter.push(createFilterForTime(options));
} else {
params.body.query.bool.filter.push(createFilterForUnresolvedAlerts());
}
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring');
return callWithRequest(req, 'search', params).then((result) => {
const hits = get(result, 'hits.hits', []);
const alerts = hits.map((alert) => alert._source);
return appendStaticAlerts(cluster, alerts, size);
});
}
return Promise.resolve({ message: prodLicenseInfo.message });
}

View file

@ -1,194 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import sinon from 'sinon';
import { createStubs } from './__fixtures__/create_stubs';
import { alertsClusterSearch } from './alerts_cluster_search';
const mockAlerts = [
{
metadata: {
severity: 1,
},
},
{
metadata: {
severity: -1,
},
},
{
metadata: {
severity: 2000,
},
resolved_timestamp: 'now',
},
];
const mockQueryResult = {
hits: {
hits: [
{
_source: mockAlerts[0],
},
{
_source: mockAlerts[1],
},
{
_source: mockAlerts[2],
},
],
},
};
// TODO: tests were not running and are not up to date.
describe.skip('Alerts Cluster Search', () => {
describe('License checks pass', () => {
const featureStub = sinon.stub().returns({
getLicenseCheckResults: () => ({ clusterAlerts: { enabled: true } }),
});
const checkLicense = () => ({ clusterAlerts: { enabled: true } });
it('max hit count option', () => {
const { mockReq, callWithRequestStub } = createStubs(mockQueryResult, featureStub);
return alertsClusterSearch(
mockReq,
'.monitoring-alerts',
{ cluster_uuid: 'cluster-1234' },
checkLicense
).then((alerts) => {
expect(alerts).to.eql(mockAlerts);
expect(callWithRequestStub.getCall(0).args[2].body.size).to.be.undefined;
});
});
it('set hit count option', () => {
const { mockReq, callWithRequestStub } = createStubs(mockQueryResult, featureStub);
return alertsClusterSearch(
mockReq,
'.monitoring-alerts',
{ cluster_uuid: 'cluster-1234' },
checkLicense,
{ size: 3 }
).then((alerts) => {
expect(alerts).to.eql(mockAlerts);
expect(callWithRequestStub.getCall(0).args[2].body.size).to.be(3);
});
});
it('should report static info-level alert in the right location', () => {
const { mockReq, callWithRequestStub } = createStubs(mockQueryResult, featureStub);
const cluster = {
cluster_uuid: 'cluster-1234',
timestamp: 'fake-timestamp',
version: '6.1.0-throwmeaway2',
license: {
cluster_needs_tls: true,
issue_date: 'fake-issue_date',
},
};
return alertsClusterSearch(mockReq, '.monitoring-alerts', cluster, checkLicense, {
size: 3,
}).then((alerts) => {
expect(alerts).to.have.length(3);
expect(alerts[0]).to.eql(mockAlerts[0]);
expect(alerts[1]).to.eql({
metadata: {
severity: 0,
cluster_uuid: cluster.cluster_uuid,
link: 'https://www.elastic.co/guide/en/x-pack/6.1/ssl-tls.html',
},
update_timestamp: cluster.timestamp,
timestamp: cluster.license.issue_date,
prefix:
'Configuring TLS will be required to apply a Gold or Platinum license when security is enabled.',
message: 'See documentation for details.',
});
expect(alerts[2]).to.eql(mockAlerts[1]);
expect(callWithRequestStub.getCall(0).args[2].body.size).to.be(3);
});
});
it('should report static info-level alert at the end if necessary', () => {
const { mockReq, callWithRequestStub } = createStubs({ hits: { hits: [] } }, featureStub);
const cluster = {
cluster_uuid: 'cluster-1234',
timestamp: 'fake-timestamp',
version: '6.1.0-throwmeaway2',
license: {
cluster_needs_tls: true,
issue_date: 'fake-issue_date',
},
};
return alertsClusterSearch(mockReq, '.monitoring-alerts', cluster, checkLicense, {
size: 3,
}).then((alerts) => {
expect(alerts).to.have.length(1);
expect(alerts[0]).to.eql({
metadata: {
severity: 0,
cluster_uuid: cluster.cluster_uuid,
link: 'https://www.elastic.co/guide/en/x-pack/6.1/ssl-tls.html',
},
update_timestamp: cluster.timestamp,
timestamp: cluster.license.issue_date,
prefix:
'Configuring TLS will be required to apply a Gold or Platinum license when security is enabled.',
message: 'See documentation for details.',
});
expect(callWithRequestStub.getCall(0).args[2].body.size).to.be(3);
});
});
});
describe('License checks fail', () => {
it('monitoring cluster license checks fail', () => {
const featureStub = sinon.stub().returns({
getLicenseCheckResults: () => ({
message: 'monitoring cluster license check fail',
clusterAlerts: { enabled: false },
}),
});
const checkLicense = sinon.stub();
const { mockReq, callWithRequestStub } = createStubs({}, featureStub);
return alertsClusterSearch(
mockReq,
'.monitoring-alerts',
{ cluster_uuid: 'cluster-1234' },
checkLicense
).then((alerts) => {
const result = { message: 'monitoring cluster license check fail' };
expect(alerts).to.eql(result);
expect(checkLicense.called).to.be(false);
expect(callWithRequestStub.called).to.be(false);
});
});
it('production cluster license checks fail', () => {
// monitoring cluster passes
const featureStub = sinon.stub().returns({
getLicenseCheckResults: () => ({ clusterAlerts: { enabled: true } }),
});
const checkLicense = sinon
.stub()
.returns({ clusterAlerts: { enabled: false }, message: 'prod goes boom' });
const { mockReq, callWithRequestStub } = createStubs({}, featureStub);
return alertsClusterSearch(
mockReq,
'.monitoring-alerts',
{ cluster_uuid: 'cluster-1234' },
checkLicense
).then((alerts) => {
const result = { message: 'prod goes boom' };
expect(alerts).to.eql(result);
expect(checkLicense.calledOnce).to.be(true);
expect(callWithRequestStub.called).to.be(false);
});
});
});
});

View file

@ -1,127 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { get, find } from 'lodash';
import { verifyMonitoringLicense } from './verify_monitoring_license';
import { i18n } from '@kbn/i18n';
export function alertsClustersAggregation(req, alertsIndex, clusters, checkLicense) {
const verification = verifyMonitoringLicense(req.server);
if (!verification.enabled) {
// return metadata detailing that alerts is disabled because of the monitoring cluster license
return Promise.resolve({ alertsMeta: verification });
}
const params = {
index: alertsIndex,
ignoreUnavailable: true,
filterPath: 'aggregations',
body: {
size: 0,
query: {
bool: {
must_not: [
{
exists: { field: 'resolved_timestamp' },
},
],
},
},
aggs: {
group_by_cluster: {
terms: {
field: 'metadata.cluster_uuid',
size: 10,
},
aggs: {
group_by_severity: {
range: {
field: 'metadata.severity',
ranges: [
{
key: 'low',
to: 1000,
},
{
key: 'medium',
from: 1000,
to: 2000,
},
{
key: 'high',
from: 2000,
},
],
},
},
},
},
},
},
};
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring');
return callWithRequest(req, 'search', params).then((result) => {
const buckets = get(result.aggregations, 'group_by_cluster.buckets');
const meta = { alertsMeta: { enabled: true } };
return clusters.reduce((reClusters, cluster) => {
let alerts;
const license = cluster.license || {};
// check the license type of the production cluster for alerts feature support
const prodLicenseInfo = checkLicense(license.type, license.status === 'active', 'production');
if (prodLicenseInfo.clusterAlerts.enabled) {
const clusterNeedsTLS = get(license, 'cluster_needs_tls', false);
const staticAlertCount = clusterNeedsTLS ? 1 : 0;
const bucket = find(buckets, { key: cluster.cluster_uuid });
const bucketDocCount = get(bucket, 'doc_count', 0);
let severities = {};
if (bucket || staticAlertCount > 0) {
if (bucketDocCount > 0 || staticAlertCount > 0) {
const groupBySeverityBuckets = get(bucket, 'group_by_severity.buckets', []);
const lowGroup = find(groupBySeverityBuckets, { key: 'low' }) || {};
const mediumGroup = find(groupBySeverityBuckets, { key: 'medium' }) || {};
const highGroup = find(groupBySeverityBuckets, { key: 'high' }) || {};
severities = {
low: (lowGroup.doc_count || 0) + staticAlertCount,
medium: mediumGroup.doc_count || 0,
high: highGroup.doc_count || 0,
};
}
alerts = {
count: bucketDocCount + staticAlertCount,
...severities,
};
}
} else {
// add metadata to the cluster's alerts object detailing that alerts are disabled because of the prod cluster license
alerts = {
clusterMeta: {
enabled: false,
message: i18n.translate(
'xpack.monitoring.clusterAlerts.unsupportedClusterAlertsDescription',
{
defaultMessage:
'Cluster [{clusterName}] license type [{licenseType}] does not support Cluster Alerts',
values: {
clusterName: cluster.cluster_name,
licenseType: `${license.type}`,
},
}
),
},
};
}
return Object.assign(reClusters, { [cluster.cluster_uuid]: alerts });
}, meta);
});
}

View file

@ -1,235 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import sinon from 'sinon';
import { merge } from 'lodash';
import { createStubs } from './__fixtures__/create_stubs';
import { alertsClustersAggregation } from './alerts_clusters_aggregation';
const clusters = [
{
cluster_uuid: 'cluster-abc0',
cluster_name: 'cluster-abc0-name',
license: { type: 'test_license' },
},
{
cluster_uuid: 'cluster-abc1',
cluster_name: 'cluster-abc1-name',
license: { type: 'test_license' },
},
{
cluster_uuid: 'cluster-abc2',
cluster_name: 'cluster-abc2-name',
license: { type: 'test_license' },
},
{
cluster_uuid: 'cluster-abc3',
cluster_name: 'cluster-abc3-name',
license: { type: 'test_license' },
},
{ cluster_uuid: 'cluster-no-license', cluster_name: 'cluster-no-license-name' },
{ cluster_uuid: 'cluster-invalid', cluster_name: 'cluster-invalid-name', license: {} },
];
const mockQueryResult = {
aggregations: {
group_by_cluster: {
buckets: [
{
key: 'cluster-abc1',
doc_count: 1,
group_by_severity: {
buckets: [{ key: 'low', doc_count: 1 }],
},
},
{
key: 'cluster-abc2',
doc_count: 2,
group_by_severity: {
buckets: [{ key: 'medium', doc_count: 2 }],
},
},
{
key: 'cluster-abc3',
doc_count: 3,
group_by_severity: {
buckets: [{ key: 'high', doc_count: 3 }],
},
},
],
},
},
};
// TODO: tests were not running and are not up to date.
describe.skip('Alerts Clusters Aggregation', () => {
describe('with alerts enabled', () => {
const featureStub = sinon.stub().returns({
getLicenseCheckResults: () => ({ clusterAlerts: { enabled: true } }),
});
const checkLicense = () => ({ clusterAlerts: { enabled: true } });
it('aggregates alert count summary by cluster', () => {
const { mockReq } = createStubs(mockQueryResult, featureStub);
return alertsClustersAggregation(mockReq, '.monitoring-alerts', clusters, checkLicense).then(
(result) => {
expect(result).to.eql({
alertsMeta: { enabled: true },
'cluster-abc0': undefined,
'cluster-abc1': {
count: 1,
high: 0,
low: 1,
medium: 0,
},
'cluster-abc2': {
count: 2,
high: 0,
low: 0,
medium: 2,
},
'cluster-abc3': {
count: 3,
high: 3,
low: 0,
medium: 0,
},
'cluster-no-license': undefined,
'cluster-invalid': undefined,
});
}
);
});
it('aggregates alert count summary by cluster include static alert', () => {
const { mockReq } = createStubs(mockQueryResult, featureStub);
const clusterLicenseNeedsTLS = { license: { cluster_needs_tls: true } };
const newClusters = Array.from(clusters);
newClusters[0] = merge({}, clusters[0], clusterLicenseNeedsTLS);
newClusters[1] = merge({}, clusters[1], clusterLicenseNeedsTLS);
return alertsClustersAggregation(
mockReq,
'.monitoring-alerts',
newClusters,
checkLicense
).then((result) => {
expect(result).to.eql({
alertsMeta: { enabled: true },
'cluster-abc0': {
count: 1,
high: 0,
medium: 0,
low: 1,
},
'cluster-abc1': {
count: 2,
high: 0,
low: 2,
medium: 0,
},
'cluster-abc2': {
count: 2,
high: 0,
low: 0,
medium: 2,
},
'cluster-abc3': {
count: 3,
high: 3,
low: 0,
medium: 0,
},
'cluster-no-license': undefined,
'cluster-invalid': undefined,
});
});
});
});
describe('with alerts disabled due to license', () => {
it('returns the input set if disabled because monitoring cluster checks', () => {
// monitoring clusters' license check to fail
const featureStub = sinon.stub().returns({
getLicenseCheckResults: () => ({
clusterAlerts: { enabled: false },
message: 'monitoring cluster license is fail',
}),
});
// prod clusters' license check to pass
const checkLicense = () => ({ clusterAlerts: { enabled: true } });
const { mockReq } = createStubs(mockQueryResult, featureStub);
return alertsClustersAggregation(mockReq, '.monitoring-alerts', clusters, checkLicense).then(
(result) => {
expect(result).to.eql({
alertsMeta: { enabled: false, message: 'monitoring cluster license is fail' },
});
}
);
});
it('returns the input set if disabled because production cluster checks', () => {
// monitoring clusters' license check to pass
const featureStub = sinon.stub().returns({
getLicenseCheckResults: () => ({ clusterAlerts: { enabled: true } }),
});
// prod clusters license check to fail
const checkLicense = () => ({ clusterAlerts: { enabled: false } });
const { mockReq } = createStubs(mockQueryResult, featureStub);
return alertsClustersAggregation(mockReq, '.monitoring-alerts', clusters, checkLicense).then(
(result) => {
expect(result).to.eql({
alertsMeta: { enabled: true },
'cluster-abc0': {
clusterMeta: {
enabled: false,
message:
'Cluster [cluster-abc0-name] license type [test_license] does not support Cluster Alerts',
},
},
'cluster-abc1': {
clusterMeta: {
enabled: false,
message:
'Cluster [cluster-abc1-name] license type [test_license] does not support Cluster Alerts',
},
},
'cluster-abc2': {
clusterMeta: {
enabled: false,
message:
'Cluster [cluster-abc2-name] license type [test_license] does not support Cluster Alerts',
},
},
'cluster-abc3': {
clusterMeta: {
enabled: false,
message:
'Cluster [cluster-abc3-name] license type [test_license] does not support Cluster Alerts',
},
},
'cluster-no-license': {
clusterMeta: {
enabled: false,
message: `Cluster [cluster-no-license-name] license type [undefined] does not support Cluster Alerts`,
},
},
'cluster-invalid': {
clusterMeta: {
enabled: false,
message: `Cluster [cluster-invalid-name] license type [undefined] does not support Cluster Alerts`,
},
},
});
}
);
});
});
});

View file

@ -1,111 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { includes } from 'lodash';
import { i18n } from '@kbn/i18n';
/**
* Function to do the work of checking license for cluster alerts feature support
* Can be used to power XpackInfo license check results as well as checking license of monitored clusters
*
* @param {String} type License type if a valid license. {@code null} if license was deleted.
* @param {Boolean} active Indicating that the overall license is active
* @param {String} clusterSource 'monitoring' or 'production'
* @param {Boolean} watcher {@code true} if Watcher is provided (or if its availability should not be checked)
*/
export function checkLicense(type, active, clusterSource, watcher = true) {
// return object, set up with safe defaults
const licenseInfo = {
clusterAlerts: { enabled: false },
};
// Disabled because there is no license
if (!type) {
return Object.assign(licenseInfo, {
message: i18n.translate(
'xpack.monitoring.clusterAlerts.checkLicense.licenseNotDeterminedDescription',
{
defaultMessage: `Cluster Alerts are not displayed because the [{clusterSource}] cluster's license could not be determined.`,
values: {
clusterSource,
},
}
),
});
}
// Disabled because the license type is not valid (basic)
if (!includes(['trial', 'standard', 'gold', 'platinum', 'enterprise'], type)) {
return Object.assign(licenseInfo, {
message: i18n.translate(
'xpack.monitoring.clusterAlerts.checkLicense.licenseIsBasicDescription',
{
defaultMessage: `Cluster Alerts are not displayed if Watcher is disabled or the [{clusterSource}] cluster's current license is Basic.`,
values: {
clusterSource,
},
}
),
});
}
// Disabled because the license is inactive
if (!active) {
return Object.assign(licenseInfo, {
message: i18n.translate(
'xpack.monitoring.clusterAlerts.checkLicense.licenseNotActiveDescription',
{
defaultMessage: `Cluster Alerts are not displayed because the [{clusterSource}] cluster's current license [{type}] is not active.`,
values: {
clusterSource,
type,
},
}
),
});
}
// Disabled because Watcher is not enabled (it may or may not be available)
if (!watcher) {
return Object.assign(licenseInfo, {
message: i18n.translate(
'xpack.monitoring.clusterAlerts.checkLicense.watcherIsDisabledDescription',
{
defaultMessage: 'Cluster Alerts are not enabled because Watcher is disabled.',
}
),
});
}
return Object.assign(licenseInfo, { clusterAlerts: { enabled: true } });
}
/**
* Function to "generate" license check results for {@code xpackInfo}.
*
* @param {Object} xpackInfo license information for the _Monitoring_ cluster
* @param {Function} _checkLicense Method exposed for easier unit testing
* @returns {Object} Response from {@code checker}
*/
export function checkLicenseGenerator(xpackInfo, _checkLicense = checkLicense) {
let type;
let active = false;
let watcher = false;
if (xpackInfo && xpackInfo.license) {
const watcherFeature = xpackInfo.feature('watcher');
if (watcherFeature) {
watcher = watcherFeature.isEnabled();
}
type = xpackInfo.license.getType();
active = xpackInfo.license.isActive();
}
return _checkLicense(type, active, 'monitoring', watcher);
}

View file

@ -1,149 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { checkLicense, checkLicenseGenerator } from './check_license';
import expect from '@kbn/expect';
import sinon from 'sinon';
describe('Monitoring Check License', () => {
describe('License undeterminable', () => {
it('null active license - results false with a message', () => {
const result = checkLicense(null, true, 'test-cluster-abc');
expect(result).to.eql({
clusterAlerts: { enabled: false },
message: `Cluster Alerts are not displayed because the [test-cluster-abc] cluster's license could not be determined.`,
});
});
});
describe('Inactive license', () => {
it('platinum inactive license - results false with a message', () => {
const result = checkLicense('platinum', false, 'test-cluster-def');
expect(result).to.eql({
clusterAlerts: { enabled: false },
message: `Cluster Alerts are not displayed because the [test-cluster-def] cluster's current license [platinum] is not active.`,
});
});
});
describe('Active license', () => {
describe('Unsupported license types', () => {
it('basic active license - results false with a message', () => {
const result = checkLicense('basic', true, 'test-cluster-ghi');
expect(result).to.eql({
clusterAlerts: { enabled: false },
message: `Cluster Alerts are not displayed if Watcher is disabled or the [test-cluster-ghi] cluster's current license is Basic.`,
});
});
});
describe('Supported license types', () => {
it('standard active license - results true with no message', () => {
const result = checkLicense('standard', true, 'test-cluster-jkl');
expect(result).to.eql({
clusterAlerts: { enabled: true },
});
});
it('gold active license - results true with no message', () => {
const result = checkLicense('gold', true, 'test-cluster-mno');
expect(result).to.eql({
clusterAlerts: { enabled: true },
});
});
it('platinum active license - results true with no message', () => {
const result = checkLicense('platinum', true, 'test-cluster-pqr');
expect(result).to.eql({
clusterAlerts: { enabled: true },
});
});
it('enterprise active license - results true with no message', () => {
const result = checkLicense('enterprise', true, 'test-cluster-pqr');
expect(result).to.eql({
clusterAlerts: { enabled: true },
});
});
describe('Watcher is not enabled', () => {
it('platinum active license - watcher disabled - results false with message', () => {
const result = checkLicense('platinum', true, 'test-cluster-pqr', false);
expect(result).to.eql({
clusterAlerts: { enabled: false },
message: 'Cluster Alerts are not enabled because Watcher is disabled.',
});
});
});
});
});
describe('XPackInfo checkLicenseGenerator', () => {
it('with deleted license', () => {
const expected = 123;
const checker = sinon.stub().returns(expected);
const result = checkLicenseGenerator(null, checker);
expect(result).to.be(expected);
expect(checker.withArgs(undefined, false, 'monitoring', false).called).to.be(true);
});
it('license without watcher', () => {
const expected = 123;
const xpackInfo = {
license: {
getType: () => 'fake-type',
isActive: () => true,
},
feature: () => null,
};
const checker = sinon.stub().returns(expected);
const result = checkLicenseGenerator(xpackInfo, checker);
expect(result).to.be(expected);
expect(checker.withArgs('fake-type', true, 'monitoring', false).called).to.be(true);
});
it('mock license with watcher', () => {
const expected = 123;
const feature = sinon
.stub()
.withArgs('watcher')
.returns({ isEnabled: () => true });
const xpackInfo = {
license: {
getType: () => 'another-type',
isActive: () => true,
},
feature,
};
const checker = sinon.stub().returns(expected);
const result = checkLicenseGenerator(xpackInfo, checker);
expect(result).to.be(expected);
expect(feature.withArgs('watcher').calledOnce).to.be(true);
expect(checker.withArgs('another-type', true, 'monitoring', true).called).to.be(true);
});
it('platinum license with watcher', () => {
const xpackInfo = {
license: {
getType: () => 'platinum',
isActive: () => true,
},
feature: () => {
return {
isEnabled: () => true,
};
},
};
const result = checkLicenseGenerator(xpackInfo);
expect(result).to.eql({ clusterAlerts: { enabled: true } });
});
});
});

View file

@ -1,47 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { get } from 'lodash';
import { i18n } from '@kbn/i18n';
/**
* Determine if an API for Cluster Alerts should respond based on the license and configuration of the monitoring cluster.
*
* Note: This does not guarantee that any production cluster has a valid license; only that Cluster Alerts in general can be used!
*
* @param {Object} server Server object containing config and plugins
* @return {Boolean} {@code true} to indicate that cluster alerts can be used.
*/
export function verifyMonitoringLicense(server) {
const config = server.config();
// if cluster alerts are enabled, then ensure that we can use it according to the license
if (config.get('monitoring.cluster_alerts.enabled')) {
const xpackInfo = get(server.plugins.monitoring, 'info');
if (xpackInfo) {
const watcherFeature = xpackInfo.getWatcherFeature();
return {
enabled: watcherFeature.isEnabled,
message: xpackInfo.getMessage(),
};
}
return {
enabled: false,
message: i18n.translate('xpack.monitoring.clusterAlerts.notDeterminedLicenseDescription', {
defaultMessage: 'Status of Cluster Alerts feature could not be determined.',
}),
};
}
return {
enabled: false,
message: i18n.translate('xpack.monitoring.clusterAlerts.disabledLicenseDescription', {
defaultMessage: 'Cluster Alerts feature is disabled.',
}),
};
}

View file

@ -1,88 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { verifyMonitoringLicense } from './verify_monitoring_license';
import expect from '@kbn/expect';
import sinon from 'sinon';
// TODO: tests were not running and are not up to date.
describe.skip('Monitoring Verify License', () => {
describe('Disabled by Configuration', () => {
const get = sinon.stub().withArgs('xpack.monitoring.cluster_alerts.enabled').returns(false);
const server = { config: sinon.stub().returns({ get }) };
it('verifyMonitoringLicense returns false without checking the license', () => {
const verification = verifyMonitoringLicense(server);
expect(verification.enabled).to.be(false);
expect(verification.message).to.be('Cluster Alerts feature is disabled.');
expect(get.withArgs('xpack.monitoring.cluster_alerts.enabled').calledOnce).to.be(true);
});
});
describe('Enabled by Configuration', () => {
it('verifyMonitoringLicense returns false if enabled by configuration, but not by license', () => {
const get = sinon.stub().withArgs('xpack.monitoring.cluster_alerts.enabled').returns(true);
const server = {
config: sinon.stub().returns({ get }),
plugins: { monitoring: { info: {} } },
};
const getLicenseCheckResults = sinon
.stub()
.returns({ clusterAlerts: { enabled: false }, message: 'failed!!' });
const feature = sinon.stub().withArgs('monitoring').returns({ getLicenseCheckResults });
server.plugins.monitoring.info = { feature };
const verification = verifyMonitoringLicense(server);
expect(verification.enabled).to.be(false);
expect(verification.message).to.be('failed!!');
expect(get.withArgs('xpack.monitoring.cluster_alerts.enabled').calledOnce).to.be(true);
expect(feature.withArgs('monitoring').calledOnce).to.be(true);
expect(getLicenseCheckResults.calledOnce).to.be(true);
});
it('verifyMonitoringLicense returns true if enabled by configuration and by license', () => {
const get = sinon.stub().withArgs('xpack.monitoring.cluster_alerts.enabled').returns(true);
const server = {
config: sinon.stub().returns({ get }),
plugins: { monitoring: { info: {} } },
};
const getLicenseCheckResults = sinon.stub().returns({ clusterAlerts: { enabled: true } });
const feature = sinon.stub().withArgs('monitoring').returns({ getLicenseCheckResults });
server.plugins.monitoring.info = { feature };
const verification = verifyMonitoringLicense(server);
expect(verification.enabled).to.be(true);
expect(verification.message).to.be.undefined;
expect(get.withArgs('xpack.monitoring.cluster_alerts.enabled').calledOnce).to.be(true);
expect(feature.withArgs('monitoring').calledOnce).to.be(true);
expect(getLicenseCheckResults.calledOnce).to.be(true);
});
});
it('Monitoring feature info cannot be determined', () => {
const get = sinon.stub().withArgs('xpack.monitoring.cluster_alerts.enabled').returns(true);
const server = {
config: sinon.stub().returns({ get }),
plugins: { monitoring: undefined }, // simulate race condition
};
const verification = verifyMonitoringLicense(server);
expect(verification.enabled).to.be(false);
expect(verification.message).to.be('Status of Cluster Alerts feature could not be determined.');
expect(get.withArgs('xpack.monitoring.cluster_alerts.enabled').calledOnce).to.be(true);
});
});

View file

@ -37,25 +37,9 @@ describe.skip('monitoring plugin deprecations', function () {
expect(log).not.toHaveBeenCalled();
});
it(`shouldn't log when cluster alerts are disabled`, function () {
const settings = {
cluster_alerts: {
enabled: false,
email_notifications: {
enabled: true,
},
},
};
const log = jest.fn();
transformDeprecations(settings, fromPath, log);
expect(log).not.toHaveBeenCalled();
});
it(`shouldn't log when email_address is specified`, function () {
const settings = {
cluster_alerts: {
enabled: true,
email_notifications: {
enabled: true,
email_address: 'foo@bar.com',
@ -71,7 +55,6 @@ describe.skip('monitoring plugin deprecations', function () {
it(`should log when email_address is missing, but alerts/notifications are both enabled`, function () {
const settings = {
cluster_alerts: {
enabled: true,
email_notifications: {
enabled: true,
},

View file

@ -15,8 +15,6 @@ import { getKibanasForClusters } from '../kibana';
import { getLogstashForClusters } from '../logstash';
import { getLogstashPipelineIds } from '../logstash/get_pipeline_ids';
import { getBeatsForClusters } from '../beats';
import { verifyMonitoringLicense } from '../../cluster_alerts/verify_monitoring_license';
import { checkLicense as checkLicenseForAlerts } from '../../cluster_alerts/check_license';
import { getClustersSummary } from './get_clusters_summary';
import {
STANDALONE_CLUSTER_CLUSTER_UUID,
@ -128,19 +126,6 @@ export async function getClustersFromRequest(
);
for (const cluster of clusters) {
const verification = verifyMonitoringLicense(req.server);
if (!verification.enabled) {
// return metadata detailing that alerts is disabled because of the monitoring cluster license
cluster.alerts = {
alertsMeta: {
enabled: verification.enabled,
message: verification.message, // NOTE: this is only defined when the alert feature is disabled
},
list: {},
};
continue;
}
if (!alertsClient) {
cluster.alerts = {
list: {},
@ -148,17 +133,7 @@ export async function getClustersFromRequest(
enabled: false,
},
};
continue;
}
// check the license type of the production cluster for alerts feature support
const license = cluster.license || {};
const prodLicenseInfo = checkLicenseForAlerts(
license.type,
license.status === 'active',
'production'
);
if (prodLicenseInfo.clusterAlerts.enabled) {
} else {
try {
cluster.alerts = {
list: Object.keys(alertStatus).reduce((accum, alertName) => {
@ -190,29 +165,7 @@ export async function getClustersFromRequest(
},
};
}
continue;
}
cluster.alerts = {
list: {},
alertsMeta: {
enabled: false,
},
clusterMeta: {
enabled: false,
message: i18n.translate(
'xpack.monitoring.clusterAlerts.unsupportedClusterAlertsDescription',
{
defaultMessage:
'Cluster [{clusterName}] license type [{licenseType}] does not support Cluster Alerts',
values: {
clusterName: cluster.cluster_name,
licenseType: `${license.type}`,
},
}
),
},
};
}
}
}

View file

@ -15760,15 +15760,6 @@
"xpack.monitoring.cluster.overview.logstashPanel.withPersistentQueuesLabel": "永続キューあり",
"xpack.monitoring.cluster.overview.pageTitle": "クラスターの概要",
"xpack.monitoring.cluster.overviewTitle": "概要",
"xpack.monitoring.clusterAlerts.checkLicense.licenseIsBasicDescription": "Watcher が無効になっているか、[{clusterSource}] クラスターの現在のライセンスがベーシックの場合、クラスターアラートは表示されません。",
"xpack.monitoring.clusterAlerts.checkLicense.licenseNotActiveDescription": "[{clusterSource}] クラスターの現在のライセンス [{type}] がアクティブでないため、クラスターアラートは表示されません。",
"xpack.monitoring.clusterAlerts.checkLicense.licenseNotDeterminedDescription": "[{clusterSource}] クラスターのライセンスが確認できなかったため、クラスターアラートは表示されません。",
"xpack.monitoring.clusterAlerts.checkLicense.watcherIsDisabledDescription": "Watcher が無効なため、クラスターアラートを利用できません。",
"xpack.monitoring.clusterAlerts.clusterNeedsTSLEnabledDescription": "セキュリティが有効な場合、ゴールドまたはプラチナライセンスの適用に TLS の構成が必要です。",
"xpack.monitoring.clusterAlerts.disabledLicenseDescription": "クラスターアラート機能は無効になっています。",
"xpack.monitoring.clusterAlerts.notDeterminedLicenseDescription": "クラスターアラート機能のステータスが確認できませんでした。",
"xpack.monitoring.clusterAlerts.seeDocumentationDescription": "詳細はドキュメンテーションをご覧ください。",
"xpack.monitoring.clusterAlerts.unsupportedClusterAlertsDescription": "クラスター [{clusterName}] ライセンスタイプ [{licenseType}] はクラスターアラートをサポートしていません",
"xpack.monitoring.clusterAlertsNavigation.clusterAlertsLinkText": "クラスターアラート",
"xpack.monitoring.clustersNavigation.clustersLinkText": "クラスター",
"xpack.monitoring.clusterStats.uuidNotFoundErrorMessage": "選択された時間範囲にクラスターが見つかりませんでした。UUID{clusterUuid}",

View file

@ -15984,15 +15984,6 @@
"xpack.monitoring.cluster.overview.logstashPanel.withPersistentQueuesLabel": "持久性队列",
"xpack.monitoring.cluster.overview.pageTitle": "集群概览",
"xpack.monitoring.cluster.overviewTitle": "概览",
"xpack.monitoring.clusterAlerts.checkLicense.licenseIsBasicDescription": "如果禁用了 Watcher 或 [{clusterSource}] 集群的当前许可为基本级许可,则“集群告警”将不会显示。",
"xpack.monitoring.clusterAlerts.checkLicense.licenseNotActiveDescription": "因为 [{clusterSource}] 集群的当前许可 [{type}] 未处于活动状态,所以“集群告警”将不会显示。",
"xpack.monitoring.clusterAlerts.checkLicense.licenseNotDeterminedDescription": "因为无法确定 [{clusterSource}] 集群的许可,所以“集群告警”将不会显示。",
"xpack.monitoring.clusterAlerts.checkLicense.watcherIsDisabledDescription": "因为禁用了 Watcher所以“集群告警”未启用。",
"xpack.monitoring.clusterAlerts.clusterNeedsTSLEnabledDescription": "启用安全性时,需要配置 TLS才能应用黄金或白金许可。",
"xpack.monitoring.clusterAlerts.disabledLicenseDescription": "“集群告警”功能已禁用。",
"xpack.monitoring.clusterAlerts.notDeterminedLicenseDescription": "无法确定“集群告警”功能的状态。",
"xpack.monitoring.clusterAlerts.seeDocumentationDescription": "有关详情,请参阅文档。",
"xpack.monitoring.clusterAlerts.unsupportedClusterAlertsDescription": "集群 [{clusterName}] 许可类型 [{licenseType}] 不支持“集群告警”",
"xpack.monitoring.clusterAlertsNavigation.clusterAlertsLinkText": "集群告警",
"xpack.monitoring.clustersNavigation.clustersLinkText": "集群",
"xpack.monitoring.clusterStats.uuidNotFoundErrorMessage": "在选定时间范围内找不到该集群。UUID{clusterUuid}",

View file

@ -102,11 +102,7 @@
},
"alerts": {
"alertsMeta": {
"enabled": false
},
"clusterMeta": {
"enabled": false,
"message": "Cluster [clustertwo] license type [basic] does not support Cluster Alerts"
"enabled": true
},
"list": {}
},

View file

@ -102,11 +102,7 @@
},
"alerts": {
"alertsMeta": {
"enabled": false
},
"clusterMeta": {
"enabled": false,
"message": "Cluster [monitoring] license type [basic] does not support Cluster Alerts"
"enabled": true
},
"list": {}
},
@ -170,11 +166,7 @@
},
"alerts": {
"alertsMeta": {
"enabled": false
},
"clusterMeta": {
"enabled": false,
"message": "Cluster [] license type [undefined] does not support Cluster Alerts"
"enabled": true
},
"list": {}
},

View file

@ -109,7 +109,7 @@ export default function ({ getService, getPageObjects }) {
it('primary basic cluster shows cluster metrics', async () => {
expect(await clusterList.getClusterName(SUPPORTED_CLUSTER_UUID)).to.be('production');
expect(await clusterList.getClusterStatus(SUPPORTED_CLUSTER_UUID)).to.be('N/A');
expect(await clusterList.getClusterStatus(SUPPORTED_CLUSTER_UUID)).to.be('Clear');
expect(await clusterList.getClusterNodesCount(SUPPORTED_CLUSTER_UUID)).to.be('2');
expect(await clusterList.getClusterIndicesCount(SUPPORTED_CLUSTER_UUID)).to.be('4');
expect(await clusterList.getClusterDataSize(SUPPORTED_CLUSTER_UUID)).to.be('1.6 MB');