diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index 599e8c54643ce..38a73ec92313f 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -191,3 +191,6 @@ Specifies the default timeout for the all rule types tasks. The time is formatte `[ms,s,m,h,d,w,M,Y]` + For example, `20m`, `24h`, `7d`, `1w`. Default: `60s`. + +`xpack.alerting.cancelAlertsOnRuleTimeout`:: +Specifies whether to skip writing alerts and scheduling actions if rule execution is cancelled due to timeout. Default: `true`. This setting can be overridden by individual rule types. \ No newline at end of file diff --git a/logs/.empty b/logs/.empty deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/package.json b/package.json index c30294bf86718..7bca78ce94f5c 100644 --- a/package.json +++ b/package.json @@ -512,6 +512,7 @@ "@types/delete-empty": "^2.0.0", "@types/ejs": "^3.0.6", "@types/elasticsearch": "^5.0.33", + "@types/elastic__datemath": "link:bazel-bin/packages/elastic-datemath/npm_module_types", "@types/enzyme": "^3.10.8", "@types/eslint": "^7.28.0", "@types/extract-zip": "^1.6.2", @@ -661,7 +662,7 @@ "callsites": "^3.1.0", "chai": "3.5.0", "chance": "1.0.18", - "chromedriver": "^94.0.0", + "chromedriver": "^95.0.0", "clean-webpack-plugin": "^3.0.0", "cmd-shim": "^2.1.0", "compression-webpack-plugin": "^4.0.0", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 214f5f9abe5cd..b96fea80f027b 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -1,7 +1,6 @@ -# Grouping target to call all underlying packages build -# targets so we can build them all at once +# It will build all declared code packages filegroup( - name = "build", + name = "build_pkg_code", srcs = [ "//packages/elastic-apm-synthtrace:build", "//packages/elastic-datemath:build", @@ -70,3 +69,24 @@ filegroup( "//packages/kbn-utils:build", ], ) + +# It will build all declared package types +filegroup( + name = "build_pkg_types", + srcs = [ + "//packages/elastic-datemath:build_types", + ], +) + + + +# Grouping target to call all underlying packages build +# targets so we can build them all at once +# It will auto build all declared code packages and types packages +filegroup( + name = "build", + srcs = [ + ":build_pkg_code", + ":build_pkg_types" + ], +) diff --git a/packages/elastic-apm-synthtrace/BUILD.bazel b/packages/elastic-apm-synthtrace/BUILD.bazel index 5d9510c6a81d5..8ae1632ffee4c 100644 --- a/packages/elastic-apm-synthtrace/BUILD.bazel +++ b/packages/elastic-apm-synthtrace/BUILD.bazel @@ -37,7 +37,7 @@ RUNTIME_DEPS = [ ] TYPES_DEPS = [ - "//packages/elastic-datemath", + "//packages/elastic-datemath:npm_module_types", "@npm//@elastic/elasticsearch", "@npm//moment", "@npm//p-limit", diff --git a/packages/elastic-datemath/BUILD.bazel b/packages/elastic-datemath/BUILD.bazel index 04eb153bcc9e1..b4ed018fe9d04 100644 --- a/packages/elastic-datemath/BUILD.bazel +++ b/packages/elastic-datemath/BUILD.bazel @@ -1,9 +1,10 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") -load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") -load("//src/dev/bazel:index.bzl", "jsts_transpiler") +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "ts_project", "pkg_npm", "pkg_npm_types") PKG_BASE_NAME = "elastic-datemath" PKG_REQUIRE_NAME = "@elastic/datemath" +TYPES_PKG_REQUIRE_NAME = "@types/elastic__datemath" SOURCE_FILES = glob([ "src/index.ts", @@ -26,7 +27,7 @@ TYPES_DEPS = [ "@npm//@types/node", ] -DEPS = TYPES_DEPS +RUNTIME_DEPS = TYPES_DEPS jsts_transpiler( name = "target_node", @@ -47,20 +48,20 @@ ts_project( name = "tsc_types", args = ['--pretty'], srcs = SRCS, - deps = DEPS, + deps = TYPES_DEPS, declaration = True, declaration_map = True, emit_declaration_only = True, out_dir = "target_types", source_map = True, root_dir = "src", - tsconfig = ":tsconfig", + tsconfig = ":tsconfig" ) js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = DEPS + [":target_node", ":tsc_types"], + deps = RUNTIME_DEPS + [":target_node"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) @@ -79,3 +80,20 @@ filegroup( ], visibility = ["//visibility:public"], ) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = TYPES_PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [ + ":npm_module_types", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/elastic-datemath/package.json b/packages/elastic-datemath/package.json index b68a24d6cb7ae..af958e3730c3d 100644 --- a/packages/elastic-datemath/package.json +++ b/packages/elastic-datemath/package.json @@ -4,7 +4,6 @@ "description": "elasticsearch datemath parser, used in kibana", "license": "Apache-2.0", "main": "./target_node/index.js", - "types": "./target_types/index.d.ts", "peerDependencies": { "moment": "^2.24.0" } diff --git a/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.test.ts b/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.test.ts index 3b5f1f777b0e3..ae21649690a99 100644 --- a/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.test.ts +++ b/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.test.ts @@ -121,7 +121,7 @@ describe('esArchiver: createGenerateDocRecordsStream()', () => { "calls": Array [ Array [ Object { - "_source": "true", + "_source": true, "index": "bar", "query": undefined, "rest_total_hits_as_int": true, @@ -136,7 +136,7 @@ describe('esArchiver: createGenerateDocRecordsStream()', () => { ], Array [ Object { - "_source": "true", + "_source": true, "index": "foo", "query": undefined, "rest_total_hits_as_int": true, diff --git a/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.ts b/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.ts index 4bd44b649afd2..b1b713f6f3cbf 100644 --- a/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.ts +++ b/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.ts @@ -38,7 +38,7 @@ export function createGenerateDocRecordsStream({ index, scroll: SCROLL_TIMEOUT, size: SCROLL_SIZE, - _source: 'true', + _source: true, query, rest_total_hits_as_int: true, }, diff --git a/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel b/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel index 24819bdd16a33..c827eb19f7a3d 100644 --- a/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel @@ -37,7 +37,7 @@ RUNTIME_DEPS = [ ] TYPES_DEPS = [ - "//packages/elastic-datemath", + "//packages/elastic-datemath:npm_module_types", "@npm//fp-ts", "@npm//io-ts", "@npm//moment", diff --git a/packages/kbn-ui-shared-deps-src/BUILD.bazel b/packages/kbn-ui-shared-deps-src/BUILD.bazel index d1c9115c0515b..f4597c1ee66b1 100644 --- a/packages/kbn-ui-shared-deps-src/BUILD.bazel +++ b/packages/kbn-ui-shared-deps-src/BUILD.bazel @@ -41,7 +41,7 @@ RUNTIME_DEPS = [ ] TYPES_DEPS = [ - "//packages/elastic-datemath", + "//packages/elastic-datemath:npm_module_types", "//packages/elastic-safer-lodash-set", "//packages/kbn-analytics", "//packages/kbn-babel-preset", diff --git a/src/core/server/logging/appenders/file/file_appender.test.mocks.ts b/src/core/server/logging/appenders/file/file_appender.test.mocks.ts index 2c2a2015b6fd3..48173d683e9a4 100644 --- a/src/core/server/logging/appenders/file/file_appender.test.mocks.ts +++ b/src/core/server/logging/appenders/file/file_appender.test.mocks.ts @@ -19,4 +19,5 @@ jest.mock('../../layouts/layouts', () => { }); export const mockCreateWriteStream = jest.fn(); -jest.mock('fs', () => ({ createWriteStream: mockCreateWriteStream })); +export const mockMkdirSync = jest.fn(); +jest.mock('fs', () => ({ createWriteStream: mockCreateWriteStream, mkdirSync: mockMkdirSync })); diff --git a/src/core/server/logging/appenders/file/file_appender.test.ts b/src/core/server/logging/appenders/file/file_appender.test.ts index 081cb16afd2ff..f3a5d483d5a7b 100644 --- a/src/core/server/logging/appenders/file/file_appender.test.ts +++ b/src/core/server/logging/appenders/file/file_appender.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { mockCreateWriteStream } from './file_appender.test.mocks'; +import { mockCreateWriteStream, mockMkdirSync } from './file_appender.test.mocks'; import { LogRecord, LogLevel } from '@kbn/logging'; import { FileAppender } from './file_appender'; @@ -15,6 +15,7 @@ const tickMs = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) beforeEach(() => { mockCreateWriteStream.mockReset(); + mockMkdirSync.mockReset(); }); test('`createConfigSchema()` creates correct schema.', () => { @@ -49,8 +50,10 @@ test('file stream is created only once and only after first `append()` is called }); const mockPath = 'mock://path/file.log'; + const mockDir = 'mock://path'; const appender = new FileAppender({ format: () => '' }, mockPath); + expect(mockMkdirSync).not.toHaveBeenCalled(); expect(mockCreateWriteStream).not.toHaveBeenCalled(); appender.append({ @@ -61,12 +64,17 @@ test('file stream is created only once and only after first `append()` is called pid: 5355, }); + expect(mockMkdirSync).toHaveBeenCalledTimes(1); + expect(mockMkdirSync).toHaveBeenCalledWith(mockDir, { + recursive: true, + }); expect(mockCreateWriteStream).toHaveBeenCalledTimes(1); expect(mockCreateWriteStream).toHaveBeenCalledWith(mockPath, { encoding: 'utf8', flags: 'a', }); + mockMkdirSync.mockClear(); mockCreateWriteStream.mockClear(); appender.append({ context: 'context-2', @@ -76,6 +84,7 @@ test('file stream is created only once and only after first `append()` is called pid: 5355, }); + expect(mockMkdirSync).not.toHaveBeenCalled(); expect(mockCreateWriteStream).not.toHaveBeenCalled(); }); diff --git a/src/core/server/logging/appenders/file/file_appender.ts b/src/core/server/logging/appenders/file/file_appender.ts index 3c22a37038bcc..1c0f842f6c630 100644 --- a/src/core/server/logging/appenders/file/file_appender.ts +++ b/src/core/server/logging/appenders/file/file_appender.ts @@ -8,7 +8,8 @@ import { schema } from '@kbn/config-schema'; import { LogRecord, Layout, DisposableAppender } from '@kbn/logging'; -import { createWriteStream, WriteStream } from 'fs'; +import { createWriteStream, WriteStream, mkdirSync } from 'fs'; +import { dirname } from 'path'; import { Layouts, LayoutConfigType } from '../../layouts/layouts'; @@ -47,6 +48,7 @@ export class FileAppender implements DisposableAppender { */ public append(record: LogRecord) { if (this.outputStream === undefined) { + this.ensureDirectory(this.path); this.outputStream = createWriteStream(this.path, { encoding: 'utf8', flags: 'a', @@ -73,4 +75,8 @@ export class FileAppender implements DisposableAppender { }); }); } + + private ensureDirectory(path: string) { + mkdirSync(dirname(path), { recursive: true }); + } } diff --git a/src/core/server/logging/appenders/rolling_file/rolling_file_manager.ts b/src/core/server/logging/appenders/rolling_file/rolling_file_manager.ts index e96c57578f908..df9c531fa2e7f 100644 --- a/src/core/server/logging/appenders/rolling_file/rolling_file_manager.ts +++ b/src/core/server/logging/appenders/rolling_file/rolling_file_manager.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import { createWriteStream, WriteStream } from 'fs'; +import { createWriteStream, WriteStream, mkdirSync } from 'fs'; +import { dirname } from 'path'; import { RollingFileContext } from './rolling_file_context'; /** @@ -40,6 +41,7 @@ export class RollingFileManager { private ensureStreamOpen() { if (this.outputStream === undefined) { + this.ensureDirectory(this.filePath); this.outputStream = createWriteStream(this.filePath, { encoding: 'utf8', flags: 'a', @@ -49,4 +51,8 @@ export class RollingFileManager { } return this.outputStream!; } + + private ensureDirectory(path: string) { + mkdirSync(dirname(path), { recursive: true }); + } } diff --git a/src/core/server/saved_objects/migrations/actions/check_for_unknown_docs.test.ts b/src/core/server/saved_objects/migrations/actions/check_for_unknown_docs.test.ts index 8a99d28b40de1..0ddb858f91bc0 100644 --- a/src/core/server/saved_objects/migrations/actions/check_for_unknown_docs.test.ts +++ b/src/core/server/saved_objects/migrations/actions/check_for_unknown_docs.test.ts @@ -98,12 +98,10 @@ describe('checkForUnknownDocs', () => { const result = await task(); expect(Either.isRight(result)).toBe(true); - expect((result as Either.Right).right).toEqual({ - unknownDocs: [], - }); + expect((result as Either.Right).right).toEqual({}); }); - it('resolves with `Either.right` when unknown docs are found', async () => { + it('resolves with `Either.left` when unknown docs are found', async () => { const client = elasticsearchClientMock.createInternalClient( elasticsearchClientMock.createSuccessTransportRequestPromise({ hits: { @@ -124,8 +122,9 @@ describe('checkForUnknownDocs', () => { const result = await task(); - expect(Either.isRight(result)).toBe(true); - expect((result as Either.Right).right).toEqual({ + expect(Either.isLeft(result)).toBe(true); + expect((result as Either.Left).left).toEqual({ + type: 'unknown_docs_found', unknownDocs: [ { id: '12', type: 'foo' }, { id: '14', type: 'bar' }, @@ -151,8 +150,9 @@ describe('checkForUnknownDocs', () => { const result = await task(); - expect(Either.isRight(result)).toBe(true); - expect((result as Either.Right).right).toEqual({ + expect(Either.isLeft(result)).toBe(true); + expect((result as Either.Left).left).toEqual({ + type: 'unknown_docs_found', unknownDocs: [{ id: '12', type: 'unknown' }], }); }); diff --git a/src/core/server/saved_objects/migrations/actions/check_for_unknown_docs.ts b/src/core/server/saved_objects/migrations/actions/check_for_unknown_docs.ts index cfeda0548b16a..6dd8fbda73c95 100644 --- a/src/core/server/saved_objects/migrations/actions/check_for_unknown_docs.ts +++ b/src/core/server/saved_objects/migrations/actions/check_for_unknown_docs.ts @@ -32,6 +32,7 @@ export interface CheckForUnknownDocsFoundDoc { /** @internal */ export interface UnknownDocsFound { + type: 'unknown_docs_found'; unknownDocs: CheckForUnknownDocsFoundDoc[]; } @@ -41,7 +42,10 @@ export const checkForUnknownDocs = indexName, unusedTypesQuery, knownTypes, - }: CheckForUnknownDocsParams): TaskEither.TaskEither => + }: CheckForUnknownDocsParams): TaskEither.TaskEither< + RetryableEsClientError | UnknownDocsFound, + {} + > => () => { const query = createUnknownDocQuery(unusedTypesQuery, knownTypes); @@ -54,9 +58,14 @@ export const checkForUnknownDocs = }) .then((response) => { const { hits } = response.body.hits; - return Either.right({ - unknownDocs: hits.map((hit) => ({ id: hit._id, type: hit._source?.type ?? 'unknown' })), - }); + if (hits.length) { + return Either.left({ + type: 'unknown_docs_found' as const, + unknownDocs: hits.map((hit) => ({ id: hit._id, type: hit._source?.type ?? 'unknown' })), + }); + } else { + return Either.right({}); + } }) .catch(catchRetryableEsClientErrors); }; diff --git a/src/core/server/saved_objects/migrations/actions/index.ts b/src/core/server/saved_objects/migrations/actions/index.ts index 158d97f3b7c27..4e88e9c448d40 100644 --- a/src/core/server/saved_objects/migrations/actions/index.ts +++ b/src/core/server/saved_objects/migrations/actions/index.ts @@ -80,6 +80,7 @@ export type { } from './update_and_pickup_mappings'; export { updateAndPickupMappings } from './update_and_pickup_mappings'; +import type { UnknownDocsFound } from './check_for_unknown_docs'; export type { CheckForUnknownDocsParams, UnknownDocsFound, @@ -141,6 +142,7 @@ export interface ActionErrorTypeMap { remove_index_not_a_concrete_index: RemoveIndexNotAConcreteIndex; documents_transform_failed: DocumentsTransformFailed; request_entity_too_large_exception: RequestEntityTooLargeException; + unknown_docs_found: UnknownDocsFound; } /** diff --git a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts index c8ff79351aadb..1effabe7bfc96 100644 --- a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts +++ b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts @@ -139,7 +139,6 @@ describe('migrateRawDocsSafely', () => { ]); const task = migrateRawDocsSafely({ serializer: new SavedObjectsSerializer(new SavedObjectTypeRegistry()), - knownTypes: new Set(['a', 'c']), migrateDoc: transform, rawDocs: [ { _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } }, @@ -184,7 +183,6 @@ describe('migrateRawDocsSafely', () => { ]); const task = migrateRawDocsSafely({ serializer: new SavedObjectsSerializer(new SavedObjectTypeRegistry()), - knownTypes: new Set(['a', 'c']), migrateDoc: transform, rawDocs: [ { _id: 'foo:b', _source: { type: 'a', a: { name: 'AAA' } } }, @@ -206,7 +204,6 @@ describe('migrateRawDocsSafely', () => { ]); const task = migrateRawDocsSafely({ serializer: new SavedObjectsSerializer(new SavedObjectTypeRegistry()), - knownTypes: new Set(['a', 'c']), migrateDoc: transform, rawDocs: [{ _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } }], }); @@ -240,7 +237,6 @@ describe('migrateRawDocsSafely', () => { }); const task = migrateRawDocsSafely({ serializer: new SavedObjectsSerializer(new SavedObjectTypeRegistry()), - knownTypes: new Set(['a', 'c']), migrateDoc: transform, rawDocs: [{ _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } }], // this is the raw doc }); @@ -256,43 +252,4 @@ describe('migrateRawDocsSafely', () => { } `); }); - - test('skips documents of unknown types', async () => { - const transform = jest.fn((doc: any) => [ - set(_.cloneDeep(doc), 'attributes.name', 'HOI!'), - ]); - const task = migrateRawDocsSafely({ - serializer: new SavedObjectsSerializer(new SavedObjectTypeRegistry()), - knownTypes: new Set(['a']), - migrateDoc: transform, - rawDocs: [ - { _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } }, - { _id: 'c:d', _source: { type: 'c', c: { name: 'DDD' } } }, - ], - }); - - const result = (await task()) as Either.Right; - expect(result._tag).toEqual('Right'); - expect(result.right.processedDocs).toEqual([ - { - _id: 'a:b', - _source: { type: 'a', a: { name: 'HOI!' }, migrationVersion: {}, references: [] }, - }, - { - _id: 'c:d', - // name field is not migrated on unknown type - _source: { type: 'c', c: { name: 'DDD' } }, - }, - ]); - - const obj1 = { - id: 'b', - type: 'a', - attributes: { name: 'AAA' }, - migrationVersion: {}, - references: [], - }; - expect(transform).toHaveBeenCalledTimes(1); - expect(transform).toHaveBeenNthCalledWith(1, obj1); - }); }); diff --git a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts index 65ea21a6778d5..fb9176d111c8e 100644 --- a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts +++ b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts @@ -25,13 +25,16 @@ export interface DocumentsTransformFailed { readonly corruptDocumentIds: string[]; readonly transformErrors: TransformErrorObjects[]; } + export interface DocumentsTransformSuccess { readonly processedDocs: SavedObjectsRawDoc[]; } + export interface TransformErrorObjects { readonly rawId: string; readonly err: TransformSavedObjectDocumentError | Error; } + type MigrateFn = ( doc: SavedObjectUnsanitizedDoc ) => Promise>>; @@ -83,7 +86,6 @@ export async function migrateRawDocs( interface MigrateRawDocsSafelyDeps { serializer: SavedObjectsSerializer; - knownTypes: ReadonlySet; migrateDoc: MigrateAndConvertFn; rawDocs: SavedObjectsRawDoc[]; } @@ -97,7 +99,6 @@ interface MigrateRawDocsSafelyDeps { */ export function migrateRawDocsSafely({ serializer, - knownTypes, migrateDoc, rawDocs, }: MigrateRawDocsSafelyDeps): TaskEither.TaskEither< @@ -111,10 +112,7 @@ export function migrateRawDocsSafely({ const corruptSavedObjectIds: string[] = []; const options = { namespaceTreatment: 'lax' as const }; for (const raw of rawDocs) { - // Do not transform documents of unknown types - if (raw?._source?.type && !knownTypes.has(raw._source.type)) { - processedDocs.push(raw); - } else if (serializer.isRawSavedObject(raw, options)) { + if (serializer.isRawSavedObject(raw, options)) { try { const savedObject = convertToRawAddMigrationVersion(raw, options, serializer); processedDocs.push( diff --git a/src/core/server/saved_objects/migrations/integration_tests/7_13_0_unknown_types.test.ts b/src/core/server/saved_objects/migrations/integration_tests/7_13_0_unknown_types.test.ts index aea84cea22862..0791b0db30c93 100644 --- a/src/core/server/saved_objects/migrations/integration_tests/7_13_0_unknown_types.test.ts +++ b/src/core/server/saved_objects/migrations/integration_tests/7_13_0_unknown_types.test.ts @@ -11,16 +11,7 @@ import fs from 'fs/promises'; import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import * as kbnTestServer from '../../../../test_helpers/kbn_server'; import { Root } from '../../../root'; -import JSON5 from 'json5'; -import { ElasticsearchClient } from '../../../elasticsearch'; -import { Env } from '@kbn/config'; -import { REPO_ROOT } from '@kbn/utils'; -import { getEnvOptions } from '../../../config/mocks'; -import { retryAsync } from '../test_helpers/retry_async'; -import { LogRecord } from '@kbn/logging'; -const kibanaVersion = Env.createDefault(REPO_ROOT, getEnvOptions()).packageInfo.version; -const targetIndex = `.kibana_${kibanaVersion}_001`; const logFilePath = Path.join(__dirname, '7_13_unknown_types.log'); async function removeLogFile() { @@ -63,152 +54,54 @@ describe('migration v2', () => { await new Promise((resolve) => setTimeout(resolve, 10000)); }); - it('logs a warning and completes the migration with unknown docs retained', async () => { - root = createRoot(); - esServer = await startES(); - await root.preboot(); - await root.setup(); - await root.start(); - - let unknownDocsWarningLog: LogRecord; - - await retryAsync( - async () => { - const logFileContent = await fs.readFile(logFilePath, 'utf-8'); - const records = logFileContent - .split('\n') - .filter(Boolean) - .map((str) => JSON5.parse(str)); - - unknownDocsWarningLog = records.find((rec) => - rec.message.startsWith(`[.kibana] CHECK_UNKNOWN_DOCUMENTS`) - ); - - expect( - unknownDocsWarningLog.message.startsWith( - '[.kibana] CHECK_UNKNOWN_DOCUMENTS Upgrades will fail for 8.0+ because documents were found for unknown saved ' + - 'object types. To ensure that upgrades will succeed in the future, either re-enable plugins or delete ' + - `these documents from the "${targetIndex}" index after the current upgrade completes.` - ) - ).toBeTruthy(); - }, - { retryAttempts: 10, retryDelayMs: 200 } - ); - - const unknownDocs = [ - { type: 'space', id: 'space:default' }, - { type: 'space', id: 'space:first' }, - { type: 'space', id: 'space:second' }, - { type: 'space', id: 'space:third' }, - { type: 'space', id: 'space:forth' }, - { type: 'space', id: 'space:fifth' }, - { type: 'space', id: 'space:sixth' }, - { type: 'foo', id: 'P2SQfHkBs3dBRGh--No5' }, - { type: 'foo', id: 'QGSZfHkBs3dBRGh-ANoD' }, - { type: 'foo', id: 'QWSZfHkBs3dBRGh-hNob' }, - ]; - - unknownDocs.forEach(({ id, type }) => { - expect(unknownDocsWarningLog.message).toEqual( - expect.stringContaining(`- "${id}" (type: "${type}")`) - ); - }); - - const client: ElasticsearchClient = esServer.es.getKibanaEsClient(); - const { body: response } = await client.indices.getSettings({ - index: targetIndex, - }); - const settings = response[targetIndex].settings as estypes.IndicesIndexStatePrefixedSettings; - expect(settings.index).not.toBeUndefined(); - expect(settings.index!.blocks?.write).not.toEqual('true'); - - // Ensure that documents for unknown types were preserved in target index in an unmigrated state - const spaceDocs = await fetchDocs(client, targetIndex, 'space'); - expect(spaceDocs.map((s) => s.id)).toEqual( - expect.arrayContaining([ - 'space:default', - 'space:first', - 'space:second', - 'space:third', - 'space:forth', - 'space:fifth', - 'space:sixth', - ]) - ); - spaceDocs.forEach((d) => { - expect(d.migrationVersion.space).toEqual('6.6.0'); - expect(d.coreMigrationVersion).toEqual('7.13.0'); - }); - const fooDocs = await fetchDocs(client, targetIndex, 'foo'); - expect(fooDocs.map((f) => f.id)).toEqual( - expect.arrayContaining([ - 'P2SQfHkBs3dBRGh--No5', - 'QGSZfHkBs3dBRGh-ANoD', - 'QWSZfHkBs3dBRGh-hNob', - ]) - ); - fooDocs.forEach((d) => { - expect(d.migrationVersion.foo).toEqual('7.13.0'); - expect(d.coreMigrationVersion).toEqual('7.13.0'); - }); - }); - - it('migrates outdated documents when types are re-enabled', async () => { + it('fails the migration if unknown types are found in the source index', async () => { // Start kibana with foo and space types disabled root = createRoot(); esServer = await startES(); await root.preboot(); await root.setup(); - await root.start(); - // Shutdown and start Kibana again with space type registered to ensure space docs get migrated - await root.shutdown(); - root = createRoot(); - await root.preboot(); - const coreSetup = await root.setup(); - coreSetup.savedObjects.registerType({ - name: 'space', - hidden: false, - mappings: { properties: {} }, - namespaceType: 'agnostic', - migrations: { - '6.6.0': (d) => d, - [kibanaVersion]: (d) => d, - }, - }); - await root.start(); - - const client: ElasticsearchClient = esServer.es.getKibanaEsClient(); - const spacesDocsMigrated = await fetchDocs(client, targetIndex, 'space'); - expect(spacesDocsMigrated.map((s) => s.id)).toEqual( - expect.arrayContaining([ - 'space:default', - 'space:first', - 'space:second', - 'space:third', - 'space:forth', - 'space:fifth', - 'space:sixth', - ]) - ); - spacesDocsMigrated.forEach((d) => { - expect(d.migrationVersion.space).toEqual(kibanaVersion); // should be migrated - expect(d.coreMigrationVersion).toEqual(kibanaVersion); - }); - - // Make sure unmigrated foo docs are also still there in an unmigrated state - const fooDocsUnmigrated = await fetchDocs(client, targetIndex, 'foo'); - expect(fooDocsUnmigrated.map((f) => f.id)).toEqual( - expect.arrayContaining([ - 'P2SQfHkBs3dBRGh--No5', - 'QGSZfHkBs3dBRGh-ANoD', - 'QWSZfHkBs3dBRGh-hNob', - ]) - ); - fooDocsUnmigrated.forEach((d) => { - expect(d.migrationVersion.foo).toEqual('7.13.0'); // should still not be migrated - expect(d.coreMigrationVersion).toEqual('7.13.0'); - }); + try { + await root.start(); + expect('should have thrown').toEqual('but it did not'); + } catch (err) { + const errorMessage = err.message; + + expect( + errorMessage.startsWith( + 'Unable to complete saved object migrations for the [.kibana] index: Migration failed because documents ' + + 'were found for unknown saved object types. To proceed with the migration, please delete these documents from the ' + + '".kibana_7.13.0_001" index.' + ) + ).toBeTruthy(); + + const unknownDocs = [ + { type: 'space', id: 'space:default' }, + { type: 'space', id: 'space:first' }, + { type: 'space', id: 'space:second' }, + { type: 'space', id: 'space:third' }, + { type: 'space', id: 'space:forth' }, + { type: 'space', id: 'space:fifth' }, + { type: 'space', id: 'space:sixth' }, + { type: 'foo', id: 'P2SQfHkBs3dBRGh--No5' }, + { type: 'foo', id: 'QGSZfHkBs3dBRGh-ANoD' }, + { type: 'foo', id: 'QWSZfHkBs3dBRGh-hNob' }, + ]; + + unknownDocs.forEach(({ id, type }) => { + expect(errorMessage).toEqual(expect.stringContaining(`- "${id}" (type: "${type}")`)); + }); + + const client = esServer.es.getClient(); + const { body: response } = await client.indices.getSettings( + { index: '.kibana_7.13.0_001' }, + { meta: true } + ); + const settings = response['.kibana_7.13.0_001'] + .settings as estypes.IndicesIndexStatePrefixedSettings; + expect(settings.index).not.toBeUndefined(); + expect(settings.index!.blocks?.write).not.toEqual('true'); + } }); }); @@ -242,26 +135,3 @@ function createRoot() { } ); } - -async function fetchDocs(esClient: ElasticsearchClient, index: string, type: string) { - const { body } = await esClient.search({ - index, - size: 10000, - body: { - query: { - bool: { - should: [ - { - term: { type }, - }, - ], - }, - }, - }, - }); - - return body.hits.hits.map((h) => ({ - ...h._source, - id: h._id, - })); -} diff --git a/src/core/server/saved_objects/migrations/integration_tests/batch_size_bytes.test.ts b/src/core/server/saved_objects/migrations/integration_tests/batch_size_bytes.test.ts index e444a3b1a8bdb..3d8dcad08149c 100644 --- a/src/core/server/saved_objects/migrations/integration_tests/batch_size_bytes.test.ts +++ b/src/core/server/saved_objects/migrations/integration_tests/batch_size_bytes.test.ts @@ -109,7 +109,7 @@ describe('migration v2', () => { await root.preboot(); await root.setup(); await expect(root.start()).rejects.toMatchInlineSnapshot( - `[Error: Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715275 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.]` + `[Error: Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715274 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.]` ); await retryAsync( @@ -122,7 +122,7 @@ describe('migration v2', () => { expect( records.find((rec) => rec.message.startsWith( - `Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715275 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.` + `Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715274 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.` ) ) ).toBeDefined(); @@ -159,7 +159,7 @@ function createRoot(options: { maxBatchSizeBytes?: number }) { }, }, { - oss: true, + oss: false, } ); } diff --git a/src/core/server/saved_objects/migrations/integration_tests/collects_corrupt_docs.test.ts b/src/core/server/saved_objects/migrations/integration_tests/collects_corrupt_docs.test.ts index e330653089c6e..e5f0206b091fd 100644 --- a/src/core/server/saved_objects/migrations/integration_tests/collects_corrupt_docs.test.ts +++ b/src/core/server/saved_objects/migrations/integration_tests/collects_corrupt_docs.test.ts @@ -170,7 +170,7 @@ function createRoot() { }, }, { - oss: true, + oss: false, } ); } diff --git a/src/core/server/saved_objects/migrations/kibana_migrator.ts b/src/core/server/saved_objects/migrations/kibana_migrator.ts index fa1172c0684a7..2e2c4e2c63c04 100644 --- a/src/core/server/saved_objects/migrations/kibana_migrator.ts +++ b/src/core/server/saved_objects/migrations/kibana_migrator.ts @@ -170,7 +170,6 @@ export class KibanaMigrator { transformRawDocs: (rawDocs: SavedObjectsRawDoc[]) => migrateRawDocsSafely({ serializer: this.serializer, - knownTypes: new Set(this.typeRegistry.getAllTypes().map((t) => t.name)), migrateDoc: this.documentMigrator.migrateAndConvert, rawDocs, }), diff --git a/src/core/server/saved_objects/migrations/model/extract_errors.test.ts b/src/core/server/saved_objects/migrations/model/extract_errors.test.ts index c2daadcd342ac..a028c40ca6597 100644 --- a/src/core/server/saved_objects/migrations/model/extract_errors.test.ts +++ b/src/core/server/saved_objects/migrations/model/extract_errors.test.ts @@ -25,7 +25,7 @@ describe('extractUnknownDocFailureReason', () => { '.kibana_15' ) ).toMatchInlineSnapshot(` - "Upgrades will fail for 8.0+ because documents were found for unknown saved object types. To ensure that upgrades will succeed in the future, either re-enable plugins or delete these documents from the \\".kibana_15\\" index after the current upgrade completes. + "Migration failed because documents were found for unknown saved object types. To proceed with the migration, please delete these documents from the \\".kibana_15\\" index. The documents with unknown types are: - \\"unknownType:12\\" (type: \\"unknownType\\") - \\"anotherUnknownType:42\\" (type: \\"anotherUnknownType\\") diff --git a/src/core/server/saved_objects/migrations/model/extract_errors.ts b/src/core/server/saved_objects/migrations/model/extract_errors.ts index 3dabb09043376..95d10603caa80 100644 --- a/src/core/server/saved_objects/migrations/model/extract_errors.ts +++ b/src/core/server/saved_objects/migrations/model/extract_errors.ts @@ -38,16 +38,15 @@ export function extractTransformFailuresReason( export function extractUnknownDocFailureReason( unknownDocs: CheckForUnknownDocsFoundDoc[], - targetIndex: string + sourceIndex: string ): string { return ( - `Upgrades will fail for 8.0+ because documents were found for unknown saved object types. ` + - `To ensure that upgrades will succeed in the future, either re-enable plugins or delete these documents from the ` + - `"${targetIndex}" index after the current upgrade completes.\n` + + `Migration failed because documents were found for unknown saved object types. ` + + `To proceed with the migration, please delete these documents from the "${sourceIndex}" index.\n` + `The documents with unknown types are:\n` + unknownDocs.map((doc) => `- "${doc.id}" (type: "${doc.type}")\n`).join('') + `You can delete them using the following command:\n` + - `curl -X POST "{elasticsearch}/${targetIndex}/_bulk?pretty" -H 'Content-Type: application/json' -d'\n` + + `curl -X POST "{elasticsearch}/${sourceIndex}/_bulk?pretty" -H 'Content-Type: application/json' -d'\n` + unknownDocs.map((doc) => `{ "delete" : { "_id" : "${doc.id}" } }\n`).join('') + `'` ); diff --git a/src/core/server/saved_objects/migrations/model/model.test.ts b/src/core/server/saved_objects/migrations/model/model.test.ts index 7cd5f63640d1d..5ca6713ca163f 100644 --- a/src/core/server/saved_objects/migrations/model/model.test.ts +++ b/src/core/server/saved_objects/migrations/model/model.test.ts @@ -717,7 +717,7 @@ describe('migrations v2 model', () => { }, } as const; - test('CHECK_UNKNOWN_DOCUMENTS -> SET_SOURCE_WRITE_BLOCK if action succeeds and no unknown docs are found', () => { + test('CHECK_UNKNOWN_DOCUMENTS -> SET_SOURCE_WRITE_BLOCK if action succeeds', () => { const checkUnknownDocumentsSourceState: CheckUnknownDocumentsState = { ...baseState, controlState: 'CHECK_UNKNOWN_DOCUMENTS', @@ -725,7 +725,7 @@ describe('migrations v2 model', () => { sourceIndexMappings: mappingsWithUnknownType, }; - const res: ResponseType<'CHECK_UNKNOWN_DOCUMENTS'> = Either.right({ unknownDocs: [] }); + const res: ResponseType<'CHECK_UNKNOWN_DOCUMENTS'> = Either.right({}); const newState = model(checkUnknownDocumentsSourceState, res); expect(newState.controlState).toEqual('SET_SOURCE_WRITE_BLOCK'); @@ -765,7 +765,7 @@ describe('migrations v2 model', () => { expect(newState.logs).toEqual([]); }); - test('CHECK_UNKNOWN_DOCUMENTS -> SET_SOURCE_WRITE_BLOCK and adds log if action succeeds and unknown docs were found', () => { + test('CHECK_UNKNOWN_DOCUMENTS -> FATAL if action fails and unknown docs were found', () => { const checkUnknownDocumentsSourceState: CheckUnknownDocumentsState = { ...baseState, controlState: 'CHECK_UNKNOWN_DOCUMENTS', @@ -773,51 +773,20 @@ describe('migrations v2 model', () => { sourceIndexMappings: mappingsWithUnknownType, }; - const res: ResponseType<'CHECK_UNKNOWN_DOCUMENTS'> = Either.right({ + const res: ResponseType<'CHECK_UNKNOWN_DOCUMENTS'> = Either.left({ + type: 'unknown_docs_found', unknownDocs: [ { id: 'dashboard:12', type: 'dashboard' }, { id: 'foo:17', type: 'foo' }, ], }); const newState = model(checkUnknownDocumentsSourceState, res); - expect(newState.controlState).toEqual('SET_SOURCE_WRITE_BLOCK'); + expect(newState.controlState).toEqual('FATAL'); expect(newState).toMatchObject({ - controlState: 'SET_SOURCE_WRITE_BLOCK', - sourceIndex: Option.some('.kibana_3'), - targetIndex: '.kibana_7.11.0_001', - }); - - // This snapshot asserts that we disable the unknown saved object - // type. Because it's mappings are disabled, we also don't copy the - // `_meta.migrationMappingPropertyHashes` for the disabled type. - expect(newState.targetIndexMappings).toMatchInlineSnapshot(` - Object { - "_meta": Object { - "migrationMappingPropertyHashes": Object { - "new_saved_object_type": "4a11183eee21e6fbad864f7a30b39ad0", - }, - }, - "properties": Object { - "disabled_saved_object_type": Object { - "dynamic": false, - "properties": Object {}, - }, - "new_saved_object_type": Object { - "properties": Object { - "value": Object { - "type": "text", - }, - }, - }, - }, - } - `); - - expect(newState.logs[0]).toMatchObject({ - level: 'warning', - message: expect.stringContaining( - 'Upgrades will fail for 8.0+ because documents were found for unknown saved object types' + controlState: 'FATAL', + reason: expect.stringContaining( + 'Migration failed because documents were found for unknown saved object types' ), }); }); diff --git a/src/core/server/saved_objects/migrations/model/model.ts b/src/core/server/saved_objects/migrations/model/model.ts index 522a43a737cb7..e9efb72bca6f5 100644 --- a/src/core/server/saved_objects/migrations/model/model.ts +++ b/src/core/server/saved_objects/migrations/model/model.ts @@ -11,7 +11,6 @@ import * as Option from 'fp-ts/lib/Option'; import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { AliasAction, isLeftTypeof } from '../actions'; -import { MigrationLog } from '../types'; import { AllActionStates, State } from '../state'; import type { ResponseType } from '../next'; import { disableUnknownTypeMappingFields } from '../core'; @@ -352,24 +351,17 @@ export const model = (currentState: State, resW: ResponseType): { add: { index: target, alias: stateP.versionAlias } }, { remove_index: { index: stateP.tempIndex } }, ]), - - logs: [ - ...stateP.logs, - ...(res.right.unknownDocs.length > 0 - ? ([ - { - level: 'warning', - message: `CHECK_UNKNOWN_DOCUMENTS ${extractUnknownDocFailureReason( - res.right.unknownDocs, - target - )}`, - }, - ] as MigrationLog[]) - : []), - ], }; } else { - return throwBadResponse(stateP, res); + if (isLeftTypeof(res.left, 'unknown_docs_found')) { + return { + ...stateP, + controlState: 'FATAL', + reason: extractUnknownDocFailureReason(res.left.unknownDocs, stateP.sourceIndex.value), + }; + } else { + return throwBadResponse(stateP, res.left); + } } } else if (stateP.controlState === 'SET_SOURCE_WRITE_BLOCK') { const res = resW as ExcludeRetryableEsError>; diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index d538690fb1920..9be58f1b71861 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -1825,7 +1825,7 @@ export class SavedObjectsRepository { index: this.getIndexForType(type), refresh, require_alias: true, - _source: 'true', + _source: true, body: { script: { source: ` diff --git a/src/dev/bazel/index.bzl b/src/dev/bazel/index.bzl index 83d6361ff95f7..fcd4212bd5329 100644 --- a/src/dev/bazel/index.bzl +++ b/src/dev/bazel/index.bzl @@ -6,14 +6,16 @@ # Side Public License, v 1. # -"""Public API interface for Bazel custom rules. +"""Public API interface for all Kibana Bazel custom rules. Please do not import from any other files when looking to use a custom rule """ load("//src/dev/bazel:jsts_transpiler.bzl", _jsts_transpiler = "jsts_transpiler") -load("//src/dev/bazel:ts_project.bzl", _ts_project = "ts_project") load("//src/dev/bazel:pkg_npm.bzl", _pkg_npm = "pkg_npm") +load("//src/dev/bazel/pkg_npm_types:index.bzl", _pkg_npm_types = "pkg_npm_types") +load("//src/dev/bazel:ts_project.bzl", _ts_project = "ts_project") jsts_transpiler = _jsts_transpiler pkg_npm = _pkg_npm +pkg_npm_types = _pkg_npm_types ts_project = _ts_project diff --git a/src/dev/bazel/pkg_npm_types/BUILD.bazel b/src/dev/bazel/pkg_npm_types/BUILD.bazel new file mode 100644 index 0000000000000..f30d0f8cb8324 --- /dev/null +++ b/src/dev/bazel/pkg_npm_types/BUILD.bazel @@ -0,0 +1,28 @@ +package(default_visibility = ["//visibility:public"]) + +load("@build_bazel_rules_nodejs//internal/node:node.bzl", "nodejs_binary") + +filegroup( + name = "packager_all_files", + srcs = glob([ + "packager/*", + ]), +) + +exports_files( + [ + "package_json.mustache", + ], + visibility = ["//visibility:public"] +) + +nodejs_binary( + name = "_packager", + data = [ + "@npm//@bazel/typescript", + "@npm//@microsoft/api-extractor", + "@npm//mustache", + ":packager_all_files" + ], + entry_point = ":packager/index.js", +) diff --git a/src/dev/bazel/pkg_npm_types/index.bzl b/src/dev/bazel/pkg_npm_types/index.bzl new file mode 100644 index 0000000000000..578ecdd885d15 --- /dev/null +++ b/src/dev/bazel/pkg_npm_types/index.bzl @@ -0,0 +1,15 @@ +# +# 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. +# + +"""Public API interface for pkg_npm_types rule. +Please do not import from any other files when looking to this rule +""" + +load(":pkg_npm_types.bzl", _pkg_npm_types = "pkg_npm_types") + +pkg_npm_types = _pkg_npm_types diff --git a/src/dev/bazel/pkg_npm_types/package_json.mustache b/src/dev/bazel/pkg_npm_types/package_json.mustache new file mode 100644 index 0000000000000..2229345252e3f --- /dev/null +++ b/src/dev/bazel/pkg_npm_types/package_json.mustache @@ -0,0 +1,8 @@ +{ + "name": "{{{NAME}}}", + "description": "Generated by Bazel", + "types": "./index.d.ts", + "private": true, + "license": "MIT", + "version": "1.1.0" +} diff --git a/src/dev/bazel/pkg_npm_types/packager/create_api_extraction.js b/src/dev/bazel/pkg_npm_types/packager/create_api_extraction.js new file mode 100644 index 0000000000000..d5f7e0c33ff1c --- /dev/null +++ b/src/dev/bazel/pkg_npm_types/packager/create_api_extraction.js @@ -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. + */ + +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +const { format, parseTsconfig } = require('@bazel/typescript'); +const { Extractor, ExtractorConfig } = require('@microsoft/api-extractor'); +const fs = require('fs'); +const path = require('path'); + +function createApiExtraction( + tsConfig, + entryPoint, + dtsBundleOut, + apiReviewFolder, + acceptApiUpdates = false +) { + const [parsedConfig, errors] = parseTsconfig(tsConfig); + if (errors && errors.length) { + console.error(format('', errors)); + return 1; + } + const pkgJson = path.resolve(path.dirname(entryPoint), 'package.json'); + if (!fs.existsSync(pkgJson)) { + fs.writeFileSync( + pkgJson, + JSON.stringify({ + name: 'GENERATED-BY-BAZEL', + description: 'This is a dummy package.json as API Extractor always requires one.', + types: './index.d.ts', + private: true, + license: 'SSPL-1.0 OR Elastic License 2.0', + version: '1.0.0', + }) + ); + } + // API extractor doesn't always support the version of TypeScript used in the repo + // example: at the moment it is not compatable with 3.2 + // to use the internal TypeScript we shall not create a program but rather pass a parsed tsConfig. + const parsedTsConfig = parsedConfig.config; + const extractorOptions = { + localBuild: acceptApiUpdates, + }; + const configObject = { + compiler: { + overrideTsconfig: parsedTsConfig, + }, + projectFolder: path.resolve(path.dirname(tsConfig)), + mainEntryPointFilePath: path.resolve(entryPoint), + apiReport: { + enabled: !!apiReviewFolder, + // TODO(alan-agius4): remove this folder name when the below issue is solved upstream + // See: https://github.com/microsoft/web-build-tools/issues/1470 + reportFileName: (apiReviewFolder && path.resolve(apiReviewFolder)) || 'invalid', + }, + docModel: { + enabled: false, + }, + dtsRollup: { + enabled: !!dtsBundleOut, + untrimmedFilePath: dtsBundleOut && path.resolve(dtsBundleOut), + }, + tsdocMetadata: { + enabled: false, + }, + }; + const options = { + configObject, + packageJson: undefined, + packageJsonFullPath: pkgJson, + configObjectFullPath: undefined, + }; + const extractorConfig = ExtractorConfig.prepare(options); + const { succeeded } = Extractor.invoke(extractorConfig, extractorOptions); + // API extractor errors are emitted by it's logger. + return succeeded ? 0 : 1; +} + +module.exports.createApiExtraction = createApiExtraction; diff --git a/src/dev/bazel/pkg_npm_types/packager/generate_package_json.js b/src/dev/bazel/pkg_npm_types/packager/generate_package_json.js new file mode 100644 index 0000000000000..d4a478a262e5b --- /dev/null +++ b/src/dev/bazel/pkg_npm_types/packager/generate_package_json.js @@ -0,0 +1,43 @@ +/* + * 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. + */ + +const fs = require('fs'); +const Mustache = require('mustache'); +const path = require('path'); + +function generatePackageJson(outputBasePath, packageJsonTemplatePath, rawPackageJsonTemplateArgs) { + const packageJsonTemplateArgsInTuples = rawPackageJsonTemplateArgs.reduce( + (a, v) => { + const lastTupleIdx = a.length - 1; + const lastTupleSize = a[lastTupleIdx].length; + + if (lastTupleSize < 2) { + a[lastTupleIdx].push(v); + + return a; + } + + return a.push([v]); + }, + [[]] + ); + const packageJsonTemplateArgs = Object.fromEntries(new Map(packageJsonTemplateArgsInTuples)); + + try { + const template = fs.readFileSync(packageJsonTemplatePath); + const renderedTemplate = Mustache.render(template.toString(), packageJsonTemplateArgs); + fs.writeFileSync(path.resolve(outputBasePath, 'package.json'), renderedTemplate); + } catch (e) { + console.error(e); + return 1; + } + + return 0; +} + +module.exports.generatePackageJson = generatePackageJson; diff --git a/src/dev/bazel/pkg_npm_types/packager/index.js b/src/dev/bazel/pkg_npm_types/packager/index.js new file mode 100644 index 0000000000000..cda299a99d76f --- /dev/null +++ b/src/dev/bazel/pkg_npm_types/packager/index.js @@ -0,0 +1,46 @@ +/* + * 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. + */ + +const { createApiExtraction } = require('./create_api_extraction'); +const { generatePackageJson } = require('./generate_package_json'); +const path = require('path'); + +const DEBUG = false; + +if (require.main === module) { + if (DEBUG) { + console.error(` +pkg_npm_types packager: running with + cwd: ${process.cwd()} + argv: + ${process.argv.join('\n ')} + `); + } + + // layout args + const [ + outputBasePath, + packageJsonTemplatePath, + stringifiedPackageJsonTemplateArgs, + tsConfig, + entryPoint, + ] = process.argv.slice(2); + const dtsBundleOutput = path.resolve(outputBasePath, 'index.d.ts'); + + // generate pkg json output + const generatePackageJsonRValue = generatePackageJson( + outputBasePath, + packageJsonTemplatePath, + stringifiedPackageJsonTemplateArgs.split(',') + ); + // create api extraction output + const createApiExtractionRValue = createApiExtraction(tsConfig, entryPoint, dtsBundleOutput); + + // setup correct exit code + process.exitCode = generatePackageJsonRValue || createApiExtractionRValue; +} diff --git a/src/dev/bazel/pkg_npm_types/pkg_npm_types.bzl b/src/dev/bazel/pkg_npm_types/pkg_npm_types.bzl new file mode 100644 index 0000000000000..a40624d31e38b --- /dev/null +++ b/src/dev/bazel/pkg_npm_types/pkg_npm_types.bzl @@ -0,0 +1,148 @@ +# +# 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. +# + +load("@npm//@bazel/typescript/internal:ts_config.bzl", "TsConfigInfo") +load("@build_bazel_rules_nodejs//:providers.bzl", "run_node", "LinkablePackageInfo", "declaration_info") +load("@build_bazel_rules_nodejs//internal/linker:link_node_modules.bzl", "module_mappings_aspect") + + +#### TODO +# Implement a way to produce source maps for api extractor +# summarised types as referenced at (https://github.com/microsoft/rushstack/issues/1886#issuecomment-933997910) + +def _deps_inputs(ctx): + """Returns all transitively referenced files on deps """ + deps_files_depsets = [] + for dep in ctx.attr.deps: + # Collect whatever is in the "data" + deps_files_depsets.append(dep.data_runfiles.files) + + # Only collect DefaultInfo files (not transitive) + deps_files_depsets.append(dep.files) + + deps_files = depset(transitive = deps_files_depsets).to_list() + return deps_files + +def _calculate_entrypoint_path(ctx): + return _join(ctx.bin_dir.path, ctx.label.package, _get_types_outdir_name(ctx), ctx.attr.entrypoint_name) + +def _get_types_outdir_name(ctx): + base_out_folder = _join(ctx.bin_dir.path, ctx.label.package) + type_dep_path = ctx.files.deps[0].path + type_dep_path_without_base_out = type_dep_path.replace(base_out_folder + "/", "", 1) + types_outdir_name = type_dep_path_without_base_out.split("/")[0] + return types_outdir_name + +def _join(*elements): + segments = [f for f in elements if f] + if len(segments): + return "/".join(segments) + return "." + +def _tsconfig_inputs(ctx): + """Returns all transitively referenced tsconfig files from "tsconfig" """ + all_inputs = [] + if TsConfigInfo in ctx.attr.tsconfig: + all_inputs.extend(ctx.attr.tsconfig[TsConfigInfo].deps) + else: + all_inputs.append(ctx.file.tsconfig) + return all_inputs + +def _pkg_npm_types_impl(ctx): + # input declarations + deps_inputs = _deps_inputs(ctx) + tsconfig_inputs = _tsconfig_inputs(ctx) + inputs = ctx.files.srcs[:] + inputs.extend(tsconfig_inputs) + inputs.extend(deps_inputs) + inputs.append(ctx.file._generated_package_json_template) + + # output dir declaration + package_path = ctx.label.package + package_dir = ctx.actions.declare_directory(ctx.label.name) + outputs = [package_dir] + + # gathering template args + template_args = [ + "NAME", ctx.attr.package_name + ] + + # layout api extractor arguments + extractor_args = ctx.actions.args() + + ## general args layout + ### [0] = base output dir + ### [1] = generated package json template input file path + ### [2] = stringified template args + ### [3] = tsconfig input file path + ### [4] = entry point from provided types to summarise + extractor_args.add(package_dir.path) + extractor_args.add(ctx.file._generated_package_json_template.path) + extractor_args.add_joined(template_args, join_with = ",", omit_if_empty = False) + extractor_args.add(tsconfig_inputs[0]) + extractor_args.add(_calculate_entrypoint_path(ctx)) + + run_node( + ctx, + inputs = inputs, + arguments = [extractor_args], + outputs = outputs, + mnemonic = "AssembleNpmTypesPackage", + progress_message = "Assembling npm types package %s" % package_dir.short_path, + executable = "_packager", + ) + + # this is a tree artifact, so correctly build the return + package_dir_depset = depset([package_dir]) + + return [ + DefaultInfo( + files = package_dir_depset, + runfiles = ctx.runfiles([package_dir]), + ), + declaration_info( + declarations = depset([package_dir]) + ), + LinkablePackageInfo( + package_name = ctx.attr.package_name, + package_path = "", + path = package_dir.path, + files = package_dir_depset, + ) + ] + +pkg_npm_types = rule( + implementation = _pkg_npm_types_impl, + attrs = { + "deps": attr.label_list( + doc = """Other targets which are the base types to summarise from""", + allow_files = True, + aspects = [module_mappings_aspect], + ), + "entrypoint_name": attr.string( + doc = """Entrypoint name of the types files group to summarise""", + default = "index.d.ts", + ), + "package_name": attr.string(), + "srcs": attr.label_list( + doc = """Files inside this directory which are inputs for the types to summarise.""", + allow_files = True, + ), + "tsconfig": attr.label(mandatory = True, allow_single_file = [".json"]), + "_packager": attr.label( + doc = "Target that executes the npm types package assembler binary", + executable = True, + cfg = "host", + default = Label("//src/dev/bazel/pkg_npm_types:_packager"), + ), + "_generated_package_json_template": attr.label( + allow_single_file = True, + default = "package_json.mustache", + ), + }, +) diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index a9a54bf6794b2..8ce8e2cb40700 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -199,6 +199,7 @@ kibana_vars=( xpack.alerting.invalidateApiKeysTask.interval xpack.alerting.invalidateApiKeysTask.removalDelay xpack.alerting.defaultRuleTaskTimeout + xpack.alerting.cancelAlertsOnRuleTimeout xpack.alerts.healthCheck.interval xpack.alerts.invalidateApiKeysTask.interval xpack.alerts.invalidateApiKeysTask.removalDelay diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 700c011d3e7e7..f5c1755b228e9 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -57,7 +57,6 @@ export const IGNORE_FILE_GLOBS = [ // TODO fix file names in APM to remove these 'x-pack/plugins/apm/public/**/*', - 'x-pack/plugins/apm/scripts/**/*', 'x-pack/plugins/maps/server/fonts/**/*', diff --git a/src/plugins/discover/public/application/discover_router.tsx b/src/plugins/discover/public/application/discover_router.tsx index 6f88c28b52bf9..66ad0cccd03c7 100644 --- a/src/plugins/discover/public/application/discover_router.tsx +++ b/src/plugins/discover/public/application/discover_router.tsx @@ -9,6 +9,7 @@ import { Redirect, Route, Router, Switch } from 'react-router-dom'; import React from 'react'; import { History } from 'history'; +import { EuiErrorBoundary } from '@elastic/eui'; import { KibanaContextProvider } from '../../../kibana_react/public'; import { ContextAppRoute } from './context'; import { SingleDocRoute } from './doc'; @@ -25,29 +26,31 @@ export const discoverRouter = (services: DiscoverServices, history: History) => return ( - - - } - /> - ( - - )} - /> - } - /> - } /> - } /> - - - + + + + } + /> + ( + + )} + /> + } + /> + } /> + } /> + + + + ); }; diff --git a/src/plugins/home/common/instruction_variant.ts b/src/plugins/home/common/instruction_variant.ts index 66c841cdc8b56..f19aa7849586b 100644 --- a/src/plugins/home/common/instruction_variant.ts +++ b/src/plugins/home/common/instruction_variant.ts @@ -32,8 +32,8 @@ export const INSTRUCTION_VARIANT = { const DISPLAY_MAP = { [INSTRUCTION_VARIANT.ESC]: 'Elastic Cloud', [INSTRUCTION_VARIANT.OSX]: 'macOS', - [INSTRUCTION_VARIANT.DEB]: 'DEB', - [INSTRUCTION_VARIANT.RPM]: 'RPM', + [INSTRUCTION_VARIANT.DEB]: 'Linux DEB', + [INSTRUCTION_VARIANT.RPM]: 'Linux RPM', [INSTRUCTION_VARIANT.DOCKER]: 'Docker', [INSTRUCTION_VARIANT.WINDOWS]: 'Windows', [INSTRUCTION_VARIANT.NODE]: 'Node.js', diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx index 75f36e20eb084..ed79135f1f09f 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx @@ -74,6 +74,8 @@ interface TableState { activeAction?: SavedObjectsManagementAction; } +const MAX_PAGINATED_ITEM = 10000; + export class Table extends PureComponent { state: TableState = { isSearchTextValid: true, @@ -150,10 +152,12 @@ export class Table extends PureComponent { allowedTypes, } = this.props; + const cappedTotalItemCount = Math.min(totalItemCount, MAX_PAGINATED_ITEM); + const pagination = { pageIndex, pageSize, - totalItemCount, + totalItemCount: cappedTotalItemCount, pageSizeOptions: [5, 10, 20, 50], }; @@ -321,6 +325,7 @@ export class Table extends PureComponent { ); const activeActionContents = this.state.activeAction?.render() ?? null; + const exceededResultCount = totalItemCount > MAX_PAGINATED_ITEM; return ( @@ -392,6 +397,18 @@ export class Table extends PureComponent { /> {queryParseError} + {exceededResultCount && ( + <> + + + + + + )}
{ savedObjectsClient = savedObjectsClientMock.create(); }); - it('calls the saved object client with the correct parameters', async () => { + it('calls `client.createPointInTimeFinder` with the correct parameters', async () => { + const query: SavedObjectsFindOptions = { + type: ['some-type', 'another-type'], + }; + + savedObjectsClient.find.mockResolvedValue({ + saved_objects: [], + total: 1, + per_page: 20, + page: 1, + }); + + await findAll(savedObjectsClient, query); + + expect(savedObjectsClient.createPointInTimeFinder).toHaveBeenCalledTimes(1); + expect(savedObjectsClient.createPointInTimeFinder).toHaveBeenCalledWith(query); + }); + + it('returns the results from the PIT search', async () => { const query: SavedObjectsFindOptions = { type: ['some-type', 'another-type'], }; @@ -41,45 +59,40 @@ describe('findAll', () => { const results = await findAll(savedObjectsClient, query); expect(savedObjectsClient.find).toHaveBeenCalledTimes(1); - expect(savedObjectsClient.find).toHaveBeenCalledWith({ - ...query, - page: 1, - }); + expect(savedObjectsClient.find).toHaveBeenCalledWith( + expect.objectContaining({ + ...query, + }) + ); expect(results).toEqual([createObj(1), createObj(2)]); }); - it('recursively call find until all objects are fetched', async () => { + it('works when the PIT search returns multiple batches', async () => { const query: SavedObjectsFindOptions = { type: ['some-type', 'another-type'], + perPage: 2, }; const objPerPage = 2; - savedObjectsClient.find.mockImplementation(({ page }) => { - const firstInPage = (page! - 1) * objPerPage + 1; + let callCount = 0; + savedObjectsClient.find.mockImplementation(({}) => { + callCount++; + const firstInPage = (callCount - 1) * objPerPage + 1; return Promise.resolve({ - saved_objects: [createObj(firstInPage), createObj(firstInPage + 1)], + saved_objects: + callCount > 3 + ? [createObj(firstInPage)] + : [createObj(firstInPage), createObj(firstInPage + 1)], total: objPerPage * 3, per_page: objPerPage, - page: page!, + page: callCount!, }); }); const results = await findAll(savedObjectsClient, query); - expect(savedObjectsClient.find).toHaveBeenCalledTimes(3); - expect(savedObjectsClient.find).toHaveBeenCalledWith({ - ...query, - page: 1, - }); - expect(savedObjectsClient.find).toHaveBeenCalledWith({ - ...query, - page: 2, - }); - expect(savedObjectsClient.find).toHaveBeenCalledWith({ - ...query, - page: 3, - }); - expect(results).toEqual(times(6, (num) => createObj(num + 1))); + expect(savedObjectsClient.find).toHaveBeenCalledTimes(4); + expect(results).toEqual(times(7, (num) => createObj(num + 1))); }); }); diff --git a/src/plugins/saved_objects_management/server/lib/find_all.ts b/src/plugins/saved_objects_management/server/lib/find_all.ts index 08681758752be..8d908234c6961 100644 --- a/src/plugins/saved_objects_management/server/lib/find_all.ts +++ b/src/plugins/saved_objects_management/server/lib/find_all.ts @@ -6,30 +6,20 @@ * Side Public License, v 1. */ -import { SavedObjectsClientContract, SavedObject, SavedObjectsFindOptions } from 'src/core/server'; +import { + SavedObjectsClientContract, + SavedObject, + SavedObjectsCreatePointInTimeFinderOptions, +} from 'src/core/server'; export const findAll = async ( client: SavedObjectsClientContract, - findOptions: SavedObjectsFindOptions + findOptions: SavedObjectsCreatePointInTimeFinderOptions ): Promise => { - return recursiveFind(client, findOptions, 1, []); -}; - -const recursiveFind = async ( - client: SavedObjectsClientContract, - findOptions: SavedObjectsFindOptions, - page: number, - allObjects: SavedObject[] -): Promise => { - const objects = await client.find({ - ...findOptions, - page, - }); - - allObjects.push(...objects.saved_objects); - if (allObjects.length < objects.total) { - return recursiveFind(client, findOptions, page + 1, allObjects); + const finder = client.createPointInTimeFinder(findOptions); + const results: SavedObject[] = []; + for await (const result of finder.find()) { + results.push(...result.saved_objects); } - - return allObjects; + return results; }; diff --git a/src/plugins/saved_objects_management/server/routes/index.test.ts b/src/plugins/saved_objects_management/server/routes/index.test.ts index 3ec6afe1c0bbc..9c3d235a83e36 100644 --- a/src/plugins/saved_objects_management/server/routes/index.test.ts +++ b/src/plugins/saved_objects_management/server/routes/index.test.ts @@ -24,7 +24,7 @@ describe('registerRoutes', () => { expect(httpSetup.createRouter).toHaveBeenCalledTimes(1); expect(router.get).toHaveBeenCalledTimes(3); - expect(router.post).toHaveBeenCalledTimes(3); + expect(router.post).toHaveBeenCalledTimes(2); expect(router.get).toHaveBeenCalledWith( expect.objectContaining({ @@ -56,11 +56,5 @@ describe('registerRoutes', () => { }), expect.any(Function) ); - expect(router.post).toHaveBeenCalledWith( - expect.objectContaining({ - path: '/api/kibana/management/saved_objects/scroll/export', - }), - expect.any(Function) - ); }); }); diff --git a/src/plugins/saved_objects_management/server/routes/index.ts b/src/plugins/saved_objects_management/server/routes/index.ts index b5b461575604d..5370088d27977 100644 --- a/src/plugins/saved_objects_management/server/routes/index.ts +++ b/src/plugins/saved_objects_management/server/routes/index.ts @@ -11,7 +11,6 @@ import { ISavedObjectsManagement } from '../services'; import { registerFindRoute } from './find'; import { registerBulkGetRoute } from './bulk_get'; import { registerScrollForCountRoute } from './scroll_count'; -import { registerScrollForExportRoute } from './scroll_export'; import { registerRelationshipsRoute } from './relationships'; import { registerGetAllowedTypesRoute } from './get_allowed_types'; @@ -25,7 +24,6 @@ export function registerRoutes({ http, managementServicePromise }: RegisterRoute registerFindRoute(router, managementServicePromise); registerBulkGetRoute(router, managementServicePromise); registerScrollForCountRoute(router); - registerScrollForExportRoute(router); registerRelationshipsRoute(router, managementServicePromise); registerGetAllowedTypesRoute(router); } diff --git a/src/plugins/saved_objects_management/server/routes/scroll_count.ts b/src/plugins/saved_objects_management/server/routes/scroll_count.ts index 89a895adf6008..9a021be4c0cde 100644 --- a/src/plugins/saved_objects_management/server/routes/scroll_count.ts +++ b/src/plugins/saved_objects_management/server/routes/scroll_count.ts @@ -7,7 +7,7 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter, SavedObjectsFindOptions } from 'src/core/server'; +import { IRouter, SavedObjectsCreatePointInTimeFinderOptions } from 'src/core/server'; import { chain } from 'lodash'; import { findAll } from '../lib'; @@ -42,7 +42,7 @@ export const registerScrollForCountRoute = (router: IRouter) => { .value(); const client = getClient({ includedHiddenTypes }); - const findOptions: SavedObjectsFindOptions = { + const findOptions: SavedObjectsCreatePointInTimeFinderOptions = { type: typesToInclude, perPage: 1000, }; diff --git a/src/plugins/saved_objects_management/server/routes/scroll_export.ts b/src/plugins/saved_objects_management/server/routes/scroll_export.ts deleted file mode 100644 index 8d11437af661b..0000000000000 --- a/src/plugins/saved_objects_management/server/routes/scroll_export.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; -import { chain } from 'lodash'; -import { findAll } from '../lib'; - -export const registerScrollForExportRoute = (router: IRouter) => { - router.post( - { - path: '/api/kibana/management/saved_objects/scroll/export', - validate: { - body: schema.object({ - typesToInclude: schema.arrayOf(schema.string()), - }), - }, - }, - router.handleLegacyErrors(async (context, req, res) => { - const { typesToInclude } = req.body; - const { getClient, typeRegistry } = context.core.savedObjects; - const includedHiddenTypes = chain(typesToInclude) - .uniq() - .filter( - (type) => typeRegistry.isHidden(type) && typeRegistry.isImportableAndExportable(type) - ) - .value(); - - const client = getClient({ includedHiddenTypes }); - - const objects = await findAll(client, { - perPage: 1000, - type: typesToInclude, - }); - - return res.ok({ - body: objects.map((hit) => { - return { - _id: hit.id, - _source: hit.attributes, - _meta: { - savedObjectVersion: 2, - }, - _migrationVersion: hit.migrationVersion, - _references: hit.references || [], - }; - }), - }); - }) - ); -}; diff --git a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap index bc66c1940ac72..0f3b0e8ef5576 100644 --- a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap @@ -61,6 +61,7 @@ exports[`CategoryAxisPanel component should init with the default set of props 1 "truncate": 0, } } + disableSingleLayerAxisControls={false} setAxisLabel={[Function]} /> diff --git a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/index.test.tsx.snap b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/index.test.tsx.snap index 05e2532073eaf..620e361946443 100644 --- a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/index.test.tsx.snap +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/index.test.tsx.snap @@ -154,6 +154,7 @@ exports[`MetricsAxisOptions component should init with the default set of props } onPositionChanged={[Function]} setCategoryAxis={[Function]} + useMultiLayerAxis={false} /> `; diff --git a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap index 175ec043cdbea..d6e95dfdb87b9 100644 --- a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap @@ -27,7 +27,6 @@ exports[`LabelOptions component should init with the default set of props 1`] = /> - + + + - + + + diff --git a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/category_axis_panel.test.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/category_axis_panel.test.tsx index 066f053d4e186..9d18200ae7eb9 100644 --- a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/category_axis_panel.test.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/category_axis_panel.test.tsx @@ -11,6 +11,7 @@ import { shallow } from 'enzyme'; import { CategoryAxisPanel, CategoryAxisPanelProps } from './category_axis_panel'; import { CategoryAxis } from '../../../../types'; import { LabelOptions } from './label_options'; +import { TruncateLabelsOption } from '../../common'; import { categoryAxis } from './mocks'; import { Position } from '@elastic/charts'; @@ -29,6 +30,7 @@ describe('CategoryAxisPanel component', () => { axis, onPositionChanged, setCategoryAxis, + useMultiLayerAxis: false, }; }); @@ -55,4 +57,16 @@ describe('CategoryAxisPanel component', () => { expect(setCategoryAxis).toHaveBeenLastCalledWith({ ...axis, position: value }); expect(onPositionChanged).toBeCalledWith(value); }); + + it('should disable label options with multilayer axis', () => { + const comp = shallow(); + const labelOptions = comp.find(LabelOptions).dive(); + const rotateLabelsOption = labelOptions.find({ paramName: 'rotate' }); + const filterLabelOption = labelOptions.find({ paramName: 'filter' }); + const truncateLabelOption = labelOptions.find(TruncateLabelsOption); + + expect(rotateLabelsOption.prop('disabled')).toBe(true); + expect(filterLabelOption.prop('disabled')).toBe(true); + expect(truncateLabelOption.prop('disabled')).toBe(true); + }); }); diff --git a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx index ee5cc950ff66b..c3d1d2dcfee4d 100644 --- a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx @@ -25,9 +25,15 @@ export interface CategoryAxisPanelProps { axis: CategoryAxis; onPositionChanged: (position: Position) => void; setCategoryAxis: (value: CategoryAxis) => void; + useMultiLayerAxis: boolean; } -function CategoryAxisPanel({ axis, onPositionChanged, setCategoryAxis }: CategoryAxisPanelProps) { +function CategoryAxisPanel({ + axis, + onPositionChanged, + setCategoryAxis, + useMultiLayerAxis, +}: CategoryAxisPanelProps) { const setAxis = useCallback( (paramName: T, value: CategoryAxis[T]) => { const updatedAxis = { @@ -95,6 +101,7 @@ function CategoryAxisPanel({ axis, onPositionChanged, setCategoryAxis }: Categor axisLabels={axis.labels} axisFilterCheckboxName={`xAxisFilterLabelsCheckbox${axis.id}`} setAxisLabel={setAxisLabel} + disableSingleLayerAxisControls={useMultiLayerAxis} /> )} diff --git a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/index.test.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/index.test.tsx index 4996018d21120..6a32b1f75016b 100644 --- a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/index.test.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/index.test.tsx @@ -31,6 +31,11 @@ jest.mock('./category_axis_panel', () => ({ jest.mock('./value_axes_panel', () => ({ ValueAxesPanel: () => 'ValueAxesPanel', })); +jest.mock('../../../../services', () => ({ + getUISettings: jest.fn(() => ({ + get: jest.fn((key: string, defaultOverride?: unknown) => defaultOverride), + })), +})); const SERIES_PARAMS = 'seriesParams'; const VALUE_AXES = 'valueAxes'; diff --git a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/index.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/index.tsx index dc7e13634d6d6..0397eec7b2c73 100644 --- a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/index.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/index.tsx @@ -10,8 +10,10 @@ import React, { useState, useEffect, useCallback } from 'react'; import { cloneDeep, get } from 'lodash'; import { EuiSpacer } from '@elastic/eui'; +import { Position } from '@elastic/charts'; -import { IAggConfig } from '../../../../../../../data/public'; +import { BUCKET_TYPES, IAggConfig } from '../../../../../../../data/public'; +import { getUISettings } from '../../../../services'; import { VisParams, ValueAxis, SeriesParam, CategoryAxis } from '../../../../types'; import { ValidationVisOptionsProps } from '../../common'; @@ -27,6 +29,7 @@ import { mapPositionOpposingOpposite, } from './utils'; import { getSeriesParams } from '../../../../utils/get_series_params'; +import { LEGACY_TIME_AXIS } from '../../../../../../../charts/common'; export type SetParamByIndex =

( axesName: 'valueAxes' | 'seriesParams', @@ -287,6 +290,20 @@ function MetricsAxisOptions(props: ValidationVisOptionsProps) { updateAxisTitle(updatedSeries); }, [firstValueAxesId, setValue, stateParams.seriesParams, updateAxisTitle, aggs, schemaName]); + const isTimeViz = aggs.aggs.some( + (agg) => + agg.schema === 'segment' && agg.enabled && agg.type?.name === BUCKET_TYPES.DATE_HISTOGRAM + ); + const xAxisIsHorizontal = + stateParams.categoryAxes[0].position === Position.Bottom || + stateParams.categoryAxes[0].position === Position.Top; + const useLegacyTimeAxis = getUISettings().get(LEGACY_TIME_AXIS, false); + const linearOrStackedBars = stateParams.seriesParams.every( + ({ mode, type }) => type !== 'histogram' || (type === 'histogram' && mode === 'stacked') + ); + const useMultiLayerAxis = + xAxisIsHorizontal && isTimeViz && !useLegacyTimeAxis && linearOrStackedBars; + return isTabSelected ? ( <> ) { axis={stateParams.categoryAxes[0]} onPositionChanged={onCategoryAxisPositionChanged} setCategoryAxis={setCategoryAxis} + useMultiLayerAxis={useMultiLayerAxis} /> ) : null; diff --git a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/label_options.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/label_options.tsx index ef48d8b6d7880..ed7ac178d3d8b 100644 --- a/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/label_options.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/label_options.tsx @@ -8,7 +8,7 @@ import React, { useCallback, useMemo } from 'react'; -import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -23,9 +23,15 @@ export interface LabelOptionsProps { axisLabels: Labels; axisFilterCheckboxName: string; setAxisLabel: SetAxisLabel; + disableSingleLayerAxisControls?: boolean; } -function LabelOptions({ axisLabels, axisFilterCheckboxName, setAxisLabel }: LabelOptionsProps) { +function LabelOptions({ + axisLabels, + axisFilterCheckboxName, + setAxisLabel, + disableSingleLayerAxisControls, +}: LabelOptionsProps) { const setAxisLabelRotate = useCallback( (paramName: 'rotate', value: Labels['rotate']) => { setAxisLabel(paramName, Number(value)); @@ -34,6 +40,15 @@ function LabelOptions({ axisLabels, axisFilterCheckboxName, setAxisLabel }: Labe ); const rotateOptions = useMemo(getRotateOptions, []); + const multilayerAxisTooltipText = disableSingleLayerAxisControls + ? i18n.translate( + 'visTypeXy.controls.pointSeries.categoryAxis.axisLabelsOptionsMultilayer.disabled', + { + defaultMessage: 'This option can be configured only with non-time-based axes', + } + ) + : undefined; + const axisLabelControlDisabled = !axisLabels.show || disableSingleLayerAxisControls; return ( <> @@ -56,39 +71,43 @@ function LabelOptions({ axisLabels, axisFilterCheckboxName, setAxisLabel }: Labe value={axisLabels.show} setValue={setAxisLabel} /> - - + + + - + + + diff --git a/test/api_integration/apis/saved_objects_management/scroll_count.ts b/test/api_integration/apis/saved_objects_management/scroll_count.ts index 088b26d8205da..ffb275e8656f0 100644 --- a/test/api_integration/apis/saved_objects_management/scroll_count.ts +++ b/test/api_integration/apis/saved_objects_management/scroll_count.ts @@ -18,77 +18,132 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); describe('scroll_count', () => { - before(async () => { - await esArchiver.load( - 'test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count' - ); - }); - after(async () => { - await esArchiver.unload( - 'test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count' - ); - }); + describe('with less than 10k objects', () => { + before(async () => { + await esArchiver.load( + 'test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count' + ); + }); + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count' + ); + }); + + it('returns the count for each included types', async () => { + const res = await supertest + .post(apiUrl) + .send({ + typesToInclude: defaultTypes, + }) + .expect(200); - it('returns the count for each included types', async () => { - const res = await supertest - .post(apiUrl) - .send({ - typesToInclude: defaultTypes, - }) - .expect(200); - - expect(res.body).to.eql({ - dashboard: 2, - 'index-pattern': 1, - search: 1, - visualization: 2, + expect(res.body).to.eql({ + dashboard: 2, + 'index-pattern': 1, + search: 1, + visualization: 2, + }); }); - }); - it('only returns count for types to include', async () => { - const res = await supertest - .post(apiUrl) - .send({ - typesToInclude: ['dashboard', 'search'], - }) - .expect(200); - - expect(res.body).to.eql({ - dashboard: 2, - search: 1, + it('only returns count for types to include', async () => { + const res = await supertest + .post(apiUrl) + .send({ + typesToInclude: ['dashboard', 'search'], + }) + .expect(200); + + expect(res.body).to.eql({ + dashboard: 2, + search: 1, + }); }); - }); - it('filters on title when `searchString` is provided', async () => { - const res = await supertest - .post(apiUrl) - .send({ - typesToInclude: defaultTypes, - searchString: 'Amazing', - }) - .expect(200); - - expect(res.body).to.eql({ - dashboard: 1, - visualization: 1, - 'index-pattern': 0, - search: 0, + it('filters on title when `searchString` is provided', async () => { + const res = await supertest + .post(apiUrl) + .send({ + typesToInclude: defaultTypes, + searchString: 'Amazing', + }) + .expect(200); + + expect(res.body).to.eql({ + dashboard: 1, + visualization: 1, + 'index-pattern': 0, + search: 0, + }); + }); + + it('includes all requested types even when none match the search', async () => { + const res = await supertest + .post(apiUrl) + .send({ + typesToInclude: ['dashboard', 'search', 'visualization'], + searchString: 'nothing-will-match', + }) + .expect(200); + + expect(res.body).to.eql({ + dashboard: 0, + visualization: 0, + search: 0, + }); }); }); - it('includes all requested types even when none match the search', async () => { - const res = await supertest - .post(apiUrl) - .send({ - typesToInclude: ['dashboard', 'search', 'visualization'], - searchString: 'nothing-will-match', - }) - .expect(200); - - expect(res.body).to.eql({ - dashboard: 0, - visualization: 0, - search: 0, + describe('scroll_count with more than 10k objects', () => { + const importVisualizations = async ({ + startIdx = 1, + endIdx, + }: { + startIdx?: number; + endIdx: number; + }) => { + const fileChunks: string[] = []; + for (let i = startIdx; i <= endIdx; i++) { + const id = `test-vis-${i}`; + fileChunks.push( + JSON.stringify({ + type: 'visualization', + id, + attributes: { + title: `My visualization (${i})`, + uiStateJSON: '{}', + visState: '{}', + }, + references: [], + }) + ); + } + + await supertest + .post(`/api/saved_objects/_import`) + .attach('file', Buffer.from(fileChunks.join('\n'), 'utf8'), 'export.ndjson') + .expect(200); + }; + + before(async () => { + await importVisualizations({ startIdx: 1, endIdx: 6000 }); + await importVisualizations({ startIdx: 6001, endIdx: 12000 }); + }); + after(async () => { + await esArchiver.emptyKibanaIndex(); + }); + + it('returns the correct count for each included types', async () => { + const res = await supertest + .post(apiUrl) + .send({ + typesToInclude: ['visualization'], + }) + .expect(200); + + expect(res.body).to.eql({ + visualization: 12000, + }); }); }); }); diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index 9c4f27fa945be..3646dbddb347d 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -96,6 +96,7 @@ The following table describes the properties of the `options` object. |producer|The id of the application producing this rule type.|string| |minimumLicenseRequired|The value of a minimum license. Most of the rules are licensed as "basic".|string| |ruleTaskTimeout|The length of time a rule can run before being cancelled due to timeout. By default, this value is "5m".|string| +|cancelAlertsOnRuleTimeout|Whether to skip writing alerts and scheduling actions if a rule execution is cancelled due to timeout. By default, this value is set to "true".|boolean| |useSavedObjectReferences.extractReferences|(Optional) When developing a rule type, you can choose to implement hooks for extracting saved object references from rule parameters. This hook will be invoked when a rule is created or updated. Implementing this hook is optional, but if an extract hook is implemented, an inject hook must also be implemented.|Function |useSavedObjectReferences.injectReferences|(Optional) When developing a rule type, you can choose to implement hooks for injecting saved object references into rule parameters. This hook will be invoked when a rule is retrieved (get or find). Implementing this hook is optional, but if an inject hook is implemented, an extract hook must also be implemented.|Function |isExportable|Whether the rule type is exportable from the Saved Objects Management UI.|boolean| diff --git a/x-pack/plugins/alerting/common/alert.ts b/x-pack/plugins/alerting/common/alert.ts index bf0c8e382c9d4..4431f185ac9ca 100644 --- a/x-pack/plugins/alerting/common/alert.ts +++ b/x-pack/plugins/alerting/common/alert.ts @@ -30,6 +30,7 @@ export enum AlertExecutionStatusErrorReasons { Execute = 'execute', Unknown = 'unknown', License = 'license', + Timeout = 'timeout', } export interface AlertExecutionStatus { diff --git a/x-pack/plugins/alerting/server/config.test.ts b/x-pack/plugins/alerting/server/config.test.ts index 63d93b9d67769..a96612beac412 100644 --- a/x-pack/plugins/alerting/server/config.test.ts +++ b/x-pack/plugins/alerting/server/config.test.ts @@ -12,6 +12,7 @@ describe('config validation', () => { const config: Record = {}; expect(configSchema.validate(config)).toMatchInlineSnapshot(` Object { + "cancelAlertsOnRuleTimeout": true, "defaultRuleTaskTimeout": "5m", "healthCheck": Object { "interval": "60m", diff --git a/x-pack/plugins/alerting/server/config.ts b/x-pack/plugins/alerting/server/config.ts index 277f0c7297df9..8b1b664534379 100644 --- a/x-pack/plugins/alerting/server/config.ts +++ b/x-pack/plugins/alerting/server/config.ts @@ -21,6 +21,7 @@ export const configSchema = schema.object({ defaultValue: DEFAULT_MAX_EPHEMERAL_ACTIONS_PER_ALERT, }), defaultRuleTaskTimeout: schema.string({ validate: validateDurationSchema, defaultValue: '5m' }), + cancelAlertsOnRuleTimeout: schema.boolean({ defaultValue: true }), }); export type AlertsConfig = TypeOf; diff --git a/x-pack/plugins/alerting/server/plugin.test.ts b/x-pack/plugins/alerting/server/plugin.test.ts index 6419a3ccc5c90..a8da891a3dd14 100644 --- a/x-pack/plugins/alerting/server/plugin.test.ts +++ b/x-pack/plugins/alerting/server/plugin.test.ts @@ -39,6 +39,7 @@ describe('Alerting Plugin', () => { }, maxEphemeralActionsPerAlert: 10, defaultRuleTaskTimeout: '5m', + cancelAlertsOnRuleTimeout: true, }); plugin = new AlertingPlugin(context); @@ -73,6 +74,7 @@ describe('Alerting Plugin', () => { }, maxEphemeralActionsPerAlert: 10, defaultRuleTaskTimeout: '5m', + cancelAlertsOnRuleTimeout: true, }); plugin = new AlertingPlugin(context); @@ -145,7 +147,7 @@ describe('Alerting Plugin', () => { }); }); - it('should apply default config value for ruleTaskTimeout', async () => { + it('should apply default config value for ruleTaskTimeout if no value is specified', async () => { const ruleType = { ...sampleAlertType, minimumLicenseRequired: 'basic', @@ -153,6 +155,35 @@ describe('Alerting Plugin', () => { await setup.registerType(ruleType); expect(ruleType.ruleTaskTimeout).toBe('5m'); }); + + it('should apply value for ruleTaskTimeout if specified', async () => { + const ruleType = { + ...sampleAlertType, + minimumLicenseRequired: 'basic', + ruleTaskTimeout: '20h', + } as AlertType; + await setup.registerType(ruleType); + expect(ruleType.ruleTaskTimeout).toBe('20h'); + }); + + it('should apply default config value for cancelAlertsOnRuleTimeout if no value is specified', async () => { + const ruleType = { + ...sampleAlertType, + minimumLicenseRequired: 'basic', + } as AlertType; + await setup.registerType(ruleType); + expect(ruleType.cancelAlertsOnRuleTimeout).toBe(true); + }); + + it('should apply value for cancelAlertsOnRuleTimeout if specified', async () => { + const ruleType = { + ...sampleAlertType, + minimumLicenseRequired: 'basic', + cancelAlertsOnRuleTimeout: false, + } as AlertType; + await setup.registerType(ruleType); + expect(ruleType.cancelAlertsOnRuleTimeout).toBe(false); + }); }); }); @@ -169,6 +200,7 @@ describe('Alerting Plugin', () => { }, maxEphemeralActionsPerAlert: 10, defaultRuleTaskTimeout: '5m', + cancelAlertsOnRuleTimeout: true, }); const plugin = new AlertingPlugin(context); @@ -210,6 +242,7 @@ describe('Alerting Plugin', () => { }, maxEphemeralActionsPerAlert: 10, defaultRuleTaskTimeout: '5m', + cancelAlertsOnRuleTimeout: true, }); const plugin = new AlertingPlugin(context); @@ -265,6 +298,7 @@ describe('Alerting Plugin', () => { }, maxEphemeralActionsPerAlert: 100, defaultRuleTaskTimeout: '5m', + cancelAlertsOnRuleTimeout: true, }); const plugin = new AlertingPlugin(context); diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index bd3eab19d220d..8be96170e664a 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -75,6 +75,7 @@ export const EVENT_LOG_ACTIONS = { newInstance: 'new-instance', recoveredInstance: 'recovered-instance', activeInstance: 'active-instance', + executeTimeout: 'execute-timeout', }; export const LEGACY_EVENT_LOG_ACTIONS = { resolvedInstance: 'resolved-instance', @@ -285,14 +286,13 @@ export class AlertingPlugin { if (!(alertType.minimumLicenseRequired in LICENSE_TYPE)) { throw new Error(`"${alertType.minimumLicenseRequired}" is not a valid license type`); } - if (!alertType.ruleTaskTimeout) { - alertingConfig.then((config) => { - alertType.ruleTaskTimeout = config.defaultRuleTaskTimeout; - ruleTypeRegistry.register(alertType); - }); - } else { + + alertingConfig.then((config) => { + alertType.ruleTaskTimeout = alertType.ruleTaskTimeout ?? config.defaultRuleTaskTimeout; + alertType.cancelAlertsOnRuleTimeout = + alertType.cancelAlertsOnRuleTimeout ?? config.cancelAlertsOnRuleTimeout; ruleTypeRegistry.register(alertType); - } + }); }, getSecurityHealth: async () => { return await getSecurityHealth( @@ -375,21 +375,24 @@ export class AlertingPlugin { return alertingAuthorizationClientFactory!.create(request); }; - taskRunnerFactory.initialize({ - logger, - getServices: this.getServicesFactory(core.savedObjects, core.elasticsearch), - getRulesClientWithRequest, - spaceIdToNamespace, - actionsPlugin: plugins.actions, - encryptedSavedObjectsClient, - basePathService: core.http.basePath, - eventLogger: this.eventLogger!, - internalSavedObjectsRepository: core.savedObjects.createInternalRepository(['alert']), - executionContext: core.executionContext, - ruleTypeRegistry: this.ruleTypeRegistry!, - kibanaBaseUrl: this.kibanaBaseUrl, - supportsEphemeralTasks: plugins.taskManager.supportsEphemeralTasks(), - maxEphemeralActionsPerAlert: this.config.then((config) => config.maxEphemeralActionsPerAlert), + this.config.then((config) => { + taskRunnerFactory.initialize({ + logger, + getServices: this.getServicesFactory(core.savedObjects, core.elasticsearch), + getRulesClientWithRequest, + spaceIdToNamespace, + actionsPlugin: plugins.actions, + encryptedSavedObjectsClient, + basePathService: core.http.basePath, + eventLogger: this.eventLogger!, + internalSavedObjectsRepository: core.savedObjects.createInternalRepository(['alert']), + executionContext: core.executionContext, + ruleTypeRegistry: this.ruleTypeRegistry!, + kibanaBaseUrl: this.kibanaBaseUrl, + supportsEphemeralTasks: plugins.taskManager.supportsEphemeralTasks(), + maxEphemeralActionsPerAlert: config.maxEphemeralActionsPerAlert, + cancelAlertsOnRuleTimeout: config.cancelAlertsOnRuleTimeout, + }); }); this.eventLogService!.registerSavedObjectProvider('alert', (request) => { diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts index 244dcb85b13e9..fc5c5cf8897f0 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts @@ -99,7 +99,7 @@ const createExecutionHandlerParams: jest.Mocked< stateVal: 'My other {{state.value}} goes here', }, supportsEphemeralTasks: false, - maxEphemeralActionsPerAlert: Promise.resolve(10), + maxEphemeralActionsPerAlert: 10, }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts index 652e032a1cbb0..d93d8cd6d1312 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts @@ -56,7 +56,7 @@ export interface CreateExecutionHandlerOptions< request: KibanaRequest; alertParams: AlertTypeParams; supportsEphemeralTasks: boolean; - maxEphemeralActionsPerAlert: Promise; + maxEphemeralActionsPerAlert: number; } interface ExecutionHandlerOptions { @@ -157,7 +157,7 @@ export function createExecutionHandler< const alertLabel = `${alertType.id}:${alertId}: '${alertName}'`; const actionsClient = await actionsPlugin.getActionsClientWithRequest(request); - let ephemeralActionsToSchedule = await maxEphemeralActionsPerAlert; + let ephemeralActionsToSchedule = maxEphemeralActionsPerAlert; for (const action of actions) { if ( !actionsPlugin.isActionExecutable(action.id, action.actionTypeId, { notifyUsage: true }) diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 07c4d0371c718..f70cbaa13f7d1 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -107,7 +107,8 @@ describe('Task Runner', () => { ruleTypeRegistry, kibanaBaseUrl: 'https://localhost:5601', supportsEphemeralTasks: false, - maxEphemeralActionsPerAlert: new Promise((resolve) => resolve(10)), + maxEphemeralActionsPerAlert: 10, + cancelAlertsOnRuleTimeout: true, }; function testAgainstEphemeralSupport( @@ -285,7 +286,7 @@ describe('Task Runner', () => { expect(call.services).toBeTruthy(); const logger = taskRunnerFactoryInitializerParams.logger; - expect(logger.debug).toHaveBeenCalledTimes(2); + expect(logger.debug).toHaveBeenCalledTimes(3); expect(logger.debug).nthCalledWith(1, 'executing alert test:1 at 1970-01-01T00:00:00.000Z'); expect(logger.debug).nthCalledWith( 2, @@ -432,7 +433,7 @@ describe('Task Runner', () => { `); const logger = customTaskRunnerFactoryInitializerParams.logger; - expect(logger.debug).toHaveBeenCalledTimes(3); + expect(logger.debug).toHaveBeenCalledTimes(4); expect(logger.debug).nthCalledWith(1, 'executing alert test:1 at 1970-01-01T00:00:00.000Z'); expect(logger.debug).nthCalledWith( 2, @@ -648,7 +649,7 @@ describe('Task Runner', () => { expect(actionsClient.ephemeralEnqueuedExecution).toHaveBeenCalledTimes(0); const logger = taskRunnerFactoryInitializerParams.logger; - expect(logger.debug).toHaveBeenCalledTimes(4); + expect(logger.debug).toHaveBeenCalledTimes(5); expect(logger.debug).nthCalledWith(1, 'executing alert test:1 at 1970-01-01T00:00:00.000Z'); expect(logger.debug).nthCalledWith( 2, @@ -848,7 +849,7 @@ describe('Task Runner', () => { expect(enqueueFunction).toHaveBeenCalledTimes(1); const logger = customTaskRunnerFactoryInitializerParams.logger; - expect(logger.debug).toHaveBeenCalledTimes(4); + expect(logger.debug).toHaveBeenCalledTimes(5); expect(logger.debug).nthCalledWith(1, 'executing alert test:1 at 1970-01-01T00:00:00.000Z'); expect(logger.debug).nthCalledWith( 2, @@ -1537,7 +1538,7 @@ describe('Task Runner', () => { `); const logger = customTaskRunnerFactoryInitializerParams.logger; - expect(logger.debug).toHaveBeenCalledTimes(4); + expect(logger.debug).toHaveBeenCalledTimes(5); expect(logger.debug).nthCalledWith(1, 'executing alert test:1 at 1970-01-01T00:00:00.000Z'); expect(logger.debug).nthCalledWith( 2, @@ -4339,7 +4340,7 @@ describe('Task Runner', () => { expect(call.services).toBeTruthy(); const logger = taskRunnerFactoryInitializerParams.logger; - expect(logger.debug).toHaveBeenCalledTimes(2); + expect(logger.debug).toHaveBeenCalledTimes(3); expect(logger.debug).nthCalledWith(1, 'executing alert test:1 at 1970-01-01T00:00:00.000Z'); expect(logger.debug).nthCalledWith( 2, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 8b93d3fa17211..f651f41ef0c1e 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -82,6 +82,7 @@ export class TaskRunner< private context: TaskRunnerContext; private logger: Logger; private taskInstance: AlertTaskInstance; + private ruleName: string | null; private alertType: NormalizedAlertType< Params, ExtractedParams, @@ -92,6 +93,7 @@ export class TaskRunner< RecoveryActionGroupId >; private readonly ruleTypeRegistry: RuleTypeRegistry; + private cancelled: boolean; constructor( alertType: NormalizedAlertType< @@ -109,8 +111,10 @@ export class TaskRunner< this.context = context; this.logger = context.logger; this.alertType = alertType; + this.ruleName = null; this.taskInstance = taskInstanceToAlertTaskInstance(taskInstance); this.ruleTypeRegistry = context.ruleTypeRegistry; + this.cancelled = false; } async getApiKeyForAlertPermissions(alertId: string, spaceId: string) { @@ -201,6 +205,39 @@ export class TaskRunner< }); } + private async updateRuleExecutionStatus( + alertId: string, + namespace: string | undefined, + executionStatus: AlertExecutionStatus + ) { + const client = this.context.internalSavedObjectsRepository; + const attributes = { + executionStatus: alertExecutionStatusToRaw(executionStatus), + }; + + try { + await partiallyUpdateAlert(client, alertId, attributes, { + ignore404: true, + namespace, + refresh: false, + }); + } catch (err) { + this.logger.error( + `error updating rule execution status for ${this.alertType.id}:${alertId} ${err.message}` + ); + } + } + + private shouldLogAndScheduleActionsForAlerts() { + // if execution hasn't been cancelled, return true + if (!this.cancelled) { + return true; + } + + // if execution has been cancelled, return true if EITHER alerting config or rule type indicate to proceed with scheduling actions + return !this.context.cancelAlertsOnRuleTimeout || !this.alertType.cancelAlertsOnRuleTimeout; + } + async executeAlertInstance( alertInstanceId: string, alertInstance: AlertInstance, @@ -355,19 +392,21 @@ export class TaskRunner< recoveredAlerts: recoveredAlertInstances, }); - generateNewAndRecoveredInstanceEvents({ - eventLogger, - originalAlertInstances, - currentAlertInstances: instancesWithScheduledActions, - recoveredAlertInstances, - alertId, - alertLabel, - namespace, - ruleType: alertType, - rule: alert, - }); + if (this.shouldLogAndScheduleActionsForAlerts()) { + generateNewAndRecoveredInstanceEvents({ + eventLogger, + originalAlertInstances, + currentAlertInstances: instancesWithScheduledActions, + recoveredAlertInstances, + alertId, + alertLabel, + namespace, + ruleType: alertType, + rule: alert, + }); + } - if (!muteAll) { + if (!muteAll && this.shouldLogAndScheduleActionsForAlerts()) { const mutedInstanceIdsSet = new Set(mutedInstanceIds); scheduleActionsForRecoveredInstances({ @@ -422,7 +461,14 @@ export class TaskRunner< ) ); } else { - this.logger.debug(`no scheduling of actions for alert ${alertLabel}: alert is muted.`); + if (muteAll) { + this.logger.debug(`no scheduling of actions for alert ${alertLabel}: alert is muted.`); + } + if (!this.shouldLogAndScheduleActionsForAlerts()) { + this.logger.debug( + `no scheduling of actions for alert ${alertLabel}: alert execution has been cancelled.` + ); + } } return { @@ -487,6 +533,8 @@ export class TaskRunner< throw new ErrorWithReason(AlertExecutionStatusErrorReasons.Read, err); } + this.ruleName = alert.name; + try { this.ruleTypeRegistry.ensureRuleTypeEnabled(alert.alertTypeId); } catch (err) { @@ -596,21 +644,13 @@ export class TaskRunner< eventLogger.logEvent(event); - const client = this.context.internalSavedObjectsRepository; - const attributes = { - executionStatus: alertExecutionStatusToRaw(executionStatus), - }; - - try { - await partiallyUpdateAlert(client, alertId, attributes, { - ignore404: true, - namespace, - refresh: false, - }); - } catch (err) { - this.logger.error( - `error updating alert execution status for ${this.alertType.id}:${alertId} ${err.message}` + if (!this.cancelled) { + this.logger.debug( + `Updating rule task for ${this.alertType.id} rule with id ${alertId} - ${JSON.stringify( + executionStatus + )}` ); + await this.updateRuleExecutionStatus(alertId, namespace, executionStatus); } return { @@ -646,6 +686,72 @@ export class TaskRunner< }), }; } + + async cancel(): Promise { + if (this.cancelled) { + return; + } + + this.cancelled = true; + + // Write event log entry + const { + params: { alertId, spaceId }, + } = this.taskInstance; + const namespace = this.context.spaceIdToNamespace(spaceId); + + this.logger.debug( + `Cancelling rule type ${this.alertType.id} with id ${alertId} - execution exceeded rule type timeout of ${this.alertType.ruleTaskTimeout}` + ); + + const eventLogger = this.context.eventLogger; + const event: IEvent = { + '@timestamp': new Date().toISOString(), + event: { + action: EVENT_LOG_ACTIONS.executeTimeout, + kind: 'alert', + category: [this.alertType.producer], + }, + message: `rule: ${this.alertType.id}:${alertId}: '${ + this.ruleName ?? '' + }' execution cancelled due to timeout - exceeded rule type timeout of ${ + this.alertType.ruleTaskTimeout + }`, + kibana: { + saved_objects: [ + { + rel: SAVED_OBJECT_REL_PRIMARY, + type: 'alert', + id: alertId, + type_id: this.alertType.id, + namespace, + }, + ], + }, + rule: { + id: alertId, + license: this.alertType.minimumLicenseRequired, + category: this.alertType.id, + ruleset: this.alertType.producer, + ...(this.ruleName ? { name: this.ruleName } : {}), + }, + }; + eventLogger.logEvent(event); + + // Update the rule saved object with execution status + const executionStatus: AlertExecutionStatus = { + lastExecutionDate: new Date(), + status: 'error', + error: { + reason: AlertExecutionStatusErrorReasons.Timeout, + message: `${this.alertType.id}:${alertId}: execution cancelled due to timeout - exceeded rule type timeout of ${this.alertType.ruleTaskTimeout}`, + }, + }; + this.logger.debug( + `Updating rule task for ${this.alertType.id} rule with id ${alertId} - execution error due to timeout` + ); + await this.updateRuleExecutionStatus(alertId, namespace, executionStatus); + } } interface TrackAlertDurationsParams< diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts new file mode 100644 index 0000000000000..95cb356af3c1a --- /dev/null +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts @@ -0,0 +1,721 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import sinon from 'sinon'; +import { + AlertExecutorOptions, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../types'; +import { ConcreteTaskInstance, TaskStatus } from '../../../task_manager/server'; +import { TaskRunnerContext } from './task_runner_factory'; +import { TaskRunner } from './task_runner'; +import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks'; +import { + loggingSystemMock, + savedObjectsRepositoryMock, + httpServiceMock, + executionContextServiceMock, +} from '../../../../../src/core/server/mocks'; +import { PluginStartContract as ActionsPluginStart } from '../../../actions/server'; +import { actionsMock, actionsClientMock } from '../../../actions/server/mocks'; +import { alertsMock, rulesClientMock } from '../mocks'; +import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; +import { IEventLogger } from '../../../event_log/server'; +import { Alert, RecoveredActionGroup } from '../../common'; +import { UntypedNormalizedAlertType } from '../rule_type_registry'; +import { ruleTypeRegistryMock } from '../rule_type_registry.mock'; + +const ruleType: jest.Mocked = { + id: 'test', + name: 'My test rule', + actionGroups: [{ id: 'default', name: 'Default' }, RecoveredActionGroup], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + recoveryActionGroup: RecoveredActionGroup, + executor: jest.fn(), + producer: 'alerts', + cancelAlertsOnRuleTimeout: true, + ruleTaskTimeout: '5m', +}; + +let fakeTimer: sinon.SinonFakeTimers; + +describe('Task Runner Cancel', () => { + let mockedTaskInstance: ConcreteTaskInstance; + + beforeAll(() => { + fakeTimer = sinon.useFakeTimers(); + mockedTaskInstance = { + id: '', + attempts: 0, + status: TaskStatus.Running, + version: '123', + runAt: new Date(), + schedule: { interval: '10s' }, + scheduledAt: new Date(), + startedAt: new Date(), + retryAt: new Date(Date.now() + 5 * 60 * 1000), + state: {}, + taskType: 'alerting:test', + params: { + alertId: '1', + }, + ownerId: null, + }; + }); + + afterAll(() => fakeTimer.restore()); + + const encryptedSavedObjectsClient = encryptedSavedObjectsMock.createClient(); + const services = alertsMock.createAlertServices(); + const actionsClient = actionsClientMock.create(); + const rulesClient = rulesClientMock.create(); + const ruleTypeRegistry = ruleTypeRegistryMock.create(); + + type TaskRunnerFactoryInitializerParamsType = jest.Mocked & { + actionsPlugin: jest.Mocked; + eventLogger: jest.Mocked; + executionContext: ReturnType; + }; + + const taskRunnerFactoryInitializerParams: TaskRunnerFactoryInitializerParamsType = { + getServices: jest.fn().mockReturnValue(services), + actionsPlugin: actionsMock.createStart(), + getRulesClientWithRequest: jest.fn().mockReturnValue(rulesClient), + encryptedSavedObjectsClient, + logger: loggingSystemMock.create().get(), + executionContext: executionContextServiceMock.createInternalStartContract(), + spaceIdToNamespace: jest.fn().mockReturnValue(undefined), + basePathService: httpServiceMock.createBasePath(), + eventLogger: eventLoggerMock.create(), + internalSavedObjectsRepository: savedObjectsRepositoryMock.create(), + ruleTypeRegistry, + kibanaBaseUrl: 'https://localhost:5601', + supportsEphemeralTasks: false, + maxEphemeralActionsPerAlert: 10, + cancelAlertsOnRuleTimeout: true, + }; + + const mockDate = new Date('2019-02-12T21:01:22.479Z'); + + const mockedRuleSavedObject: Alert = { + id: '1', + consumer: 'bar', + createdAt: mockDate, + updatedAt: mockDate, + throttle: null, + muteAll: false, + notifyWhen: 'onActiveAlert', + enabled: true, + alertTypeId: ruleType.id, + apiKey: '', + apiKeyOwner: 'elastic', + schedule: { interval: '10s' }, + name: 'rule-name', + tags: ['rule-', '-tags'], + createdBy: 'rule-creator', + updatedBy: 'rule-updater', + mutedInstanceIds: [], + params: { + bar: true, + }, + actions: [ + { + group: 'default', + id: '1', + actionTypeId: 'action', + params: { + foo: true, + }, + }, + { + group: RecoveredActionGroup.id, + id: '2', + actionTypeId: 'action', + params: { + isResolved: true, + }, + }, + ], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, + }; + + beforeEach(() => { + jest.resetAllMocks(); + taskRunnerFactoryInitializerParams.getServices.mockReturnValue(services); + taskRunnerFactoryInitializerParams.getRulesClientWithRequest.mockReturnValue(rulesClient); + taskRunnerFactoryInitializerParams.actionsPlugin.getActionsClientWithRequest.mockResolvedValue( + actionsClient + ); + taskRunnerFactoryInitializerParams.actionsPlugin.renderActionParameterTemplates.mockImplementation( + (actionTypeId, actionId, params) => params + ); + ruleTypeRegistry.get.mockReturnValue(ruleType); + taskRunnerFactoryInitializerParams.executionContext.withContext.mockImplementation((ctx, fn) => + fn() + ); + rulesClient.get.mockResolvedValue(mockedRuleSavedObject); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + apiKey: Buffer.from('123:abc').toString('base64'), + }, + references: [], + }); + taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); + taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); + }); + + test('updates rule saved object execution status and writes to event log entry when task is cancelled mid-execution', async () => { + const taskRunner = new TaskRunner( + ruleType, + mockedTaskInstance, + taskRunnerFactoryInitializerParams + ); + + const promise = taskRunner.run(); + await Promise.resolve(); + await taskRunner.cancel(); + await promise; + + const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; + // execute-start event, timeout event and then an execute event because rule executors are not cancelling anything yet + expect(eventLogger.logEvent).toHaveBeenCalledTimes(3); + expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, { + '@timestamp': '1970-01-01T00:00:00.000Z', + event: { + action: 'execute-start', + category: ['alerts'], + kind: 'alert', + }, + kibana: { + saved_objects: [ + { + id: '1', + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + ], + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, + }, + message: 'alert execution start: "1"', + rule: { + category: 'test', + id: '1', + license: 'basic', + ruleset: 'alerts', + }, + }); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith(2, { + '@timestamp': '1970-01-01T00:00:00.000Z', + event: { + action: 'execute-timeout', + category: ['alerts'], + kind: 'alert', + }, + kibana: { + saved_objects: [ + { + id: '1', + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + ], + }, + message: `rule: test:1: '' execution cancelled due to timeout - exceeded rule type timeout of 5m`, + rule: { + category: 'test', + id: '1', + license: 'basic', + ruleset: 'alerts', + }, + }); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith(3, { + '@timestamp': '1970-01-01T00:00:00.000Z', + event: { + action: 'execute', + category: ['alerts'], + kind: 'alert', + outcome: 'success', + }, + kibana: { + alerting: { + status: 'ok', + }, + saved_objects: [ + { + id: '1', + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + ], + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, + }, + message: `alert executed: test:1: 'rule-name'`, + rule: { + category: 'test', + id: '1', + license: 'basic', + name: 'rule-name', + ruleset: 'alerts', + }, + }); + + expect( + taskRunnerFactoryInitializerParams.internalSavedObjectsRepository.update + ).toHaveBeenCalledTimes(1); + expect( + taskRunnerFactoryInitializerParams.internalSavedObjectsRepository.update + ).toHaveBeenCalledWith( + 'alert', + '1', + { + executionStatus: { + error: { + message: `test:1: execution cancelled due to timeout - exceeded rule type timeout of 5m`, + reason: 'timeout', + }, + lastDuration: 0, + lastExecutionDate: '1970-01-01T00:00:00.000Z', + status: 'error', + }, + }, + { refresh: false, namespace: undefined } + ); + }); + + test('actionsPlugin.execute is called if rule execution is cancelled but cancelAlertsOnRuleTimeout from config is false', async () => { + ruleType.executor.mockImplementation( + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, + string + >) => { + executorServices.alertInstanceFactory('1').scheduleActions('default'); + } + ); + // setting cancelAlertsOnRuleTimeout to false here + const taskRunner = new TaskRunner(ruleType, mockedTaskInstance, { + ...taskRunnerFactoryInitializerParams, + cancelAlertsOnRuleTimeout: false, + }); + + const promise = taskRunner.run(); + await Promise.resolve(); + await taskRunner.cancel(); + await promise; + + testActionsExecute(); + }); + + test('actionsPlugin.execute is called if rule execution is cancelled but cancelAlertsOnRuleTimeout for ruleType is false', async () => { + ruleTypeRegistry.get.mockReturnValue({ + ...ruleType, + cancelAlertsOnRuleTimeout: false, + }); + ruleType.executor.mockImplementation( + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, + string + >) => { + executorServices.alertInstanceFactory('1').scheduleActions('default'); + } + ); + // setting cancelAlertsOnRuleTimeout for ruleType to false here + const taskRunner = new TaskRunner( + { + ...ruleType, + cancelAlertsOnRuleTimeout: false, + }, + mockedTaskInstance, + taskRunnerFactoryInitializerParams + ); + + const promise = taskRunner.run(); + await Promise.resolve(); + await taskRunner.cancel(); + await promise; + + testActionsExecute(); + }); + + test('actionsPlugin.execute is skipped if rule execution is cancelled and cancelAlertsOnRuleTimeout for both config and ruleType are true', async () => { + ruleType.executor.mockImplementation( + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, + string + >) => { + executorServices.alertInstanceFactory('1').scheduleActions('default'); + } + ); + const taskRunner = new TaskRunner( + ruleType, + mockedTaskInstance, + taskRunnerFactoryInitializerParams + ); + + const promise = taskRunner.run(); + await Promise.resolve(); + await taskRunner.cancel(); + await promise; + + const logger = taskRunnerFactoryInitializerParams.logger; + expect(logger.debug).toHaveBeenCalledTimes(6); + expect(logger.debug).nthCalledWith(1, 'executing alert test:1 at 1970-01-01T00:00:00.000Z'); + expect(logger.debug).nthCalledWith( + 2, + `Cancelling rule type test with id 1 - execution exceeded rule type timeout of 5m` + ); + expect(logger.debug).nthCalledWith( + 3, + `Updating rule task for test rule with id 1 - execution error due to timeout` + ); + expect(logger.debug).nthCalledWith( + 4, + `alert test:1: 'rule-name' has 1 active alert instances: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` + ); + expect(logger.debug).nthCalledWith( + 5, + `no scheduling of actions for alert test:1: 'rule-name': alert execution has been cancelled.` + ); + expect(logger.debug).nthCalledWith( + 6, + 'alertExecutionStatus for test:1: {"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' + ); + + const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; + expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); + expect(eventLogger.logEvent).toHaveBeenCalledTimes(3); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, { + '@timestamp': '1970-01-01T00:00:00.000Z', + event: { + action: 'execute-start', + category: ['alerts'], + kind: 'alert', + }, + kibana: { + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, + saved_objects: [ + { + id: '1', + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + ], + }, + message: `alert execution start: \"1\"`, + rule: { + category: 'test', + id: '1', + license: 'basic', + ruleset: 'alerts', + }, + }); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith(2, { + '@timestamp': '1970-01-01T00:00:00.000Z', + event: { + action: 'execute-timeout', + category: ['alerts'], + kind: 'alert', + }, + kibana: { + saved_objects: [ + { + id: '1', + namespace: undefined, + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + ], + }, + message: `rule: test:1: '' execution cancelled due to timeout - exceeded rule type timeout of 5m`, + rule: { + category: 'test', + id: '1', + license: 'basic', + ruleset: 'alerts', + }, + }); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith(3, { + '@timestamp': '1970-01-01T00:00:00.000Z', + event: { + action: 'execute', + category: ['alerts'], + kind: 'alert', + outcome: 'success', + }, + kibana: { + alerting: { + status: 'active', + }, + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, + saved_objects: [ + { + id: '1', + namespace: undefined, + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + ], + }, + message: "alert executed: test:1: 'rule-name'", + rule: { + category: 'test', + id: '1', + license: 'basic', + name: 'rule-name', + ruleset: 'alerts', + }, + }); + }); + + function testActionsExecute() { + const logger = taskRunnerFactoryInitializerParams.logger; + expect(logger.debug).toHaveBeenCalledTimes(5); + expect(logger.debug).nthCalledWith(1, 'executing alert test:1 at 1970-01-01T00:00:00.000Z'); + expect(logger.debug).nthCalledWith( + 2, + `Cancelling rule type test with id 1 - execution exceeded rule type timeout of 5m` + ); + expect(logger.debug).nthCalledWith( + 3, + `Updating rule task for test rule with id 1 - execution error due to timeout` + ); + expect(logger.debug).nthCalledWith( + 4, + `alert test:1: 'rule-name' has 1 active alert instances: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` + ); + expect(logger.debug).nthCalledWith( + 5, + 'alertExecutionStatus for test:1: {"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' + ); + + const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; + expect(eventLogger.logEvent).toHaveBeenCalledTimes(6); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, { + '@timestamp': '1970-01-01T00:00:00.000Z', + event: { + action: 'execute-start', + category: ['alerts'], + kind: 'alert', + }, + kibana: { + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, + saved_objects: [ + { + id: '1', + namespace: undefined, + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + ], + }, + message: `alert execution start: "1"`, + rule: { + category: 'test', + id: '1', + license: 'basic', + ruleset: 'alerts', + }, + }); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith(2, { + '@timestamp': '1970-01-01T00:00:00.000Z', + event: { + action: 'execute-timeout', + category: ['alerts'], + kind: 'alert', + }, + kibana: { + saved_objects: [ + { + id: '1', + namespace: undefined, + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + ], + }, + message: `rule: test:1: '' execution cancelled due to timeout - exceeded rule type timeout of 5m`, + rule: { + category: 'test', + id: '1', + license: 'basic', + ruleset: 'alerts', + }, + }); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith(3, { + event: { + action: 'new-instance', + category: ['alerts'], + kind: 'alert', + duration: 0, + start: '1970-01-01T00:00:00.000Z', + }, + kibana: { + alerting: { + action_group_id: 'default', + instance_id: '1', + }, + saved_objects: [ + { + id: '1', + namespace: undefined, + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + ], + }, + message: "test:1: 'rule-name' created new instance: '1'", + rule: { + category: 'test', + id: '1', + license: 'basic', + name: 'rule-name', + namespace: undefined, + ruleset: 'alerts', + }, + }); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith(4, { + event: { + action: 'active-instance', + category: ['alerts'], + duration: 0, + kind: 'alert', + start: '1970-01-01T00:00:00.000Z', + }, + kibana: { + alerting: { + action_group_id: 'default', + instance_id: '1', + }, + saved_objects: [ + { id: '1', namespace: undefined, rel: 'primary', type: 'alert', type_id: 'test' }, + ], + }, + message: "test:1: 'rule-name' active instance: '1' in actionGroup: 'default'", + rule: { + category: 'test', + id: '1', + license: 'basic', + name: 'rule-name', + ruleset: 'alerts', + }, + }); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith(5, { + event: { + action: 'execute-action', + category: ['alerts'], + kind: 'alert', + }, + kibana: { + alerting: { + instance_id: '1', + action_group_id: 'default', + }, + saved_objects: [ + { + id: '1', + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + { + id: '1', + type: 'action', + type_id: 'action', + }, + ], + }, + message: + "alert: test:1: 'rule-name' instanceId: '1' scheduled actionGroup: 'default' action: action:1", + rule: { + category: 'test', + id: '1', + license: 'basic', + name: 'rule-name', + ruleset: 'alerts', + }, + }); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith(6, { + '@timestamp': '1970-01-01T00:00:00.000Z', + event: { action: 'execute', category: ['alerts'], kind: 'alert', outcome: 'success' }, + kibana: { + alerting: { + status: 'active', + }, + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, + saved_objects: [ + { + id: '1', + namespace: undefined, + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + ], + }, + message: "alert executed: test:1: 'rule-name'", + rule: { + category: 'test', + id: '1', + license: 'basic', + name: 'rule-name', + ruleset: 'alerts', + }, + }); + } +}); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts index d262607958347..b799dd2f4043d 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts @@ -83,7 +83,8 @@ describe('Task Runner Factory', () => { ruleTypeRegistry: ruleTypeRegistryMock.create(), kibanaBaseUrl: 'https://localhost:5601', supportsEphemeralTasks: true, - maxEphemeralActionsPerAlert: new Promise((resolve) => resolve(10)), + maxEphemeralActionsPerAlert: 10, + cancelAlertsOnRuleTimeout: true, executionContext, }; diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts index 524b779a0d9ac..fc4b8eee89f5e 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts @@ -44,7 +44,8 @@ export interface TaskRunnerContext { ruleTypeRegistry: RuleTypeRegistry; kibanaBaseUrl: string | undefined; supportsEphemeralTasks: boolean; - maxEphemeralActionsPerAlert: Promise; + maxEphemeralActionsPerAlert: number; + cancelAlertsOnRuleTimeout: boolean; } export class TaskRunnerFactory { diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 82bb94b121840..c1645936c06e9 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -160,6 +160,7 @@ export interface AlertType< defaultScheduleInterval?: string; minimumScheduleInterval?: string; ruleTaskTimeout?: string; + cancelAlertsOnRuleTimeout?: boolean; } export type UntypedAlertType = AlertType< AlertTypeParams, diff --git a/x-pack/plugins/apm/common/utils/environment_query.ts b/x-pack/plugins/apm/common/utils/environment_query.ts index e2f9a722e3de2..bc02e4cd2518b 100644 --- a/x-pack/plugins/apm/common/utils/environment_query.ts +++ b/x-pack/plugins/apm/common/utils/environment_query.ts @@ -6,11 +6,15 @@ */ import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { SERVICE_ENVIRONMENT } from '../elasticsearch_fieldnames'; +import { + SERVICE_ENVIRONMENT, + SERVICE_NODE_NAME, +} from '../elasticsearch_fieldnames'; import { ENVIRONMENT_ALL, ENVIRONMENT_NOT_DEFINED, } from '../environment_filter_values'; +import { SERVICE_NODE_NAME_MISSING } from '../service_nodes'; export function environmentQuery( environment: string @@ -25,3 +29,17 @@ export function environmentQuery( return [{ term: { [SERVICE_ENVIRONMENT]: environment } }]; } + +export function serviceNodeNameQuery( + serviceNodeName?: string +): QueryDslQueryContainer[] { + if (!serviceNodeName) { + return []; + } + + if (serviceNodeName === SERVICE_NODE_NAME_MISSING) { + return [{ bool: { must_not: [{ exists: { field: SERVICE_NODE_NAME } }] } }]; + } + + return [{ term: { [SERVICE_NODE_NAME]: serviceNodeName } }]; +} diff --git a/x-pack/plugins/apm/dev_docs/local_setup.md b/x-pack/plugins/apm/dev_docs/local_setup.md index 21d861fbb4e0b..4ed5d8c1b86dd 100644 --- a/x-pack/plugins/apm/dev_docs/local_setup.md +++ b/x-pack/plugins/apm/dev_docs/local_setup.md @@ -40,7 +40,7 @@ elasticsearch.password: changeme APM behaves differently depending on which the role and permissions a logged in user has. To create the users run: ```sh -node x-pack/plugins/apm/scripts/create-apm-users-and-roles.js --username admin --password changeme --kibana-url http://localhost:5601 --role-suffix +node x-pack/plugins/apm/scripts/create_apm_users_and_roles.js --username admin --password changeme --kibana-url http://localhost:5601 --role-suffix ``` This will create: diff --git a/x-pack/plugins/apm/dev_docs/query_debugging_in_development_and_production.md b/x-pack/plugins/apm/dev_docs/query_debugging_in_development_and_production.md index 0dcf20d3e2fed..5811306a05fda 100644 --- a/x-pack/plugins/apm/dev_docs/query_debugging_in_development_and_production.md +++ b/x-pack/plugins/apm/dev_docs/query_debugging_in_development_and_production.md @@ -14,4 +14,147 @@ There will be an `_inspect` key containing every Elasticsearch query made during ![image](https://user-images.githubusercontent.com/209966/140500012-b075adf0-8401-40fd-99f8-85b68711de17.png) +## Example +When "Inspect ES queries" are enabed all API calls to the APM API will be include the query param `_inspect=true`. For the environments API the request / response will be: + +``` +GET /internal/apm/environments?start=&end=&_inspect=true +``` + +```json +{ + "environments": [ + "production", + "testing", + "ENVIRONMENT_NOT_DEFINED" + ], + "_inspect": [ + { + "id": "get_environments (/internal/apm/environments)", + "json": { + "size": 0, + "query": { + "bool": { + "filter": [ + { + "range": { + "@timestamp": { + "gte": 1636918740000, + "lte": 1636919672329, + "format": "epoch_millis" + } + } + }, + { + "terms": { + "processor.event": [ + "transaction", + "metric", + "error" + ] + } + }, + { + "range": { + "observer.version_major": { + "gte": 7 + } + } + } + ] + } + }, + "aggs": { + "environments": { + "terms": { + "field": "service.environment", + "missing": "ENVIRONMENT_NOT_DEFINED", + "size": 100 + } + } + } + }, + "name": "get_environments (/internal/apm/environments)", + "response": { + "json": { + "took": 10, + "timed_out": false, + "_shards": { + "total": 17, + "successful": 17, + "skipped": 0, + "failed": 0 + }, + "hits": { + "total": { + "value": 10000, + "relation": "gte" + }, + "max_score": null, + "hits": [] + }, + "aggregations": { + "environments": { + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0, + "buckets": [ + { + "key": "production", + "doc_count": 27643 + }, + { + "key": "testing", + "doc_count": 960 + }, + { + "key": "ENVIRONMENT_NOT_DEFINED", + "doc_count": 63 + } + ] + } + } + } + }, + "startTime": 1636919683285, + "stats": { + "kibanaApiQueryParameters": { + "label": "Kibana API query parameters", + "description": "The query parameters used in the Kibana API request that initiated the Elasticsearch request.", + "value": "{\n \"start\": \"2021-11-14T19:39:00.000Z\",\n \"end\": \"2021-11-14T19:54:32.329Z\",\n \"_inspect\": \"true\"\n}" + }, + "kibanaApiRoute": { + "label": "Kibana API route", + "description": "The route of the Kibana API request that initiated the Elasticsearch request.", + "value": "GET /internal/apm/environments" + }, + "indexPattern": { + "label": "Index pattern", + "value": [ + "traces-apm*,apm-*", + "metrics-apm*,apm-*", + "logs-apm*,apm-*" + ], + "description": "The index pattern that connected to the Elasticsearch indices." + }, + "hits": { + "label": "Hits", + "value": "0", + "description": "The number of documents returned by the query." + }, + "queryTime": { + "label": "Query time", + "value": "10ms", + "description": "The time it took to process the query. Does not include the time to send the request or parse it in the browser." + }, + "hitsTotal": { + "label": "Hits (total)", + "value": "> 10000", + "description": "The number of documents that match the query." + } + }, + "status": 1 + } + ] +} +``` diff --git a/x-pack/plugins/apm/dev_docs/telemetry.md b/x-pack/plugins/apm/dev_docs/telemetry.md index 5462fbcc3b36c..27b9e57447467 100644 --- a/x-pack/plugins/apm/dev_docs/telemetry.md +++ b/x-pack/plugins/apm/dev_docs/telemetry.md @@ -29,7 +29,7 @@ Once uploaded to the telemetry cluster, the data telemetry is stored in ### Generating sample data -The script in `scripts/upload-telemetry-data` can generate sample telemetry data and upload it to a cluster of your choosing. +The script in `scripts/upload_telemetry_data` can generate sample telemetry data and upload it to a cluster of your choosing. You'll need to set the `GITHUB_TOKEN` environment variable to a token that has `repo` scope so it can read from the [elastic/telemetry](https://github.com/elastic/telemetry) repository. (You probably have a token that works for this in diff --git a/x-pack/plugins/apm/dev_docs/typescript.md b/x-pack/plugins/apm/dev_docs/typescript.md index 6de61b665a1b1..110c4a2d1ec97 100644 --- a/x-pack/plugins/apm/dev_docs/typescript.md +++ b/x-pack/plugins/apm/dev_docs/typescript.md @@ -4,8 +4,8 @@ Kibana and X-Pack are very large TypeScript projects, and it comes at a cost. Ed To run the optimization: -`$ node x-pack/plugins/apm/scripts/optimize-tsconfig` +`$ node x-pack/plugins/apm/scripts/optimize_tsconfig` To undo the optimization: -`$ node x-pack/plugins/apm/scripts/unoptimize-tsconfig` +`$ node x-pack/plugins/apm/scripts/unoptimize_tsconfig` diff --git a/x-pack/plugins/apm/dev_docs/updating_functional_tests_archives.md b/x-pack/plugins/apm/dev_docs/updating_functional_tests_archives.md index 88e434d07d38f..3d1a5f0b00bb4 100644 --- a/x-pack/plugins/apm/dev_docs/updating_functional_tests_archives.md +++ b/x-pack/plugins/apm/dev_docs/updating_functional_tests_archives.md @@ -3,6 +3,6 @@ Some of our API tests use an archive generated by the [`esarchiver`](https://www.elastic.co/guide/en/kibana/current/development-tests.html#development-functional-tests) script. Updating the main archive (`apm_8.0.0`) is a scripted process, where a 30m snapshot is downloaded from a cluster running the [APM Integration Testing server](https://github.com/elastic/apm-integration-testing). The script will copy the generated archives into the `fixtures/es_archiver` folders of our test suites (currently `basic` and `trial`). It will also generate a file that contains metadata about the archive, that can be imported to get the time range of the snapshot. Usage: -`node x-pack/plugins/apm/scripts/create-functional-tests-archive --es-url=https://admin:changeme@localhost:9200 --kibana-url=https://localhost:5601` +`node x-pack/plugins/apm/scripts/create_functional_tests_archive --es-url=https://admin:changeme@localhost:9200 --kibana-url=https://localhost:5601` diff --git a/x-pack/plugins/apm/ftr_e2e/cypress_start.ts b/x-pack/plugins/apm/ftr_e2e/cypress_start.ts index c8ab216cbce5c..9e2b9daa5631f 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress_start.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress_start.ts @@ -11,7 +11,7 @@ import { argv } from 'yargs'; import Url from 'url'; import cypress from 'cypress'; import { FtrProviderContext } from './ftr_provider_context'; -import { createApmUsersAndRoles } from '../scripts/create-apm-users-and-roles/create_apm_users_and_roles'; +import { createApmUsersAndRoles } from '../scripts/create_apm_users_and_roles/create_apm_users_and_roles'; import { esArchiverLoad, esArchiverUnload } from './cypress/tasks/es_archiver'; export async function cypressStart( diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/span_flyout/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/span_flyout/index.tsx index 4921dfe0606c3..457daef851bcf 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/span_flyout/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/span_flyout/index.tsx @@ -101,11 +101,11 @@ export function SpanFlyout({ const stackframes = span.span.stacktrace; const codeLanguage = parentTransaction?.service.language?.name; const spanDb = span.span.db; - const httpContext = span.span.http; const spanTypes = getSpanTypes(span); - const spanHttpStatusCode = httpContext?.response?.status_code; - const spanHttpUrl = httpContext?.url?.original; - const spanHttpMethod = httpContext?.method; + const spanHttpStatusCode = + span.http?.response?.status_code || span.span?.http?.response?.status_code; + const spanHttpUrl = span.url?.original || span.span?.http?.url?.original; + const spanHttpMethod = span.http?.request?.method || span.span?.http?.method; return ( diff --git a/x-pack/plugins/apm/public/components/shared/charts/metrics_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/metrics_chart/index.tsx index 9f437a95e7dd9..1c2b7fdeb7714 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/metrics_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/metrics_chart/index.tsx @@ -7,6 +7,7 @@ import { EuiTitle } from '@elastic/eui'; import React from 'react'; +import { APIReturnType } from '../../../../services/rest/createCallApmApi'; import { asDecimal, asInteger, @@ -14,8 +15,6 @@ import { getDurationFormatter, getFixedByteFormatter, } from '../../../../../common/utils/formatters'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { GenericMetricsChart } from '../../../../../server/lib/metrics/transform_metrics_chart'; import { Maybe } from '../../../../../typings/common'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; import { TimeseriesChart } from '../timeseries_chart'; @@ -24,7 +23,11 @@ import { getResponseTimeTickFormatter, } from '../transaction_charts/helper'; -function getYTickFormatter(chart: GenericMetricsChart) { +type MetricChartApiResponse = + APIReturnType<'GET /internal/apm/services/{serviceName}/metrics/charts'>; +type MetricChart = MetricChartApiResponse['charts'][0]; + +function getYTickFormatter(chart: MetricChart) { const max = getMaxY(chart.series); switch (chart.yUnit) { @@ -50,7 +53,7 @@ function getYTickFormatter(chart: GenericMetricsChart) { interface Props { start: Maybe; end: Maybe; - chart: GenericMetricsChart; + chart: MetricChart; fetchStatus: FETCH_STATUS; } diff --git a/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts index ef360698192e1..15037ecf6adb8 100644 --- a/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts @@ -5,14 +5,16 @@ * 2.0. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { MetricsChartsByAgentAPIResponse } from '../../server/lib/metrics/get_metrics_chart_data_by_agent'; +import type { APIReturnType } from '../services/rest/createCallApmApi'; import { useApmServiceContext } from '../context/apm_service/use_apm_service_context'; import { useFetcher } from './use_fetcher'; import { useTimeRange } from './use_time_range'; import { useApmParams } from './use_apm_params'; -const INITIAL_DATA: MetricsChartsByAgentAPIResponse = { +type MetricChartApiResponse = + APIReturnType<'GET /internal/apm/services/{serviceName}/metrics/charts'>; + +const INITIAL_DATA: MetricChartApiResponse = { charts: [], }; diff --git a/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx index f0d6dc72b72a5..7df52b20376b0 100644 --- a/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx +++ b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx @@ -4,13 +4,17 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiButton } from '@elastic/eui'; -import { EuiFlexItem } from '@elastic/eui'; -import { EuiFlexGroup } from '@elastic/eui'; -import { EuiPanel } from '@elastic/eui'; -import { EuiCard } from '@elastic/eui'; -import { EuiImage } from '@elastic/eui'; -import { EuiLoadingSpinner } from '@elastic/eui'; +import { + EuiButton, + EuiFlexItem, + EuiFlexGroup, + EuiPanel, + EuiCard, + EuiImage, + EuiLoadingSpinner, + EuiText, + EuiSpacer, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { HttpStart } from 'kibana/public'; import React, { useEffect, useState } from 'react'; @@ -90,18 +94,32 @@ function TutorialFleetInstructions({ http, basePath, isDarkTheme }: Props) { } )} footer={ - - {i18n.translate( - 'xpack.apm.tutorial.apmServer.fleet.apmIntegration.button', - { - defaultMessage: 'APM integration', - } - )} - + <> + + {i18n.translate( + 'xpack.apm.tutorial.apmServer.fleet.apmIntegration.button', + { + defaultMessage: 'APM integration', + } + )} + + + +

+ {i18n.translate( + 'xpack.apm.tutorial.apmServer.fleet.apmIntegration.description', + { + defaultMessage: + 'Fleet allows you to centrally manage Elastic Agents running the APM integration. The default option is to install a Fleet Server on a dedicated host. For setups without a dedicated host, we recommend following the instructions to install the standalone APM Server for your operating system by selecting the respective tab above.', + } + )} +

+ + } /> diff --git a/x-pack/plugins/apm/scripts/aggregate-latency-metrics.js b/x-pack/plugins/apm/scripts/aggregate_latency_metrics.js similarity index 92% rename from x-pack/plugins/apm/scripts/aggregate-latency-metrics.js rename to x-pack/plugins/apm/scripts/aggregate_latency_metrics.js index 350c3e4256b0b..9cbfe73eeb6dc 100644 --- a/x-pack/plugins/apm/scripts/aggregate-latency-metrics.js +++ b/x-pack/plugins/apm/scripts/aggregate_latency_metrics.js @@ -10,7 +10,7 @@ require('@kbn/optimizer').registerNodeAutoTranspilation(); const { aggregateLatencyMetrics, -} = require('./aggregate-latency-metrics/index.ts'); +} = require('./aggregate_latency_metrics/index.ts'); aggregateLatencyMetrics().catch((err) => { if (err.meta && err.meta.body) { diff --git a/x-pack/plugins/apm/scripts/aggregate-latency-metrics/index.ts b/x-pack/plugins/apm/scripts/aggregate_latency_metrics/index.ts similarity index 99% rename from x-pack/plugins/apm/scripts/aggregate-latency-metrics/index.ts rename to x-pack/plugins/apm/scripts/aggregate_latency_metrics/index.ts index d5cc9a63dbfcd..f9aa943b5eceb 100644 --- a/x-pack/plugins/apm/scripts/aggregate-latency-metrics/index.ts +++ b/x-pack/plugins/apm/scripts/aggregate_latency_metrics/index.ts @@ -24,7 +24,7 @@ import { TRANSACTION_RESULT, PROCESSOR_EVENT, } from '../../common/elasticsearch_fieldnames'; -import { createOrUpdateIndex } from '../shared/create-or-update-index'; +import { createOrUpdateIndex } from '../shared/create_or_update_index'; import { parseIndexUrl } from '../shared/parse_index_url'; import { ESClient, getEsClient } from '../shared/get_es_client'; diff --git a/x-pack/plugins/apm/scripts/create-apm-users-and-roles.js b/x-pack/plugins/apm/scripts/create_apm_users_and_roles.js similarity index 92% rename from x-pack/plugins/apm/scripts/create-apm-users-and-roles.js rename to x-pack/plugins/apm/scripts/create_apm_users_and_roles.js index d64364cb38928..8d8b0c2d3d813 100644 --- a/x-pack/plugins/apm/scripts/create-apm-users-and-roles.js +++ b/x-pack/plugins/apm/scripts/create_apm_users_and_roles.js @@ -20,4 +20,4 @@ // eslint-disable-next-line import/no-extraneous-dependencies require('@kbn/optimizer').registerNodeAutoTranspilation(); -require('./create-apm-users-and-roles/create_apm_users_and_roles_cli.ts'); +require('./create_apm_users_and_roles/create_apm_users_and_roles_cli.ts'); diff --git a/x-pack/plugins/apm/scripts/create-apm-users-and-roles/create_apm_users_and_roles.ts b/x-pack/plugins/apm/scripts/create_apm_users_and_roles/create_apm_users_and_roles.ts similarity index 100% rename from x-pack/plugins/apm/scripts/create-apm-users-and-roles/create_apm_users_and_roles.ts rename to x-pack/plugins/apm/scripts/create_apm_users_and_roles/create_apm_users_and_roles.ts diff --git a/x-pack/plugins/apm/scripts/create-apm-users-and-roles/create_apm_users_and_roles_cli.ts b/x-pack/plugins/apm/scripts/create_apm_users_and_roles/create_apm_users_and_roles_cli.ts similarity index 100% rename from x-pack/plugins/apm/scripts/create-apm-users-and-roles/create_apm_users_and_roles_cli.ts rename to x-pack/plugins/apm/scripts/create_apm_users_and_roles/create_apm_users_and_roles_cli.ts diff --git a/x-pack/plugins/apm/scripts/create-apm-users-and-roles/helpers/call_kibana.ts b/x-pack/plugins/apm/scripts/create_apm_users_and_roles/helpers/call_kibana.ts similarity index 100% rename from x-pack/plugins/apm/scripts/create-apm-users-and-roles/helpers/call_kibana.ts rename to x-pack/plugins/apm/scripts/create_apm_users_and_roles/helpers/call_kibana.ts diff --git a/x-pack/plugins/apm/scripts/create-apm-users-and-roles/helpers/create_or_update_user.ts b/x-pack/plugins/apm/scripts/create_apm_users_and_roles/helpers/create_or_update_user.ts similarity index 100% rename from x-pack/plugins/apm/scripts/create-apm-users-and-roles/helpers/create_or_update_user.ts rename to x-pack/plugins/apm/scripts/create_apm_users_and_roles/helpers/create_or_update_user.ts diff --git a/x-pack/plugins/apm/scripts/create-apm-users-and-roles/helpers/create_role.ts b/x-pack/plugins/apm/scripts/create_apm_users_and_roles/helpers/create_role.ts similarity index 100% rename from x-pack/plugins/apm/scripts/create-apm-users-and-roles/helpers/create_role.ts rename to x-pack/plugins/apm/scripts/create_apm_users_and_roles/helpers/create_role.ts diff --git a/x-pack/plugins/apm/scripts/create-apm-users-and-roles/helpers/get_version.ts b/x-pack/plugins/apm/scripts/create_apm_users_and_roles/helpers/get_version.ts similarity index 100% rename from x-pack/plugins/apm/scripts/create-apm-users-and-roles/helpers/get_version.ts rename to x-pack/plugins/apm/scripts/create_apm_users_and_roles/helpers/get_version.ts diff --git a/x-pack/plugins/apm/scripts/create-apm-users-and-roles/roles/power_user_role.ts b/x-pack/plugins/apm/scripts/create_apm_users_and_roles/roles/power_user_role.ts similarity index 100% rename from x-pack/plugins/apm/scripts/create-apm-users-and-roles/roles/power_user_role.ts rename to x-pack/plugins/apm/scripts/create_apm_users_and_roles/roles/power_user_role.ts diff --git a/x-pack/plugins/apm/scripts/create-apm-users-and-roles/roles/read_only_user_role.ts b/x-pack/plugins/apm/scripts/create_apm_users_and_roles/roles/read_only_user_role.ts similarity index 100% rename from x-pack/plugins/apm/scripts/create-apm-users-and-roles/roles/read_only_user_role.ts rename to x-pack/plugins/apm/scripts/create_apm_users_and_roles/roles/read_only_user_role.ts diff --git a/x-pack/plugins/apm/scripts/create-functional-tests-archive.js b/x-pack/plugins/apm/scripts/create_functional_tests_archive.js similarity index 88% rename from x-pack/plugins/apm/scripts/create-functional-tests-archive.js rename to x-pack/plugins/apm/scripts/create_functional_tests_archive.js index 6e979a2802a85..55290f00a6d8c 100644 --- a/x-pack/plugins/apm/scripts/create-functional-tests-archive.js +++ b/x-pack/plugins/apm/scripts/create_functional_tests_archive.js @@ -9,4 +9,4 @@ // eslint-disable-next-line import/no-extraneous-dependencies require('@kbn/optimizer').registerNodeAutoTranspilation(); -require('./create-functional-tests-archive/index.ts'); +require('./create_functional_tests_archive/index.ts'); diff --git a/x-pack/plugins/apm/scripts/create-functional-tests-archive/index.ts b/x-pack/plugins/apm/scripts/create_functional_tests_archive/index.ts similarity index 100% rename from x-pack/plugins/apm/scripts/create-functional-tests-archive/index.ts rename to x-pack/plugins/apm/scripts/create_functional_tests_archive/index.ts diff --git a/x-pack/plugins/apm/scripts/optimize-tsconfig.js b/x-pack/plugins/apm/scripts/optimize_tsconfig.js similarity index 84% rename from x-pack/plugins/apm/scripts/optimize-tsconfig.js rename to x-pack/plugins/apm/scripts/optimize_tsconfig.js index 8e7268141a993..fa15f0f582e89 100644 --- a/x-pack/plugins/apm/scripts/optimize-tsconfig.js +++ b/x-pack/plugins/apm/scripts/optimize_tsconfig.js @@ -5,7 +5,7 @@ * 2.0. */ -const { optimizeTsConfig } = require('./optimize-tsconfig/optimize'); +const { optimizeTsConfig } = require('./optimize_tsconfig/optimize'); optimizeTsConfig().catch((err) => { console.error(err); diff --git a/x-pack/plugins/apm/scripts/optimize-tsconfig/optimize.js b/x-pack/plugins/apm/scripts/optimize_tsconfig/optimize.js similarity index 98% rename from x-pack/plugins/apm/scripts/optimize-tsconfig/optimize.js rename to x-pack/plugins/apm/scripts/optimize_tsconfig/optimize.js index 68cba9b397f2e..fa45bfa12f3b7 100644 --- a/x-pack/plugins/apm/scripts/optimize-tsconfig/optimize.js +++ b/x-pack/plugins/apm/scripts/optimize_tsconfig/optimize.js @@ -110,7 +110,7 @@ async function optimizeTsConfig() { await setIgnoreChanges(); // eslint-disable-next-line no-console console.log( - 'Created an optimized tsconfig.json for APM. To undo these changes, run `./scripts/unoptimize-tsconfig.js`' + 'Created an optimized tsconfig.json for APM. To undo these changes, run `./scripts/unoptimize_tsconfig.js`' ); } diff --git a/x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js b/x-pack/plugins/apm/scripts/optimize_tsconfig/paths.js similarity index 93% rename from x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js rename to x-pack/plugins/apm/scripts/optimize_tsconfig/paths.js index 3a21a89e30917..df430dabafc9c 100644 --- a/x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js +++ b/x-pack/plugins/apm/scripts/optimize_tsconfig/paths.js @@ -9,7 +9,7 @@ const path = require('path'); const kibanaRoot = path.resolve(__dirname, '../../../../..'); const tsconfigTpl = path.resolve(__dirname, './tsconfig.json'); -const tsconfigTplTest = path.resolve(__dirname, './test-tsconfig.json'); +const tsconfigTplTest = path.resolve(__dirname, './test_tsconfig.json'); const filesToIgnore = [ path.resolve(kibanaRoot, 'tsconfig.json'), diff --git a/x-pack/plugins/apm/scripts/optimize-tsconfig/test-tsconfig.json b/x-pack/plugins/apm/scripts/optimize_tsconfig/test_tsconfig.json similarity index 100% rename from x-pack/plugins/apm/scripts/optimize-tsconfig/test-tsconfig.json rename to x-pack/plugins/apm/scripts/optimize_tsconfig/test_tsconfig.json diff --git a/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json b/x-pack/plugins/apm/scripts/optimize_tsconfig/tsconfig.json similarity index 100% rename from x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json rename to x-pack/plugins/apm/scripts/optimize_tsconfig/tsconfig.json diff --git a/x-pack/plugins/apm/scripts/optimize-tsconfig/unoptimize.js b/x-pack/plugins/apm/scripts/optimize_tsconfig/unoptimize.js similarity index 100% rename from x-pack/plugins/apm/scripts/optimize-tsconfig/unoptimize.js rename to x-pack/plugins/apm/scripts/optimize_tsconfig/unoptimize.js diff --git a/x-pack/plugins/apm/scripts/precommit.js b/x-pack/plugins/apm/scripts/precommit.js index a259cf932c912..ab19094c4e610 100644 --- a/x-pack/plugins/apm/scripts/precommit.js +++ b/x-pack/plugins/apm/scripts/precommit.js @@ -40,8 +40,8 @@ const tasks = new Listr( resolve( __dirname, useOptimizedTsConfig - ? './optimize-tsconfig.js' - : './unoptimize-tsconfig.js' + ? './optimize_tsconfig.js' + : './unoptimize_tsconfig.js' ), ], execaOpts diff --git a/x-pack/plugins/apm/scripts/shared/create-or-update-index.ts b/x-pack/plugins/apm/scripts/shared/create_or_update_index.ts similarity index 100% rename from x-pack/plugins/apm/scripts/shared/create-or-update-index.ts rename to x-pack/plugins/apm/scripts/shared/create_or_update_index.ts diff --git a/x-pack/plugins/apm/scripts/shared/download-telemetry-template.ts b/x-pack/plugins/apm/scripts/shared/download_telemetry_template.ts similarity index 100% rename from x-pack/plugins/apm/scripts/shared/download-telemetry-template.ts rename to x-pack/plugins/apm/scripts/shared/download_telemetry_template.ts diff --git a/x-pack/plugins/apm/scripts/shared/get-http-auth.ts b/x-pack/plugins/apm/scripts/shared/get_http_auth.ts similarity index 91% rename from x-pack/plugins/apm/scripts/shared/get-http-auth.ts rename to x-pack/plugins/apm/scripts/shared/get_http_auth.ts index 1adc5de10bed0..9a51000228d08 100644 --- a/x-pack/plugins/apm/scripts/shared/get-http-auth.ts +++ b/x-pack/plugins/apm/scripts/shared/get_http_auth.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { KibanaConfig } from './read-kibana-config'; +import { KibanaConfig } from './read_kibana_config'; export const getHttpAuth = (config: KibanaConfig) => { const httpAuth = diff --git a/x-pack/plugins/apm/scripts/shared/read-kibana-config.ts b/x-pack/plugins/apm/scripts/shared/read_kibana_config.ts similarity index 100% rename from x-pack/plugins/apm/scripts/shared/read-kibana-config.ts rename to x-pack/plugins/apm/scripts/shared/read_kibana_config.ts diff --git a/x-pack/plugins/apm/scripts/unoptimize-tsconfig.js b/x-pack/plugins/apm/scripts/unoptimize_tsconfig.js similarity index 84% rename from x-pack/plugins/apm/scripts/unoptimize-tsconfig.js rename to x-pack/plugins/apm/scripts/unoptimize_tsconfig.js index 7ca4017ccc9ff..330abd963393e 100644 --- a/x-pack/plugins/apm/scripts/unoptimize-tsconfig.js +++ b/x-pack/plugins/apm/scripts/unoptimize_tsconfig.js @@ -5,7 +5,7 @@ * 2.0. */ -const { unoptimizeTsConfig } = require('./optimize-tsconfig/unoptimize'); +const { unoptimizeTsConfig } = require('./optimize_tsconfig/unoptimize'); unoptimizeTsConfig().catch((err) => { console.error(err); diff --git a/x-pack/plugins/apm/scripts/upload-telemetry-data.js b/x-pack/plugins/apm/scripts/upload_telemetry_data.js similarity index 90% rename from x-pack/plugins/apm/scripts/upload-telemetry-data.js rename to x-pack/plugins/apm/scripts/upload_telemetry_data.js index d393ddcf608fa..f101fb73aba95 100644 --- a/x-pack/plugins/apm/scripts/upload-telemetry-data.js +++ b/x-pack/plugins/apm/scripts/upload_telemetry_data.js @@ -9,4 +9,4 @@ // eslint-disable-next-line import/no-extraneous-dependencies require('@kbn/optimizer').registerNodeAutoTranspilation(); -require('./upload-telemetry-data/index.ts'); +require('./upload_telemetry_data/index.ts'); diff --git a/x-pack/plugins/apm/scripts/upload-telemetry-data/generate-sample-documents.ts b/x-pack/plugins/apm/scripts/upload_telemetry_data/generate_sample_documents.ts similarity index 100% rename from x-pack/plugins/apm/scripts/upload-telemetry-data/generate-sample-documents.ts rename to x-pack/plugins/apm/scripts/upload_telemetry_data/generate_sample_documents.ts diff --git a/x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts b/x-pack/plugins/apm/scripts/upload_telemetry_data/index.ts similarity index 93% rename from x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts rename to x-pack/plugins/apm/scripts/upload_telemetry_data/index.ts index 990376ca3e6ba..ece8e33e6aacb 100644 --- a/x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts +++ b/x-pack/plugins/apm/scripts/upload_telemetry_data/index.ts @@ -17,12 +17,12 @@ import { argv } from 'yargs'; import { Logger } from 'kibana/server'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { CollectTelemetryParams } from '../../server/lib/apm_telemetry/collect_data_telemetry'; -import { downloadTelemetryTemplate } from '../shared/download-telemetry-template'; +import { downloadTelemetryTemplate } from '../shared/download_telemetry_template'; import { mergeApmTelemetryMapping } from '../../common/apm_telemetry'; -import { generateSampleDocuments } from './generate-sample-documents'; -import { readKibanaConfig } from '../shared/read-kibana-config'; -import { getHttpAuth } from '../shared/get-http-auth'; -import { createOrUpdateIndex } from '../shared/create-or-update-index'; +import { generateSampleDocuments } from './generate_sample_documents'; +import { readKibanaConfig } from '../shared/read_kibana_config'; +import { getHttpAuth } from '../shared/get_http_auth'; +import { createOrUpdateIndex } from '../shared/create_or_update_index'; import { getEsClient } from '../shared/get_es_client'; async function uploadData() { diff --git a/x-pack/plugins/apm/server/lib/correlations/queries/get_request_base.test.ts b/x-pack/plugins/apm/server/lib/correlations/queries/get_request_base.test.ts index eb771e1e1aaf4..9df412b65b8d3 100644 --- a/x-pack/plugins/apm/server/lib/correlations/queries/get_request_base.test.ts +++ b/x-pack/plugins/apm/server/lib/correlations/queries/get_request_base.test.ts @@ -10,35 +10,27 @@ import { getRequestBase } from './get_request_base'; describe('correlations', () => { describe('getRequestBase', () => { - it('returns the request base parameters', () => { + it('defaults to not setting `ignore_throttled`', () => { const requestBase = getRequestBase({ index: 'apm-*', - includeFrozen: true, environment: ENVIRONMENT_ALL.value, kuery: '', start: 1577836800000, end: 1609459200000, }); - expect(requestBase).toEqual({ - index: 'apm-*', - ignore_throttled: false, - ignore_unavailable: true, - }); + expect(requestBase.ignore_throttled).toEqual(undefined); }); - it('defaults ignore_throttled to true', () => { + it('adds `ignore_throttled=false` when `includeFrozen=true`', () => { const requestBase = getRequestBase({ index: 'apm-*', + includeFrozen: true, environment: ENVIRONMENT_ALL.value, kuery: '', start: 1577836800000, end: 1609459200000, }); - expect(requestBase).toEqual({ - index: 'apm-*', - ignore_throttled: true, - ignore_unavailable: true, - }); + expect(requestBase.ignore_throttled).toEqual(false); }); }); }); diff --git a/x-pack/plugins/apm/server/lib/correlations/queries/get_request_base.ts b/x-pack/plugins/apm/server/lib/correlations/queries/get_request_base.ts index 5ab4e3b26122d..02719ee3929ce 100644 --- a/x-pack/plugins/apm/server/lib/correlations/queries/get_request_base.ts +++ b/x-pack/plugins/apm/server/lib/correlations/queries/get_request_base.ts @@ -13,6 +13,6 @@ export const getRequestBase = ({ }: CorrelationsParams) => ({ index, // matches APM's event client settings - ignore_throttled: includeFrozen === undefined ? true : !includeFrozen, + ...(includeFrozen ? { ignore_throttled: false } : {}), ignore_unavailable: true, }); diff --git a/x-pack/plugins/apm/server/lib/correlations/queries/query_field_candidates.test.ts b/x-pack/plugins/apm/server/lib/correlations/queries/query_field_candidates.test.ts index 02af6637e5bb3..957e2393558ed 100644 --- a/x-pack/plugins/apm/server/lib/correlations/queries/query_field_candidates.test.ts +++ b/x-pack/plugins/apm/server/lib/correlations/queries/query_field_candidates.test.ts @@ -88,7 +88,7 @@ describe('query_field_candidates', () => { size: 1000, }, index: params.index, - ignore_throttled: !params.includeFrozen, + ignore_throttled: params.includeFrozen ? false : undefined, ignore_unavailable: true, }); }); diff --git a/x-pack/plugins/apm/server/lib/correlations/queries/query_histogram.test.ts b/x-pack/plugins/apm/server/lib/correlations/queries/query_histogram.test.ts index 3c5726ee586da..c9ea70452bdb0 100644 --- a/x-pack/plugins/apm/server/lib/correlations/queries/query_histogram.test.ts +++ b/x-pack/plugins/apm/server/lib/correlations/queries/query_histogram.test.ts @@ -63,7 +63,7 @@ describe('query_histogram', () => { size: 0, }, index: params.index, - ignore_throttled: !params.includeFrozen, + ignore_throttled: params.includeFrozen ? false : undefined, ignore_unavailable: true, }); }); diff --git a/x-pack/plugins/apm/server/lib/correlations/queries/query_histogram_range_steps.test.ts b/x-pack/plugins/apm/server/lib/correlations/queries/query_histogram_range_steps.test.ts index 3a79b4375e4a5..526207beaabce 100644 --- a/x-pack/plugins/apm/server/lib/correlations/queries/query_histogram_range_steps.test.ts +++ b/x-pack/plugins/apm/server/lib/correlations/queries/query_histogram_range_steps.test.ts @@ -66,7 +66,7 @@ describe('query_histogram_range_steps', () => { size: 0, }, index: params.index, - ignore_throttled: !params.includeFrozen, + ignore_throttled: params.includeFrozen ? false : undefined, ignore_unavailable: true, }); }); diff --git a/x-pack/plugins/apm/server/lib/correlations/queries/query_percentiles.test.ts b/x-pack/plugins/apm/server/lib/correlations/queries/query_percentiles.test.ts index 67b2f580e3f4d..4e637d1ca6f4a 100644 --- a/x-pack/plugins/apm/server/lib/correlations/queries/query_percentiles.test.ts +++ b/x-pack/plugins/apm/server/lib/correlations/queries/query_percentiles.test.ts @@ -65,7 +65,7 @@ describe('query_percentiles', () => { track_total_hits: true, }, index: params.index, - ignore_throttled: !params.includeFrozen, + ignore_throttled: params.includeFrozen ? false : undefined, ignore_unavailable: true, }); }); diff --git a/x-pack/plugins/apm/server/lib/correlations/queries/query_ranges.test.ts b/x-pack/plugins/apm/server/lib/correlations/queries/query_ranges.test.ts index 3cafc17e2681b..5c43b771f69ee 100644 --- a/x-pack/plugins/apm/server/lib/correlations/queries/query_ranges.test.ts +++ b/x-pack/plugins/apm/server/lib/correlations/queries/query_ranges.test.ts @@ -82,7 +82,7 @@ describe('query_ranges', () => { size: 0, }, index: params.index, - ignore_throttled: !params.includeFrozen, + ignore_throttled: params.includeFrozen ? false : undefined, ignore_unavailable: true, }); }); diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts index 2c8d0f6998bf8..4d4c935d20e76 100644 --- a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts +++ b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts @@ -5,6 +5,10 @@ * 2.0. */ +import { AggregationsTermsAggregationOrder } from '@elastic/elasticsearch/lib/api/types'; +import { ProcessorEvent } from '../../../common/processor_event'; +import { environmentQuery } from '../../../common/utils/environment_query'; +import { kqlQuery, rangeQuery } from '../../../../observability/server'; import { ERROR_CULPRIT, ERROR_EXC_HANDLED, @@ -12,9 +16,8 @@ import { ERROR_EXC_TYPE, ERROR_GROUP_ID, ERROR_LOG_MESSAGE, + SERVICE_NAME, } from '../../../common/elasticsearch_fieldnames'; -import { getErrorGroupsProjection } from '../../projections/errors'; -import { mergeProjection } from '../../projections/util/merge_projection'; import { getErrorName } from '../helpers/get_error_name'; import { Setup } from '../helpers/setup_request'; @@ -42,27 +45,31 @@ export async function getErrorGroups({ // sort buckets by last occurrence of error const sortByLatestOccurrence = sortField === 'latestOccurrenceAt'; - const projection = getErrorGroupsProjection({ - environment, - kuery, - serviceName, - start, - end, - }); - - const order = sortByLatestOccurrence - ? { - max_timestamp: sortDirection, - } + const maxTimestampAggKey = 'max_timestamp'; + const order: AggregationsTermsAggregationOrder = sortByLatestOccurrence + ? { [maxTimestampAggKey]: sortDirection } : { _count: sortDirection }; - const params = mergeProjection(projection, { + const params = { + apm: { + events: [ProcessorEvent.error as const], + }, body: { size: 0, + query: { + bool: { + filter: [ + { term: { [SERVICE_NAME]: serviceName } }, + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ], + }, + }, aggs: { error_groups: { terms: { - ...projection.body.aggs.error_groups.terms, + field: ERROR_GROUP_ID, size: 500, order, }, @@ -83,19 +90,13 @@ export async function getErrorGroups({ }, }, ...(sortByLatestOccurrence - ? { - max_timestamp: { - max: { - field: '@timestamp', - }, - }, - } + ? { [maxTimestampAggKey]: { max: { field: '@timestamp' } } } : {}), }, }, }, }, - }); + }; const resp = await apmEventClient.search('get_error_groups', params); diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts index 26b00b075a5c8..a45d314c33719 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts @@ -100,7 +100,7 @@ export function createApmEventClient({ const searchParams = { ...withPossibleLegacyDataFilter, - ignore_throttled: !includeFrozen, + ...(includeFrozen ? { ignore_throttled: false } : {}), ignore_unavailable: true, preference: 'any', }; diff --git a/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts index 52e9e5a8ea74a..7b3201095106e 100644 --- a/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts +++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts @@ -132,7 +132,6 @@ describe('setupRequest', () => { }, }, ignore_unavailable: true, - ignore_throttled: true, preference: 'any', }, { @@ -251,7 +250,7 @@ describe('without a bool filter', () => { }); describe('with includeFrozen=false', () => { - it('sets `ignore_throttled=true`', async () => { + it('should NOT send "ignore_throttled:true" in the request', async () => { const mockResources = getMockResources(); // mock includeFrozen to return false @@ -268,7 +267,7 @@ describe('with includeFrozen=false', () => { const params = mockResources.context.core.elasticsearch.client.asCurrentUser.search.mock .calls[0][0]; - expect(params.ignore_throttled).toBe(true); + expect(params.ignore_throttled).toBe(undefined); }); }); diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts index 62e23d19b00bd..54e10bd8adde0 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts @@ -9,7 +9,7 @@ import { Setup } from '../../helpers/setup_request'; import { getCPUChartData } from './shared/cpu'; import { getMemoryChartData } from './shared/memory'; -export async function getDefaultMetricsCharts({ +export function getDefaultMetricsCharts({ environment, kuery, serviceName, @@ -24,10 +24,8 @@ export async function getDefaultMetricsCharts({ start: number; end: number; }) { - const charts = await Promise.all([ + return Promise.all([ getCPUChartData({ environment, kuery, setup, serviceName, start, end }), getMemoryChartData({ environment, kuery, setup, serviceName, start, end }), ]); - - return { charts }; } diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts index 117b372d445d2..2d4cf2f70ab5f 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts @@ -11,17 +11,26 @@ import { isFiniteNumber } from '../../../../../../common/utils/is_finite_number' import { Setup } from '../../../../helpers/setup_request'; import { getMetricsDateHistogramParams } from '../../../../helpers/metrics'; import { ChartBase } from '../../../types'; -import { getMetricsProjection } from '../../../../../projections/metrics'; -import { mergeProjection } from '../../../../../projections/util/merge_projection'; + import { AGENT_NAME, LABEL_NAME, METRIC_JAVA_GC_COUNT, METRIC_JAVA_GC_TIME, + SERVICE_NAME, } from '../../../../../../common/elasticsearch_fieldnames'; import { getBucketSize } from '../../../../helpers/get_bucket_size'; import { getVizColorForIndex } from '../../../../../../common/viz_colors'; import { JAVA_AGENT_NAMES } from '../../../../../../common/agent_name'; +import { + environmentQuery, + serviceNodeNameQuery, +} from '../../../../../../common/utils/environment_query'; +import { + kqlQuery, + rangeQuery, +} from '../../../../../../../observability/server'; +import { ProcessorEvent } from '../../../../../../common/processor_event'; export async function fetchAndTransformGcMetrics({ environment, @@ -50,26 +59,24 @@ export async function fetchAndTransformGcMetrics({ const { bucketSize } = getBucketSize({ start, end }); - const projection = getMetricsProjection({ - environment, - kuery, - serviceName, - serviceNodeName, - start, - end, - }); - // GC rate and time are reported by the agents as monotonically // increasing counters, which means that we have to calculate // the delta in an es query. In the future agent might start // reporting deltas. - const params = mergeProjection(projection, { + const params = { + apm: { + events: [ProcessorEvent.metric], + }, body: { size: 0, query: { bool: { filter: [ - ...projection.body.query.bool.filter, + { term: { [SERVICE_NAME]: serviceName } }, + ...serviceNodeNameQuery(serviceNodeName), + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), { exists: { field: fieldName } }, { terms: { [AGENT_NAME]: JAVA_AGENT_NAMES } }, ], @@ -114,7 +121,7 @@ export async function fetchAndTransformGcMetrics({ }, }, }, - }); + }; const response = await apmEventClient.search(operationName, params); diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts index 164777539c9d5..9039bb19ebb78 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts @@ -32,7 +32,7 @@ export function getJavaMetricsCharts({ start: number; end: number; }) { - return withApmSpan('get_java_system_metric_charts', async () => { + return withApmSpan('get_java_system_metric_charts', () => { const options = { environment, kuery, @@ -43,7 +43,7 @@ export function getJavaMetricsCharts({ end, }; - const charts = await Promise.all([ + return Promise.all([ getCPUChartData(options), getMemoryChartData(options), getHeapMemoryChart(options), @@ -52,7 +52,5 @@ export function getJavaMetricsCharts({ getGcRateChart(options), getGcTimeChart(options), ]); - - return { charts }; }); } diff --git a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts index 581a0782e4d72..0a24179ed9fc6 100644 --- a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts +++ b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts @@ -5,15 +5,23 @@ * 2.0. */ -import { Overwrite, Unionize } from 'utility-types'; +import { Unionize } from 'utility-types'; +import { euiLightVars as theme } from '@kbn/ui-shared-deps-src/theme'; +import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import { getVizColorForIndex } from '../../../common/viz_colors'; import { AggregationOptionsByType } from '../../../../../../src/core/types/elasticsearch'; -import { getMetricsProjection } from '../../projections/metrics'; -import { mergeProjection } from '../../projections/util/merge_projection'; -import { APMEventESSearchRequest } from '../helpers/create_es_client/create_apm_event_client'; import { getMetricsDateHistogramParams } from '../helpers/metrics'; import { Setup } from '../helpers/setup_request'; -import { transformDataToMetricsChart } from './transform_metrics_chart'; import { ChartBase } from './types'; +import { + environmentQuery, + serviceNodeNameQuery, +} from '../../../common/utils/environment_query'; +import { kqlQuery, rangeQuery } from '../../../../observability/server'; +import { ProcessorEvent } from '../../../common/processor_event'; +import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames'; +import { APMEventESSearchRequest } from '../helpers/create_es_client/create_apm_event_client'; +import { PromiseReturnType } from '../../../../observability/typings/common'; type MetricsAggregationMap = Unionize<{ min: AggregationOptionsByType['min']; @@ -24,31 +32,20 @@ type MetricsAggregationMap = Unionize<{ type MetricAggs = Record; -export type GenericMetricsRequest = Overwrite< - APMEventESSearchRequest, - { - body: { - aggs: { - timeseriesData: { - date_histogram: AggregationOptionsByType['date_histogram']; - aggs: MetricAggs; - }; - } & MetricAggs; - }; - } ->; - -interface Filter { - exists?: { - field: string; - }; - term?: { - [key: string]: string; - }; - terms?: { - [key: string]: string[]; +export type GenericMetricsRequest = APMEventESSearchRequest & { + body: { + aggs: { + timeseriesData: { + date_histogram: AggregationOptionsByType['date_histogram']; + aggs: MetricAggs; + }; + } & MetricAggs; }; -} +}; + +export type GenericMetricsChart = PromiseReturnType< + typeof fetchAndTransformMetrics +>; export async function fetchAndTransformMetrics({ environment, @@ -72,26 +69,27 @@ export async function fetchAndTransformMetrics({ end: number; chartBase: ChartBase; aggs: T; - additionalFilters?: Filter[]; + additionalFilters?: QueryDslQueryContainer[]; operationName: string; }) { const { apmEventClient, config } = setup; - const projection = getMetricsProjection({ - environment, - kuery, - serviceName, - serviceNodeName, - start, - end, - }); - - const params: GenericMetricsRequest = mergeProjection(projection, { + const params: GenericMetricsRequest = { + apm: { + events: [ProcessorEvent.metric], + }, body: { size: 0, query: { bool: { - filter: [...projection.body.query.bool.filter, ...additionalFilters], + filter: [ + { term: { [SERVICE_NAME]: serviceName } }, + ...serviceNodeNameQuery(serviceNodeName), + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ...additionalFilters, + ], }, }, aggs: { @@ -106,9 +104,43 @@ export async function fetchAndTransformMetrics({ ...aggs, }, }, - }); + }; + + const { hits, aggregations } = await apmEventClient.search( + operationName, + params + ); + const timeseriesData = aggregations?.timeseriesData; - const response = await apmEventClient.search(operationName, params); + return { + title: chartBase.title, + key: chartBase.key, + yUnit: chartBase.yUnit, + series: + hits.total.value === 0 + ? [] + : Object.keys(chartBase.series).map((seriesKey, i) => { + // @ts-ignore + const overallValue = aggregations?.[seriesKey]?.value as number; - return transformDataToMetricsChart(response, chartBase); + return { + title: chartBase.series[seriesKey].title, + key: seriesKey, + type: chartBase.type, + color: + chartBase.series[seriesKey].color || + getVizColorForIndex(i, theme), + overallValue, + data: + timeseriesData?.buckets.map((bucket) => { + const { value } = bucket[seriesKey]; + const y = value === null || isNaN(value) ? null : value; + return { + x: bucket.key, + y, + }; + }) || [], + }; + }), + }; } diff --git a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts index 29991034f7c5e..611bb8196032c 100644 --- a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts +++ b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts @@ -8,12 +8,8 @@ import { Setup } from '../helpers/setup_request'; import { getJavaMetricsCharts } from './by_agent/java'; import { getDefaultMetricsCharts } from './by_agent/default'; -import { GenericMetricsChart } from './transform_metrics_chart'; import { isJavaAgentName } from '../../../common/agent_name'; - -export interface MetricsChartsByAgentAPIResponse { - charts: GenericMetricsChart[]; -} +import { GenericMetricsChart } from './fetch_and_transform_metrics'; export async function getMetricsChartDataByAgent({ environment, @@ -33,7 +29,7 @@ export async function getMetricsChartDataByAgent({ agentName: string; start: number; end: number; -}): Promise { +}): Promise { if (isJavaAgentName(agentName)) { return getJavaMetricsCharts({ environment, diff --git a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.test.ts b/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.test.ts deleted file mode 100644 index 107e2d5774816..0000000000000 --- a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { transformDataToMetricsChart } from './transform_metrics_chart'; -import { ChartType, YUnit } from '../../../typings/timeseries'; - -test('transformDataToMetricsChart should transform an ES result into a chart object', () => { - const response = { - hits: { total: { value: 5000 } }, - aggregations: { - a: { value: 1000 }, - b: { value: 1000 }, - c: { value: 1000 }, - timeseriesData: { - buckets: [ - { - a: { value: 10 }, - b: { value: 10 }, - c: { value: 10 }, - key: 1, - doc_count: 0, - }, - { - a: { value: 20 }, - b: { value: 20 }, - c: { value: 20 }, - key: 2, - doc_count: 0, - }, - { - a: { value: 30 }, - b: { value: 30 }, - c: { value: 30 }, - key: 3, - doc_count: 0, - }, - ], - }, - }, - } as any; - - const chartBase = { - title: 'Test Chart Title', - type: 'linemark' as ChartType, - key: 'test_chart_key', - yUnit: 'number' as YUnit, - series: { - a: { title: 'Series A', color: 'red' }, - b: { title: 'Series B', color: 'blue' }, - c: { title: 'Series C', color: 'green' }, - }, - }; - - const chart = transformDataToMetricsChart(response, chartBase); - - expect(chart).toMatchInlineSnapshot(` -Object { - "key": "test_chart_key", - "series": Array [ - Object { - "color": "red", - "data": Array [ - Object { - "x": 1, - "y": 10, - }, - Object { - "x": 2, - "y": 20, - }, - Object { - "x": 3, - "y": 30, - }, - ], - "key": "a", - "overallValue": 1000, - "title": "Series A", - "type": "linemark", - }, - Object { - "color": "blue", - "data": Array [ - Object { - "x": 1, - "y": 10, - }, - Object { - "x": 2, - "y": 20, - }, - Object { - "x": 3, - "y": 30, - }, - ], - "key": "b", - "overallValue": 1000, - "title": "Series B", - "type": "linemark", - }, - Object { - "color": "green", - "data": Array [ - Object { - "x": 1, - "y": 10, - }, - Object { - "x": 2, - "y": 20, - }, - Object { - "x": 3, - "y": 30, - }, - ], - "key": "c", - "overallValue": 1000, - "title": "Series C", - "type": "linemark", - }, - ], - "title": "Test Chart Title", - "yUnit": "number", -} -`); -}); diff --git a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts b/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts deleted file mode 100644 index fea853af93b84..0000000000000 --- a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { euiLightVars as theme } from '@kbn/ui-shared-deps-src/theme'; -import { ESSearchResponse } from '../../../../../../src/core/types/elasticsearch'; -import { getVizColorForIndex } from '../../../common/viz_colors'; -import { GenericMetricsRequest } from './fetch_and_transform_metrics'; -import { ChartBase } from './types'; - -export type GenericMetricsChart = ReturnType< - typeof transformDataToMetricsChart ->; - -export function transformDataToMetricsChart( - result: ESSearchResponse, - chartBase: ChartBase -) { - const { aggregations } = result; - const timeseriesData = aggregations?.timeseriesData; - - return { - title: chartBase.title, - key: chartBase.key, - yUnit: chartBase.yUnit, - series: - result.hits.total.value > 0 - ? Object.keys(chartBase.series).map((seriesKey, i) => { - const overallValue = aggregations?.[seriesKey]?.value; - - return { - title: chartBase.series[seriesKey].title, - key: seriesKey, - type: chartBase.type, - color: - chartBase.series[seriesKey].color || - getVizColorForIndex(i, theme), - overallValue, - data: - timeseriesData?.buckets.map((bucket) => { - const { value } = bucket[seriesKey]; - const y = value === null || isNaN(value) ? null : value; - return { - x: bucket.key, - y, - }; - }) || [], - }; - }) - : [], - }; -} diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts index aaf55413d9774..9f94bdd9275c0 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts @@ -156,7 +156,6 @@ async function getServicesData(options: IEnvOptions) { export type ConnectionsResponse = PromiseReturnType; export type ServicesResponse = PromiseReturnType; -export type ServiceMapAPIResponse = PromiseReturnType; export function getServiceMap(options: IEnvOptions) { return withApmSpan('get_service_map', async () => { diff --git a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap index 1b72fc8867570..e0591a90b1c19 100644 --- a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap @@ -35,11 +35,6 @@ Object { "service.name": "foo", }, }, - Object { - "term": Object { - "service.node.name": "bar", - }, - }, Object { "range": Object { "@timestamp": Object { @@ -49,6 +44,11 @@ Object { }, }, }, + Object { + "term": Object { + "service.node.name": "bar", + }, + }, ], }, }, @@ -92,6 +92,15 @@ Object { "service.name": "foo", }, }, + Object { + "range": Object { + "@timestamp": Object { + "format": "epoch_millis", + "gte": 0, + "lte": 50000, + }, + }, + }, Object { "bool": Object { "must_not": Array [ @@ -103,15 +112,6 @@ Object { ], }, }, - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 0, - "lte": 50000, - }, - }, - }, ], }, }, @@ -191,6 +191,7 @@ Object { ], }, }, + "size": 0, }, } `; diff --git a/x-pack/plugins/apm/server/lib/service_nodes/index.ts b/x-pack/plugins/apm/server/lib/service_nodes/index.ts index 0ae7274c3b33f..541fbb79c9e50 100644 --- a/x-pack/plugins/apm/server/lib/service_nodes/index.ts +++ b/x-pack/plugins/apm/server/lib/service_nodes/index.ts @@ -14,8 +14,13 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes'; import { asMutableArray } from '../../../common/utils/as_mutable_array'; -import { getServiceNodesProjection } from '../../projections/service_nodes'; -import { mergeProjection } from '../../projections/util/merge_projection'; +import { + SERVICE_NAME, + SERVICE_NODE_NAME, +} from '../../../common/elasticsearch_fieldnames'; +import { ProcessorEvent } from '../../../common/processor_event'; +import { kqlQuery, rangeQuery } from '../../../../observability/server'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { Setup } from '../helpers/setup_request'; const getServiceNodes = async ({ @@ -35,20 +40,26 @@ const getServiceNodes = async ({ }) => { const { apmEventClient } = setup; - const projection = getServiceNodesProjection({ - kuery, - serviceName, - environment, - start, - end, - }); - - const params = mergeProjection(projection, { + const params = { + apm: { + events: [ProcessorEvent.metric], + }, body: { + size: 0, + query: { + bool: { + filter: [ + { term: { [SERVICE_NAME]: serviceName } }, + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ], + }, + }, aggs: { nodes: { terms: { - ...projection.body.aggs.nodes.terms, + field: SERVICE_NODE_NAME, size: 10000, missing: SERVICE_NODE_NAME_MISSING, }, @@ -57,7 +68,7 @@ const getServiceNodes = async ({ top_metrics: { metrics: asMutableArray([{ field: HOST_NAME }] as const), sort: { - '@timestamp': 'desc', + '@timestamp': 'desc' as const, }, }, }, @@ -85,7 +96,7 @@ const getServiceNodes = async ({ }, }, }, - }); + }; const response = await apmEventClient.search('get_service_nodes', params); diff --git a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts index ef52c4b0f4927..ab0fa91529917 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts @@ -11,8 +11,16 @@ import { CONTAINER_ID, } from '../../../common/elasticsearch_fieldnames'; import { NOT_AVAILABLE_LABEL } from '../../../common/i18n'; -import { mergeProjection } from '../../projections/util/merge_projection'; -import { getServiceNodesProjection } from '../../projections/service_nodes'; +import { + SERVICE_NAME, + SERVICE_NODE_NAME, +} from '../../../common/elasticsearch_fieldnames'; +import { ProcessorEvent } from '../../../common/processor_event'; +import { kqlQuery, rangeQuery } from '../../../../observability/server'; +import { + environmentQuery, + serviceNodeNameQuery, +} from '../../../common/utils/environment_query'; import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; export async function getServiceNodeMetadata({ @@ -32,39 +40,48 @@ export async function getServiceNodeMetadata({ }) { const { apmEventClient } = setup; - const query = mergeProjection( - getServiceNodesProjection({ - kuery, - serviceName, - serviceNodeName, - environment: ENVIRONMENT_ALL.value, - start, - end, - }), - { - body: { - size: 0, - aggs: { - host: { - terms: { - field: HOST_NAME, - size: 1, - }, + const params = { + apm: { + events: [ProcessorEvent.metric], + }, + body: { + size: 0, + query: { + bool: { + filter: [ + { term: { [SERVICE_NAME]: serviceName } }, + ...rangeQuery(start, end), + ...environmentQuery(ENVIRONMENT_ALL.value), + ...kqlQuery(kuery), + ...serviceNodeNameQuery(serviceNodeName), + ], + }, + }, + aggs: { + nodes: { + terms: { + field: SERVICE_NODE_NAME, }, - containerId: { - terms: { - field: CONTAINER_ID, - size: 1, - }, + }, + host: { + terms: { + field: HOST_NAME, + size: 1, + }, + }, + containerId: { + terms: { + field: CONTAINER_ID, + size: 1, }, }, }, - } - ); + }, + }; const response = await apmEventClient.search( 'get_service_node_metadata', - query + params ); return { diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/index.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/index.ts index dadb29d156e0b..a22c1d35dc663 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/index.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/index.ts @@ -8,14 +8,9 @@ import { withApmSpan } from '../../../../utils/with_apm_span'; import { getAllEnvironments } from '../../../environments/get_all_environments'; import { Setup } from '../../../helpers/setup_request'; -import { PromiseReturnType } from '../../../../../../observability/typings/common'; import { getExistingEnvironmentsForService } from './get_existing_environments_for_service'; import { ALL_OPTION_VALUE } from '../../../../../common/agent_configuration/all_option'; -export type AgentConfigurationEnvironmentsAPIResponse = PromiseReturnType< - typeof getEnvironments ->; - export async function getEnvironments({ serviceName, setup, diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_service_names.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_service_names.ts index 06bd900872a20..fc5167159b98d 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_service_names.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_service_names.ts @@ -7,15 +7,10 @@ import { ProcessorEvent } from '../../../../common/processor_event'; import { Setup } from '../../helpers/setup_request'; -import { PromiseReturnType } from '../../../../../observability/typings/common'; import { SERVICE_NAME } from '../../../../common/elasticsearch_fieldnames'; import { ALL_OPTION_VALUE } from '../../../../common/agent_configuration/all_option'; import { getProcessorEventForTransactions } from '../../helpers/transactions'; -export type AgentConfigurationServicesAPIResponse = PromiseReturnType< - typeof getServiceNames ->; - export async function getServiceNames({ setup, searchAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/projections/errors.ts b/x-pack/plugins/apm/server/projections/errors.ts deleted file mode 100644 index b256428143400..0000000000000 --- a/x-pack/plugins/apm/server/projections/errors.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - SERVICE_NAME, - ERROR_GROUP_ID, -} from '../../common/elasticsearch_fieldnames'; -import { rangeQuery, kqlQuery } from '../../../observability/server'; -import { environmentQuery } from '../../common/utils/environment_query'; -import { ProcessorEvent } from '../../common/processor_event'; - -export function getErrorGroupsProjection({ - environment, - kuery, - serviceName, - start, - end, -}: { - environment: string; - kuery: string; - serviceName: string; - start: number; - end: number; -}) { - return { - apm: { - events: [ProcessorEvent.error as const], - }, - body: { - query: { - bool: { - filter: [ - { term: { [SERVICE_NAME]: serviceName } }, - ...rangeQuery(start, end), - ...environmentQuery(environment), - ...kqlQuery(kuery), - ], - }, - }, - aggs: { - error_groups: { - terms: { - field: ERROR_GROUP_ID, - }, - }, - }, - }, - }; -} diff --git a/x-pack/plugins/apm/server/projections/metrics.ts b/x-pack/plugins/apm/server/projections/metrics.ts deleted file mode 100644 index 417281f2de487..0000000000000 --- a/x-pack/plugins/apm/server/projections/metrics.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { - SERVICE_NAME, - SERVICE_NODE_NAME, -} from '../../common/elasticsearch_fieldnames'; -import { rangeQuery, kqlQuery } from '../../../observability/server'; -import { environmentQuery } from '../../common/utils/environment_query'; -import { SERVICE_NODE_NAME_MISSING } from '../../common/service_nodes'; -import { ProcessorEvent } from '../../common/processor_event'; - -function getServiceNodeNameFilters(serviceNodeName?: string) { - if (!serviceNodeName) { - return []; - } - - if (serviceNodeName === SERVICE_NODE_NAME_MISSING) { - return [{ bool: { must_not: [{ exists: { field: SERVICE_NODE_NAME } }] } }]; - } - - return [{ term: { [SERVICE_NODE_NAME]: serviceNodeName } }]; -} - -export function getMetricsProjection({ - environment, - kuery, - serviceName, - serviceNodeName, - start, - end, -}: { - environment: string; - kuery: string; - serviceName: string; - serviceNodeName?: string; - start: number; - end: number; -}) { - const filter = [ - { term: { [SERVICE_NAME]: serviceName } }, - ...getServiceNodeNameFilters(serviceNodeName), - ...rangeQuery(start, end), - ...environmentQuery(environment), - ...kqlQuery(kuery), - ] as QueryDslQueryContainer[]; - - return { - apm: { - events: [ProcessorEvent.metric], - }, - body: { - query: { - bool: { - filter, - }, - }, - }, - }; -} diff --git a/x-pack/plugins/apm/server/projections/service_nodes.ts b/x-pack/plugins/apm/server/projections/service_nodes.ts deleted file mode 100644 index 5d97af0ad9860..0000000000000 --- a/x-pack/plugins/apm/server/projections/service_nodes.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { SERVICE_NODE_NAME } from '../../common/elasticsearch_fieldnames'; -import { mergeProjection } from './util/merge_projection'; -import { getMetricsProjection } from './metrics'; - -export function getServiceNodesProjection({ - serviceName, - serviceNodeName, - environment, - kuery, - start, - end, -}: { - serviceName: string; - serviceNodeName?: string; - environment: string; - kuery: string; - start: number; - end: number; -}) { - return mergeProjection( - getMetricsProjection({ - serviceName, - serviceNodeName, - environment, - kuery, - start, - end, - }), - { - body: { - aggs: { - nodes: { - terms: { - field: SERVICE_NODE_NAME, - }, - }, - }, - }, - } - ); -} diff --git a/x-pack/plugins/apm/server/routes/metrics.ts b/x-pack/plugins/apm/server/routes/metrics.ts index 1817c3e1546bd..36a504859797a 100644 --- a/x-pack/plugins/apm/server/routes/metrics.ts +++ b/x-pack/plugins/apm/server/routes/metrics.ts @@ -37,7 +37,8 @@ const metricsChartsRoute = createApmServerRoute({ const { serviceName } = params.path; const { agentName, environment, kuery, serviceNodeName, start, end } = params.query; - return await getMetricsChartDataByAgent({ + + const charts = await getMetricsChartDataByAgent({ environment, kuery, setup, @@ -47,6 +48,8 @@ const metricsChartsRoute = createApmServerRoute({ start, end, }); + + return { charts }; }, }); diff --git a/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts b/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts index ba99b0624c441..8051ef2a72b6a 100644 --- a/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts +++ b/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts @@ -64,30 +64,6 @@ export function onPremInstructions({ iconType: 'alert', }, instructionVariants: [ - // hides fleet section when plugin is disabled - ...(isFleetPluginEnabled - ? [ - { - id: INSTRUCTION_VARIANT.FLEET, - instructions: [ - { - title: i18n.translate('xpack.apm.tutorial.fleet.title', { - defaultMessage: 'Fleet', - }), - customComponentName: 'TutorialFleetInstructions', - }, - ], - }, - ] - : []), - { - id: INSTRUCTION_VARIANT.OSX, - instructions: [ - createDownloadServerOsx(), - EDIT_CONFIG, - START_SERVER_UNIX, - ], - }, { id: INSTRUCTION_VARIANT.DEB, instructions: [ @@ -104,10 +80,34 @@ export function onPremInstructions({ START_SERVER_UNIX_SYSV, ], }, + { + id: INSTRUCTION_VARIANT.OSX, + instructions: [ + createDownloadServerOsx(), + EDIT_CONFIG, + START_SERVER_UNIX, + ], + }, { id: INSTRUCTION_VARIANT.WINDOWS, instructions: createWindowsServerInstructions(), }, + // hides fleet section when plugin is disabled + ...(isFleetPluginEnabled + ? [ + { + id: INSTRUCTION_VARIANT.FLEET, + instructions: [ + { + title: i18n.translate('xpack.apm.tutorial.fleet.title', { + defaultMessage: 'Fleet', + }), + customComponentName: 'TutorialFleetInstructions', + }, + ], + }, + ] + : []), ], statusCheck: { title: i18n.translate( diff --git a/x-pack/plugins/apm/typings/es_schemas/raw/fields/url.ts b/x-pack/plugins/apm/typings/es_schemas/raw/fields/url.ts index e9d25904aa0b4..001d6370e5f06 100644 --- a/x-pack/plugins/apm/typings/es_schemas/raw/fields/url.ts +++ b/x-pack/plugins/apm/typings/es_schemas/raw/fields/url.ts @@ -8,4 +8,5 @@ export interface Url { domain?: string; full: string; + original?: string; } diff --git a/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts b/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts index 01bc0ed52ecae..d191cb6d4e84c 100644 --- a/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts +++ b/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts @@ -7,8 +7,10 @@ import { APMBaseDoc } from './apm_base_doc'; import { EventOutcome } from './fields/event_outcome'; +import { Http } from './fields/http'; import { Stackframe } from './fields/stackframe'; import { TimestampUs } from './fields/timestamp_us'; +import { Url } from './fields/url'; interface Processor { name: 'transaction'; @@ -67,4 +69,6 @@ export interface SpanRaw extends APMBaseDoc { id: string; }; child?: { id: string[] }; + http?: Http; + url?: Url; } diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/__fixtures__/test_tables.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/__fixtures__/test_tables.ts index 18aa70534b0ba..22c4d16d9810a 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/__fixtures__/test_tables.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/__fixtures__/test_tables.ts @@ -21,6 +21,11 @@ const testTable: Datatable = { name: 'name', meta: { type: 'string' }, }, + { + id: 'title_id', + name: 'title', + meta: { type: 'string' }, + }, { id: 'time', name: 'time', @@ -49,6 +54,7 @@ const testTable: Datatable = { price: 605, quantity: 100, in_stock: true, + title_id: 'title1', }, { name: 'product1', @@ -56,6 +62,7 @@ const testTable: Datatable = { price: 583, quantity: 200, in_stock: true, + title_id: 'title2', }, { name: 'product1', @@ -63,6 +70,7 @@ const testTable: Datatable = { price: 420, quantity: 300, in_stock: true, + title_id: 'title3', }, { name: 'product2', @@ -70,6 +78,7 @@ const testTable: Datatable = { price: 216, quantity: 350, in_stock: false, + title_id: 'title4', }, { name: 'product2', @@ -77,6 +86,7 @@ const testTable: Datatable = { price: 200, quantity: 256, in_stock: false, + title_id: 'title5', }, { name: 'product2', @@ -84,6 +94,7 @@ const testTable: Datatable = { price: 190, quantity: 231, in_stock: false, + title_id: 'title6', }, { name: 'product3', @@ -91,6 +102,7 @@ const testTable: Datatable = { price: 67, quantity: 240, in_stock: true, + title_id: 'title7', }, { name: 'product4', @@ -98,6 +110,7 @@ const testTable: Datatable = { price: 311, quantity: 447, in_stock: false, + title_id: 'title8', }, { name: 'product5', @@ -105,6 +118,7 @@ const testTable: Datatable = { price: 288, quantity: 384, in_stock: true, + title_id: 'title9', }, ], }; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/columns.test.js b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/columns.test.js index d7f28559ee0ef..0184920285a6d 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/columns.test.js +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/columns.test.js @@ -20,15 +20,20 @@ describe('columns', () => { it('returns a datatable with included columns and without excluded columns', () => { const arbitraryRowIndex = 7; const result = fn(testTable, { - include: 'name, price, quantity, foo, bar', + include: 'name, title_id, price, quantity, foo, bar', exclude: 'price, quantity, fizz, buzz', }); expect(result.columns[0]).toHaveProperty('name', 'name'); + expect(result.columns[1]).toHaveProperty('id', 'title_id'); expect(result.rows[arbitraryRowIndex]).toHaveProperty( 'name', testTable.rows[arbitraryRowIndex].name ); + expect(result.rows[arbitraryRowIndex]).toHaveProperty( + 'title_id', + testTable.rows[arbitraryRowIndex].title_id + ); expect(result.rows[arbitraryRowIndex]).not.toHaveProperty('price'); expect(result.rows[arbitraryRowIndex]).not.toHaveProperty('quantity'); expect(result.rows[arbitraryRowIndex]).not.toHaveProperty('foo'); @@ -46,8 +51,8 @@ describe('columns', () => { expect( fn(testTable, { - include: 'price, quantity, in_stock', - exclude: 'price, quantity, in_stock', + include: 'price, quantity, in_stock, title_id', + exclude: 'price, quantity, in_stock, title_id', }) ).toEqual(emptyTable); }); @@ -56,15 +61,17 @@ describe('columns', () => { it('returns a datatable with included columns only', () => { const arbitraryRowIndex = 3; const result = fn(testTable, { - include: 'name, time, in_stock', + include: 'name, time, in_stock, title_id', }); - expect(result.columns).toHaveLength(3); - expect(Object.keys(result.rows[0])).toHaveLength(3); + expect(result.columns).toHaveLength(4); + expect(Object.keys(result.rows[0])).toHaveLength(4); expect(result.columns[0]).toHaveProperty('name', 'name'); expect(result.columns[1]).toHaveProperty('name', 'time'); expect(result.columns[2]).toHaveProperty('name', 'in_stock'); + expect(result.columns[3]).toHaveProperty('id', 'title_id'); + expect(result.rows[arbitraryRowIndex]).toHaveProperty( 'name', testTable.rows[arbitraryRowIndex].name @@ -77,6 +84,10 @@ describe('columns', () => { 'in_stock', testTable.rows[arbitraryRowIndex].in_stock ); + expect(result.rows[arbitraryRowIndex]).toHaveProperty( + 'title_id', + testTable.rows[arbitraryRowIndex].title_id + ); }); it('ignores invalid columns', () => { @@ -102,14 +113,15 @@ describe('columns', () => { describe('exclude', () => { it('returns a datatable without excluded columns', () => { const arbitraryRowIndex = 5; - const result = fn(testTable, { exclude: 'price, quantity, foo, bar' }); + const result = fn(testTable, { exclude: 'price, quantity, foo, bar, title_id' }); - expect(result.columns.length).toEqual(testTable.columns.length - 2); - expect(Object.keys(result.rows[0])).toHaveLength(testTable.columns.length - 2); + expect(result.columns.length).toEqual(testTable.columns.length - 3); + expect(Object.keys(result.rows[0])).toHaveLength(testTable.columns.length - 3); expect(result.rows[arbitraryRowIndex]).not.toHaveProperty('price'); expect(result.rows[arbitraryRowIndex]).not.toHaveProperty('quantity'); expect(result.rows[arbitraryRowIndex]).not.toHaveProperty('foo'); expect(result.rows[arbitraryRowIndex]).not.toHaveProperty('bar'); + expect(result.rows[arbitraryRowIndex]).not.toHaveProperty('title_id'); }); it('ignores invalid columns', () => { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/columns.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/columns.ts index a81368e23a477..e627086d70d9d 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/columns.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/columns.ts @@ -14,6 +14,26 @@ interface Arguments { exclude: string; } +const prepareFields = (fields: string) => fields.split(',').map((field) => field.trim()); + +const getFieldsIds = (cols: DatatableColumn[]) => cols.map((col) => col.id ?? col.name); + +const splitColumnsByFields = ( + cols: DatatableColumn[], + fields: string[], + saveOther: boolean = false +) => + cols.reduce<{ matched: DatatableColumn[]; other: DatatableColumn[] }>( + (splitColumns, col) => { + if (fields.includes(col.id) || fields.includes(col.name)) { + return { ...splitColumns, matched: [...splitColumns.matched, col] }; + } + + return saveOther ? { ...splitColumns, other: [...splitColumns.other, col] } : splitColumns; + }, + { matched: [], other: [] } + ); + export function columns(): ExpressionFunctionDefinition< 'columns', Datatable, @@ -44,27 +64,25 @@ export function columns(): ExpressionFunctionDefinition< let result = { ...input }; if (exclude) { - const fields = exclude.split(',').map((field) => field.trim()); - const cols = contextColumns.filter((col) => !fields.includes(col.name)); - const rows = cols.length > 0 ? contextRows.map((row) => omit(row, fields)) : []; - - result = { rows, columns: cols, ...rest }; + const fields = prepareFields(exclude); + const { matched: excluded, other } = splitColumnsByFields(result.columns, fields, true); + const fieldsIds = getFieldsIds(excluded); + const rows = excluded.length ? result.rows.map((row) => omit(row, fieldsIds)) : result.rows; + result = { rows, columns: other, ...rest }; } if (include) { - const fields = include.split(',').map((field) => field.trim()); - // const columns = result.columns.filter(col => fields.includes(col.name)); + const fields = prepareFields(include); + const { matched: included } = splitColumnsByFields(result.columns, fields); + const fieldsIds = getFieldsIds(included); // Include columns in the order the user specified - const cols: DatatableColumn[] = []; + const cols = fields.reduce((includedCols, field) => { + const column = find(included, (col) => col.id === field || col.name === field); + return column ? [...includedCols, column] : includedCols; + }, []); - fields.forEach((field) => { - const column = find(result.columns, { name: field }); - if (column) { - cols.push(column); - } - }); - const rows = cols.length > 0 ? result.rows.map((row) => pick(row, fields)) : []; + const rows = cols.length ? result.rows.map((row) => pick(row, fieldsIds)) : []; result = { rows, columns: cols, ...rest }; } diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx index 990d44584cf05..8843bed9a2b43 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx @@ -39,6 +39,8 @@ const useConnectorsMock = useConnectors as jest.Mock; const useCaseConfigureMock = useCaseConfigure as jest.Mock; const useGetUrlSearchMock = jest.fn(); const useActionTypesMock = useActionTypes as jest.Mock; +const getAddConnectorFlyoutMock = jest.fn(); +const getEditConnectorFlyoutMock = jest.fn(); describe('ConfigureCases', () => { beforeAll(() => { @@ -46,6 +48,12 @@ describe('ConfigureCases', () => { actionTypeTitle: '.servicenow', iconClass: 'logoSecurity', }); + + useKibanaMock().services.triggersActionsUi.getAddConnectorFlyout = + getAddConnectorFlyoutMock.mockReturnValue(
); + + useKibanaMock().services.triggersActionsUi.getEditConnectorFlyout = + getEditConnectorFlyoutMock.mockReturnValue(
); }); beforeEach(() => { @@ -72,12 +80,12 @@ describe('ConfigureCases', () => { expect(wrapper.find('[data-test-subj="closure-options-radio-group"]').exists()).toBeTruthy(); }); - test('it does NOT render the ConnectorAddFlyout', () => { - expect(wrapper.find('ConnectorAddFlyout').exists()).toBeFalsy(); + test('it does NOT render the add connector flyout', () => { + expect(wrapper.find('[data-test-subj="add-connector-flyout"]').exists()).toBeFalsy(); }); - test('it does NOT render the ConnectorEditFlyout', () => { - expect(wrapper.find('ConnectorEditFlyout').exists()).toBeFalsy(); + test('it does NOT render the edit connector flyout"]', () => { + expect(wrapper.find('[data-test-subj="edit-connector-flyout"]').exists()).toBeFalsy(); }); test('it does NOT render the EuiCallOut', () => { @@ -176,8 +184,8 @@ describe('ConfigureCases', () => { expect(wrapper.find(ClosureOptions).prop('closureTypeSelected')).toBe('close-by-user'); // Flyouts - expect(wrapper.find('ConnectorAddFlyout').exists()).toBe(false); - expect(wrapper.find('ConnectorEditFlyout').exists()).toBe(false); + expect(wrapper.find('[data-test-subj="add-connector-flyout"]').exists()).toBe(false); + expect(wrapper.find('[data-test-subj="edit-connector-flyout"]').exists()).toBe(false); }); test('it disables correctly when the user cannot crud', () => { @@ -541,21 +549,25 @@ describe('ConfigureCases', () => { await waitFor(() => { wrapper.update(); - expect(wrapper.find('ConnectorAddFlyout').exists()).toBe(true); - expect(wrapper.find('ConnectorAddFlyout').prop('actionTypes')).toEqual([ - expect.objectContaining({ - id: '.servicenow', - }), - expect.objectContaining({ - id: '.jira', - }), - expect.objectContaining({ - id: '.resilient', - }), + expect(wrapper.find('[data-test-subj="add-connector-flyout"]').exists()).toBe(true); + expect(getAddConnectorFlyoutMock).toHaveBeenCalledWith( expect.objectContaining({ - id: '.servicenow-sir', - }), - ]); + actionTypes: [ + expect.objectContaining({ + id: '.servicenow', + }), + expect.objectContaining({ + id: '.jira', + }), + expect.objectContaining({ + id: '.resilient', + }), + expect.objectContaining({ + id: '.servicenow-sir', + }), + ], + }) + ); }); }); @@ -588,8 +600,10 @@ describe('ConfigureCases', () => { await waitFor(() => { wrapper.update(); - expect(wrapper.find('ConnectorEditFlyout').exists()).toBe(true); - expect(wrapper.find('ConnectorEditFlyout').prop('initialConnector')).toEqual(connectors[1]); + expect(wrapper.find('[data-test-subj="edit-connector-flyout"]').exists()).toBe(true); + expect(getEditConnectorFlyoutMock).toHaveBeenCalledWith( + expect.objectContaining({ initialConnector: connectors[1] }) + ); }); expect( diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/_index.scss b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/_index.scss index b99f34f95cd74..7ac8aa4365732 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/_index.scss +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/_index.scss @@ -3,6 +3,7 @@ @import 'components/field_data_row/index'; $panelWidthS: #{'max(20%, 225px)'}; +$panelWidthM: #{'max(30%, 300px)'}; $panelWidthL: #{'max(40%, 450px)'}; .dvExpandedRow { @@ -52,9 +53,6 @@ $panelWidthL: #{'max(40%, 450px)'}; } .dvSummaryTable { - .euiTableRow > .euiTableRowCell { - border-bottom: 0; - } .euiTableHeaderCell { display: none; } @@ -63,6 +61,11 @@ $panelWidthL: #{'max(40%, 450px)'}; .dvSummaryTable__wrapper { min-width: $panelWidthS; max-width: $panelWidthS; + + &.dvPanel__dateSummary { + min-width: $panelWidthM; + max-width: $panelWidthM; + } } .dvTopValues__wrapper { diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx index b56e6ae815645..f4060bc16a39c 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx @@ -6,7 +6,13 @@ */ import React, { FC, ReactNode, useMemo } from 'react'; -import { EuiBasicTable, EuiSpacer, RIGHT_ALIGNMENT, HorizontalAlignment } from '@elastic/eui'; +import { + EuiBasicTable, + EuiSpacer, + RIGHT_ALIGNMENT, + LEFT_ALIGNMENT, + HorizontalAlignment, +} from '@elastic/eui'; import { Axis, BarSeries, Chart, Settings, ScaleType } from '@elastic/charts'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -73,12 +79,13 @@ export const BooleanContent: FC = ({ config }) => { name: '', render: (_: string, summaryItem: { display: ReactNode }) => summaryItem.display, width: '25px', - align: RIGHT_ALIGNMENT as HorizontalAlignment, + align: LEFT_ALIGNMENT as HorizontalAlignment, }, { field: 'value', name: '', render: (v: string) => {v}, + align: RIGHT_ALIGNMENT as HorizontalAlignment, }, ]; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/date_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/date_content.tsx index 8d5704fc16fd5..352782b4bb301 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/date_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/date_content.tsx @@ -6,13 +6,12 @@ */ import React, { FC, ReactNode } from 'react'; -import { EuiBasicTable, HorizontalAlignment } from '@elastic/eui'; +import { EuiBasicTable, HorizontalAlignment, LEFT_ALIGNMENT, RIGHT_ALIGNMENT } from '@elastic/eui'; // @ts-ignore import { formatDate } from '@elastic/eui/lib/services/format'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { RIGHT_ALIGNMENT } from '@elastic/eui'; import type { FieldDataRowProps } from '../../types/field_data_row'; import { ExpandedRowFieldHeader } from '../expanded_row_field_header'; import { DocumentStatsTable } from './document_stats'; @@ -65,19 +64,20 @@ export const DateContent: FC = ({ config }) => { field: 'function', render: (func: string, summaryItem: { display: ReactNode }) => summaryItem.display, width: '70px', - align: RIGHT_ALIGNMENT as HorizontalAlignment, + align: LEFT_ALIGNMENT as HorizontalAlignment, }, { field: 'value', name: '', render: (v: string) => {v}, + align: RIGHT_ALIGNMENT as HorizontalAlignment, }, ]; return ( - + {summaryTableTitle} className={'dvSummaryTable'} diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/document_stats.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/document_stats.tsx index 5995b81555f9b..9805514365903 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/document_stats.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/document_stats.tsx @@ -8,7 +8,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { FC, ReactNode } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiBasicTable, HorizontalAlignment, RIGHT_ALIGNMENT } from '@elastic/eui'; +import { EuiBasicTable, HorizontalAlignment, LEFT_ALIGNMENT, RIGHT_ALIGNMENT } from '@elastic/eui'; import { ExpandedRowFieldHeader } from '../expanded_row_field_header'; import { FieldDataRowProps } from '../../types'; import { roundToDecimalPlace } from '../../../utils'; @@ -20,12 +20,13 @@ const metaTableColumns = [ name: '', render: (_: string, metaItem: { display: ReactNode }) => metaItem.display, width: '25px', - align: RIGHT_ALIGNMENT as HorizontalAlignment, + align: LEFT_ALIGNMENT as HorizontalAlignment, }, { field: 'value', name: '', render: (v: string) => {v}, + align: RIGHT_ALIGNMENT as HorizontalAlignment, }, ]; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_panel.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_panel.tsx index b738dbdf67178..4246a53464f74 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_panel.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_panel.tsx @@ -20,7 +20,7 @@ export const ExpandedRowPanel: FC = ({ children, dataTestSubj, grow, clas = ({ config, onAddFilter }) => name: '', render: (summaryItem: { display: ReactNode }) => summaryItem.display, width: '25px', - align: RIGHT_ALIGNMENT as HorizontalAlignment, + align: LEFT_ALIGNMENT as HorizontalAlignment, }, { field: 'value', name: '', render: (v: string) => {v}, + align: RIGHT_ALIGNMENT as HorizontalAlignment, }, ]; diff --git a/x-pack/plugins/fleet/server/index.ts b/x-pack/plugins/fleet/server/index.ts index c3dd408925cf0..17425050a4e10 100644 --- a/x-pack/plugins/fleet/server/index.ts +++ b/x-pack/plugins/fleet/server/index.ts @@ -17,7 +17,6 @@ import { import { FleetPlugin } from './plugin'; -export { default as apm } from 'elastic-apm-node'; export type { AgentService, ESIndexPatternService, diff --git a/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx b/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx index 9ed4047e45bd3..bb9e7ced40c53 100644 --- a/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx +++ b/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx @@ -75,8 +75,12 @@ export function SavedViewsToolbarControls(props: Props) { setModalOpen(true); }, [find, hideSavedViewMenu]); const showSavedViewMenu = useCallback(() => { + if (isSavedViewMenuOpen) { + setIsSavedViewMenuOpen(false); + return; + } setIsSavedViewMenuOpen(true); - }, [setIsSavedViewMenuOpen]); + }, [setIsSavedViewMenuOpen, isSavedViewMenuOpen]); const save = useCallback( (name: string, hasTime: boolean = false) => { const currentState = { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx index 5b432f85efde2..92633d5e7305b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx @@ -157,7 +157,7 @@ describe('LayerPanel', () => { }); describe('single group', () => { - it('should render the non-editable state', async () => { + it('should render the non-editable state and optional label', async () => { mockVisualization.getConfiguration.mockReturnValue({ groups: [ { @@ -172,8 +172,11 @@ describe('LayerPanel', () => { }); const { instance } = await mountWithProvider(); + const group = instance.find('.lnsLayerPanel__dimensionContainer[data-test-subj="lnsGroup"]'); expect(group).toHaveLength(1); + const optionalLabel = instance.find('[data-test-subj="lnsGroup_optional"]').first(); + expect(optionalLabel.text()).toEqual('Optional'); }); it('should render the group with a way to add a new column', async () => { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 51d880e8f7c1c..6af3d88b17d41 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -14,6 +14,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiFormRow, + EuiText, EuiIconTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -385,7 +386,7 @@ export function LayerPanel( {groups.map((group, groupIndex) => { const isMissing = !isEmptyLayer && group.required && group.accessors.length === 0; - + const isOptional = !group.required; return ( } + labelAppend={ + isOptional ? ( + + {i18n.translate('xpack.lens.editorFrame.optionalDimensionLabel', { + defaultMessage: 'Optional', + })} + + ) : null + } labelType="legend" key={group.groupId} isInvalid={isMissing} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimensions_editor_helpers.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimensions_editor_helpers.tsx index 876711c5d6aaf..ce8c7d61bfc16 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimensions_editor_helpers.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimensions_editor_helpers.tsx @@ -161,7 +161,12 @@ export const DimensionEditorTabs = ({ tabs }: { tabs: DimensionEditorTab[] }) => > {tabs.map(({ id, enabled, state, onClick, label }) => { return enabled ? ( - + {label} ) : null; diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx b/x-pack/plugins/lens/public/metric_visualization/visualization.tsx index 1b30e6c7fd932..5693d67546f99 100644 --- a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/visualization.tsx @@ -106,6 +106,7 @@ export const metricVisualization: Visualization = { accessors: props.state.accessor ? [{ columnId: props.state.accessor }] : [], supportsMoreColumns: !props.state.accessor, filterOperations: (op: OperationMetadata) => !op.isBucketed && op.dataType === 'number', + required: true, }, ], }; diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index d3d461e8f8741..8ac9076919dd9 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -30,7 +30,7 @@ export class XyVisualization { const { getXyChartRenderer, getXyVisualization } = await import('../async_services'); const [, { charts, fieldFormats }] = await core.getStartServices(); const palettes = await charts.palettes.getPalettes(); - + const useLegacyTimeAxis = core.uiSettings.get(LEGACY_TIME_AXIS); expressions.registerRenderer( getXyChartRenderer({ formatFactory, @@ -38,10 +38,10 @@ export class XyVisualization { chartsActiveCursorService: charts.activeCursor, paletteService: palettes, timeZone: getTimeZone(core.uiSettings), - useLegacyTimeAxis: core.uiSettings.get(LEGACY_TIME_AXIS), + useLegacyTimeAxis, }) ); - return getXyVisualization({ paletteService: palettes, fieldFormats }); + return getXyVisualization({ paletteService: palettes, fieldFormats, useLegacyTimeAxis }); }); } } diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts index 7d1bd64abe906..033e324a5d02d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts @@ -19,6 +19,7 @@ describe('#toExpression', () => { const xyVisualization = getXyVisualization({ paletteService: chartPluginMock.createPaletteRegistry(), fieldFormats: fieldFormatsServiceMock.createStartContract(), + useLegacyTimeAxis: false, }); let mockDatasource: ReturnType; let frame: ReturnType; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 0c3fa21708263..029cfe8ecbe40 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -40,6 +40,7 @@ const fieldFormatsMock = fieldFormatsServiceMock.createStartContract(); const xyVisualization = getXyVisualization({ paletteService: paletteServiceMock, fieldFormats: fieldFormatsMock, + useLegacyTimeAxis: false, }); describe('xy_visualization', () => { diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 2f3ec7e2723d4..0ba74f4b8bb3a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -98,9 +98,11 @@ function getDescription(state?: State) { export const getXyVisualization = ({ paletteService, fieldFormats, + useLegacyTimeAxis, }: { paletteService: PaletteRegistry; fieldFormats: FieldFormatsStart; + useLegacyTimeAxis: boolean; }): Visualization => ({ id: 'lnsXY', @@ -573,7 +575,7 @@ export const getXyVisualization = ({ renderToolbar(domElement, props) { render( - + , domElement ); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx index e0a30bdb2c511..4ce1667ee1008 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx @@ -21,7 +21,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { XYLayerConfig, AxesSettingsConfig, AxisExtentConfig } from '../../../common/expressions'; -import { ToolbarPopover, useDebouncedValue } from '../../shared_components'; +import { ToolbarPopover, useDebouncedValue, TooltipWrapper } from '../../shared_components'; import { isHorizontalChart } from '../state_helpers'; import { EuiIconAxisBottom } from '../../assets/axis_bottom'; import { EuiIconAxisLeft } from '../../assets/axis_left'; @@ -104,6 +104,11 @@ export interface AxisSettingsPopoverProps { hasBarOrAreaOnAxis: boolean; hasPercentageAxis: boolean; dataBounds?: { min: number; max: number }; + + /** + * Toggle the visibility of legacy axis settings when using the new multilayer time axis + */ + useMultilayerTimeAxis?: boolean; } const popoverConfig = ( @@ -210,6 +215,7 @@ export const AxisSettingsPopover: React.FunctionComponent { const isHorizontal = layers?.length ? isHorizontalChart(layers) : false; const config = popoverConfig(axis, isHorizontal); @@ -314,30 +320,41 @@ export const AxisSettingsPopover: React.FunctionComponent - - value === orientation)!.id} - onChange={(optionId) => { - const newOrientation = axisOrientationOptions.find(({ id }) => id === optionId)!.value; - setOrientation(axis, newOrientation); - }} - /> - + > + value === orientation)!.id} + onChange={(optionId) => { + const newOrientation = axisOrientationOptions.find( + ({ id }) => id === optionId + )!.value; + setOrientation(axis, newOrientation); + }} + /> + + {setEndzoneVisibility && ( <> diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx index cef4a5f01ce8a..6a43be64ec1d4 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx @@ -176,8 +176,10 @@ function hasPercentageAxis(axisGroups: GroupsConfiguration, groupId: string, sta ); } -export const XyToolbar = memo(function XyToolbar(props: VisualizationToolbarProps) { - const { state, setState, frame } = props; +export const XyToolbar = memo(function XyToolbar( + props: VisualizationToolbarProps & { useLegacyTimeAxis?: boolean } +) { + const { state, setState, frame, useLegacyTimeAxis } = props; const shouldRotate = state?.layers.length ? isHorizontalChart(state.layers) : false; const axisGroups = getAxesConfiguration(state?.layers, shouldRotate, frame.activeData); @@ -327,6 +329,28 @@ export const XyToolbar = memo(function XyToolbar(props: VisualizationToolbarProp [setState, state] ); + const filteredBarLayers = state?.layers.filter((layer) => layer.seriesType.includes('bar')); + const chartHasMoreThanOneBarSeries = + filteredBarLayers.length > 1 || + filteredBarLayers.some((layer) => layer.accessors.length > 1 || layer.splitAccessor); + + const isTimeHistogramModeEnabled = state?.layers.some( + ({ xAccessor, layerId, seriesType, splitAccessor }) => { + if (!xAccessor) { + return false; + } + const xAccessorOp = props.frame.datasourceLayers[layerId].getOperationForColumnId(xAccessor); + return ( + getScaleType(xAccessorOp, ScaleType.Linear) === ScaleType.Time && + xAccessorOp?.isBucketed && + (seriesType.includes('stacked') || !splitAccessor) && + (seriesType.includes('stacked') || + !seriesType.includes('bar') || + !chartHasMoreThanOneBarSeries) + ); + } + ); + return ( @@ -487,6 +511,9 @@ export const XyToolbar = memo(function XyToolbar(props: VisualizationToolbarProp setEndzoneVisibility={onChangeEndzoneVisiblity} hasBarOrAreaOnAxis={false} hasPercentageAxis={false} + useMultilayerTimeAxis={ + isTimeHistogramModeEnabled && !useLegacyTimeAxis && !shouldRotate + } /> { diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/index/advanced.js b/x-pack/plugins/monitoring/public/components/elasticsearch/index/advanced.js index 2f76e9b5cc8bb..ed86a4bb58048 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/index/advanced.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/index/advanced.js @@ -23,8 +23,6 @@ import { AlertsCallout } from '../../../alerts/callout'; export const AdvancedIndex = ({ indexSummary, metrics, alerts, ...props }) => { const metricsToShow = [ - metrics.index_1, - metrics.index_2, metrics.index_3, metrics.index_4, metrics.index_total, diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/index/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/index/index.js index 9bdaa513998b5..15a297f3cac73 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/index/index.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/index/index.js @@ -31,7 +31,6 @@ export const Index = ({ ...props }) => { const metricsToShow = [ - metrics.index_mem, metrics.index_size, metrics.index_search_request_rate, metrics.index_request_rate, diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/node/advanced.js b/x-pack/plugins/monitoring/public/components/elasticsearch/node/advanced.js index 5d95aa8bec676..30ddcf09ede26 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/node/advanced.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/node/advanced.js @@ -27,8 +27,6 @@ export const AdvancedNode = ({ nodeSummary, metrics, alerts, ...props }) => { metrics.node_gc_time, metrics.node_jvm_mem, metrics.node_cpu_utilization, - metrics.node_index_1, - metrics.node_index_2, metrics.node_index_3, metrics.node_index_4, metrics.node_index_time, diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/node/node.js b/x-pack/plugins/monitoring/public/components/elasticsearch/node/node.js index 0b03f1077f9cb..0f545de7960d5 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/node/node.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/node/node.js @@ -43,7 +43,6 @@ export const Node = ({ nodeSummary, metrics, logs, alerts, nodeId, clusterUuid, */ const metricsToShow = [ metrics.node_jvm_mem, - metrics.node_mem, metrics.node_total_io, metrics.node_cpu_metric, metrics.node_load_average, diff --git a/x-pack/plugins/monitoring/server/lib/cluster/__fixtures__/clusters.json b/x-pack/plugins/monitoring/server/lib/cluster/__fixtures__/clusters.json index 49a82492a2301..71e27d81ef9b6 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/__fixtures__/clusters.json +++ b/x-pack/plugins/monitoring/server/lib/cluster/__fixtures__/clusters.json @@ -69,13 +69,6 @@ }, "segments": { "count": 9, - "memory_in_bytes": 14272, - "terms_memory_in_bytes": 10251, - "stored_fields_memory_in_bytes": 2808, - "term_vectors_memory_in_bytes": 0, - "norms_memory_in_bytes": 320, - "points_memory_in_bytes": 9, - "doc_values_memory_in_bytes": 884, "index_writer_memory_in_bytes": 0, "version_map_memory_in_bytes": 0, "fixed_bit_set_memory_in_bytes": 0, @@ -417,13 +410,6 @@ }, "segments": { "count": 27, - "memory_in_bytes": 120198, - "terms_memory_in_bytes": 85510, - "stored_fields_memory_in_bytes": 8480, - "term_vectors_memory_in_bytes": 0, - "norms_memory_in_bytes": 4928, - "points_memory_in_bytes": 1020, - "doc_values_memory_in_bytes": 20260, "index_writer_memory_in_bytes": 174805, "version_map_memory_in_bytes": 16836, "fixed_bit_set_memory_in_bytes": 104, diff --git a/x-pack/plugins/monitoring/server/lib/details/__snapshots__/get_metrics.test.js.snap b/x-pack/plugins/monitoring/server/lib/details/__snapshots__/get_metrics.test.js.snap index 842372f633086..f6c5079091a5d 100644 --- a/x-pack/plugins/monitoring/server/lib/details/__snapshots__/get_metrics.test.js.snap +++ b/x-pack/plugins/monitoring/server/lib/details/__snapshots__/get_metrics.test.js.snap @@ -1516,7 +1516,7 @@ Object { exports[`getMetrics and getSeries should return metrics with object structure for metric 1`] = ` Object { - "index_1": Array [ + "index_3": Array [ Object { "bucket_size": "10 mins", "data": Array [ @@ -1871,14 +1871,14 @@ Object { ], "metric": Object { "app": "elasticsearch", - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards.", - "field": "index_stats.total.segments.memory_in_bytes", + "description": "Heap memory used by Fixed Bit Sets (e.g., deeply nested documents). This is a part of Lucene Total.", + "field": "index_stats.total.segments.fixed_bit_set_memory_in_bytes", "format": "0.0 b", "hasCalculation": false, "isDerivative": false, - "label": "Lucene Total", + "label": "Fixed Bitsets", "metricAgg": "max", - "title": "Index Memory - Lucene 1", + "title": "Index Memory - Lucene", "units": "B", }, "timeRange": Object { @@ -2240,750 +2240,12 @@ Object { ], "metric": Object { "app": "elasticsearch", - "description": "Heap memory used by Stored Fields (e.g., _source). This is a part of Lucene Total.", - "field": "index_stats.total.segments.stored_fields_memory_in_bytes", + "description": "Heap memory used by Versioning (e.g., updates and deletes). This is NOT a part of Lucene Total.", + "field": "index_stats.total.segments.version_map_memory_in_bytes", "format": "0.0 b", "hasCalculation": false, "isDerivative": false, - "label": "Stored Fields", - "metricAgg": "max", - "title": "Index Memory", - "units": "B", - }, - "timeRange": Object { - "max": 1499054399999, - "min": 1498968000000, - }, - }, - Object { - "bucket_size": "10 mins", - "data": Array [ - Array [ - 1498968000000, - 2.1666666666667, - ], - Array [ - 1498968600000, - 2.3166666666667, - ], - Array [ - 1498969200000, - 2.3666666666667, - ], - Array [ - 1498969800000, - 2.5, - ], - Array [ - 1498970400000, - 2.6166666666667, - ], - Array [ - 1498971000000, - 2.85, - ], - Array [ - 1498971600000, - 2.8666666666667, - ], - Array [ - 1498972200000, - 3.0833333333333, - ], - Array [ - 1498972800000, - 1.6666666666667, - ], - Array [ - 1498973400000, - 0.11666666666667, - ], - Array [ - 1498974000000, - 0.13333333333333, - ], - Array [ - 1498974600000, - 0.43333333333333, - ], - Array [ - 1498975200000, - 1.15, - ], - Array [ - 1498975800000, - 1.1333333333333, - ], - Array [ - 1498976400000, - 1.15, - ], - Array [ - 1498977000000, - 1.05, - ], - Array [ - 1498977600000, - 1.1833333333333, - ], - Array [ - 1498978200000, - 0.91666666666667, - ], - Array [ - 1498978800000, - 0.95, - ], - Array [ - 1498979400000, - 1.0166666666667, - ], - Array [ - 1498980000000, - 1.0333333333333, - ], - Array [ - 1498980600000, - 1.05, - ], - Array [ - 1498981200000, - 1.0333333333333, - ], - Array [ - 1498981800000, - 1.0833333333333, - ], - Array [ - 1498982400000, - 1, - ], - Array [ - 1498983000000, - 1.1333333333333, - ], - Array [ - 1498983600000, - 1.3666666666667, - ], - Array [ - 1498984200000, - 1.9833333333333, - ], - Array [ - 1498984800000, - 2, - ], - Array [ - 1498985400000, - 2.1833333333333, - ], - Array [ - 1498986000000, - 2.2, - ], - Array [ - 1498986600000, - 2.3666666666667, - ], - Array [ - 1498987200000, - 2.4166666666667, - ], - Array [ - 1498987800000, - 2.55, - ], - Array [ - 1498988400000, - 2.7, - ], - Array [ - 1498989000000, - 2.8166666666667, - ], - Array [ - 1498989600000, - 2.9333333333333, - ], - Array [ - 1498990200000, - 3.0833333333333, - ], - Array [ - 1498990800000, - 3.1166666666667, - ], - Array [ - 1498991400000, - 3.3833333333333, - ], - Array [ - 1498992000000, - 3.4666666666667, - ], - Array [ - 1498992600000, - 1.0666666666667, - ], - Array [ - 1498993200000, - 1, - ], - Array [ - 1498993800000, - 1.0833333333333, - ], - Array [ - 1498994400000, - 1.1, - ], - Array [ - 1498995000000, - 1.05, - ], - Array [ - 1498995600000, - 1.0333333333333, - ], - Array [ - 1498996200000, - 1.1166666666667, - ], - Array [ - 1498996800000, - 1.0333333333333, - ], - Array [ - 1498997400000, - 1.0833333333333, - ], - Array [ - 1498998000000, - 1.0666666666667, - ], - Array [ - 1498998600000, - 1.0166666666667, - ], - Array [ - 1498999200000, - 1, - ], - Array [ - 1498999800000, - 1.05, - ], - Array [ - 1499000400000, - 1.0333333333333, - ], - Array [ - 1499001000000, - 1.8166666666667, - ], - Array [ - 1499001600000, - 2.0166666666667, - ], - Array [ - 1499002200000, - 2.0833333333333, - ], - Array [ - 1499002800000, - 2.2, - ], - Array [ - 1499003400000, - 2.3, - ], - Array [ - 1499004000000, - 2.3666666666667, - ], - Array [ - 1499004600000, - 2.5593220338983, - ], - Array [ - 1499005200000, - 2.7166666666667, - ], - Array [ - 1499005800000, - 2.8833333333333, - ], - Array [ - 1499006400000, - 3, - ], - Array [ - 1499007000000, - 3.0833333333333, - ], - Array [ - 1499007600000, - 3.25, - ], - Array [ - 1499008200000, - 3.3, - ], - Array [ - 1499008800000, - 3.5333333333333, - ], - Array [ - 1499009400000, - 3.7166666666667, - ], - Array [ - 1499010000000, - 3.7333333333333, - ], - Array [ - 1499010600000, - 3.9833333333333, - ], - Array [ - 1499011200000, - 4.1666666666667, - ], - Array [ - 1499011800000, - 2.7166666666667, - ], - Array [ - 1499012400000, - 1.0333333333333, - ], - Array [ - 1499013000000, - 1.1, - ], - Array [ - 1499013600000, - 1.2166666666667, - ], - Array [ - 1499014200000, - 1.2, - ], - Array [ - 1499014800000, - 0.98333333333333, - ], - Array [ - 1499015400000, - 1.25, - ], - Array [ - 1499016000000, - 1, - ], - Array [ - 1499016600000, - 1.2666666666667, - ], - Array [ - 1499017200000, - 1.0833333333333, - ], - Array [ - 1499017800000, - 1.05, - ], - Array [ - 1499018400000, - 0.93333333333333, - ], - Array [ - 1499019000000, - 1.05, - ], - Array [ - 1499019600000, - 1.5, - ], - ], - "metric": Object { - "app": "elasticsearch", - "description": "Heap memory used by Doc Values. This is a part of Lucene Total.", - "field": "index_stats.total.segments.doc_values_memory_in_bytes", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false, - "label": "Doc Values", - "metricAgg": "max", - "title": "Index Memory", - "units": "B", - }, - "timeRange": Object { - "max": 1499054399999, - "min": 1498968000000, - }, - }, - Object { - "bucket_size": "10 mins", - "data": Array [ - Array [ - 1498968000000, - 2.1666666666667, - ], - Array [ - 1498968600000, - 2.3166666666667, - ], - Array [ - 1498969200000, - 2.3666666666667, - ], - Array [ - 1498969800000, - 2.5, - ], - Array [ - 1498970400000, - 2.6166666666667, - ], - Array [ - 1498971000000, - 2.85, - ], - Array [ - 1498971600000, - 2.8666666666667, - ], - Array [ - 1498972200000, - 3.0833333333333, - ], - Array [ - 1498972800000, - 1.6666666666667, - ], - Array [ - 1498973400000, - 0.11666666666667, - ], - Array [ - 1498974000000, - 0.13333333333333, - ], - Array [ - 1498974600000, - 0.43333333333333, - ], - Array [ - 1498975200000, - 1.15, - ], - Array [ - 1498975800000, - 1.1333333333333, - ], - Array [ - 1498976400000, - 1.15, - ], - Array [ - 1498977000000, - 1.05, - ], - Array [ - 1498977600000, - 1.1833333333333, - ], - Array [ - 1498978200000, - 0.91666666666667, - ], - Array [ - 1498978800000, - 0.95, - ], - Array [ - 1498979400000, - 1.0166666666667, - ], - Array [ - 1498980000000, - 1.0333333333333, - ], - Array [ - 1498980600000, - 1.05, - ], - Array [ - 1498981200000, - 1.0333333333333, - ], - Array [ - 1498981800000, - 1.0833333333333, - ], - Array [ - 1498982400000, - 1, - ], - Array [ - 1498983000000, - 1.1333333333333, - ], - Array [ - 1498983600000, - 1.3666666666667, - ], - Array [ - 1498984200000, - 1.9833333333333, - ], - Array [ - 1498984800000, - 2, - ], - Array [ - 1498985400000, - 2.1833333333333, - ], - Array [ - 1498986000000, - 2.2, - ], - Array [ - 1498986600000, - 2.3666666666667, - ], - Array [ - 1498987200000, - 2.4166666666667, - ], - Array [ - 1498987800000, - 2.55, - ], - Array [ - 1498988400000, - 2.7, - ], - Array [ - 1498989000000, - 2.8166666666667, - ], - Array [ - 1498989600000, - 2.9333333333333, - ], - Array [ - 1498990200000, - 3.0833333333333, - ], - Array [ - 1498990800000, - 3.1166666666667, - ], - Array [ - 1498991400000, - 3.3833333333333, - ], - Array [ - 1498992000000, - 3.4666666666667, - ], - Array [ - 1498992600000, - 1.0666666666667, - ], - Array [ - 1498993200000, - 1, - ], - Array [ - 1498993800000, - 1.0833333333333, - ], - Array [ - 1498994400000, - 1.1, - ], - Array [ - 1498995000000, - 1.05, - ], - Array [ - 1498995600000, - 1.0333333333333, - ], - Array [ - 1498996200000, - 1.1166666666667, - ], - Array [ - 1498996800000, - 1.0333333333333, - ], - Array [ - 1498997400000, - 1.0833333333333, - ], - Array [ - 1498998000000, - 1.0666666666667, - ], - Array [ - 1498998600000, - 1.0166666666667, - ], - Array [ - 1498999200000, - 1, - ], - Array [ - 1498999800000, - 1.05, - ], - Array [ - 1499000400000, - 1.0333333333333, - ], - Array [ - 1499001000000, - 1.8166666666667, - ], - Array [ - 1499001600000, - 2.0166666666667, - ], - Array [ - 1499002200000, - 2.0833333333333, - ], - Array [ - 1499002800000, - 2.2, - ], - Array [ - 1499003400000, - 2.3, - ], - Array [ - 1499004000000, - 2.3666666666667, - ], - Array [ - 1499004600000, - 2.5593220338983, - ], - Array [ - 1499005200000, - 2.7166666666667, - ], - Array [ - 1499005800000, - 2.8833333333333, - ], - Array [ - 1499006400000, - 3, - ], - Array [ - 1499007000000, - 3.0833333333333, - ], - Array [ - 1499007600000, - 3.25, - ], - Array [ - 1499008200000, - 3.3, - ], - Array [ - 1499008800000, - 3.5333333333333, - ], - Array [ - 1499009400000, - 3.7166666666667, - ], - Array [ - 1499010000000, - 3.7333333333333, - ], - Array [ - 1499010600000, - 3.9833333333333, - ], - Array [ - 1499011200000, - 4.1666666666667, - ], - Array [ - 1499011800000, - 2.7166666666667, - ], - Array [ - 1499012400000, - 1.0333333333333, - ], - Array [ - 1499013000000, - 1.1, - ], - Array [ - 1499013600000, - 1.2166666666667, - ], - Array [ - 1499014200000, - 1.2, - ], - Array [ - 1499014800000, - 0.98333333333333, - ], - Array [ - 1499015400000, - 1.25, - ], - Array [ - 1499016000000, - 1, - ], - Array [ - 1499016600000, - 1.2666666666667, - ], - Array [ - 1499017200000, - 1.0833333333333, - ], - Array [ - 1499017800000, - 1.05, - ], - Array [ - 1499018400000, - 0.93333333333333, - ], - Array [ - 1499019000000, - 1.05, - ], - Array [ - 1499019600000, - 1.5, - ], - ], - "metric": Object { - "app": "elasticsearch", - "description": "Heap memory used by Norms (normalization factors for query-time, text scoring). This is a part of Lucene Total.", - "field": "index_stats.total.segments.norms_memory_in_bytes", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false, - "label": "Norms", + "label": "Version Map", "metricAgg": "max", "title": "Index Memory", "units": "B", diff --git a/x-pack/plugins/monitoring/server/lib/details/get_metrics.test.js b/x-pack/plugins/monitoring/server/lib/details/get_metrics.test.js index 3c991f08f6153..80234ee369aee 100644 --- a/x-pack/plugins/monitoring/server/lib/details/get_metrics.test.js +++ b/x-pack/plugins/monitoring/server/lib/details/get_metrics.test.js @@ -87,13 +87,8 @@ describe('getMetrics and getSeries', () => { const req = getMockReq(nonDerivMetricsBuckets); const metricSet = [ { - name: 'index_1', - keys: [ - 'index_mem_overall_1', - 'index_mem_stored_fields', - 'index_mem_doc_values', - 'index_mem_norms', - ], + name: 'index_3', + keys: ['index_mem_fixed_bit_set', 'index_mem_versions'], }, ]; const result = await getMetrics(req, indexPattern, metricSet); diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/__fixtures__/cluster_data.json b/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/__fixtures__/cluster_data.json index b8abe525b3fa6..a2c58a801cd8c 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/__fixtures__/cluster_data.json +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/__fixtures__/cluster_data.json @@ -2785,17 +2785,10 @@ }, "segments": { "count": 46, - "doc_values_memory_in_bytes": 61544, "file_sizes": {}, "fixed_bit_set_memory_in_bytes": 400, "index_writer_memory_in_bytes": 287996, "max_unsafe_auto_id_timestamp": 1518549739793, - "memory_in_bytes": 306088, - "norms_memory_in_bytes": 11520, - "points_memory_in_bytes": 3705, - "stored_fields_memory_in_bytes": 14456, - "term_vectors_memory_in_bytes": 0, - "terms_memory_in_bytes": 214863, "version_map_memory_in_bytes": 10008 }, "shards": { diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/__fixtures__/cluster.json b/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/__fixtures__/cluster.json index a49eb35086469..4f9daf62924c7 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/__fixtures__/cluster.json +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/__fixtures__/cluster.json @@ -49,17 +49,10 @@ }, "segments": { "count": 202, - "doc_values_memory_in_bytes": 602248, "file_sizes": {}, "fixed_bit_set_memory_in_bytes": 608, "index_writer_memory_in_bytes": 2368414, "max_unsafe_auto_id_timestamp": 1505411847867, - "memory_in_bytes": 5079404, - "norms_memory_in_bytes": 475392, - "points_memory_in_bytes": 11512, - "stored_fields_memory_in_bytes": 65080, - "term_vectors_memory_in_bytes": 0, - "terms_memory_in_bytes": 3925172, "version_map_memory_in_bytes": 697814 }, "shards": { diff --git a/x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap b/x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap index 56d923da56202..cbd6a610d5eb8 100644 --- a/x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap +++ b/x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap @@ -2577,20 +2577,6 @@ Object { "units": "ms", "uuidField": "source_node.uuid", }, - "index_mem_doc_values": SingleIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Heap memory used by Doc Values. This is a part of Lucene Total.", - "field": "index_stats.total.segments.doc_values_memory_in_bytes", - "format": "0.0 b", - "label": "Doc Values", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", - "type": "index", - "units": "B", - "uuidField": "source_node.uuid", - }, "index_mem_fielddata": IndexMemoryMetric { "app": "elasticsearch", "derivative": false, @@ -2614,91 +2600,7 @@ Object { "label": "Fixed Bitsets", "metricAgg": "max", "timestampField": "timestamp", - "title": "Index Memory", - "type": "index", - "units": "B", - "uuidField": "source_node.uuid", - }, - "index_mem_norms": SingleIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Heap memory used by Norms (normalization factors for query-time, text scoring). This is a part of Lucene Total.", - "field": "index_stats.total.segments.norms_memory_in_bytes", - "format": "0.0 b", - "label": "Norms", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", - "type": "index", - "units": "B", - "uuidField": "source_node.uuid", - }, - "index_mem_overall": SingleIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards.", - "field": "index_stats.total.segments.memory_in_bytes", - "format": "0.0 b", - "label": "Lucene Total", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", - "type": "index", - "units": "B", - "uuidField": "source_node.uuid", - }, - "index_mem_overall_1": SingleIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards.", - "field": "index_stats.total.segments.memory_in_bytes", - "format": "0.0 b", - "label": "Lucene Total", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory - Lucene 1", - "type": "index", - "units": "B", - "uuidField": "source_node.uuid", - }, - "index_mem_overall_2": SingleIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards.", - "field": "index_stats.total.segments.memory_in_bytes", - "format": "0.0 b", - "label": "Lucene Total", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory - Lucene 2", - "type": "index", - "units": "B", - "uuidField": "source_node.uuid", - }, - "index_mem_overall_3": SingleIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards.", - "field": "index_stats.total.segments.memory_in_bytes", - "format": "0.0 b", - "label": "Lucene Total", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory - Lucene 3", - "type": "index", - "units": "B", - "uuidField": "source_node.uuid", - }, - "index_mem_points": SingleIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Heap memory used by Points (e.g., numbers, IPs, and geo data). This is a part of Lucene Total.", - "field": "index_stats.total.segments.points_memory_in_bytes", - "format": "0.0 b", - "label": "Points", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", + "title": "Index Memory - Lucene", "type": "index", "units": "B", "uuidField": "source_node.uuid", @@ -2731,48 +2633,6 @@ Object { "units": "B", "uuidField": "source_node.uuid", }, - "index_mem_stored_fields": SingleIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Heap memory used by Stored Fields (e.g., _source). This is a part of Lucene Total.", - "field": "index_stats.total.segments.stored_fields_memory_in_bytes", - "format": "0.0 b", - "label": "Stored Fields", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", - "type": "index", - "units": "B", - "uuidField": "source_node.uuid", - }, - "index_mem_term_vectors": SingleIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Heap memory used by Term Vectors. This is a part of Lucene Total.", - "field": "index_stats.total.segments.term_vectors_memory_in_bytes", - "format": "0.0 b", - "label": "Term Vectors", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", - "type": "index", - "units": "B", - "uuidField": "source_node.uuid", - }, - "index_mem_terms": SingleIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Heap memory used by Terms (e.g., text). This is a part of Lucene Total.", - "field": "index_stats.total.segments.terms_memory_in_bytes", - "format": "0.0 b", - "label": "Terms", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", - "type": "index", - "units": "B", - "uuidField": "source_node.uuid", - }, "index_mem_versions": SingleIndexMemoryMetric { "app": "elasticsearch", "derivative": false, @@ -4145,20 +4005,6 @@ Object { "units": "ms", "uuidField": "source_node.uuid", }, - "node_index_mem_doc_values": NodeIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Heap memory used by Doc Values. This is a part of Lucene Total.", - "field": "node_stats.indices.segments.doc_values_memory_in_bytes", - "format": "0.0 b", - "label": "Doc Values", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", - "type": "node", - "units": "B", - "uuidField": "source_node.uuid", - }, "node_index_mem_fielddata": IndexMemoryMetric { "app": "elasticsearch", "derivative": false, @@ -4182,91 +4028,7 @@ Object { "label": "Fixed Bitsets", "metricAgg": "max", "timestampField": "timestamp", - "title": "Index Memory", - "type": "node", - "units": "B", - "uuidField": "source_node.uuid", - }, - "node_index_mem_norms": NodeIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Heap memory used by Norms (normalization factors for query-time, text scoring). This is a part of Lucene Total.", - "field": "node_stats.indices.segments.norms_memory_in_bytes", - "format": "0.0 b", - "label": "Norms", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", - "type": "node", - "units": "B", - "uuidField": "source_node.uuid", - }, - "node_index_mem_overall": NodeIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards on this node.", - "field": "node_stats.indices.segments.memory_in_bytes", - "format": "0.0 b", - "label": "Lucene Total", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", - "type": "node", - "units": "B", - "uuidField": "source_node.uuid", - }, - "node_index_mem_overall_1": NodeIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards on this node.", - "field": "node_stats.indices.segments.memory_in_bytes", - "format": "0.0 b", - "label": "Lucene Total", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory - Lucene 1", - "type": "node", - "units": "B", - "uuidField": "source_node.uuid", - }, - "node_index_mem_overall_2": NodeIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards on this node.", - "field": "node_stats.indices.segments.memory_in_bytes", - "format": "0.0 b", - "label": "Lucene Total", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory - Lucene 2", - "type": "node", - "units": "B", - "uuidField": "source_node.uuid", - }, - "node_index_mem_overall_3": NodeIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards on this node.", - "field": "node_stats.indices.segments.memory_in_bytes", - "format": "0.0 b", - "label": "Lucene Total", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory - Lucene 3", - "type": "node", - "units": "B", - "uuidField": "source_node.uuid", - }, - "node_index_mem_points": NodeIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Heap memory used by Points (e.g., numbers, IPs, and geo data). This is a part of Lucene Total.", - "field": "node_stats.indices.segments.points_memory_in_bytes", - "format": "0.0 b", - "label": "Points", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", + "title": "Index Memory - Lucene", "type": "node", "units": "B", "uuidField": "source_node.uuid", @@ -4299,48 +4061,6 @@ Object { "units": "B", "uuidField": "source_node.uuid", }, - "node_index_mem_stored_fields": NodeIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Heap memory used by Stored Fields (e.g., _source). This is a part of Lucene Total.", - "field": "node_stats.indices.segments.stored_fields_memory_in_bytes", - "format": "0.0 b", - "label": "Stored Fields", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", - "type": "node", - "units": "B", - "uuidField": "source_node.uuid", - }, - "node_index_mem_term_vectors": NodeIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Heap memory used by Term Vectors. This is a part of Lucene Total.", - "field": "node_stats.indices.segments.term_vectors_memory_in_bytes", - "format": "0.0 b", - "label": "Term Vectors", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", - "type": "node", - "units": "B", - "uuidField": "source_node.uuid", - }, - "node_index_mem_terms": NodeIndexMemoryMetric { - "app": "elasticsearch", - "derivative": false, - "description": "Heap memory used by Terms (e.g., text). This is a part of Lucene Total.", - "field": "node_stats.indices.segments.terms_memory_in_bytes", - "format": "0.0 b", - "label": "Terms", - "metricAgg": "max", - "timestampField": "timestamp", - "title": "Index Memory", - "type": "node", - "units": "B", - "uuidField": "source_node.uuid", - }, "node_index_mem_versions": NodeIndexMemoryMetric { "app": "elasticsearch", "derivative": false, diff --git a/x-pack/plugins/monitoring/server/lib/metrics/elasticsearch/metrics.js b/x-pack/plugins/monitoring/server/lib/metrics/elasticsearch/metrics.js index 6fe93619a5f9b..83841b0ecb39e 100644 --- a/x-pack/plugins/monitoring/server/lib/metrics/elasticsearch/metrics.js +++ b/x-pack/plugins/monitoring/server/lib/metrics/elasticsearch/metrics.js @@ -307,73 +307,6 @@ export const metrics = { metricAgg: 'max', units: '', }), - index_mem_overall: new SingleIndexMemoryMetric({ - field: 'memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esIndex.luceneTotalLabel', { - defaultMessage: 'Lucene Total', - }), - description: i18n.translate('xpack.monitoring.metrics.esIndex.luceneTotalDescription', { - defaultMessage: - 'Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards.', - }), - }), - index_mem_overall_1: new SingleIndexMemoryMetric({ - field: 'memory_in_bytes', - title: i18n.translate('xpack.monitoring.metrics.esIndex.indexMemoryLucene1Title', { - defaultMessage: 'Index Memory - Lucene 1', - }), - label: i18n.translate('xpack.monitoring.metrics.esIndex.indexMemoryLucene1.luceneTotalLabel', { - defaultMessage: 'Lucene Total', - }), - description: i18n.translate( - 'xpack.monitoring.metrics.esIndex.indexMemoryLucene1.luceneTotalDescription', - { - defaultMessage: - 'Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards.', - } - ), - }), - index_mem_overall_2: new SingleIndexMemoryMetric({ - field: 'memory_in_bytes', - title: i18n.translate('xpack.monitoring.metrics.esIndex.indexMemoryLucene2Title', { - defaultMessage: 'Index Memory - Lucene 2', - }), - label: i18n.translate('xpack.monitoring.metrics.esIndex.indexMemoryLucene2.luceneTotalLabel', { - defaultMessage: 'Lucene Total', - }), - description: i18n.translate( - 'xpack.monitoring.metrics.esIndex.indexMemoryLucene2.luceneTotalDescription', - { - defaultMessage: - 'Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards.', - } - ), - }), - index_mem_overall_3: new SingleIndexMemoryMetric({ - field: 'memory_in_bytes', - title: i18n.translate('xpack.monitoring.metrics.esIndex.indexMemoryLucene3Title', { - defaultMessage: 'Index Memory - Lucene 3', - }), - label: i18n.translate('xpack.monitoring.metrics.esIndex.indexMemoryLucene3.luceneTotalLabel', { - defaultMessage: 'Lucene Total', - }), - description: i18n.translate( - 'xpack.monitoring.metrics.esIndex.indexMemoryLucene3.luceneTotalDescription', - { - defaultMessage: - 'Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards.', - } - ), - }), - index_mem_doc_values: new SingleIndexMemoryMetric({ - field: 'doc_values_memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esIndex.docValuesLabel', { - defaultMessage: 'Doc Values', - }), - description: i18n.translate('xpack.monitoring.metrics.esIndex.docValuesDescription', { - defaultMessage: 'Heap memory used by Doc Values. This is a part of Lucene Total.', - }), - }), // Note: This is not segment memory, unlike SingleIndexMemoryMetrics index_mem_fielddata: new IndexMemoryMetric({ field: 'index_stats.total.fielddata.memory_size_in_bytes', @@ -389,6 +322,9 @@ export const metrics = { }), index_mem_fixed_bit_set: new SingleIndexMemoryMetric({ field: 'fixed_bit_set_memory_in_bytes', + title: i18n.translate('xpack.monitoring.metrics.esIndex.fixedBitsetsTitle', { + defaultMessage: 'Index Memory - Lucene', + }), label: i18n.translate('xpack.monitoring.metrics.esIndex.fixedBitsetsLabel', { defaultMessage: 'Fixed Bitsets', }), @@ -397,26 +333,6 @@ export const metrics = { 'Heap memory used by Fixed Bit Sets (e.g., deeply nested documents). This is a part of Lucene Total.', }), }), - index_mem_norms: new SingleIndexMemoryMetric({ - field: 'norms_memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esIndex.normsLabel', { - defaultMessage: 'Norms', - }), - description: i18n.translate('xpack.monitoring.metrics.esIndex.normsDescription', { - defaultMessage: - 'Heap memory used by Norms (normalization factors for query-time, text scoring). This is a part of Lucene Total.', - }), - }), - index_mem_points: new SingleIndexMemoryMetric({ - field: 'points_memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esIndex.pointsLabel', { - defaultMessage: 'Points', - }), - description: i18n.translate('xpack.monitoring.metrics.esIndex.pointsDescription', { - defaultMessage: - 'Heap memory used by Points (e.g., numbers, IPs, and geo data). This is a part of Lucene Total.', - }), - }), // Note: This is not segment memory, unlike SingleIndexMemoryMetrics index_mem_query_cache: new IndexMemoryMetric({ field: 'index_stats.total.query_cache.memory_size_in_bytes', @@ -448,34 +364,6 @@ export const metrics = { }), type: 'index', }), - index_mem_stored_fields: new SingleIndexMemoryMetric({ - field: 'stored_fields_memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esIndex.storedFieldsLabel', { - defaultMessage: 'Stored Fields', - }), - description: i18n.translate('xpack.monitoring.metrics.esIndex.storedFieldsDescription', { - defaultMessage: - 'Heap memory used by Stored Fields (e.g., _source). This is a part of Lucene Total.', - }), - }), - index_mem_term_vectors: new SingleIndexMemoryMetric({ - field: 'term_vectors_memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esIndex.termVectorsLabel', { - defaultMessage: 'Term Vectors', - }), - description: i18n.translate('xpack.monitoring.metrics.esIndex.termVectorsDescription', { - defaultMessage: 'Heap memory used by Term Vectors. This is a part of Lucene Total.', - }), - }), - index_mem_terms: new SingleIndexMemoryMetric({ - field: 'terms_memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esIndex.termsLabel', { - defaultMessage: 'Terms', - }), - description: i18n.translate('xpack.monitoring.metrics.esIndex.termsDescription', { - defaultMessage: 'Heap memory used by Terms (e.g., text). This is a part of Lucene Total.', - }), - }), index_mem_versions: new SingleIndexMemoryMetric({ field: 'version_map_memory_in_bytes', label: i18n.translate('xpack.monitoring.metrics.esIndex.versionMapLabel', { @@ -1050,73 +938,6 @@ export const metrics = { metricAgg: 'max', units: '', }), - node_index_mem_overall: new NodeIndexMemoryMetric({ - field: 'memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esNode.luceneTotalLabel', { - defaultMessage: 'Lucene Total', - }), - description: i18n.translate('xpack.monitoring.metrics.esNode.luceneTotalDescription', { - defaultMessage: - 'Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards on this node.', - }), - }), - node_index_mem_overall_1: new NodeIndexMemoryMetric({ - field: 'memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esNode.indexMemoryLucene1.lucenceTotalLabel', { - defaultMessage: 'Lucene Total', - }), - title: i18n.translate('xpack.monitoring.metrics.esNode.indexMemoryLucene1Title', { - defaultMessage: 'Index Memory - Lucene 1', - }), - description: i18n.translate( - 'xpack.monitoring.metrics.esNode.indexMemoryLucene1.lucenceTotalDescription', - { - defaultMessage: - 'Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards on this node.', - } - ), - }), - node_index_mem_overall_2: new NodeIndexMemoryMetric({ - field: 'memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esNode.indexMemoryLucene2.lucenceTotalLabel', { - defaultMessage: 'Lucene Total', - }), - title: i18n.translate('xpack.monitoring.metrics.esNode.indexMemoryLucene2Title', { - defaultMessage: 'Index Memory - Lucene 2', - }), - description: i18n.translate( - 'xpack.monitoring.metrics.esNode.indexMemoryLucene2.lucenceTotalDescription', - { - defaultMessage: - 'Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards on this node.', - } - ), - }), - node_index_mem_overall_3: new NodeIndexMemoryMetric({ - field: 'memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esNode.indexMemoryLucene3.lucenceTotalLabel', { - defaultMessage: 'Lucene Total', - }), - title: i18n.translate('xpack.monitoring.metrics.esNode.indexMemoryLucene3Title', { - defaultMessage: 'Index Memory - Lucene 3', - }), - description: i18n.translate( - 'xpack.monitoring.metrics.esNode.indexMemoryLucene3.lucenceTotalDescription', - { - defaultMessage: - 'Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards on this node.', - } - ), - }), - node_index_mem_doc_values: new NodeIndexMemoryMetric({ - field: 'doc_values_memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esNode.docValuesLabel', { - defaultMessage: 'Doc Values', - }), - description: i18n.translate('xpack.monitoring.metrics.esNode.docValuesDescription', { - defaultMessage: 'Heap memory used by Doc Values. This is a part of Lucene Total.', - }), - }), // Note: This is not segment memory, unlike the rest of the SingleIndexMemoryMetrics node_index_mem_fielddata: new IndexMemoryMetric({ field: 'node_stats.indices.fielddata.memory_size_in_bytes', @@ -1132,6 +953,9 @@ export const metrics = { }), node_index_mem_fixed_bit_set: new NodeIndexMemoryMetric({ field: 'fixed_bit_set_memory_in_bytes', + title: i18n.translate('xpack.monitoring.metrics.esNode.fixedBitsetsTitle', { + defaultMessage: 'Index Memory - Lucene', + }), label: i18n.translate('xpack.monitoring.metrics.esNode.fixedBitsetsLabel', { defaultMessage: 'Fixed Bitsets', }), @@ -1140,26 +964,6 @@ export const metrics = { 'Heap memory used by Fixed Bit Sets (e.g., deeply nested documents). This is a part of Lucene Total.', }), }), - node_index_mem_norms: new NodeIndexMemoryMetric({ - field: 'norms_memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esNode.normsLabel', { - defaultMessage: 'Norms', - }), - description: i18n.translate('xpack.monitoring.metrics.esNode.normsDescription', { - defaultMessage: - 'Heap memory used by Norms (normalization factors for query-time, text scoring). This is a part of Lucene Total.', - }), - }), - node_index_mem_points: new NodeIndexMemoryMetric({ - field: 'points_memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esNode.pointsLabel', { - defaultMessage: 'Points', - }), - description: i18n.translate('xpack.monitoring.metrics.esNode.pointsDescription', { - defaultMessage: - 'Heap memory used by Points (e.g., numbers, IPs, and geo data). This is a part of Lucene Total.', - }), - }), // Note: This is not segment memory, unlike SingleIndexMemoryMetrics node_index_mem_query_cache: new IndexMemoryMetric({ field: 'node_stats.indices.query_cache.memory_size_in_bytes', @@ -1191,34 +995,6 @@ export const metrics = { }), type: 'node', }), - node_index_mem_stored_fields: new NodeIndexMemoryMetric({ - field: 'stored_fields_memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esNode.storedFieldsLabel', { - defaultMessage: 'Stored Fields', - }), - description: i18n.translate('xpack.monitoring.metrics.esNode.storedFieldsDescription', { - defaultMessage: - 'Heap memory used by Stored Fields (e.g., _source). This is a part of Lucene Total.', - }), - }), - node_index_mem_term_vectors: new NodeIndexMemoryMetric({ - field: 'term_vectors_memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esNode.termVectorsLabel', { - defaultMessage: 'Term Vectors', - }), - description: i18n.translate('xpack.monitoring.metrics.esNode.termVectorsDescription', { - defaultMessage: 'Heap memory used by Term Vectors. This is a part of Lucene Total.', - }), - }), - node_index_mem_terms: new NodeIndexMemoryMetric({ - field: 'terms_memory_in_bytes', - label: i18n.translate('xpack.monitoring.metrics.esNode.termsLabel', { - defaultMessage: 'Terms', - }), - description: i18n.translate('xpack.monitoring.metrics.esNode.termsDescription', { - defaultMessage: 'Heap memory used by Terms (e.g., text). This is a part of Lucene Total.', - }), - }), node_index_mem_versions: new NodeIndexMemoryMetric({ field: 'version_map_memory_in_bytes', label: i18n.translate('xpack.monitoring.metrics.esNode.versionMapLabel', { diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_index_detail.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_index_detail.js index 14712ecd59f0e..fce09eac4918f 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_index_detail.js +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_index_detail.js @@ -8,25 +8,7 @@ export const metricSet = { advanced: [ { - keys: [ - 'index_mem_overall_1', - 'index_mem_stored_fields', - 'index_mem_doc_values', - 'index_mem_norms', - ], - name: 'index_1', - }, - { - keys: ['index_mem_overall_2', 'index_mem_terms', 'index_mem_points'], - name: 'index_2', - }, - { - keys: [ - 'index_mem_overall_3', - 'index_mem_fixed_bit_set', - 'index_mem_term_vectors', - 'index_mem_versions', - ], + keys: ['index_mem_fixed_bit_set', 'index_mem_versions'], name: 'index_3', }, { @@ -82,10 +64,6 @@ export const metricSet = { keys: ['index_store_total_size', 'index_store_primaries_size'], name: 'index_size', }, - { - keys: ['index_mem_overall', 'index_mem_terms', 'index_mem_points'], - name: 'index_mem', - }, 'index_document_count', { keys: ['index_segment_count_total', 'index_segment_count_primaries'], diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_node_detail.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_node_detail.js index dcb5b70cf8963..5303dc452f850 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_node_detail.js +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_node_detail.js @@ -20,25 +20,7 @@ export const metricSets = { name: 'node_gc_time', }, { - keys: [ - 'node_index_mem_overall_1', - 'node_index_mem_stored_fields', - 'node_index_mem_doc_values', - 'node_index_mem_norms', - ], - name: 'node_index_1', - }, - { - keys: ['node_index_mem_overall_2', 'node_index_mem_terms', 'node_index_mem_points'], - name: 'node_index_2', - }, - { - keys: [ - 'node_index_mem_overall_3', - 'node_index_mem_fixed_bit_set', - 'node_index_mem_term_vectors', - 'node_index_mem_versions', - ], + keys: ['node_index_mem_fixed_bit_set', 'node_index_mem_versions'], name: 'node_index_3', }, { @@ -101,10 +83,6 @@ export const metricSets = { keys: ['node_jvm_mem_max_in_bytes', 'node_jvm_mem_used_in_bytes'], name: 'node_jvm_mem', }, - { - keys: ['node_index_mem_overall', 'node_index_mem_terms', 'node_index_mem_points'], - name: 'node_mem', - }, { keys: [], name: 'node_cpu_metric', diff --git a/x-pack/plugins/reporting/server/config/index.ts b/x-pack/plugins/reporting/server/config/index.ts index fa836fd47cde3..711e930484e01 100644 --- a/x-pack/plugins/reporting/server/config/index.ts +++ b/x-pack/plugins/reporting/server/config/index.ts @@ -18,10 +18,10 @@ export const config: PluginConfigDescriptor = { exposeToBrowser: { poll: true, roles: true }, schema: ConfigSchema, deprecations: ({ unused }) => [ - unused('capture.browser.chromium.maxScreenshotDimension'), // unused since 7.8 - unused('poll.jobCompletionNotifier.intervalErrorMultiplier'), // unused since 7.10 - unused('poll.jobsRefresh.intervalErrorMultiplier'), // unused since 7.10 - unused('capture.viewport'), // deprecated as unused since 7.16 + unused('capture.browser.chromium.maxScreenshotDimension', { level: 'warning' }), // unused since 7.8 + unused('poll.jobCompletionNotifier.intervalErrorMultiplier', { level: 'warning' }), // unused since 7.10 + unused('poll.jobsRefresh.intervalErrorMultiplier', { level: 'warning' }), // unused since 7.10 + unused('capture.viewport', { level: 'warning' }), // deprecated as unused since 7.16 (settings, fromPath, addDeprecation) => { const reporting = get(settings, fromPath); if (reporting?.roles?.enabled !== false) { diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts.ts b/x-pack/plugins/security_solution/cypress/screens/alerts.ts index 0d2eab221b49a..6177234575ec3 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts.ts @@ -7,10 +7,8 @@ export const ADD_EXCEPTION_BTN = '[data-test-subj="add-exception-menu-item"]'; -export const ALERTS = '[data-test-subj="events-viewer-panel"] [data-test-subj="event"]'; - -export const ALERTS_COUNT = - '[data-test-subj="events-viewer-panel"] [data-test-subj="server-side-event-count"]'; +export const ALERT_COUNT_TABLE_FIRST_ROW_COUNT = + '[data-test-subj="alertsCountTable"] tr:nth-child(1) td:nth-child(2) .euiTableCellContent__text'; export const ALERT_CHECKBOX = '[data-test-subj~="select-event"].euiCheckbox__input'; @@ -28,16 +26,28 @@ export const ALERT_RULE_SEVERITY = '[data-test-subj="formatted-field-kibana.aler export const ALERT_DATA_GRID = '[data-test-subj="dataGridWrapper"]'; +export const ALERTS = '[data-test-subj="events-viewer-panel"][data-test-subj="event"]'; + +export const ALERTS_COUNT = + '[data-test-subj="events-viewer-panel"] [data-test-subj="server-side-event-count"]'; + +export const ALERTS_TREND_SIGNAL_RULE_NAME_PANEL = + '[data-test-subj="render-content-kibana.alert.rule.name"]'; + export const CLOSE_ALERT_BTN = '[data-test-subj="close-alert-status"]'; export const CLOSE_SELECTED_ALERTS_BTN = '[data-test-subj="close-alert-status"]'; export const CLOSED_ALERTS_FILTER_BTN = '[data-test-subj="closedAlerts"]'; +export const DESTINATION_IP = '[data-test-subj^=formatted-field][data-test-subj$=destination\\.ip]'; + export const EMPTY_ALERT_TABLE = '[data-test-subj="tGridEmptyState"]'; export const EXPAND_ALERT_BTN = '[data-test-subj="expand-event"]'; +export const HOST_NAME = '[data-test-subj^=formatted-field][data-test-subj$=host\\.name]'; + export const ACKNOWLEDGED_ALERTS_FILTER_BTN = '[data-test-subj="acknowledgedAlerts"]'; export const LOADING_ALERTS_PANEL = '[data-test-subj="loading-alerts-panel"]'; @@ -53,18 +63,26 @@ export const OPEN_ALERT_BTN = '[data-test-subj="open-alert-status"]'; export const OPENED_ALERTS_FILTER_BTN = '[data-test-subj="openAlerts"]'; +export const PROCESS_NAME = '[data-test-subj="formatted-field-process.name"]'; + +export const REASON = '[data-test-subj^=formatted-field][data-test-subj$=reason]'; + +export const RISK_SCORE = '[data-test-subj^=formatted-field][data-test-subj$=risk_score]'; + +export const RULE_NAME = '[data-test-subj^=formatted-field][data-test-subj$=rule\\.name]'; + export const SELECTED_ALERTS = '[data-test-subj="selectedShowBulkActionsButton"]'; export const SEND_ALERT_TO_TIMELINE_BTN = '[data-test-subj="send-alert-to-timeline-button"]'; +export const SEVERITY = '[data-test-subj^=formatted-field][data-test-subj$=severity]'; + +export const SOURCE_IP = '[data-test-subj^=formatted-field][data-test-subj$=source\\.ip]'; + export const TAKE_ACTION_POPOVER_BTN = '[data-test-subj="selectedShowBulkActionsButton"]'; export const TIMELINE_CONTEXT_MENU_BTN = '[data-test-subj="timeline-context-menu-button"]'; -export const ATTACH_ALERT_TO_CASE_BUTTON = '[data-test-subj="add-existing-case-menu-item"]'; +export const USER_NAME = '[data-test-subj^=formatted-field][data-test-subj$=user\\.name]'; -export const ALERT_COUNT_TABLE_FIRST_ROW_COUNT = - '[data-test-subj="alertsCountTable"] tr:nth-child(1) td:nth-child(2) .euiTableCellContent__text'; - -export const ALERTS_TREND_SIGNAL_RULE_NAME_PANEL = - '[data-test-subj="render-content-kibana.alert.rule.name"]'; +export const ATTACH_ALERT_TO_CASE_BUTTON = '[data-test-subj="add-existing-case-menu-item"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts index 85a4fa257a957..70ccfcea7ba2c 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts @@ -30,6 +30,28 @@ export const JSON_VIEW_TAB = '[data-test-subj="jsonViewTab"]'; export const JSON_TEXT = '[data-test-subj="jsonView"]'; +export const OVERVIEW_HOST_NAME = + '[data-test-subj="eventDetails"] [data-test-subj="host-details-button"]'; + +export const OVERVIEW_RISK_SCORE = + '[data-test-subj="eventDetails"] [data-test-subj^=formatted][data-test-subj$=risk_score]'; + +export const OVERVIEW_RULE = + '[data-test-subj="eventDetails"] [data-test-subj^=formatted][data-test-subj$=rule\\.name]'; + +export const OVERVIEW_SEVERITY = + '[data-test-subj="eventDetails"] [data-test-subj^=formatted][data-test-subj$=rule\\.severity]'; + +export const OVERVIEW_STATUS = '[data-test-subj="eventDetails"] [data-test-subj$=status]'; + +export const OVERVIEW_THRESHOLD_COUNT = + '[data-test-subj="eventDetails"] [data-test-subj^=formatted][data-test-subj$=threshold_result\\.count]'; + +export const OVERVIEW_THRESHOLD_VALUE = + '[data-test-subj="eventDetails"] [data-test-subj$=threshold_result\\.terms]'; + +export const SUMMARY_VIEW = '[data-test-subj="summary-view"]'; + export const TABLE_CELL = '.euiTableRowCell'; export const TABLE_TAB = '[data-test-subj="tableTab"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/timelines.ts b/x-pack/plugins/security_solution/cypress/screens/timelines.ts index 5e64e4fbb5ece..21febda41d062 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timelines.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timelines.ts @@ -11,9 +11,7 @@ export const EXPAND_NOTES_BTN = '[data-test-subj="expand-notes"]'; export const EXPORT_TIMELINE_ACTION = '[data-test-subj="export-timeline-action"]'; -export const IMPORT_BTN = '.euiButton__text'; - -export const IMPORT_BTN_POSITION = 8; +export const IMPORT_BTN = '.euiButton.euiButton--primary.euiButton--fill'; export const IMPORT_TIMELINE_BTN = '[data-test-subj="open-import-data-modal-btn"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index 84b81108f8be3..e304d08f4c68f 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts @@ -149,6 +149,10 @@ export const goToRuleDetails = () => { cy.get(RULE_NAME).first().click({ force: true }); }; +export const goToTheRuleDetailsOf = (ruleName: string) => { + cy.get(RULE_NAME).contains(ruleName).click(); +}; + export const loadPrebuiltDetectionRules = () => { cy.get(LOAD_PREBUILT_RULES_BTN).should('exist').click({ force: true }); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/timelines.ts b/x-pack/plugins/security_solution/cypress/tasks/timelines.ts index 07c752a191968..d7f69638d0c55 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timelines.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timelines.ts @@ -12,7 +12,6 @@ import { EXPAND_NOTES_BTN, EXPORT_TIMELINE_ACTION, IMPORT_BTN, - IMPORT_BTN_POSITION, IMPORT_TIMELINE_BTN, INPUT_FILE, TIMELINES_TABLE, @@ -34,7 +33,7 @@ export const importTimeline = (timeline: string) => { cy.get(IMPORT_TIMELINE_BTN).click(); cy.get(INPUT_FILE).should('exist'); cy.get(INPUT_FILE).trigger('click', { force: true }).attachFile(timeline).trigger('change'); - cy.get(IMPORT_BTN).eq(IMPORT_BTN_POSITION).click({ force: true }); + cy.get(IMPORT_BTN).last().click({ force: true }); cy.get(INPUT_FILE).should('not.exist'); }; diff --git a/x-pack/plugins/security_solution/cypress/upgrade_integration/custom_query_rule.spec.ts b/x-pack/plugins/security_solution/cypress/upgrade_integration/custom_query_rule.spec.ts index 1af7a3f9bed03..e4464ae43dd62 100644 --- a/x-pack/plugins/security_solution/cypress/upgrade_integration/custom_query_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/upgrade_integration/custom_query_rule.spec.ts @@ -4,8 +4,18 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { ALERT_DETAILS_CELLS, SERVER_SIDE_EVENT_COUNT } from '../screens/alerts_detection_rules'; +import { + DESTINATION_IP, + HOST_NAME, + PROCESS_NAME, + REASON, + RISK_SCORE, + RULE_NAME, + SEVERITY, + SOURCE_IP, + USER_NAME, +} from '../screens/alerts'; +import { SERVER_SIDE_EVENT_COUNT } from '../screens/alerts_detection_rules'; import { ADDITIONAL_LOOK_BACK_DETAILS, ABOUT_DETAILS, @@ -24,7 +34,7 @@ import { } from '../screens/rule_details'; import { waitForPageToBeLoaded } from '../tasks/common'; -import { waitForRulesTableToBeLoaded, goToRuleDetails } from '../tasks/alerts_detection_rules'; +import { waitForRulesTableToBeLoaded, goToTheRuleDetailsOf } from '../tasks/alerts_detection_rules'; import { loginAndWaitForPage } from '../tasks/login'; import { DETECTIONS_RULE_MANAGEMENT_URL } from '../urls/navigation'; @@ -38,8 +48,8 @@ const alert = { reason: 'file event with process test, file The file to test, by Security Solution on security-solution.local created low alert Custom query rule for upgrade.', hostName: 'security-solution.local', - username: 'test', - processName: 'The file to test', + username: 'Security Solution', + processName: 'test', fileName: 'The file to test', sourceIp: '127.0.0.1', destinationIp: '127.0.0.2', @@ -49,7 +59,7 @@ const rule = { customQuery: '*:*', name: 'Custom query rule for upgrade', description: 'My description', - index: ['auditbeat-*'], + index: ['auditbeat-custom*'], severity: 'Low', riskScore: '7', timelineTemplate: 'none', @@ -62,7 +72,7 @@ describe('After an upgrade, the custom query rule', () => { before(() => { loginAndWaitForPage(DETECTIONS_RULE_MANAGEMENT_URL); waitForRulesTableToBeLoaded(); - goToRuleDetails(); + goToTheRuleDetailsOf(rule.name); waitForPageToBeLoaded(); }); @@ -89,54 +99,15 @@ describe('After an upgrade, the custom query rule', () => { }); }); - it('Displays the alert details', () => { - cy.get(ALERT_DETAILS_CELLS).first().focus(); - cy.get(ALERT_DETAILS_CELLS).first().type('{rightarrow}'); - cy.get(ALERT_DETAILS_CELLS) - .contains(alert.rule) - .then(($el) => { - cy.wrap($el).type('{rightarrow}'); - }); - cy.get(ALERT_DETAILS_CELLS) - .contains(alert.severity) - .then(($el) => { - cy.wrap($el).type('{rightarrow}'); - }); - cy.get(ALERT_DETAILS_CELLS) - .contains(alert.riskScore) - .then(($el) => { - cy.wrap($el).type('{rightarrow}'); - }); - cy.get(ALERT_DETAILS_CELLS) - .contains(alert.reason) - .then(($el) => { - cy.wrap($el).type('{rightarrow}'); - }); - cy.get(ALERT_DETAILS_CELLS) - .contains(alert.hostName) - .then(($el) => { - cy.wrap($el).type('{rightarrow}'); - }); - cy.get(ALERT_DETAILS_CELLS) - .contains(alert.username) - .then(($el) => { - cy.wrap($el).type('{rightarrow}'); - }); - cy.get(ALERT_DETAILS_CELLS) - .contains(alert.processName) - .then(($el) => { - cy.wrap($el).type('{rightarrow}'); - }); - cy.get(ALERT_DETAILS_CELLS) - .contains(alert.fileName) - .then(($el) => { - cy.wrap($el).type('{rightarrow}'); - }); - cy.get(ALERT_DETAILS_CELLS) - .contains(alert.sourceIp) - .then(($el) => { - cy.wrap($el).type('{rightarrow}'); - }); - cy.get(ALERT_DETAILS_CELLS).contains(alert.destinationIp); + it('Displays the alert details at the tgrid', () => { + cy.get(RULE_NAME).should('have.text', alert.rule); + cy.get(SEVERITY).should('have.text', alert.severity); + cy.get(RISK_SCORE).should('have.text', alert.riskScore); + cy.get(REASON).should('have.text', alert.reason).type('{rightarrow}'); + cy.get(HOST_NAME).should('have.text', alert.hostName); + cy.get(USER_NAME).should('have.text', alert.username); + cy.get(PROCESS_NAME).should('have.text', alert.processName); + cy.get(SOURCE_IP).should('have.text', alert.sourceIp); + cy.get(DESTINATION_IP).should('have.text', alert.destinationIp); }); }); diff --git a/x-pack/plugins/security_solution/cypress/upgrade_integration/import_timeline.spec.ts b/x-pack/plugins/security_solution/cypress/upgrade_integration/import_timeline.spec.ts index 3ce3937b3e4b2..464c42d9fc220 100644 --- a/x-pack/plugins/security_solution/cypress/upgrade_integration/import_timeline.spec.ts +++ b/x-pack/plugins/security_solution/cypress/upgrade_integration/import_timeline.spec.ts @@ -54,7 +54,7 @@ const username = 'elastic'; const timelineDetails = { dateStart: 'Oct 11, 2020 @ 00:00:00.000', dateEnd: 'Oct 11, 2030 @ 17:13:15.851', - queryTab: 'Query2', + queryTab: 'Query4', correlationTab: 'Correlation', analyzerTab: 'Analyzer', notesTab: 'Notes2', diff --git a/x-pack/plugins/security_solution/cypress/upgrade_integration/threshold_rule.spec.ts b/x-pack/plugins/security_solution/cypress/upgrade_integration/threshold_rule.spec.ts new file mode 100644 index 0000000000000..b6dbcd0e3232c --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/upgrade_integration/threshold_rule.spec.ts @@ -0,0 +1,126 @@ +/* + * 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 { HOST_NAME, REASON, RISK_SCORE, RULE_NAME, SEVERITY } from '../screens/alerts'; +import { SERVER_SIDE_EVENT_COUNT } from '../screens/alerts_detection_rules'; +import { + ADDITIONAL_LOOK_BACK_DETAILS, + ABOUT_DETAILS, + ABOUT_RULE_DESCRIPTION, + CUSTOM_QUERY_DETAILS, + DEFINITION_DETAILS, + getDetails, + INDEX_PATTERNS_DETAILS, + RISK_SCORE_DETAILS, + RULE_NAME_HEADER, + RULE_TYPE_DETAILS, + RUNS_EVERY_DETAILS, + SCHEDULE_DETAILS, + SEVERITY_DETAILS, + THRESHOLD_DETAILS, + TIMELINE_TEMPLATE_DETAILS, +} from '../screens/rule_details'; + +import { expandFirstAlert } from '../tasks/alerts'; +import { waitForPageToBeLoaded } from '../tasks/common'; +import { waitForRulesTableToBeLoaded, goToRuleDetails } from '../tasks/alerts_detection_rules'; +import { loginAndWaitForPage } from '../tasks/login'; + +import { DETECTIONS_RULE_MANAGEMENT_URL } from '../urls/navigation'; +import { + OVERVIEW_HOST_NAME, + OVERVIEW_RISK_SCORE, + OVERVIEW_RULE, + OVERVIEW_SEVERITY, + OVERVIEW_STATUS, + OVERVIEW_THRESHOLD_COUNT, + OVERVIEW_THRESHOLD_VALUE, + SUMMARY_VIEW, +} from '../screens/alerts_details'; + +const EXPECTED_NUMBER_OF_ALERTS = '1'; + +const alert = { + rule: 'Threshold rule', + severity: 'medium', + riskScore: '17', + reason: 'event created medium alert Threshold rule.', + hostName: 'security-solution.local', + thresholdCount: '2', +}; + +const rule = { + customQuery: '*:*', + name: 'Threshold rule', + description: 'Threshold rule for testing upgrade', + index: ['auditbeat-threshold*'], + severity: 'Medium', + riskScore: '17', + timelineTemplate: 'none', + runsEvery: '60s', + lookBack: '2999999m', + timeline: 'None', + thresholdField: 'host.name', + threholdValue: '1', +}; + +describe('After an upgrade, the threshold rule', () => { + before(() => { + loginAndWaitForPage(DETECTIONS_RULE_MANAGEMENT_URL); + waitForRulesTableToBeLoaded(); + goToRuleDetails(); + waitForPageToBeLoaded(); + }); + + it('Has the expected alerts number', () => { + cy.get(SERVER_SIDE_EVENT_COUNT).contains(EXPECTED_NUMBER_OF_ALERTS); + }); + + it('Displays the rule details', () => { + cy.get(RULE_NAME_HEADER).should('contain', rule.name); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); + cy.get(ABOUT_DETAILS).within(() => { + getDetails(SEVERITY_DETAILS).should('have.text', rule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', rule.riskScore); + }); + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', rule.index.join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.customQuery); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Threshold'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', rule.timeline); + getDetails(THRESHOLD_DETAILS).should( + 'have.text', + `Results aggregated by ${rule.thresholdField} >= ${rule.threholdValue}` + ); + }); + cy.get(SCHEDULE_DETAILS).within(() => { + getDetails(RUNS_EVERY_DETAILS).should('have.text', rule.runsEvery); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', rule.lookBack); + }); + }); + + it('Displays the alert details in the TGrid', () => { + cy.get(RULE_NAME).should('have.text', alert.rule); + cy.get(SEVERITY).should('have.text', alert.severity); + cy.get(RISK_SCORE).should('have.text', alert.riskScore); + cy.get(REASON).should('have.text', alert.reason); + cy.get(HOST_NAME).should('have.text', alert.hostName); + }); + + it('Displays the Overview alert details in the alert flyout', () => { + expandFirstAlert(); + + cy.get(OVERVIEW_STATUS).should('have.text', 'open'); + cy.get(OVERVIEW_RULE).should('have.text', alert.rule); + cy.get(OVERVIEW_SEVERITY).should('have.text', alert.severity); + cy.get(OVERVIEW_RISK_SCORE).should('have.text', alert.riskScore); + cy.get(OVERVIEW_HOST_NAME).should('have.text', alert.hostName); + cy.get(OVERVIEW_THRESHOLD_COUNT).should('have.text', alert.thresholdCount); + cy.get(OVERVIEW_THRESHOLD_VALUE).should('have.text', alert.hostName); + cy.get(SUMMARY_VIEW).should('contain', `${rule.thresholdField} [threshold]`); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.tsx index 6795557f17f1a..ad86b0f362234 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.tsx +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { memo, useMemo } from 'react'; +import React, { memo, useMemo, useRef, useEffect } from 'react'; import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiTextColor, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { useTestIdGenerator } from '../../../../management/components/hooks/use_test_id_generator'; @@ -28,12 +28,20 @@ export interface EndpointHostIsolationStatusProps { export const EndpointHostIsolationStatus = memo( ({ isIsolated, pendingIsolate = 0, pendingUnIsolate = 0, 'data-test-subj': dataTestSubj }) => { const getTestId = useTestIdGenerator(dataTestSubj); - const isPendingStatuseDisabled = useIsExperimentalFeatureEnabled( + const isPendingStatusDisabled = useIsExperimentalFeatureEnabled( 'disableIsolationUIPendingStatuses' ); + const wasReleasing = useRef(false); + const wasIsolating = useRef(false); + + useEffect(() => { + wasReleasing.current = pendingIsolate === 0 && pendingUnIsolate > 0; + wasIsolating.current = pendingIsolate > 0 && pendingUnIsolate === 0; + }, [pendingIsolate, pendingUnIsolate]); + return useMemo(() => { - if (isPendingStatuseDisabled) { + if (isPendingStatusDisabled) { // If nothing is pending and host is not currently isolated, then render nothing if (!isIsolated) { return null; @@ -49,21 +57,23 @@ export const EndpointHostIsolationStatus = memo - - - ); + // If nothing is pending + if (!(pendingIsolate || pendingUnIsolate)) { + // and host is either releasing and or currently released, then render nothing + if ((!wasIsolating.current && wasReleasing.current) || !isIsolated) { + return null; + } + // else host was isolating or is isolated, then show isolation badge + else if ((!isIsolated && wasIsolating.current && !wasReleasing.current) || isIsolated) { + return ( + + + + ); + } } // If there are multiple types of pending isolation actions, then show count of actions with tooltip that displays breakdown @@ -136,7 +146,7 @@ export const EndpointHostIsolationStatus = memo( { index: ENDPOINT_ACTION_RESPONSES_INDEX, + size: 10000, body: { query: { bool: { diff --git a/x-pack/plugins/task_manager/server/task_pool.test.ts b/x-pack/plugins/task_manager/server/task_pool.test.ts index 05eb7bd1b43e1..8b10fb6c1ca3d 100644 --- a/x-pack/plugins/task_manager/server/task_pool.test.ts +++ b/x-pack/plugins/task_manager/server/task_pool.test.ts @@ -357,6 +357,33 @@ describe('TaskPool', () => { ); }); + test('only allows one task with the same id in the task pool', async () => { + const logger = loggingSystemMock.create().get(); + const pool = new TaskPool({ + maxWorkers$: of(2), + logger, + }); + + const shouldRun = mockRun(); + const shouldNotRun = mockRun(); + + const taskId = uuid.v4(); + const task1 = mockTask({ id: taskId, run: shouldRun }); + const task2 = mockTask({ + id: taskId, + run: shouldNotRun, + isSameTask() { + return true; + }, + }); + + await pool.run([task1]); + await pool.run([task2]); + + expect(shouldRun).toHaveBeenCalledTimes(1); + expect(shouldNotRun).not.toHaveBeenCalled(); + }); + function mockRun() { return jest.fn(async () => { await sleep(0); @@ -367,6 +394,7 @@ describe('TaskPool', () => { function mockTask(overrides = {}) { return { isExpired: false, + taskExecutionId: uuid.v4(), id: uuid.v4(), cancel: async () => undefined, markTaskAsRunning: jest.fn(async () => true), @@ -387,6 +415,9 @@ describe('TaskPool', () => { createTaskRunner: jest.fn(), }; }, + isSameTask() { + return false; + }, ...overrides, }; } diff --git a/x-pack/plugins/task_manager/server/task_pool.ts b/x-pack/plugins/task_manager/server/task_pool.ts index 87de0e691d471..d8a9fdd6223fe 100644 --- a/x-pack/plugins/task_manager/server/task_pool.ts +++ b/x-pack/plugins/task_manager/server/task_pool.ts @@ -112,9 +112,21 @@ export class TaskPool { if (tasksToRun.length) { await Promise.all( tasksToRun - .filter((taskRunner) => !this.tasksInPool.has(taskRunner.id)) + .filter( + (taskRunner) => + !Array.from(this.tasksInPool.keys()).some((executionId: string) => + taskRunner.isSameTask(executionId) + ) + ) .map(async (taskRunner) => { - this.tasksInPool.set(taskRunner.id, taskRunner); + // We use taskRunner.taskExecutionId instead of taskRunner.id as key for the task pool map because + // task cancellation is a non-blocking procedure. We calculate the expiration and immediately remove + // the task from the task pool. There is a race condition that can occur when a recurring tasks's schedule + // matches its timeout value. A new instance of the task can be claimed and added to the task pool before + // the cancel function (meant for the previous instance of the task) is actually called. This means the wrong + // task instance is cancelled. We introduce the taskExecutionId to differentiate between these overlapping instances and + // ensure that the correct task instance is cancelled. + this.tasksInPool.set(taskRunner.taskExecutionId, taskRunner); return taskRunner .markTaskAsRunning() .then((hasTaskBeenMarkAsRunning: boolean) => @@ -165,12 +177,12 @@ export class TaskPool { } }) .then(() => { - this.tasksInPool.delete(taskRunner.id); + this.tasksInPool.delete(taskRunner.taskExecutionId); }); } private handleFailureOfMarkAsRunning(task: TaskRunner, err: Error) { - this.tasksInPool.delete(task.id); + this.tasksInPool.delete(task.taskExecutionId); this.logger.error(`Failed to mark Task ${task.toString()} as running: ${err.message}`); } @@ -198,7 +210,7 @@ export class TaskPool { private async cancelTask(task: TaskRunner) { try { this.logger.debug(`Cancelling task ${task.toString()}.`); - this.tasksInPool.delete(task.id); + this.tasksInPool.delete(task.taskExecutionId); await task.cancel(); } catch (err) { this.logger.error(`Failed to cancel task ${task.toString()}: ${err}`); diff --git a/x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts b/x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts index 358a8003382b0..0695ed149c9a4 100644 --- a/x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts +++ b/x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts @@ -12,6 +12,7 @@ */ import apm from 'elastic-apm-node'; +import uuid from 'uuid'; import { withSpan } from '@kbn/apm-utils'; import { identity } from 'lodash'; import { Logger, ExecutionContextStart } from '../../../../../src/core/server'; @@ -75,6 +76,7 @@ export class EphemeralTaskManagerRunner implements TaskRunner { private beforeRun: Middleware['beforeRun']; private beforeMarkRunning: Middleware['beforeMarkRunning']; private onTaskEvent: (event: TaskRun | TaskMarkRunning) => void; + private uuid: string; private readonly executionContext: ExecutionContextStart; /** @@ -102,6 +104,7 @@ export class EphemeralTaskManagerRunner implements TaskRunner { this.beforeMarkRunning = beforeMarkRunning; this.onTaskEvent = onTaskEvent; this.executionContext = executionContext; + this.uuid = uuid.v4(); } /** @@ -111,6 +114,21 @@ export class EphemeralTaskManagerRunner implements TaskRunner { return this.instance.task.id; } + /** + * Gets the exeuction id of this task instance. + */ + public get taskExecutionId() { + return `${this.id}::${this.uuid}`; + } + + /** + * Test whether given execution ID identifies a different execution of this same task + * @param id + */ + public isSameTask(executionId: string) { + return executionId.startsWith(this.id); + } + /** * Gets the task type of this task instance. */ diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts index 86e2230461eb5..02be86c3db0c2 100644 --- a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts +++ b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts @@ -32,6 +32,10 @@ const minutesFromNow = (mins: number): Date => secondsFromNow(mins * 60); let fakeTimer: sinon.SinonFakeTimers; +jest.mock('uuid', () => ({ + v4: () => 'NEW_UUID', +})); + beforeAll(() => { fakeTimer = sinon.useFakeTimers(); }); @@ -45,6 +49,19 @@ describe('TaskManagerRunner', () => { end: jest.fn(), }; + test('execution ID', async () => { + const { runner } = await pendingStageSetup({ + instance: { + id: 'foo', + taskType: 'bar', + }, + }); + + expect(runner.taskExecutionId).toEqual(`foo::NEW_UUID`); + expect(runner.isSameTask(`foo::ANOTHER_UUID`)).toEqual(true); + expect(runner.isSameTask(`bar::ANOTHER_UUID`)).toEqual(false); + }); + describe('Pending Stage', () => { beforeEach(() => { jest.clearAllMocks(); diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.ts index 2a5d48845ce48..cd07b4db728c4 100644 --- a/x-pack/plugins/task_manager/server/task_running/task_runner.ts +++ b/x-pack/plugins/task_manager/server/task_running/task_runner.ts @@ -12,6 +12,7 @@ */ import apm from 'elastic-apm-node'; +import uuid from 'uuid'; import { withSpan } from '@kbn/apm-utils'; import { identity, defaults, flow } from 'lodash'; import { @@ -68,9 +69,11 @@ export interface TaskRunner { markTaskAsRunning: () => Promise; run: () => Promise>; id: string; + taskExecutionId: string; stage: string; isEphemeral?: boolean; toString: () => string; + isSameTask: (executionId: string) => boolean; } export enum TaskRunningStage { @@ -141,6 +144,7 @@ export class TaskManagerRunner implements TaskRunner { private beforeMarkRunning: Middleware['beforeMarkRunning']; private onTaskEvent: (event: TaskRun | TaskMarkRunning) => void; private defaultMaxAttempts: number; + private uuid: string; private readonly executionContext: ExecutionContextStart; /** @@ -173,6 +177,7 @@ export class TaskManagerRunner implements TaskRunner { this.onTaskEvent = onTaskEvent; this.defaultMaxAttempts = defaultMaxAttempts; this.executionContext = executionContext; + this.uuid = uuid.v4(); } /** @@ -182,6 +187,21 @@ export class TaskManagerRunner implements TaskRunner { return this.instance.task.id; } + /** + * Gets the execution id of this task instance. + */ + public get taskExecutionId() { + return `${this.id}::${this.uuid}`; + } + + /** + * Test whether given execution ID identifies a different execution of this same task + * @param id + */ + public isSameTask(executionId: string) { + return executionId.startsWith(this.id); + } + /** * Gets the task type of this task instance. */ diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index eee055e75eb8f..a4ec0285361e4 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4279,7 +4279,6 @@ "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.titleColumnName": "タイトル", "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.typeColumnName": "型", "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModalTitle": "保存されたオブジェクトの削除", - "savedObjectsManagement.objectsTable.export.dangerNotification": "エクスポートを生成できません", "savedObjectsManagement.objectsTable.export.successNotification": "ファイルはバックグラウンドでダウンロード中です", "savedObjectsManagement.objectsTable.export.successWithExcludedObjectsNotification": "ファイルはバックグラウンドでダウンロード中です。一部のオブジェクトはエクスポートから除外されました。除外されたオブジェクトの一覧は、エクスポートされたファイルの最後の行をご覧ください。", "savedObjectsManagement.objectsTable.export.successWithMissingRefsNotification": "ファイルはバックグラウンドでダウンロード中です。一部の関連オブジェクトが見つかりませんでした。足りないオブジェクトの一覧は、エクスポートされたファイルの最後の行をご覧ください。", @@ -18326,8 +18325,6 @@ "xpack.monitoring.metrics.esIndex.disk.storePrimariesDescription": "ディスク上のプライマリシャードのサイズです。", "xpack.monitoring.metrics.esIndex.disk.storePrimariesLabel": "格納サイズ(プライマリ)", "xpack.monitoring.metrics.esIndex.diskTitle": "ディスク", - "xpack.monitoring.metrics.esIndex.docValuesDescription": "ドキュメント値が使用中のヒープ領域です。Lucene の一部です。", - "xpack.monitoring.metrics.esIndex.docValuesLabel": "ドキュメント値", "xpack.monitoring.metrics.esIndex.fielddataDescription": "フィールドデータ(例:グローバル序数またはテキストフィールドで特別に有効化されたフィールドデータ)が使用中のヒープ領域です。同じシャードのものですが、Lucene 合計には含まれません。", "xpack.monitoring.metrics.esIndex.fielddataLabel": "フィールドデータ", "xpack.monitoring.metrics.esIndex.fixedBitsetsDescription": "固定ビットセット(例:ディープネスト構造のドキュメント)が使用中のヒープ領域です。Lucene の一部です。", @@ -18340,15 +18337,6 @@ "xpack.monitoring.metrics.esIndex.indexMemoryEs.queryCacheDescription": "クエリキャッシュ(例:キャッシュされたフィルター)が使用中のヒープ領域です。同じシャードのものですが、Lucene 合計には含まれません。", "xpack.monitoring.metrics.esIndex.indexMemoryEs.queryCacheLabel": "クエリキャッシュ", "xpack.monitoring.metrics.esIndex.indexMemoryEsTitle": "インデックスメモリー - {elasticsearch}", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene1.luceneTotalDescription": "Lucene が現在のインデックスに使用中の合計ヒープ領域です。これはプライマリとレプリカシャードの他のフィールドの合計です。", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene1.luceneTotalLabel": "Lucene 合計", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene1Title": "インデックスメモリー - Lucene 1", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene2.luceneTotalDescription": "Lucene が現在のインデックスに使用中の合計ヒープ領域です。これはプライマリとレプリカシャードの他のフィールドの合計です。", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene2.luceneTotalLabel": "Lucene 合計", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene2Title": "インデックスメモリー - Lucene 2", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene3.luceneTotalDescription": "Lucene が現在のインデックスに使用中の合計ヒープ領域です。これはプライマリとレプリカシャードの他のフィールドの合計です。", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene3.luceneTotalLabel": "Lucene 合計", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene3Title": "インデックスメモリー - Lucene 3", "xpack.monitoring.metrics.esIndex.indexWriterDescription": "Index Writer が使用中のヒープ領域です。Lucene 合計の一部ではありません。", "xpack.monitoring.metrics.esIndex.indexWriterLabel": "Index Writer", "xpack.monitoring.metrics.esIndex.latency.indexingLatencyDescription": "ドキュメントのインデックスの平均レイテンシです。ドキュメントのインデックスの所要時間をインデックス数で割った時間です。プライマリシャードのみが含まれています。", @@ -18356,12 +18344,6 @@ "xpack.monitoring.metrics.esIndex.latency.searchLatencyDescription": "検索の平均レイテンシです。検索の実行に所要した時間を送信された検索数で割った時間です。プライマリとレプリカシャードが含まれています。", "xpack.monitoring.metrics.esIndex.latency.searchLatencyLabel": "検索レイテンシ", "xpack.monitoring.metrics.esIndex.latencyTitle": "レイテンシ", - "xpack.monitoring.metrics.esIndex.luceneTotalDescription": "Lucene が現在のインデックスに使用中の合計ヒープ領域です。これはプライマリとレプリカシャードの他のフィールドの合計です。", - "xpack.monitoring.metrics.esIndex.luceneTotalLabel": "Lucene 合計", - "xpack.monitoring.metrics.esIndex.normsDescription": "Norms(クエリ時間、テキストスコアリングの規格化因子)が使用中のヒープ領域です。Lucene の一部です。", - "xpack.monitoring.metrics.esIndex.normsLabel": "Norms", - "xpack.monitoring.metrics.esIndex.pointsDescription": "ポイント(例:数字、IP、地理データ)が使用中のヒープ領域です。Lucene の一部です。", - "xpack.monitoring.metrics.esIndex.pointsLabel": "ポイント", "xpack.monitoring.metrics.esIndex.refreshTime.primariesDescription": "プライマリシャードの更新オペレーションの所要時間です。", "xpack.monitoring.metrics.esIndex.refreshTime.primariesLabel": "プライマリ", "xpack.monitoring.metrics.esIndex.refreshTime.totalDescription": "プライマリとレプリカシャードの更新オペレーションの所要時間です。", @@ -18384,17 +18366,6 @@ "xpack.monitoring.metrics.esIndex.searchRate.totalShardsDescription": "プライマリとレプリカシャードで実行されている検索リクエストの数です。1 つの検索を複数シャードに対して実行することができます!", "xpack.monitoring.metrics.esIndex.searchRate.totalShardsLabel": "合計シャード", "xpack.monitoring.metrics.esIndex.searchRateTitle": "検索レート", - "xpack.monitoring.metrics.esIndex.segmentCount.primariesDescription": "プライマリシャードのセグメント数です。", - "xpack.monitoring.metrics.esIndex.segmentCount.primariesLabel": "プライマリ", - "xpack.monitoring.metrics.esIndex.segmentCount.totalDescription": "プライマリとレプリカシャードのセグメント数です。", - "xpack.monitoring.metrics.esIndex.segmentCount.totalLabel": "合計", - "xpack.monitoring.metrics.esIndex.segmentCountTitle": "セグメントカウント", - "xpack.monitoring.metrics.esIndex.storedFieldsDescription": "格納フィールド(例:_source)が使用中のヒープ領域です。Lucene の一部です。", - "xpack.monitoring.metrics.esIndex.storedFieldsLabel": "格納フィールド", - "xpack.monitoring.metrics.esIndex.termsDescription": "用語が使用中のヒープ領域です(例:テキスト)。Lucene の一部です。", - "xpack.monitoring.metrics.esIndex.termsLabel": "用語", - "xpack.monitoring.metrics.esIndex.termVectorsDescription": "用語ベクトルが使用中のヒープ領域です。Lucene の一部です。", - "xpack.monitoring.metrics.esIndex.termVectorsLabel": "用語ベクトル", "xpack.monitoring.metrics.esIndex.throttleTime.indexingDescription": "プライマリとレプリカシャードのインデックスオペレーションのスロットリングの所要時間です。", "xpack.monitoring.metrics.esIndex.throttleTime.indexingLabel": "インデックス", "xpack.monitoring.metrics.esIndex.throttleTime.indexingPrimariesDescription": "プライマリシャードのインデックスオペレーションのスロットリングの所要時間です。", @@ -18421,8 +18392,6 @@ "xpack.monitoring.metrics.esNode.diskFreeSpaceLabel": "ディスクの空き容量", "xpack.monitoring.metrics.esNode.documentCountDescription": "プライマリシャードのみのドキュメントの合計数です。", "xpack.monitoring.metrics.esNode.documentCountLabel": "ドキュメントカウント", - "xpack.monitoring.metrics.esNode.docValuesDescription": "ドキュメント値が使用中のヒープ領域です。Lucene の一部です。", - "xpack.monitoring.metrics.esNode.docValuesLabel": "ドキュメント値", "xpack.monitoring.metrics.esNode.fielddataDescription": "フィールドデータ(例:グローバル序数またはテキストフィールドで特別に有効化されたフィールドデータ)が使用中のヒープ領域です。同じシャードのものですが、Lucene 合計には含まれません。", "xpack.monitoring.metrics.esNode.fielddataLabel": "フィールドデータ", "xpack.monitoring.metrics.esNode.fixedBitsetsDescription": "固定ビットセット(例:ディープネスト構造のドキュメント)が使用中のヒープ領域です。Lucene の一部です。", @@ -18452,15 +18421,6 @@ "xpack.monitoring.metrics.esNode.indexMemoryEs.queryCacheDescription": "クエリキャッシュ(例:キャッシュされたフィルター)が使用中のヒープ領域です。同じシャードのものですが、Lucene 合計には含まれません。", "xpack.monitoring.metrics.esNode.indexMemoryEs.queryCacheLabel": "クエリキャッシュ", "xpack.monitoring.metrics.esNode.indexMemoryEsTitle": "インデックスメモリー - {elasticsearch}", - "xpack.monitoring.metrics.esNode.indexMemoryLucene1.lucenceTotalDescription": "Lucene が現在のインデックスに使用中の合計ヒープ領域です。これはノードのプライマリとレプリカシャードの他のフィールドの合計です。", - "xpack.monitoring.metrics.esNode.indexMemoryLucene1.lucenceTotalLabel": "Lucene 合計", - "xpack.monitoring.metrics.esNode.indexMemoryLucene1Title": "インデックスメモリー - Lucene 1", - "xpack.monitoring.metrics.esNode.indexMemoryLucene2.lucenceTotalDescription": "Lucene が現在のインデックスに使用中の合計ヒープ領域です。これはノードのプライマリとレプリカシャードの他のフィールドの合計です。", - "xpack.monitoring.metrics.esNode.indexMemoryLucene2.lucenceTotalLabel": "Lucene 合計", - "xpack.monitoring.metrics.esNode.indexMemoryLucene2Title": "インデックスメモリー - Lucene 2", - "xpack.monitoring.metrics.esNode.indexMemoryLucene3.lucenceTotalDescription": "Lucene が現在のインデックスに使用中の合計ヒープ領域です。これはノードのプライマリとレプリカシャードの他のフィールドの合計です。", - "xpack.monitoring.metrics.esNode.indexMemoryLucene3.lucenceTotalLabel": "Lucene 合計", - "xpack.monitoring.metrics.esNode.indexMemoryLucene3Title": "インデックスメモリー - Lucene 3", "xpack.monitoring.metrics.esNode.indexThrottlingTimeDescription": "インデックススロットリングの所要時間です。結合に時間がかかっていることを示します。", "xpack.monitoring.metrics.esNode.indexThrottlingTimeLabel": "インデックススロットリング時間", "xpack.monitoring.metrics.esNode.indexWriterDescription": "Index Writer が使用中のヒープ領域です。Lucene 合計の一部ではありません。", @@ -18476,14 +18436,8 @@ "xpack.monitoring.metrics.esNode.latency.searchDescription": "検索の平均レイテンシです。検索の実行に所要した時間を送信された検索数で割った時間です。プライマリとレプリカシャードが含まれています。", "xpack.monitoring.metrics.esNode.latency.searchLabel": "検索", "xpack.monitoring.metrics.esNode.latencyTitle": "レイテンシ", - "xpack.monitoring.metrics.esNode.luceneTotalDescription": "Lucene が現在のインデックスに使用中の合計ヒープ領域です。これはノードのプライマリとレプリカシャードの他のフィールドの合計です。", - "xpack.monitoring.metrics.esNode.luceneTotalLabel": "Lucene 合計", "xpack.monitoring.metrics.esNode.mergeRateDescription": "結合されたセグメントのバイト数です。この数字が大きいほどディスクアクティビティが多いことを示します。", "xpack.monitoring.metrics.esNode.mergeRateLabel": "結合レート", - "xpack.monitoring.metrics.esNode.normsDescription": "Norms(クエリ時間、テキストスコアリングの規格化因子)が使用中のヒープ領域です。Lucene の一部です。", - "xpack.monitoring.metrics.esNode.normsLabel": "Norms", - "xpack.monitoring.metrics.esNode.pointsDescription": "ポイント(例:数字、IP、地理データ)が使用中のヒープ領域です。Lucene の一部です。", - "xpack.monitoring.metrics.esNode.pointsLabel": "ポイント", "xpack.monitoring.metrics.esNode.readThreads.getQueueDescription": "キューにある GET オペレーションの数です。", "xpack.monitoring.metrics.esNode.readThreads.getQueueLabel": "GET キュー", "xpack.monitoring.metrics.esNode.readThreads.getRejectionsDescription": "キューがいっぱいの時に拒否された GET オペレーションの数です。", @@ -18503,15 +18457,9 @@ "xpack.monitoring.metrics.esNode.searchRateTitle": "検索レート", "xpack.monitoring.metrics.esNode.segmentCountDescription": "このノードのプライマリとレプリカシャードの最大セグメントカウントです。", "xpack.monitoring.metrics.esNode.segmentCountLabel": "セグメントカウント", - "xpack.monitoring.metrics.esNode.storedFieldsDescription": "格納フィールド(例:_source)が使用中のヒープ領域です。Lucene の一部です。", - "xpack.monitoring.metrics.esNode.storedFieldsLabel": "格納フィールド", "xpack.monitoring.metrics.esNode.systemLoad.last1MinuteDescription": "過去 1 分間の平均負荷です。", "xpack.monitoring.metrics.esNode.systemLoad.last1MinuteLabel": "1m", "xpack.monitoring.metrics.esNode.systemLoadTitle": "システム負荷", - "xpack.monitoring.metrics.esNode.termsDescription": "用語が使用中のヒープ領域です(例:テキスト)。Lucene の一部です。", - "xpack.monitoring.metrics.esNode.termsLabel": "用語", - "xpack.monitoring.metrics.esNode.termVectorsDescription": "用語ベクトルが使用中のヒープ領域です。Lucene の一部です。", - "xpack.monitoring.metrics.esNode.termVectorsLabel": "用語ベクトル", "xpack.monitoring.metrics.esNode.threadQueue.getDescription": "このノードでプロセス待ちの Get オペレーションの数です。", "xpack.monitoring.metrics.esNode.threadQueue.getLabel": "Get", "xpack.monitoring.metrics.esNode.threadQueueTitle": "スレッドキュー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3a5d909e12049..4dc213d5897d8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4320,7 +4320,6 @@ "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.titleColumnName": "标题", "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.typeColumnName": "类型", "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModalTitle": "删除已保存对象", - "savedObjectsManagement.objectsTable.export.dangerNotification": "无法生成导出", "savedObjectsManagement.objectsTable.export.successNotification": "您的文件正在后台下载", "savedObjectsManagement.objectsTable.export.successWithExcludedObjectsNotification": "您的文件正在后台下载。一些对象已从导出中排除。有关已排除对象列表,请查看导出文件的最后一行。", "savedObjectsManagement.objectsTable.export.successWithMissingRefsNotification": "您的文件正在后台下载。找不到某些相关对象。有关缺失对象列表,请查看导出文件的最后一行。", @@ -18597,8 +18596,6 @@ "xpack.monitoring.metrics.esIndex.disk.storePrimariesDescription": "磁盘上主分片的大小。", "xpack.monitoring.metrics.esIndex.disk.storePrimariesLabel": "存储(主分片)", "xpack.monitoring.metrics.esIndex.diskTitle": "磁盘", - "xpack.monitoring.metrics.esIndex.docValuesDescription": "文档值使用的堆内存。这是 Lucene 合计的组成部分。", - "xpack.monitoring.metrics.esIndex.docValuesLabel": "文档值", "xpack.monitoring.metrics.esIndex.fielddataDescription": "Fielddata(例如全局序号或文本字段上显式启用的 Fielddata)使用的堆内存。这适用于相同的分片,但不是 Lucene 合计的组成部分。", "xpack.monitoring.metrics.esIndex.fielddataLabel": "Fielddata", "xpack.monitoring.metrics.esIndex.fixedBitsetsDescription": "固定位集(例如深嵌套文档)使用的堆内存。这是 Lucene 合计的组成部分。", @@ -18611,15 +18608,6 @@ "xpack.monitoring.metrics.esIndex.indexMemoryEs.queryCacheDescription": "查询缓存(例如缓存的筛选)使用的堆内存。这适用于相同的分片,但不是 Lucene 合计的组成部分。", "xpack.monitoring.metrics.esIndex.indexMemoryEs.queryCacheLabel": "查询缓存", "xpack.monitoring.metrics.esIndex.indexMemoryEsTitle": "索引内存 - {elasticsearch}", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene1.luceneTotalDescription": "Lucene 用于当前索引的堆内存合计。这是主分片和副本分片的其他字段合计。", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene1.luceneTotalLabel": "Lucene 合计", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene1Title": "索引内存 - Lucene 1", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene2.luceneTotalDescription": "Lucene 用于当前索引的堆内存合计。这是主分片和副本分片的其他字段合计。", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene2.luceneTotalLabel": "Lucene 合计", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene2Title": "索引内存 - Lucene 2", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene3.luceneTotalDescription": "Lucene 用于当前索引的堆内存合计。这是主分片和副本分片的其他字段合计。", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene3.luceneTotalLabel": "Lucene 合计", - "xpack.monitoring.metrics.esIndex.indexMemoryLucene3Title": "索引内存 - Lucene 3", "xpack.monitoring.metrics.esIndex.indexWriterDescription": "索引编写器使用的堆内存。这不是 Lucene 合计的组成部分。", "xpack.monitoring.metrics.esIndex.indexWriterLabel": "索引编写器", "xpack.monitoring.metrics.esIndex.latency.indexingLatencyDescription": "索引文档的平均延迟,即索引文档所用时间除以索引文档的数目。这仅考虑主分片。", @@ -18627,12 +18615,6 @@ "xpack.monitoring.metrics.esIndex.latency.searchLatencyDescription": "搜索的平均延迟,即执行搜索所用的时间除以提交的搜索数目。这考虑主分片和副本分片。", "xpack.monitoring.metrics.esIndex.latency.searchLatencyLabel": "搜索延迟", "xpack.monitoring.metrics.esIndex.latencyTitle": "延迟", - "xpack.monitoring.metrics.esIndex.luceneTotalDescription": "Lucene 用于当前索引的堆内存合计。这是主分片和副本分片的其他字段合计。", - "xpack.monitoring.metrics.esIndex.luceneTotalLabel": "Lucene 合计", - "xpack.monitoring.metrics.esIndex.normsDescription": "Norms(查询时间、文本评分的标准化因子)使用的堆内存。这是 Lucene 合计的组成部分。", - "xpack.monitoring.metrics.esIndex.normsLabel": "Norms", - "xpack.monitoring.metrics.esIndex.pointsDescription": "Points(数字、IP 和地理数据)使用的堆内存。这是 Lucene 合计的组成部分。", - "xpack.monitoring.metrics.esIndex.pointsLabel": "点", "xpack.monitoring.metrics.esIndex.refreshTime.primariesDescription": "对主分片执行刷新操作所花费的时间量。", "xpack.monitoring.metrics.esIndex.refreshTime.primariesLabel": "主分片", "xpack.monitoring.metrics.esIndex.refreshTime.totalDescription": "对主分片和副本分片执行刷新操作所花费的时间量。", @@ -18655,17 +18637,6 @@ "xpack.monitoring.metrics.esIndex.searchRate.totalShardsDescription": "跨主分片和副本分片执行的搜索请求数目。可以对多个分片运行单个搜索!", "xpack.monitoring.metrics.esIndex.searchRate.totalShardsLabel": "分片合计", "xpack.monitoring.metrics.esIndex.searchRateTitle": "搜索速率", - "xpack.monitoring.metrics.esIndex.segmentCount.primariesDescription": "主分片的段数。", - "xpack.monitoring.metrics.esIndex.segmentCount.primariesLabel": "主分片", - "xpack.monitoring.metrics.esIndex.segmentCount.totalDescription": "主分片和副本分片的段数。", - "xpack.monitoring.metrics.esIndex.segmentCount.totalLabel": "合计", - "xpack.monitoring.metrics.esIndex.segmentCountTitle": "段计数", - "xpack.monitoring.metrics.esIndex.storedFieldsDescription": "存储字段(例如 _source)使用的堆内存。这是 Lucene 合计的组成部分。", - "xpack.monitoring.metrics.esIndex.storedFieldsLabel": "存储字段", - "xpack.monitoring.metrics.esIndex.termsDescription": "字词(例如文本)使用的堆内存。这是 Lucene 合计的组成部分。", - "xpack.monitoring.metrics.esIndex.termsLabel": "词", - "xpack.monitoring.metrics.esIndex.termVectorsDescription": "字词向量使用的堆内存。这是 Lucene 合计的组成部分。", - "xpack.monitoring.metrics.esIndex.termVectorsLabel": "字词向量", "xpack.monitoring.metrics.esIndex.throttleTime.indexingDescription": "对主分片和副本分片限制索引操作所花费的时间量。", "xpack.monitoring.metrics.esIndex.throttleTime.indexingLabel": "索引", "xpack.monitoring.metrics.esIndex.throttleTime.indexingPrimariesDescription": "对主分片限制索引操作所花费的时间量。", @@ -18692,8 +18663,6 @@ "xpack.monitoring.metrics.esNode.diskFreeSpaceLabel": "磁盘可用空间", "xpack.monitoring.metrics.esNode.documentCountDescription": "总文档数目,仅包括主分片。", "xpack.monitoring.metrics.esNode.documentCountLabel": "文档计数", - "xpack.monitoring.metrics.esNode.docValuesDescription": "文档值使用的堆内存。这是 Lucene 合计的组成部分。", - "xpack.monitoring.metrics.esNode.docValuesLabel": "文档值", "xpack.monitoring.metrics.esNode.fielddataDescription": "Fielddata(例如全局序号或文本字段上显式启用的 Fielddata)使用的堆内存。这适用于相同的分片,但不是 Lucene 合计的组成部分。", "xpack.monitoring.metrics.esNode.fielddataLabel": "Fielddata", "xpack.monitoring.metrics.esNode.fixedBitsetsDescription": "固定位集(例如深嵌套文档)使用的堆内存。这是 Lucene 合计的组成部分。", @@ -18723,15 +18692,6 @@ "xpack.monitoring.metrics.esNode.indexMemoryEs.queryCacheDescription": "查询缓存(例如缓存的筛选)使用的堆内存。这适用于相同的分片,但不是 Lucene 合计的组成部分。", "xpack.monitoring.metrics.esNode.indexMemoryEs.queryCacheLabel": "查询缓存", "xpack.monitoring.metrics.esNode.indexMemoryEsTitle": "索引内存 - {elasticsearch}", - "xpack.monitoring.metrics.esNode.indexMemoryLucene1.lucenceTotalDescription": "Lucene 用于当前索引的堆内存合计。这是此节点上主分片和副本分片的其他字段合计。", - "xpack.monitoring.metrics.esNode.indexMemoryLucene1.lucenceTotalLabel": "Lucene 合计", - "xpack.monitoring.metrics.esNode.indexMemoryLucene1Title": "索引内存 - Lucene 1", - "xpack.monitoring.metrics.esNode.indexMemoryLucene2.lucenceTotalDescription": "Lucene 用于当前索引的堆内存合计。这是此节点上主分片和副本分片的其他字段合计。", - "xpack.monitoring.metrics.esNode.indexMemoryLucene2.lucenceTotalLabel": "Lucene 合计", - "xpack.monitoring.metrics.esNode.indexMemoryLucene2Title": "索引内存 - Lucene 2", - "xpack.monitoring.metrics.esNode.indexMemoryLucene3.lucenceTotalDescription": "Lucene 用于当前索引的堆内存合计。这是此节点上主分片和副本分片的其他字段合计。", - "xpack.monitoring.metrics.esNode.indexMemoryLucene3.lucenceTotalLabel": "Lucene 合计", - "xpack.monitoring.metrics.esNode.indexMemoryLucene3Title": "索引内存 - Lucene 3", "xpack.monitoring.metrics.esNode.indexThrottlingTimeDescription": "因索引限制所花费的时间量,其表示合并缓慢。", "xpack.monitoring.metrics.esNode.indexThrottlingTimeLabel": "索引限制时间", "xpack.monitoring.metrics.esNode.indexWriterDescription": "索引编写器使用的堆内存。这不是 Lucene 合计的组成部分。", @@ -18747,14 +18707,8 @@ "xpack.monitoring.metrics.esNode.latency.searchDescription": "搜索的平均延迟,即执行搜索所用的时间除以提交的搜索数目。这考虑主分片和副本分片。", "xpack.monitoring.metrics.esNode.latency.searchLabel": "搜索", "xpack.monitoring.metrics.esNode.latencyTitle": "延迟", - "xpack.monitoring.metrics.esNode.luceneTotalDescription": "Lucene 用于当前索引的堆内存合计。这是此节点上主分片和副本分片的其他字段合计。", - "xpack.monitoring.metrics.esNode.luceneTotalLabel": "Lucene 合计", "xpack.monitoring.metrics.esNode.mergeRateDescription": "已合并段的字节数。较大数值表示磁盘活动较密集。", "xpack.monitoring.metrics.esNode.mergeRateLabel": "合并速率", - "xpack.monitoring.metrics.esNode.normsDescription": "Norms(查询时间、文本评分的标准化因子)使用的堆内存。这是 Lucene 合计的组成部分。", - "xpack.monitoring.metrics.esNode.normsLabel": "Norms", - "xpack.monitoring.metrics.esNode.pointsDescription": "Points(数字、IP 和地理数据)使用的堆内存。这是 Lucene 合计的组成部分。", - "xpack.monitoring.metrics.esNode.pointsLabel": "点", "xpack.monitoring.metrics.esNode.readThreads.getQueueDescription": "队列中的 GET 操作数目。", "xpack.monitoring.metrics.esNode.readThreads.getQueueLabel": "GET 队列", "xpack.monitoring.metrics.esNode.readThreads.getRejectionsDescription": "被拒绝(发生在队列已满时)的 GET 操作数目。", @@ -18774,15 +18728,9 @@ "xpack.monitoring.metrics.esNode.searchRateTitle": "搜索速率", "xpack.monitoring.metrics.esNode.segmentCountDescription": "此节点上主分片和副本分片的最大段计数。", "xpack.monitoring.metrics.esNode.segmentCountLabel": "段计数", - "xpack.monitoring.metrics.esNode.storedFieldsDescription": "存储字段(例如 _source)使用的堆内存。这是 Lucene 合计的组成部分。", - "xpack.monitoring.metrics.esNode.storedFieldsLabel": "存储字段", "xpack.monitoring.metrics.esNode.systemLoad.last1MinuteDescription": "过去 1 分钟的负载平均值。", "xpack.monitoring.metrics.esNode.systemLoad.last1MinuteLabel": "1 分钟", "xpack.monitoring.metrics.esNode.systemLoadTitle": "系统负载", - "xpack.monitoring.metrics.esNode.termsDescription": "字词(例如文本)使用的堆内存。这是 Lucene 合计的组成部分。", - "xpack.monitoring.metrics.esNode.termsLabel": "词", - "xpack.monitoring.metrics.esNode.termVectorsDescription": "字词向量使用的堆内存。这是 Lucene 合计的组成部分。", - "xpack.monitoring.metrics.esNode.termVectorsLabel": "字词向量", "xpack.monitoring.metrics.esNode.threadQueue.getDescription": "此节点上等候处理的 Get 操作数目。", "xpack.monitoring.metrics.esNode.threadQueue.getLabel": "获取", "xpack.monitoring.metrics.esNode.threadQueueTitle": "线程队列", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts index 95322fa0d0bcf..8181a5171d198 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts @@ -92,10 +92,18 @@ export const ALERT_ERROR_LICENSE_REASON = i18n.translate( } ); +export const ALERT_ERROR_TIMEOUT_REASON = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertErrorReasonTimeout', + { + defaultMessage: 'Rule execution cancelled due to timeout.', + } +); + export const alertsErrorReasonTranslationsMapping = { read: ALERT_ERROR_READING_REASON, decrypt: ALERT_ERROR_DECRYPTING_REASON, execute: ALERT_ERROR_EXECUTION_REASON, unknown: ALERT_ERROR_UNKNOWN_REASON, license: ALERT_ERROR_LICENSE_REASON, + timeout: ALERT_ERROR_TIMEOUT_REASON, }; diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts index 7978d6c82025f..00adf916bb713 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts @@ -465,6 +465,56 @@ function getPatternFiringAlertType() { return result; } +function getLongRunningPatternRuleType(cancelAlertsOnRuleTimeout: boolean = true) { + const paramsSchema = schema.object({ + pattern: schema.arrayOf(schema.boolean()), + }); + type ParamsType = TypeOf; + interface State extends AlertTypeState { + patternIndex?: number; + } + const result: AlertType = { + id: `test.patternLongRunning${ + cancelAlertsOnRuleTimeout === true ? '.cancelAlertsOnRuleTimeout' : '' + }`, + name: 'Test: Run Long on a Pattern', + actionGroups: [{ id: 'default', name: 'Default' }], + producer: 'alertsFixture', + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + ruleTaskTimeout: '3s', + cancelAlertsOnRuleTimeout, + async executor(ruleExecutorOptions) { + const { services, state, params } = ruleExecutorOptions; + const pattern = params.pattern; + if (!Array.isArray(pattern)) { + throw new Error(`pattern is not an array`); + } + + // await new Promise((resolve) => setTimeout(resolve, 5000)); + + // get the pattern index, return if past it + const patternIndex = state.patternIndex ?? 0; + if (patternIndex >= pattern.length) { + return { patternIndex }; + } + + // run long if pattern says to + if (pattern[patternIndex] === true) { + await new Promise((resolve) => setTimeout(resolve, 10000)); + } + + services.alertInstanceFactory('alert').scheduleActions('default', {}); + + return { + patternIndex: patternIndex + 1, + }; + }, + }; + return result; +} + export function defineAlertTypes( core: CoreSetup, { alerting }: Pick @@ -579,4 +629,6 @@ export function defineAlertTypes( alerting.registerType(longRunningAlertType); alerting.registerType(goldNoopAlertType); alerting.registerType(exampleAlwaysFiringAlertType); + alerting.registerType(getLongRunningPatternRuleType()); + alerting.registerType(getLongRunningPatternRuleType(false)); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index.ts index 965cf352ab7ed..14748737f0d53 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index.ts @@ -12,5 +12,6 @@ export default function alertingTests({ loadTestFile }: FtrProviderContext) { describe('builtin alertTypes', () => { loadTestFile(require.resolve('./index_threshold')); loadTestFile(require.resolve('./es_query')); + loadTestFile(require.resolve('./long_running')); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/long_running/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/long_running/index.ts new file mode 100644 index 0000000000000..d997cebc6fd8d --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/long_running/index.ts @@ -0,0 +1,15 @@ +/* + * 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 { FtrProviderContext } from '../../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function alertingTests({ loadTestFile }: FtrProviderContext) { + describe('long_running_rule', () => { + loadTestFile(require.resolve('./rule')); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/long_running/rule.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/long_running/rule.ts new file mode 100644 index 0000000000000..da9e7aadb571e --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/long_running/rule.ts @@ -0,0 +1,161 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { Spaces } from '../../../../scenarios'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getUrlPrefix, ObjectRemover, getEventLog } from '../../../../../common/lib'; + +const RULE_INTERVAL_SECONDS = 3; + +// eslint-disable-next-line import/no-default-export +export default function ruleTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const retry = getService('retry'); + + describe('rule', async () => { + const objectRemover = new ObjectRemover(supertest); + + afterEach(async () => { + await objectRemover.removeAll(); + }); + + it('writes event log document for timeout for each rule execution that ends in timeout - every execution times out', async () => { + const ruleId = await createRule({ + name: 'long running rule', + ruleTypeId: 'test.patternLongRunning.cancelAlertsOnRuleTimeout', + pattern: [true, true, true, true], + }); + // get the events we're expecting + const events = await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: Spaces.space1.id, + type: 'alert', + id: ruleId, + provider: 'alerting', + actions: new Map([ + // make sure the counts of the # of events per type are as expected + ['execute-start', { gte: 4 }], + ['execute', { gte: 4 }], + ['execute-timeout', { gte: 4 }], + ]), + }); + }); + + // no active|recovered|new instance events should exist + expect(events.filter((event) => event?.event?.action === 'active-instance').length).to.equal( + 0 + ); + expect(events.filter((event) => event?.event?.action === 'new-instance').length).to.equal(0); + expect( + events.filter((event) => event?.event?.action === 'recovered-instance').length + ).to.equal(0); + + // rule execution status should be in error with reason timeout + const { status, body: rule } = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${ruleId}` + ); + expect(status).to.eql(200); + expect(rule.execution_status.status).to.eql('error'); + expect(rule.execution_status.error.message).to.eql( + `test.patternLongRunning.cancelAlertsOnRuleTimeout:${ruleId}: execution cancelled due to timeout - exceeded rule type timeout of 3s` + ); + expect(rule.execution_status.error.reason).to.eql('timeout'); + }); + + it('writes event log document for timeout for each rule execution that ends in timeout - some executions times out', async () => { + const ruleId = await createRule({ + name: 'long running rule', + ruleTypeId: 'test.patternLongRunning.cancelAlertsOnRuleTimeout', + pattern: [false, true, false, false], + }); + // get the events we're expecting + await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: Spaces.space1.id, + type: 'alert', + id: ruleId, + provider: 'alerting', + actions: new Map([ + // make sure the counts of the # of events per type are as expected + ['execute-start', { gte: 4 }], + ['execute', { gte: 4 }], + ['execute-timeout', { gte: 1 }], + ['new-instance', { equal: 1 }], + ['active-instance', { gte: 2 }], + ]), + }); + }); + + const { status, body: rule } = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${ruleId}` + ); + expect(status).to.eql(200); + expect(rule.execution_status.status).to.eql('active'); + }); + + it('still logs alert docs when rule exceeds timeout when cancelAlertsOnRuleTimeout is false on rule type', async () => { + const ruleId = await createRule({ + name: 'long running rule', + ruleTypeId: 'test.patternLongRunning', + pattern: [true, true, true, true], + }); + // get the events we're expecting + await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: Spaces.space1.id, + type: 'alert', + id: ruleId, + provider: 'alerting', + actions: new Map([ + // make sure the counts of the # of events per type are as expected + ['execute-start', { gte: 4 }], + ['execute', { gte: 4 }], + ['execute-timeout', { gte: 4 }], + ['new-instance', { gte: 1 }], + ['active-instance', { gte: 3 }], + ]), + }); + }); + }); + + interface CreateRuleParams { + name: string; + ruleTypeId: string; + pattern: boolean[]; + } + + async function createRule(params: CreateRuleParams): Promise { + const { status, body: createdRule } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send({ + name: params.name, + consumer: 'alerts', + enabled: true, + rule_type_id: params.ruleTypeId, + schedule: { interval: `${RULE_INTERVAL_SECONDS}s` }, + actions: [], + notify_when: 'onActiveAlert', + params: { + pattern: params.pattern, + }, + }); + + expect(status).to.be(200); + + const ruleId = createdRule.id; + objectRemover.add(Spaces.space1.id, ruleId, 'rule', 'alerting'); + + return ruleId; + } + }); +} diff --git a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/index_detail.json b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/index_detail.json index 65094144d6ff0..31c18ec43f414 100644 --- a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/index_detail.json +++ b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/index_detail.json @@ -501,299 +501,6 @@ ] } ], - "index_mem": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "index_stats.total.segments.memory_in_bytes", - "metricAgg": "max", - "label": "Lucene Total", - "title": "Index Memory", - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1507235520000, - 2378656 - ], - [ - 1507235530000, - 2378656 - ], - [ - 1507235540000, - 2378656 - ], - [ - 1507235550000, - 2378656 - ], - [ - 1507235560000, - 2378656 - ], - [ - 1507235570000, - 2378656 - ], - [ - 1507235580000, - 2378656 - ], - [ - 1507235590000, - 2378656 - ], - [ - 1507235600000, - 2378656 - ], - [ - 1507235610000, - 2378656 - ], - [ - 1507235620000, - 2378656 - ], - [ - 1507235630000, - 2378656 - ], - [ - 1507235640000, - 2378656 - ], - [ - 1507235650000, - 2378656 - ], - [ - 1507235660000, - 2378656 - ], - [ - 1507235670000, - 2378656 - ], - [ - 1507235680000, - 2378656 - ], - [ - 1507235690000, - 2378656 - ], - [ - 1507235700000, - 2378656 - ] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "index_stats.total.segments.terms_memory_in_bytes", - "metricAgg": "max", - "label": "Terms", - "title": "Index Memory", - "description": "Heap memory used by Terms (e.g., text). This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1507235520000, - 1889180 - ], - [ - 1507235530000, - 1889180 - ], - [ - 1507235540000, - 1889180 - ], - [ - 1507235550000, - 1889180 - ], - [ - 1507235560000, - 1889180 - ], - [ - 1507235570000, - 1889180 - ], - [ - 1507235580000, - 1889180 - ], - [ - 1507235590000, - 1889180 - ], - [ - 1507235600000, - 1889180 - ], - [ - 1507235610000, - 1889180 - ], - [ - 1507235620000, - 1889180 - ], - [ - 1507235630000, - 1889180 - ], - [ - 1507235640000, - 1889180 - ], - [ - 1507235650000, - 1889180 - ], - [ - 1507235660000, - 1889180 - ], - [ - 1507235670000, - 1889180 - ], - [ - 1507235680000, - 1889180 - ], - [ - 1507235690000, - 1889180 - ], - [ - 1507235700000, - 1889180 - ] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "index_stats.total.segments.points_memory_in_bytes", - "metricAgg": "max", - "label": "Points", - "title": "Index Memory", - "description": "Heap memory used by Points (e.g., numbers, IPs, and geo data). This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1507235520000, - 4644 - ], - [ - 1507235530000, - 4644 - ], - [ - 1507235540000, - 4644 - ], - [ - 1507235550000, - 4644 - ], - [ - 1507235560000, - 4644 - ], - [ - 1507235570000, - 4644 - ], - [ - 1507235580000, - 4644 - ], - [ - 1507235590000, - 4644 - ], - [ - 1507235600000, - 4644 - ], - [ - 1507235610000, - 4644 - ], - [ - 1507235620000, - 4644 - ], - [ - 1507235630000, - 4644 - ], - [ - 1507235640000, - 4644 - ], - [ - 1507235650000, - 4644 - ], - [ - 1507235660000, - 4644 - ], - [ - 1507235670000, - 4644 - ], - [ - 1507235680000, - 4644 - ], - [ - 1507235690000, - 4644 - ], - [ - 1507235700000, - 4644 - ] - ] - } - ], "index_document_count": [ { "bucket_size": "10 seconds", diff --git a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/index_detail_advanced.json b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/index_detail_advanced.json index 01985489bf0d2..e58b82de53a81 100644 --- a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/index_detail_advanced.json +++ b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/index_detail_advanced.json @@ -10,321 +10,7 @@ "status": "green" }, "metrics": { - "index_1": [{ - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "index_stats.total.segments.memory_in_bytes", - "metricAgg": "max", - "label": "Lucene Total", - "title": "Index Memory - Lucene 1", - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 2378656], - [1507235530000, 2378656], - [1507235540000, 2378656], - [1507235550000, 2378656], - [1507235560000, 2378656], - [1507235570000, 2378656], - [1507235580000, 2378656], - [1507235590000, 2378656], - [1507235600000, 2378656], - [1507235610000, 2378656], - [1507235620000, 2378656], - [1507235630000, 2378656], - [1507235640000, 2378656], - [1507235650000, 2378656], - [1507235660000, 2378656], - [1507235670000, 2378656], - [1507235680000, 2378656], - [1507235690000, 2378656], - [1507235700000, 2378656] - ] - }, { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "index_stats.total.segments.stored_fields_memory_in_bytes", - "metricAgg": "max", - "label": "Stored Fields", - "title": "Index Memory", - "description": "Heap memory used by Stored Fields (e.g., _source). This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 19024], - [1507235530000, 19024], - [1507235540000, 19024], - [1507235550000, 19024], - [1507235560000, 19024], - [1507235570000, 19024], - [1507235580000, 19024], - [1507235590000, 19024], - [1507235600000, 19024], - [1507235610000, 19024], - [1507235620000, 19024], - [1507235630000, 19024], - [1507235640000, 19024], - [1507235650000, 19024], - [1507235660000, 19024], - [1507235670000, 19024], - [1507235680000, 19024], - [1507235690000, 19024], - [1507235700000, 19024] - ] - }, { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "index_stats.total.segments.doc_values_memory_in_bytes", - "metricAgg": "max", - "label": "Doc Values", - "title": "Index Memory", - "description": "Heap memory used by Doc Values. This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 232336], - [1507235530000, 232336], - [1507235540000, 232336], - [1507235550000, 232336], - [1507235560000, 232336], - [1507235570000, 232336], - [1507235580000, 232336], - [1507235590000, 232336], - [1507235600000, 232336], - [1507235610000, 232336], - [1507235620000, 232336], - [1507235630000, 232336], - [1507235640000, 232336], - [1507235650000, 232336], - [1507235660000, 232336], - [1507235670000, 232336], - [1507235680000, 232336], - [1507235690000, 232336], - [1507235700000, 232336] - ] - }, { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "index_stats.total.segments.norms_memory_in_bytes", - "metricAgg": "max", - "label": "Norms", - "title": "Index Memory", - "description": "Heap memory used by Norms (normalization factors for query-time, text scoring). This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 233472], - [1507235530000, 233472], - [1507235540000, 233472], - [1507235550000, 233472], - [1507235560000, 233472], - [1507235570000, 233472], - [1507235580000, 233472], - [1507235590000, 233472], - [1507235600000, 233472], - [1507235610000, 233472], - [1507235620000, 233472], - [1507235630000, 233472], - [1507235640000, 233472], - [1507235650000, 233472], - [1507235660000, 233472], - [1507235670000, 233472], - [1507235680000, 233472], - [1507235690000, 233472], - [1507235700000, 233472] - ] - }], - "index_2": [{ - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "index_stats.total.segments.memory_in_bytes", - "metricAgg": "max", - "label": "Lucene Total", - "title": "Index Memory - Lucene 2", - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 2378656], - [1507235530000, 2378656], - [1507235540000, 2378656], - [1507235550000, 2378656], - [1507235560000, 2378656], - [1507235570000, 2378656], - [1507235580000, 2378656], - [1507235590000, 2378656], - [1507235600000, 2378656], - [1507235610000, 2378656], - [1507235620000, 2378656], - [1507235630000, 2378656], - [1507235640000, 2378656], - [1507235650000, 2378656], - [1507235660000, 2378656], - [1507235670000, 2378656], - [1507235680000, 2378656], - [1507235690000, 2378656], - [1507235700000, 2378656] - ] - }, { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "index_stats.total.segments.terms_memory_in_bytes", - "metricAgg": "max", - "label": "Terms", - "title": "Index Memory", - "description": "Heap memory used by Terms (e.g., text). This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 1889180], - [1507235530000, 1889180], - [1507235540000, 1889180], - [1507235550000, 1889180], - [1507235560000, 1889180], - [1507235570000, 1889180], - [1507235580000, 1889180], - [1507235590000, 1889180], - [1507235600000, 1889180], - [1507235610000, 1889180], - [1507235620000, 1889180], - [1507235630000, 1889180], - [1507235640000, 1889180], - [1507235650000, 1889180], - [1507235660000, 1889180], - [1507235670000, 1889180], - [1507235680000, 1889180], - [1507235690000, 1889180], - [1507235700000, 1889180] - ] - }, { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "index_stats.total.segments.points_memory_in_bytes", - "metricAgg": "max", - "label": "Points", - "title": "Index Memory", - "description": "Heap memory used by Points (e.g., numbers, IPs, and geo data). This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 4644], - [1507235530000, 4644], - [1507235540000, 4644], - [1507235550000, 4644], - [1507235560000, 4644], - [1507235570000, 4644], - [1507235580000, 4644], - [1507235590000, 4644], - [1507235600000, 4644], - [1507235610000, 4644], - [1507235620000, 4644], - [1507235630000, 4644], - [1507235640000, 4644], - [1507235650000, 4644], - [1507235660000, 4644], - [1507235670000, 4644], - [1507235680000, 4644], - [1507235690000, 4644], - [1507235700000, 4644] - ] - }], "index_3": [{ - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "index_stats.total.segments.memory_in_bytes", - "metricAgg": "max", - "label": "Lucene Total", - "title": "Index Memory - Lucene 3", - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 2378656], - [1507235530000, 2378656], - [1507235540000, 2378656], - [1507235550000, 2378656], - [1507235560000, 2378656], - [1507235570000, 2378656], - [1507235580000, 2378656], - [1507235590000, 2378656], - [1507235600000, 2378656], - [1507235610000, 2378656], - [1507235620000, 2378656], - [1507235630000, 2378656], - [1507235640000, 2378656], - [1507235650000, 2378656], - [1507235660000, 2378656], - [1507235670000, 2378656], - [1507235680000, 2378656], - [1507235690000, 2378656], - [1507235700000, 2378656] - ] - }, { "bucket_size": "10 seconds", "timeRange": { "min": 1507235508000, @@ -335,7 +21,7 @@ "field": "index_stats.total.segments.fixed_bit_set_memory_in_bytes", "metricAgg": "max", "label": "Fixed Bitsets", - "title": "Index Memory", + "title": "Index Memory - Lucene", "description": "Heap memory used by Fixed Bit Sets (e.g., deeply nested documents). This is a part of Lucene Total.", "units": "B", "format": "0.0 b", @@ -363,45 +49,6 @@ [1507235690000, 0], [1507235700000, 0] ] - }, { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "index_stats.total.segments.term_vectors_memory_in_bytes", - "metricAgg": "max", - "label": "Term Vectors", - "title": "Index Memory", - "description": "Heap memory used by Term Vectors. This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 0], - [1507235530000, 0], - [1507235540000, 0], - [1507235550000, 0], - [1507235560000, 0], - [1507235570000, 0], - [1507235580000, 0], - [1507235590000, 0], - [1507235600000, 0], - [1507235610000, 0], - [1507235620000, 0], - [1507235630000, 0], - [1507235640000, 0], - [1507235650000, 0], - [1507235660000, 0], - [1507235670000, 0], - [1507235680000, 0], - [1507235690000, 0], - [1507235700000, 0] - ] }, { "bucket_size": "10 seconds", "timeRange": { diff --git a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/node_detail.json b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/node_detail.json index 32096b0b97067..0a7e3ee12a18f 100644 --- a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/node_detail.json +++ b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/node_detail.json @@ -709,299 +709,6 @@ ] } ], - "node_mem": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "node_stats.indices.segments.memory_in_bytes", - "metricAgg": "max", - "label": "Lucene Total", - "title": "Index Memory", - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards on this node.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1507235520000, - 4797457 - ], - [ - 1507235530000, - 4797457 - ], - [ - 1507235540000, - 4797457 - ], - [ - 1507235550000, - 4797457 - ], - [ - 1507235560000, - 4823580 - ], - [ - 1507235570000, - 4823580 - ], - [ - 1507235580000, - 4823580 - ], - [ - 1507235590000, - 4823580 - ], - [ - 1507235600000, - 4823580 - ], - [ - 1507235610000, - 4838368 - ], - [ - 1507235620000, - 4741420 - ], - [ - 1507235630000, - 4741420 - ], - [ - 1507235640000, - 4741420 - ], - [ - 1507235650000, - 4741420 - ], - [ - 1507235660000, - 4741420 - ], - [ - 1507235670000, - 4757998 - ], - [ - 1507235680000, - 4787542 - ], - [ - 1507235690000, - 4787542 - ], - [ - 1507235700000, - 4787542 - ] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "node_stats.indices.segments.terms_memory_in_bytes", - "metricAgg": "max", - "label": "Terms", - "title": "Index Memory", - "description": "Heap memory used by Terms (e.g., text). This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1507235520000, - 3764438 - ], - [ - 1507235530000, - 3764438 - ], - [ - 1507235540000, - 3764438 - ], - [ - 1507235550000, - 3764438 - ], - [ - 1507235560000, - 3786762 - ], - [ - 1507235570000, - 3786762 - ], - [ - 1507235580000, - 3786762 - ], - [ - 1507235590000, - 3786762 - ], - [ - 1507235600000, - 3786762 - ], - [ - 1507235610000, - 3799306 - ], - [ - 1507235620000, - 3715996 - ], - [ - 1507235630000, - 3715996 - ], - [ - 1507235640000, - 3715996 - ], - [ - 1507235650000, - 3715996 - ], - [ - 1507235660000, - 3715996 - ], - [ - 1507235670000, - 3729890 - ], - [ - 1507235680000, - 3755528 - ], - [ - 1507235690000, - 3755528 - ], - [ - 1507235700000, - 3755528 - ] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "node_stats.indices.segments.points_memory_in_bytes", - "metricAgg": "max", - "label": "Points", - "title": "Index Memory", - "description": "Heap memory used by Points (e.g., numbers, IPs, and geo data). This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1507235520000, - 12171 - ], - [ - 1507235530000, - 12171 - ], - [ - 1507235540000, - 12171 - ], - [ - 1507235550000, - 12171 - ], - [ - 1507235560000, - 12198 - ], - [ - 1507235570000, - 12198 - ], - [ - 1507235580000, - 12198 - ], - [ - 1507235590000, - 12198 - ], - [ - 1507235600000, - 12198 - ], - [ - 1507235610000, - 12218 - ], - [ - 1507235620000, - 12120 - ], - [ - 1507235630000, - 12120 - ], - [ - 1507235640000, - 12120 - ], - [ - 1507235650000, - 12120 - ], - [ - 1507235660000, - 12120 - ], - [ - 1507235670000, - 12140 - ], - [ - 1507235680000, - 12166 - ], - [ - 1507235690000, - 12166 - ], - [ - 1507235700000, - 12166 - ] - ] - } - ], "node_cpu_metric": [ { "bucket_size": "10 seconds", diff --git a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/node_detail_advanced.json b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/node_detail_advanced.json index 2eb7d54effdfb..e08fabe28d91a 100644 --- a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/node_detail_advanced.json +++ b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/node_detail_advanced.json @@ -268,331 +268,7 @@ ] } ], - "node_index_1": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "node_stats.indices.segments.memory_in_bytes", - "metricAgg": "max", - "label": "Lucene Total", - "title": "Index Memory - Lucene 1", - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards on this node.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 4797457], - [1507235530000, 4797457], - [1507235540000, 4797457], - [1507235550000, 4797457], - [1507235560000, 4823580], - [1507235570000, 4823580], - [1507235580000, 4823580], - [1507235590000, 4823580], - [1507235600000, 4823580], - [1507235610000, 4838368], - [1507235620000, 4741420], - [1507235630000, 4741420], - [1507235640000, 4741420], - [1507235650000, 4741420], - [1507235660000, 4741420], - [1507235670000, 4757998], - [1507235680000, 4787542], - [1507235690000, 4787542], - [1507235700000, 4787542] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "node_stats.indices.segments.stored_fields_memory_in_bytes", - "metricAgg": "max", - "label": "Stored Fields", - "title": "Index Memory", - "description": "Heap memory used by Stored Fields (e.g., _source). This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 56792], - [1507235530000, 56792], - [1507235540000, 56792], - [1507235550000, 56792], - [1507235560000, 57728], - [1507235570000, 57728], - [1507235580000, 57728], - [1507235590000, 57728], - [1507235600000, 57728], - [1507235610000, 58352], - [1507235620000, 56192], - [1507235630000, 56192], - [1507235640000, 56192], - [1507235650000, 56192], - [1507235660000, 56192], - [1507235670000, 56816], - [1507235680000, 57440], - [1507235690000, 57440], - [1507235700000, 57440] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "node_stats.indices.segments.doc_values_memory_in_bytes", - "metricAgg": "max", - "label": "Doc Values", - "title": "Index Memory", - "description": "Heap memory used by Doc Values. This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 516824], - [1507235530000, 516824], - [1507235540000, 516824], - [1507235550000, 516824], - [1507235560000, 517292], - [1507235570000, 517292], - [1507235580000, 517292], - [1507235590000, 517292], - [1507235600000, 517292], - [1507235610000, 517612], - [1507235620000, 514808], - [1507235630000, 514808], - [1507235640000, 514808], - [1507235650000, 514808], - [1507235660000, 514808], - [1507235670000, 515312], - [1507235680000, 516008], - [1507235690000, 516008], - [1507235700000, 516008] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "node_stats.indices.segments.norms_memory_in_bytes", - "metricAgg": "max", - "label": "Norms", - "title": "Index Memory", - "description": "Heap memory used by Norms (normalization factors for query-time, text scoring). This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 447232], - [1507235530000, 447232], - [1507235540000, 447232], - [1507235550000, 447232], - [1507235560000, 449600], - [1507235570000, 449600], - [1507235580000, 449600], - [1507235590000, 449600], - [1507235600000, 449600], - [1507235610000, 450880], - [1507235620000, 442304], - [1507235630000, 442304], - [1507235640000, 442304], - [1507235650000, 442304], - [1507235660000, 442304], - [1507235670000, 443840], - [1507235680000, 446400], - [1507235690000, 446400], - [1507235700000, 446400] - ] - } - ], - "node_index_2": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "node_stats.indices.segments.memory_in_bytes", - "metricAgg": "max", - "label": "Lucene Total", - "title": "Index Memory - Lucene 2", - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards on this node.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 4797457], - [1507235530000, 4797457], - [1507235540000, 4797457], - [1507235550000, 4797457], - [1507235560000, 4823580], - [1507235570000, 4823580], - [1507235580000, 4823580], - [1507235590000, 4823580], - [1507235600000, 4823580], - [1507235610000, 4838368], - [1507235620000, 4741420], - [1507235630000, 4741420], - [1507235640000, 4741420], - [1507235650000, 4741420], - [1507235660000, 4741420], - [1507235670000, 4757998], - [1507235680000, 4787542], - [1507235690000, 4787542], - [1507235700000, 4787542] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "node_stats.indices.segments.terms_memory_in_bytes", - "metricAgg": "max", - "label": "Terms", - "title": "Index Memory", - "description": "Heap memory used by Terms (e.g., text). This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 3764438], - [1507235530000, 3764438], - [1507235540000, 3764438], - [1507235550000, 3764438], - [1507235560000, 3786762], - [1507235570000, 3786762], - [1507235580000, 3786762], - [1507235590000, 3786762], - [1507235600000, 3786762], - [1507235610000, 3799306], - [1507235620000, 3715996], - [1507235630000, 3715996], - [1507235640000, 3715996], - [1507235650000, 3715996], - [1507235660000, 3715996], - [1507235670000, 3729890], - [1507235680000, 3755528], - [1507235690000, 3755528], - [1507235700000, 3755528] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "node_stats.indices.segments.points_memory_in_bytes", - "metricAgg": "max", - "label": "Points", - "title": "Index Memory", - "description": "Heap memory used by Points (e.g., numbers, IPs, and geo data). This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 12171], - [1507235530000, 12171], - [1507235540000, 12171], - [1507235550000, 12171], - [1507235560000, 12198], - [1507235570000, 12198], - [1507235580000, 12198], - [1507235590000, 12198], - [1507235600000, 12198], - [1507235610000, 12218], - [1507235620000, 12120], - [1507235630000, 12120], - [1507235640000, 12120], - [1507235650000, 12120], - [1507235660000, 12120], - [1507235670000, 12140], - [1507235680000, 12166], - [1507235690000, 12166], - [1507235700000, 12166] - ] - } - ], "node_index_3": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "node_stats.indices.segments.memory_in_bytes", - "metricAgg": "max", - "label": "Lucene Total", - "title": "Index Memory - Lucene 3", - "description": "Total heap memory used by Lucene for current index. This is the sum of other fields for primary and replica shards on this node.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 4797457], - [1507235530000, 4797457], - [1507235540000, 4797457], - [1507235550000, 4797457], - [1507235560000, 4823580], - [1507235570000, 4823580], - [1507235580000, 4823580], - [1507235590000, 4823580], - [1507235600000, 4823580], - [1507235610000, 4838368], - [1507235620000, 4741420], - [1507235630000, 4741420], - [1507235640000, 4741420], - [1507235650000, 4741420], - [1507235660000, 4741420], - [1507235670000, 4757998], - [1507235680000, 4787542], - [1507235690000, 4787542], - [1507235700000, 4787542] - ] - }, { "bucket_size": "10 seconds", "timeRange": { @@ -604,7 +280,7 @@ "field": "node_stats.indices.segments.fixed_bit_set_memory_in_bytes", "metricAgg": "max", "label": "Fixed Bitsets", - "title": "Index Memory", + "title": "Index Memory - Lucene", "description": "Heap memory used by Fixed Bit Sets (e.g., deeply nested documents). This is a part of Lucene Total.", "units": "B", "format": "0.0 b", @@ -633,46 +309,6 @@ [1507235700000, 3976] ] }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1507235508000, - "max": 1507235712000 - }, - "metric": { - "app": "elasticsearch", - "field": "node_stats.indices.segments.term_vectors_memory_in_bytes", - "metricAgg": "max", - "label": "Term Vectors", - "title": "Index Memory", - "description": "Heap memory used by Term Vectors. This is a part of Lucene Total.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [1507235520000, 0], - [1507235530000, 0], - [1507235540000, 0], - [1507235550000, 0], - [1507235560000, 0], - [1507235570000, 0], - [1507235580000, 0], - [1507235590000, 0], - [1507235600000, 0], - [1507235610000, 0], - [1507235620000, 0], - [1507235630000, 0], - [1507235640000, 0], - [1507235650000, 0], - [1507235660000, 0], - [1507235670000, 0], - [1507235680000, 0], - [1507235690000, 0], - [1507235700000, 0] - ] - }, { "bucket_size": "10 seconds", "timeRange": { diff --git a/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.spec.ts b/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.spec.ts index 74af2c2dba008..d70ee347d4c2d 100644 --- a/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.spec.ts +++ b/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.spec.ts @@ -7,35 +7,39 @@ import expect from '@kbn/expect'; import { first } from 'lodash'; -import { MetricsChartsByAgentAPIResponse } from '../../../../plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent'; -import { GenericMetricsChart } from '../../../../plugins/apm/server/lib/metrics/transform_metrics_chart'; +import { GenericMetricsChart } from '../../../../plugins/apm/server/lib/metrics/fetch_and_transform_metrics'; +import { SupertestReturnType } from '../../common/apm_api_supertest'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -interface ChartResponse { - body: MetricsChartsByAgentAPIResponse; - status: number; -} +type ChartResponse = SupertestReturnType<'GET /internal/apm/services/{serviceName}/metrics/charts'>; export default function ApiTest({ getService }: FtrProviderContext) { const registry = getService('registry'); - const supertest = getService('legacySupertestAsApmReadUser'); + const apmApiClient = getService('apmApiClient'); registry.when( 'Metrics charts when data is loaded', { config: 'basic', archives: ['metrics_8.0.0'] }, () => { describe('for opbeans-node', () => { - const start = encodeURIComponent('2020-09-08T14:50:00.000Z'); - const end = encodeURIComponent('2020-09-08T14:55:00.000Z'); - const agentName = 'nodejs'; - describe('returns metrics data', () => { let chartsResponse: ChartResponse; before(async () => { - chartsResponse = await supertest.get( - `/internal/apm/services/opbeans-node/metrics/charts?start=${start}&end=${end}&agentName=${agentName}&kuery=&environment=ENVIRONMENT_ALL` - ); + chartsResponse = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services/{serviceName}/metrics/charts', + params: { + path: { serviceName: 'opbeans-node' }, + query: { + start: '2020-09-08T14:50:00.000Z', + end: '2020-09-08T14:55:00.000Z', + agentName: 'nodejs', + environment: 'ENVIRONMENT_ALL', + kuery: ``, + }, + }, + }); }); + it('contains CPU usage and System memory usage chart data', async () => { expect(chartsResponse.status).to.be(200); expectSnapshot(chartsResponse.body.charts.map((chart) => chart.title)).toMatchInline(` @@ -112,17 +116,22 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); describe('for opbeans-java', () => { - const agentName = 'java'; - describe('returns metrics data', () => { - const start = encodeURIComponent('2020-09-08T14:55:30.000Z'); - const end = encodeURIComponent('2020-09-08T15:00:00.000Z'); - let chartsResponse: ChartResponse; before(async () => { - chartsResponse = await supertest.get( - `/internal/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&agentName=${agentName}&environment=ENVIRONMENT_ALL&kuery=` - ); + chartsResponse = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services/{serviceName}/metrics/charts', + params: { + path: { serviceName: 'opbeans-java' }, + query: { + start: '2020-09-08T14:55:30.000Z', + end: '2020-09-08T15:00:00.000Z', + agentName: 'java', + environment: 'ENVIRONMENT_ALL', + kuery: ``, + }, + }, + }); }); it('has correct chart data', async () => { @@ -406,12 +415,19 @@ export default function ApiTest({ getService }: FtrProviderContext) { // 9223372036854771712 = memory limit for a c-group when no memory limit is specified it('calculates system memory usage using system total field when cgroup limit is equal to 9223372036854771712', async () => { - const start = encodeURIComponent('2020-09-08T15:00:30.000Z'); - const end = encodeURIComponent('2020-09-08T15:05:00.000Z'); - - const chartsResponse: ChartResponse = await supertest.get( - `/internal/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&agentName=${agentName}&environment=ENVIRONMENT_ALL&kuery=` - ); + const chartsResponse = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services/{serviceName}/metrics/charts', + params: { + path: { serviceName: 'opbeans-java' }, + query: { + start: '2020-09-08T15:00:30.000Z', + end: '2020-09-08T15:05:00.000Z', + agentName: 'java', + environment: 'ENVIRONMENT_ALL', + kuery: ``, + }, + }, + }); const systemMemoryUsageChart = chartsResponse.body.charts.find( ({ key }) => key === 'memory_usage_chart' diff --git a/x-pack/test/functional/apps/discover/value_suggestions.ts b/x-pack/test/functional/apps/discover/value_suggestions.ts index 6d0047a27c7e3..9a56ee944debb 100644 --- a/x-pack/test/functional/apps/discover/value_suggestions.ts +++ b/x-pack/test/functional/apps/discover/value_suggestions.ts @@ -90,7 +90,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - describe('useTimeRange disabled', () => { + // FLAKY: https://github.com/elastic/kibana/issues/116892 + describe.skip('useTimeRange disabled', () => { before(async () => { await setAutocompleteUseTimeRange(false); }); diff --git a/x-pack/test/functional/apps/infra/home_page.ts b/x-pack/test/functional/apps/infra/home_page.ts index ff27b5fe4e370..5046fa3ba90af 100644 --- a/x-pack/test/functional/apps/infra/home_page.ts +++ b/x-pack/test/functional/apps/infra/home_page.ts @@ -105,7 +105,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.infraHome.clearSearchTerm(); }); - it('change color palette', async () => { + // Tracked in https://github.com/elastic/kibana/issues/118481 + it.skip('change color palette', async () => { await pageObjects.infraHome.openLegendControls(); await pageObjects.infraHome.changePalette('temperature'); await pageObjects.infraHome.applyLegendControls(); diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts index bcdf978d46cad..55a11e6ec2d20 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts @@ -166,7 +166,8 @@ export default function ({ getService }: FtrProviderContext) { await ml.securityUI.loginAsMlPowerUser(); }); - describe('with farequote', function () { + // FLAKY: https://github.com/elastic/kibana/issues/118472 + describe.skip('with farequote', function () { // Run tests on full farequote index. it(`${farequoteDataViewTestData.suiteTitle} loads the data visualizer selector page`, async () => { // Start navigation from the base of the ML app. diff --git a/x-pack/test/functional/apps/spaces/spaces_selection.ts b/x-pack/test/functional/apps/spaces/spaces_selection.ts index 4344f8ff61733..4a61421775163 100644 --- a/x-pack/test/functional/apps/spaces/spaces_selection.ts +++ b/x-pack/test/functional/apps/spaces/spaces_selection.ts @@ -31,7 +31,8 @@ export default function spaceSelectorFunctionalTests({ ); this.tags('includeFirefox'); - describe('Space Selector', () => { + // FLAKY: https://github.com/elastic/kibana/issues/99581 + describe.skip('Space Selector', () => { before(async () => { await PageObjects.security.forceLogout(); }); @@ -92,7 +93,9 @@ export default function spaceSelectorFunctionalTests({ }); }); - describe('Search spaces in popover', () => { + // FLAKY: https://github.com/elastic/kibana/issues/118356 + // FLAKY: https://github.com/elastic/kibana/issues/118474 + describe.skip('Search spaces in popover', () => { const spaceId = 'default'; before(async () => { await PageObjects.security.forceLogout(); diff --git a/yarn.lock b/yarn.lock index 7a66ff2bf4127..9972f5a2e1dcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6101,6 +6101,10 @@ resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.0.6.tgz#aca442289df623bfa8e47c23961f0357847b83fe" integrity sha512-fj1hi+ZSW0xPLrJJD+YNwIh9GZbyaIepG26E/gXvp8nCa2pYokxUYO1sK9qjGxp2g8ryZYuon7wmjpwE2cyASQ== +"@types/elastic__datemath@link:bazel-bin/packages/elastic-datemath/npm_module_types": + version "0.0.0" + uid "" + "@types/elasticsearch@^5.0.33": version "5.0.33" resolved "https://registry.yarnpkg.com/@types/elasticsearch/-/elasticsearch-5.0.33.tgz#b0fd37dc674f498223b6d68c313bdfd71f4d812b" @@ -10418,10 +10422,10 @@ chrome-trace-event@^1.0.2: dependencies: tslib "^1.9.0" -chromedriver@^94.0.0: - version "94.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-94.0.0.tgz#f6a3533976ba72413a01672954040c3544ea9d30" - integrity sha512-x4hK7R7iOyAhdLHJEcOyGBW/oa2kno6AqpHVLd+n3G7c2Vk9XcAXMz84XhNItqykJvTc6E3z/JRIT1eHYH//Eg== +chromedriver@^95.0.0: + version "95.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-95.0.0.tgz#ecf854cac6df5137a651dcc132edf55612d3db7f" + integrity sha512-HwSg7S0ZZYsHTjULwxFHrrUqEpz1+ljDudJM3eOquvqD5QKnR5pSe/GlBTY9UU2tVFRYz8bEHYC4Y8qxciQiLQ== dependencies: "@testim/chrome-version" "^1.0.7" axios "^0.21.2"