mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Add CI check to ensure SO mapping addition are done correctly (#172056)
## Summary Fix https://github.com/elastic/kibana/issues/172055 Add a CI check verifying that any mapping addition (done after a type's initial introduction) correctly defines the added mappings as a `mappings_addition` change in a model version of the owning type (or throws otherwise) Similar to https://github.com/elastic/kibana/pull/169610 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
c96b63c5a2
commit
62d0ce4c7c
29 changed files with 1892 additions and 82 deletions
|
@ -42,7 +42,14 @@ export type {
|
|||
MigrationStatus,
|
||||
MigrateDocumentOptions,
|
||||
} from './src/migration';
|
||||
export { parseObjectKey, getObjectKey, getIndexForType } from './src/utils';
|
||||
export {
|
||||
parseObjectKey,
|
||||
getObjectKey,
|
||||
getIndexForType,
|
||||
getFieldListFromTypeMapping,
|
||||
getFieldListMapFromMappingDefinitions,
|
||||
type FieldListMap,
|
||||
} from './src/utils';
|
||||
export {
|
||||
modelVersionVirtualMajor,
|
||||
globalSwitchToModelVersionAt,
|
||||
|
@ -68,4 +75,6 @@ export {
|
|||
buildModelVersionTransformFn,
|
||||
aggregateMappingAdditions,
|
||||
convertModelVersionBackwardConversionSchema,
|
||||
getVersionAddedMappings,
|
||||
getVersionAddedFields,
|
||||
} from './src/model_version';
|
||||
|
|
|
@ -37,3 +37,4 @@ export { getModelVersionDelta } from './get_version_delta';
|
|||
export { buildModelVersionTransformFn } from './build_transform_fn';
|
||||
export { aggregateMappingAdditions } from './aggregate_model_changes';
|
||||
export { convertModelVersionBackwardConversionSchema } from './backward_conversion_schema';
|
||||
export { getVersionAddedFields, getVersionAddedMappings } from './version_mapping_changes';
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type {
|
||||
SavedObjectsModelVersion,
|
||||
SavedObjectsModelChange,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
import { getVersionAddedMappings, getVersionAddedFields } from './version_mapping_changes';
|
||||
|
||||
const createVersion = (changes: SavedObjectsModelChange[]): SavedObjectsModelVersion => {
|
||||
return {
|
||||
changes,
|
||||
};
|
||||
};
|
||||
|
||||
describe('getVersionAddedMappings', () => {
|
||||
it('returns empty mappings when the version has no changes', () => {
|
||||
const version = createVersion([]);
|
||||
expect(getVersionAddedMappings(version)).toEqual({});
|
||||
});
|
||||
|
||||
it('returns empty mappings when the version has no `mappings_addition` changes', () => {
|
||||
const version = createVersion([
|
||||
{
|
||||
type: 'data_backfill',
|
||||
backfillFn: jest.fn(),
|
||||
},
|
||||
]);
|
||||
expect(getVersionAddedMappings(version)).toEqual({});
|
||||
});
|
||||
|
||||
it(`returns the change's mappings when the version has a single 'mappings_addition' changes`, () => {
|
||||
const version = createVersion([
|
||||
{
|
||||
type: 'data_backfill',
|
||||
backfillFn: jest.fn(),
|
||||
},
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {
|
||||
nested: {
|
||||
properties: {
|
||||
foo: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(getVersionAddedMappings(version)).toEqual({
|
||||
nested: {
|
||||
properties: {
|
||||
foo: { type: 'text' },
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`merges the mappings when the version has multiple 'mappings_addition' changes`, () => {
|
||||
const version = createVersion([
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {
|
||||
top: { type: 'text' },
|
||||
nested: {
|
||||
properties: {
|
||||
bar: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'data_backfill',
|
||||
backfillFn: jest.fn(),
|
||||
},
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {
|
||||
nested: {
|
||||
properties: {
|
||||
foo: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(getVersionAddedMappings(version)).toEqual({
|
||||
top: { type: 'text' },
|
||||
nested: {
|
||||
properties: {
|
||||
foo: { type: 'text' },
|
||||
bar: { type: 'text' },
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getVersionAddedFields', () => {
|
||||
it('returns empty mappings when the version has no changes', () => {
|
||||
const version = createVersion([]);
|
||||
expect(getVersionAddedFields(version)).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns empty mappings when the version has no `mappings_addition` changes', () => {
|
||||
const version = createVersion([
|
||||
{
|
||||
type: 'data_backfill',
|
||||
backfillFn: jest.fn(),
|
||||
},
|
||||
]);
|
||||
expect(getVersionAddedFields(version)).toEqual([]);
|
||||
});
|
||||
|
||||
it(`returns the change's mappings when the version has a single 'mappings_addition' changes`, () => {
|
||||
const version = createVersion([
|
||||
{
|
||||
type: 'data_backfill',
|
||||
backfillFn: jest.fn(),
|
||||
},
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {
|
||||
nested: {
|
||||
properties: {
|
||||
foo: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(getVersionAddedFields(version)).toEqual(['nested', 'nested.foo']);
|
||||
});
|
||||
|
||||
it(`merges the mappings when the version has multiple 'mappings_addition' changes`, () => {
|
||||
const version = createVersion([
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {
|
||||
top: { type: 'text' },
|
||||
nested: {
|
||||
properties: {
|
||||
bar: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'data_backfill',
|
||||
backfillFn: jest.fn(),
|
||||
},
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {
|
||||
nested: {
|
||||
properties: {
|
||||
foo: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(getVersionAddedFields(version)).toEqual(['nested', 'nested.bar', 'nested.foo', 'top']);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { merge } from 'lodash';
|
||||
import type {
|
||||
SavedObjectsMappingProperties,
|
||||
SavedObjectsModelVersion,
|
||||
SavedObjectsModelMappingsAdditionChange,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
import { getFieldListFromTypeMapping } from '../utils/get_field_list';
|
||||
|
||||
/**
|
||||
* Return the mappings that were introduced in the given version.
|
||||
* If multiple 'mappings_addition' changes are present for the version,
|
||||
* they will be deep-merged.
|
||||
*/
|
||||
export const getVersionAddedMappings = (
|
||||
version: SavedObjectsModelVersion
|
||||
): SavedObjectsMappingProperties => {
|
||||
const mappingChanges = version.changes.filter(
|
||||
(change) => change.type === 'mappings_addition'
|
||||
) as SavedObjectsModelMappingsAdditionChange[];
|
||||
return merge({}, ...mappingChanges.map((change) => change.addedMappings));
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the list of fields, sorted, that were introduced in the given version.
|
||||
*/
|
||||
export const getVersionAddedFields = (version: SavedObjectsModelVersion): string[] => {
|
||||
const addedMappings = getVersionAddedMappings(version);
|
||||
return getFieldListFromTypeMapping({ properties: addedMappings });
|
||||
};
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { SavedObjectsTypeMappingDefinition } from '@kbn/core-saved-objects-server';
|
||||
import { getFieldListFromTypeMapping } from './get_field_list';
|
||||
|
||||
describe('getFieldListFromTypeMapping', () => {
|
||||
it('returns an empty list for empty mappings', () => {
|
||||
const mappings: SavedObjectsTypeMappingDefinition = {
|
||||
properties: {},
|
||||
};
|
||||
expect(getFieldListFromTypeMapping(mappings)).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns the correct list for top level fields', () => {
|
||||
const mappings: SavedObjectsTypeMappingDefinition = {
|
||||
properties: {
|
||||
foo: { type: 'text' },
|
||||
bar: { type: 'text' },
|
||||
},
|
||||
};
|
||||
expect(getFieldListFromTypeMapping(mappings)).toEqual(['bar', 'foo']);
|
||||
});
|
||||
|
||||
it('returns the correct list for deep fields', () => {
|
||||
const mappings: SavedObjectsTypeMappingDefinition = {
|
||||
properties: {
|
||||
foo: {
|
||||
properties: {
|
||||
hello: { type: 'text' },
|
||||
dolly: { type: 'text' },
|
||||
},
|
||||
},
|
||||
bar: { type: 'text' },
|
||||
},
|
||||
};
|
||||
expect(getFieldListFromTypeMapping(mappings)).toEqual(['bar', 'foo', 'foo.dolly', 'foo.hello']);
|
||||
});
|
||||
|
||||
it('returns the correct list for any depth', () => {
|
||||
const mappings: SavedObjectsTypeMappingDefinition = {
|
||||
properties: {
|
||||
foo: {
|
||||
properties: {
|
||||
hello: { type: 'text' },
|
||||
dolly: {
|
||||
properties: {
|
||||
far: { type: 'text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
bar: { type: 'text' },
|
||||
},
|
||||
};
|
||||
expect(getFieldListFromTypeMapping(mappings)).toEqual([
|
||||
'bar',
|
||||
'foo',
|
||||
'foo.dolly',
|
||||
'foo.dolly.far',
|
||||
'foo.hello',
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { MappingProperty as EsMappingProperty } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import type {
|
||||
SavedObjectsTypeMappingDefinition,
|
||||
SavedObjectsFieldMapping,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
import type { SavedObjectsTypeMappingDefinitions } from '../mappings';
|
||||
|
||||
export type FieldListMap = Record<string, string[]>;
|
||||
|
||||
/**
|
||||
* Return the list of fields present in each individual type mappings present in the definition.
|
||||
*/
|
||||
export const getFieldListMapFromMappingDefinitions = (
|
||||
mappings: SavedObjectsTypeMappingDefinitions
|
||||
): FieldListMap => {
|
||||
return Object.entries(mappings).reduce<FieldListMap>((memo, [typeName, typeMappings]) => {
|
||||
memo[typeName] = getFieldListFromTypeMapping(typeMappings);
|
||||
return memo;
|
||||
}, {});
|
||||
};
|
||||
|
||||
type AnyFieldMapping = SavedObjectsFieldMapping | EsMappingProperty;
|
||||
|
||||
interface QueueItem {
|
||||
fieldPath: string[];
|
||||
fieldDef: AnyFieldMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of fields present in the provided mappings.
|
||||
* Note that fields only containing properties are still considered fields by this function.
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* getFieldListFromTypeMapping({
|
||||
* properties: {
|
||||
* foo: {
|
||||
* properties: {
|
||||
* hello: { type: 'text' },
|
||||
* dolly: { type: 'text' },
|
||||
* },
|
||||
* },
|
||||
* },
|
||||
* });
|
||||
* // ['foo', 'foo.dolly', 'foo.hello']
|
||||
* ```
|
||||
*/
|
||||
export const getFieldListFromTypeMapping = (
|
||||
typeMappings: SavedObjectsTypeMappingDefinition
|
||||
): string[] => {
|
||||
const fieldList: string[] = [];
|
||||
const queue: QueueItem[] = [];
|
||||
|
||||
Object.entries(typeMappings.properties).forEach(([fieldName, fieldDef]) => {
|
||||
queue.push({
|
||||
fieldPath: [fieldName],
|
||||
fieldDef,
|
||||
});
|
||||
});
|
||||
|
||||
while (queue.length > 0) {
|
||||
const item = queue.pop()!;
|
||||
fieldList.push(item.fieldPath.join('.'));
|
||||
if ('properties' in item.fieldDef) {
|
||||
Object.entries(item.fieldDef.properties ?? {}).forEach(([fieldName, fieldDef]) => {
|
||||
queue.push({
|
||||
fieldPath: [...item.fieldPath, fieldName],
|
||||
fieldDef,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return fieldList.sort();
|
||||
};
|
|
@ -8,3 +8,8 @@
|
|||
|
||||
export { getObjectKey, parseObjectKey } from './object_key';
|
||||
export { getIndexForType } from './get_index_for_type';
|
||||
export {
|
||||
getFieldListFromTypeMapping,
|
||||
getFieldListMapFromMappingDefinitions,
|
||||
type FieldListMap,
|
||||
} from './get_field_list';
|
||||
|
|
970
packages/kbn-check-mappings-update-cli/current_fields.json
Normal file
970
packages/kbn-check-mappings-update-cli/current_fields.json
Normal file
|
@ -0,0 +1,970 @@
|
|||
{
|
||||
"core-usage-stats": [],
|
||||
"legacy-url-alias": [
|
||||
"disabled",
|
||||
"resolveCounter",
|
||||
"sourceId",
|
||||
"targetId",
|
||||
"targetNamespace",
|
||||
"targetType"
|
||||
],
|
||||
"config": [
|
||||
"buildNum"
|
||||
],
|
||||
"config-global": [
|
||||
"buildNum"
|
||||
],
|
||||
"url": [
|
||||
"accessDate",
|
||||
"createDate",
|
||||
"slug"
|
||||
],
|
||||
"usage-counters": [
|
||||
"domainId"
|
||||
],
|
||||
"task": [
|
||||
"attempts",
|
||||
"enabled",
|
||||
"ownerId",
|
||||
"retryAt",
|
||||
"runAt",
|
||||
"schedule",
|
||||
"schedule.interval",
|
||||
"scheduledAt",
|
||||
"scope",
|
||||
"status",
|
||||
"taskType"
|
||||
],
|
||||
"guided-onboarding-guide-state": [
|
||||
"guideId",
|
||||
"isActive"
|
||||
],
|
||||
"guided-onboarding-plugin-state": [],
|
||||
"ui-metric": [
|
||||
"count"
|
||||
],
|
||||
"application_usage_totals": [],
|
||||
"application_usage_daily": [
|
||||
"timestamp"
|
||||
],
|
||||
"event_loop_delays_daily": [
|
||||
"lastUpdatedAt"
|
||||
],
|
||||
"index-pattern": [
|
||||
"name",
|
||||
"title",
|
||||
"type"
|
||||
],
|
||||
"sample-data-telemetry": [
|
||||
"installCount",
|
||||
"unInstallCount"
|
||||
],
|
||||
"space": [
|
||||
"name"
|
||||
],
|
||||
"spaces-usage-stats": [],
|
||||
"exception-list-agnostic": [
|
||||
"_tags",
|
||||
"comments",
|
||||
"comments.comment",
|
||||
"comments.created_at",
|
||||
"comments.created_by",
|
||||
"comments.id",
|
||||
"comments.updated_at",
|
||||
"comments.updated_by",
|
||||
"created_at",
|
||||
"created_by",
|
||||
"description",
|
||||
"entries",
|
||||
"entries.entries",
|
||||
"entries.entries.field",
|
||||
"entries.entries.operator",
|
||||
"entries.entries.type",
|
||||
"entries.entries.value",
|
||||
"entries.field",
|
||||
"entries.list",
|
||||
"entries.list.id",
|
||||
"entries.list.type",
|
||||
"entries.operator",
|
||||
"entries.type",
|
||||
"entries.value",
|
||||
"expire_time",
|
||||
"immutable",
|
||||
"item_id",
|
||||
"list_id",
|
||||
"list_type",
|
||||
"meta",
|
||||
"name",
|
||||
"os_types",
|
||||
"tags",
|
||||
"tie_breaker_id",
|
||||
"type",
|
||||
"updated_by",
|
||||
"version"
|
||||
],
|
||||
"exception-list": [
|
||||
"_tags",
|
||||
"comments",
|
||||
"comments.comment",
|
||||
"comments.created_at",
|
||||
"comments.created_by",
|
||||
"comments.id",
|
||||
"comments.updated_at",
|
||||
"comments.updated_by",
|
||||
"created_at",
|
||||
"created_by",
|
||||
"description",
|
||||
"entries",
|
||||
"entries.entries",
|
||||
"entries.entries.field",
|
||||
"entries.entries.operator",
|
||||
"entries.entries.type",
|
||||
"entries.entries.value",
|
||||
"entries.field",
|
||||
"entries.list",
|
||||
"entries.list.id",
|
||||
"entries.list.type",
|
||||
"entries.operator",
|
||||
"entries.type",
|
||||
"entries.value",
|
||||
"expire_time",
|
||||
"immutable",
|
||||
"item_id",
|
||||
"list_id",
|
||||
"list_type",
|
||||
"meta",
|
||||
"name",
|
||||
"os_types",
|
||||
"tags",
|
||||
"tie_breaker_id",
|
||||
"type",
|
||||
"updated_by",
|
||||
"version"
|
||||
],
|
||||
"telemetry": [],
|
||||
"file": [
|
||||
"FileKind",
|
||||
"Meta",
|
||||
"Status",
|
||||
"Updated",
|
||||
"created",
|
||||
"extension",
|
||||
"hash",
|
||||
"mime_type",
|
||||
"name",
|
||||
"size",
|
||||
"user"
|
||||
],
|
||||
"fileShare": [
|
||||
"created",
|
||||
"name",
|
||||
"token",
|
||||
"valid_until"
|
||||
],
|
||||
"action": [
|
||||
"actionTypeId",
|
||||
"name"
|
||||
],
|
||||
"action_task_params": [],
|
||||
"connector_token": [
|
||||
"connectorId",
|
||||
"tokenType"
|
||||
],
|
||||
"query": [
|
||||
"description",
|
||||
"title"
|
||||
],
|
||||
"kql-telemetry": [],
|
||||
"search-session": [
|
||||
"created",
|
||||
"realmName",
|
||||
"realmType",
|
||||
"sessionId",
|
||||
"username"
|
||||
],
|
||||
"search-telemetry": [],
|
||||
"file-upload-usage-collection-telemetry": [
|
||||
"file_upload",
|
||||
"file_upload.index_creation_count"
|
||||
],
|
||||
"apm-indices": [],
|
||||
"tag": [
|
||||
"color",
|
||||
"description",
|
||||
"name"
|
||||
],
|
||||
"alert": [
|
||||
"actions",
|
||||
"actions.actionRef",
|
||||
"actions.actionTypeId",
|
||||
"actions.group",
|
||||
"alertTypeId",
|
||||
"consumer",
|
||||
"createdAt",
|
||||
"createdBy",
|
||||
"enabled",
|
||||
"executionStatus",
|
||||
"executionStatus.error",
|
||||
"executionStatus.error.message",
|
||||
"executionStatus.error.reason",
|
||||
"executionStatus.lastDuration",
|
||||
"executionStatus.lastExecutionDate",
|
||||
"executionStatus.numberOfTriggeredActions",
|
||||
"executionStatus.status",
|
||||
"executionStatus.warning",
|
||||
"executionStatus.warning.message",
|
||||
"executionStatus.warning.reason",
|
||||
"lastRun",
|
||||
"lastRun.alertsCount",
|
||||
"lastRun.alertsCount.active",
|
||||
"lastRun.alertsCount.ignored",
|
||||
"lastRun.alertsCount.new",
|
||||
"lastRun.alertsCount.recovered",
|
||||
"lastRun.outcome",
|
||||
"lastRun.outcomeOrder",
|
||||
"legacyId",
|
||||
"mapped_params",
|
||||
"mapped_params.risk_score",
|
||||
"mapped_params.severity",
|
||||
"monitoring",
|
||||
"monitoring.run",
|
||||
"monitoring.run.calculated_metrics",
|
||||
"monitoring.run.calculated_metrics.p50",
|
||||
"monitoring.run.calculated_metrics.p95",
|
||||
"monitoring.run.calculated_metrics.p99",
|
||||
"monitoring.run.calculated_metrics.success_ratio",
|
||||
"monitoring.run.last_run",
|
||||
"monitoring.run.last_run.metrics",
|
||||
"monitoring.run.last_run.metrics.duration",
|
||||
"monitoring.run.last_run.metrics.gap_duration_s",
|
||||
"monitoring.run.last_run.metrics.total_alerts_created",
|
||||
"monitoring.run.last_run.metrics.total_alerts_detected",
|
||||
"monitoring.run.last_run.metrics.total_indexing_duration_ms",
|
||||
"monitoring.run.last_run.metrics.total_search_duration_ms",
|
||||
"monitoring.run.last_run.timestamp",
|
||||
"muteAll",
|
||||
"mutedInstanceIds",
|
||||
"name",
|
||||
"notifyWhen",
|
||||
"params",
|
||||
"revision",
|
||||
"running",
|
||||
"schedule",
|
||||
"schedule.interval",
|
||||
"scheduledTaskId",
|
||||
"snoozeSchedule",
|
||||
"snoozeSchedule.duration",
|
||||
"snoozeSchedule.id",
|
||||
"snoozeSchedule.skipRecurrences",
|
||||
"tags",
|
||||
"throttle",
|
||||
"updatedAt",
|
||||
"updatedBy"
|
||||
],
|
||||
"api_key_pending_invalidation": [
|
||||
"apiKeyId",
|
||||
"createdAt"
|
||||
],
|
||||
"rules-settings": [
|
||||
"flapping"
|
||||
],
|
||||
"maintenance-window": [
|
||||
"enabled",
|
||||
"events"
|
||||
],
|
||||
"graph-workspace": [
|
||||
"description",
|
||||
"kibanaSavedObjectMeta",
|
||||
"kibanaSavedObjectMeta.searchSourceJSON",
|
||||
"legacyIndexPatternRef",
|
||||
"numLinks",
|
||||
"numVertices",
|
||||
"title",
|
||||
"version",
|
||||
"wsState"
|
||||
],
|
||||
"search": [
|
||||
"description",
|
||||
"title"
|
||||
],
|
||||
"visualization": [
|
||||
"description",
|
||||
"kibanaSavedObjectMeta",
|
||||
"title",
|
||||
"version"
|
||||
],
|
||||
"canvas-element": [
|
||||
"@created",
|
||||
"@timestamp",
|
||||
"content",
|
||||
"help",
|
||||
"image",
|
||||
"name"
|
||||
],
|
||||
"canvas-workpad": [
|
||||
"@created",
|
||||
"@timestamp",
|
||||
"name"
|
||||
],
|
||||
"canvas-workpad-template": [
|
||||
"help",
|
||||
"name",
|
||||
"tags",
|
||||
"template_key"
|
||||
],
|
||||
"event-annotation-group": [
|
||||
"description",
|
||||
"title"
|
||||
],
|
||||
"dashboard": [
|
||||
"controlGroupInput",
|
||||
"controlGroupInput.chainingSystem",
|
||||
"controlGroupInput.controlStyle",
|
||||
"controlGroupInput.ignoreParentSettingsJSON",
|
||||
"controlGroupInput.panelsJSON",
|
||||
"description",
|
||||
"hits",
|
||||
"kibanaSavedObjectMeta",
|
||||
"kibanaSavedObjectMeta.searchSourceJSON",
|
||||
"optionsJSON",
|
||||
"panelsJSON",
|
||||
"refreshInterval",
|
||||
"refreshInterval.display",
|
||||
"refreshInterval.pause",
|
||||
"refreshInterval.section",
|
||||
"refreshInterval.value",
|
||||
"timeFrom",
|
||||
"timeRestore",
|
||||
"timeTo",
|
||||
"title",
|
||||
"version"
|
||||
],
|
||||
"links": [
|
||||
"description",
|
||||
"links",
|
||||
"title"
|
||||
],
|
||||
"lens": [
|
||||
"description",
|
||||
"state",
|
||||
"title",
|
||||
"visualizationType"
|
||||
],
|
||||
"lens-ui-telemetry": [
|
||||
"count",
|
||||
"date",
|
||||
"name",
|
||||
"type"
|
||||
],
|
||||
"map": [
|
||||
"bounds",
|
||||
"description",
|
||||
"layerListJSON",
|
||||
"mapStateJSON",
|
||||
"title",
|
||||
"uiStateJSON",
|
||||
"version"
|
||||
],
|
||||
"cases-comments": [
|
||||
"actions",
|
||||
"actions.type",
|
||||
"alertId",
|
||||
"comment",
|
||||
"created_at",
|
||||
"created_by",
|
||||
"created_by.username",
|
||||
"externalReferenceAttachmentTypeId",
|
||||
"owner",
|
||||
"persistableStateAttachmentTypeId",
|
||||
"pushed_at",
|
||||
"type",
|
||||
"updated_at"
|
||||
],
|
||||
"cases-configure": [
|
||||
"closure_type",
|
||||
"created_at",
|
||||
"owner"
|
||||
],
|
||||
"cases-connector-mappings": [
|
||||
"owner"
|
||||
],
|
||||
"cases": [
|
||||
"assignees",
|
||||
"assignees.uid",
|
||||
"category",
|
||||
"closed_at",
|
||||
"closed_by",
|
||||
"closed_by.email",
|
||||
"closed_by.full_name",
|
||||
"closed_by.profile_uid",
|
||||
"closed_by.username",
|
||||
"connector",
|
||||
"connector.fields",
|
||||
"connector.fields.key",
|
||||
"connector.fields.value",
|
||||
"connector.name",
|
||||
"connector.type",
|
||||
"created_at",
|
||||
"created_by",
|
||||
"created_by.email",
|
||||
"created_by.full_name",
|
||||
"created_by.profile_uid",
|
||||
"created_by.username",
|
||||
"customFields",
|
||||
"customFields.key",
|
||||
"customFields.type",
|
||||
"customFields.value",
|
||||
"description",
|
||||
"duration",
|
||||
"external_service",
|
||||
"external_service.connector_name",
|
||||
"external_service.external_id",
|
||||
"external_service.external_title",
|
||||
"external_service.external_url",
|
||||
"external_service.pushed_at",
|
||||
"external_service.pushed_by",
|
||||
"external_service.pushed_by.email",
|
||||
"external_service.pushed_by.full_name",
|
||||
"external_service.pushed_by.profile_uid",
|
||||
"external_service.pushed_by.username",
|
||||
"owner",
|
||||
"settings",
|
||||
"settings.syncAlerts",
|
||||
"severity",
|
||||
"status",
|
||||
"tags",
|
||||
"title",
|
||||
"total_alerts",
|
||||
"total_comments",
|
||||
"updated_at",
|
||||
"updated_by",
|
||||
"updated_by.email",
|
||||
"updated_by.full_name",
|
||||
"updated_by.profile_uid",
|
||||
"updated_by.username"
|
||||
],
|
||||
"cases-user-actions": [
|
||||
"action",
|
||||
"created_at",
|
||||
"created_by",
|
||||
"created_by.username",
|
||||
"owner",
|
||||
"payload",
|
||||
"payload.assignees",
|
||||
"payload.assignees.uid",
|
||||
"payload.comment",
|
||||
"payload.comment.externalReferenceAttachmentTypeId",
|
||||
"payload.comment.persistableStateAttachmentTypeId",
|
||||
"payload.comment.type",
|
||||
"payload.connector",
|
||||
"payload.connector.type",
|
||||
"type"
|
||||
],
|
||||
"cases-telemetry": [],
|
||||
"infrastructure-monitoring-log-view": [
|
||||
"name"
|
||||
],
|
||||
"metrics-data-source": [],
|
||||
"ingest_manager_settings": [
|
||||
"fleet_server_hosts",
|
||||
"has_seen_add_data_notice",
|
||||
"prerelease_integrations_enabled",
|
||||
"secret_storage_requirements_met"
|
||||
],
|
||||
"ingest-agent-policies": [
|
||||
"agent_features",
|
||||
"agent_features.enabled",
|
||||
"agent_features.name",
|
||||
"data_output_id",
|
||||
"description",
|
||||
"download_source_id",
|
||||
"fleet_server_host_id",
|
||||
"inactivity_timeout",
|
||||
"is_default",
|
||||
"is_default_fleet_server",
|
||||
"is_managed",
|
||||
"is_preconfigured",
|
||||
"is_protected",
|
||||
"keep_monitoring_alive",
|
||||
"monitoring_enabled",
|
||||
"monitoring_output_id",
|
||||
"name",
|
||||
"namespace",
|
||||
"overrides",
|
||||
"revision",
|
||||
"schema_version",
|
||||
"status",
|
||||
"unenroll_timeout",
|
||||
"updated_at",
|
||||
"updated_by"
|
||||
],
|
||||
"ingest-outputs": [
|
||||
"allow_edit",
|
||||
"auth_type",
|
||||
"broker_ack_reliability",
|
||||
"broker_buffer_size",
|
||||
"broker_timeout",
|
||||
"ca_sha256",
|
||||
"ca_trusted_fingerprint",
|
||||
"channel_buffer_size",
|
||||
"client_id",
|
||||
"compression",
|
||||
"compression_level",
|
||||
"config",
|
||||
"config_yaml",
|
||||
"connection_type",
|
||||
"hash",
|
||||
"hash.hash",
|
||||
"hash.random",
|
||||
"headers",
|
||||
"headers.key",
|
||||
"headers.value",
|
||||
"hosts",
|
||||
"is_default",
|
||||
"is_default_monitoring",
|
||||
"is_preconfigured",
|
||||
"key",
|
||||
"name",
|
||||
"output_id",
|
||||
"partition",
|
||||
"password",
|
||||
"proxy_id",
|
||||
"random",
|
||||
"random.group_events",
|
||||
"required_acks",
|
||||
"round_robin",
|
||||
"round_robin.group_events",
|
||||
"sasl",
|
||||
"sasl.mechanism",
|
||||
"secrets",
|
||||
"secrets.password",
|
||||
"secrets.password.id",
|
||||
"secrets.service_token",
|
||||
"secrets.service_token.id",
|
||||
"secrets.ssl",
|
||||
"secrets.ssl.key",
|
||||
"secrets.ssl.key.id",
|
||||
"service_token",
|
||||
"shipper",
|
||||
"ssl",
|
||||
"timeout",
|
||||
"topics",
|
||||
"topics.topic",
|
||||
"topics.when",
|
||||
"topics.when.condition",
|
||||
"topics.when.type",
|
||||
"type",
|
||||
"username",
|
||||
"version"
|
||||
],
|
||||
"ingest-package-policies": [
|
||||
"created_at",
|
||||
"created_by",
|
||||
"description",
|
||||
"elasticsearch",
|
||||
"enabled",
|
||||
"inputs",
|
||||
"is_managed",
|
||||
"name",
|
||||
"namespace",
|
||||
"package",
|
||||
"package.name",
|
||||
"package.title",
|
||||
"package.version",
|
||||
"policy_id",
|
||||
"revision",
|
||||
"secret_references",
|
||||
"secret_references.id",
|
||||
"updated_at",
|
||||
"updated_by",
|
||||
"vars"
|
||||
],
|
||||
"epm-packages": [
|
||||
"es_index_patterns",
|
||||
"experimental_data_stream_features",
|
||||
"experimental_data_stream_features.data_stream",
|
||||
"experimental_data_stream_features.features",
|
||||
"experimental_data_stream_features.features.synthetic_source",
|
||||
"experimental_data_stream_features.features.tsdb",
|
||||
"install_format_schema_version",
|
||||
"install_source",
|
||||
"install_started_at",
|
||||
"install_status",
|
||||
"install_version",
|
||||
"installed_es",
|
||||
"installed_es.deferred",
|
||||
"installed_es.id",
|
||||
"installed_es.type",
|
||||
"installed_es.version",
|
||||
"installed_kibana",
|
||||
"installed_kibana_space_id",
|
||||
"internal",
|
||||
"keep_policies_up_to_date",
|
||||
"latest_install_failed_attempts",
|
||||
"name",
|
||||
"package_assets",
|
||||
"verification_key_id",
|
||||
"verification_status",
|
||||
"version"
|
||||
],
|
||||
"epm-packages-assets": [
|
||||
"asset_path",
|
||||
"data_base64",
|
||||
"data_utf8",
|
||||
"install_source",
|
||||
"media_type",
|
||||
"package_name",
|
||||
"package_version"
|
||||
],
|
||||
"fleet-preconfiguration-deletion-record": [
|
||||
"id"
|
||||
],
|
||||
"ingest-download-sources": [
|
||||
"host",
|
||||
"is_default",
|
||||
"name",
|
||||
"proxy_id",
|
||||
"source_id"
|
||||
],
|
||||
"fleet-fleet-server-host": [
|
||||
"host_urls",
|
||||
"is_default",
|
||||
"is_preconfigured",
|
||||
"name",
|
||||
"proxy_id"
|
||||
],
|
||||
"fleet-proxy": [
|
||||
"certificate",
|
||||
"certificate_authorities",
|
||||
"certificate_key",
|
||||
"is_preconfigured",
|
||||
"name",
|
||||
"proxy_headers",
|
||||
"url"
|
||||
],
|
||||
"fleet-message-signing-keys": [],
|
||||
"fleet-uninstall-tokens": [
|
||||
"policy_id",
|
||||
"token_plain"
|
||||
],
|
||||
"osquery-manager-usage-metric": [
|
||||
"count",
|
||||
"errors"
|
||||
],
|
||||
"osquery-saved-query": [
|
||||
"created_at",
|
||||
"created_by",
|
||||
"description",
|
||||
"ecs_mapping",
|
||||
"id",
|
||||
"interval",
|
||||
"platform",
|
||||
"query",
|
||||
"timeout",
|
||||
"updated_at",
|
||||
"updated_by",
|
||||
"version"
|
||||
],
|
||||
"osquery-pack": [
|
||||
"created_at",
|
||||
"created_by",
|
||||
"description",
|
||||
"enabled",
|
||||
"name",
|
||||
"queries",
|
||||
"queries.ecs_mapping",
|
||||
"queries.id",
|
||||
"queries.interval",
|
||||
"queries.platform",
|
||||
"queries.query",
|
||||
"queries.timeout",
|
||||
"queries.version",
|
||||
"shards",
|
||||
"updated_at",
|
||||
"updated_by",
|
||||
"version"
|
||||
],
|
||||
"osquery-pack-asset": [
|
||||
"description",
|
||||
"name",
|
||||
"queries",
|
||||
"queries.ecs_mapping",
|
||||
"queries.id",
|
||||
"queries.interval",
|
||||
"queries.platform",
|
||||
"queries.query",
|
||||
"queries.timeout",
|
||||
"queries.version",
|
||||
"shards",
|
||||
"version"
|
||||
],
|
||||
"csp-rule-template": [
|
||||
"metadata",
|
||||
"metadata.benchmark",
|
||||
"metadata.benchmark.id",
|
||||
"metadata.benchmark.name",
|
||||
"metadata.benchmark.posture_type",
|
||||
"metadata.benchmark.rule_number",
|
||||
"metadata.benchmark.version",
|
||||
"metadata.id",
|
||||
"metadata.name",
|
||||
"metadata.section",
|
||||
"metadata.version"
|
||||
],
|
||||
"slo": [
|
||||
"budgetingMethod",
|
||||
"description",
|
||||
"enabled",
|
||||
"id",
|
||||
"indicator",
|
||||
"indicator.params",
|
||||
"indicator.type",
|
||||
"name",
|
||||
"tags"
|
||||
],
|
||||
"threshold-explorer-view": [],
|
||||
"observability-onboarding-state": [
|
||||
"progress",
|
||||
"state",
|
||||
"type"
|
||||
],
|
||||
"ml-job": [
|
||||
"datafeed_id",
|
||||
"job_id",
|
||||
"type"
|
||||
],
|
||||
"ml-trained-model": [
|
||||
"job",
|
||||
"job.create_time",
|
||||
"job.job_id",
|
||||
"model_id"
|
||||
],
|
||||
"ml-module": [
|
||||
"datafeeds",
|
||||
"defaultIndexPattern",
|
||||
"description",
|
||||
"id",
|
||||
"jobs",
|
||||
"logo",
|
||||
"query",
|
||||
"tags",
|
||||
"title",
|
||||
"type"
|
||||
],
|
||||
"uptime-dynamic-settings": [],
|
||||
"synthetics-privates-locations": [],
|
||||
"synthetics-monitor": [
|
||||
"alert",
|
||||
"alert.status",
|
||||
"alert.status.enabled",
|
||||
"alert.tls",
|
||||
"alert.tls.enabled",
|
||||
"custom_heartbeat_id",
|
||||
"enabled",
|
||||
"hash",
|
||||
"hosts",
|
||||
"id",
|
||||
"journey_id",
|
||||
"locations",
|
||||
"locations.id",
|
||||
"locations.label",
|
||||
"name",
|
||||
"origin",
|
||||
"project_id",
|
||||
"schedule",
|
||||
"schedule.number",
|
||||
"tags",
|
||||
"throttling",
|
||||
"throttling.label",
|
||||
"type",
|
||||
"urls"
|
||||
],
|
||||
"uptime-synthetics-api-key": [
|
||||
"apiKey"
|
||||
],
|
||||
"synthetics-param": [],
|
||||
"infrastructure-ui-source": [],
|
||||
"inventory-view": [],
|
||||
"metrics-explorer-view": [],
|
||||
"upgrade-assistant-reindex-operation": [
|
||||
"indexName",
|
||||
"status"
|
||||
],
|
||||
"upgrade-assistant-ml-upgrade-operation": [
|
||||
"snapshotId"
|
||||
],
|
||||
"monitoring-telemetry": [
|
||||
"reportedClusterUuids"
|
||||
],
|
||||
"enterprise_search_telemetry": [],
|
||||
"app_search_telemetry": [],
|
||||
"workplace_search_telemetry": [],
|
||||
"siem-ui-timeline-note": [
|
||||
"created",
|
||||
"createdBy",
|
||||
"eventId",
|
||||
"note",
|
||||
"updated",
|
||||
"updatedBy"
|
||||
],
|
||||
"siem-ui-timeline-pinned-event": [
|
||||
"created",
|
||||
"createdBy",
|
||||
"eventId",
|
||||
"updated",
|
||||
"updatedBy"
|
||||
],
|
||||
"siem-detection-engine-rule-actions": [
|
||||
"actions",
|
||||
"actions.actionRef",
|
||||
"actions.action_type_id",
|
||||
"actions.group",
|
||||
"actions.id",
|
||||
"actions.params",
|
||||
"alertThrottle",
|
||||
"ruleAlertId",
|
||||
"ruleThrottle"
|
||||
],
|
||||
"security-rule": [
|
||||
"rule_id",
|
||||
"version"
|
||||
],
|
||||
"siem-ui-timeline": [
|
||||
"columns",
|
||||
"columns.aggregatable",
|
||||
"columns.category",
|
||||
"columns.columnHeaderType",
|
||||
"columns.description",
|
||||
"columns.example",
|
||||
"columns.id",
|
||||
"columns.indexes",
|
||||
"columns.name",
|
||||
"columns.placeholder",
|
||||
"columns.searchable",
|
||||
"columns.type",
|
||||
"created",
|
||||
"createdBy",
|
||||
"dataProviders",
|
||||
"dataProviders.and",
|
||||
"dataProviders.and.enabled",
|
||||
"dataProviders.and.excluded",
|
||||
"dataProviders.and.id",
|
||||
"dataProviders.and.kqlQuery",
|
||||
"dataProviders.and.name",
|
||||
"dataProviders.and.queryMatch",
|
||||
"dataProviders.and.queryMatch.displayField",
|
||||
"dataProviders.and.queryMatch.displayValue",
|
||||
"dataProviders.and.queryMatch.field",
|
||||
"dataProviders.and.queryMatch.operator",
|
||||
"dataProviders.and.queryMatch.value",
|
||||
"dataProviders.and.type",
|
||||
"dataProviders.enabled",
|
||||
"dataProviders.excluded",
|
||||
"dataProviders.id",
|
||||
"dataProviders.kqlQuery",
|
||||
"dataProviders.name",
|
||||
"dataProviders.queryMatch",
|
||||
"dataProviders.queryMatch.displayField",
|
||||
"dataProviders.queryMatch.displayValue",
|
||||
"dataProviders.queryMatch.field",
|
||||
"dataProviders.queryMatch.operator",
|
||||
"dataProviders.queryMatch.value",
|
||||
"dataProviders.type",
|
||||
"dateRange",
|
||||
"dateRange.end",
|
||||
"dateRange.start",
|
||||
"description",
|
||||
"eqlOptions",
|
||||
"eqlOptions.eventCategoryField",
|
||||
"eqlOptions.query",
|
||||
"eqlOptions.size",
|
||||
"eqlOptions.tiebreakerField",
|
||||
"eqlOptions.timestampField",
|
||||
"eventType",
|
||||
"excludedRowRendererIds",
|
||||
"favorite",
|
||||
"favorite.favoriteDate",
|
||||
"favorite.fullName",
|
||||
"favorite.keySearch",
|
||||
"favorite.userName",
|
||||
"filters",
|
||||
"filters.exists",
|
||||
"filters.match_all",
|
||||
"filters.meta",
|
||||
"filters.meta.alias",
|
||||
"filters.meta.controlledBy",
|
||||
"filters.meta.disabled",
|
||||
"filters.meta.field",
|
||||
"filters.meta.formattedValue",
|
||||
"filters.meta.index",
|
||||
"filters.meta.key",
|
||||
"filters.meta.negate",
|
||||
"filters.meta.params",
|
||||
"filters.meta.relation",
|
||||
"filters.meta.type",
|
||||
"filters.meta.value",
|
||||
"filters.missing",
|
||||
"filters.query",
|
||||
"filters.range",
|
||||
"filters.script",
|
||||
"indexNames",
|
||||
"kqlMode",
|
||||
"kqlQuery",
|
||||
"kqlQuery.filterQuery",
|
||||
"kqlQuery.filterQuery.kuery",
|
||||
"kqlQuery.filterQuery.kuery.expression",
|
||||
"kqlQuery.filterQuery.kuery.kind",
|
||||
"kqlQuery.filterQuery.serializedQuery",
|
||||
"savedSearchId",
|
||||
"sort",
|
||||
"sort.columnId",
|
||||
"sort.columnType",
|
||||
"sort.sortDirection",
|
||||
"status",
|
||||
"templateTimelineId",
|
||||
"templateTimelineVersion",
|
||||
"timelineType",
|
||||
"title",
|
||||
"updated",
|
||||
"updatedBy"
|
||||
],
|
||||
"endpoint:user-artifact-manifest": [
|
||||
"artifacts",
|
||||
"schemaVersion"
|
||||
],
|
||||
"security-solution-signals-migration": [
|
||||
"sourceIndex",
|
||||
"updated",
|
||||
"version"
|
||||
],
|
||||
"risk-engine-configuration": [
|
||||
"dataViewId",
|
||||
"enabled",
|
||||
"filter",
|
||||
"identifierType",
|
||||
"interval",
|
||||
"pageSize",
|
||||
"range",
|
||||
"range.end",
|
||||
"range.start"
|
||||
],
|
||||
"policy-settings-protection-updates-note": [
|
||||
"note"
|
||||
],
|
||||
"apm-telemetry": [],
|
||||
"apm-server-schema": [
|
||||
"schemaJson"
|
||||
],
|
||||
"apm-service-group": [
|
||||
"color",
|
||||
"description",
|
||||
"groupName",
|
||||
"kuery"
|
||||
],
|
||||
"apm-custom-dashboards": [
|
||||
"dashboardSavedObjectId",
|
||||
"kuery",
|
||||
"serviceEnvironmentFilterEnabled",
|
||||
"serviceNameFilterEnabled"
|
||||
]
|
||||
}
|
|
@ -11,7 +11,7 @@ import Path from 'path';
|
|||
|
||||
import type { SavedObjectsTypeMappingDefinitions } from '@kbn/core-saved-objects-base-server-internal';
|
||||
|
||||
export const CURRENT_MAPPINGS_FILE = Path.resolve(__dirname, '../current_mappings.json');
|
||||
export const CURRENT_MAPPINGS_FILE = Path.resolve(__dirname, '../../current_mappings.json');
|
||||
|
||||
export async function readCurrentMappings(): Promise<SavedObjectsTypeMappingDefinitions> {
|
||||
let currentMappingsJson;
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import ChildProcess from 'child_process';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
import * as Rx from 'rxjs';
|
||||
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
|
@ -18,19 +17,13 @@ import type { SavedObjectsTypeMappingDefinitions } from '@kbn/core-saved-objects
|
|||
|
||||
import type { Result } from './extract_mappings_from_plugins_worker';
|
||||
|
||||
function routeToLog(readable: Readable, log: SomeDevLog, level: 'debug' | 'error') {
|
||||
return observeLines(readable).pipe(
|
||||
Rx.tap((line) => {
|
||||
log[level](line);
|
||||
}),
|
||||
Rx.ignoreElements()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a worker process that starts the core with all plugins enabled and sends back the
|
||||
* saved object mappings for all plugins. We run this in a child process so that we can
|
||||
* harvest logs and feed them into the logger when debugging.
|
||||
* saved object mappings for all plugins.
|
||||
*
|
||||
* We run this in a child process to make it easier to kill the kibana instance once done
|
||||
* (dodges issues with open handles), and so that we can harvest logs and feed them into
|
||||
* the logger when debugging.
|
||||
*/
|
||||
export async function extractMappingsFromPlugins(
|
||||
log: SomeDevLog
|
||||
|
@ -78,3 +71,12 @@ export async function extractMappingsFromPlugins(
|
|||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
function routeToLog(readable: Readable, log: SomeDevLog, level: 'debug' | 'error') {
|
||||
return observeLines(readable).pipe(
|
||||
Rx.tap((line) => {
|
||||
log[level](line);
|
||||
}),
|
||||
Rx.ignoreElements()
|
||||
);
|
||||
}
|
|
@ -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
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { runMappingsCompatibilityChecks } from './run_mappings_compatibility_check';
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
import { CleanupTask } from '@kbn/dev-cli-runner';
|
||||
import { createTestEsCluster } from '@kbn/test';
|
||||
import { extractMappingsFromPlugins } from './extract_mappings_from_plugins';
|
||||
import { checkAdditiveOnlyChange } from './check_additive_only_change';
|
||||
import { checkIncompatibleMappings } from './check_incompatible_mappings';
|
||||
import { readCurrentMappings, updateCurrentMappings } from './current_mappings';
|
||||
|
||||
export const runMappingsCompatibilityChecks = async ({
|
||||
fix,
|
||||
verify,
|
||||
log,
|
||||
addCleanupTask,
|
||||
}: {
|
||||
fix: boolean;
|
||||
verify: boolean;
|
||||
log: ToolingLog;
|
||||
addCleanupTask: (task: CleanupTask) => void;
|
||||
}) => {
|
||||
/**
|
||||
* Algorithm for checking compatible mappings. Should work in CI or local
|
||||
* dev environment.
|
||||
* 1. Extract mappings from code as JSON object
|
||||
* 2. Check if extracted mappings is different from current_mappings.json, current_mappings.json stores
|
||||
* the mappings from upstream and is commited to each branch
|
||||
* 3. Start a fresh ES node
|
||||
* 4. Upload current_mappings.json to ES node
|
||||
* 5. Upload extracted mappings.json to ES node
|
||||
* 6. Check result of response to step 5, if bad response the mappings are incompatible
|
||||
* 7. If good response, write extracted mappings to current_mappings.json
|
||||
*/
|
||||
|
||||
log.info('Extracting mappings from plugins');
|
||||
const extractedMappings = await log.indent(4, async () => {
|
||||
return await extractMappingsFromPlugins(log);
|
||||
});
|
||||
|
||||
const currentMappings = await readCurrentMappings();
|
||||
const isMappingChanged = !deepEqual(currentMappings, extractedMappings);
|
||||
|
||||
if (!isMappingChanged) {
|
||||
log.success('Mappings are unchanged.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (verify) {
|
||||
log.info('Checking if any mappings have been removed');
|
||||
await log.indent(4, async () => {
|
||||
return checkAdditiveOnlyChange(log, currentMappings, extractedMappings);
|
||||
});
|
||||
|
||||
log.info('Starting es...');
|
||||
const esClient = await log.indent(4, async () => {
|
||||
const cluster = createTestEsCluster({ log });
|
||||
await cluster.start();
|
||||
addCleanupTask(() => cluster.cleanup());
|
||||
return cluster.getClient();
|
||||
});
|
||||
|
||||
log.info(`Checking if mappings are compatible`);
|
||||
await log.indent(4, async () => {
|
||||
await checkIncompatibleMappings({
|
||||
log,
|
||||
esClient,
|
||||
currentMappings,
|
||||
nextMappings: extractedMappings,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (fix) {
|
||||
await updateCurrentMappings(extractedMappings);
|
||||
log.warning(
|
||||
`Updated extracted mappings in current_mappings.json file, please commit the changes if desired.`
|
||||
);
|
||||
} else {
|
||||
log.warning(
|
||||
`The extracted mappings do not match the current_mappings.json file, run with --fix to update.`
|
||||
);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { difference } from 'lodash';
|
||||
|
||||
export interface CompareResult {
|
||||
error: boolean;
|
||||
fieldsToAdd: string[];
|
||||
registeredFields: string[];
|
||||
missingFromModelVersion: string[];
|
||||
missingFromDefinition: string[];
|
||||
}
|
||||
|
||||
export const compareFieldLists = ({
|
||||
currentFields,
|
||||
registeredFields = [],
|
||||
modelVersionFields = [],
|
||||
}: {
|
||||
currentFields: string[] | undefined;
|
||||
registeredFields: string[] | undefined;
|
||||
modelVersionFields: string[] | undefined;
|
||||
}): CompareResult => {
|
||||
// type not present in the file, so it was just added.
|
||||
// in that case we just update the file to add all the registered fields.
|
||||
if (!currentFields) {
|
||||
return {
|
||||
error: false,
|
||||
registeredFields,
|
||||
fieldsToAdd: registeredFields,
|
||||
missingFromModelVersion: [],
|
||||
missingFromDefinition: [],
|
||||
};
|
||||
}
|
||||
|
||||
// we search all registered/mv fields not already in the file
|
||||
const registeredFieldsNotInCurrent = difference(registeredFields, currentFields);
|
||||
const modelVersionFieldsNotInCurrent = difference(modelVersionFields, currentFields);
|
||||
|
||||
// then we search for registered fields not in model versions, and the opposite
|
||||
const registeredFieldsNotInModelVersions = difference(
|
||||
registeredFieldsNotInCurrent,
|
||||
modelVersionFieldsNotInCurrent
|
||||
);
|
||||
const modelVersionFieldsNotRegistered = difference(
|
||||
modelVersionFieldsNotInCurrent,
|
||||
registeredFieldsNotInCurrent
|
||||
);
|
||||
|
||||
// if any non-file field is present only in mapping definition or in model version, then there's an error on the type
|
||||
const anyFieldMissing =
|
||||
registeredFieldsNotInModelVersions.length > 0 || modelVersionFieldsNotRegistered.length > 0;
|
||||
|
||||
return {
|
||||
error: anyFieldMissing,
|
||||
registeredFields,
|
||||
fieldsToAdd: registeredFieldsNotInCurrent,
|
||||
missingFromModelVersion: registeredFieldsNotInModelVersions,
|
||||
missingFromDefinition: modelVersionFieldsNotRegistered,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { readFile, writeFile } from 'fs/promises';
|
||||
import Path from 'path';
|
||||
import { FieldListMap } from '@kbn/core-saved-objects-base-server-internal';
|
||||
|
||||
const CURRENT_FIELDS_FILE_PATH = Path.resolve(__dirname, '../../current_fields.json');
|
||||
|
||||
export const readCurrentFields = async (): Promise<FieldListMap> => {
|
||||
try {
|
||||
const fileContent = await readFile(CURRENT_FIELDS_FILE_PATH, 'utf-8');
|
||||
return JSON.parse(fileContent);
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
return {};
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const writeCurrentFields = async (fieldMap: FieldListMap) => {
|
||||
await writeFile(CURRENT_FIELDS_FILE_PATH, JSON.stringify(fieldMap, null, 2) + '\n', 'utf-8');
|
||||
};
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import ChildProcess from 'child_process';
|
||||
import { Readable } from 'stream';
|
||||
import * as Rx from 'rxjs';
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
import { SomeDevLog } from '@kbn/some-dev-log';
|
||||
import { observeLines } from '@kbn/stdio-dev-helpers';
|
||||
import type { Result } from './extract_field_lists_from_plugins_worker';
|
||||
|
||||
/**
|
||||
* Run a worker process that starts the core with all plugins enabled and sends back the
|
||||
* registered fields for all plugins.
|
||||
*
|
||||
* We run this in a child process to make it easier to kill the kibana instance once done
|
||||
* (dodges issues with open handles), and so that we can harvest logs and feed them into
|
||||
* the logger when debugging.
|
||||
*/
|
||||
export async function extractFieldListsFromPlugins(log: SomeDevLog): Promise<Result> {
|
||||
log.info('Loading core with all plugins enabled so that we can get all savedObject mappings...');
|
||||
|
||||
const fork = ChildProcess.fork(require.resolve('./extract_field_lists_from_plugins_worker.ts'), {
|
||||
execArgv: ['--require=@kbn/babel-register/install'],
|
||||
cwd: REPO_ROOT,
|
||||
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
||||
});
|
||||
|
||||
const result = await Rx.firstValueFrom(
|
||||
Rx.merge(
|
||||
// the actual value we are interested in
|
||||
Rx.fromEvent(fork, 'message'),
|
||||
|
||||
// worker logs are written to the logger, but dropped from the stream
|
||||
routeToLog(fork.stdout!, log, 'debug'),
|
||||
routeToLog(fork.stderr!, log, 'error'),
|
||||
|
||||
// if an error occurs running the worker throw it into the stream
|
||||
Rx.fromEvent(fork, 'error').pipe(
|
||||
Rx.map((err) => {
|
||||
throw err;
|
||||
})
|
||||
)
|
||||
).pipe(
|
||||
Rx.takeUntil(Rx.fromEvent(fork, 'exit')),
|
||||
Rx.map((results) => {
|
||||
const [outcome] = results as [Result];
|
||||
log.debug('message received from worker', outcome);
|
||||
fork.kill('SIGILL');
|
||||
return outcome;
|
||||
}),
|
||||
Rx.defaultIfEmpty(undefined)
|
||||
)
|
||||
);
|
||||
|
||||
if (!result) {
|
||||
throw new Error('worker exited without sending mappings');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function routeToLog(readable: Readable, log: SomeDevLog, level: 'debug' | 'error') {
|
||||
return observeLines(readable).pipe(
|
||||
Rx.tap((line) => {
|
||||
log[level](line);
|
||||
}),
|
||||
Rx.ignoreElements()
|
||||
);
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { createRootWithCorePlugins } from '@kbn/core-test-helpers-kbn-server';
|
||||
import { set } from '@kbn/safer-lodash-set';
|
||||
import { PLUGIN_SYSTEM_ENABLE_ALL_PLUGINS_CONFIG_PATH } from '@kbn/core-plugins-server-internal/src/constants';
|
||||
import {
|
||||
FieldListMap,
|
||||
getFieldListMapFromMappingDefinitions,
|
||||
SavedObjectsTypeMappingDefinitions,
|
||||
} from '@kbn/core-saved-objects-base-server-internal';
|
||||
import { getFieldListMapFromModelVersions } from './get_field_list_from_model_version';
|
||||
|
||||
export interface Result {
|
||||
fieldsFromRegisteredTypes: FieldListMap;
|
||||
fieldsFromModelVersions: FieldListMap;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
if (!process.send) {
|
||||
throw new Error('worker must be run in a node.js fork');
|
||||
}
|
||||
|
||||
const settings = {
|
||||
logging: {
|
||||
loggers: [{ name: 'root', level: 'info', appenders: ['console'] }],
|
||||
},
|
||||
migrations: { skip: true },
|
||||
elasticsearch: { skipStartupConnectionCheck: true },
|
||||
};
|
||||
|
||||
set(settings, PLUGIN_SYSTEM_ENABLE_ALL_PLUGINS_CONFIG_PATH, true);
|
||||
|
||||
const root = createRootWithCorePlugins(settings, {
|
||||
basePath: false,
|
||||
cache: false,
|
||||
dev: true,
|
||||
disableOptimizer: true,
|
||||
silent: false,
|
||||
dist: false,
|
||||
oss: false,
|
||||
runExamples: false,
|
||||
watch: false,
|
||||
});
|
||||
|
||||
await root.preboot();
|
||||
const { savedObjects } = await root.setup();
|
||||
const typeRegistry = savedObjects.getTypeRegistry();
|
||||
|
||||
const registeredTypes = typeRegistry.getAllTypes();
|
||||
const registeredMappings = registeredTypes.reduce<SavedObjectsTypeMappingDefinitions>(
|
||||
(memo, type) => {
|
||||
memo[type.name] = type.mappings;
|
||||
return memo;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
const fieldsFromRegisteredTypes = getFieldListMapFromMappingDefinitions(registeredMappings);
|
||||
const fieldsFromModelVersions = getFieldListMapFromModelVersions(registeredTypes);
|
||||
|
||||
const result: Result = {
|
||||
fieldsFromRegisteredTypes,
|
||||
fieldsFromModelVersions,
|
||||
};
|
||||
process.send(result);
|
||||
})().catch((error) => {
|
||||
process.stderr.write(`UNHANDLED ERROR: ${error.stack}`);
|
||||
process.exit(1);
|
||||
});
|
|
@ -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
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { SavedObjectsType } from '@kbn/core-saved-objects-server';
|
||||
import { FieldListMap, getVersionAddedFields } from '@kbn/core-saved-objects-base-server-internal';
|
||||
|
||||
const getModelVersionAddedFieldsForType = (typeDef: SavedObjectsType): string[] => {
|
||||
const addedFieldSet = new Set<string>();
|
||||
const versions =
|
||||
typeof typeDef.modelVersions === 'function'
|
||||
? typeDef.modelVersions()
|
||||
: typeDef.modelVersions ?? {};
|
||||
Object.values(versions).forEach((version) => {
|
||||
const addedFields = getVersionAddedFields(version);
|
||||
addedFields.forEach((field) => addedFieldSet.add(field));
|
||||
});
|
||||
return [...addedFieldSet].sort();
|
||||
};
|
||||
|
||||
export const getFieldListMapFromModelVersions = (types: SavedObjectsType[]): FieldListMap => {
|
||||
return types.reduce<FieldListMap>((memo, type) => {
|
||||
memo[type.name] = getModelVersionAddedFieldsForType(type);
|
||||
return memo;
|
||||
}, {});
|
||||
};
|
|
@ -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
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { runModelVersionMappingAdditionsChecks } from './run_versions_mapping_additions_check';
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
import { createFailError } from '@kbn/dev-cli-errors';
|
||||
import { FieldListMap } from '@kbn/core-saved-objects-base-server-internal';
|
||||
import { compareFieldLists, type CompareResult } from './compare_type_field_lists';
|
||||
import { readCurrentFields, writeCurrentFields } from './current_fields';
|
||||
import { extractFieldListsFromPlugins } from './extract_field_lists_from_plugins';
|
||||
|
||||
export const runModelVersionMappingAdditionsChecks = async ({
|
||||
fix,
|
||||
override,
|
||||
verify,
|
||||
log,
|
||||
}: {
|
||||
fix: boolean;
|
||||
override: boolean;
|
||||
verify: boolean;
|
||||
log: ToolingLog;
|
||||
}) => {
|
||||
log.info('Generating field lists from registry and file');
|
||||
const { fieldsFromRegisteredTypes, fieldsFromModelVersions } = await extractFieldListsFromPlugins(
|
||||
log
|
||||
);
|
||||
const currentFields = await readCurrentFields();
|
||||
|
||||
const allTypeNames = [
|
||||
...new Set([
|
||||
...Object.keys(fieldsFromRegisteredTypes),
|
||||
...Object.keys(currentFields),
|
||||
...Object.keys(fieldsFromModelVersions),
|
||||
]),
|
||||
];
|
||||
|
||||
log.info('Generating field delta');
|
||||
const results = allTypeNames.reduce<Record<string, CompareResult>>((memo, typeName) => {
|
||||
memo[typeName] = compareFieldLists({
|
||||
registeredFields: fieldsFromRegisteredTypes[typeName],
|
||||
currentFields: currentFields[typeName],
|
||||
modelVersionFields: fieldsFromModelVersions[typeName],
|
||||
});
|
||||
return memo;
|
||||
}, {});
|
||||
|
||||
const hasError = Object.values(results).some((result) => result.error);
|
||||
if (hasError) {
|
||||
const errorMessage = getErrorMessage(results);
|
||||
if (verify) {
|
||||
throw createFailError(errorMessage + `\nUse --override --no-verify`);
|
||||
} else {
|
||||
log.warning(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (fix || override) {
|
||||
log.info(`Updating field file with override: ${override}`);
|
||||
const updatedFields = updateCurrentFields(currentFields, results, override);
|
||||
await writeCurrentFields(updatedFields);
|
||||
}
|
||||
};
|
||||
|
||||
const getErrorMessage = (results: Record<string, CompareResult>): string => {
|
||||
const errors = Object.entries(results)
|
||||
.filter(([_, result]) => result.error)
|
||||
.reduce<string[]>((memo, [typeName, result]) => {
|
||||
if (result.missingFromDefinition.length) {
|
||||
memo.push(
|
||||
`- ${typeName}: found mappings from model version not present in mappings definition: ${result.missingFromDefinition.join(
|
||||
','
|
||||
)}`
|
||||
);
|
||||
}
|
||||
if (result.missingFromModelVersion.length) {
|
||||
memo.push(
|
||||
`- ${typeName}: found mappings from root definition not present in any model version: ${result.missingFromModelVersion.join(
|
||||
','
|
||||
)}`
|
||||
);
|
||||
}
|
||||
return memo;
|
||||
}, []);
|
||||
|
||||
return `Found issues in savedObjects mappings:\n${errors.join('\n')}`;
|
||||
};
|
||||
|
||||
const updateCurrentFields = (
|
||||
currentFields: FieldListMap,
|
||||
results: Record<string, CompareResult>,
|
||||
override: boolean
|
||||
): FieldListMap => {
|
||||
// mutating the field lists is fine
|
||||
const updatedFields = override ? {} : { ...currentFields };
|
||||
Object.entries(results).forEach(([typeName, typeResult]) => {
|
||||
if (override) {
|
||||
updatedFields[typeName] = [...typeResult.registeredFields].sort();
|
||||
} else {
|
||||
if (!typeResult.error) {
|
||||
updatedFields[typeName] = [
|
||||
...new Set([...(updatedFields[typeName] || []), ...typeResult.fieldsToAdd]),
|
||||
].sort();
|
||||
}
|
||||
}
|
||||
});
|
||||
return updatedFields;
|
||||
};
|
|
@ -6,95 +6,46 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import { run } from '@kbn/dev-cli-runner';
|
||||
import { createTestEsCluster } from '@kbn/test';
|
||||
|
||||
import { extractMappingsFromPlugins } from './extract_mappings_from_plugins';
|
||||
|
||||
import { checkAdditiveOnlyChange } from './check_additive_only_change';
|
||||
import { checkIncompatibleMappings } from './check_incompatible_mappings';
|
||||
import { readCurrentMappings, updateCurrentMappings } from './current_mappings';
|
||||
import { runMappingsCompatibilityChecks } from './compatibility';
|
||||
import { runModelVersionMappingAdditionsChecks } from './mappings_additions';
|
||||
|
||||
run(
|
||||
async ({ log, flagsReader, addCleanupTask }) => {
|
||||
const fix = flagsReader.boolean('fix');
|
||||
const verify = flagsReader.boolean('verify');
|
||||
const override = flagsReader.boolean('override');
|
||||
const task = flagsReader.string('task');
|
||||
|
||||
/**
|
||||
* Algorithm for checking compatible mappings. Should work in CI or local
|
||||
* dev environment.
|
||||
* 1. Extract mappings from code as JSON object
|
||||
* 2. Check if extracted mappings is different from current_mappings.json, current_mappings.json stores
|
||||
* the mappings from upstream and is commited to each branch
|
||||
* 3. Start a fresh ES node
|
||||
* 4. Upload current_mappings.json to ES node
|
||||
* 5. Upload extracted mappings.json to ES node
|
||||
* 6. Check result of response to step 5, if bad response the mappings are incompatible
|
||||
* 7. If good response, write extracted mappings to current_mappings.json
|
||||
*/
|
||||
|
||||
log.info('Extracting mappings from plugins');
|
||||
const extractedMappings = await log.indent(4, async () => {
|
||||
return await extractMappingsFromPlugins(log);
|
||||
});
|
||||
|
||||
const currentMappings = await readCurrentMappings();
|
||||
const isMappingChanged = !deepEqual(currentMappings, extractedMappings);
|
||||
|
||||
if (!isMappingChanged) {
|
||||
log.success('Mappings are unchanged.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (verify) {
|
||||
log.info('Checking if any mappings have been removed');
|
||||
if (!task || task === 'mapping-addition') {
|
||||
log.info('Running model version mapping addition checks');
|
||||
await log.indent(4, async () => {
|
||||
return checkAdditiveOnlyChange(log, currentMappings, extractedMappings);
|
||||
});
|
||||
|
||||
log.info('Starting es...');
|
||||
const esClient = await log.indent(4, async () => {
|
||||
const cluster = createTestEsCluster({ log });
|
||||
await cluster.start();
|
||||
addCleanupTask(() => cluster.cleanup());
|
||||
return cluster.getClient();
|
||||
});
|
||||
|
||||
log.info(`Checking if mappings are compatible`);
|
||||
await log.indent(4, async () => {
|
||||
await checkIncompatibleMappings({
|
||||
log,
|
||||
esClient,
|
||||
currentMappings,
|
||||
nextMappings: extractedMappings,
|
||||
});
|
||||
await runModelVersionMappingAdditionsChecks({ fix, override, verify, log });
|
||||
});
|
||||
}
|
||||
|
||||
if (fix) {
|
||||
await updateCurrentMappings(extractedMappings);
|
||||
log.warning(
|
||||
`Updated extracted mappings in current_mappings.json file, please commit the changes if desired.`
|
||||
);
|
||||
} else {
|
||||
log.warning(
|
||||
`The extracted mappings do not match the current_mappings.json file, run with --fix to update.`
|
||||
);
|
||||
if (!task || task === 'compatibility') {
|
||||
log.info('Running mapping compatibility checks');
|
||||
await log.indent(4, async () => {
|
||||
await runMappingsCompatibilityChecks({ fix, verify, log, addCleanupTask });
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
description: `
|
||||
Determine if the current SavedObject mappings in the source code can be applied to the current mappings from upstream.
|
||||
Determine if the changes performed to the savedObjects mappings are following our standards
|
||||
`,
|
||||
flags: {
|
||||
boolean: ['fix', 'verify'],
|
||||
boolean: ['fix', 'override', 'verify'],
|
||||
string: ['task'],
|
||||
default: {
|
||||
verify: true,
|
||||
mappings: true,
|
||||
},
|
||||
help: `
|
||||
--fix If the current mappings differ from the mappings in the file, update the current_mappings.json file
|
||||
--override If the current mappings differ from the mappings in the file, update the current_mappings.json file
|
||||
--no-verify Don't run any validation, just update the current_mappings.json file.
|
||||
--task Specify which task(s) to run (compatibility | mapping-addition)
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -26,5 +26,7 @@
|
|||
"@kbn/test",
|
||||
"@kbn/core-elasticsearch-client-server-mocks",
|
||||
"@kbn/safer-lodash-set",
|
||||
"@kbn/tooling-log",
|
||||
"@kbn/core-saved-objects-server",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
|
|||
"infrastructure-ui-source": "113182d6895764378dfe7fa9fa027244f3a457c4",
|
||||
"ingest-agent-policies": "7633e578f60c074f8267bc50ec4763845e431437",
|
||||
"ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d",
|
||||
"ingest-outputs": "4dd3cb38a91c848df95336a24a5abde2c8560fd1",
|
||||
"ingest-outputs": "20bd44ce6016079c3b28f1b2bc241e7715be48f8",
|
||||
"ingest-package-policies": "f4c2767e852b700a8b82678925b86bac08958b43",
|
||||
"ingest_manager_settings": "64955ef1b7a9ffa894d4bb9cf863b5602bfa6885",
|
||||
"inventory-view": "b8683c8e352a286b4aca1ab21003115a4800af83",
|
||||
|
|
|
@ -310,6 +310,25 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({
|
|||
},
|
||||
],
|
||||
},
|
||||
'3': {
|
||||
changes: [
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {
|
||||
secrets: {
|
||||
properties: {
|
||||
service_token: {
|
||||
dynamic: false,
|
||||
properties: {
|
||||
id: { type: 'keyword' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
migrations: {
|
||||
'7.13.0': migrateOutputToV7130,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue