-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Logs/Metrics UI] Add deprecated field configuration to Deprecations …
…API (#115103) * [Logs/Metrics UI] Add deprecated field configuration to Deprecations API * Add correction steps * Add unit test for source config deprecations * Apply suggestions from code review Co-authored-by: Chris Cowan <[email protected]> * Lint fix Co-authored-by: Chris Cowan <[email protected]>
- Loading branch information
1 parent
2aaa515
commit adbb808
Showing
3 changed files
with
304 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/* | ||
* 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 { getInfraDeprecationsFactory } from './deprecations'; | ||
|
||
describe('Infra plugin deprecations', () => { | ||
describe('Source configuration deprecations', () => { | ||
test('returns no deprecations when all fields are set to the default values', async () => { | ||
const sources = { | ||
getAllSourceConfigurations: () => [ | ||
{ | ||
configuration: { | ||
name: 'Default', | ||
fields: { | ||
timestamp: '@timestamp', | ||
tiebreaker: '_doc', | ||
container: 'container.id', | ||
host: 'host.name', | ||
pod: 'kubernetes.pod.uid', | ||
}, | ||
}, | ||
}, | ||
{ | ||
configuration: { | ||
name: 'Alternate', | ||
fields: { | ||
timestamp: '@timestamp', | ||
tiebreaker: '_doc', | ||
container: 'container.id', | ||
host: 'host.name', | ||
pod: 'kubernetes.pod.uid', | ||
}, | ||
}, | ||
}, | ||
], | ||
}; | ||
const getDeprecations = getInfraDeprecationsFactory(sources as any); | ||
const deprecations = await getDeprecations({} as any); | ||
expect(deprecations.length).toBe(0); | ||
}); | ||
}); | ||
test('returns expected deprecations when some fields are not set to default values in one or more source configurations', async () => { | ||
const sources = { | ||
getAllSourceConfigurations: () => [ | ||
{ | ||
configuration: { | ||
name: 'Default', | ||
fields: { | ||
timestamp: 'not-@timestamp', | ||
tiebreaker: '_doc', | ||
container: 'not-container.id', | ||
host: 'host.name', | ||
pod: 'not-kubernetes.pod.uid', | ||
}, | ||
}, | ||
}, | ||
{ | ||
configuration: { | ||
name: 'Alternate', | ||
fields: { | ||
timestamp: 'not-@timestamp', | ||
tiebreaker: 'not-_doc', | ||
container: 'container.id', | ||
host: 'not-host.name', | ||
pod: 'kubernetes.pod.uid', | ||
}, | ||
}, | ||
}, | ||
], | ||
}; | ||
const getDeprecations = getInfraDeprecationsFactory(sources as any); | ||
const deprecations = await getDeprecations({} as any); | ||
expect(deprecations.length).toBe(5); | ||
expect( | ||
deprecations.map((d) => | ||
d.title.replace(/Source configuration field "(.*)" is deprecated./, '$1') | ||
) | ||
).toEqual( | ||
expect.arrayContaining(['timestamp', 'tiebreaker', 'container ID', 'host name', 'pod ID']) | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
/* | ||
* 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 { i18n } from '@kbn/i18n'; | ||
import { get } from 'lodash'; | ||
import { | ||
ConfigDeprecationProvider, | ||
ConfigDeprecation, | ||
DeprecationsDetails, | ||
GetDeprecationsContext, | ||
} from 'src/core/server'; | ||
import { InfraSources } from './lib/sources'; | ||
|
||
const deprecatedFieldMessage = (fieldName: string, defaultValue: string, configNames: string[]) => | ||
i18n.translate('xpack.infra.deprecations.deprecatedFieldDescription', { | ||
defaultMessage: | ||
'Configuring the "{fieldName}" field has been deprecated and will be removed in 8.0.0. This plugin is designed to work with ECS, and expects this field to have a value of `{defaultValue}`. It has a different value configured in Source {configCount, plural, one {Configuration} other {Configurations}}: {configNames}', | ||
values: { | ||
fieldName, | ||
defaultValue, | ||
configNames: configNames.join(', '), | ||
configCount: configNames.length, | ||
}, | ||
}); | ||
|
||
const DEFAULT_VALUES = { | ||
timestamp: '@timestamp', | ||
tiebreaker: '_doc', | ||
container: 'container.id', | ||
host: 'host.name', | ||
pod: 'kubernetes.pod.uid', | ||
}; | ||
|
||
const FIELD_DEPRECATION_FACTORIES: Record<string, (configNames: string[]) => DeprecationsDetails> = | ||
{ | ||
timestamp: (configNames) => ({ | ||
level: 'critical', | ||
title: i18n.translate('xpack.infra.deprecations.timestampFieldTitle', { | ||
defaultMessage: 'Source configuration field "timestamp" is deprecated.', | ||
}), | ||
message: deprecatedFieldMessage( | ||
i18n.translate('xpack.infra.deprecations.timestampFieldName', { | ||
defaultMessage: 'timestamp', | ||
}), | ||
DEFAULT_VALUES.timestamp, | ||
configNames | ||
), | ||
correctiveActions: { | ||
manualSteps: [ | ||
i18n.translate('xpack.infra.deprecations.timestampAdjustIndexing', { | ||
defaultMessage: 'Adjust your indexing to use "{field}" as a timestamp.', | ||
values: { field: '@timestamp' }, | ||
}), | ||
], | ||
}, | ||
}), | ||
tiebreaker: (configNames) => ({ | ||
level: 'critical', | ||
title: i18n.translate('xpack.infra.deprecations.tiebreakerFieldTitle', { | ||
defaultMessage: 'Source configuration field "tiebreaker" is deprecated.', | ||
}), | ||
message: deprecatedFieldMessage( | ||
i18n.translate('xpack.infra.deprecations.tiebreakerFieldName', { | ||
defaultMessage: 'tiebreaker', | ||
}), | ||
DEFAULT_VALUES.tiebreaker, | ||
configNames | ||
), | ||
correctiveActions: { | ||
manualSteps: [ | ||
i18n.translate('xpack.infra.deprecations.tiebreakerAdjustIndexing', { | ||
defaultMessage: 'Adjust your indexing to use "{field}" as a tiebreaker.', | ||
values: { field: '_doc' }, | ||
}), | ||
], | ||
}, | ||
}), | ||
host: (configNames) => ({ | ||
level: 'critical', | ||
title: i18n.translate('xpack.infra.deprecations.hostnameFieldTitle', { | ||
defaultMessage: 'Source configuration field "host name" is deprecated.', | ||
}), | ||
message: deprecatedFieldMessage( | ||
i18n.translate('xpack.infra.deprecations.hostnameFieldName', { | ||
defaultMessage: 'host name', | ||
}), | ||
DEFAULT_VALUES.host, | ||
configNames | ||
), | ||
correctiveActions: { | ||
manualSteps: [ | ||
i18n.translate('xpack.infra.deprecations.hostAdjustIndexing', { | ||
defaultMessage: 'Adjust your indexing to identify hosts using "{field}"', | ||
values: { field: 'host.name' }, | ||
}), | ||
], | ||
}, | ||
}), | ||
pod: (configNames) => ({ | ||
level: 'critical', | ||
title: i18n.translate('xpack.infra.deprecations.podIdFieldTitle', { | ||
defaultMessage: 'Source configuration field "pod ID" is deprecated.', | ||
}), | ||
message: deprecatedFieldMessage( | ||
i18n.translate('xpack.infra.deprecations.podIdFieldName', { defaultMessage: 'pod ID' }), | ||
DEFAULT_VALUES.pod, | ||
configNames | ||
), | ||
correctiveActions: { | ||
manualSteps: [ | ||
i18n.translate('xpack.infra.deprecations.podAdjustIndexing', { | ||
defaultMessage: 'Adjust your indexing to identify Kubernetes pods using "{field}"', | ||
values: { field: 'kubernetes.pod.uid' }, | ||
}), | ||
], | ||
}, | ||
}), | ||
container: (configNames) => ({ | ||
level: 'critical', | ||
title: i18n.translate('xpack.infra.deprecations.containerIdFieldTitle', { | ||
defaultMessage: 'Source configuration field "container ID" is deprecated.', | ||
}), | ||
message: deprecatedFieldMessage( | ||
i18n.translate('xpack.infra.deprecations.containerIdFieldName', { | ||
defaultMessage: 'container ID', | ||
}), | ||
DEFAULT_VALUES.container, | ||
configNames | ||
), | ||
correctiveActions: { | ||
manualSteps: [ | ||
i18n.translate('xpack.infra.deprecations.containerAdjustIndexing', { | ||
defaultMessage: 'Adjust your indexing to identify Docker containers using "{field}"', | ||
values: { field: 'container.id' }, | ||
}), | ||
], | ||
}, | ||
}), | ||
}; | ||
|
||
export const configDeprecations: ConfigDeprecationProvider = () => [ | ||
...Object.keys(FIELD_DEPRECATION_FACTORIES).map( | ||
(key): ConfigDeprecation => | ||
(completeConfig, rootPath, addDeprecation) => { | ||
const configuredValue = get(completeConfig, `xpack.infra.sources.default.fields.${key}`); | ||
if (typeof configuredValue === 'undefined') { | ||
return completeConfig; | ||
} | ||
addDeprecation({ | ||
title: i18n.translate('xpack.infra.deprecations.deprecatedFieldConfigTitle', { | ||
defaultMessage: '"{fieldKey}" is deprecated.', | ||
values: { | ||
fieldKey: key, | ||
}, | ||
}), | ||
message: i18n.translate('xpack.infra.deprecations.deprecatedFieldConfigDescription', { | ||
defaultMessage: | ||
'Configuring "xpack.infra.sources.default.fields.{fieldKey}" has been deprecated and will be removed in 8.0.0.', | ||
values: { | ||
fieldKey: key, | ||
}, | ||
}), | ||
level: 'warning', | ||
correctiveActions: { | ||
manualSteps: [ | ||
i18n.translate('xpack.infra.deprecations.removeConfigField', { | ||
defaultMessage: | ||
'Remove "xpack.infra.sources.default.fields.{fieldKey}" from your Kibana configuration.', | ||
values: { fieldKey: key }, | ||
}), | ||
], | ||
}, | ||
} as Parameters<typeof addDeprecation>[0]); | ||
|
||
return completeConfig; | ||
} | ||
), | ||
]; | ||
|
||
export const getInfraDeprecationsFactory = | ||
(sources: InfraSources) => | ||
async ({ savedObjectsClient }: GetDeprecationsContext) => { | ||
const deprecatedFieldsToSourceConfigMap: Map<string, string[]> = new Map(); | ||
const sourceConfigurations = await sources.getAllSourceConfigurations(savedObjectsClient); | ||
|
||
for (const { configuration } of sourceConfigurations) { | ||
const { name, fields } = configuration; | ||
for (const [key, defaultValue] of Object.entries(DEFAULT_VALUES)) { | ||
const configuredValue = Reflect.get(fields, key); | ||
if (configuredValue !== defaultValue) { | ||
const affectedConfigNames = deprecatedFieldsToSourceConfigMap.get(key) ?? []; | ||
affectedConfigNames.push(name); | ||
deprecatedFieldsToSourceConfigMap.set(key, affectedConfigNames); | ||
} | ||
} | ||
} | ||
|
||
const deprecations: DeprecationsDetails[] = []; | ||
if (deprecatedFieldsToSourceConfigMap.size > 0) { | ||
for (const [fieldName, affectedConfigNames] of deprecatedFieldsToSourceConfigMap.entries()) { | ||
const deprecationFactory = Reflect.get(FIELD_DEPRECATION_FACTORIES, fieldName); | ||
deprecations.push(deprecationFactory(affectedConfigNames)); | ||
} | ||
} | ||
|
||
return deprecations; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters