Deprecate xpack:defaultAdminEmail for monitoring alerts (#22195)

This commit is contained in:
Larry Gregory 2018-09-06 10:26:05 -04:00 committed by GitHub
parent 5f02f3e4ea
commit 5f96c903f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 245 additions and 45 deletions

View file

@ -46,8 +46,6 @@ To receive email notifications for the Cluster Alerts:
1. Configure an email account as described in 1. Configure an email account as described in
{xpack-ref}/actions-email.html#configuring-email[Configuring Email Accounts]. {xpack-ref}/actions-email.html#configuring-email[Configuring Email Accounts].
2. Navigate to the *Management* page in {kib}. 2. Configure the `xpack.monitoring.cluster_alerts.email_notifications.email_address` setting in `kibana.yml` with your email address.
3. Go to the *Advanced Settings* page, find the `xpack:defaultAdminEmail`
setting, and enter your email address.
Email notifications are sent only when Cluster Alerts are triggered and resolved. Email notifications are sent only when Cluster Alerts are triggered and resolved.

View file

@ -112,4 +112,66 @@ describe('monitoring plugin deprecations', function () {
expect(log.called).to.be(false); expect(log.called).to.be(false);
}); });
describe('cluster_alerts.email_notifications.email_address', function () {
it(`shouldn't log when email notifications are disabled`, function () {
const settings = {
cluster_alerts: {
email_notifications: {
enabled: false
}
}
};
const log = sinon.spy();
transformDeprecations(settings, log);
expect(log.called).to.be(false);
});
it(`shouldn't log when cluster alerts are disabled`, function () {
const settings = {
cluster_alerts: {
enabled: false,
email_notifications: {
enabled: true
}
}
};
const log = sinon.spy();
transformDeprecations(settings, log);
expect(log.called).to.be(false);
});
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'
}
}
};
const log = sinon.spy();
transformDeprecations(settings, log);
expect(log.called).to.be(false);
});
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
}
}
};
const log = sinon.spy();
transformDeprecations(settings, log);
expect(log.called).to.be(true);
});
});
}); });

View file

@ -94,7 +94,7 @@ export const CALCULATE_DURATION_UNTIL = 'until';
/** /**
* In order to show ML Jobs tab in the Elasticsearch section / tab navigation, license must be supported * In order to show ML Jobs tab in the Elasticsearch section / tab navigation, license must be supported
*/ */
export const ML_SUPPORTED_LICENSES = [ 'trial', 'platinum' ]; export const ML_SUPPORTED_LICENSES = ['trial', 'platinum'];
/** /**
* Metadata service URLs for the different cloud services that have constant URLs (e.g., unlike GCP, which is a constant prefix). * Metadata service URLs for the different cloud services that have constant URLs (e.g., unlike GCP, which is a constant prefix).
@ -135,7 +135,12 @@ export const DEFAULT_NO_DATA_MESSAGE_WITH_FILTER = (
); );
export const TABLE_ACTION_UPDATE_FILTER = 'UPDATE_FILTER'; export const TABLE_ACTION_UPDATE_FILTER = 'UPDATE_FILTER';
export const TABLE_ACTION_RESET_PAGING = 'RESET_PAGING'; export const TABLE_ACTION_RESET_PAGING = 'RESET_PAGING';
export const DEBOUNCE_SLOW_MS = 17; // roughly how long it takes to render a frame at 60fps export const DEBOUNCE_SLOW_MS = 17; // roughly how long it takes to render a frame at 60fps
export const DEBOUNCE_FAST_MS = 10; // roughly how long it takes to render a frame at 100fps export const DEBOUNCE_FAST_MS = 10; // roughly how long it takes to render a frame at 100fps
/**
* 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';

View file

@ -13,7 +13,7 @@ import { XPACK_INFO_API_DEFAULT_POLL_FREQUENCY_IN_MILLIS } from '../../server/li
*/ */
export const config = (Joi) => { export const config = (Joi) => {
const { array, boolean, number, object, string } = Joi; const { array, boolean, number, object, string } = Joi;
const DEFAULT_REQUEST_HEADERS = [ 'authorization' ]; const DEFAULT_REQUEST_HEADERS = ['authorization'];
return object({ return object({
ccs: object({ ccs: object({
@ -49,7 +49,8 @@ export const config = (Joi) => {
enabled: boolean().default(true), enabled: boolean().default(true),
index: string().default('.monitoring-alerts-6'), index: string().default('.monitoring-alerts-6'),
email_notifications: object({ email_notifications: object({
enabled: boolean().default(true) enabled: boolean().default(true),
email_address: string().email(),
}).default() }).default()
}).default(), }).default(),
xpack_api_polling_frequency_millis: number().default(XPACK_INFO_API_DEFAULT_POLL_FREQUENCY_IN_MILLIS), xpack_api_polling_frequency_millis: number().default(XPACK_INFO_API_DEFAULT_POLL_FREQUENCY_IN_MILLIS),

View file

@ -5,6 +5,7 @@
*/ */
import { get, has, set } from 'lodash'; import { get, has, set } from 'lodash';
import { CLUSTER_ALERTS_ADDRESS_CONFIG_KEY } from './common/constants';
/** /**
* Re-writes deprecated user-defined config settings and logs warnings as a * Re-writes deprecated user-defined config settings and logs warnings as a
@ -29,12 +30,19 @@ export const deprecations = ({ rename }) => {
delete settings.elasticsearch.ssl.verify; delete settings.elasticsearch.ssl.verify;
log('Config key "xpack.monitoring.elasticsearch.ssl.verify" is deprecated. ' + log('Config key "xpack.monitoring.elasticsearch.ssl.verify" is deprecated. ' +
'It has been replaced with "xpack.monitoring.elasticsearch.ssl.verificationMode"'); 'It has been replaced with "xpack.monitoring.elasticsearch.ssl.verificationMode"');
}, },
(settings, log) => { (settings, log) => {
if (has(settings, 'report_stats')) { if (has(settings, 'report_stats')) {
log('Config key "xpack.monitoring.report_stats" is deprecated and will be removed in 7.0. ' + log('Config key "xpack.monitoring.report_stats" is deprecated and will be removed in 7.0. ' +
'Use "xpack.xpack_main.telemetry.enabled" instead.'); 'Use "xpack.xpack_main.telemetry.enabled" instead.');
}
},
(settings, log) => {
const clusterAlertsEnabled = get(settings, 'cluster_alerts.enabled');
const emailNotificationsEnabled = clusterAlertsEnabled && get(settings, 'cluster_alerts.email_notifications.enabled');
if (emailNotificationsEnabled && !get(settings, CLUSTER_ALERTS_ADDRESS_CONFIG_KEY)) {
log(`Config key "${CLUSTER_ALERTS_ADDRESS_CONFIG_KEY}" will be required for email notifications to work in 7.0."`);
} }
}, },
]; ];

View file

@ -8,27 +8,31 @@ import expect from 'expect.js';
import { checkForEmailValue } from '../get_settings_collector'; import { checkForEmailValue } from '../get_settings_collector';
describe('getSettingsCollector / checkForEmailValue', () => { describe('getSettingsCollector / checkForEmailValue', () => {
const mockLogger = {
warn: () => { }
};
it('ignores shouldUseNull=true value and returns email if email value if one is set', async () => { it('ignores shouldUseNull=true value and returns email if email value if one is set', async () => {
const shouldUseNull = true; const shouldUseNull = true;
const getDefaultAdminEmailMock = () => 'test@elastic.co'; const getDefaultAdminEmailMock = () => 'test@elastic.co';
expect(await checkForEmailValue(undefined, undefined, shouldUseNull, getDefaultAdminEmailMock)).to.be('test@elastic.co'); expect(await checkForEmailValue(undefined, undefined, mockLogger, shouldUseNull, getDefaultAdminEmailMock)).to.be('test@elastic.co');
}); });
it('ignores shouldUseNull=false value and returns email if email value if one is set', async () => { it('ignores shouldUseNull=false value and returns email if email value if one is set', async () => {
const shouldUseNull = false; const shouldUseNull = false;
const getDefaultAdminEmailMock = () => 'test@elastic.co'; const getDefaultAdminEmailMock = () => 'test@elastic.co';
expect(await checkForEmailValue(undefined, undefined, shouldUseNull, getDefaultAdminEmailMock)).to.be('test@elastic.co'); expect(await checkForEmailValue(undefined, undefined, mockLogger, shouldUseNull, getDefaultAdminEmailMock)).to.be('test@elastic.co');
}); });
it('returns a null if no email value is set and null is allowed', async () => { it('returns a null if no email value is set and null is allowed', async () => {
const shouldUseNull = true; const shouldUseNull = true;
const getDefaultAdminEmailMock = () => null; const getDefaultAdminEmailMock = () => null;
expect(await checkForEmailValue(undefined, undefined, shouldUseNull, getDefaultAdminEmailMock)).to.be(null); expect(await checkForEmailValue(undefined, undefined, mockLogger, shouldUseNull, getDefaultAdminEmailMock)).to.be(null);
}); });
it('returns undefined if no email value is set and null is not allowed', async () => { it('returns undefined if no email value is set and null is not allowed', async () => {
const shouldUseNull = false; const shouldUseNull = false;
const getDefaultAdminEmailMock = () => null; const getDefaultAdminEmailMock = () => null;
expect(await checkForEmailValue(undefined, undefined, shouldUseNull, getDefaultAdminEmailMock)).to.be(undefined); expect(await checkForEmailValue(undefined, undefined, mockLogger, shouldUseNull, getDefaultAdminEmailMock)).to.be(undefined);
}); });
}); });

View file

@ -9,16 +9,23 @@ import sinon from 'sinon';
import { set } from 'lodash'; import { set } from 'lodash';
import { XPACK_DEFAULT_ADMIN_EMAIL_UI_SETTING } from '../../../../../../server/lib/constants'; import { XPACK_DEFAULT_ADMIN_EMAIL_UI_SETTING } from '../../../../../../server/lib/constants';
import { getDefaultAdminEmail } from '../get_settings_collector'; import { getDefaultAdminEmail, resetDeprecationWarning } from '../get_settings_collector';
import { CLUSTER_ALERTS_ADDRESS_CONFIG_KEY } from '../../../../common/constants';
describe('getSettingsCollector / getDefaultAdminEmail', () => { describe('getSettingsCollector / getDefaultAdminEmail', () => {
function setup({ enabled = true, docExists = true, adminEmail = 'admin@email.com' }) { function setup({ enabled = true, docExists = true, defaultAdminEmail = 'default-admin@email.com', adminEmail = null }) {
const config = { get: sinon.stub() }; const config = { get: sinon.stub() };
config.get config.get
.withArgs('xpack.monitoring.cluster_alerts.email_notifications.enabled') .withArgs('xpack.monitoring.cluster_alerts.email_notifications.enabled')
.returns(enabled); .returns(enabled);
if (adminEmail) {
config.get
.withArgs(`xpack.monitoring.${CLUSTER_ALERTS_ADDRESS_CONFIG_KEY}`)
.returns(adminEmail);
}
config.get config.get
.withArgs('kibana.index') .withArgs('kibana.index')
.returns('.kibana'); .returns('.kibana');
@ -29,8 +36,8 @@ describe('getSettingsCollector / getDefaultAdminEmail', () => {
const doc = {}; const doc = {};
if (docExists) { if (docExists) {
if (adminEmail) { if (defaultAdminEmail) {
set(doc, ['_source', 'config', XPACK_DEFAULT_ADMIN_EMAIL_UI_SETTING], adminEmail); set(doc, ['_source', 'config', XPACK_DEFAULT_ADMIN_EMAIL_UI_SETTING], defaultAdminEmail);
} else { } else {
set(doc, '_source.config', {}); set(doc, '_source.config', {});
} }
@ -46,41 +53,131 @@ describe('getSettingsCollector / getDefaultAdminEmail', () => {
})) }))
.returns(doc); .returns(doc);
const log = {
warn: sinon.stub()
};
return { return {
config, config,
callCluster callCluster,
log,
}; };
} }
describe('xpack.monitoring.cluster_alerts.email_notifications.enabled = false', () => { describe('using xpack:defaultAdminEmail', () => {
it('returns null', async () => { beforeEach(() => {
const { config, callCluster } = setup({ enabled: false }); resetDeprecationWarning();
expect(await getDefaultAdminEmail(config, callCluster)).to.be(null); });
sinon.assert.notCalled(callCluster);
describe('xpack.monitoring.cluster_alerts.email_notifications.enabled = false', () => {
it('returns null', async () => {
const { config, callCluster, log } = setup({ enabled: false });
expect(await getDefaultAdminEmail(config, callCluster, log)).to.be(null);
sinon.assert.notCalled(callCluster);
});
it('does not log a deprecation warning', async () => {
const { config, callCluster, log } = setup({ enabled: false });
await getDefaultAdminEmail(config, callCluster, log);
sinon.assert.notCalled(log.warn);
});
});
describe('doc does not exist', () => {
it('returns null', async () => {
const { config, callCluster, log } = setup({ docExists: false });
expect(await getDefaultAdminEmail(config, callCluster, log)).to.be(null);
sinon.assert.calledOnce(callCluster);
});
it('logs a deprecation warning', async () => {
const { config, callCluster, log } = setup({ docExists: false });
await getDefaultAdminEmail(config, callCluster, log);
sinon.assert.calledOnce(log.warn);
});
});
describe('value is not defined', () => {
it('returns null', async () => {
const { config, callCluster, log } = setup({ defaultAdminEmail: false });
expect(await getDefaultAdminEmail(config, callCluster, log)).to.be(null);
sinon.assert.calledOnce(callCluster);
});
it('logs a deprecation warning', async () => {
const { config, callCluster, log } = setup({ defaultAdminEmail: false });
await getDefaultAdminEmail(config, callCluster, log);
sinon.assert.calledOnce(log.warn);
});
});
describe('value is defined', () => {
it('returns value', async () => {
const { config, callCluster, log } = setup({ defaultAdminEmail: 'hello@world' });
expect(await getDefaultAdminEmail(config, callCluster, log)).to.be('hello@world');
sinon.assert.calledOnce(callCluster);
});
it('logs a deprecation warning', async () => {
const { config, callCluster, log } = setup({ defaultAdminEmail: 'hello@world' });
await getDefaultAdminEmail(config, callCluster, log);
sinon.assert.calledOnce(log.warn);
});
}); });
}); });
describe('doc does not exist', () => { describe('using xpack.monitoring.cluster_alerts.email_notifications.email_address', () => {
it('returns null', async () => { beforeEach(() => {
const { config, callCluster } = setup({ docExists: false }); resetDeprecationWarning();
expect(await getDefaultAdminEmail(config, callCluster)).to.be(null);
sinon.assert.calledOnce(callCluster);
}); });
});
describe('value is not defined', () => { describe('xpack.monitoring.cluster_alerts.email_notifications.enabled = false', () => {
it('returns null', async () => { it('returns null', async () => {
const { config, callCluster } = setup({ adminEmail: false }); const { config, callCluster, log } = setup({ enabled: false });
expect(await getDefaultAdminEmail(config, callCluster)).to.be(null); expect(await getDefaultAdminEmail(config, callCluster, log)).to.be(null);
sinon.assert.calledOnce(callCluster); sinon.assert.notCalled(callCluster);
});
it('does not log a deprecation warning', async () => {
const { config, callCluster, log } = setup({ enabled: false });
await getDefaultAdminEmail(config, callCluster, log);
sinon.assert.notCalled(log.warn);
});
}); });
});
describe('value is defined', () => { describe('value is not defined', () => {
it('returns value', async () => { it('returns value from xpack:defaultAdminEmail', async () => {
const { config, callCluster } = setup({ adminEmail: 'hello@world' }); const { config, callCluster, log } = setup({
expect(await getDefaultAdminEmail(config, callCluster)).to.be('hello@world'); defaultAdminEmail: 'default-admin@email.com',
sinon.assert.calledOnce(callCluster); adminEmail: false
});
expect(await getDefaultAdminEmail(config, callCluster, log)).to.be('default-admin@email.com');
sinon.assert.calledOnce(callCluster);
});
it('logs a deprecation warning', async () => {
const { config, callCluster, log } = setup({
defaultAdminEmail: 'default-admin@email.com',
adminEmail: false
});
await getDefaultAdminEmail(config, callCluster, log);
sinon.assert.calledOnce(log.warn);
});
});
describe('value is defined', () => {
it('returns value', async () => {
const { config, callCluster, log } = setup({ adminEmail: 'hello@world' });
expect(await getDefaultAdminEmail(config, callCluster, log)).to.be('hello@world');
sinon.assert.notCalled(callCluster);
});
it('does not log a deprecation warning', async () => {
const { config, callCluster, log } = setup({ adminEmail: 'hello@world' });
await getDefaultAdminEmail(config, callCluster, log);
sinon.assert.notCalled(log.warn);
});
}); });
}); });
}); });

View file

@ -6,24 +6,48 @@
import { get } from 'lodash'; import { get } from 'lodash';
import { XPACK_DEFAULT_ADMIN_EMAIL_UI_SETTING } from '../../../../../server/lib/constants'; import { XPACK_DEFAULT_ADMIN_EMAIL_UI_SETTING } from '../../../../../server/lib/constants';
import { KIBANA_SETTINGS_TYPE } from '../../../common/constants'; import { CLUSTER_ALERTS_ADDRESS_CONFIG_KEY, KIBANA_SETTINGS_TYPE } from '../../../common/constants';
let loggedDeprecationWarning = false;
export function resetDeprecationWarning() {
loggedDeprecationWarning = false;
}
/* /*
* Check if Cluster Alert email notifications is enabled in config * Check if Cluster Alert email notifications is enabled in config
* If so, use uiSettings API to fetch the X-Pack default admin email * If so, use uiSettings API to fetch the X-Pack default admin email
*/ */
export async function getDefaultAdminEmail(config, callCluster) { export async function getDefaultAdminEmail(config, callCluster, log) {
if (!config.get('xpack.monitoring.cluster_alerts.email_notifications.enabled')) { if (!config.get('xpack.monitoring.cluster_alerts.email_notifications.enabled')) {
return null; return null;
} }
const emailAddressConfigKey = `xpack.monitoring.${CLUSTER_ALERTS_ADDRESS_CONFIG_KEY}`;
const configuredEmailAddress = config.get(emailAddressConfigKey);
if (configuredEmailAddress) {
return configuredEmailAddress;
}
// DEPRECATED (Remove below in 7.0): If an email address is not configured in kibana.yml, then fallback to xpack:defaultAdminEmail
if (!loggedDeprecationWarning) {
const message = (
`Monitoring is using ${XPACK_DEFAULT_ADMIN_EMAIL_UI_SETTING} for cluster alert notifications, ` +
`which will not be supported in Kibana 7.0. Please configure ${emailAddressConfigKey} in your kibana.yml settings`
);
log.warn(message);
loggedDeprecationWarning = true;
}
const index = config.get('kibana.index'); const index = config.get('kibana.index');
const version = config.get('pkg.version'); const version = config.get('pkg.version');
const uiSettingsDoc = await callCluster('get', { const uiSettingsDoc = await callCluster('get', {
index, index,
type: 'doc', type: 'doc',
id: `config:${version}`, id: `config:${version}`,
ignore: [ 400, 404 ] // 400 if the index is closed, 404 if it does not exist ignore: [400, 404] // 400 if the index is closed, 404 if it does not exist
}); });
return get(uiSettingsDoc, ['_source', 'config', XPACK_DEFAULT_ADMIN_EMAIL_UI_SETTING], null); return get(uiSettingsDoc, ['_source', 'config', XPACK_DEFAULT_ADMIN_EMAIL_UI_SETTING], null);
@ -35,10 +59,11 @@ let shouldUseNull = true;
export async function checkForEmailValue( export async function checkForEmailValue(
config, config,
callCluster, callCluster,
log,
_shouldUseNull = shouldUseNull, _shouldUseNull = shouldUseNull,
_getDefaultAdminEmail = getDefaultAdminEmail _getDefaultAdminEmail = getDefaultAdminEmail
) { ) {
const defaultAdminEmail = await _getDefaultAdminEmail(config, callCluster); const defaultAdminEmail = await _getDefaultAdminEmail(config, callCluster, log);
// Allow null so clearing the advanced setting will be reflected in the data // Allow null so clearing the advanced setting will be reflected in the data
const isAcceptableNull = defaultAdminEmail === null && _shouldUseNull; const isAcceptableNull = defaultAdminEmail === null && _shouldUseNull;
@ -61,7 +86,7 @@ export function getSettingsCollector(server) {
type: KIBANA_SETTINGS_TYPE, type: KIBANA_SETTINGS_TYPE,
async fetch(callCluster) { async fetch(callCluster) {
let kibanaSettingsData; let kibanaSettingsData;
const defaultAdminEmail = await checkForEmailValue(config, callCluster); const defaultAdminEmail = await checkForEmailValue(config, callCluster, this.log);
// skip everything if defaultAdminEmail === undefined // skip everything if defaultAdminEmail === undefined
if (defaultAdminEmail || (defaultAdminEmail === null && shouldUseNull)) { if (defaultAdminEmail || (defaultAdminEmail === null && shouldUseNull)) {