From ffe1cdddc76b7bc0a2b9410fc9a0a501e545e6f9 Mon Sep 17 00:00:00 2001 From: Vitalii Dmyterko <92328789+vitaliidm@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:27:09 +0100 Subject: [PATCH 01/78] [Security Solution] Removes internal tags (#130664) Addresses: - https://github.com/elastic/kibana/issues/124899 ## Summary - removes usage of internal tags (tags start with `__internal`) in codebase - migrates rules (removes existing internal tags from rules) ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../server/saved_objects/migrations.test.ts | 35 +++++ .../server/saved_objects/migrations.ts | 33 +++++ .../fixtures/saved_objects/rule.ndjson | 5 +- .../security_solution/common/constants.ts | 9 -- .../detection_engine/alerts/mock.ts | 28 +--- .../detection_engine/rules/api.test.ts | 6 +- .../rules/use_rule_with_fallback.test.tsx | 2 - .../detection_engine/rules/utils.test.ts | 17 ++- .../detection_engine/rules/utils.ts | 13 +- .../notifications/legacy_add_tags.test.ts | 28 ---- .../notifications/legacy_add_tags.ts | 14 -- .../legacy_create_notifications.ts | 4 +- .../legacy_read_notifications.ts | 3 +- .../routes/__mocks__/request_responses.ts | 6 +- .../get_prepackaged_rules_status_route.ts | 2 +- .../legacy_create_legacy_notification.ts | 4 +- .../routes/rules/utils.test.ts | 23 +-- .../detection_engine/routes/rules/utils.ts | 5 - .../rule_types/factories/utils/build_alert.ts | 3 +- .../detection_engine/rules/add_tags.test.ts | 49 ------- .../lib/detection_engine/rules/add_tags.ts | 20 --- .../detection_engine/rules/create_rules.ts | 3 +- .../rules/duplicate_rule.test.ts | 5 +- .../detection_engine/rules/duplicate_rule.ts | 3 +- .../lib/detection_engine/rules/edit_rule.ts | 6 +- .../rules/get_existing_prepackaged_rules.ts | 5 +- .../rules/get_export_by_object_ids.ts | 7 +- .../lib/detection_engine/rules/patch_rules.ts | 3 +- .../lib/detection_engine/rules/read_rules.ts | 5 +- .../detection_engine/rules/update_rules.ts | 3 +- .../schemas/rule_converters.ts | 6 +- .../scripts/find_rule_by_filter.sh | 6 +- .../signals/saved_object_references/README.md | 5 +- .../detection_engine/tags/read_tags.test.ts | 136 +----------------- .../lib/detection_engine/tags/read_tags.ts | 16 +-- .../usage/detections/get_metrics.test.ts | 2 +- .../detections/ml_jobs/get_metrics.mocks.ts | 15 +- .../get_rule_object_correlations.ts | 6 +- .../usage/queries/utils/is_elastic_rule.ts | 11 -- .../spaces_only/tests/alerting/migrations.ts | 20 ++- .../tests/resolve_read_rules.ts | 5 +- .../utils/downgrade_immutable_rule.ts | 3 +- .../utils/find_immutable_rule_by_id.ts | 8 +- .../functional/es_archives/alerts/data.json | 110 ++++++++++++-- 44 files changed, 252 insertions(+), 446 deletions(-) delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_add_tags.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_add_tags.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/add_tags.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/add_tags.ts delete mode 100644 x-pack/plugins/security_solution/server/usage/queries/utils/is_elastic_rule.ts diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts b/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts index 6b736006fc074..921412d4e79e8 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts @@ -2253,6 +2253,41 @@ describe('successful migrations', () => { }); }); + describe('8.3.0', () => { + test('removes internal tags', () => { + const migration830 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.3.0']; + const alert = getMockData( + { + tags: [ + '__internal_immutable:false', + '__internal_rule_id:064e3fed-6328-416b-bb85-c08265088f41', + 'test-tag', + ], + alertTypeId: 'siem.queryRule', + }, + true + ); + + const migratedAlert830 = migration830(alert, migrationContext); + + expect(migratedAlert830.attributes.tags).toEqual(['test-tag']); + }); + + test('do not remove internal tags if rule is not Security solution rule', () => { + const migration830 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.3.0']; + const alert = getMockData( + { + tags: ['__internal_immutable:false', 'tag-1'], + }, + true + ); + + const migratedAlert830 = migration830(alert, migrationContext); + + expect(migratedAlert830.attributes.tags).toEqual(['__internal_immutable:false', 'tag-1']); + }); + }); + describe('Metrics Inventory Threshold rule', () => { test('Migrates incorrect action group spelling', () => { const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0']; diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations.ts b/x-pack/plugins/alerting/server/saved_objects/migrations.ts index 18de27eb6919e..69d88e196dcfd 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations.ts @@ -152,6 +152,12 @@ export function getMigrations( pipeMigrations(addMappedParams) ); + const migrationRules830 = createEsoMigration( + encryptedSavedObjects, + (doc: SavedObjectUnsanitizedDoc): doc is SavedObjectUnsanitizedDoc => true, + pipeMigrations(removeInternalTags) + ); + return { '7.10.0': executeMigrationWithErrorHandling(migrationWhenRBACWasIntroduced, '7.10.0'), '7.11.0': executeMigrationWithErrorHandling(migrationAlertUpdatedAtAndNotifyWhen, '7.11.0'), @@ -163,6 +169,7 @@ export function getMigrations( '8.0.0': executeMigrationWithErrorHandling(migrationRules800, '8.0.0'), '8.0.1': executeMigrationWithErrorHandling(migrationRules801, '8.0.1'), '8.2.0': executeMigrationWithErrorHandling(migrationRules820, '8.2.0'), + '8.3.0': executeMigrationWithErrorHandling(migrationRules830, '8.3.0'), }; } @@ -864,6 +871,32 @@ function getCorrespondingAction( ) as RawRuleAction; } } +/** + * removes internal tags(starts with '__internal') from Security Solution rules + * @param doc rule to be migrated + * @returns migrated rule if it's Security Solution rule or unchanged if not + */ +function removeInternalTags( + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc { + if (!isDetectionEngineAADRuleType(doc)) { + return doc; + } + + const { + attributes: { tags }, + } = doc; + + const filteredTags = (tags ?? []).filter((tag) => !tag.startsWith('__internal_')); + + return { + ...doc, + attributes: { + ...doc.attributes, + tags: filteredTags, + }, + }; +} function pipeMigrations(...migrations: AlertMigration[]): AlertMigration { return (doc: SavedObjectUnsanitizedDoc) => diff --git a/x-pack/plugins/osquery/cypress/fixtures/saved_objects/rule.ndjson b/x-pack/plugins/osquery/cypress/fixtures/saved_objects/rule.ndjson index 75bdecb5be428..f688dc0731c7f 100644 --- a/x-pack/plugins/osquery/cypress/fixtures/saved_objects/rule.ndjson +++ b/x-pack/plugins/osquery/cypress/fixtures/saved_objects/rule.ndjson @@ -8,10 +8,7 @@ "version": "WzE5MjksMV0=", "attributes": { "name": "Test-rule", - "tags": [ - "__internal_rule_id:22308402-5e0e-421b-8d22-a47ddc4b0188", - "__internal_immutable:false" - ], + "tags": [], "alertTypeId": "siem.queryRule", "consumer": "siem", "params": { diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 74f9bff078b89..d71c001f64dd1 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -206,15 +206,6 @@ export const IP_REPUTATION_LINKS_SETTING_DEFAULT = `[ */ export const LEGACY_NOTIFICATIONS_ID = `siem.notifications` as const; -/** - * Special internal structure for tags for signals. This is used - * to filter out tags that have internal structures within them. - */ -export const INTERNAL_IDENTIFIER = '__internal' as const; -export const INTERNAL_RULE_ID_KEY = `${INTERNAL_IDENTIFIER}_rule_id` as const; -export const INTERNAL_RULE_ALERT_ID_KEY = `${INTERNAL_IDENTIFIER}_rule_alert_id` as const; -export const INTERNAL_IMMUTABLE_KEY = `${INTERNAL_IDENTIFIER}_immutable` as const; - /** * Internal actions route */ diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/mock.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/mock.ts index 58ec564c6911b..f35c4158fa236 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/mock.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/mock.ts @@ -188,12 +188,7 @@ export const alertsMock: AlertSearchResponse = { query: 'host.name : *', references: ['https://google.com'], severity: 'high', - tags: [ - 'host.name exists', - 'for testing', - '__internal_rule_id:82b2b065-a2ee-49fc-9d6d-781a75c3d280', - '__internal_immutable:false', - ], + tags: ['host.name exists', 'for testing'], type: 'query', to: 'now', enabled: true, @@ -428,12 +423,7 @@ export const alertsMock: AlertSearchResponse = { query: 'host.name : *', references: ['https://google.com'], severity: 'high', - tags: [ - 'host.name exists', - 'for testing', - '__internal_rule_id:82b2b065-a2ee-49fc-9d6d-781a75c3d280', - '__internal_immutable:false', - ], + tags: ['host.name exists', 'for testing'], type: 'query', to: 'now', enabled: true, @@ -634,12 +624,7 @@ export const alertsMock: AlertSearchResponse = { query: 'host.name : *', references: ['https://google.com'], severity: 'high', - tags: [ - 'host.name exists', - 'for testing', - '__internal_rule_id:82b2b065-a2ee-49fc-9d6d-781a75c3d280', - '__internal_immutable:false', - ], + tags: ['host.name exists', 'for testing'], type: 'query', to: 'now', enabled: true, @@ -838,12 +823,7 @@ export const alertsMock: AlertSearchResponse = { query: 'host.name : *', references: ['https://google.com'], severity: 'high', - tags: [ - 'host.name exists', - 'for testing', - '__internal_rule_id:82b2b065-a2ee-49fc-9d6d-781a75c3d280', - '__internal_immutable:false', - ], + tags: ['host.name exists', 'for testing'], type: 'query', to: 'now', enabled: true, diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts index 3c534ca7294a5..b999040674a91 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts @@ -200,7 +200,7 @@ describe('Detections Rules API', () => { expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', { method: 'GET', query: { - filter: 'alert.attributes.tags: "__internal_immutable:false"', + filter: 'alert.attributes.params.immutable: false', page: 1, per_page: 20, sort_field: 'enabled', @@ -228,7 +228,7 @@ describe('Detections Rules API', () => { expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', { method: 'GET', query: { - filter: 'alert.attributes.tags: "__internal_immutable:true"', + filter: 'alert.attributes.params.immutable: true', page: 1, per_page: 20, sort_field: 'enabled', @@ -383,7 +383,7 @@ describe('Detections Rules API', () => { method: 'GET', query: { filter: - 'alert.attributes.tags: "__internal_immutable:false" AND alert.attributes.tags: "__internal_immutable:true" AND alert.attributes.tags:("hello" AND "world") AND (alert.attributes.name: "ruleName" OR alert.attributes.params.index: "ruleName" OR alert.attributes.params.threat.tactic.id: "ruleName" OR alert.attributes.params.threat.tactic.name: "ruleName" OR alert.attributes.params.threat.technique.id: "ruleName" OR alert.attributes.params.threat.technique.name: "ruleName" OR alert.attributes.params.threat.technique.subtechnique.id: "ruleName" OR alert.attributes.params.threat.technique.subtechnique.name: "ruleName")', + 'alert.attributes.tags:("hello" AND "world") AND (alert.attributes.name: "ruleName" OR alert.attributes.params.index: "ruleName" OR alert.attributes.params.threat.tactic.id: "ruleName" OR alert.attributes.params.threat.tactic.name: "ruleName" OR alert.attributes.params.threat.technique.id: "ruleName" OR alert.attributes.params.threat.technique.name: "ruleName" OR alert.attributes.params.threat.technique.subtechnique.id: "ruleName" OR alert.attributes.params.threat.technique.subtechnique.name: "ruleName")', page: 1, per_page: 20, sort_field: 'enabled', diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_with_fallback.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_with_fallback.test.tsx index 40d2e8663b618..d7c4ad8772bd2 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_with_fallback.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_with_fallback.test.tsx @@ -154,8 +154,6 @@ describe('useRuleWithFallback', () => { "tags": Array [ "host.name exists", "for testing", - "__internal_rule_id:82b2b065-a2ee-49fc-9d6d-781a75c3d280", - "__internal_immutable:false", ], "threat": Array [ Object { diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.test.ts index a26a4aec3ec02..dff02aa3d3a75 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.test.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.test.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { INTERNAL_IMMUTABLE_KEY } from '../../../../../common/constants'; import { FilterOptions } from './types'; import { convertRulesFilterToKQL } from './utils'; @@ -42,13 +41,23 @@ describe('convertRulesFilterToKQL', () => { it('handles presence of "showCustomRules" properly', () => { const kql = convertRulesFilterToKQL({ ...filterOptions, showCustomRules: true }); - expect(kql).toBe(`alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:false"`); + expect(kql).toBe(`alert.attributes.params.immutable: false`); }); it('handles presence of "showElasticRules" properly', () => { const kql = convertRulesFilterToKQL({ ...filterOptions, showElasticRules: true }); - expect(kql).toBe(`alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true"`); + expect(kql).toBe(`alert.attributes.params.immutable: true`); + }); + + it('handles presence of "showElasticRules" and "showCustomRules" at the same time properly', () => { + const kql = convertRulesFilterToKQL({ + ...filterOptions, + showElasticRules: true, + showCustomRules: true, + }); + + expect(kql).toBe(''); }); it('handles presence of "tags" properly', () => { @@ -66,7 +75,7 @@ describe('convertRulesFilterToKQL', () => { }); expect(kql).toBe( - `alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true" AND alert.attributes.tags:("tag1" AND "tag2") AND (alert.attributes.name: "foo" OR alert.attributes.params.index: "foo" OR alert.attributes.params.threat.tactic.id: "foo" OR alert.attributes.params.threat.tactic.name: "foo" OR alert.attributes.params.threat.technique.id: "foo" OR alert.attributes.params.threat.technique.name: "foo" OR alert.attributes.params.threat.technique.subtechnique.id: "foo" OR alert.attributes.params.threat.technique.subtechnique.name: "foo")` + `alert.attributes.params.immutable: true AND alert.attributes.tags:("tag1" AND "tag2") AND (alert.attributes.name: "foo" OR alert.attributes.params.index: "foo" OR alert.attributes.params.threat.tactic.id: "foo" OR alert.attributes.params.threat.tactic.name: "foo" OR alert.attributes.params.threat.technique.id: "foo" OR alert.attributes.params.threat.technique.name: "foo" OR alert.attributes.params.threat.technique.subtechnique.id: "foo" OR alert.attributes.params.threat.technique.subtechnique.name: "foo")` ); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.ts index 069746223731c..8c576979a15c0 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { INTERNAL_IMMUTABLE_KEY } from '../../../../../common/constants'; import { escapeKuery } from '../../../../common/lib/keury'; import { FilterOptions } from './types'; @@ -35,12 +34,12 @@ export const convertRulesFilterToKQL = ({ }: FilterOptions): string => { const filters: string[] = []; - if (showCustomRules) { - filters.push(`alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:false"`); - } - - if (showElasticRules) { - filters.push(`alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true"`); + if (showCustomRules && showElasticRules) { + // if both showCustomRules && showElasticRules selected we omit filter, as it includes all existing rules + } else if (showElasticRules) { + filters.push('alert.attributes.params.immutable: true'); + } else if (showCustomRules) { + filters.push('alert.attributes.params.immutable: false'); } if (tags.length > 0) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_add_tags.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_add_tags.test.ts deleted file mode 100644 index 95051ad3d8021..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_add_tags.test.ts +++ /dev/null @@ -1,28 +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. - */ - -// eslint-disable-next-line no-restricted-imports -import { legacyAddTags } from './legacy_add_tags'; -import { INTERNAL_RULE_ALERT_ID_KEY } from '../../../../common/constants'; - -describe('legacyAdd_tags', () => { - test('it should add a rule id as an internal structure', () => { - const tags = legacyAddTags([], 'rule-1'); - expect(tags).toEqual([`${INTERNAL_RULE_ALERT_ID_KEY}:rule-1`]); - }); - - test('it should not allow duplicate tags to be created', () => { - const tags = legacyAddTags(['tag-1', 'tag-1'], 'rule-1'); - expect(tags).toEqual(['tag-1', `${INTERNAL_RULE_ALERT_ID_KEY}:rule-1`]); - }); - - test('it should not allow duplicate internal tags to be created when called two times in a row', () => { - const tags1 = legacyAddTags(['tag-1'], 'rule-1'); - const tags2 = legacyAddTags(tags1, 'rule-1'); - expect(tags2).toEqual(['tag-1', `${INTERNAL_RULE_ALERT_ID_KEY}:rule-1`]); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_add_tags.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_add_tags.ts deleted file mode 100644 index b17d8d7226a64..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_add_tags.ts +++ /dev/null @@ -1,14 +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 { INTERNAL_RULE_ALERT_ID_KEY } from '../../../../common/constants'; - -/** - * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function - */ -export const legacyAddTags = (tags: string[], ruleAlertId: string): string[] => - Array.from(new Set([...tags, `${INTERNAL_RULE_ALERT_ID_KEY}:${ruleAlertId}`])); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_create_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_create_notifications.ts index dec6d51aa9082..e82b19faa40bd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_create_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_create_notifications.ts @@ -9,8 +9,6 @@ import { SanitizedRule } from '@kbn/alerting-plugin/common'; import { SERVER_APP_ID, LEGACY_NOTIFICATIONS_ID } from '../../../../common/constants'; // eslint-disable-next-line no-restricted-imports import { CreateNotificationParams, LegacyRuleNotificationAlertTypeParams } from './legacy_types'; -// eslint-disable-next-line no-restricted-imports -import { legacyAddTags } from './legacy_add_tags'; /** * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function @@ -26,7 +24,7 @@ export const legacyCreateNotifications = async ({ rulesClient.create({ data: { name, - tags: legacyAddTags([], ruleAlertId), + tags: [], alertTypeId: LEGACY_NOTIFICATIONS_ID, consumer: SERVER_APP_ID, params: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_read_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_read_notifications.ts index 05f64e19df26e..3e28ac879e2c3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_read_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_read_notifications.ts @@ -10,7 +10,6 @@ import { RuleTypeParams, SanitizedRule } from '@kbn/alerting-plugin/common'; import { LegacyReadNotificationParams, legacyIsAlertType } from './legacy_types'; // eslint-disable-next-line no-restricted-imports import { legacyFindNotifications } from './legacy_find_notifications'; -import { INTERNAL_RULE_ALERT_ID_KEY } from '../../../../common/constants'; /** * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function @@ -39,7 +38,7 @@ export const legacyReadNotifications = async ({ } else if (ruleAlertId != null) { const notificationFromFind = await legacyFindNotifications({ rulesClient, - filter: `alert.attributes.tags: "${INTERNAL_RULE_ALERT_ID_KEY}:${ruleAlertId}"`, + filter: `alert.attributes.params.ruleAlertId: "${ruleAlertId}"`, page: 1, }); if ( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 10607e352000d..8e2f0da4e65c9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -17,8 +17,6 @@ import { DETECTION_ENGINE_SIGNALS_STATUS_URL, DETECTION_ENGINE_PRIVILEGES_URL, DETECTION_ENGINE_QUERY_SIGNALS_URL, - INTERNAL_RULE_ID_KEY, - INTERNAL_IMMUTABLE_KEY, DETECTION_ENGINE_PREPACKAGED_URL, DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL, DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL, @@ -393,7 +391,7 @@ export const nonRuleAlert = () => ({ export const getRuleMock = (params: T): SanitizedRule => ({ id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', name: 'Detect Root/Admin Users', - tags: [`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:false`], + tags: [], alertTypeId: ruleTypeMappings[params.type], consumer: 'siem', params, @@ -693,7 +691,7 @@ export const getSignalsMigrationStatusRequest = () => export const legacyGetNotificationResult = (): LegacyRuleNotificationAlertType => ({ id: '200dbf2f-b269-4bf9-aa85-11ba32ba73ba', name: 'Notification for Rule Test', - tags: ['__internal_rule_alert_id:85b64e8a-2e40-4096-86af-5ac172c10825'], + tags: [], alertTypeId: 'siem.notifications', consumer: 'siem', params: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts index d35df4d51ee02..0f70a12e463cf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts @@ -61,7 +61,7 @@ export const getPrepackagedRulesStatusRoute = ( page: 1, sortField: 'enabled', sortOrder: 'desc', - filter: 'alert.attributes.tags:"__internal_immutable:false"', + filter: 'alert.attributes.params.immutable: false', fields: undefined, }); const frameworkRequest = await buildFrameworkRequest(context, security, request); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/legacy_create_legacy_notification.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/legacy_create_legacy_notification.ts index 6b6e65329c06f..ffccedb691db4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/legacy_create_legacy_notification.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/legacy_create_legacy_notification.ts @@ -16,8 +16,6 @@ import { legacyReadNotifications } from '../../notifications/legacy_read_notific // eslint-disable-next-line no-restricted-imports import { LegacyRuleNotificationAlertTypeParams } from '../../notifications/legacy_types'; // eslint-disable-next-line no-restricted-imports -import { legacyAddTags } from '../../notifications/legacy_add_tags'; -// eslint-disable-next-line no-restricted-imports import { legacyCreateNotifications } from '../../notifications/legacy_create_notifications'; /** @@ -72,7 +70,7 @@ export const legacyCreateLegacyNotificationRoute = ( await rulesClient.update({ id: notification.id, data: { - tags: legacyAddTags([], ruleAlertId), + tags: [], name, schedule: { interval, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.test.ts index 6e2a5d08becbc..2fb1dccc64f80 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.test.ts @@ -13,7 +13,6 @@ import { getIdError, transformFindAlerts, transform, - transformTags, getIdBulkError, transformAlertsToRules, getDuplicates, @@ -23,7 +22,6 @@ import { migrateLegacyActionsIds, } from './utils'; import { getRuleMock } from '../__mocks__/request_responses'; -import { INTERNAL_IDENTIFIER } from '../../../../../common/constants'; import { PartialFilter } from '../../types'; import { BulkError, createBulkErrorObject } from '../utils'; import { getOutputRuleAlertForRest } from '../__mocks__/utils'; @@ -99,9 +97,9 @@ describe('utils', () => { expect(ruleWithEnabledFalse).toEqual(expected); }); - test('should work with tags but filter out any internal tags', () => { + test('should work with tags', () => { const fullRule = getRuleMock(getQueryRuleParams()); - fullRule.tags = ['tag 1', 'tag 2', `${INTERNAL_IDENTIFIER}_some_other_value`]; + fullRule.tags = ['tag 1', 'tag 2']; const rule = internalRuleToAPIResponse(fullRule); const expected = getOutputRuleAlertForRest(); expected.tags = ['tag 1', 'tag 2']; @@ -382,23 +380,6 @@ describe('utils', () => { }); }); - describe('transformTags', () => { - test('it returns tags that have no internal structures', () => { - expect(transformTags(['tag 1', 'tag 2'])).toEqual(['tag 1', 'tag 2']); - }); - - test('it returns empty tags given empty tags', () => { - expect(transformTags([])).toEqual([]); - }); - - test('it returns tags with internal tags stripped out', () => { - expect(transformTags(['tag 1', `${INTERNAL_IDENTIFIER}_some_value`, 'tag 2'])).toEqual([ - 'tag 1', - 'tag 2', - ]); - }); - }); - describe('getIdBulkError', () => { test('outputs message about id and rule_id not being found if both are not null', () => { const error = getIdBulkError({ id: '123', ruleId: '456' }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts index 27f3f160a5288..850d971b39c59 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts @@ -17,7 +17,6 @@ import { RuleExecutionSummary } from '../../../../../common/detection_engine/sch import { RulesSchema } from '../../../../../common/detection_engine/schemas/response/rules_schema'; import { ImportRulesSchemaDecoded } from '../../../../../common/detection_engine/schemas/request/import_rules_schema'; import { CreateRulesBulkSchema } from '../../../../../common/detection_engine/schemas/request/create_rules_bulk_schema'; -import { INTERNAL_IDENTIFIER } from '../../../../../common/constants'; import { RuleAlertType, isAlertType } from '../../rules/types'; import { createBulkErrorObject, BulkError, OutputError } from '../utils'; import { internalRuleToAPIResponse } from '../../schemas/rule_converters'; @@ -88,10 +87,6 @@ export const getIdBulkError = ({ } }; -export const transformTags = (tags: string[]): string[] => { - return tags.filter((tag) => !tag.startsWith(INTERNAL_IDENTIFIER)); -}; - export const transformAlertsToRules = ( rules: RuleAlertType[], legacyRuleActions: Record diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts index e1259be5062a0..8492ec6cd2c26 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts @@ -78,7 +78,6 @@ import { commonParamsCamelToSnake, typeSpecificCamelToSnake, } from '../../../schemas/rule_converters'; -import { transformTags } from '../../../routes/rules/utils'; import { transformAlertToRuleAction } from '../../../../../../common/detection_engine/transform_actions'; import { AncestorLatest, @@ -215,7 +214,7 @@ export const buildAlert = ( [ALERT_RULE_RULE_ID]: params.ruleId, [ALERT_RULE_RULE_NAME_OVERRIDE]: params.ruleNameOverride, [ALERT_RULE_SEVERITY_MAPPING]: params.severityMapping, - [ALERT_RULE_TAGS]: transformTags(tags), + [ALERT_RULE_TAGS]: tags, [ALERT_RULE_THREAT]: params.threat, [ALERT_RULE_THROTTLE]: throttle ?? undefined, [ALERT_RULE_TIMELINE_ID]: params.timelineId, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/add_tags.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/add_tags.test.ts deleted file mode 100644 index 93fddc06b8068..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/add_tags.test.ts +++ /dev/null @@ -1,49 +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 { addTags } from './add_tags'; -import { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; - -describe('add_tags', () => { - test('it should add a rule id as an internal structure with immutable true', () => { - const tags = addTags([], 'rule-1', true); - expect(tags).toEqual([`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:true`]); - }); - - test('it should add a rule id as an internal structure with immutable false', () => { - const tags = addTags([], 'rule-1', false); - expect(tags).toEqual([`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:false`]); - }); - - test('it should not allow duplicate tags to be created', () => { - const tags = addTags(['tag-1', 'tag-1'], 'rule-1', false); - expect(tags).toEqual([ - 'tag-1', - `${INTERNAL_RULE_ID_KEY}:rule-1`, - `${INTERNAL_IMMUTABLE_KEY}:false`, - ]); - }); - - test('it should not allow duplicate internal tags to be created when called two times in a row', () => { - const tags1 = addTags(['tag-1'], 'rule-1', false); - const tags2 = addTags(tags1, 'rule-1', false); - expect(tags2).toEqual([ - 'tag-1', - `${INTERNAL_RULE_ID_KEY}:rule-1`, - `${INTERNAL_IMMUTABLE_KEY}:false`, - ]); - }); - - test('it should overwrite existing immutable tag if it exists', () => { - const tags1 = addTags(['tag-1', `${INTERNAL_IMMUTABLE_KEY}:true`], 'rule-1', false); - expect(tags1).toEqual([ - 'tag-1', - `${INTERNAL_RULE_ID_KEY}:rule-1`, - `${INTERNAL_IMMUTABLE_KEY}:false`, - ]); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/add_tags.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/add_tags.ts deleted file mode 100644 index d66f961b38598..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/add_tags.ts +++ /dev/null @@ -1,20 +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 { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; - -export const addTags = (tags: string[], ruleId: string, immutable: boolean): string[] => { - return Array.from( - new Set([ - ...tags.filter( - (tag) => !(tag.startsWith(INTERNAL_RULE_ID_KEY) || tag.startsWith(INTERNAL_IMMUTABLE_KEY)) - ), - `${INTERNAL_RULE_ID_KEY}:${ruleId}`, - `${INTERNAL_IMMUTABLE_KEY}:${immutable}`, - ]) - ); -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts index ed2d1e823cd1c..24017adc20626 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts @@ -19,7 +19,6 @@ import { SERVER_APP_ID, } from '../../../../common/constants'; import { CreateRulesOptions } from './types'; -import { addTags } from './add_tags'; import { PartialFilter } from '../types'; import { transformToAlertThrottle, transformToNotifyWhen } from './utils'; @@ -83,7 +82,7 @@ export const createRules = async ({ }, data: { name, - tags: addTags(tags, ruleId, immutable), + tags, alertTypeId: ruleTypeMappings[type], consumer: SERVER_APP_ID, params: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.test.ts index f93312ade4cfc..04d8e66a076fb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.test.ts @@ -6,7 +6,6 @@ */ import uuid from 'uuid'; -import { INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; import { duplicateRule } from './duplicate_rule'; jest.mock('uuid', () => ({ @@ -22,7 +21,7 @@ describe('duplicateRule', () => { id: 'oldTestRuleId', notifyWhen: 'onActiveAlert', name: 'test', - tags: ['test', '__internal_rule_id:oldTestRuleId', `${INTERNAL_IMMUTABLE_KEY}:false`], + tags: ['test'], alertTypeId: 'siem.signals', consumer: 'siem', params: { @@ -125,8 +124,6 @@ describe('duplicateRule', () => { }, "tags": Array [ "test", - "__internal_rule_id:newId", - "__internal_immutable:false", ], "throttle": null, } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.ts index d3dba0f61df2e..4ef21d0450517 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.ts @@ -13,7 +13,6 @@ import { ruleTypeMappings } from '@kbn/securitysolution-rules'; import { SanitizedRule } from '@kbn/alerting-plugin/common'; import { SERVER_APP_ID } from '../../../../common/constants'; import { InternalRuleCreate, RuleParams } from '../schemas/rule_schemas'; -import { addTags } from './add_tags'; const DUPLICATE_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.cloneRule.duplicateTitle', @@ -26,7 +25,7 @@ export const duplicateRule = (rule: SanitizedRule): InternalRuleCrea const newRuleId = uuid.v4(); return { name: `${rule.name} [${DUPLICATE_TITLE}]`, - tags: addTags(rule.tags, newRuleId, false), + tags: rule.tags, alertTypeId: ruleTypeMappings[rule.params.type], consumer: SERVER_APP_ID, params: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/edit_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/edit_rule.ts index 93a297e6965b4..c7f17e100205c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/edit_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/edit_rule.ts @@ -11,7 +11,6 @@ import { validate } from '@kbn/securitysolution-io-ts-utils'; import type { RulesClient } from '@kbn/alerting-plugin/server'; import { RuleAlertType } from './types'; import { InternalRuleUpdate, internalRuleUpdate } from '../schemas/rule_schemas'; -import { addTags } from './add_tags'; class EditRuleError extends Error { public readonly statusCode: number; @@ -103,10 +102,7 @@ const validateAndSanitizeChanges = ( throw new EditRuleError(`Internal rule editing error: can't change "params.version"`, 500); } - return { - ...changed, - tags: addTags(changed.tags, changed.params.ruleId, changed.params.immutable), - }; + return changed; }; const isRuleChanged = (originalRule: RuleAlertType, editedRule: RuleAlertType): boolean => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts index a15cc1d3eec1f..440d3db36bd63 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts @@ -6,12 +6,11 @@ */ import { RulesClient } from '@kbn/alerting-plugin/server'; -import { INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; import { RuleAlertType, isAlertTypes } from './types'; import { findRules } from './find_rules'; -export const FILTER_NON_PREPACKED_RULES = `alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:false"`; -export const FILTER_PREPACKED_RULES = `alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true"`; +export const FILTER_NON_PREPACKED_RULES = 'alert.attributes.params.immutable: false'; +export const FILTER_PREPACKED_RULES = 'alert.attributes.params.immutable: true'; export const getNonPackagedRulesCount = async ({ rulesClient, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts index 836c9b035f305..dcf3f7532b53c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts @@ -16,7 +16,6 @@ import { RulesSchema } from '../../../../common/detection_engine/schemas/respons import { getExportDetailsNdjson } from './get_export_details_ndjson'; import { isAlertType } from './types'; -import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants'; import { findRules } from './find_rules'; import { getRuleExceptionsForExport } from './get_export_rule_exceptions'; @@ -92,10 +91,8 @@ export const getRulesFromObjects = async ( const chunkedObjects = chunk(objects, 1024); const filter = chunkedObjects .map((chunkedArray) => { - const joinedIds = chunkedArray - .map((object) => `"${INTERNAL_RULE_ID_KEY}:${object.rule_id}"`) - .join(' OR '); - return `alert.attributes.tags: (${joinedIds})`; + const joinedIds = chunkedArray.map((object) => object.rule_id).join(' OR '); + return `alert.attributes.params.ruleId: (${joinedIds})`; }) .join(' OR '); const rules = await findRules({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts index 05f58e44ecaa2..ad2443b34fa95 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts @@ -14,7 +14,6 @@ import { normalizeThresholdObject, } from '../../../../common/detection_engine/utils'; import { internalRuleUpdate, RuleParams } from '../schemas/rule_schemas'; -import { addTags } from './add_tags'; import { PatchRulesOptions } from './types'; import { calculateInterval, @@ -190,7 +189,7 @@ export const patchRules = async ({ ); const newRule = { - tags: addTags(tags ?? rule.tags, rule.params.ruleId, rule.params.immutable), + tags: tags ?? rule.tags, name: calculateName({ updatedName: name, originalName: rule.name }), schedule: { interval: calculateInterval(interval, rule.schedule.interval), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts index 14a63df87ebe8..ef9d867105e10 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts @@ -6,7 +6,6 @@ */ import { ResolvedSanitizedRule, SanitizedRule } from '@kbn/alerting-plugin/common'; -import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants'; import { RuleParams } from '../schemas/rule_schemas'; import { findRules } from './find_rules'; import { isAlertType, ReadRuleOptions } from './types'; @@ -17,7 +16,7 @@ import { isAlertType, ReadRuleOptions } from './types'; * and the id will either be found through `rulesClient.get({ id })` or it will not * be returned as a not-found or a thrown error that is not 404. * @param ruleId - This is a close second to being fast as long as it can find the rule_id from - * a filter query against the tags using `alert.attributes.tags: "__internal:${ruleId}"]` + * a filter query against the ruleId property in params using `alert.attributes.params.ruleId: "${ruleId}"` */ export const readRules = async ({ rulesClient, @@ -49,7 +48,7 @@ export const readRules = async ({ } else if (ruleId != null) { const ruleFromFind = await findRules({ rulesClient, - filter: `alert.attributes.tags: "${INTERNAL_RULE_ID_KEY}:${ruleId}"`, + filter: `alert.attributes.params.ruleId: "${ruleId}"`, page: 1, fields: undefined, perPage: undefined, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts index df9a09ba98bb5..ba65b76f01c4a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts @@ -12,7 +12,6 @@ import { DEFAULT_MAX_SIGNALS } from '../../../../common/constants'; import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; import { UpdateRulesOptions } from './types'; -import { addTags } from './add_tags'; import { typeSpecificSnakeToCamel } from '../schemas/rule_converters'; import { internalRuleUpdate, RuleParams } from '../schemas/rule_schemas'; import { maybeMute, transformToAlertThrottle, transformToNotifyWhen } from './utils'; @@ -39,7 +38,7 @@ export const updateRules = async ({ const enabled = ruleUpdate.enabled ?? true; const newInternalRule = { name: ruleUpdate.name, - tags: addTags(ruleUpdate.tags ?? [], existingRule.params.ruleId, existingRule.params.immutable), + tags: ruleUpdate.tags ?? [], params: { author: ruleUpdate.author ?? [], buildingBlockType: ruleUpdate.building_block_type, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_converters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_converters.ts index 8b7fdcf568539..fd80bec1f6ad9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_converters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_converters.ts @@ -29,10 +29,8 @@ import { ResponseTypeSpecific, } from '../../../../common/detection_engine/schemas/request'; import { AppClient } from '../../../types'; -import { addTags } from '../rules/add_tags'; import { DEFAULT_MAX_SIGNALS, SERVER_APP_ID } from '../../../../common/constants'; import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; -import { transformTags } from '../routes/rules/utils'; import { transformFromAlertThrottle, transformToAlertThrottle, @@ -133,7 +131,7 @@ export const convertCreateAPIToInternalSchema = ( const newRuleId = input.rule_id ?? uuid.v4(); return { name: input.name, - tags: addTags(input.tags ?? [], newRuleId, false), + tags: input.tags ?? [], alertTypeId: ruleTypeMappings[input.type], consumer: SERVER_APP_ID, params: { @@ -301,7 +299,7 @@ export const internalRuleToAPIResponse = ( created_at: rule.createdAt.toISOString(), created_by: rule.createdBy ?? 'elastic', name: rule.name, - tags: transformTags(rule.tags), + tags: rule.tags, interval: rule.schedule.interval, enabled: rule.enabled, // Security solution shared rule params diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/find_rule_by_filter.sh b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/find_rule_by_filter.sh index 6aab0cd3c9728..35199b775b33c 100755 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/find_rule_by_filter.sh +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/find_rule_by_filter.sh @@ -26,13 +26,13 @@ FILTER=${1:-'alert.attributes.enabled:%20true'} # ./find_rule_by_filter.sh "alert.attributes.tags:tag_1" # Example get all pre-packaged rules -# ./find_rule_by_filter.sh "alert.attributes.tags:%20%22__internal_immutable:true%22" +# ./find_rule_by_filter.sh "alert.attributes.params.immutable:%20true" # Example get all non pre-packaged rules -# ./find_rule_by_filter.sh "alert.attributes.tags:%20%22__internal_immutable:false%22" +# ./find_rule_by_filter.sh "alert.attributes.params.immutable:%20false" # Example get all non pre-packaged rules and a tag_1 -# ./find_rule_by_filter.sh "alert.attributes.tags:%20%22__internal_immutable:false%22%20AND%20alert.attributes.tags:tag_1" +# ./find_rule_by_filter.sh "alert.attributes.params.immutable:%20false%20AND%20alert.attributes.tags:tag_1" curl -s -k \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ -X GET ${KIBANA_URL}${SPACE_URL}/api/detection_engine/rules/_find?filter=$FILTER | jq . diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md index c76a69db084ca..cf8e3262e2826 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md @@ -53,10 +53,7 @@ to any newly saved rule: "_source" : { "alert" : { "name" : "kql test rule 1", - "tags" : [ - "__internal_rule_id:4ec223b9-77fa-4895-8539-6b3e586a2858", - "__internal_immutable:false" - ], + "tags" : [], "alertTypeId" : "siem.signals", "other data... other data": "other data...other data", "exceptionsList" : [ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.test.ts index 4d6327e05f2d3..2154e672e0089 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.test.ts @@ -6,10 +6,8 @@ */ import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; - import { getRuleMock, getFindResultWithMultiHits } from '../routes/__mocks__/request_responses'; -import { INTERNAL_RULE_ID_KEY, INTERNAL_IDENTIFIER } from '../../../../common/constants'; -import { readRawTags, readTags, convertTagsToSet, convertToTags, isTags } from './read_tags'; +import { readTags, convertTagsToSet, convertToTags, isTags } from './read_tags'; import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; describe('read_tags', () => { @@ -17,88 +15,6 @@ describe('read_tags', () => { jest.resetAllMocks(); }); - describe('readRawTags', () => { - test('it should return the intersection of tags to where none are repeating', async () => { - const result1 = getRuleMock(getQueryRuleParams()); - result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - result1.params.ruleId = 'rule-1'; - result1.tags = ['tag 1', 'tag 2', 'tag 3']; - - const result2 = getRuleMock(getQueryRuleParams()); - result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - result2.params.ruleId = 'rule-2'; - result2.tags = ['tag 1', 'tag 2', 'tag 3', 'tag 4']; - - const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - - const tags = await readRawTags({ rulesClient }); - expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); - }); - - test('it should return the intersection of tags to where some are repeating values', async () => { - const result1 = getRuleMock(getQueryRuleParams()); - result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - result1.params.ruleId = 'rule-1'; - result1.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3']; - - const result2 = getRuleMock(getQueryRuleParams()); - result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - result2.params.ruleId = 'rule-2'; - result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4']; - - const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - - const tags = await readRawTags({ rulesClient }); - expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); - }); - - test('it should work with no tags defined between two results', async () => { - const result1 = getRuleMock(getQueryRuleParams()); - result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - result1.params.ruleId = 'rule-1'; - result1.tags = []; - - const result2 = getRuleMock(getQueryRuleParams()); - result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - result2.params.ruleId = 'rule-2'; - result2.tags = []; - - const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - - const tags = await readRawTags({ rulesClient }); - expect(tags).toEqual([]); - }); - - test('it should work with a single tag which has repeating values in it', async () => { - const result1 = getRuleMock(getQueryRuleParams()); - result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - result1.params.ruleId = 'rule-1'; - result1.tags = ['tag 1', 'tag 1', 'tag 1', 'tag 2']; - - const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - - const tags = await readRawTags({ rulesClient }); - expect(tags).toEqual(['tag 1', 'tag 2']); - }); - - test('it should work with a single tag which has empty tags', async () => { - const result1 = getRuleMock(getQueryRuleParams()); - result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - result1.params.ruleId = 'rule-1'; - result1.tags = []; - - const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - - const tags = await readRawTags({ rulesClient }); - expect(tags).toEqual([]); - }); - }); - describe('readTags', () => { test('it should return the intersection of tags to where none are repeating', async () => { const result1 = getRuleMock(getQueryRuleParams()); @@ -179,56 +95,6 @@ describe('read_tags', () => { const tags = await readTags({ rulesClient }); expect(tags).toEqual([]); }); - - test('it should filter out any __internal tags for things such as alert_id', async () => { - const result1 = getRuleMock(getQueryRuleParams()); - result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - result1.params.ruleId = 'rule-1'; - result1.tags = [ - `${INTERNAL_IDENTIFIER}_some_value`, - `${INTERNAL_RULE_ID_KEY}_some_value`, - 'tag 1', - ]; - - const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - - const tags = await readTags({ rulesClient }); - expect(tags).toEqual(['tag 1']); - }); - - test('it should filter out any __internal tags with two different results', async () => { - const result1 = getRuleMock(getQueryRuleParams()); - result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - result1.params.ruleId = 'rule-1'; - result1.tags = [ - `${INTERNAL_IDENTIFIER}_some_value`, - `${INTERNAL_RULE_ID_KEY}_some_value`, - 'tag 1', - 'tag 2', - 'tag 3', - 'tag 4', - 'tag 5', - ]; - - const result2 = getRuleMock(getQueryRuleParams()); - result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - result2.params.ruleId = 'rule-2'; - result2.tags = [ - `${INTERNAL_IDENTIFIER}_some_value`, - `${INTERNAL_RULE_ID_KEY}_some_value`, - 'tag 1', - 'tag 2', - 'tag 3', - 'tag 4', - ]; - - const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - - const tags = await readTags({ rulesClient }); - expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4', 'tag 5']); - }); }); describe('convertTagsToSet', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.ts index 8f3e1468278cf..4ab3ccc831af1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.ts @@ -7,7 +7,6 @@ import { has } from 'lodash/fp'; import { RulesClient } from '@kbn/alerting-plugin/server'; -import { INTERNAL_IDENTIFIER } from '../../../../common/constants'; import { findRules } from '../rules/find_rules'; export interface TagType { @@ -20,11 +19,11 @@ export const isTags = (obj: object): obj is TagType => { }; export const convertToTags = (tagObjects: object[]): string[] => { - const tags = tagObjects.reduce((accum, tagObj) => { + const tags = tagObjects.reduce((acc, tagObj) => { if (isTags(tagObj)) { - return [...accum, ...tagObj.tags]; + return [...acc, ...tagObj.tags]; } else { - return accum; + return acc; } }, []); return tags; @@ -41,15 +40,6 @@ export const convertTagsToSet = (tagObjects: object[]): Set => { // Ref: https://www.elastic.co/guide/en/kibana/master/saved-objects-api.html export const readTags = async ({ rulesClient, -}: { - rulesClient: RulesClient; -}): Promise => { - const tags = await readRawTags({ rulesClient }); - return tags.filter((tag) => !tag.startsWith(INTERNAL_IDENTIFIER)); -}; - -export const readRawTags = async ({ - rulesClient, }: { rulesClient: RulesClient; perPage?: number; diff --git a/x-pack/plugins/security_solution/server/usage/detections/get_metrics.test.ts b/x-pack/plugins/security_solution/server/usage/detections/get_metrics.test.ts index 4e782f914df70..9c40d662544c1 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/get_metrics.test.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/get_metrics.test.ts @@ -131,7 +131,7 @@ describe('Detections Usage and Metrics', () => { esClient.search.mockResponseOnce(getEventLogElasticRules()); esClient.search.mockResponseOnce(getElasticLogCustomRules()); esClient.search.mockResponseOnce(getMockRuleAlertsResponse(800)); - savedObjectsClient.find.mockResolvedValueOnce(getMockRuleSearchResponse('not_immutable')); + savedObjectsClient.find.mockResolvedValueOnce(getMockRuleSearchResponse(false)); savedObjectsClient.find.mockResolvedValueOnce(getMockAlertCaseCommentsResponse()); // Get empty saved object for legacy notification system. savedObjectsClient.find.mockResolvedValueOnce(getEmptySavedObjectResponse()); diff --git a/x-pack/plugins/security_solution/server/usage/detections/ml_jobs/get_metrics.mocks.ts b/x-pack/plugins/security_solution/server/usage/detections/ml_jobs/get_metrics.mocks.ts index 0863ca6cef411..4ad8f2213a682 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/ml_jobs/get_metrics.mocks.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/ml_jobs/get_metrics.mocks.ts @@ -291,7 +291,7 @@ export const getMockMlDatafeedStatsResponse = () => ({ }); export const getMockRuleSearchResponse = ( - immutableTag: string = '__internal_immutable:true' + immutable: boolean = true ): SavedObjectsFindResponse => ({ page: 1, @@ -304,16 +304,7 @@ export const getMockRuleSearchResponse = ( namespaces: ['default'], attributes: { name: 'Azure Diagnostic Settings Deletion', - tags: [ - 'Elastic', - 'Cloud', - 'Azure', - 'Continuous Monitoring', - 'SecOps', - 'Monitoring', - '__internal_rule_id:5370d4cd-2bb3-4d71-abf5-1e1d0ff5a2de', - `${immutableTag}`, - ], + tags: ['Elastic', 'Cloud', 'Azure', 'Continuous Monitoring', 'SecOps', 'Monitoring'], alertTypeId: 'siem.queryRule', consumer: 'siem', params: { @@ -326,7 +317,7 @@ export const getMockRuleSearchResponse = ( 'Deletion of diagnostic settings may be done by a system or network administrator. Verify whether the username, hostname, and/or resource name should be making changes in your environment. Diagnostic settings deletion from unfamiliar users or hosts should be investigated. If known behavior is causing false positives, it can be exempted from the rule.', ], from: 'now-25m', - immutable: true, + immutable, query: 'event.dataset:azure.activitylogs and azure.activitylogs.operation_name:"MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/DELETE" and event.outcome:(Success or success)', language: 'kuery', diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/transform_utils/get_rule_object_correlations.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/transform_utils/get_rule_object_correlations.ts index 169cb502521df..5387c63aace3e 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/transform_utils/get_rule_object_correlations.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/transform_utils/get_rule_object_correlations.ts @@ -9,8 +9,6 @@ import type { SavedObjectsFindResult } from '@kbn/core/server'; import type { RuleMetric } from '../types'; import type { RuleSearchResult } from '../../../types'; -import { isElasticRule } from '../../../queries/utils/is_elastic_rule'; - export interface RuleObjectCorrelationsOptions { ruleResults: Array>; legacyNotificationRuleIds: Map< @@ -32,7 +30,6 @@ export const getRuleObjectCorrelations = ({ return ruleResults.map((result) => { const ruleId = result.id; const { attributes } = result; - const isElastic = isElasticRule(attributes.tags); // Even if the legacy notification is set to "no_actions" we still count the rule as having a legacy notification that is not migrated yet. const hasLegacyNotification = legacyNotificationRuleIds.get(ruleId) != null; @@ -50,7 +47,8 @@ export const getRuleObjectCorrelations = ({ rule_type: attributes.params.type, rule_version: attributes.params.version, enabled: attributes.enabled, - elastic_rule: isElastic, + // if rule immutable, it's Elastic/prebuilt + elastic_rule: attributes.params.immutable, created_on: attributes.createdAt, updated_on: attributes.updatedAt, alert_count_daily: alertsCounts.get(ruleId) || 0, diff --git a/x-pack/plugins/security_solution/server/usage/queries/utils/is_elastic_rule.ts b/x-pack/plugins/security_solution/server/usage/queries/utils/is_elastic_rule.ts deleted file mode 100644 index f08959702b290..0000000000000 --- a/x-pack/plugins/security_solution/server/usage/queries/utils/is_elastic_rule.ts +++ /dev/null @@ -1,11 +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 { INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; - -export const isElasticRule = (tags: string[] = []) => - tags.includes(`${INTERNAL_IMMUTABLE_KEY}:true`); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts index 33cb64dcdbed1..4622e84081506 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts @@ -398,14 +398,9 @@ export default function createGetTests({ getService }: FtrProviderContext) { // Only the rule that was enabled should be tagged expect(responseEnabledBeforeMigration.body._source?.alert?.tags).to.eql([ - '__internal_rule_id:064e3fed-6328-416b-bb85-c08265088f41', - '__internal_immutable:false', 'auto_disabled_8.0', ]); - expect(responseDisabledBeforeMigration.body._source?.alert?.tags).to.eql([ - '__internal_rule_id:364e3fed-6328-416b-bb85-c08265088f41', - '__internal_immutable:false', - ]); + expect(responseDisabledBeforeMigration.body._source?.alert?.tags).to.eql([]); }); it('8.2.0 migrates params to mapped_params for specific params properties', async () => { @@ -423,5 +418,18 @@ export default function createGetTests({ getService }: FtrProviderContext) { severity: '80-critical', }); }); + + it('8.3.0 removes internal tags in Security Solution rule', async () => { + const response = await es.get<{ alert: RawRule }>( + { + index: '.kibana', + id: 'alert:8990af61-c09a-11ec-9164-4bfd6fc32c43', + }, + { meta: true } + ); + + expect(response.statusCode).to.equal(200); + expect(response.body._source?.alert?.tags).to.eql(['test-tag-1', 'foo-tag']); + }); }); } diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/resolve_read_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/resolve_read_rules.ts index 2b4b31bb5510b..5c2bdafbd5c32 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/resolve_read_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/resolve_read_rules.ts @@ -65,10 +65,7 @@ export default ({ getService }: FtrProviderContext) => { body: { alert: { name: 'test 7.14', - tags: [ - '__internal_rule_id:82747bb8-bae0-4b59-8119-7f65ac564e14', - '__internal_immutable:false', - ], + tags: [], alertTypeId: 'siem.queryRule', consumer: 'siem', params: { diff --git a/x-pack/test/detection_engine_api_integration/utils/downgrade_immutable_rule.ts b/x-pack/test/detection_engine_api_integration/utils/downgrade_immutable_rule.ts index 36ebfaee231d9..57f20a1c0e645 100644 --- a/x-pack/test/detection_engine_api_integration/utils/downgrade_immutable_rule.ts +++ b/x-pack/test/detection_engine_api_integration/utils/downgrade_immutable_rule.ts @@ -7,7 +7,6 @@ import type { ToolingLog } from '@kbn/tooling-log'; import type { Client } from '@elastic/elasticsearch'; -import { INTERNAL_RULE_ID_KEY } from '@kbn/security-solution-plugin/common/constants'; import { countDownES } from './count_down_es'; export const downgradeImmutableRule = async ( @@ -29,7 +28,7 @@ export const downgradeImmutableRule = async ( }, query: { term: { - 'alert.tags': `${INTERNAL_RULE_ID_KEY}:${ruleId}`, + 'alert.params.ruleId': ruleId, }, }, }, diff --git a/x-pack/test/detection_engine_api_integration/utils/find_immutable_rule_by_id.ts b/x-pack/test/detection_engine_api_integration/utils/find_immutable_rule_by_id.ts index 2d77cffcdfe41..0e6fe73685c48 100644 --- a/x-pack/test/detection_engine_api_integration/utils/find_immutable_rule_by_id.ts +++ b/x-pack/test/detection_engine_api_integration/utils/find_immutable_rule_by_id.ts @@ -9,11 +9,7 @@ import type { ToolingLog } from '@kbn/tooling-log'; import type SuperTest from 'supertest'; import type { FullResponseSchema } from '@kbn/security-solution-plugin/common/detection_engine/schemas/request'; -import { - DETECTION_ENGINE_RULES_URL, - INTERNAL_IMMUTABLE_KEY, - INTERNAL_RULE_ID_KEY, -} from '@kbn/security-solution-plugin/common/constants'; +import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; /** * Helper to cut down on the noise in some of the tests. This @@ -32,7 +28,7 @@ export const findImmutableRuleById = async ( }> => { const response = await supertest .get( - `${DETECTION_ENGINE_RULES_URL}/_find?filter=alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true" AND alert.attributes.tags: "${INTERNAL_RULE_ID_KEY}:${ruleId}"` + `${DETECTION_ENGINE_RULES_URL}/_find?filter=alert.attributes.params.immutable: true AND alert.attributes.params.ruleId: "${ruleId}"` ) .set('kbn-xsrf', 'true') .send(); diff --git a/x-pack/test/functional/es_archives/alerts/data.json b/x-pack/test/functional/es_archives/alerts/data.json index 39ce6248c7ebb..1c096d9df9930 100644 --- a/x-pack/test/functional/es_archives/alerts/data.json +++ b/x-pack/test/functional/es_archives/alerts/data.json @@ -671,10 +671,7 @@ "source":{ "alert":{ "name":"enabled 7.16.1 query rule", - "tags":[ - "__internal_rule_id:064e3fed-6328-416b-bb85-c08265088f41", - "__internal_immutable:false" - ], + "tags":[], "alertTypeId":"siem.signals", "consumer":"siem", "params":{ @@ -767,10 +764,7 @@ "source":{ "alert":{ "name":"disabled 7.16.1 query rule", - "tags":[ - "__internal_rule_id:364e3fed-6328-416b-bb85-c08265088f41", - "__internal_immutable:false" - ], + "tags":[], "alertTypeId":"siem.signals", "consumer":"siem", "params":{ @@ -895,4 +889,104 @@ ] } } +} + +{ + "type":"doc", + "value":{ + "id":"alert:8990af61-c09a-11ec-9164-4bfd6fc32c43", + "index":".kibana_1", + "source":{ + "alert":{ + "name":"Test remove internal tags", + "alertTypeId":"siem.queryRule", + "consumer":"siem", + "params":{ + "immutable":true, + "ruleId":"bf9638eb-7d3c-4f40-83d7-8c40a7c80f2e", + "author":[ + + ], + "description":"remove interns tags mock rule", + "falsePositives":[ + + ], + "from":"now-36000060s", + "license":"", + "outputIndex":".siem-signals-default", + "meta":{ + "from":"10000h" + }, + "maxSignals":100, + "riskScore":21, + "riskScoreMapping":[ + + ], + "severity":"low", + "severityMapping":[ + + ], + "threat":[ + + ], + "to":"now", + "references":[ + + ], + "version":4, + "exceptionsList":[ + ], + "type":"query", + "language":"kuery", + "index":[ + "apm-*-transaction*", + "traces-apm*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "logs-*", + "packetbeat-*", + "winlogbeat-*", + "test-index-3" + ], + "query":"*:*", + "filters":[ + + ] + }, + "schedule":{ + "interval":"1m" + }, + "enabled":true, + "actions":[ + + ], + "throttle":null, + "apiKeyOwner":null, + "apiKey":null, + "createdBy":"elastic", + "updatedBy":"elastic", + "createdAt":"2021-07-27T20:42:55.896Z", + "muteAll":false, + "mutedInstanceIds":[ + + ], + "scheduledTaskId":null, + "tags":[ + "__internal_rule_id:364e3fed-6328-416b-bb85-c08265088f41", + "__internal_immutable:false", + "test-tag-1", + "foo-tag" + ] + }, + "type":"alert", + "migrationVersion":{ + "alert":"8.2.0" + }, + "updated_at":"2021-08-13T23:00:11.985Z", + "references":[ + + ] + } + } } \ No newline at end of file From caedd861b0958f74f954a9e188db8c3884377a29 Mon Sep 17 00:00:00 2001 From: Kyle Pollich Date: Tue, 26 Apr 2022 12:49:07 -0400 Subject: [PATCH 02/78] Fix open API docs for excludeInstallStatus query param (#130986) --- .../plugins/fleet/common/openapi/bundled.json | 559 ++++++++++++++---- .../plugins/fleet/common/openapi/bundled.yaml | 9 +- .../common/openapi/paths/epm@packages.yaml | 8 +- 3 files changed, 459 insertions(+), 117 deletions(-) diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index 77dd66a5c20d1..b3c37f5e567c3 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -115,7 +115,10 @@ "type": "string" } }, - "required": ["name", "version"] + "required": [ + "name", + "version" + ] } } } @@ -292,7 +295,17 @@ }, "operationId": "list-all-packages" }, - "parameters": [] + "parameters": [ + { + "in": "query", + "name": "excludeInstallStatus", + "schema": { + "type": "boolean", + "default": false + }, + "description": "Whether to exclude the install status of each package. Enabling this option will opt in to caching for the response via `cache-control` headers. If you don't need up-to-date installation info for a package, and are querying for a list of available packages, providing this flag can improve performance substantially." + } + ] }, "/epm/packages/_bulk": { "post": { @@ -337,13 +350,21 @@ "properties": { "status": { "type": "string", - "enum": ["installed", "installing", "install_failed", "not_installed"] + "enum": [ + "installed", + "installing", + "install_failed", + "not_installed" + ] }, "savedObject": { "type": "string" } }, - "required": ["status", "savedObject"] + "required": [ + "status", + "savedObject" + ] } ] } @@ -399,11 +420,16 @@ ] } }, - "required": ["id", "type"] + "required": [ + "id", + "type" + ] } } }, - "required": ["response"] + "required": [ + "response" + ] } } } @@ -462,11 +488,16 @@ ] } }, - "required": ["id", "type"] + "required": [ + "id", + "type" + ] } } }, - "required": ["response"] + "required": [ + "response" + ] } } } @@ -518,13 +549,21 @@ "properties": { "status": { "type": "string", - "enum": ["installed", "installing", "install_failed", "not_installed"] + "enum": [ + "installed", + "installing", + "install_failed", + "not_installed" + ] }, "savedObject": { "type": "string" } }, - "required": ["status", "savedObject"] + "required": [ + "status", + "savedObject" + ] } ] } @@ -587,7 +626,10 @@ ] } }, - "required": ["id", "type"] + "required": [ + "id", + "type" + ] } }, "_meta": { @@ -595,12 +637,18 @@ "properties": { "install_source": { "type": "string", - "enum": ["registry", "upload", "bundled"] + "enum": [ + "registry", + "upload", + "bundled" + ] } } } }, - "required": ["items"] + "required": [ + "items" + ] } } } @@ -661,11 +709,16 @@ ] } }, - "required": ["id", "type"] + "required": [ + "id", + "type" + ] } } }, - "required": ["items"] + "required": [ + "items" + ] } } } @@ -736,11 +789,16 @@ ] } }, - "required": ["id", "type"] + "required": [ + "id", + "type" + ] } } }, - "required": ["items"] + "required": [ + "items" + ] } } } @@ -840,7 +898,9 @@ "$ref": "#/components/schemas/package_usage_stats" } }, - "required": ["response"] + "required": [ + "response" + ] } } } @@ -915,7 +975,10 @@ "type": "string" } }, - "required": ["admin_username", "admin_password"] + "required": [ + "admin_username", + "admin_password" + ] } } } @@ -1156,7 +1219,9 @@ "type": "string" } }, - "required": ["success"] + "required": [ + "success" + ] } } } @@ -1217,7 +1282,9 @@ "$ref": "#/components/schemas/agent" } }, - "required": ["item"] + "required": [ + "item" + ] } } } @@ -1240,7 +1307,9 @@ "$ref": "#/components/schemas/agent" } }, - "required": ["item"] + "required": [ + "item" + ] } } } @@ -1266,10 +1335,14 @@ "properties": { "action": { "type": "string", - "enum": ["deleted"] + "enum": [ + "deleted" + ] } }, - "required": ["action"] + "required": [ + "action" + ] } } } @@ -1389,7 +1462,9 @@ "type": "string" } }, - "required": ["policy_id"] + "required": [ + "policy_id" + ] } } } @@ -1436,7 +1511,9 @@ }, "statusCode": { "type": "number", - "enum": [400] + "enum": [ + 400 + ] } } } @@ -1544,7 +1621,9 @@ "type": "string" } }, - "required": ["success"] + "required": [ + "success" + ] } } } @@ -1580,7 +1659,10 @@ ] } }, - "required": ["policy_id", "agents"] + "required": [ + "policy_id", + "agents" + ] } } } @@ -1608,7 +1690,9 @@ "type": "string" } }, - "required": ["success"] + "required": [ + "success" + ] } } } @@ -1647,7 +1731,9 @@ ] } }, - "required": ["agents"] + "required": [ + "agents" + ] } } } @@ -1682,7 +1768,12 @@ "type": "number" } }, - "required": ["items", "total", "page", "perPage"] + "required": [ + "items", + "total", + "page", + "perPage" + ] } } } @@ -1766,7 +1857,9 @@ "$ref": "#/components/schemas/agent_policy" } }, - "required": ["item"] + "required": [ + "item" + ] } } } @@ -1791,7 +1884,9 @@ "$ref": "#/components/schemas/agent_policy" } }, - "required": ["item"] + "required": [ + "item" + ] } } } @@ -1845,7 +1940,9 @@ "$ref": "#/components/schemas/agent_policy" } }, - "required": ["item"] + "required": [ + "item" + ] } } } @@ -1864,7 +1961,9 @@ "type": "string" } }, - "required": ["name"] + "required": [ + "name" + ] } } }, @@ -1891,7 +1990,10 @@ "type": "boolean" } }, - "required": ["id", "success"] + "required": [ + "id", + "success" + ] } } } @@ -1907,7 +2009,9 @@ "type": "string" } }, - "required": ["agentPolicyId"] + "required": [ + "agentPolicyId" + ] } } } @@ -1983,7 +2087,12 @@ "type": "number" } }, - "required": ["items", "page", "perPage", "total"] + "required": [ + "items", + "page", + "perPage", + "total" + ] } } } @@ -2009,7 +2118,9 @@ }, "action": { "type": "string", - "enum": ["created"] + "enum": [ + "created" + ] } } } @@ -2052,7 +2163,9 @@ "$ref": "#/components/schemas/enrollment_api_key" } }, - "required": ["item"] + "required": [ + "item" + ] } } } @@ -2074,10 +2187,14 @@ "properties": { "action": { "type": "string", - "enum": ["deleted"] + "enum": [ + "deleted" + ] } }, - "required": ["action"] + "required": [ + "action" + ] } } } @@ -2127,7 +2244,12 @@ "type": "number" } }, - "required": ["items", "page", "perPage", "total"] + "required": [ + "items", + "page", + "perPage", + "total" + ] } } } @@ -2152,7 +2274,9 @@ }, "action": { "type": "string", - "enum": ["created"] + "enum": [ + "created" + ] } } } @@ -2194,7 +2318,9 @@ "$ref": "#/components/schemas/enrollment_api_key" } }, - "required": ["item"] + "required": [ + "item" + ] } } } @@ -2215,10 +2341,14 @@ "properties": { "action": { "type": "string", - "enum": ["deleted"] + "enum": [ + "deleted" + ] } }, - "required": ["action"] + "required": [ + "action" + ] } } } @@ -2260,7 +2390,9 @@ "type": "number" } }, - "required": ["items"] + "required": [ + "items" + ] } } } @@ -2286,7 +2418,9 @@ "$ref": "#/components/schemas/package_policy" } }, - "required": ["item"] + "required": [ + "item" + ] } } } @@ -2348,7 +2482,9 @@ "type": "boolean" } }, - "required": ["packagePolicyIds"] + "required": [ + "packagePolicyIds" + ] } } } @@ -2373,7 +2509,10 @@ "type": "boolean" } }, - "required": ["id", "success"] + "required": [ + "id", + "success" + ] } } } @@ -2404,7 +2543,9 @@ } } }, - "required": ["packagePolicyIds"] + "required": [ + "packagePolicyIds" + ] } } } @@ -2429,7 +2570,10 @@ "type": "boolean" } }, - "required": ["id", "success"] + "required": [ + "id", + "success" + ] } } } @@ -2458,7 +2602,9 @@ "type": "string" } }, - "required": ["packagePolicyIds"] + "required": [ + "packagePolicyIds" + ] } } } @@ -2483,7 +2629,9 @@ "$ref": "#/components/schemas/upgrade_agent_diff" } }, - "required": ["hasErrors"] + "required": [ + "hasErrors" + ] } } } @@ -2508,7 +2656,9 @@ "$ref": "#/components/schemas/package_policy" } }, - "required": ["item"] + "required": [ + "item" + ] } } } @@ -2553,7 +2703,10 @@ "type": "boolean" } }, - "required": ["item", "sucess"] + "required": [ + "item", + "sucess" + ] } } } @@ -2636,7 +2789,9 @@ }, "type": { "type": "string", - "enum": ["elasticsearch"] + "enum": [ + "elasticsearch" + ] }, "is_default": { "type": "boolean" @@ -2657,7 +2812,10 @@ "type": "string" } }, - "required": ["name", "type"] + "required": [ + "name", + "type" + ] } } } @@ -2681,7 +2839,9 @@ "$ref": "#/components/schemas/output" } }, - "required": ["item"] + "required": [ + "item" + ] } } } @@ -2714,7 +2874,9 @@ "type": "string" } }, - "required": ["id"] + "required": [ + "id" + ] } } } @@ -2740,7 +2902,9 @@ }, "type": { "type": "string", - "enum": ["elasticsearch"] + "enum": [ + "elasticsearch" + ] }, "is_default": { "type": "boolean" @@ -2764,7 +2928,10 @@ "type": "string" } }, - "required": ["name", "type"] + "required": [ + "name", + "type" + ] } } } @@ -2781,7 +2948,9 @@ "$ref": "#/components/schemas/output" } }, - "required": ["item"] + "required": [ + "item" + ] } } } @@ -2900,11 +3069,17 @@ "type": "string" } }, - "required": ["name", "message"] + "required": [ + "name", + "message" + ] } } }, - "required": ["isInitialized", "nonFatalErrors"] + "required": [ + "isInitialized", + "nonFatalErrors" + ] }, "preconfigured_agent_policies": { "title": "Preconfigured agent policies", @@ -2926,7 +3101,10 @@ "type": "array", "items": { "type": "string", - "enum": ["logs", "metrics"] + "enum": [ + "logs", + "metrics" + ] } }, "namespace": { @@ -3023,14 +3201,20 @@ } } }, - "required": ["type"] + "required": [ + "type" + ] } } } } } }, - "required": ["name", "namespace", "package_policies"] + "required": [ + "name", + "namespace", + "package_policies" + ] }, "settings": { "title": "Settings", @@ -3052,7 +3236,10 @@ } } }, - "required": ["fleet_server_hosts", "id"] + "required": [ + "fleet_server_hosts", + "id" + ] }, "fleet_settings_response": { "title": "Fleet settings response", @@ -3062,7 +3249,9 @@ "$ref": "#/components/schemas/settings" } }, - "required": ["item"] + "required": [ + "item" + ] }, "get_categories_response": { "title": "Get categories response", @@ -3084,7 +3273,11 @@ "type": "number" } }, - "required": ["id", "title", "count"] + "required": [ + "id", + "title", + "count" + ] } }, "items": { @@ -3102,11 +3295,17 @@ "type": "number" } }, - "required": ["id", "title", "count"] + "required": [ + "id", + "title", + "count" + ] } } }, - "required": ["items"] + "required": [ + "items" + ] }, "search_result": { "title": "Search result", @@ -3173,7 +3372,9 @@ } } }, - "required": ["items"] + "required": [ + "items" + ] }, "bulk_install_packages_response": { "title": "Bulk install packages response", @@ -3209,7 +3410,9 @@ } } }, - "required": ["items"] + "required": [ + "items" + ] }, "package_info": { "title": "Package information", @@ -3289,7 +3492,10 @@ "type": "string" } }, - "required": ["src", "path"] + "required": [ + "src", + "path" + ] } }, "icons": { @@ -3339,7 +3545,10 @@ "type": "string" } }, - "required": ["name", "default"] + "required": [ + "name", + "default" + ] } }, "type": { @@ -3349,7 +3558,14 @@ "type": "string" } }, - "required": ["title", "name", "release", "ingeset_pipeline", "type", "package"] + "required": [ + "title", + "name", + "release", + "ingeset_pipeline", + "type", + "package" + ] } }, "download": { @@ -3411,7 +3627,9 @@ "type": "integer" } }, - "required": ["agent_policy_count"] + "required": [ + "agent_policy_count" + ] }, "fleet_status_response": { "title": "Fleet status response", @@ -3424,23 +3642,38 @@ "type": "array", "items": { "type": "string", - "enum": ["tls_required", "api_keys", "fleet_admin_user", "fleet_server"] + "enum": [ + "tls_required", + "api_keys", + "fleet_admin_user", + "fleet_server" + ] } }, "missing_optional_features": { "type": "array", "items": { "type": "string", - "enum": ["encrypted_saved_object_encryption_key_required"] + "enum": [ + "encrypted_saved_object_encryption_key_required" + ] } } }, - "required": ["isReady", "missing_requirements", "missing_optional_features"] + "required": [ + "isReady", + "missing_requirements", + "missing_optional_features" + ] }, "agent_type": { "type": "string", "title": "Agent type", - "enum": ["PERMANENT", "EPHEMERAL", "TEMPORARY"] + "enum": [ + "PERMANENT", + "EPHEMERAL", + "TEMPORARY" + ] }, "agent_metadata": { "title": "Agent metadata", @@ -3449,7 +3682,13 @@ "agent_status": { "type": "string", "title": "Agent status", - "enum": ["offline", "error", "online", "inactive", "warning"] + "enum": [ + "offline", + "error", + "online", + "inactive", + "warning" + ] }, "agent": { "title": "Agent", @@ -3504,7 +3743,13 @@ "type": "string" } }, - "required": ["type", "active", "enrolled_at", "id", "status"] + "required": [ + "type", + "active", + "enrolled_at", + "id", + "status" + ] }, "get_agents_response": { "title": "Get Agent response", @@ -3533,7 +3778,12 @@ "type": "number" } }, - "required": ["items", "total", "page", "perPage"] + "required": [ + "items", + "total", + "page", + "perPage" + ] }, "bulk_upgrade_agents": { "title": "Bulk upgrade agents", @@ -3559,7 +3809,10 @@ ] } }, - "required": ["agents", "version"] + "required": [ + "agents", + "version" + ] }, "upgrade_agent": { "title": "Upgrade agent", @@ -3571,7 +3824,9 @@ "type": "string" } }, - "required": ["version"] + "required": [ + "version" + ] }, { "type": "object", @@ -3583,7 +3838,9 @@ "type": "string" } }, - "required": ["version"] + "required": [ + "version" + ] } ] }, @@ -3600,7 +3857,12 @@ }, "type": { "type": "string", - "enum": ["POLICY_CHANGE", "UNENROLL", "UPGRADE", "POLICY_REASSIGN"] + "enum": [ + "POLICY_CHANGE", + "UNENROLL", + "UPGRADE", + "POLICY_REASSIGN" + ] } } }, @@ -3614,7 +3876,12 @@ "properties": { "log_level": { "type": "string", - "enum": ["debug", "info", "warning", "error"] + "enum": [ + "debug", + "info", + "warning", + "error" + ] } } } @@ -3642,7 +3909,10 @@ "type": "array", "items": { "type": "string", - "enum": ["metrics", "logs"] + "enum": [ + "metrics", + "logs" + ] } }, "data_output_id": { @@ -3654,7 +3924,10 @@ "nullable": true } }, - "required": ["name", "namespace"] + "required": [ + "name", + "namespace" + ] }, "new_package_policy": { "title": "New package policy", @@ -3677,7 +3950,10 @@ "type": "string" } }, - "required": ["name", "version"] + "required": [ + "name", + "version" + ] }, "namespace": { "type": "string" @@ -3713,7 +3989,10 @@ "type": "object" } }, - "required": ["type", "enabled"] + "required": [ + "type", + "enabled" + ] } }, "policy_id": { @@ -3726,7 +4005,10 @@ "type": "string" } }, - "required": ["inputs", "name"] + "required": [ + "inputs", + "name" + ] }, "package_policy": { "title": "Package policy", @@ -3745,7 +4027,10 @@ "items": {} } }, - "required": ["id", "revision"] + "required": [ + "id", + "revision" + ] }, { "$ref": "#/components/schemas/new_package_policy" @@ -3765,7 +4050,10 @@ }, "status": { "type": "string", - "enum": ["active", "inactive"] + "enum": [ + "active", + "inactive" + ] }, "packagePolicies": { "oneOf": [ @@ -3803,7 +4091,10 @@ "type": "number" } }, - "required": ["id", "status"] + "required": [ + "id", + "status" + ] } ] }, @@ -3880,7 +4171,13 @@ "type": "string" } }, - "required": ["id", "api_key_id", "api_key", "active", "created_at"] + "required": [ + "id", + "api_key_id", + "api_key", + "active", + "created_at" + ] }, "upgrade_diff": { "title": "Package policy Upgrade dryrun", @@ -3946,10 +4243,16 @@ "type": "string" } }, - "required": ["dataset", "type"] + "required": [ + "dataset", + "type" + ] } }, - "required": ["id", "data_stream"] + "required": [ + "id", + "data_stream" + ] } ] }, @@ -3979,7 +4282,9 @@ "type": "string" } }, - "required": ["namespace"] + "required": [ + "namespace" + ] }, "use_output": { "type": "string" @@ -3998,7 +4303,10 @@ "type": "string" } }, - "required": ["name", "version"] + "required": [ + "name", + "version" + ] } } }, @@ -4006,7 +4314,14 @@ "$ref": "#/components/schemas/full_agent_policy_input_stream" } }, - "required": ["id", "name", "revision", "type", "data_stream", "use_output"] + "required": [ + "id", + "name", + "revision", + "type", + "data_stream", + "use_output" + ] } ] }, @@ -4044,7 +4359,11 @@ "type": "string" } }, - "required": ["name", "title", "version"] + "required": [ + "name", + "title", + "version" + ] }, "namespace": { "type": "string" @@ -4080,7 +4399,11 @@ "type": "object" } }, - "required": ["type", "enabled", "streams"] + "required": [ + "type", + "enabled", + "streams" + ] } }, "policy_id": { @@ -4096,7 +4419,12 @@ "type": "boolean" } }, - "required": ["name", "namespace", "policy_id", "enabled"] + "required": [ + "name", + "namespace", + "policy_id", + "enabled" + ] }, "output": { "title": "Output", @@ -4116,7 +4444,9 @@ }, "type": { "type": "string", - "enum": ["elasticsearch"] + "enum": [ + "elasticsearch" + ] }, "hosts": { "type": "array", @@ -4154,7 +4484,12 @@ } } }, - "required": ["id", "is_default", "name", "type"] + "required": [ + "id", + "is_default", + "name", + "type" + ] } } }, @@ -4163,4 +4498,4 @@ "basicAuth": [] } ] -} +} \ No newline at end of file diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index 011a242df7fdc..36175ffc59a88 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -186,13 +186,16 @@ paths: operationId: list-all-packages parameters: - in: query - name: includeInstallStatus + name: excludeInstallStatus schema: type: boolean default: false description: >- - Whether to include the install status of each package. Defaults to - false to allow for caching of package requests. + Whether to exclude the install status of each package. Enabling this + option will opt in to caching for the response via `cache-control` + headers. If you don't need up-to-date installation info for a package, + and are querying for a list of available packages, providing this flag + can improve performance substantially. /epm/packages/_bulk: post: summary: Packages - Bulk install diff --git a/x-pack/plugins/fleet/common/openapi/paths/epm@packages.yaml b/x-pack/plugins/fleet/common/openapi/paths/epm@packages.yaml index 295ab298745fe..9c29b9d18357c 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/epm@packages.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/epm@packages.yaml @@ -11,8 +11,12 @@ get: operationId: list-all-packages parameters: - in: query - name: includeInstallStatus + name: excludeInstallStatus schema: type: boolean default: false - description: Whether to include the install status of each package. Defaults to false to allow for caching of package requests. + description: >- + Whether to exclude the install status of each package. Enabling this option will opt in to + caching for the response via `cache-control` headers. If you don't need up-to-date installation + info for a package, and are querying for a list of available packages, providing this flag can + improve performance substantially. From 7ff1ffca5e8a684157bcbba59df4456a8afe190f Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Tue, 26 Apr 2022 13:07:32 -0400 Subject: [PATCH 03/78] [CI] Split OSS CI Group 11 (#130927) --- .buildkite/pipelines/es_snapshots/verify.yml | 2 +- .buildkite/pipelines/on_merge.yml | 2 +- .buildkite/pipelines/pull_request/base.yml | 2 +- .ci/ci_groups.yml | 1 + test/functional/apps/visualize/index.ts | 7 ++++++- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.buildkite/pipelines/es_snapshots/verify.yml b/.buildkite/pipelines/es_snapshots/verify.yml index 23ec38085c3d9..a4a6b1303f70d 100755 --- a/.buildkite/pipelines/es_snapshots/verify.yml +++ b/.buildkite/pipelines/es_snapshots/verify.yml @@ -42,7 +42,7 @@ steps: - command: .buildkite/scripts/steps/functional/oss_cigroup.sh label: 'OSS CI Group' - parallelism: 11 + parallelism: 12 agents: queue: ci-group-4d depends_on: build diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index 8702493d9f4cf..8b94f4d7c22c7 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -66,7 +66,7 @@ steps: - command: .buildkite/scripts/steps/functional/oss_cigroup.sh label: 'OSS CI Group' - parallelism: 11 + parallelism: 12 agents: queue: n2-4-spot depends_on: build diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index 658d855d86cfd..fc12a96964fb2 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -32,7 +32,7 @@ steps: - command: .buildkite/scripts/steps/functional/oss_cigroup.sh label: 'OSS CI Group' - parallelism: 11 + parallelism: 12 agents: queue: n2-4-spot depends_on: build diff --git a/.ci/ci_groups.yml b/.ci/ci_groups.yml index c3786f299d4c0..508f118ce9dd7 100644 --- a/.ci/ci_groups.yml +++ b/.ci/ci_groups.yml @@ -10,6 +10,7 @@ root: - ciGroup9 - ciGroup10 - ciGroup11 + - ciGroup12 xpack: - ciGroup1 diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts index 5ccb59e17880e..d68fb4b253123 100644 --- a/test/functional/apps/visualize/index.ts +++ b/test/functional/apps/visualize/index.ts @@ -97,8 +97,13 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { describe('visualize ciGroup11', function () { this.tags('ciGroup11'); - loadTestFile(require.resolve('./_tag_cloud')); loadTestFile(require.resolve('./_tsvb_time_series')); + }); + + describe('visualize ciGroup12', function () { + this.tags('ciGroup12'); + + loadTestFile(require.resolve('./_tag_cloud')); loadTestFile(require.resolve('./_tsvb_markdown')); loadTestFile(require.resolve('./_tsvb_table')); loadTestFile(require.resolve('./_vega_chart')); From 181626b9c665327226cbb183b5909f2c26270e1d Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Tue, 26 Apr 2022 13:32:08 -0400 Subject: [PATCH 04/78] [CI] Split default ciGroup24 up a bit (#130997) --- .../tests/exception_operators_data_types/index.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/index.ts index 85cc484146032..ded3df8b6716c 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/index.ts @@ -22,13 +22,23 @@ export default ({ loadTestFile }: FtrProviderContext): void => { describe('', function () { this.tags('ciGroup24'); - loadTestFile(require.resolve('./ip')); - loadTestFile(require.resolve('./ip_array')); loadTestFile(require.resolve('./keyword')); loadTestFile(require.resolve('./keyword_array')); loadTestFile(require.resolve('./long')); loadTestFile(require.resolve('./text')); loadTestFile(require.resolve('./text_array')); }); + + describe('', function () { + this.tags('ciGroup16'); + + loadTestFile(require.resolve('./ip')); + }); + + describe('', function () { + this.tags('ciGroup21'); + + loadTestFile(require.resolve('./ip_array')); + }); }); }; From 26217f3a7a0c266bf9ddf079633a0fcc10877e27 Mon Sep 17 00:00:00 2001 From: liza-mae Date: Tue, 26 Apr 2022 11:52:36 -0600 Subject: [PATCH 05/78] Upgrade patch reporting issue, add more tests, reorganize files (#130839) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- test/functional/page_objects/home_page.ts | 15 +++++ .../functional/page_objects/graph_page.ts | 5 ++ .../upgrade/apps/canvas/canvas_smoke_tests.ts | 20 +++---- .../apps/canvas/{index.js => index.ts} | 6 +- .../apps/dashboard/dashboard_smoke_tests.ts | 4 +- .../apps/dashboard/{index.js => index.ts} | 6 +- .../apps/discover/discover_smoke_tests.ts | 57 +++++++++++++++++++ x-pack/test/upgrade/apps/discover/index.ts | 14 +++++ .../upgrade/apps/graph/graph_smoke_tests.ts | 45 +++++++++++++++ x-pack/test/upgrade/apps/graph/index.ts | 14 +++++ x-pack/test/upgrade/apps/logs/index.ts | 14 +++++ .../upgrade/apps/logs/logs_smoke_tests.ts | 39 +++++++++++++ .../upgrade/apps/maps/{index.js => index.ts} | 6 +- .../upgrade/apps/maps/maps_smoke_tests.ts | 8 +-- .../apps/reporting/{index.js => index.ts} | 6 +- .../apps/reporting/reporting_smoke_tests.ts | 23 +++++--- x-pack/test/upgrade/config.ts | 9 ++- x-pack/test/upgrade/ftr_provider_context.d.ts | 2 +- x-pack/test/upgrade/page_objects.ts | 10 ---- .../{services.ts => services/index.ts} | 4 +- .../{ => services}/maps_upgrade_services.ts | 2 +- .../reporting_upgrade_services.ts} | 8 +-- 22 files changed, 265 insertions(+), 52 deletions(-) rename x-pack/test/upgrade/apps/canvas/{index.js => index.ts} (72%) rename x-pack/test/upgrade/apps/dashboard/{index.js => index.ts} (72%) create mode 100644 x-pack/test/upgrade/apps/discover/discover_smoke_tests.ts create mode 100644 x-pack/test/upgrade/apps/discover/index.ts create mode 100644 x-pack/test/upgrade/apps/graph/graph_smoke_tests.ts create mode 100644 x-pack/test/upgrade/apps/graph/index.ts create mode 100644 x-pack/test/upgrade/apps/logs/index.ts create mode 100644 x-pack/test/upgrade/apps/logs/logs_smoke_tests.ts rename x-pack/test/upgrade/apps/maps/{index.js => index.ts} (72%) rename x-pack/test/upgrade/apps/reporting/{index.js => index.ts} (72%) delete mode 100644 x-pack/test/upgrade/page_objects.ts rename x-pack/test/upgrade/{services.ts => services/index.ts} (73%) rename x-pack/test/upgrade/{ => services}/maps_upgrade_services.ts (97%) rename x-pack/test/upgrade/{reporting_services.ts => services/reporting_upgrade_services.ts} (95%) diff --git a/test/functional/page_objects/home_page.ts b/test/functional/page_objects/home_page.ts index 235afa27f0c82..1e3e6a9634f4c 100644 --- a/test/functional/page_objects/home_page.ts +++ b/test/functional/page_objects/home_page.ts @@ -93,6 +93,21 @@ export class HomePageObject extends FtrService { await this.find.clickByLinkText('Map'); } + async launchSampleLogs(id: string) { + await this.launchSampleDataSet(id); + await this.find.clickByLinkText('Logs'); + } + + async launchSampleGraph(id: string) { + await this.launchSampleDataSet(id); + await this.find.clickByLinkText('Graph'); + } + + async launchSampleML(id: string) { + await this.launchSampleDataSet(id); + await this.find.clickByLinkText('ML jobs'); + } + async launchSampleDataSet(id: string) { await this.addSampleDataSet(id); await this.common.closeToastIfExists(); diff --git a/x-pack/test/functional/page_objects/graph_page.ts b/x-pack/test/functional/page_objects/graph_page.ts index 1203d4077b5b8..aec58d27acbbe 100644 --- a/x-pack/test/functional/page_objects/graph_page.ts +++ b/x-pack/test/functional/page_objects/graph_page.ts @@ -283,4 +283,9 @@ export class GraphPageObject extends FtrService { const el = await this.find.byCssSelector('small.gphLinkSummary__term--2'); return await el.getVisibleText(); } + + async getAllGraphNodes() { + const el = await this.find.allByCssSelector('.gphNode'); + return el.length; + } } diff --git a/x-pack/test/upgrade/apps/canvas/canvas_smoke_tests.ts b/x-pack/test/upgrade/apps/canvas/canvas_smoke_tests.ts index b12b751e94c07..7de0e1247f01a 100644 --- a/x-pack/test/upgrade/apps/canvas/canvas_smoke_tests.ts +++ b/x-pack/test/upgrade/apps/canvas/canvas_smoke_tests.ts @@ -14,7 +14,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const browser = getService('browser'); - describe('canvas smoke tests', function describeIndexTests() { + describe('upgrade canvas smoke tests', function describeIndexTests() { const spaces = [ { space: 'default', basePath: '' }, { space: 'automation', basePath: 's/automation' }, @@ -28,17 +28,17 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { ]; spaces.forEach(({ space, basePath }) => { - describe('space ' + space, () => { - beforeEach(async () => { - await PageObjects.common.navigateToActualUrl('home', '/tutorial_directory/sampleData', { - basePath, - }); - await PageObjects.header.waitUntilLoadingHasFinished(); - }); - canvasTests.forEach(({ name, numElements, page }) => { - it('renders elements on workpad ' + name + ' page ' + page, async () => { + canvasTests.forEach(({ name, numElements, page }) => { + describe('space: ' + space, () => { + before(async () => { + await PageObjects.common.navigateToActualUrl('home', '/tutorial_directory/sampleData', { + basePath, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.home.launchSampleCanvas(name); await PageObjects.header.waitUntilLoadingHasFinished(); + }); + it('renders elements on workpad ' + name + ' page ' + page, async () => { const currentUrl = await browser.getCurrentUrl(); const [, hash] = currentUrl.split('#/'); if (hash.length === 0) { diff --git a/x-pack/test/upgrade/apps/canvas/index.js b/x-pack/test/upgrade/apps/canvas/index.ts similarity index 72% rename from x-pack/test/upgrade/apps/canvas/index.js rename to x-pack/test/upgrade/apps/canvas/index.ts index 0ecc2e98ea67a..f28bff9a1820c 100644 --- a/x-pack/test/upgrade/apps/canvas/index.js +++ b/x-pack/test/upgrade/apps/canvas/index.ts @@ -5,8 +5,10 @@ * 2.0. */ -export default ({ loadTestFile }) => { +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { describe('upgrade', function () { loadTestFile(require.resolve('./canvas_smoke_tests')); }); -}; +} diff --git a/x-pack/test/upgrade/apps/dashboard/dashboard_smoke_tests.ts b/x-pack/test/upgrade/apps/dashboard/dashboard_smoke_tests.ts index 2f35f0e1e12d3..b87ec4dfe4159 100644 --- a/x-pack/test/upgrade/apps/dashboard/dashboard_smoke_tests.ts +++ b/x-pack/test/upgrade/apps/dashboard/dashboard_smoke_tests.ts @@ -18,7 +18,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const browser = getService('browser'); - describe('dashboard smoke tests', function describeIndexTests() { + describe('upgrade dashboard smoke tests', function describeIndexTests() { const spaces = [ { space: 'default', basePath: '' }, { space: 'automation', basePath: 's/automation' }, @@ -31,7 +31,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { ]; spaces.forEach(({ space, basePath }) => { - describe('space ' + space, () => { + describe('space: ' + space, () => { beforeEach(async () => { await PageObjects.common.navigateToActualUrl('home', '/tutorial_directory/sampleData', { basePath, diff --git a/x-pack/test/upgrade/apps/dashboard/index.js b/x-pack/test/upgrade/apps/dashboard/index.ts similarity index 72% rename from x-pack/test/upgrade/apps/dashboard/index.js rename to x-pack/test/upgrade/apps/dashboard/index.ts index b12d1270f79f9..1f380a18dfb70 100644 --- a/x-pack/test/upgrade/apps/dashboard/index.js +++ b/x-pack/test/upgrade/apps/dashboard/index.ts @@ -5,8 +5,10 @@ * 2.0. */ -export default ({ loadTestFile }) => { +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { describe('upgrade', function () { loadTestFile(require.resolve('./dashboard_smoke_tests')); }); -}; +} diff --git a/x-pack/test/upgrade/apps/discover/discover_smoke_tests.ts b/x-pack/test/upgrade/apps/discover/discover_smoke_tests.ts new file mode 100644 index 0000000000000..150458919d41d --- /dev/null +++ b/x-pack/test/upgrade/apps/discover/discover_smoke_tests.ts @@ -0,0 +1,57 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'header', 'home', 'discover', 'timePicker']); + + describe('upgrade discover smoke tests', function describeIndexTests() { + const spaces = [ + { space: 'default', basePath: '' }, + { space: 'automation', basePath: 's/automation' }, + ]; + + const discoverTests = [ + { name: 'kibana_sample_data_flights', timefield: true, hits: '' }, + { name: 'kibana_sample_data_logs', timefield: true, hits: '' }, + { name: 'kibana_sample_data_ecommerce', timefield: true, hits: '' }, + ]; + + spaces.forEach(({ space, basePath }) => { + discoverTests.forEach(({ name, timefield, hits }) => { + describe('space: ' + space + ', name: ' + name, () => { + before(async () => { + await PageObjects.common.navigateToApp('discover', { + basePath, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.selectIndexPattern(name); + await PageObjects.discover.waitUntilSearchingHasFinished(); + if (timefield) { + await PageObjects.timePicker.setCommonlyUsedTime('Last_24 hours'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + } + }); + it('shows hit count greater than zero', async () => { + const hitCount = await PageObjects.discover.getHitCount(); + if (hits === '') { + expect(hitCount).to.be.greaterThan(0); + } else { + expect(hitCount).to.be.equal(hits); + } + }); + it('shows table rows not empty', async () => { + const tableRows = await PageObjects.discover.getDocTableRows(); + expect(tableRows.length).to.be.greaterThan(0); + }); + }); + }); + }); + }); +} diff --git a/x-pack/test/upgrade/apps/discover/index.ts b/x-pack/test/upgrade/apps/discover/index.ts new file mode 100644 index 0000000000000..18fdd6992b892 --- /dev/null +++ b/x-pack/test/upgrade/apps/discover/index.ts @@ -0,0 +1,14 @@ +/* + * 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 '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('upgrade', function () { + loadTestFile(require.resolve('./discover_smoke_tests')); + }); +} diff --git a/x-pack/test/upgrade/apps/graph/graph_smoke_tests.ts b/x-pack/test/upgrade/apps/graph/graph_smoke_tests.ts new file mode 100644 index 0000000000000..cca4e220c7aa5 --- /dev/null +++ b/x-pack/test/upgrade/apps/graph/graph_smoke_tests.ts @@ -0,0 +1,45 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'header', 'home', 'graph']); + + describe('upgrade graph smoke tests', function describeIndexTests() { + const spaces = [ + { space: 'default', basePath: '' }, + { space: 'automation', basePath: 's/automation' }, + ]; + + const graphTests = [ + { name: 'flights', numNodes: 91 }, + { name: 'logs', numNodes: 27 }, + { name: 'ecommerce', numNodes: 12 }, + ]; + + spaces.forEach(({ space, basePath }) => { + graphTests.forEach(({ name, numNodes }) => { + describe('space: ' + space, () => { + before(async () => { + await PageObjects.common.navigateToActualUrl('home', '/tutorial_directory/sampleData', { + basePath, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.home.launchSampleGraph(name); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + it('renders graph for ' + name, async () => { + const elements = await PageObjects.graph.getAllGraphNodes(); + expect(elements).to.be.equal(numNodes); + }); + }); + }); + }); + }); +} diff --git a/x-pack/test/upgrade/apps/graph/index.ts b/x-pack/test/upgrade/apps/graph/index.ts new file mode 100644 index 0000000000000..649b1a740bb6c --- /dev/null +++ b/x-pack/test/upgrade/apps/graph/index.ts @@ -0,0 +1,14 @@ +/* + * 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 '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('upgrade', function () { + loadTestFile(require.resolve('./graph_smoke_tests')); + }); +} diff --git a/x-pack/test/upgrade/apps/logs/index.ts b/x-pack/test/upgrade/apps/logs/index.ts new file mode 100644 index 0000000000000..ed4181f75d76d --- /dev/null +++ b/x-pack/test/upgrade/apps/logs/index.ts @@ -0,0 +1,14 @@ +/* + * 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 '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('upgrade', function () { + loadTestFile(require.resolve('./logs_smoke_tests')); + }); +} diff --git a/x-pack/test/upgrade/apps/logs/logs_smoke_tests.ts b/x-pack/test/upgrade/apps/logs/logs_smoke_tests.ts new file mode 100644 index 0000000000000..8d9964f25422e --- /dev/null +++ b/x-pack/test/upgrade/apps/logs/logs_smoke_tests.ts @@ -0,0 +1,39 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'header', 'home']); + const logsUi = getService('logsUi'); + + describe('upgrade logs smoke tests', function describeIndexTests() { + const spaces = [ + { space: 'default', basePath: '' }, + { space: 'automation', basePath: 's/automation' }, + ]; + + spaces.forEach(({ space, basePath }) => { + describe('space: ' + space, () => { + before(async () => { + await PageObjects.common.navigateToActualUrl('home', '/tutorial_directory/sampleData', { + basePath, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.home.launchSampleLogs('logs'); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + + it('should show log streams', async () => { + const logStreamEntries = await logsUi.logStreamPage.getStreamEntries(); + expect(logStreamEntries.length).to.be.greaterThan(100); + }); + }); + }); + }); +} diff --git a/x-pack/test/upgrade/apps/maps/index.js b/x-pack/test/upgrade/apps/maps/index.ts similarity index 72% rename from x-pack/test/upgrade/apps/maps/index.js rename to x-pack/test/upgrade/apps/maps/index.ts index 57d175a62ceb3..7fac9ae891128 100644 --- a/x-pack/test/upgrade/apps/maps/index.js +++ b/x-pack/test/upgrade/apps/maps/index.ts @@ -5,8 +5,10 @@ * 2.0. */ -export default ({ loadTestFile }) => { +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { describe('upgrade', function () { loadTestFile(require.resolve('./maps_smoke_tests')); }); -}; +} diff --git a/x-pack/test/upgrade/apps/maps/maps_smoke_tests.ts b/x-pack/test/upgrade/apps/maps/maps_smoke_tests.ts index 98e31129a3ccd..ecb000d691ab0 100644 --- a/x-pack/test/upgrade/apps/maps/maps_smoke_tests.ts +++ b/x-pack/test/upgrade/apps/maps/maps_smoke_tests.ts @@ -82,7 +82,7 @@ export default function ({ // Only update the baseline images from Jenkins session images after comparing them // These tests might fail locally because of scaling factors and resolution. - describe('maps smoke tests', function describeIndexTests() { + describe('upgrade maps smoke tests', function describeIndexTests() { const spaces = [ { space: 'default', basePath: '' }, { space: 'automation', basePath: 's/automation' }, @@ -101,7 +101,7 @@ export default function ({ }); spaces.forEach(({ space, basePath }) => { - describe('space ' + space + ' ecommerce', () => { + describe('space: ' + space + ', name: ecommerce', () => { before(async () => { await PageObjects.common.navigateToActualUrl('home', '/tutorial_directory/sampleData', { basePath, @@ -129,7 +129,7 @@ export default function ({ expect(percentDifference.toFixed(3)).to.be.lessThan(0.031); }); }); - describe('space ' + space + ' flights', () => { + describe('space: ' + space + ', name: flights', () => { before(async () => { await PageObjects.common.navigateToActualUrl('home', '/tutorial_directory/sampleData', { basePath, @@ -153,7 +153,7 @@ export default function ({ expect(percentDifference.toFixed(3)).to.be.lessThan(0.031); }); }); - describe('space ' + space + ' web logs', () => { + describe('space: ' + space + ', name: web logs', () => { before(async () => { await PageObjects.common.navigateToActualUrl('home', '/tutorial_directory/sampleData', { basePath, diff --git a/x-pack/test/upgrade/apps/reporting/index.js b/x-pack/test/upgrade/apps/reporting/index.ts similarity index 72% rename from x-pack/test/upgrade/apps/reporting/index.js rename to x-pack/test/upgrade/apps/reporting/index.ts index 5bd5081a3568c..c4c70660935ad 100644 --- a/x-pack/test/upgrade/apps/reporting/index.js +++ b/x-pack/test/upgrade/apps/reporting/index.ts @@ -5,8 +5,10 @@ * 2.0. */ -export default ({ loadTestFile }) => { +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { describe('upgrade', function () { loadTestFile(require.resolve('./reporting_smoke_tests')); }); -}; +} diff --git a/x-pack/test/upgrade/apps/reporting/reporting_smoke_tests.ts b/x-pack/test/upgrade/apps/reporting/reporting_smoke_tests.ts index 14136b23abfd5..f7b0eef003c91 100644 --- a/x-pack/test/upgrade/apps/reporting/reporting_smoke_tests.ts +++ b/x-pack/test/upgrade/apps/reporting/reporting_smoke_tests.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { parse } from 'url'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { ReportingUsageStats } from '../../reporting_services'; +import { ReportingUsageStats } from '../../services/reporting_upgrade_services'; interface UsageStats { reporting: ReportingUsageStats; @@ -21,6 +21,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); const PageObjects = getPageObjects(['common', 'header', 'home', 'dashboard', 'share']); const testSubjects = getService('testSubjects'); + const log = getService('log'); const spaces = [ { space: 'default', basePath: '' }, @@ -39,7 +40,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { { name: 'ecommerce', type: 'png', link: 'PNG Reports' }, ]; - describe('reporting smoke tests', () => { + describe('upgrade reporting smoke tests', () => { let completedReportCount: number; let usage: UsageStats; describe('initial state', () => { @@ -53,7 +54,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); spaces.forEach(({ space, basePath }) => { - describe('generate report space ' + space, () => { + describe('generate report for space ' + space, () => { beforeEach(async () => { await PageObjects.common.navigateToActualUrl('home', '/tutorial_directory/sampleData', { basePath, @@ -63,7 +64,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { completedReportCount = reportingAPI.getCompletedReportCount(usage); }); reportingTests.forEach(({ name, type, link }) => { - it('name ' + name + ' type ' + type, async () => { + it('name: ' + name + ' type: ' + type, async () => { await PageObjects.home.launchSampleDashboard(name); await PageObjects.share.openShareMenuItem(link); if (type === 'pdf_optimize') { @@ -89,9 +90,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const postUrl = await find.byXPath(`//button[descendant::*[text()='Copy POST URL']]`); await postUrl.click(); const url = await browser.getClipboardValue(); - await reportingAPI.expectAllJobsToFinishSuccessfully([ - await reportingAPI.postJob(parse(url).pathname + '?' + parse(url).query), - ]); + // Add try/catch for https://github.com/elastic/elastic-stack-testing/issues/1199 + // Waiting for job to finish sometimes gets socket hang up error, from what I + // observed during debug testing the command does complete. + // Checking expected report count will still fail if the job did not finish. + try { + await reportingAPI.expectAllJobsToFinishSuccessfully([ + await reportingAPI.postJob(parse(url).pathname + '?' + parse(url).query), + ]); + } catch (e) { + log.debug(`Error waiting for job to finish: ${e}`); + } usage = (await usageAPI.getUsageStats()) as UsageStats; reportingAPI.expectCompletedReportCount(usage, completedReportCount + 1); }); diff --git a/x-pack/test/upgrade/config.ts b/x-pack/test/upgrade/config.ts index 7722c244223cf..78d61d5239556 100644 --- a/x-pack/test/upgrade/config.ts +++ b/x-pack/test/upgrade/config.ts @@ -6,9 +6,9 @@ */ import { FtrConfigProviderContext } from '@kbn/test'; -import { pageObjects } from './page_objects'; -import { ReportingAPIProvider } from './reporting_services'; -import { MapsHelper } from './maps_upgrade_services'; +import { pageObjects } from '../functional/page_objects'; +import { ReportingAPIProvider } from './services/reporting_upgrade_services'; +import { MapsHelper } from './services/maps_upgrade_services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { const apiConfig = await readConfigFile(require.resolve('../api_integration/config')); @@ -20,6 +20,9 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { testFiles: [ require.resolve('./apps/canvas'), require.resolve('./apps/dashboard'), + require.resolve('./apps/discover'), + require.resolve('./apps/graph'), + require.resolve('./apps/logs'), require.resolve('./apps/maps'), require.resolve('./apps/reporting'), ], diff --git a/x-pack/test/upgrade/ftr_provider_context.d.ts b/x-pack/test/upgrade/ftr_provider_context.d.ts index 24f5087ef7fe2..2cd67b6698a70 100644 --- a/x-pack/test/upgrade/ftr_provider_context.d.ts +++ b/x-pack/test/upgrade/ftr_provider_context.d.ts @@ -7,7 +7,7 @@ import { GenericFtrProviderContext } from '@kbn/test'; -import { pageObjects } from './page_objects'; +import { pageObjects } from '../functional/page_objects'; import { services } from './services'; export type FtrProviderContext = GenericFtrProviderContext; diff --git a/x-pack/test/upgrade/page_objects.ts b/x-pack/test/upgrade/page_objects.ts deleted file mode 100644 index c8b0c9050dbb9..0000000000000 --- a/x-pack/test/upgrade/page_objects.ts +++ /dev/null @@ -1,10 +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 { pageObjects } from '../functional/page_objects'; - -export { pageObjects }; diff --git a/x-pack/test/upgrade/services.ts b/x-pack/test/upgrade/services/index.ts similarity index 73% rename from x-pack/test/upgrade/services.ts rename to x-pack/test/upgrade/services/index.ts index ca5c23ba335e3..cfd227cd86aff 100644 --- a/x-pack/test/upgrade/services.ts +++ b/x-pack/test/upgrade/services/index.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { services as functionalServices } from '../functional/services'; -import { services as reportingServices } from './reporting_services'; +import { services as functionalServices } from '../../functional/services'; +import { services as reportingServices } from './reporting_upgrade_services'; import { services as mapsUpgradeServices } from './maps_upgrade_services'; export const services = { diff --git a/x-pack/test/upgrade/maps_upgrade_services.ts b/x-pack/test/upgrade/services/maps_upgrade_services.ts similarity index 97% rename from x-pack/test/upgrade/maps_upgrade_services.ts rename to x-pack/test/upgrade/services/maps_upgrade_services.ts index b5553eeb9366d..7b80bce7682f0 100644 --- a/x-pack/test/upgrade/maps_upgrade_services.ts +++ b/x-pack/test/upgrade/services/maps_upgrade_services.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from './ftr_provider_context'; +import { FtrProviderContext } from '../ftr_provider_context'; export function MapsHelper({ getPageObjects, getService }: FtrProviderContext) { const PageObjects = getPageObjects(['maps']); diff --git a/x-pack/test/upgrade/reporting_services.ts b/x-pack/test/upgrade/services/reporting_upgrade_services.ts similarity index 95% rename from x-pack/test/upgrade/reporting_services.ts rename to x-pack/test/upgrade/services/reporting_upgrade_services.ts index 2467cee0ad822..1a1e6113c3735 100644 --- a/x-pack/test/upgrade/reporting_services.ts +++ b/x-pack/test/upgrade/services/reporting_upgrade_services.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { indexTimestamp } from '@kbn/reporting-plugin/server/lib/store/index_timestamp'; -import { services as xpackServices } from '../functional/services'; -import { services as apiIntegrationServices } from '../api_integration/services'; -import { FtrProviderContext } from './ftr_provider_context'; +import { services as xpackServices } from '../../functional/services'; +import { services as apiIntegrationServices } from '../../api_integration/services'; +import { FtrProviderContext } from '../ftr_provider_context'; interface PDFAppCounts { app: { @@ -104,7 +104,7 @@ export function ReportingAPIProvider({ getService }: FtrProviderContext) { * * @return {Promise} A function to call to clean up the index alias that was added. */ - async coerceReportsIntoExistingIndex(indexName: string) { + async coerceReportsIntoExistingIndex(indexName: string): Promise { log.debug(`ReportingAPI.coerceReportsIntoExistingIndex(${indexName})`); // Adding an index alias coerces the report to be generated on an existing index which means any new From ac8df39f079da6182f8ba7f17cfd398a90866aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Tue, 26 Apr 2022 20:27:30 +0200 Subject: [PATCH 06/78] Move `node-libs-browser` to `ui-shared-deps-npm` (#130877) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...libs_browser_polyfills_in_entry_bundles.ts | 53 +++++++++++++++++++ .../src/babel_runtime_helpers/index.ts | 1 + .../src/babel_runtime_helpers/parse_stats.ts | 1 + .../kbn-ui-shared-deps-npm/webpack.config.js | 4 ++ ...find_node_libs_browser_polyfills_in_use.js | 10 ++++ 5 files changed, 69 insertions(+) create mode 100644 packages/kbn-optimizer/src/babel_runtime_helpers/find_node_libs_browser_polyfills_in_entry_bundles.ts create mode 100644 scripts/find_node_libs_browser_polyfills_in_use.js diff --git a/packages/kbn-optimizer/src/babel_runtime_helpers/find_node_libs_browser_polyfills_in_entry_bundles.ts b/packages/kbn-optimizer/src/babel_runtime_helpers/find_node_libs_browser_polyfills_in_entry_bundles.ts new file mode 100644 index 0000000000000..06ad13da2b2f2 --- /dev/null +++ b/packages/kbn-optimizer/src/babel_runtime_helpers/find_node_libs_browser_polyfills_in_entry_bundles.ts @@ -0,0 +1,53 @@ +/* + * 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 Path from 'path'; + +import { run } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; + +import { OptimizerConfig } from '../optimizer'; +import { parseStats, inAnyEntryChunk } from './parse_stats'; + +export async function runFindNodeLibsBrowserPolyfillsInEntryBundlesCli() { + run(async ({ log }) => { + const config = OptimizerConfig.create({ + includeCoreBundle: true, + repoRoot: REPO_ROOT, + }); + + const paths = config.bundles.map((b) => Path.resolve(b.outputDir, 'stats.json')); + + log.info('analyzing', paths.length, 'stats files'); + log.verbose(paths); + + const imports = new Set(); + for (const path of paths) { + const stats = parseStats(path); + + for (const module of stats.modules) { + if (!inAnyEntryChunk(stats, module)) { + continue; + } + + // Relying on module name instead of actual imports because these are usual polyfills that assume the global + // Node.js environment when development (i.e.: Buffer doesn't require an import to be used). + if (module.name.includes('node-libs-browser/node_modules/')) { + imports.add(module.name); + } + } + } + + log.success('found', imports.size, 'node-libs-browser/* imports in entry bundles'); + log.write( + Array.from(imports, (i) => `'${i}',`) + .sort() + .join('\n') + ); + }); +} diff --git a/packages/kbn-optimizer/src/babel_runtime_helpers/index.ts b/packages/kbn-optimizer/src/babel_runtime_helpers/index.ts index 58a3ddf263a1d..3a7987f867bc5 100644 --- a/packages/kbn-optimizer/src/babel_runtime_helpers/index.ts +++ b/packages/kbn-optimizer/src/babel_runtime_helpers/index.ts @@ -7,3 +7,4 @@ */ export * from './find_babel_runtime_helpers_in_entry_bundles'; +export * from './find_node_libs_browser_polyfills_in_entry_bundles'; diff --git a/packages/kbn-optimizer/src/babel_runtime_helpers/parse_stats.ts b/packages/kbn-optimizer/src/babel_runtime_helpers/parse_stats.ts index fac0b099b5195..9b9ba11f90c9a 100644 --- a/packages/kbn-optimizer/src/babel_runtime_helpers/parse_stats.ts +++ b/packages/kbn-optimizer/src/babel_runtime_helpers/parse_stats.ts @@ -20,6 +20,7 @@ const partialObject =

(props: P) => { export type Module = TypeOf; const moduleSchema = partialObject({ identifier: schema.string(), + name: schema.string(), chunks: schema.arrayOf(schema.oneOf([schema.string(), schema.number()])), reasons: schema.arrayOf( partialObject({ diff --git a/packages/kbn-ui-shared-deps-npm/webpack.config.js b/packages/kbn-ui-shared-deps-npm/webpack.config.js index 0a709181b04d2..ddcb71fdb2c86 100644 --- a/packages/kbn-ui-shared-deps-npm/webpack.config.js +++ b/packages/kbn-ui-shared-deps-npm/webpack.config.js @@ -37,6 +37,10 @@ module.exports = (_, argv) => { 'regenerator-runtime/runtime', 'whatwg-fetch', 'symbol-observable', + // Parts of node-libs-browser that are used in many places across Kibana + 'buffer', + 'punycode', + 'util', /** * babel runtime helpers referenced from entry chunks diff --git a/scripts/find_node_libs_browser_polyfills_in_use.js b/scripts/find_node_libs_browser_polyfills_in_use.js new file mode 100644 index 0000000000000..4e53e5e551075 --- /dev/null +++ b/scripts/find_node_libs_browser_polyfills_in_use.js @@ -0,0 +1,10 @@ +/* + * 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. + */ + +require('../src/setup_node_env/no_transpilation'); +require('@kbn/optimizer').runFindNodeLibsBrowserPolyfillsInEntryBundlesCli(); From b655b48c16dde29e98103742642dd50292938ff4 Mon Sep 17 00:00:00 2001 From: Kyle Pollich Date: Tue, 26 Apr 2022 15:54:21 -0400 Subject: [PATCH 07/78] [Fleet] Add redesigned Fleet Server flyout (#127786) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP new fleet server flyout * WIP finish up quick start tab * Refactor quick start steps into separate files: * Initial refactor of existing fleet server instructions * Move quick start form return value to explicit type * Flesh out fleet server commands * Fix translation error * Migrate on prem instructions component over to new file structure * Makes quick start tab actually create policy * Fix type errors * Fix missing hooks + update snapshots * Fix paths in mocks * Fix translations * WIP test fixes * Implement enabled/disabled state for new steps * Fix cypress tests * Force re-render to get last test passing * Fix failing tests * Fix import errors after conflicts * Fix snapshot tests * Use id instead of full policy for policy selector * Replace Fleet Server instructions w/ Advanced Tab contents * First pass at integrating add agent/fleet server flyouts * Test commit * Fix imports * Fix imports * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Enforce https-only fleet server URL's + improve errors * Fix failing tests * Fix fleet server command in quick start * Show success state in Quick start when policy exists + use first fleet server host if it exists * Set initial service token value when Fleet Server policy ID is initiall set * Generate service token instead of enrollment token * Fix fleet server flyout opening from unhealthy callout * Revert service token change + use EuiComboBox for fleet server host * Fix checks + use custom option text * Move fleet server host combobox to component * Use new combobox in advanced tab * Fix translations * Fix unused import * Don't recreate quick start policy if it already exists * Actually use quick start policy fields 🙃 * Fix policy check * Update x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/fleet_server_host_combobox.tsx Co-authored-by: Mark Hopkin * Update x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/install_fleet_server.tsx Co-authored-by: Mark Hopkin * Update x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/set_deployment_mode.tsx Co-authored-by: Mark Hopkin * Fix formatting issue * Clean up fleet server settings variable declaration per PR review Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Mark Hopkin --- .../plugins/fleet/.storybook/context/cloud.ts | 4 +- .../plugins/fleet/.storybook/context/http.ts | 56 +- .../fleet/.storybook/context/index.tsx | 2 +- .../integration/fleet_settings.spec.ts | 8 +- .../cypress/integration/fleet_startup.spec.ts | 3 + .../advanced_tab.tsx | 66 ++ .../components/fleet_server_host_combobox.tsx | 77 ++ .../components/index.ts | 7 + .../flyout.stories.tsx | 33 + .../fleet_server_instructions/hooks/index.ts | 18 + .../hooks/use_advanced_form.ts | 46 ++ .../hooks/use_fleet_server_host.ts | 103 +++ .../hooks/use_quick_start_form.ts | 140 ++++ .../hooks/use_select_fleet_server_policy.ts | 42 + .../hooks/use_service_token.ts | 37 + .../hooks/use_wait_for_fleet_server.ts | 61 ++ .../fleet_server_instructions/index.tsx | 154 ++++ .../instructions.stories.tsx | 23 + .../quick_start_tab.tsx | 41 + .../steps/add_fleet_server_host.tsx | 165 ++++ .../steps/confirm_fleet_server_connection.tsx | 66 ++ .../steps/create_service_token.tsx | 136 ++++ .../steps/get_started.tsx | 130 +++ .../fleet_server_instructions/steps/index.ts | 14 + .../steps/install_fleet_server.tsx | 97 +++ .../steps/select_agent_policy.tsx | 81 ++ .../steps/set_deployment_mode.tsx | 117 +++ .../fleet_server_instructions/utils/index.ts | 8 + .../utils}/install_command_utils.test.ts | 153 +++- .../utils/install_command_utils.ts | 119 +++ .../applications/fleet/components/index.ts | 1 + .../agent_policy_advanced_fields/index.tsx | 4 +- .../components/actions_menu.tsx | 3 +- .../sections/agents/agent_list_page/index.tsx | 13 +- .../components/enrollment_recommendation.tsx | 104 +++ .../fleet_server_on_prem_instructions.tsx | 748 ------------------ .../components/index.tsx | 2 +- .../components/install_command_utils.ts | 65 -- .../fleet_server_requirement_page.tsx | 28 +- .../fleet/sections/agents/index.tsx | 90 ++- .../use_fleet_server_host_form.test.tsx | 8 +- .../use_fleet_server_host_form.tsx | 2 +- .../agent_enrollment_flyout.test.mocks.ts | 14 +- .../agent_enrollment_flyout.test.tsx | 108 +-- .../agent_policy_select_create.tsx | 6 +- .../agent_policy_selection.test.tsx | 2 +- .../agent_policy_selection.tsx | 44 +- .../agent_enrollment_flyout/instructions.tsx | 17 +- .../steps/agent_policy_selection_step.tsx | 2 +- .../steps/compute_steps.tsx | 83 -- .../steps/install_managed_agent_step.tsx | 2 +- .../steps/install_standalone_agent_step.tsx | 3 +- .../install_section.tsx | 7 +- .../standalone/index.tsx | 2 +- .../plugins/fleet/public/components/index.ts | 1 + .../manual => }/platform_selector.tsx | 4 +- .../agents => }/services/has_fleet_server.ts | 4 +- x-pack/plugins/fleet/public/services/index.ts | 1 + .../translations/translations/fr-FR.json | 8 - .../translations/translations/ja-JP.json | 8 - .../translations/translations/zh-CN.json | 8 - 61 files changed, 2262 insertions(+), 1137 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/fleet_server_host_combobox.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/index.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/flyout.stories.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/index.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_fleet_server_host.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_select_fleet_server_policy.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_service_token.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_wait_for_fleet_server.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/index.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/instructions.stories.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/confirm_fleet_server_connection.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/create_service_token.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/get_started.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/index.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/install_fleet_server.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/select_agent_policy.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/set_deployment_mode.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/index.ts rename x-pack/plugins/fleet/public/applications/fleet/{sections/agents/agent_requirements_page/components => components/fleet_server_instructions/utils}/install_command_utils.test.ts (56%) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx delete mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx delete mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils.ts rename x-pack/plugins/fleet/public/components/{enrollment_instructions/manual => }/platform_selector.tsx (96%) rename x-pack/plugins/fleet/public/{applications/fleet/sections/agents => }/services/has_fleet_server.ts (79%) diff --git a/x-pack/plugins/fleet/.storybook/context/cloud.ts b/x-pack/plugins/fleet/.storybook/context/cloud.ts index 1291f94cea788..eccb41d6aa8c0 100644 --- a/x-pack/plugins/fleet/.storybook/context/cloud.ts +++ b/x-pack/plugins/fleet/.storybook/context/cloud.ts @@ -11,9 +11,9 @@ export const getCloud = ({ isCloudEnabled }: { isCloudEnabled: boolean }) => { const cloud: CloudSetup = { isCloudEnabled, baseUrl: 'https://base.url', - cloudId: 'cloud-id', + cloudId: isCloudEnabled ? 'cloud-id' : undefined, cname: 'found.io', - deploymentUrl: 'https://deployment.url', + deploymentUrl: isCloudEnabled ? 'https://deployment.url' : undefined, organizationUrl: 'https://organization.url', profileUrl: 'https://profile.url', snapshotsUrl: 'https://snapshots.url', diff --git a/x-pack/plugins/fleet/.storybook/context/http.ts b/x-pack/plugins/fleet/.storybook/context/http.ts index 5167ad5500364..ff5b36a82aa66 100644 --- a/x-pack/plugins/fleet/.storybook/context/http.ts +++ b/x-pack/plugins/fleet/.storybook/context/http.ts @@ -27,7 +27,7 @@ export const getHttp = (basepath = BASE_PATH) => { serverBasePath: basepath, }, get: (async (path: string, options: HttpFetchOptions) => { - action('get')(path, options); + action('get')(path, JSON.stringify(options)); // TODO: all of this needs revision, as it's far too clunky... but it works for now, // with the few paths we're supporting. if (path === '/api/fleet/agents/setup') { @@ -35,7 +35,7 @@ export const getHttp = (basepath = BASE_PATH) => { isReady = true; return { isReady: false, missing_requirements: ['api_keys', 'fleet_server'] }; } - return { isInitialized: true, nonFatalErrors: [] }; + return { isReady: true, isInitialized: true, nonFatalErrors: [], missing_requirements: [] }; } if (path === '/api/fleet/epm/categories') { @@ -79,9 +79,59 @@ export const getHttp = (basepath = BASE_PATH) => { return { success: true }; } - action(path)('KP: UNSUPPORTED ROUTE'); + if (path.match('/api/fleet/agent_policies')) { + return { items: [] }; + } + + if (path.match('/api/fleet/settings')) { + return { item: { fleet_server_hosts: [] } }; + } + + if (path.match('/api/fleet/outputs')) { + return { + items: [{ name: 'Default Output', is_default: true, hosts: ['https://test.es:9200'] }], + }; + } + + action(path)(`UNSUPPORTED ROUTE: GET ${path}`); return {}; }) as HttpHandler, + post: (async (path: string, options: HttpFetchOptions) => { + action('post')(path, JSON.stringify(options)); + + if (path.match('/api/fleet/settings')) { + return { items: [] }; + } + + if (path.match('/api/fleet/service_tokens')) { + return { + name: 'test-token', + value: 'test-token-value', + }; + } + + if (path.match('/api/fleet/agent_policies')) { + return { + item: { + id: 'test-policy', + name: 'Test Policy', + namespace: 'default', + description: 'Test Policy', + monitoring_enabled: ['metrics'], + data_output_id: 'test-output', + monitoring_output_id: 'test-output', + status: 'active', + packagePolicies: ['test-package-policy'], + updated_on: new Date(), + updated_by: 'elastic', + revision: 0, + agents: 0, + }, + }; + } + + action(path)(`UNSUPPORTED ROUTE: POST ${path}`); + }) as HttpHandler, } as unknown as HttpStart; return http; diff --git a/x-pack/plugins/fleet/.storybook/context/index.tsx b/x-pack/plugins/fleet/.storybook/context/index.tsx index a60ecaf5a1572..15ee77506cc0e 100644 --- a/x-pack/plugins/fleet/.storybook/context/index.tsx +++ b/x-pack/plugins/fleet/.storybook/context/index.tsx @@ -66,7 +66,7 @@ export const StorybookContext: React.FC<{ storyContext?: StoryContext }> = ({ chrome: getChrome(), cloud: { ...getCloud({ isCloudEnabled }), - CloudContextProvider: () => <>, + CloudContextProvider: ({ children }) => <>{children}, }, customIntegrations: { ContextProvider: getStorybookContextProvider(), diff --git a/x-pack/plugins/fleet/cypress/integration/fleet_settings.spec.ts b/x-pack/plugins/fleet/cypress/integration/fleet_settings.spec.ts index 76c8f129584bd..338030e91f3da 100644 --- a/x-pack/plugins/fleet/cypress/integration/fleet_settings.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/fleet_settings.spec.ts @@ -30,20 +30,20 @@ describe('Edit settings', () => { it('should update Fleet server hosts', () => { cy.getBySel('editHostsBtn').click(); - cy.get('[placeholder="Specify host URL"').type('http://localhost:8220'); + cy.get('[placeholder="Specify host URL"').type('https://localhost:8220'); cy.intercept('/api/fleet/settings', { - item: { id: 'fleet-default-settings', fleet_server_hosts: ['http://localhost:8220'] }, + item: { id: 'fleet-default-settings', fleet_server_hosts: ['https://localhost:8220'] }, }); cy.intercept('PUT', '/api/fleet/settings', { - fleet_server_hosts: ['http://localhost:8220'], + fleet_server_hosts: ['https://localhost:8220'], }).as('updateSettings'); cy.getBySel('saveApplySettingsBtn').click(); cy.getBySel(CONFIRM_MODAL_BTN).click(); cy.wait('@updateSettings').then((interception) => { - expect(interception.request.body.fleet_server_hosts[0]).to.equal('http://localhost:8220'); + expect(interception.request.body.fleet_server_hosts[0]).to.equal('https://localhost:8220'); }); }); diff --git a/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts b/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts index 4f7f4378c0fc2..c269d1fe2ba2d 100644 --- a/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts @@ -77,12 +77,15 @@ describe('Fleet startup', () => { }); it('should create Fleet Server policy', () => { + cy.getBySel('fleetServerFlyoutTab-advanced').click(); cy.getBySel('createFleetServerPolicyBtn').click(); // verify policy is created and has fleet server and system package verifyPolicy('Fleet Server policy 1', ['Fleet Server', 'System']); navigateToTab(AGENTS_TAB); + cy.getBySel('fleetServerFlyoutTab-advanced').click(); + // verify create button changed to dropdown cy.getBySel('agentPolicyDropdown'); diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx new file mode 100644 index 0000000000000..87b4a1bda7ff7 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx @@ -0,0 +1,66 @@ +/* + * 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 { EuiSteps } from '@elastic/eui'; +import React from 'react'; + +import { useAdvancedForm } from './hooks'; + +import { + getAddFleetServerHostStep, + getSelectAgentPolicyStep, + getGenerateServiceTokenStep, + getSetDeploymentModeStep, + getInstallFleetServerStep, + getConfirmFleetServerConnectionStep, +} from './steps'; + +export const AdvancedTab: React.FunctionComponent = () => { + const { + eligibleFleetServerPolicies, + refreshEligibleFleetServerPolicies, + fleetServerPolicyId, + setFleetServerPolicyId, + isFleetServerReady, + serviceToken, + isLoadingServiceToken, + generateServiceToken, + fleetServerHostForm, + deploymentMode, + setDeploymentMode, + } = useAdvancedForm(); + + const steps = [ + getSelectAgentPolicyStep({ + policyId: fleetServerPolicyId, + setPolicyId: setFleetServerPolicyId, + eligibleFleetServerPolicies, + refreshEligibleFleetServerPolicies, + }), + getSetDeploymentModeStep({ + deploymentMode, + setDeploymentMode, + disabled: !Boolean(fleetServerPolicyId), + }), + getAddFleetServerHostStep({ fleetServerHostForm, disabled: !Boolean(fleetServerPolicyId) }), + getGenerateServiceTokenStep({ + serviceToken, + generateServiceToken, + isLoadingServiceToken, + disabled: !Boolean(fleetServerHostForm.isFleetServerHostSubmitted), + }), + getInstallFleetServerStep({ + isFleetServerReady, + serviceToken, + fleetServerHost: fleetServerHostForm.fleetServerHost, + fleetServerPolicyId, + disabled: !Boolean(serviceToken), + }), + getConfirmFleetServerConnectionStep({ isFleetServerReady, disabled: !Boolean(serviceToken) }), + ]; + + return ; +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/fleet_server_host_combobox.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/fleet_server_host_combobox.tsx new file mode 100644 index 0000000000000..0508bd9108d3a --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/fleet_server_host_combobox.tsx @@ -0,0 +1,77 @@ +/* + * 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 React, { useState } from 'react'; +import type { EuiComboBoxOptionOption } from '@elastic/eui'; +import { EuiComboBox, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; + +interface Props { + fleetServerHost: string | undefined; + fleetServerHostSettings: string[]; + isDisabled: boolean; + isInvalid: boolean; + onFleetServerHostChange: (host: string) => void; +} + +export const FleetServerHostComboBox: React.FunctionComponent = ({ + fleetServerHost, + fleetServerHostSettings, + isDisabled = false, + isInvalid = false, + onFleetServerHostChange, +}) => { + // Track options that are created inline + const [createdOptions, setCreatedOptions] = useState([]); + + const options = [...createdOptions, ...fleetServerHostSettings].map((option) => ({ + label: option, + value: option, + })); + + const handleChange = (selectedOptions: Array>) => { + const host = selectedOptions[0].value ?? ''; + onFleetServerHostChange(host); + }; + + const handleCreateOption = (option: string) => { + setCreatedOptions([...createdOptions, option]); + onFleetServerHostChange(option); + }; + + return ( + + fullWidth + isClearable={false} + singleSelection={{ asPlainText: true }} + placeholder="https://fleet-server-host.com:8220" + options={options} + customOptionText={i18n.translate( + 'xpack.fleet.fleetServerSetup.addFleetServerHostCustomOptionText', + { + defaultMessage: 'Add {searchValuePlaceholder} as a new Fleet Server host', + values: { searchValuePlaceholder: '{searchValue}' }, + } + )} + selectedOptions={fleetServerHost ? [{ label: fleetServerHost, value: fleetServerHost }] : []} + prepend={ + + + + } + noSuggestions={fleetServerHostSettings.length === 0} + data-test-subj="fleetServerHostInput" + isDisabled={isDisabled} + isInvalid={isInvalid} + onChange={handleChange} + onCreateOption={handleCreateOption} + /> + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/index.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/index.ts new file mode 100644 index 0000000000000..904271187dc8b --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/index.ts @@ -0,0 +1,7 @@ +/* + * 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. + */ +export * from './fleet_server_host_combobox'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/flyout.stories.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/flyout.stories.tsx new file mode 100644 index 0000000000000..b08c965d084c8 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/flyout.stories.tsx @@ -0,0 +1,33 @@ +/* + * 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 React from 'react'; +import { EuiButton } from '@elastic/eui'; + +import { FleetServerFlyout as FleetServerFlyoutComponent } from '.'; + +export const FleetServerFlyout = () => { + const [isOpen, setIsOpen] = React.useState(false); + + return ( +

+ setIsOpen(true)}> + Show flyout + + {isOpen && setIsOpen(false)} />} +
+ ); +}; + +FleetServerFlyout.args = { + isCloudEnabled: false, +}; + +export default { + component: FleetServerFlyout, + title: 'Sections/Fleet/Agents/Fleet Server Instructions/In Flyout', +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/index.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/index.ts new file mode 100644 index 0000000000000..6c702d5433ce0 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/index.ts @@ -0,0 +1,18 @@ +/* + * 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. + */ + +// These hooks are tightly coupled to each tab of the Fleet server instructions component, and provide +// all necessary data to drive those UI's +export * from './use_advanced_form'; +export * from './use_quick_start_form'; + +// These are individual hooks for one-off consumption. These are typically composed in the hooks above, +// but exported here to support individual usage. +export * from './use_wait_for_fleet_server'; +export * from './use_select_fleet_server_policy'; +export * from './use_service_token'; +export * from './use_fleet_server_host'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form.ts new file mode 100644 index 0000000000000..5ed9f9faa0372 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useState } from 'react'; + +import type { DeploymentMode } from '../steps'; + +import { useFleetServerHost } from './use_fleet_server_host'; +import { useSelectFleetServerPolicy } from './use_select_fleet_server_policy'; +import { useServiceToken } from './use_service_token'; +import { useWaitForFleetServer } from './use_wait_for_fleet_server'; + +/** + * Provides all data/state required for the "advanced" tab in the Fleet Server instructions/flyout + */ +export const useAdvancedForm = (defaultAgentPolicyId?: string) => { + const { + eligibleFleetServerPolicies, + refreshEligibleFleetServerPolicies, + fleetServerPolicyId, + setFleetServerPolicyId, + } = useSelectFleetServerPolicy(defaultAgentPolicyId); + const { isFleetServerReady } = useWaitForFleetServer(); + const { serviceToken, isLoadingServiceToken, generateServiceToken } = useServiceToken(); + const fleetServerHostForm = useFleetServerHost(); + + const [deploymentMode, setDeploymentMode] = useState('quickstart'); + + return { + eligibleFleetServerPolicies, + refreshEligibleFleetServerPolicies, + fleetServerPolicyId, + setFleetServerPolicyId, + isFleetServerReady, + serviceToken, + isLoadingServiceToken, + generateServiceToken, + fleetServerHostForm, + deploymentMode, + setDeploymentMode, + }; +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_fleet_server_host.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_fleet_server_host.ts new file mode 100644 index 0000000000000..05eeccf4a9312 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_fleet_server_host.ts @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { useCallback, useEffect, useState } from 'react'; + +import { sendPutSettings, useGetSettings } from '../../../hooks'; + +const URL_REGEX = /^(https):\/\/[^\s$.?#].[^\s]*$/gm; + +export interface FleetServerHostForm { + saveFleetServerHost: () => Promise; + fleetServerHost?: string; + fleetServerHostSettings: string[]; + isFleetServerHostSubmitted: boolean; + setFleetServerHost: React.Dispatch>; + error?: string; + validateFleetServerHost: () => boolean; +} + +export const useFleetServerHost = (): FleetServerHostForm => { + const [fleetServerHost, setFleetServerHost] = useState(); + const [isFleetServerHostSubmitted, setIsFleetServerHostSubmitted] = useState(false); + const [error, setError] = useState(); + + const { data: settings } = useGetSettings(); + + useEffect(() => { + const settingsFleetServerHosts = settings?.item.fleet_server_hosts ?? []; + + if (settingsFleetServerHosts.length) { + setFleetServerHost(settingsFleetServerHosts[0]); + } + }, [settings?.item.fleet_server_hosts]); + + const validateFleetServerHost = useCallback(() => { + if (!fleetServerHost) { + setError( + i18n.translate('xpack.fleet.fleetServerHost.requiredError', { + defaultMessage: 'Fleet server host is required.', + }) + ); + + return false; + } else if (!fleetServerHost.startsWith('https')) { + setError( + i18n.translate('xpack.fleet.fleetServerHost.requiresHttpsError', { + defaultMessage: 'Fleet server host must begin with "https"', + }) + ); + + return false; + } else if (!fleetServerHost.match(URL_REGEX)) { + setError( + i18n.translate('xpack.fleet.fleetServerSetup.addFleetServerHostInvalidUrlError', { + defaultMessage: 'Invalid URL', + }) + ); + + return false; + } + + return true; + }, [fleetServerHost]); + + const saveFleetServerHost = useCallback(async () => { + setIsFleetServerHostSubmitted(false); + + if (!validateFleetServerHost()) { + return; + } + + // If the Fleet Server host provided already exists in settings, don't submit it + if (settings?.item.fleet_server_hosts.includes(fleetServerHost!)) { + setIsFleetServerHostSubmitted(true); + return; + } + + const res = await sendPutSettings({ + fleet_server_hosts: [fleetServerHost!, ...(settings?.item.fleet_server_hosts || [])], + }); + + if (res.error) { + throw res.error; + } + + setIsFleetServerHostSubmitted(true); + }, [fleetServerHost, settings?.item.fleet_server_hosts, validateFleetServerHost]); + + return { + saveFleetServerHost, + fleetServerHost, + fleetServerHostSettings: settings?.item.fleet_server_hosts ?? [], + isFleetServerHostSubmitted, + setFleetServerHost, + error, + validateFleetServerHost, + }; +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts new file mode 100644 index 0000000000000..84fd39aeec378 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts @@ -0,0 +1,140 @@ +/* + * 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 { useState, useCallback, useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { sendCreateAgentPolicy, sendGetOneAgentPolicy, useStartServices } from '../../../hooks'; + +import type { NewAgentPolicy } from '../../../types'; + +import { useSelectFleetServerPolicy } from './use_select_fleet_server_policy'; +import { useServiceToken } from './use_service_token'; +import { useFleetServerHost } from './use_fleet_server_host'; + +const QUICK_START_FLEET_SERVER_POLICY_FIELDS: NewAgentPolicy = { + id: 'fleet-server-policy', + name: 'Fleet Server Policy', + description: 'Fleet Server policy generated by Kibana', + namespace: 'default', + has_fleet_server: true, + monitoring_enabled: ['logs', 'metrics'], + is_default_fleet_server: true, +}; + +export type QuickStartCreateFormStatus = 'initial' | 'loading' | 'error' | 'success'; + +export interface QuickStartCreateForm { + status: QuickStartCreateFormStatus; + error?: string; + submit: () => void; + fleetServerHost?: string; + fleetServerHostSettings: string[]; + isFleetServerHostSubmitted: boolean; + onFleetServerHostChange: (value: string) => void; + fleetServerPolicyId?: string; + serviceToken?: string; +} + +/** + * Provides a unified interface that combines the following operations: + * 1. Setting a Fleet Server host in Fleet's settings + * 2. Creating an agent policy that contains the `fleet_server` integration + * 3. Generating a service token used by Fleet Server + */ +export const useQuickStartCreateForm = (): QuickStartCreateForm => { + const [status, setStatus] = useState<'initial' | 'loading' | 'error' | 'success'>('initial'); + const [error, setError] = useState(); + + const { + fleetServerHost, + fleetServerHostSettings, + isFleetServerHostSubmitted, + setFleetServerHost, + validateFleetServerHost, + saveFleetServerHost, + error: fleetServerError, + } = useFleetServerHost(); + + // When a validation error is surfaced from the Fleet Server host form, we want to treat it + // the same way we do errors from the service token or policy creation steps + useEffect(() => { + setStatus('error'); + setError(fleetServerError); + }, [fleetServerError]); + + const { notifications } = useStartServices(); + const { fleetServerPolicyId, setFleetServerPolicyId } = useSelectFleetServerPolicy(); + const { serviceToken, generateServiceToken } = useServiceToken(); + + const onFleetServerHostChange = useCallback( + (value: string) => { + setFleetServerHost(value); + }, + [setFleetServerHost] + ); + + const submit = useCallback(async () => { + try { + if (validateFleetServerHost()) { + setStatus('loading'); + await saveFleetServerHost(); + await generateServiceToken(); + + const existingPolicy = await sendGetOneAgentPolicy( + QUICK_START_FLEET_SERVER_POLICY_FIELDS.id! + ); + + // Don't attempt to create the policy if it's already been created in a previous quick start flow + if (existingPolicy.data?.item) { + setFleetServerPolicyId(existingPolicy.data?.item.id); + } else { + const createPolicyResponse = await sendCreateAgentPolicy( + QUICK_START_FLEET_SERVER_POLICY_FIELDS, + { + withSysMonitoring: true, + } + ); + + setFleetServerPolicyId(createPolicyResponse.data?.item.id); + } + + setFleetServerHost(fleetServerHost); + setStatus('success'); + } + } catch (err) { + notifications.toasts.addError(err, { + title: i18n.translate('xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle', { + defaultMessage: 'Error adding Fleet Server host', + }), + }); + + setStatus('error'); + setError(err.message); + } + }, [ + validateFleetServerHost, + saveFleetServerHost, + generateServiceToken, + setFleetServerHost, + fleetServerHost, + setFleetServerPolicyId, + notifications.toasts, + ]); + + return { + status, + error, + submit, + fleetServerPolicyId, + fleetServerHost, + fleetServerHostSettings, + isFleetServerHostSubmitted, + onFleetServerHostChange, + serviceToken, + }; +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_select_fleet_server_policy.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_select_fleet_server_policy.ts new file mode 100644 index 0000000000000..add318e85a7ae --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_select_fleet_server_policy.ts @@ -0,0 +1,42 @@ +/* + * 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 { useEffect, useMemo, useState } from 'react'; + +import { useGetAgentPolicies } from '../../../hooks'; +import { policyHasFleetServer } from '../../../services'; + +export const useSelectFleetServerPolicy = (defaultAgentPolicyId?: string) => { + const [fleetServerPolicyId, setFleetServerPolicyId] = useState( + defaultAgentPolicyId + ); + const { data: agentPoliciesData, resendRequest } = useGetAgentPolicies({ + full: true, + }); + + const eligibleFleetServerPolicies = useMemo( + () => + agentPoliciesData + ? agentPoliciesData.items?.filter((item) => policyHasFleetServer(item)) + : [], + [agentPoliciesData] + ); + + useEffect(() => { + // Default to the first policy found with a fleet server integration installed + if (eligibleFleetServerPolicies.length && !fleetServerPolicyId) { + setFleetServerPolicyId(eligibleFleetServerPolicies[0].id); + } + }, [eligibleFleetServerPolicies, fleetServerPolicyId]); + + return { + fleetServerPolicyId, + setFleetServerPolicyId, + eligibleFleetServerPolicies, + refreshEligibleFleetServerPolicies: resendRequest, + }; +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_service_token.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_service_token.ts new file mode 100644 index 0000000000000..3b729a6776b52 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_service_token.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useState, useCallback } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { useStartServices, sendGenerateServiceToken } from '../../../hooks'; + +export const useServiceToken = () => { + const { notifications } = useStartServices(); + const [serviceToken, setServiceToken] = useState(); + const [isLoadingServiceToken, setIsLoadingServiceToken] = useState(false); + + const generateServiceToken = useCallback(async () => { + setIsLoadingServiceToken(true); + try { + const { data } = await sendGenerateServiceToken(); + if (data?.value) { + setServiceToken(data?.value); + } + } catch (err) { + notifications.toasts.addError(err, { + title: i18n.translate('xpack.fleet.fleetServerSetup.errorGeneratingTokenTitleText', { + defaultMessage: 'Error generating token', + }), + }); + } finally { + setIsLoadingServiceToken(false); + } + }, [notifications.toasts]); + + return { serviceToken, isLoadingServiceToken, generateServiceToken }; +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_wait_for_fleet_server.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_wait_for_fleet_server.ts new file mode 100644 index 0000000000000..4da59560e408e --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_wait_for_fleet_server.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { useEffect, useState } from 'react'; + +import { sendGetFleetStatus, useStartServices } from '../../../hooks'; + +const REFRESH_INTERVAL = 10000; + +/** + * Polls the Fleet status endpoint until the `fleet_server` requirement does not appear + * in the `missing_requirements` list. + */ +export const useWaitForFleetServer = () => { + const [isFleetServerReady, setIsFleetServerReady] = useState(false); + const { notifications } = useStartServices(); + + useEffect(() => { + let interval: ReturnType | null = null; + + if (!isFleetServerReady) { + interval = setInterval(async () => { + try { + const res = await sendGetFleetStatus(); + + if (res.error) { + throw res.error; + } + if (res.data?.isReady && !res.data?.missing_requirements?.includes('fleet_server')) { + setIsFleetServerReady(true); + + if (interval) { + clearInterval(interval); + } + } + } catch (err) { + notifications.toasts.addError(err, { + title: i18n.translate('xpack.fleet.fleetServerSetup.errorRefreshingFleetServerStatus', { + defaultMessage: 'Error refreshing Fleet Server status', + }), + }); + } + }, REFRESH_INTERVAL); + } + + const cleanup = () => { + if (interval) { + clearInterval(interval); + } + }; + + return cleanup; + }, [notifications.toasts, isFleetServerReady]); + + return { isFleetServerReady }; +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/index.tsx new file mode 100644 index 0000000000000..6c48b499b9553 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/index.tsx @@ -0,0 +1,154 @@ +/* + * 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 React, { useState } from 'react'; +import { + EuiFlexGroup, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiLink, + EuiSpacer, + EuiTab, + EuiTabs, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import styled from 'styled-components'; + +import { useStartServices } from '../../hooks'; + +import { QuickStartTab } from './quick_start_tab'; +import { AdvancedTab } from './advanced_tab'; + +const ContentWrapper = styled(EuiFlexGroup)` + height: 100%; + margin: 0 auto; +`; + +interface Props { + onClose: () => void; +} + +const useFleetServerTabs = () => { + const [currentTab, setCurrentTab] = useState('quickStart'); + + const quickStartTab = { + id: 'quickStart', + name: 'Quick Start', + content: , + }; + + const advancedTab = { + id: 'advanced', + name: 'Advanced', + content: , + }; + + const currentTabContent = + currentTab === 'quickStart' ? quickStartTab.content : advancedTab.content; + + return { tabs: [quickStartTab, advancedTab], currentTab, setCurrentTab, currentTabContent }; +}; + +const Header: React.FunctionComponent<{ + isFlyout?: boolean; + currentTab: string; + tabs: Array<{ id: string; name: string; content: React.ReactNode }>; + onTabClick: (id: string) => void; +}> = ({ isFlyout = false, currentTab: currentTabId, tabs, onTabClick }) => { + const { docLinks } = useStartServices(); + + return ( + <> + +

+ +

+
+ + + + + + + + ), + }} + /> + + + + + + {tabs.map((tab) => ( + onTabClick(tab.id)} + > + {tab.name} + + ))} + + + ); +}; + +// Renders instructions inside of a flyout +export const FleetServerFlyout: React.FunctionComponent = ({ onClose }) => { + const { tabs, currentTab, setCurrentTab, currentTabContent } = useFleetServerTabs(); + + return ( + + +
setCurrentTab(id)} + isFlyout + /> + + + {currentTabContent} + + ); +}; + +// Renders instructions directly +export const FleetServerInstructions: React.FunctionComponent = () => { + const { tabs, currentTab, setCurrentTab, currentTabContent } = useFleetServerTabs(); + + return ( + +
setCurrentTab(id)} /> + + + + {currentTabContent} + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/instructions.stories.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/instructions.stories.tsx new file mode 100644 index 0000000000000..9993fab723a44 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/instructions.stories.tsx @@ -0,0 +1,23 @@ +/* + * 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 React from 'react'; + +import { FleetServerInstructions as FleetServerInstructionsComponent } from '.'; + +export const FleetServerInstructions = () => { + return ; +}; + +FleetServerInstructions.args = { + isCloudEnabled: false, +}; + +export default { + component: FleetServerInstructions, + title: 'Sections/Fleet/Agents/Fleet Server Instructions/Without Flyout', +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx new file mode 100644 index 0000000000000..cf8abc2fe9e16 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx @@ -0,0 +1,41 @@ +/* + * 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 React from 'react'; +import { EuiSteps } from '@elastic/eui'; + +import { useQuickStartCreateForm, useWaitForFleetServer } from './hooks'; + +import { + getGettingStartedStep, + getConfirmFleetServerConnectionStep, + getInstallFleetServerStep, +} from './steps'; + +export const QuickStartTab: React.FunctionComponent = () => { + const quickStartCreateForm = useQuickStartCreateForm(); + const { isFleetServerReady } = useWaitForFleetServer(); + + const steps = [ + getGettingStartedStep({ + quickStartCreateForm, + }), + getInstallFleetServerStep({ + isFleetServerReady, + fleetServerHost: quickStartCreateForm.fleetServerHost, + fleetServerPolicyId: quickStartCreateForm.fleetServerPolicyId, + serviceToken: quickStartCreateForm.serviceToken, + disabled: quickStartCreateForm.status !== 'success', + }), + getConfirmFleetServerConnectionStep({ + isFleetServerReady, + disabled: quickStartCreateForm.status !== 'success', + }), + ]; + + return ; +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx new file mode 100644 index 0000000000000..e64e23f039f89 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx @@ -0,0 +1,165 @@ +/* + * 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 React, { useState, useCallback } from 'react'; +import type { EuiStepProps } from '@elastic/eui'; +import { + EuiButton, + EuiCallOut, + EuiCode, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormErrorText, + EuiLink, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { useStartServices, useLink } from '../../../hooks'; +import type { FleetServerHostForm } from '../hooks'; +import { FleetServerHostComboBox } from '../components'; + +export const getAddFleetServerHostStep = ({ + fleetServerHostForm, + disabled, +}: { + fleetServerHostForm: FleetServerHostForm; + disabled: boolean; +}): EuiStepProps => { + return { + title: i18n.translate('xpack.fleet.fleetServerSetup.addFleetServerHostStepTitle', { + defaultMessage: 'Add your Fleet Server host', + }), + status: disabled ? 'disabled' : undefined, + children: disabled ? null : ( + + ), + }; +}; + +export const AddFleetServerHostStepContent = ({ + fleetServerHostForm, +}: { + fleetServerHostForm: FleetServerHostForm; +}) => { + const { + fleetServerHost, + fleetServerHostSettings, + setFleetServerHost, + validateFleetServerHost, + saveFleetServerHost, + error, + } = fleetServerHostForm; + + const [isLoading, setIsLoading] = useState(false); + const [submittedFleetServerHost, setSubmittedFleetServerHost] = useState(); + const { notifications } = useStartServices(); + const { getHref } = useLink(); + + const onSubmit = useCallback(async () => { + try { + setSubmittedFleetServerHost(''); + setIsLoading(true); + + if (validateFleetServerHost()) { + await saveFleetServerHost(); + setSubmittedFleetServerHost(fleetServerHost); + } + } catch (err) { + notifications.toasts.addError(err, { + title: i18n.translate('xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle', { + defaultMessage: 'Error adding Fleet Server host', + }), + }); + } finally { + setIsLoading(false); + } + }, [validateFleetServerHost, saveFleetServerHost, fleetServerHost, notifications.toasts]); + + const onChange = useCallback( + (host: string) => { + setFleetServerHost(host); + + if (error) { + validateFleetServerHost(); + } + }, + [error, setFleetServerHost, validateFleetServerHost] + ); + + return ( + + + 8220 }} + /> + + + + + + {error && {error}} + + + + + + + + {submittedFleetServerHost && ( + <> + + + } + > + + + + ), + }} + /> + + + )} + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/confirm_fleet_server_connection.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/confirm_fleet_server_connection.tsx new file mode 100644 index 0000000000000..5aa5c01a108a4 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/confirm_fleet_server_connection.tsx @@ -0,0 +1,66 @@ +/* + * 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 React, { useContext } from 'react'; + +import type { EuiStepProps } from '@elastic/eui'; +import { EuiButton, EuiLoadingSpinner, EuiSpacer } from '@elastic/eui'; +import { EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { agentFlyoutContext } from '../../../sections/agents'; + +export function getConfirmFleetServerConnectionStep({ + disabled, + isFleetServerReady, +}: { + disabled: boolean; + isFleetServerReady: boolean; +}): EuiStepProps { + return { + title: isFleetServerReady + ? i18n.translate('xpack.fleet.fleetServerFlyout.confirmConnectionSuccessTitle', { + defaultMessage: 'Fleet Server connected', + }) + : i18n.translate('xpack.fleet.fleetServerFlyout.confirmConnectionTitle', { + defaultMessage: 'Confirm connection', + }), + status: isFleetServerReady ? 'complete' : 'disabled', + children: !disabled && ( + + ), + }; +} + +const ConfirmFleetServerConnectionStepContent: React.FunctionComponent<{ + isFleetServerReady: boolean; +}> = ({ isFleetServerReady }) => { + const addAgentFlyout = useContext(agentFlyoutContext); + + return isFleetServerReady ? ( + <> + + + + + + + + + + + ) : ( + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/create_service_token.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/create_service_token.tsx new file mode 100644 index 0000000000000..dcfa150c2d32c --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/create_service_token.tsx @@ -0,0 +1,136 @@ +/* + * 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 React from 'react'; +import styled from 'styled-components'; + +import type { EuiStepProps } from '@elastic/eui'; +import { + EuiButton, + EuiCallOut, + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +const FlexItemWithMinWidth = styled(EuiFlexItem)` + min-width: 0px; + max-width: 100%; +`; + +export const ContentWrapper = styled(EuiFlexGroup)` + height: 100%; + margin: 0 auto; + max-width: 800px; +`; + +// Otherwise the copy button is over the text +const CommandCode = styled.div.attrs(() => { + return { + className: 'eui-textBreakAll', + }; +})` + margin-right: ${(props) => props.theme.eui.paddingSizes.m}; +`; + +export const getGenerateServiceTokenStep = ({ + disabled = false, + serviceToken, + generateServiceToken, + isLoadingServiceToken, +}: { + disabled?: boolean; + serviceToken?: string; + generateServiceToken: () => void; + isLoadingServiceToken: boolean; +}): EuiStepProps => { + return { + title: i18n.translate('xpack.fleet.fleetServerSetup.stepGenerateServiceTokenTitle', { + defaultMessage: 'Generate a service token', + }), + status: disabled ? 'disabled' : undefined, + children: !disabled && ( + + ), + }; +}; + +const ServiceTokenStepContent: React.FunctionComponent<{ + serviceToken?: string; + generateServiceToken: () => void; + isLoadingServiceToken: boolean; +}> = ({ serviceToken, generateServiceToken, isLoadingServiceToken }) => { + return ( + <> + + + + + {!serviceToken ? ( + + + { + generateServiceToken(); + }} + data-test-subj="fleetServerGenerateServiceTokenBtn" + > + + + + + ) : ( + <> + + } + /> + + + + + + + + + + {serviceToken} + + + + + )} + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/get_started.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/get_started.tsx new file mode 100644 index 0000000000000..50e9b6b72002c --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/get_started.tsx @@ -0,0 +1,130 @@ +/* + * 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 React from 'react'; + +import type { EuiStepProps } from '@elastic/eui'; +import { + EuiButton, + EuiCallOut, + EuiCode, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormErrorText, + EuiLink, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { useLink } from '../../../hooks'; + +import type { QuickStartCreateForm } from '../hooks'; +import { FleetServerHostComboBox } from '../components'; + +export function getGettingStartedStep({ + quickStartCreateForm, +}: { + quickStartCreateForm: QuickStartCreateForm; +}): EuiStepProps { + return { + title: i18n.translate('xpack.fleet.fleetServerFlyout.getStartedTitle', { + defaultMessage: 'Get started with Fleet Server', + }), + status: quickStartCreateForm.status === 'success' ? 'complete' : 'current', + children: , + }; +} + +const GettingStartedStepContent: React.FunctionComponent<{ + quickStartCreateForm: QuickStartCreateForm; +}> = ({ quickStartCreateForm }) => { + const { getHref } = useLink(); + + const { fleetServerHost, fleetServerHostSettings, onFleetServerHostChange } = + quickStartCreateForm; + + if (quickStartCreateForm.status === 'success') { + return ( + + + {fleetServerHost}, + fleetSettingsLink: ( + + + + ), + }} + /> + + + ); + } + + return ( + <> + + 8220 }} + /> + + + + + + + + + + {quickStartCreateForm.status === 'error' && ( + {quickStartCreateForm.error} + )} + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/index.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/index.ts new file mode 100644 index 0000000000000..11adf2693927b --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/index.ts @@ -0,0 +1,14 @@ +/* + * 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. + */ + +export * from './add_fleet_server_host'; +export * from './confirm_fleet_server_connection'; +export * from './create_service_token'; +export * from './get_started'; +export * from './install_fleet_server'; +export * from './select_agent_policy'; +export * from './set_deployment_mode'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/install_fleet_server.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/install_fleet_server.tsx new file mode 100644 index 0000000000000..dac9555ba3149 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/install_fleet_server.tsx @@ -0,0 +1,97 @@ +/* + * 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 React from 'react'; + +import type { EuiStepProps } from '@elastic/eui'; +import { EuiSpacer, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import type { PLATFORM_TYPE } from '../../../hooks'; +import { useDefaultOutput, useKibanaVersion } from '../../../hooks'; + +import { PlatformSelector } from '../..'; + +import { getInstallCommandForPlatform } from '../utils'; + +export function getInstallFleetServerStep({ + isFleetServerReady, + disabled, + serviceToken, + fleetServerHost, + fleetServerPolicyId, +}: { + isFleetServerReady: boolean; + disabled: boolean; + serviceToken?: string; + fleetServerHost?: string; + fleetServerPolicyId?: string; +}): EuiStepProps { + return { + title: i18n.translate('xpack.fleet.fleetServerFlyout.installFleetServerTitle', { + defaultMessage: 'Install Fleet Server to a centralized host', + }), + status: disabled ? 'disabled' : isFleetServerReady ? 'complete' : 'incomplete', + children: !disabled && ( + + ), + }; +} + +const InstallFleetServerStepContent: React.FunctionComponent<{ + serviceToken?: string; + fleetServerHost?: string; + fleetServerPolicyId?: string; +}> = ({ serviceToken, fleetServerHost, fleetServerPolicyId }) => { + const kibanaVersion = useKibanaVersion(); + const { output } = useDefaultOutput(); + + const installCommands = (['linux', 'mac', 'windows', 'deb', 'rpm'] as PLATFORM_TYPE[]).reduce( + (acc, platform) => { + acc[platform] = getInstallCommandForPlatform( + platform, + output?.hosts?.[0] ?? '', + serviceToken ?? '', + fleetServerPolicyId, + fleetServerHost, + false, + output?.ca_trusted_fingerprint, + kibanaVersion + ); + + return acc; + }, + {} as Record + ); + + return ( + <> + + + + + + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/select_agent_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/select_agent_policy.tsx new file mode 100644 index 0000000000000..778716bacb02e --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/select_agent_policy.tsx @@ -0,0 +1,81 @@ +/* + * 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 type { EuiStepProps } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useEffect } from 'react'; + +import { SelectCreateAgentPolicy } from '../..'; + +import type { GetAgentPoliciesResponseItem } from '../../../types'; + +export const getSelectAgentPolicyStep = ({ + policyId, + setPolicyId, + eligibleFleetServerPolicies, + refreshEligibleFleetServerPolicies, +}: { + policyId?: string; + setPolicyId: (v?: string) => void; + eligibleFleetServerPolicies: GetAgentPoliciesResponseItem[]; + refreshEligibleFleetServerPolicies: () => void; +}): EuiStepProps => { + return { + title: + eligibleFleetServerPolicies.length === 0 + ? i18n.translate('xpack.fleet.fleetServerSetup.stepCreateAgentPolicyTitle', { + defaultMessage: 'Create a policy for Fleet Server', + }) + : i18n.translate('xpack.fleet.fleetServerSetup.stepSelectAgentPolicyTitle', { + defaultMessage: 'Select a policy for Fleet Server', + }), + status: policyId ? 'complete' : undefined, + children: ( + + ), + }; +}; + +const SelectAgentPolicyStepContent: React.FunctionComponent<{ + policyId?: string; + setPolicyId: (v?: string) => void; + eligibleFleetServerPolicies: GetAgentPoliciesResponseItem[]; + refreshEligibleFleetServerPolicies: () => void; +}> = ({ + policyId, + setPolicyId, + eligibleFleetServerPolicies, + refreshEligibleFleetServerPolicies, +}) => { + useEffect(() => { + // Select default value + if (eligibleFleetServerPolicies.length && !policyId) { + setPolicyId(eligibleFleetServerPolicies[0].id); + } + }, [eligibleFleetServerPolicies, policyId, setPolicyId]); + + const setSelectedPolicyId = (agentPolicyId?: string) => { + setPolicyId(agentPolicyId); + }; + + return ( + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/set_deployment_mode.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/set_deployment_mode.tsx new file mode 100644 index 0000000000000..9833fc1d4527f --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/set_deployment_mode.tsx @@ -0,0 +1,117 @@ +/* + * 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 React, { useCallback, useMemo } from 'react'; + +import type { EuiStepProps } from '@elastic/eui'; +import { EuiRadioGroup, EuiSpacer } from '@elastic/eui'; +import { EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +export type DeploymentMode = 'production' | 'quickstart'; + +export const getSetDeploymentModeStep = ({ + deploymentMode, + setDeploymentMode, + disabled, +}: { + deploymentMode: DeploymentMode; + setDeploymentMode: (v: DeploymentMode) => void; + disabled: boolean; +}): EuiStepProps => { + return { + title: i18n.translate('xpack.fleet.fleetServerSetup.stepDeploymentModeTitle', { + defaultMessage: 'Choose a deployment mode for security', + }), + status: disabled ? 'disabled' : undefined, + children: disabled ? null : ( + + ), + }; +}; + +const DeploymentModeStepContent = ({ + deploymentMode, + setDeploymentMode, +}: { + deploymentMode: DeploymentMode; + setDeploymentMode: (v: DeploymentMode) => void; +}) => { + const onChangeCallback = useCallback( + (v: string) => { + const value = v.split('_')[0]; + if (value === 'production' || value === 'quickstart') { + setDeploymentMode(value); + } + }, + [setDeploymentMode] + ); + + // radio id has to be unique so that the component works even if appears twice in DOM (Agents tab, Add agent flyout) + const radioSuffix = useMemo(() => Date.now(), []); + + return ( + <> + + + + + + + + ), + }} + /> + ), + }, + { + id: `production_${radioSuffix}`, + label: ( + + + + ), + }} + /> + ), + }, + ]} + idSelected={`${deploymentMode}_${radioSuffix}`} + onChange={onChangeCallback} + name={`radio group ${radioSuffix}`} + /> + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/index.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/index.ts new file mode 100644 index 0000000000000..fcbab4728cbe2 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export * from './install_command_utils'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils.test.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.test.ts similarity index 56% rename from x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils.test.ts rename to x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.test.ts index 774b7871f0353..89a246c5c6265 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils.test.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.test.ts @@ -10,50 +10,85 @@ import { getInstallCommandForPlatform } from './install_command_utils'; describe('getInstallCommandForPlatform', () => { describe('without policy id', () => { it('should return the correct command if the the policyId is not set for linux', () => { - const res = getInstallCommandForPlatform('http://elasticsearch:9200', 'service-token-1'); + const res = getInstallCommandForPlatform( + 'linux', + 'http://elasticsearch:9200', + 'service-token-1' + ); - expect(res.linux).toMatchInlineSnapshot(` - "sudo ./elastic-agent install \\\\ + expect(res).toMatchInlineSnapshot(` + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip \\\\ + tar xzvf elastic-agent--linux-x86_64.zip \\\\ + cd elastic-agent--linux-x86_64 \\\\ + sudo ./elastic-agent install \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1" `); }); it('should return the correct command if the the policyId is not set for mac', () => { - const res = getInstallCommandForPlatform('http://elasticsearch:9200', 'service-token-1'); + const res = getInstallCommandForPlatform( + 'mac', + 'http://elasticsearch:9200', + 'service-token-1' + ); - expect(res.mac).toMatchInlineSnapshot(` - "sudo ./elastic-agent install \\\\ + expect(res).toMatchInlineSnapshot(` + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--darwin-x86_64.tar.gz \\\\ + tar xzvf elastic-agent--darwin-x86_64.tar.gz \\\\ + cd elastic-agent--darwin-x86_64 \\\\ + sudo ./elastic-agent install \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1" `); }); it('should return the correct command if the the policyId is not set for windows', () => { - const res = getInstallCommandForPlatform('http://elasticsearch:9200', 'service-token-1'); + const res = getInstallCommandForPlatform( + 'windows', + 'http://elasticsearch:9200', + 'service-token-1' + ); - expect(res.windows).toMatchInlineSnapshot(` - ".\\\\elastic-agent.exe install \` + expect(res).toMatchInlineSnapshot(` + "wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--windows-x86_64.tar.gz -OutFile elastic-agent--windows-x86_64.tar.gz \` + Expand-Archive .\\\\elastic-agent--windows-x86_64.tar.gz \` + cd elastic-agent--windows-x86_64\` + .\\\\elastic-agent.exe install \` --fleet-server-es=http://elasticsearch:9200 \` --fleet-server-service-token=service-token-1" `); }); it('should return the correct command if the the policyId is not set for rpm', () => { - const res = getInstallCommandForPlatform('http://elasticsearch:9200', 'service-token-1'); + const res = getInstallCommandForPlatform( + 'rpm', + 'http://elasticsearch:9200', + 'service-token-1' + ); - expect(res.rpm).toMatchInlineSnapshot(` - "sudo elastic-agent enroll \\\\ + expect(res).toMatchInlineSnapshot(` + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--x86_64.rpm \\\\ + tar xzvf elastic-agent--x86_64.rpm \\\\ + cd elastic-agent--x86_64 \\\\ + sudo elastic-agent enroll \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1" `); }); it('should return the correct command if the the policyId is not set for deb', () => { - const res = getInstallCommandForPlatform('http://elasticsearch:9200', 'service-token-1'); + const res = getInstallCommandForPlatform( + 'deb', + 'http://elasticsearch:9200', + 'service-token-1' + ); - expect(res.deb).toMatchInlineSnapshot(` - "sudo elastic-agent enroll \\\\ + expect(res).toMatchInlineSnapshot(` + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--amd64.deb \\\\ + tar xzvf elastic-agent--amd64.deb \\\\ + cd elastic-agent--amd64 \\\\ + sudo elastic-agent enroll \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1" `); @@ -61,6 +96,7 @@ describe('getInstallCommandForPlatform', () => { it('should return the correct command sslCATrustedFingerprint option is passed', () => { const res = getInstallCommandForPlatform( + 'linux', 'http://elasticsearch:9200', 'service-token-1', undefined, @@ -69,8 +105,11 @@ describe('getInstallCommandForPlatform', () => { 'fingerprint123456' ); - expect(res.linux).toMatchInlineSnapshot(` - "sudo ./elastic-agent install \\\\ + expect(res).toMatchInlineSnapshot(` + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip \\\\ + tar xzvf elastic-agent--linux-x86_64.zip \\\\ + cd elastic-agent--linux-x86_64 \\\\ + sudo ./elastic-agent install \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ --fleet-server-es-ca-trusted-fingerprint=fingerprint123456" @@ -81,13 +120,17 @@ describe('getInstallCommandForPlatform', () => { describe('with policy id', () => { it('should return the correct command if the the policyId is set for linux', () => { const res = getInstallCommandForPlatform( + 'linux', 'http://elasticsearch:9200', 'service-token-1', 'policy-1' ); - expect(res.linux).toMatchInlineSnapshot(` - "sudo ./elastic-agent install \\\\ + expect(res).toMatchInlineSnapshot(` + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip \\\\ + tar xzvf elastic-agent--linux-x86_64.zip \\\\ + cd elastic-agent--linux-x86_64 \\\\ + sudo ./elastic-agent install \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ --fleet-server-policy=policy-1" @@ -96,13 +139,17 @@ describe('getInstallCommandForPlatform', () => { it('should return the correct command if the the policyId is set for mac', () => { const res = getInstallCommandForPlatform( + 'mac', 'http://elasticsearch:9200', 'service-token-1', 'policy-1' ); - expect(res.mac).toMatchInlineSnapshot(` - "sudo ./elastic-agent install \\\\ + expect(res).toMatchInlineSnapshot(` + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--darwin-x86_64.tar.gz \\\\ + tar xzvf elastic-agent--darwin-x86_64.tar.gz \\\\ + cd elastic-agent--darwin-x86_64 \\\\ + sudo ./elastic-agent install \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ --fleet-server-policy=policy-1" @@ -111,13 +158,17 @@ describe('getInstallCommandForPlatform', () => { it('should return the correct command if the the policyId is set for windows', () => { const res = getInstallCommandForPlatform( + 'windows', 'http://elasticsearch:9200', 'service-token-1', 'policy-1' ); - expect(res.windows).toMatchInlineSnapshot(` - ".\\\\elastic-agent.exe install \` + expect(res).toMatchInlineSnapshot(` + "wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--windows-x86_64.tar.gz -OutFile elastic-agent--windows-x86_64.tar.gz \` + Expand-Archive .\\\\elastic-agent--windows-x86_64.tar.gz \` + cd elastic-agent--windows-x86_64\` + .\\\\elastic-agent.exe install \` --fleet-server-es=http://elasticsearch:9200 \` --fleet-server-service-token=service-token-1 \` --fleet-server-policy=policy-1" @@ -126,13 +177,17 @@ describe('getInstallCommandForPlatform', () => { it('should return the correct command if the the policyId is set for rpm', () => { const res = getInstallCommandForPlatform( + 'rpm', 'http://elasticsearch:9200', 'service-token-1', 'policy-1' ); - expect(res.rpm).toMatchInlineSnapshot(` - "sudo elastic-agent enroll \\\\ + expect(res).toMatchInlineSnapshot(` + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--x86_64.rpm \\\\ + tar xzvf elastic-agent--x86_64.rpm \\\\ + cd elastic-agent--x86_64 \\\\ + sudo elastic-agent enroll \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ --fleet-server-policy=policy-1" @@ -141,13 +196,17 @@ describe('getInstallCommandForPlatform', () => { it('should return the correct command if the the policyId is set for deb', () => { const res = getInstallCommandForPlatform( + 'deb', 'http://elasticsearch:9200', 'service-token-1', 'policy-1' ); - expect(res.deb).toMatchInlineSnapshot(` - "sudo elastic-agent enroll \\\\ + expect(res).toMatchInlineSnapshot(` + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--amd64.deb \\\\ + tar xzvf elastic-agent--amd64.deb \\\\ + cd elastic-agent--amd64 \\\\ + sudo elastic-agent enroll \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ --fleet-server-policy=policy-1" @@ -158,6 +217,7 @@ describe('getInstallCommandForPlatform', () => { describe('with policy id and fleet server host and production deployment', () => { it('should return the correct command if the the policyId is set for linux', () => { const res = getInstallCommandForPlatform( + 'linux', 'http://elasticsearch:9200', 'service-token-1', 'policy-1', @@ -165,8 +225,11 @@ describe('getInstallCommandForPlatform', () => { true ); - expect(res.linux).toMatchInlineSnapshot(` - "sudo ./elastic-agent install --url=http://fleetserver:8220 \\\\ + expect(res).toMatchInlineSnapshot(` + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip \\\\ + tar xzvf elastic-agent--linux-x86_64.zip \\\\ + cd elastic-agent--linux-x86_64 \\\\ + sudo ./elastic-agent install--url=http://fleetserver:8220 \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ --fleet-server-policy=policy-1 \\\\ @@ -179,6 +242,7 @@ describe('getInstallCommandForPlatform', () => { it('should return the correct command if the the policyId is set for mac', () => { const res = getInstallCommandForPlatform( + 'mac', 'http://elasticsearch:9200', 'service-token-1', 'policy-1', @@ -186,8 +250,11 @@ describe('getInstallCommandForPlatform', () => { true ); - expect(res.mac).toMatchInlineSnapshot(` - "sudo ./elastic-agent install --url=http://fleetserver:8220 \\\\ + expect(res).toMatchInlineSnapshot(` + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--darwin-x86_64.tar.gz \\\\ + tar xzvf elastic-agent--darwin-x86_64.tar.gz \\\\ + cd elastic-agent--darwin-x86_64 \\\\ + sudo ./elastic-agent install --url=http://fleetserver:8220 \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ --fleet-server-policy=policy-1 \\\\ @@ -200,6 +267,7 @@ describe('getInstallCommandForPlatform', () => { it('should return the correct command if the the policyId is set for windows', () => { const res = getInstallCommandForPlatform( + 'windows', 'http://elasticsearch:9200', 'service-token-1', 'policy-1', @@ -207,8 +275,11 @@ describe('getInstallCommandForPlatform', () => { true ); - expect(res.windows).toMatchInlineSnapshot(` - ".\\\\elastic-agent.exe install --url=http://fleetserver:8220 \` + expect(res).toMatchInlineSnapshot(` + "wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--windows-x86_64.tar.gz -OutFile elastic-agent--windows-x86_64.tar.gz \` + Expand-Archive .\\\\elastic-agent--windows-x86_64.tar.gz \` + cd elastic-agent--windows-x86_64\` + .\\\\elastic-agent.exe install --url=http://fleetserver:8220 \` --fleet-server-es=http://elasticsearch:9200 \` --fleet-server-service-token=service-token-1 \` --fleet-server-policy=policy-1 \` @@ -221,6 +292,7 @@ describe('getInstallCommandForPlatform', () => { it('should return the correct command if the the policyId is set for rpm', () => { const res = getInstallCommandForPlatform( + 'rpm', 'http://elasticsearch:9200', 'service-token-1', 'policy-1', @@ -228,8 +300,11 @@ describe('getInstallCommandForPlatform', () => { true ); - expect(res.rpm).toMatchInlineSnapshot(` - "sudo elastic-agent enroll --url=http://fleetserver:8220 \\\\ + expect(res).toMatchInlineSnapshot(` + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--x86_64.rpm \\\\ + tar xzvf elastic-agent--x86_64.rpm \\\\ + cd elastic-agent--x86_64 \\\\ + sudo elastic-agent enroll --url=http://fleetserver:8220 \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ --fleet-server-policy=policy-1 \\\\ @@ -242,6 +317,7 @@ describe('getInstallCommandForPlatform', () => { it('should return the correct command if the the policyId is set for deb', () => { const res = getInstallCommandForPlatform( + 'deb', 'http://elasticsearch:9200', 'service-token-1', 'policy-1', @@ -249,8 +325,11 @@ describe('getInstallCommandForPlatform', () => { true ); - expect(res.deb).toMatchInlineSnapshot(` - "sudo elastic-agent enroll --url=http://fleetserver:8220 \\\\ + expect(res).toMatchInlineSnapshot(` + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--amd64.deb \\\\ + tar xzvf elastic-agent--amd64.deb \\\\ + cd elastic-agent--amd64 \\\\ + sudo elastic-agent enroll --url=http://fleetserver:8220 \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ --fleet-server-policy=policy-1 \\\\ diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts new file mode 100644 index 0000000000000..525af7cf95103 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts @@ -0,0 +1,119 @@ +/* + * 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 type { PLATFORM_TYPE } from '../../../hooks'; + +export type CommandsByPlatform = { + [key in PLATFORM_TYPE]: string; +}; + +function getArtifact(platform: PLATFORM_TYPE, kibanaVersion: string) { + const ARTIFACT_BASE_URL = 'https://artifacts.elastic.co/downloads/beats/elastic-agent'; + + const artifactMap: Record< + PLATFORM_TYPE, + { fullUrl: string; filename: string; unpackedDir: string } + > = { + linux: { + fullUrl: `${ARTIFACT_BASE_URL}/elastic-agent-${kibanaVersion}-linux-x86_64.zip`, + filename: `elastic-agent-${kibanaVersion}-linux-x86_64.zip`, + unpackedDir: `elastic-agent-${kibanaVersion}-linux-x86_64`, + }, + mac: { + fullUrl: `${ARTIFACT_BASE_URL}/elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz`, + filename: `elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz`, + unpackedDir: `elastic-agent-${kibanaVersion}-darwin-x86_64`, + }, + windows: { + fullUrl: `${ARTIFACT_BASE_URL}/elastic-agent-${kibanaVersion}-windows-x86_64.tar.gz`, + filename: `elastic-agent-${kibanaVersion}-windows-x86_64.tar.gz`, + unpackedDir: `elastic-agent-${kibanaVersion}-windows-x86_64`, + }, + deb: { + fullUrl: `${ARTIFACT_BASE_URL}/elastic-agent-${kibanaVersion}-amd64.deb`, + filename: `elastic-agent-${kibanaVersion}-amd64.deb`, + unpackedDir: `elastic-agent-${kibanaVersion}-amd64`, + }, + rpm: { + fullUrl: `${ARTIFACT_BASE_URL}/elastic-agent-${kibanaVersion}-x86_64.rpm`, + filename: `elastic-agent-${kibanaVersion}-x86_64.rpm`, + unpackedDir: `elastic-agent-${kibanaVersion}-x86_64`, + }, + }; + + return artifactMap[platform]; +} + +export function getInstallCommandForPlatform( + platform: PLATFORM_TYPE, + esHost: string, + serviceToken: string, + policyId?: string, + fleetServerHost?: string, + isProductionDeployment?: boolean, + sslCATrustedFingerprint?: string, + kibanaVersion?: string +): string { + const newLineSeparator = platform === 'windows' ? '`\n' : '\\\n'; + + const artifact = getArtifact(platform, kibanaVersion ?? ''); + const downloadCommand = + platform === 'windows' + ? [ + `wget ${artifact.fullUrl} -OutFile ${artifact.filename}`, + `Expand-Archive .\\${artifact.filename}`, + `cd ${artifact.unpackedDir}`, + ].join(` ${newLineSeparator}`) + : [ + `curl -L -O ${artifact.fullUrl}`, + `tar xzvf ${artifact.filename}`, + `cd ${artifact.unpackedDir}`, + ].join(` ${newLineSeparator}`); + + const commandArguments = []; + + if (isProductionDeployment && fleetServerHost) { + commandArguments.push(['url', fleetServerHost]); + } + + commandArguments.push(['fleet-server-es', esHost]); + commandArguments.push(['fleet-server-service-token', serviceToken]); + if (policyId) { + commandArguments.push(['fleet-server-policy', policyId]); + } + + if (sslCATrustedFingerprint) { + commandArguments.push(['fleet-server-es-ca-trusted-fingerprint', sslCATrustedFingerprint]); + } + + if (isProductionDeployment) { + commandArguments.push(['certificate-authorities', '']); + if (!sslCATrustedFingerprint) { + commandArguments.push(['fleet-server-es-ca', '']); + } + commandArguments.push(['fleet-server-cert', '']); + commandArguments.push(['fleet-server-cert-key', '']); + } + + const commandArgumentsStr = commandArguments.reduce((acc, [key, val]) => { + if (acc === '' && key === 'url') { + return `--${key}=${val}`; + } + const valOrEmpty = val ? `=${val}` : ''; + return (acc += ` ${newLineSeparator} --${key}${valOrEmpty}`); + }, ''); + + const commands = { + linux: `${downloadCommand} ${newLineSeparator}sudo ./elastic-agent install${commandArgumentsStr}`, + mac: `${downloadCommand} ${newLineSeparator}sudo ./elastic-agent install ${commandArgumentsStr}`, + windows: `${downloadCommand}${newLineSeparator}.\\elastic-agent.exe install ${commandArgumentsStr}`, + deb: `${downloadCommand} ${newLineSeparator}sudo elastic-agent enroll ${commandArgumentsStr}`, + rpm: `${downloadCommand} ${newLineSeparator}sudo elastic-agent enroll ${commandArgumentsStr}`, + }; + + return commands[platform]; +} diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/index.ts b/x-pack/plugins/fleet/public/applications/fleet/components/index.ts index 5e927c5b0e3d6..a299013ab547e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/index.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/components/index.ts @@ -8,3 +8,4 @@ export * from '../../../components'; export * from './search_bar'; +export * from './fleet_server_instructions'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx index 9fdcc0f73297f..e1c6bbafa21a7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx @@ -28,11 +28,11 @@ import { useStartServices } from '../../../../hooks'; import { AgentPolicyPackageBadge } from '../../../../components'; -import { policyHasFleetServer } from '../../../agents/services/has_fleet_server'; - import { AgentPolicyDeleteProvider } from '../agent_policy_delete_provider'; import type { ValidationResults } from '../agent_policy_validation'; +import { policyHasFleetServer } from '../../../../services'; + import { useOutputOptions, DEFAULT_OUTPUT_VALUE } from './hooks'; interface Props { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx index e9b04aacecb68..44e87d7fb4e63 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx @@ -18,8 +18,7 @@ import { AgentUpgradeAgentModal, } from '../../components'; import { useAgentRefresh } from '../hooks'; -import { isAgentUpgradeable } from '../../../../services'; -import { policyHasFleetServer } from '../../services/has_fleet_server'; +import { isAgentUpgradeable, policyHasFleetServer } from '../../../../services'; export const AgentDetailsActionMenu: React.FunctionComponent<{ agent: Agent; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx index 7b3f1fedb160a..d5757419e8ea7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useMemo, useCallback, useRef, useEffect } from 'react'; +import React, { useState, useMemo, useCallback, useRef, useEffect, useContext } from 'react'; import { EuiBasicTable, EuiButton, @@ -52,6 +52,8 @@ import { } from '../components'; import { useFleetServerUnhealthy } from '../hooks/use_fleet_server_unhealthy'; +import { agentFlyoutContext } from '..'; + import { AgentTableHeader } from './components/table_header'; import type { SelectionMode } from './components/bulk_actions'; import { SearchAndFilterBar } from './components/search_and_filter_bar'; @@ -201,6 +203,8 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { isOpen: false, }); + const flyoutContext = useContext(agentFlyoutContext); + // Agent actions states const [agentToReassign, setAgentToReassign] = useState(undefined); const [agentToUnenroll, setAgentToUnenroll] = useState(undefined); @@ -378,11 +382,8 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { // Fleet server unhealthy status const { isUnhealthy: isFleetServerUnhealthy } = useFleetServerUnhealthy(); const onClickAddFleetServer = useCallback(() => { - setEnrollmentFlyoutState({ - isOpen: true, - selectedPolicyId: agentPolicies.length > 0 ? agentPolicies[0].id : undefined, - }); - }, [agentPolicies]); + flyoutContext?.openFleetServerFlyout(); + }, [flyoutContext]); const columns = [ { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx new file mode 100644 index 0000000000000..86990d84d5130 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx @@ -0,0 +1,104 @@ +/* + * 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 React, { useContext } from 'react'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { useStartServices } from '../../../../hooks'; + +import { agentFlyoutContext } from '../..'; + +export const EnrollmentRecommendation: React.FunctionComponent<{ + showStandaloneTab: () => void; +}> = ({ showStandaloneTab }) => { + const flyoutContext = useContext(agentFlyoutContext); + + const { docLinks } = useStartServices(); + + return ( + <> + + + + + + + +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + + + + ), + }} + /> +
  • +
+
+ + + + + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx deleted file mode 100644 index 93547bba36de8..0000000000000 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx +++ /dev/null @@ -1,748 +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 React, { useState, useMemo, useCallback, useEffect } from 'react'; -import { - EuiButton, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiText, - EuiLink, - EuiSteps, - EuiCode, - EuiCodeBlock, - EuiCallOut, - EuiRadioGroup, - EuiFieldText, - EuiForm, - EuiFormErrorText, -} from '@elastic/eui'; -import type { EuiStepProps } from '@elastic/eui/src/components/steps/step'; -import styled from 'styled-components'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; - -import { DownloadStep, SelectCreateAgentPolicy } from '../../../../components'; -import { - useStartServices, - useDefaultOutput, - sendGenerateServiceToken, - usePlatform, - useGetAgentPolicies, - useGetSettings, - sendPutSettings, - sendGetFleetStatus, - useFleetStatus, - useLink, -} from '../../../../hooks'; -import type { PLATFORM_TYPE } from '../../../../hooks'; -import type { AgentPolicy } from '../../../../types'; -import { FleetServerOnPremRequiredCallout } from '../../components'; - -import { policyHasFleetServer } from '../../services/has_fleet_server'; - -import { PlatformSelector } from '../../../../../../components/enrollment_instructions/manual/platform_selector'; - -import type { CommandsByPlatform } from './install_command_utils'; -import { getInstallCommandForPlatform } from './install_command_utils'; - -const URL_REGEX = /^(https):\/\/[^\s$.?#].[^\s]*$/gm; -const REFRESH_INTERVAL = 10000; - -type DeploymentMode = 'production' | 'quickstart'; - -const FlexItemWithMinWidth = styled(EuiFlexItem)` - min-width: 0px; - max-width: 100%; -`; - -export const ContentWrapper = styled(EuiFlexGroup)` - height: 100%; - margin: 0 auto; - max-width: 800px; -`; - -// Otherwise the copy button is over the text -const CommandCode = styled.div.attrs(() => { - return { - className: 'eui-textBreakAll', - }; -})` - margin-right: ${(props) => props.theme.eui.paddingSizes.m}; -`; - -export const ServiceTokenStep = ({ - disabled = false, - serviceToken, - getServiceToken, - isLoadingServiceToken, -}: { - disabled?: boolean; - serviceToken?: string; - getServiceToken: () => void; - isLoadingServiceToken: boolean; -}): EuiStepProps => { - return { - title: i18n.translate('xpack.fleet.fleetServerSetup.stepGenerateServiceTokenTitle', { - defaultMessage: 'Generate a service token', - }), - status: disabled ? 'disabled' : undefined, - children: !disabled && ( - <> - - - - - {!serviceToken ? ( - - - { - getServiceToken(); - }} - data-test-subj="fleetServerGenerateServiceTokenBtn" - > - - - - - ) : ( - <> - - } - /> - - - - - - - - - - {serviceToken} - - - - - )} - - ), - }; -}; - -export const FleetServerCommandStep = ({ - serviceToken, - installCommand, - platform, - setPlatform, -}: { - serviceToken?: string; - installCommand: CommandsByPlatform; - platform: string; - setPlatform: (platform: PLATFORM_TYPE) => void; -}): EuiStepProps => { - const { docLinks } = useStartServices(); - - return { - title: i18n.translate('xpack.fleet.fleetServerSetup.stepInstallAgentTitle', { - defaultMessage: 'Start Fleet Server', - }), - status: !serviceToken ? 'disabled' : undefined, - children: serviceToken ? ( - <> - - - - - ), - }} - /> - - - - - ) : null, - }; -}; - -export const useFleetServerInstructions = (policyId?: string) => { - const { output } = useDefaultOutput(); - const { notifications } = useStartServices(); - const [serviceToken, setServiceToken] = useState(); - const [isLoadingServiceToken, setIsLoadingServiceToken] = useState(false); - const { platform, setPlatform } = usePlatform(); - const [deploymentMode, setDeploymentMode] = useState('production'); - const { data: settings, resendRequest: refreshSettings } = useGetSettings(); - const fleetServerHost = settings?.item.fleet_server_hosts?.[0]; - const esHost = output?.hosts?.[0]; - const sslCATrustedFingerprint: string | undefined = output?.ca_trusted_fingerprint; - - const installCommand = useMemo((): CommandsByPlatform => { - if (!serviceToken || !esHost) { - return { - linux: '', - mac: '', - windows: '', - deb: '', - rpm: '', - }; - } - - return getInstallCommandForPlatform( - esHost, - serviceToken, - policyId, - fleetServerHost, - deploymentMode === 'production', - sslCATrustedFingerprint - ); - }, [serviceToken, esHost, policyId, fleetServerHost, deploymentMode, sslCATrustedFingerprint]); - - const getServiceToken = useCallback(async () => { - setIsLoadingServiceToken(true); - try { - const { data } = await sendGenerateServiceToken(); - if (data?.value) { - setServiceToken(data?.value); - } - } catch (err) { - notifications.toasts.addError(err, { - title: i18n.translate('xpack.fleet.fleetServerSetup.errorGeneratingTokenTitleText', { - defaultMessage: 'Error generating token', - }), - }); - } - - setIsLoadingServiceToken(false); - }, [notifications.toasts]); - - const addFleetServerHost = useCallback( - async (host: string) => { - const res = await sendPutSettings({ - fleet_server_hosts: [host, ...(settings?.item.fleet_server_hosts || [])], - }); - if (res.error) { - throw res.error; - } - await refreshSettings(); - }, - [refreshSettings, settings?.item.fleet_server_hosts] - ); - - return { - addFleetServerHost, - fleetServerHost, - deploymentMode, - setDeploymentMode, - serviceToken, - getServiceToken, - isLoadingServiceToken, - installCommand, - platform, - setPlatform, - }; -}; - -const AgentPolicySelectionStep = ({ - selectedPolicy, - setPolicyId, - agentPolicies, - refreshAgentPolicies, -}: { - selectedPolicy?: AgentPolicy; - setPolicyId: (v?: string) => void; - agentPolicies: AgentPolicy[]; - refreshAgentPolicies: () => void; -}): EuiStepProps => { - return { - title: - agentPolicies.length === 0 - ? i18n.translate('xpack.fleet.fleetServerSetup.stepCreateAgentPolicyTitle', { - defaultMessage: 'Create an agent policy to host Fleet Server', - }) - : i18n.translate('xpack.fleet.fleetServerSetup.stepSelectAgentPolicyTitle', { - defaultMessage: 'Select an agent policy to host Fleet Server', - }), - status: undefined, - children: ( - <> - - - ), - }; -}; - -export const addFleetServerHostStep = ({ - addFleetServerHost, -}: { - addFleetServerHost: (v: string) => Promise; -}): EuiStepProps => { - return { - title: i18n.translate('xpack.fleet.fleetServerSetup.addFleetServerHostStepTitle', { - defaultMessage: 'Add your Fleet Server host', - }), - status: undefined, - children: , - }; -}; - -export const AddFleetServerHostStepContent = ({ - addFleetServerHost, -}: { - addFleetServerHost: (v: string) => Promise; -}) => { - const [calloutHost, setCalloutHost] = useState(); - const [isLoading, setIsLoading] = useState(false); - const [fleetServerHost, setFleetServerHost] = useState(''); - const [error, setError] = useState(); - const { notifications } = useStartServices(); - - const { getHref } = useLink(); - - const validate = useCallback( - (host: string) => { - if (host.match(URL_REGEX)) { - setError(undefined); - return true; - } else { - setError( - i18n.translate('xpack.fleet.fleetServerSetup.addFleetServerHostInvalidUrlError', { - defaultMessage: 'Valid https URL required.', - }) - ); - return false; - } - }, - [setError] - ); - - const onSubmit = useCallback(async () => { - try { - setIsLoading(true); - if (validate(fleetServerHost)) { - await addFleetServerHost(fleetServerHost); - setCalloutHost(fleetServerHost); - setFleetServerHost(''); - } else { - setCalloutHost(''); - } - } catch (err) { - notifications.toasts.addError(err, { - title: i18n.translate('xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle', { - defaultMessage: 'Error adding Fleet Server host', - }), - }); - } finally { - setIsLoading(false); - } - }, [fleetServerHost, addFleetServerHost, validate, notifications.toasts]); - - const onChange = useCallback( - (e: React.ChangeEvent) => { - setFleetServerHost(e.target.value); - if (error) { - validate(e.target.value); - } - }, - [error, validate, setFleetServerHost] - ); - - return ( - - - 8220 }} - /> - - - - - - - - } - data-test-subj="fleetServerHostInput" - /> - {error && {error}} - - - - - - - - {calloutHost && ( - <> - - - } - > - - - - ), - }} - /> - - - )} - - ); -}; - -export const deploymentModeStep = ({ - deploymentMode, - setDeploymentMode, -}: { - deploymentMode: DeploymentMode; - setDeploymentMode: (v: DeploymentMode) => void; -}): EuiStepProps => { - return { - title: i18n.translate('xpack.fleet.fleetServerSetup.stepDeploymentModeTitle', { - defaultMessage: 'Choose a deployment mode for security', - }), - status: undefined, - children: ( - - ), - }; -}; - -const DeploymentModeStepContent = ({ - deploymentMode, - setDeploymentMode, -}: { - deploymentMode: DeploymentMode; - setDeploymentMode: (v: DeploymentMode) => void; -}) => { - const onChangeCallback = useCallback( - (v: string) => { - const value = v.split('_')[0]; - if (value === 'production' || value === 'quickstart') { - setDeploymentMode(value); - } - }, - [setDeploymentMode] - ); - - // radio id has to be unique so that the component works even if appears twice in DOM (Agents tab, Add agent flyout) - const radioSuffix = useMemo(() => Date.now(), []); - - return ( - <> - - - - - - - - ), - }} - /> - ), - }, - { - id: `production_${radioSuffix}`, - label: ( - - - - ), - }} - /> - ), - }, - ]} - idSelected={`${deploymentMode}_${radioSuffix}`} - onChange={onChangeCallback} - name={`radio group ${radioSuffix}`} - /> - - ); -}; - -const WaitingForFleetServerStep = ({ - status, -}: { - status: 'loading' | 'disabled' | 'complete'; -}): EuiStepProps => { - return { - title: i18n.translate('xpack.fleet.fleetServerSetup.stepWaitingForFleetServerTitle', { - defaultMessage: 'Waiting for Fleet Server to connect...', - }), - status, - children: undefined, - }; -}; - -const CompleteStep = (): EuiStepProps => { - const fleetStatus = useFleetStatus(); - - const onContinueClick = () => { - fleetStatus.refresh(); - }; - - return { - title: i18n.translate('xpack.fleet.fleetServerSetup.stepFleetServerCompleteTitle', { - defaultMessage: 'Fleet Server connected', - }), - status: 'complete', - children: ( - <> - - - - - - - ), - }; -}; - -const findPolicyById = (policies: AgentPolicy[], id: string | undefined) => { - if (!id) return undefined; - return policies.find((p) => p.id === id); -}; - -export const OnPremInstructions: React.FC = () => { - const { notifications } = useStartServices(); - - const { data, resendRequest: refreshAgentPolicies } = useGetAgentPolicies({ full: true }); - - const agentPolicies = useMemo( - () => (data ? data.items.filter((item) => policyHasFleetServer(item)) : []), - [data] - ); - - // Select default value - let defaultValue = ''; - if (agentPolicies.length) { - defaultValue = agentPolicies[0].id; - } - const [policyId, setPolicyId] = useState(defaultValue); - const selectedPolicy = findPolicyById(agentPolicies, policyId); - - const { - serviceToken, - getServiceToken, - isLoadingServiceToken, - installCommand, - platform, - setPlatform, - deploymentMode, - setDeploymentMode, - fleetServerHost, - addFleetServerHost, - } = useFleetServerInstructions(policyId); - - const { docLinks } = useStartServices(); - - const [isWaitingForFleetServer, setIsWaitingForFleetServer] = useState(true); - - useEffect(() => { - const interval = setInterval(async () => { - try { - const res = await sendGetFleetStatus(); - if (res.error) { - throw res.error; - } - if (res.data?.isReady && !res.data?.missing_requirements?.includes('fleet_server')) { - setIsWaitingForFleetServer(false); - } - } catch (err) { - notifications.toasts.addError(err, { - title: i18n.translate('xpack.fleet.fleetServerSetup.errorRefreshingFleetServerStatus', { - defaultMessage: 'Error refreshing Fleet Server status', - }), - }); - } - }, REFRESH_INTERVAL); - - return () => clearInterval(interval); - }, [notifications.toasts]); - - return ( - <> - - - -

- -

- - - - - ), - }} - /> -
- - - - ); -}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/index.tsx index 3118fda75400f..293be6d8b98e4 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/index.tsx @@ -6,4 +6,4 @@ */ export { CloudInstructions } from './fleet_server_cloud_instructions'; -export * from './fleet_server_on_prem_instructions'; +export { EnrollmentRecommendation } from './enrollment_recommendation'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils.ts deleted file mode 100644 index b73eb547b6ddf..0000000000000 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils.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 type { PLATFORM_TYPE } from '../../../../hooks'; - -export type CommandsByPlatform = { - [key in PLATFORM_TYPE]: string; -}; - -export function getInstallCommandForPlatform( - esHost: string, - serviceToken: string, - policyId?: string, - fleetServerHost?: string, - isProductionDeployment?: boolean, - sslCATrustedFingerprint?: string -): CommandsByPlatform { - const commandArguments: Array<[string, string] | [string]> = []; - - if (isProductionDeployment && fleetServerHost) { - commandArguments.push(['url', fleetServerHost]); - } - - commandArguments.push(['fleet-server-es', esHost]); - commandArguments.push(['fleet-server-service-token', serviceToken]); - if (policyId) { - commandArguments.push(['fleet-server-policy', policyId]); - } - - if (sslCATrustedFingerprint) { - commandArguments.push(['fleet-server-es-ca-trusted-fingerprint', sslCATrustedFingerprint]); - } - - if (isProductionDeployment) { - commandArguments.push(['certificate-authorities', '']); - if (!sslCATrustedFingerprint) { - commandArguments.push(['fleet-server-es-ca', '']); - } - commandArguments.push(['fleet-server-cert', '']); - commandArguments.push(['fleet-server-cert-key', '']); - } - - const commandArgumentsStr = (platform?: string) => { - const newLineSeparator = platform === 'windows' ? '`\n' : '\\\n'; - return commandArguments.reduce((acc, [key, val]) => { - if (acc === '' && key === 'url') { - return `--${key}=${val}`; - } - const valOrEmpty = val ? `=${val}` : ''; - return (acc += ` ${newLineSeparator} --${key}${valOrEmpty}`); - }, ''); - }; - - return { - linux: `sudo ./elastic-agent install ${commandArgumentsStr()}`, - mac: `sudo ./elastic-agent install ${commandArgumentsStr()}`, - windows: `.\\elastic-agent.exe install ${commandArgumentsStr('windows')}`, - deb: `sudo elastic-agent enroll ${commandArgumentsStr()}`, - rpm: `sudo elastic-agent enroll ${commandArgumentsStr()}`, - }; -} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx index bee7fbfd85371..b4818a6908adf 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx @@ -13,9 +13,11 @@ import { useStartServices, sendGetPermissionsCheck } from '../../../hooks'; import { FleetServerMissingPrivileges } from '../components/fleet_server_callouts'; -import { Loading } from '../../../components'; +import { Loading } from '../components'; -import { CloudInstructions, OnPremInstructions } from './components'; +import { FleetServerInstructions } from '../../../components'; + +import { CloudInstructions, EnrollmentRecommendation } from './components'; const FlexItemWithMinWidth = styled(EuiFlexItem)` min-width: 0px; @@ -27,7 +29,16 @@ const ContentWrapper = styled(EuiFlexGroup)` margin: 0 auto; `; -export const FleetServerRequirementPage = () => { +export const FleetServerRequirementPage: React.FunctionComponent< + | { + showEnrollmentRecommendation?: false; + showStandaloneTab?: never; + } + | { + showEnrollmentRecommendation?: true; + showStandaloneTab: () => void; + } +> = ({ showStandaloneTab = () => {}, showEnrollmentRecommendation = true }) => { const startService = useStartServices(); const deploymentUrl = startService.cloud?.deploymentUrl; @@ -56,12 +67,7 @@ export const FleetServerRequirementPage = () => { return ( <> - + {deploymentUrl ? ( @@ -69,8 +75,10 @@ export const FleetServerRequirementPage = () => { ) : permissionsError ? ( + ) : showEnrollmentRecommendation ? ( + ) : ( - + )} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx index d2434e109ca31..57da2fcf36d76 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx @@ -5,13 +5,13 @@ * 2.0. */ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { createContext, useCallback, useEffect, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { Router, Route, Switch, useHistory } from 'react-router-dom'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPortal } from '@elastic/eui'; import { FLEET_ROUTING_PATHS } from '../../constants'; -import { Loading, Error, AgentEnrollmentFlyout } from '../../components'; +import { Loading, Error, AgentEnrollmentFlyout, FleetServerFlyout } from '../../components'; import { useConfig, useFleetStatus, useBreadcrumbs, useAuthz, useGetSettings } from '../../hooks'; import { DefaultLayout, WithoutHeaderLayout } from '../../layouts'; @@ -21,6 +21,18 @@ import { AgentDetailsPage } from './agent_details_page'; import { NoAccessPage } from './error_pages/no_access'; import { FleetServerUpgradeModal } from './components/fleet_server_upgrade_modal'; +// TODO: Move all instances of toggling these flyouts to a global context object to avoid cases in which +// we can render duplicate "stacked" flyouts +export const agentFlyoutContext = createContext< + | { + openEnrollmentFlyout: () => void; + closeEnrollmentFlyout: () => void; + openFleetServerFlyout: () => void; + closeFleetServerFlyout: () => void; + } + | undefined +>(undefined); + export const AgentsApp: React.FunctionComponent = () => { useBreadcrumbs('agent_list'); const history = useHistory(); @@ -31,6 +43,8 @@ export const AgentsApp: React.FunctionComponent = () => { const settings = useGetSettings(); const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false); + const [isFleetServerFlyoutOpen, setIsFleetServerFlyoutOpen] = useState(false); + const [fleetServerModalVisible, setFleetServerModalVisible] = useState(false); const onCloseFleetServerModal = useCallback(() => { setFleetServerModalVisible(false); @@ -81,15 +95,6 @@ export const AgentsApp: React.FunctionComponent = () => { const rightColumn = hasOnlyFleetServerMissingRequirement ? ( <> - {isEnrollmentFlyoutOpen && ( - - setIsEnrollmentFlyoutOpen(false)} - /> - - )} { ) : undefined; return ( - - - - - - - - {fleetServerModalVisible && ( - - )} - {hasOnlyFleetServerMissingRequirement ? ( - - ) : ( - - )} - - - - + setIsEnrollmentFlyoutOpen(true), + closeEnrollmentFlyout: () => setIsEnrollmentFlyoutOpen(false), + openFleetServerFlyout: () => setIsFleetServerFlyoutOpen(true), + closeFleetServerFlyout: () => setIsFleetServerFlyoutOpen(false), + }} + > + + + + + + + + {fleetServerModalVisible && ( + + )} + {hasOnlyFleetServerMissingRequirement ? ( + + ) : ( + + )} + + + + + {isEnrollmentFlyoutOpen && ( + + setIsEnrollmentFlyoutOpen(false)} + /> + + )} + + {isFleetServerFlyoutOpen && ( + + setIsFleetServerFlyoutOpen(false)} /> + + )} + + ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.test.tsx index 151a3d5354c17..aeb49928f3d1e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.test.tsx @@ -27,7 +27,7 @@ describe('useFleetServerHostsForm', () => { const { result } = testRenderer.renderHook(() => useFleetServerHostsForm([], onSucess)); act(() => - result.current.fleetServerHostsInput.props.onChange(['http://test.fr', 'http://test.fr']) + result.current.fleetServerHostsInput.props.onChange(['https://test.fr', 'https://test.fr']) ); await act(() => result.current.submit()); @@ -54,7 +54,7 @@ describe('useFleetServerHostsForm', () => { testRenderer.startServices.http.post.mockResolvedValue({}); const { result } = testRenderer.renderHook(() => useFleetServerHostsForm([], onSucess)); - act(() => result.current.fleetServerHostsInput.props.onChange(['http://test.fr'])); + act(() => result.current.fleetServerHostsInput.props.onChange(['https://test.fr'])); await act(() => result.current.submit()); expect(onSucess).toBeCalled(); @@ -67,14 +67,14 @@ describe('useFleetServerHostsForm', () => { const { result } = testRenderer.renderHook(() => useFleetServerHostsForm([], onSucess)); act(() => - result.current.fleetServerHostsInput.props.onChange(['http://test.fr', 'http://test.fr']) + result.current.fleetServerHostsInput.props.onChange(['https://test.fr', 'https://test.fr']) ); await act(() => result.current.submit()); expect(onSucess).not.toBeCalled(); expect(result.current.isDisabled).toBeTruthy(); - act(() => result.current.fleetServerHostsInput.props.onChange(['http://test.fr'])); + act(() => result.current.fleetServerHostsInput.props.onChange(['https://test.fr'])); expect(result.current.isDisabled).toBeFalsy(); await act(() => result.current.submit()); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.tsx index ac196576ba889..b19e8ddda0427 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.tsx @@ -15,7 +15,7 @@ import { isDiffPathProtocol } from '../../../../../../../common'; import { useConfirmModal } from '../../hooks/use_confirm_modal'; import { getAgentAndPolicyCount } from '../../services/agent_and_policies_count'; -const URL_REGEX = /^(https?):\/\/[^\s$.?#].[^\s]*$/gm; +const URL_REGEX = /^(https):\/\/[^\s$.?#].[^\s]*$/gm; const ConfirmTitle = () => ( { return { ...module, useGetSettings: jest.fn(), - sendGetFleetStatus: jest.fn(), sendGetOneAgentPolicy: jest.fn(), useGetAgents: jest.fn(), useGetAgentPolicies: jest.fn(), @@ -36,14 +35,14 @@ jest.mock('../../applications/fleet/sections/agents/hooks/use_fleet_server_unhea }); jest.mock( - '../../applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions', + '../../applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form', () => { const module = jest.requireActual( - '../../applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions' + '../../applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form' ); return { ...module, - useFleetServerInstructions: jest.fn(), + useAdvancedForm: jest.fn(), }; } ); @@ -82,6 +81,9 @@ jest.mock('./steps', () => { }; }); -jest.mock('../../applications/fleet/sections/agents/services/has_fleet_server', () => { - return { policyHasFleetServer: jest.fn().mockReturnValue(true) }; +jest.mock('../../services', () => { + return { + ...jest.requireActual('../../services'), + policyHasFleetServer: jest.fn().mockReturnValue(true), + }; }); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx index b2680129dd7d5..6e46ec90d5faf 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx @@ -25,8 +25,7 @@ import { useFleetStatus, } from '../../hooks'; -import { useFleetServerInstructions } from '../../applications/fleet/sections/agents/agent_requirements_page/components'; - +import { useAdvancedForm } from '../../applications/fleet/components/fleet_server_instructions/hooks'; import { useFleetServerUnhealthy } from '../../applications/fleet/sections/agents/hooks/use_fleet_server_unhealthy'; import type { FlyOutProps } from './types'; @@ -44,8 +43,8 @@ const TestComponent = (props: FlyOutProps) => ( ); -const setup = async (props?: FlyOutProps) => { - const testBed = await registerTestBed(TestComponent)(props); +const setup = (props?: FlyOutProps) => { + const testBed = registerTestBed(TestComponent)(props); const { find, component } = testBed; return { @@ -78,7 +77,7 @@ const testAgentPolicy: AgentPolicy = { describe('', () => { let testBed: TestBed; - beforeEach(async () => { + beforeEach(() => { (useGetSettings as jest.Mock).mockReturnValue({ data: { item: { fleet_server_hosts: ['test'] } }, }); @@ -93,13 +92,24 @@ describe('', () => { data: { item: { package_policies: [] } }, }); - (useFleetServerInstructions as jest.Mock).mockReturnValue({ + (useAdvancedForm as jest.Mock).mockReturnValue({ + eligibleFleetServerPolicies: [{ name: 'test', id: 'test' }], + refreshEligibleFleetServerPolicies: jest.fn(), + fleetServerPolicyId: 'test', + setFleetServerPolicyId: jest.fn(), + isFleetServerReady: true, serviceToken: 'test', - getServiceToken: jest.fn(), isLoadingServiceToken: false, - installCommand: jest.fn(), - platform: 'test', - setPlatform: jest.fn(), + generateServiceToken: jest.fn(), + fleetServerHostForm: { + saveFleetServerHost: jest.fn(), + fleetServerHost: 'https://test.server:8220', + setFleetServerHost: jest.fn(), + error: '', + validateFleetServerHost: jest.fn(), + }, + deploymentMode: 'quickstart', + setDeploymentMode: jest.fn(), }); (useGetAgents as jest.Mock).mockReturnValue({ @@ -111,8 +121,8 @@ describe('', () => { refreshAgentPolicies: jest.fn(), }); - await act(async () => { - testBed = await setup({ + act(() => { + testBed = setup({ onClose: jest.fn(), }); testBed.component.update(); @@ -123,15 +133,15 @@ describe('', () => { jest.clearAllMocks(); }); - it('should show loading when agent policies are loading', async () => { + it('should show loading when agent policies are loading', () => { (useAgentEnrollmentFlyoutData as jest.Mock).mockReturnValue?.({ agentPolicies: [], refreshAgentPolicies: jest.fn(), isLoadingInitialAgentPolicies: true, }); - await act(async () => { - testBed = await setup({ + act(() => { + testBed = setup({ onClose: jest.fn(), }); testBed.component.update(); @@ -143,7 +153,7 @@ describe('', () => { }); describe('managed instructions', () => { - it('uses the agent policy selection step', async () => { + it('uses the agent policy selection step', () => { const { exists } = testBed; expect(exists('agentEnrollmentFlyout')).toBe(true); @@ -152,10 +162,10 @@ describe('', () => { }); describe('with a specific policy', () => { - beforeEach(async () => { + beforeEach(() => { jest.clearAllMocks(); - await act(async () => { - testBed = await setup({ + act(() => { + testBed = setup({ agentPolicy: testAgentPolicy, onClose: jest.fn(), }); @@ -172,10 +182,10 @@ describe('', () => { }); describe('with a specific policy when no agentPolicies set', () => { - beforeEach(async () => { + beforeEach(() => { jest.clearAllMocks(); - await act(async () => { - testBed = await setup({ + act(() => { + testBed = setup({ agentPolicy: testAgentPolicy, onClose: jest.fn(), }); @@ -189,41 +199,41 @@ describe('', () => { expect(exists('agent-enrollment-key-selection-step')).toBe(true); }); }); - }); - // Skipped due to UI changing in https://github.com/elastic/kibana/issues/125534. These tests should be rethought overall - // to provide value around the new flyout structure - describe.skip('standalone instructions', () => { - it('uses the agent policy selection step', async () => { - const { exists, actions } = testBed; - actions.goToStandaloneTab(); + // Skipped due to UI changing in https://github.com/elastic/kibana/issues/125534. These tests should be rethought overall + // to provide value around the new flyout structure + describe.skip('standalone instructions', () => { + it('uses the agent policy selection step', async () => { + const { exists, actions } = testBed; + actions.goToStandaloneTab(); - expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(exists('agent-policy-selection-step')).toBe(true); - expect(exists('agent-enrollment-key-selection-step')).toBe(false); - }); + expect(exists('agentEnrollmentFlyout')).toBe(true); + expect(exists('agent-policy-selection-step')).toBe(true); + expect(exists('agent-enrollment-key-selection-step')).toBe(false); + }); - describe('with a specific policy', () => { - beforeEach(async () => { - jest.clearAllMocks(); - await act(async () => { - testBed = await setup({ - agentPolicy: testAgentPolicy, - onClose: jest.fn(), + describe('with a specific policy', () => { + beforeEach(() => { + jest.clearAllMocks(); + act(() => { + testBed = setup({ + agentPolicy: testAgentPolicy, + onClose: jest.fn(), + }); + testBed.component.update(); }); - testBed.component.update(); }); - }); - it('does not use either of the agent policy selection or enrollment key steps', () => { - const { exists, actions } = testBed; - jest.clearAllMocks(); + it('does not use either of the agent policy selection or enrollment key steps', () => { + const { exists, actions } = testBed; + jest.clearAllMocks(); - actions.goToStandaloneTab(); + actions.goToStandaloneTab(); - expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(exists('agent-policy-selection-step')).toBe(false); - expect(exists('agent-enrollment-key-selection-step')).toBe(false); + expect(exists('agentEnrollmentFlyout')).toBe(true); + expect(exists('agent-policy-selection-step')).toBe(false); + expect(exists('agent-enrollment-key-selection-step')).toBe(false); + }); }); }); }); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx index 81ec1236920b8..304239ac77dad 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx @@ -20,7 +20,7 @@ import { AgentPolicySelection } from '.'; interface Props { agentPolicies: AgentPolicy[]; - selectedPolicy?: AgentPolicy; + selectedPolicyId?: string; setSelectedPolicyId: (agentPolicyId?: string) => void; excludeFleetServer?: boolean; withKeySelection: boolean; @@ -34,7 +34,7 @@ export const SelectCreateAgentPolicy: React.FC = ({ agentPolicies, excludeFleetServer, setSelectedPolicyId, - selectedPolicy, + selectedPolicyId, withKeySelection, selectedApiKeyId, onKeyChange, @@ -111,7 +111,7 @@ export const SelectCreateAgentPolicy: React.FC = ({ onKeyChange={onKeyChange} excludeFleetServer={excludeFleetServer} onClickCreatePolicy={onClickCreatePolicy} - selectedPolicy={selectedPolicy} + selectedPolicyId={selectedPolicyId} setSelectedPolicyId={setSelectedPolicyId} isFleetServerPolicy={isFleetServerPolicy} /> diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx index c5a6076308525..4c02ddeeaaf27 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx @@ -23,7 +23,7 @@ describe('step select agent policy', () => { (renderResult = testRenderer.render( void; excludeFleetServer?: boolean; onClickCreatePolicy: () => void; @@ -50,23 +50,10 @@ type Props = { } ); -const resolveAgentId = ( - agentPolicies: AgentPolicy[], - selectedAgentPolicyId?: string -): undefined | string => { - if (agentPolicies.length && !selectedAgentPolicyId) { - if (agentPolicies.length === 1) { - return agentPolicies[0].id; - } - } - - return selectedAgentPolicyId; -}; - export const AgentPolicySelection: React.FC = (props) => { const { agentPolicies, - selectedPolicy, + selectedPolicyId, setSelectedPolicyId, excludeFleetServer, onClickCreatePolicy, @@ -75,17 +62,6 @@ export const AgentPolicySelection: React.FC = (props) => { const hasFleetAllPrivileges = useAuthz().fleet.all; - useEffect( - function useDefaultAgentPolicyEffect() { - const resolvedId = resolveAgentId(agentPolicies, selectedPolicy?.id); - // find AgentPolicy - if (resolvedId !== selectedPolicy?.id) { - setSelectedPolicyId(resolvedId); - } - }, - [agentPolicies, setSelectedPolicyId, selectedPolicy] - ); - const onChangeCallback = (event: React.ChangeEvent) => { const { value } = event.target; setSelectedPolicyId(value); @@ -144,7 +120,7 @@ export const AgentPolicySelection: React.FC = (props) => { value: agentPolicy.id, text: agentPolicy.name, }))} - value={selectedPolicy?.id} + value={selectedPolicyId} onChange={onChangeCallback} aria-label={i18n.translate( 'xpack.fleet.enrollmentStepAgentPolicy.policySelectAriaLabel', @@ -152,28 +128,28 @@ export const AgentPolicySelection: React.FC = (props) => { defaultMessage: 'Agent policy', } )} - hasNoInitialSelection={!selectedPolicy?.id} + hasNoInitialSelection={!selectedPolicyId} data-test-subj="agentPolicyDropdown" - isInvalid={!selectedPolicy?.id} + isInvalid={!selectedPolicyId} /> - {selectedPolicy?.id && !isFleetServerPolicy && ( + {selectedPolicyId && !isFleetServerPolicy && ( <> )} - {props.withKeySelection && props.onKeyChange && selectedPolicy?.id && ( + {props.withKeySelection && props.onKeyChange && selectedPolicyId && ( <> )} diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx index 5e6ea53261cd8..7dba93e5ddd3e 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx @@ -13,17 +13,19 @@ import { useFleetStatus, useGetAgents } from '../../hooks'; import { FleetServerRequirementPage } from '../../applications/fleet/sections/agents/agent_requirements_page'; -import { policyHasFleetServer } from '../../applications/fleet/sections/agents/services/has_fleet_server'; - import { FLEET_SERVER_PACKAGE } from '../../constants'; import { useFleetServerUnhealthy } from '../../applications/fleet/sections/agents/hooks/use_fleet_server_unhealthy'; import { Loading } from '..'; +import { policyHasFleetServer } from '../../services'; + +import { AdvancedTab } from '../../applications/fleet/components/fleet_server_instructions/advanced_tab'; + import type { InstructionProps } from './types'; -import { ManagedSteps, StandaloneSteps, FleetServerSteps } from './steps'; +import { ManagedSteps, StandaloneSteps } from './steps'; import { DefaultMissingRequirements } from './default_missing_requirements'; export const Instructions = (props: InstructionProps) => { @@ -35,6 +37,7 @@ export const Instructions = (props: InstructionProps) => { selectionType, setSelectionType, mode, + setMode, isIntegrationFlow, } = props; const fleetStatus = useFleetStatus(); @@ -86,7 +89,7 @@ export const Instructions = (props: InstructionProps) => { if (mode === 'managed') { if (showFleetServerEnrollment) { - return ; + return setMode('standalone')} />; } else if (showAgentEnrollment) { return ( <> @@ -101,11 +104,7 @@ export const Instructions = (props: InstructionProps) => { )} - {isFleetServerPolicySelected ? ( - - ) : ( - - )} + {isFleetServerPolicySelected ? : } ); } diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_policy_selection_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_policy_selection_step.tsx index c26bfd3f0e2b8..a0828bb72f489 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_policy_selection_step.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_policy_selection_step.tsx @@ -40,7 +40,7 @@ export const AgentPolicySelectionStep = ({ <> = ({ return ; }; - -export const FleetServerSteps: React.FunctionComponent = ({ - agentPolicy, - agentPolicies, - selectedPolicy, - setSelectedPolicyId, - refreshAgentPolicies, -}) => { - const [selectedApiKeyId, setSelectedAPIKeyId] = useState(); - - const apiKey = useGetOneEnrollmentAPIKey(selectedApiKeyId); - const apiKeyData = apiKey?.data; - const fleetServerInstructions = useFleetServerInstructions(apiKeyData?.item?.policy_id); - - const fleetServerSteps = useMemo(() => { - const { - serviceToken, - getServiceToken, - isLoadingServiceToken, - installCommand: managedInstallCommands, - platform, - setPlatform, - deploymentMode, - setDeploymentMode, - addFleetServerHost, - } = fleetServerInstructions; - - return [ - deploymentModeStep({ deploymentMode, setDeploymentMode }), - addFleetServerHostStep({ addFleetServerHost }), - ServiceTokenStep({ serviceToken, getServiceToken, isLoadingServiceToken }), - FleetServerCommandStep({ - serviceToken, - installCommand: managedInstallCommands, - platform, - setPlatform, - }), - ]; - }, [fleetServerInstructions]); - - const instructionsSteps = useMemo(() => { - const steps: EuiContainedStepProps[] = !agentPolicy - ? [ - AgentPolicySelectionStep({ - selectedPolicy, - agentPolicies, - selectedApiKeyId, - setSelectedAPIKeyId, - setSelectedPolicyId, - refreshAgentPolicies, - }), - ] - : [ - AgentEnrollmentKeySelectionStep({ - selectedPolicy, - selectedApiKeyId, - setSelectedAPIKeyId, - }), - ]; - - steps.push(...fleetServerSteps); - - return steps; - }, [ - agentPolicy, - selectedPolicy, - agentPolicies, - selectedApiKeyId, - setSelectedPolicyId, - refreshAgentPolicies, - fleetServerSteps, - ]); - - return ; -}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx index 59f6fdeafe727..bbb3d8d4794c3 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx @@ -14,7 +14,7 @@ import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/st import type { GetOneEnrollmentAPIKeyResponse } from '../../../../common/types/rest_spec/enrollment_api_key'; import { InstallSection } from '../../enrollment_instructions/install_section'; -import type { CommandsByPlatform } from '../../../applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils'; +import type { CommandsByPlatform } from '../../../applications/fleet/components/fleet_server_instructions/utils/install_command_utils'; import type { K8sMode } from '../types'; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_standalone_agent_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_standalone_agent_step.tsx index fb6ddfd393dcc..74ce555f7c2e8 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_standalone_agent_step.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_standalone_agent_step.tsx @@ -10,7 +10,8 @@ import { i18n } from '@kbn/i18n'; import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; -import type { CommandsByPlatform } from '../../../applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils'; +import type { CommandsByPlatform } from '../../../applications/fleet/components/fleet_server_instructions/utils/install_command_utils'; + import { InstallSection } from '../../enrollment_instructions/install_section'; import type { K8sMode } from '../types'; diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/install_section.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/install_section.tsx index 0cf43902db7e2..1ebe68b8c5282 100644 --- a/x-pack/plugins/fleet/public/components/enrollment_instructions/install_section.tsx +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/install_section.tsx @@ -7,13 +7,12 @@ import React from 'react'; +import type { CommandsByPlatform } from '../../applications/fleet/components/fleet_server_instructions/utils'; + import { InstallationMessage } from '../agent_enrollment_flyout/installation_message'; import type { K8sMode } from '../agent_enrollment_flyout/types'; - -import type { CommandsByPlatform } from '../../applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils'; - -import { PlatformSelector } from './manual/platform_selector'; +import { PlatformSelector } from '../platform_selector'; interface Props { installCommand: CommandsByPlatform; diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx index 89c1dfe3cac37..75378cdc86378 100644 --- a/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { CommandsByPlatform } from '../../../applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils'; +import type { CommandsByPlatform } from '../../../applications/fleet/components/fleet_server_instructions/utils/install_command_utils'; import type { K8sMode } from '../../agent_enrollment_flyout/types'; export const StandaloneInstructions = ( diff --git a/x-pack/plugins/fleet/public/components/index.ts b/x-pack/plugins/fleet/public/components/index.ts index 9df4182bc8a4e..723b376699e07 100644 --- a/x-pack/plugins/fleet/public/components/index.ts +++ b/x-pack/plugins/fleet/public/components/index.ts @@ -23,3 +23,4 @@ export { AddAgentHelpPopover } from './add_agent_help_popover'; export { EuiButtonWithTooltip } from './eui_button_with_tooltip'; export * from './link_and_revision'; export * from './agent_enrollment_flyout'; +export * from './platform_selector'; diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/platform_selector.tsx b/x-pack/plugins/fleet/public/components/platform_selector.tsx similarity index 96% rename from x-pack/plugins/fleet/public/components/enrollment_instructions/manual/platform_selector.tsx rename to x-pack/plugins/fleet/public/components/platform_selector.tsx index e03e43907f829..ae18f56b4b3ac 100644 --- a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/platform_selector.tsx +++ b/x-pack/plugins/fleet/public/components/platform_selector.tsx @@ -10,8 +10,8 @@ import styled from 'styled-components'; import { EuiSpacer, EuiCodeBlock, EuiButtonGroup, EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { PLATFORM_TYPE } from '../../../hooks'; -import { PLATFORM_OPTIONS, usePlatform } from '../../../hooks'; +import type { PLATFORM_TYPE } from '../hooks'; +import { PLATFORM_OPTIONS, usePlatform } from '../hooks'; interface Props { linuxCommand: string; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/has_fleet_server.ts b/x-pack/plugins/fleet/public/services/has_fleet_server.ts similarity index 79% rename from x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/has_fleet_server.ts rename to x-pack/plugins/fleet/public/services/has_fleet_server.ts index c10049303234c..e1100d6447aa2 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/has_fleet_server.ts +++ b/x-pack/plugins/fleet/public/services/has_fleet_server.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { AgentPolicy, PackagePolicy } from '../../../types'; -import { FLEET_SERVER_PACKAGE } from '../../../constants'; +import { FLEET_SERVER_PACKAGE } from '../constants'; +import type { AgentPolicy, PackagePolicy } from '../types'; export function policyHasFleetServer(agentPolicy: AgentPolicy) { return agentPolicy.package_policies?.some( diff --git a/x-pack/plugins/fleet/public/services/index.ts b/x-pack/plugins/fleet/public/services/index.ts index 306b081dce6c4..9de918d01d707 100644 --- a/x-pack/plugins/fleet/public/services/index.ts +++ b/x-pack/plugins/fleet/public/services/index.ts @@ -44,3 +44,4 @@ export { export * from './pkg_key_from_package_info'; export * from './ui_extensions'; export * from './increment_policy_name'; +export * from './has_fleet_server'; diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 9840a808ee27b..3cdeff1d91709 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -12792,7 +12792,6 @@ "xpack.fleet.fleetServerSetup.cloudDeploymentLink": "Modifier le déploiement", "xpack.fleet.fleetServerSetup.cloudSetupText": "Un serveur Fleet est nécessaire pour enregistrer des agents avec Fleet. Le moyen le plus simple d’en obtenir un est d’ajouter un serveur d’intégration, qui prend en charge l’intégration du serveur Fleet. Vous pouvez l’ajouter à votre déploiement dans la console cloud. Pour en savoir plus, consultez {link}", "xpack.fleet.fleetServerSetup.cloudSetupTitle": "Activer un serveur Fleet", - "xpack.fleet.fleetServerSetup.continueButton": "Continuer", "xpack.fleet.fleetServerSetup.deploymentModeProductionOption": "{production} : fournissez vos propres certificats. Cette option demande aux agents de préciser une clé de certificat lors de leur enregistrement avec Fleet", "xpack.fleet.fleetServerSetup.deploymentModeQuickStartOption": "{quickStart} : le serveur Fleet génère un certificat autosigné. Les agents suivants doivent être enregistrés avec l'indicateur --insecure. Non recommandé pour les cas d'utilisation en production.", "xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle": "Erreur lors de l'ajout de l'hôte du serveur Fleet", @@ -12801,23 +12800,16 @@ "xpack.fleet.fleetServerSetup.fleetSettingsLink": "Paramètres de Fleet", "xpack.fleet.fleetServerSetup.generateServiceTokenButton": "Générer un jeton de service", "xpack.fleet.fleetServerSetup.generateServiceTokenDescription": "Un jeton de service accorde au serveur Fleet les autorisations en écriture nécessaires dans Elasticsearch.", - "xpack.fleet.fleetServerSetup.installAgentDescription": "Depuis le répertoire de l'agent, copiez et exécutez la commande de démarrage rapide appropriée pour lancer un agent Elastic en tant que serveur Fleet à l'aide du jeton généré et d'un certificat autosigné. Reportez-vous au {userGuideLink} pour obtenir les instructions d'utilisation de vos propres certificats à des fins de déploiement de production. Toutes les commandes nécessitent des privilèges d'administrateur.", "xpack.fleet.fleetServerSetup.productionText": "Production", "xpack.fleet.fleetServerSetup.quickStartText": "Démarrage rapide", "xpack.fleet.fleetServerSetup.saveServiceTokenDescription": "Enregistrez les informations de votre jeton de service. Ce message s'affiche une seule fois.", "xpack.fleet.fleetServerSetup.serviceTokenLabel": "Jeton de service", "xpack.fleet.fleetServerSetup.setupGuideLink": "Guide de Fleet et d’Elastic Agent", - "xpack.fleet.fleetServerSetup.setupText": "Un serveur Fleet est nécessaire pour enregistrer des agents avec Fleet. Suivez les instructions ci-après pour configurer un serveur Fleet. Pour en savoir plus, consultez le {userGuideLink}.", - "xpack.fleet.fleetServerSetup.setupTitle": "Ajouter un serveur Fleet", "xpack.fleet.fleetServerSetup.stepCreateAgentPolicyTitle": "Créer une stratégie d’agent pour héberger le serveur Fleet", "xpack.fleet.fleetServerSetup.stepDeploymentModeDescriptionText": "Fleet utilise le protocole TLS (Transport Layer Security) pour chiffrer le trafic entre les agents Elastic et d'autres composants de la Suite Elastic. Sélectionnez un mode de déploiement pour définir comment vous souhaitez gérer les certificats. Votre sélection impactera la commande de configuration du serveur Fleet qui s'affichera à une étape ultérieure.", "xpack.fleet.fleetServerSetup.stepDeploymentModeTitle": "Sélectionner un mode de déploiement pour Security", - "xpack.fleet.fleetServerSetup.stepFleetServerCompleteDescription": "Vous pouvez désormais enregistrer des agents avec Fleet.", - "xpack.fleet.fleetServerSetup.stepFleetServerCompleteTitle": "Serveur Fleet connecté", "xpack.fleet.fleetServerSetup.stepGenerateServiceTokenTitle": "Générer un jeton de service", - "xpack.fleet.fleetServerSetup.stepInstallAgentTitle": "Lancer le serveur Fleet", "xpack.fleet.fleetServerSetup.stepSelectAgentPolicyTitle": "Sélectionner une stratégie d’agent pour héberger le serveur Fleet", - "xpack.fleet.fleetServerSetup.stepWaitingForFleetServerTitle": "En attente de connexion du serveur Fleet…", "xpack.fleet.fleetServerSetup.waitingText": "En attente de connexion d'un serveur Fleet…", "xpack.fleet.fleetServerSetupPermissionDeniedErrorMessage": "Le serveur Fleet doit être configuré. Pour cela, le privilège de cluster {roleName} est requis. Contactez votre administrateur.", "xpack.fleet.fleetServerSetupPermissionDeniedErrorTitle": "Autorisation refusée", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ef4216a0ee22c..5adf5a0561ebe 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -12912,7 +12912,6 @@ "xpack.fleet.fleetServerSetup.cloudDeploymentLink": "デプロイを編集", "xpack.fleet.fleetServerSetup.cloudSetupText": "Fleetにエージェントを登録する前に、Fleetサーバーが必要です。取得するための最も簡単な方法は、Fleetサーバー統合を実行する統合サーバーを追加することです。クラウドコンソールでデプロイに追加できます。詳細は{link}をご覧ください。", "xpack.fleet.fleetServerSetup.cloudSetupTitle": "Fleetサーバーを有効にする", - "xpack.fleet.fleetServerSetup.continueButton": "続行", "xpack.fleet.fleetServerSetup.deploymentModeProductionOption": "{production} – 独自の証明書を指定します。このオプションでは、Fleetに登録するときに、エージェントで証明書鍵を指定する必要があります。", "xpack.fleet.fleetServerSetup.deploymentModeQuickStartOption": "{quickStart} – Fleetサーバーは自己署名証明書を生成します。後続のエージェントは--insecureフラグを使用して登録する必要があります。本番ユースケースには推奨されません。", "xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle": "Fleetサーバーホストの追加エラー", @@ -12921,23 +12920,16 @@ "xpack.fleet.fleetServerSetup.fleetSettingsLink": "Fleet設定", "xpack.fleet.fleetServerSetup.generateServiceTokenButton": "サービストークンを生成", "xpack.fleet.fleetServerSetup.generateServiceTokenDescription": "サービストークンは、Elasticsearchに書き込むためのFleetサーバーアクセス権を付与します。", - "xpack.fleet.fleetServerSetup.installAgentDescription": "エージェントディレクトリから、適切なクイックスタートコマンドをコピーして実行し、生成されたトークンと自己署名証明書を使用して、ElasticエージェントをFleetサーバーとして起動します。本番デプロイで独自の証明書を使用する手順については、{userGuideLink}を参照してください。すべてのコマンドには管理者権限が必要です。", "xpack.fleet.fleetServerSetup.productionText": "本番運用", "xpack.fleet.fleetServerSetup.quickStartText": "クイックスタート", "xpack.fleet.fleetServerSetup.saveServiceTokenDescription": "サービストークン情報を保存します。これは1回だけ表示されます。", "xpack.fleet.fleetServerSetup.serviceTokenLabel": "サービストークン", "xpack.fleet.fleetServerSetup.setupGuideLink": "FleetおよびElasticエージェントガイド", - "xpack.fleet.fleetServerSetup.setupText": "Fleetにエージェントを登録する前に、Fleetサーバーが必要です。Fleetサーバーのセットアップについては、次の手順に従ってください。詳細については、{userGuideLink}を参照してください。", - "xpack.fleet.fleetServerSetup.setupTitle": "Fleetサーバーを追加", "xpack.fleet.fleetServerSetup.stepCreateAgentPolicyTitle": "Fleetサーバーをホストするエージェントポリシーを作成", "xpack.fleet.fleetServerSetup.stepDeploymentModeDescriptionText": "FleetはTransport Layer Security(TLS)を使用して、ElasticエージェントとElastic Stackの他のコンポーネントとの間の通信を暗号化します。デプロイモードを選択し、証明書を処理する方法を決定します。選択内容は後続のステップに表示されるFleetサーバーセットアップコマンドに影響します。", "xpack.fleet.fleetServerSetup.stepDeploymentModeTitle": "セキュリティのデプロイモードを選択", - "xpack.fleet.fleetServerSetup.stepFleetServerCompleteDescription": "エージェントをFleetに登録できます。", - "xpack.fleet.fleetServerSetup.stepFleetServerCompleteTitle": "Fleetサーバーが接続されました", "xpack.fleet.fleetServerSetup.stepGenerateServiceTokenTitle": "サービストークンを生成", - "xpack.fleet.fleetServerSetup.stepInstallAgentTitle": "Fleetサーバーを起動", "xpack.fleet.fleetServerSetup.stepSelectAgentPolicyTitle": "Fleetサーバーをホストするエージェントポリシーを選択", - "xpack.fleet.fleetServerSetup.stepWaitingForFleetServerTitle": "Fleetサーバーの接続を待機しています...", "xpack.fleet.fleetServerSetup.waitingText": "Fleetサーバーの接続を待機しています...", "xpack.fleet.fleetServerSetupPermissionDeniedErrorMessage": "Fleetサーバーを設定する必要があります。これには{roleName}クラスター権限が必要です。管理者にお問い合わせください。", "xpack.fleet.fleetServerSetupPermissionDeniedErrorTitle": "パーミッションが拒否されました", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 6f56727710d9d..a9e19a19413cb 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -12936,7 +12936,6 @@ "xpack.fleet.fleetServerSetup.cloudDeploymentLink": "编辑部署", "xpack.fleet.fleetServerSetup.cloudSetupText": "需要提供 Fleet 服务器,才能使用 Fleet 注册代理。获取 Fleet 服务器的最简单方法是添加集成服务器,它会运行 Fleet 服务器集成。您可以在云控制台中将其添加到部署中。有关更多信息,请参阅{link}", "xpack.fleet.fleetServerSetup.cloudSetupTitle": "启用 Fleet 服务器", - "xpack.fleet.fleetServerSetup.continueButton": "继续", "xpack.fleet.fleetServerSetup.deploymentModeProductionOption": "{production} – 提供您自己的证书。注册到 Fleet 时,此选项将需要代理指定证书密钥", "xpack.fleet.fleetServerSetup.deploymentModeQuickStartOption": "{quickStart} – Fleet 服务器将生成自签名证书。必须使用 --insecure 标志注册后续代理。不推荐用于生产用例。", "xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle": "添加 Fleet 服务器主机时出错", @@ -12945,23 +12944,16 @@ "xpack.fleet.fleetServerSetup.fleetSettingsLink": "Fleet 设置", "xpack.fleet.fleetServerSetup.generateServiceTokenButton": "生成服务令牌", "xpack.fleet.fleetServerSetup.generateServiceTokenDescription": "服务令牌授予 Fleet 服务器向 Elasticsearch 写入的权限。", - "xpack.fleet.fleetServerSetup.installAgentDescription": "从代理目录中,复制并运行适当的快速启动命令,以使用生成的令牌和自签名证书将 Elastic 代理启动为 Fleet 服务器。有关如何将自己的证书用于生产部署,请参阅 {userGuideLink}。所有命令都需要管理员权限。", "xpack.fleet.fleetServerSetup.productionText": "生产", "xpack.fleet.fleetServerSetup.quickStartText": "快速启动", "xpack.fleet.fleetServerSetup.saveServiceTokenDescription": "保存服务令牌信息。其仅显示一次。", "xpack.fleet.fleetServerSetup.serviceTokenLabel": "服务令牌", "xpack.fleet.fleetServerSetup.setupGuideLink": "Fleet 和 Elastic 代理指南", - "xpack.fleet.fleetServerSetup.setupText": "需要提供 Fleet 服务器,才能使用 Fleet 注册代理。按照下面的说明设置 Fleet 服务器。有关详细信息,请参阅{userGuideLink}。", - "xpack.fleet.fleetServerSetup.setupTitle": "添加 Fleet 服务器", "xpack.fleet.fleetServerSetup.stepCreateAgentPolicyTitle": "创建代理策略来托管 Fleet 服务器", "xpack.fleet.fleetServerSetup.stepDeploymentModeDescriptionText": "Fleet 使用传输层安全 (TLS) 加密 Elastic 代理和 Elastic Stack 中的其他组件之间的流量。选择部署模式来决定处理证书的方式。您的选择将影响后面步骤中显示的 Fleet 服务器设置命令。", "xpack.fleet.fleetServerSetup.stepDeploymentModeTitle": "为安全选择部署模式", - "xpack.fleet.fleetServerSetup.stepFleetServerCompleteDescription": "现在可以将代理注册到 Fleet。", - "xpack.fleet.fleetServerSetup.stepFleetServerCompleteTitle": "Fleet 服务器已连接", "xpack.fleet.fleetServerSetup.stepGenerateServiceTokenTitle": "生成服务令牌", - "xpack.fleet.fleetServerSetup.stepInstallAgentTitle": "启动 Fleet 服务器", "xpack.fleet.fleetServerSetup.stepSelectAgentPolicyTitle": "选择代理策略来托管 Fleet 服务器", - "xpack.fleet.fleetServerSetup.stepWaitingForFleetServerTitle": "正在等待 Fleet 服务器连接......", "xpack.fleet.fleetServerSetup.waitingText": "等候 Fleet 服务器连接......", "xpack.fleet.fleetServerSetupPermissionDeniedErrorMessage": "需要设置 Fleet 服务器。这需要 {roleName} 集群权限。请联系您的管理员。", "xpack.fleet.fleetServerSetupPermissionDeniedErrorTitle": "权限被拒绝", From eedfec19cc0cf33d81e9d5d8c6bfe78a1f39ff71 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 26 Apr 2022 14:59:03 -0600 Subject: [PATCH 08/78] [ML] Data Frame Analytics: ensure page header updates with selection (#130836) * ensure header updates with current selected id * fix multiple graphs in map view * use nullish coalescing operator instead of or --- .../pages/analytics_exploration/page.tsx | 2 +- .../data_frame_analytics/pages/job_map/page.tsx | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx index c35ad5bacf371..524556e12a9af 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx @@ -108,7 +108,7 @@ export const Page: FC<{ /> ) : null} {jobIdToUse !== undefined && ( - + { <> {isIdSelectorFlyoutVisible ? ( { ) : null} {jobId !== undefined ? ( - + { - {mapJobId || mapModelId || analyticsId ? ( + {jobId ?? modelId ? ( ) : ( From d371f3aaee07d4a28d04da026ee46613da634c38 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 26 Apr 2022 15:15:32 -0600 Subject: [PATCH 09/78] [ML] Data Frame Analytics creation wizard: add support for filters in saved searches (#130744) * support savedSearch filters in DFA results * fix linting error * fix error when no search has filter but no query --- .../configuration_step/use_saved_search.ts | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/use_saved_search.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/use_saved_search.ts index c4611a1740913..0a75c6467f9d0 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/use_saved_search.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/use_saved_search.ts @@ -7,12 +7,12 @@ import { useState, useEffect } from 'react'; import { + buildEsQuery, + buildQueryFromFilters, decorateQuery, fromKueryExpression, - luceneStringToDsl, toElasticsearchQuery, } from '@kbn/es-query'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { useMlContext } from '../../../../../contexts/ml'; import { SEARCH_QUERY_LANGUAGE } from '../../../../../../../common/constants/search'; import { getQueryFromSavedSearchObject } from '../../../../../util/index_utils'; @@ -36,19 +36,42 @@ export function useSavedSearch() { const { currentSavedSearch, currentDataView, kibanaConfig } = mlContext; const getQueryData = () => { - let qry: estypes.QueryDslQueryContainer = {}; + let qry: any = {}; let qryString; if (currentSavedSearch !== null) { - const { query } = getQueryFromSavedSearchObject(currentSavedSearch); + const { query, filter } = getQueryFromSavedSearchObject(currentSavedSearch); const queryLanguage = query.language; qryString = query.query; if (queryLanguage === SEARCH_QUERY_LANGUAGE.KUERY) { const ast = fromKueryExpression(qryString); qry = toElasticsearchQuery(ast, currentDataView); + const filterQuery = buildQueryFromFilters(filter, currentDataView); + if (qry.bool === undefined) { + qry.bool = {}; + // toElasticsearchQuery may add a single match_all item to the + // root of its returned query, rather than putting it inside + // a bool.should + // in this case, move it to a bool.should + if (qry.match_all !== undefined) { + qry.bool.should = { + match_all: qry.match_all, + }; + delete qry.match_all; + } + } + + if (Array.isArray(qry.bool.filter) === false) { + qry.bool.filter = qry.bool.filter === undefined ? [] : [qry.bool.filter]; + } + if (Array.isArray(qry.bool.must_not) === false) { + qry.bool.must_not = qry.bool.must_not === undefined ? [] : [qry.bool.must_not]; + } + qry.bool.filter = [...qry.bool.filter, ...filterQuery.filter]; + qry.bool.must_not = [...qry.bool.must_not, ...filterQuery.must_not]; } else { - qry = luceneStringToDsl(qryString); + qry = buildEsQuery(currentDataView, [query], filter); decorateQuery(qry, kibanaConfig.get('query:queryString:options')); } From e9328920e710ebba2feea3c3e761f901abdd41dd Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Tue, 26 Apr 2022 17:46:17 -0400 Subject: [PATCH 10/78] [CI] Split up Default CI Group 11 and 25 a bit (#130998) --- .../security_and_spaces/tests/trial/index.ts | 26 ++++++++++++------- .../security_and_spaces/tests/index.ts | 7 ++++- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/index.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/index.ts index 985393eafe719..3c1ee84296270 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/index.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/index.ts @@ -11,8 +11,6 @@ import { createSpacesAndUsers, deleteSpacesAndUsers } from '../../../common/lib/ // eslint-disable-next-line import/no-default-export export default ({ loadTestFile, getService }: FtrProviderContext): void => { describe('cases security and spaces enabled: trial', function () { - this.tags('ciGroup25'); - before(async () => { await createSpacesAndUsers(getService); }); @@ -21,15 +19,23 @@ export default ({ loadTestFile, getService }: FtrProviderContext): void => { await deleteSpacesAndUsers(getService); }); - // Trial - loadTestFile(require.resolve('./cases/push_case')); - loadTestFile(require.resolve('./cases/user_actions/get_all_user_actions')); - loadTestFile(require.resolve('./configure')); + describe('', function () { + this.tags('ciGroup13'); + + // Trial + loadTestFile(require.resolve('./cases/push_case')); + loadTestFile(require.resolve('./cases/user_actions/get_all_user_actions')); + loadTestFile(require.resolve('./configure')); + }); - // Common - loadTestFile(require.resolve('../common')); + describe('', function () { + this.tags('ciGroup25'); - // NOTE: These need to be at the end because they could delete the .kibana index and inadvertently remove the users and spaces - loadTestFile(require.resolve('../common/migrations')); + // Common + loadTestFile(require.resolve('../common')); + + // NOTE: These need to be at the end because they could delete the .kibana index and inadvertently remove the users and spaces + loadTestFile(require.resolve('../common/migrations')); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts index a6d052a22bee6..a3c4dd8ed3be1 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts @@ -14,7 +14,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => { this.tags('ciGroup11'); loadTestFile(require.resolve('./aliases')); - loadTestFile(require.resolve('./create_endpoint_exceptions')); loadTestFile(require.resolve('./add_actions')); loadTestFile(require.resolve('./update_actions')); loadTestFile(require.resolve('./add_prepackaged_rules')); @@ -54,6 +53,12 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./migrations')); }); + describe('', function () { + this.tags('ciGroup26'); + + loadTestFile(require.resolve('./create_endpoint_exceptions')); + }); + describe('', function () { this.tags('ciGroup14'); From 330dbbe304a8ed6547f789bca99f43c92653e14d Mon Sep 17 00:00:00 2001 From: Kevin Qualters <56408403+kqualters-elastic@users.noreply.github.com> Date: Tue, 26 Apr 2022 18:27:09 -0400 Subject: [PATCH 11/78] Make only a single api request for exceptions when opening an alert in timeline (#130941) --- .github/CODEOWNERS | 1 + .../use_investigate_in_timeline.tsx | 76 +++++++++---------- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 869160bfa0fd1..215514d58f601 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -415,6 +415,7 @@ /x-pack/plugins/security_solution/cypress/integration/urls @elastic/security-threat-hunting-investigations /x-pack/plugins/security_solution/public/common/components/alerts_viewer @elastic/security-threat-hunting-investigations +/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_action @elastic/security-threat-hunting-investigations /x-pack/plugins/security_solution/public/common/components/event_details @elastic/security-threat-hunting-investigations /x-pack/plugins/security_solution/public/common/components/events_viewer @elastic/security-threat-hunting-investigations /x-pack/plugins/security_solution/public/common/components/markdown_editor @elastic/security-threat-hunting-investigations diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx index 58163029667e6..53a2dece1bd5c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx @@ -14,7 +14,7 @@ import { ALERT_RULE_EXCEPTIONS_LIST } from '@kbn/rule-data-utils'; import { ExceptionListIdentifiers, ExceptionListItemSchema, - ReadExceptionListSchema, + ExceptionListTypeEnum, } from '@kbn/securitysolution-io-ts-list-types'; import { useApi } from '@kbn/securitysolution-list-hooks'; @@ -51,48 +51,48 @@ export const useInvestigateInTimeline = ({ const getExceptions = useCallback( async (ecsData: Ecs): Promise => { - const exceptionsLists: ReadExceptionListSchema[] = ( - getField(ecsData, ALERT_RULE_EXCEPTIONS_LIST) ?? [] - ) - .map((list: string) => JSON.parse(list)) - .filter((list: ExceptionListIdentifiers) => list.type === 'detection'); + const exceptionsLists = (getField(ecsData, ALERT_RULE_EXCEPTIONS_LIST) ?? []).reduce( + (acc: ExceptionListIdentifiers[], next: string) => { + const parsedList = JSON.parse(next); + if (parsedList.type === 'detection') { + const formattedList = { + id: parsedList.id, + listId: parsedList.list_id, + type: ExceptionListTypeEnum.DETECTION, + namespaceType: parsedList.namespace_type, + }; + acc.push(formattedList); + } + return acc; + }, + [] + ); const allExceptions: ExceptionListItemSchema[] = []; if (exceptionsLists.length > 0) { - for (const list of exceptionsLists) { - if (list.id && list.list_id && list.namespace_type) { - await getExceptionListsItems({ - lists: [ - { - id: list.id, - listId: list.list_id, - type: 'detection', - namespaceType: list.namespace_type, - }, - ], - filterOptions: [], - pagination: { - page: 0, - perPage: 10000, - total: 10000, - }, - showDetectionsListsOnly: true, - showEndpointListsOnly: false, - onSuccess: ({ exceptions }) => { - allExceptions.push(...exceptions); - }, - onError: (err: string[]) => { - addError(err, { - title: i18n.translate( - 'xpack.securitySolution.detectionEngine.alerts.fetchExceptionsFailure', - { defaultMessage: 'Error fetching exceptions.' } - ), - }); - }, + await getExceptionListsItems({ + lists: exceptionsLists, + filterOptions: [], + pagination: { + page: 0, + perPage: 10000, + total: 10000, + }, + showDetectionsListsOnly: true, + showEndpointListsOnly: false, + onSuccess: ({ exceptions }) => { + allExceptions.push(...exceptions); + }, + onError: (err: string[]) => { + addError(err, { + title: i18n.translate( + 'xpack.securitySolution.detectionEngine.alerts.fetchExceptionsFailure', + { defaultMessage: 'Error fetching exceptions.' } + ), }); - } - } + }, + }); } return allExceptions; }, From 2d31ac1a88728e789906c17fb6547e7e5f703834 Mon Sep 17 00:00:00 2001 From: Rickyanto Ang Date: Tue, 26 Apr 2022 15:54:17 -0700 Subject: [PATCH 12/78] [8.2.1] [Session View] mouse over highlighting gap fix (#129679) * mouse over highlighting gap fix * addressing PR comments + more CSS fixes * added show more button if we have more than 5 execs * added padding for first alert in groups alert * fix check CI issue * addressing PR comments --- .../detail_panel_alert_group_item/index.tsx | 1 + .../detail_panel_alert_list_item/index.tsx | 2 +- .../detail_panel_alert_list_item/styles.ts | 12 ++++++++++++ .../detail_panel_description_list/styles.ts | 4 ++-- .../components/detail_panel_list_item/styles.ts | 8 ++++---- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/session_view/public/components/detail_panel_alert_group_item/index.tsx b/x-pack/plugins/session_view/public/components/detail_panel_alert_group_item/index.tsx index a83e41d793e23..562bd013ebd60 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_alert_group_item/index.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_alert_group_item/index.tsx @@ -66,6 +66,7 @@ export const DetailPanelAlertGroupItem = ({ data-test-subj={ALERT_GROUP_ITEM_COUNT_TEST_ID} className="eui-alignCenter" size="m" + css={styles.alertCountArrowPad} > {alertsCount} diff --git a/x-pack/plugins/session_view/public/components/detail_panel_alert_list_item/index.tsx b/x-pack/plugins/session_view/public/components/detail_panel_alert_list_item/index.tsx index 1c6dd0a57b7e7..26fd2af6b69d6 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_alert_list_item/index.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_alert_list_item/index.tsx @@ -59,7 +59,7 @@ export const DetailPanelAlertListItem = ({ const forceState = !isInvestigated ? 'open' : undefined; return minimal ? ( -
+
diff --git a/x-pack/plugins/session_view/public/components/detail_panel_alert_list_item/styles.ts b/x-pack/plugins/session_view/public/components/detail_panel_alert_list_item/styles.ts index 403c6d3e2cacb..4cd77e48c2c4d 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_alert_list_item/styles.ts +++ b/x-pack/plugins/session_view/public/components/detail_panel_alert_list_item/styles.ts @@ -79,6 +79,10 @@ export const useStyles = (minimal = false, isInvestigated = false) => { minWidth: 0, }; + const alertCountArrowPad: CSSObject = { + marginRight: size.xs, + }; + const processPanel: CSSObject = { border: `${borderThickness} solid ${colors.lightShade}`, fontFamily: font.familyCode, @@ -103,6 +107,12 @@ export const useStyles = (minimal = false, isInvestigated = false) => { float: 'right', }; + const firstAlertPad: CSSObject = { + '&:first-child': { + paddingTop: size.base, + }, + }; + const minimalHR: CSSObject = { marginBottom: 0, }; @@ -114,9 +124,11 @@ export const useStyles = (minimal = false, isInvestigated = false) => { alertTitle, alertIcon, alertAccordionButton, + alertCountArrowPad, processPanel, investigatedLabel, minimalContextMenu, + firstAlertPad, minimalHR, }; }, [euiTheme, isInvestigated, minimal]); diff --git a/x-pack/plugins/session_view/public/components/detail_panel_description_list/styles.ts b/x-pack/plugins/session_view/public/components/detail_panel_description_list/styles.ts index d1f3198a10c85..be847e52562b5 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_description_list/styles.ts +++ b/x-pack/plugins/session_view/public/components/detail_panel_description_list/styles.ts @@ -20,14 +20,14 @@ export const useStyles = () => { const tabListTitle = { width: '40%', display: 'flex', - alignItems: 'center', + alignItems: 'baseline', marginTop: '0px', }; const tabListDescription = { width: '60%', display: 'flex', - alignItems: 'center', + alignItems: 'baseline', marginTop: '0px', }; diff --git a/x-pack/plugins/session_view/public/components/detail_panel_list_item/styles.ts b/x-pack/plugins/session_view/public/components/detail_panel_list_item/styles.ts index 4e15221638f91..48a8dba781ee4 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_list_item/styles.ts +++ b/x-pack/plugins/session_view/public/components/detail_panel_list_item/styles.ts @@ -20,11 +20,11 @@ export const useStyles = ({ display }: StylesDeps) => { const item: CSSObject = { display, alignContent: 'center', - padding: `0px ${euiTheme.size.s} `, + padding: `${euiTheme.size.xs} ${euiTheme.size.s} `, width: '100%', fontWeight: 'inherit', - height: euiTheme.size.xl, - lineHeight: euiTheme.size.l, + height: 'max-content', + minHeight: euiTheme.size.l, letterSpacing: '0px', textAlign: 'left', @@ -43,7 +43,7 @@ export const useStyles = ({ display }: StylesDeps) => { '&:hover': { background: transparentize(euiTheme.colors.primary, 0.1), }, - height: '100%', + height: 'fit-content', }; return { From 137fcd08526079811ece0d6b27aaad18a11129cf Mon Sep 17 00:00:00 2001 From: Milton Hultgren Date: Wed, 27 Apr 2022 08:43:12 +0200 Subject: [PATCH 13/78] [Stack Monitoring] Add note about dedicated monitoring to No Data screen (#130872) * [Stack Monitoring] Add note about dedicated monitoring to No Data screen (#130775) * Update snapshots --- .../components/no_data/__snapshots__/no_data.test.js.snap | 6 ++++++ .../monitoring/public/components/no_data/no_data.js | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/x-pack/plugins/monitoring/public/components/no_data/__snapshots__/no_data.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/__snapshots__/no_data.test.js.snap index 8852d104fe00a..e0068c8a150d8 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/__snapshots__/no_data.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/no_data/__snapshots__/no_data.test.js.snap @@ -38,6 +38,9 @@ exports[`NoData should show a default message if reason is unknown 1`] = `

Have you set up monitoring yet? If so, make sure that the selected time period in the upper right includes monitoring data.

+

+ If you have configured monitoring data to be sent to a dedicated monitoring cluster you should access that data with the Kibana instance attached to the monitoring cluster. +

Have you set up monitoring yet? If so, make sure that the selected time period in the upper right includes monitoring data.

+

+ If you have configured monitoring data to be sent to a dedicated monitoring cluster you should access that data with the Kibana instance attached to the monitoring cluster. +

+

+ +

From 349cca2b77ef9edf994781c21f25d3722f164997 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Wed, 27 Apr 2022 11:14:21 +0300 Subject: [PATCH 14/78] [Timelion] fix requesting not permitted or used data views (#130899) --- .../vis_types/timelion/public/helpers/arg_value_suggestions.ts | 2 +- src/plugins/vis_types/timelion/public/timelion_vis_type.tsx | 2 +- .../vis_types/timelion/server/series_functions/es/index.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/vis_types/timelion/public/helpers/arg_value_suggestions.ts b/src/plugins/vis_types/timelion/public/helpers/arg_value_suggestions.ts index 31e4cdaf176c3..e74afb7556684 100644 --- a/src/plugins/vis_types/timelion/public/helpers/arg_value_suggestions.ts +++ b/src/plugins/vis_types/timelion/public/helpers/arg_value_suggestions.ts @@ -24,7 +24,7 @@ export function getArgValueSuggestions() { } const indexPatternTitle = get(indexPatternArg, 'value.text'); - return (await indexPatterns.find(indexPatternTitle)).find( + return (await indexPatterns.find(indexPatternTitle, 1)).find( (index) => index.title === indexPatternTitle ); } diff --git a/src/plugins/vis_types/timelion/public/timelion_vis_type.tsx b/src/plugins/vis_types/timelion/public/timelion_vis_type.tsx index cac1f127e11c9..f32a485ac2565 100644 --- a/src/plugins/vis_types/timelion/public/timelion_vis_type.tsx +++ b/src/plugins/vis_types/timelion/public/timelion_vis_type.tsx @@ -57,7 +57,7 @@ export function getTimelionVisDefinition(dependencies: TimelionVisDependencies) ); if (indexArg?.value.text) { - return getIndexPatterns().find(indexArg.value.text); + return getIndexPatterns().find(indexArg.value.text, 1); } } catch { // timelion expression is invalid diff --git a/src/plugins/vis_types/timelion/server/series_functions/es/index.js b/src/plugins/vis_types/timelion/server/series_functions/es/index.js index 2001a39bf7901..5fb0480e074c9 100644 --- a/src/plugins/vis_types/timelion/server/series_functions/es/index.js +++ b/src/plugins/vis_types/timelion/server/series_functions/es/index.js @@ -104,7 +104,7 @@ export default new Datasource('es', { fit: 'nearest', }); const indexPatternsService = tlConfig.getIndexPatternsService(); - const indexPatternSpec = (await indexPatternsService.find(config.index)).find( + const indexPatternSpec = (await indexPatternsService.find(config.index, 1)).find( (index) => index.title === config.index ); From f62530bae971e21d06c4bb15a4b270409e355f75 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Wed, 27 Apr 2022 09:34:29 +0100 Subject: [PATCH 15/78] [Fleet] Refactor of install package button code (#131015) * move install integration button to separate component * move install path generation to util function * small refactor * add unit tests * rorder deps to make diff less noisy --- .../components/add_integration_button.tsx | 60 +++++++++ .../epm/screens/detail/components/index.tsx | 1 + .../sections/epm/screens/detail/index.tsx | 125 ++++-------------- .../utils/get_install_route_options.test.ts | 69 ++++++++++ .../detail/utils/get_install_route_options.ts | 82 ++++++++++++ .../epm/screens/detail/utils/index.ts | 8 ++ 6 files changed, 243 insertions(+), 102 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/add_integration_button.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.test.ts create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.ts create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/index.ts diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/add_integration_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/add_integration_button.tsx new file mode 100644 index 0000000000000..f2ec544922b47 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/add_integration_button.tsx @@ -0,0 +1,60 @@ +/* + * 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 React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { EuiButtonWithTooltip } from '../../../../../components'; + +interface AddIntegrationButtonProps { + userCanInstallPackages?: boolean; + missingSecurityConfiguration: boolean; + packageName: string; + href: string; + onClick: Function; +} + +export function AddIntegrationButton(props: AddIntegrationButtonProps) { + const { userCanInstallPackages, missingSecurityConfiguration, packageName, href, onClick } = + props; + + const tooltip = !userCanInstallPackages + ? { + content: missingSecurityConfiguration ? ( + + ) : ( + + ), + } + : undefined; + + return ( + onClick(e)} + data-test-subj="addIntegrationPolicyButton" + tooltip={tooltip} + > + + + ); +} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/index.tsx index 8716d78dfb7bd..c0438bf6dfe8d 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/index.tsx @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +export { AddIntegrationButton } from './add_integration_button'; export { UpdateIcon } from './update_icon'; export { IntegrationAgentPolicyCount } from './integration_agent_policy_count'; export { IconPanel, LoadingIconPanel } from './icon_panel'; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 5ac2836876351..d17b24a4dcdb0 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -35,26 +35,25 @@ import { useAuthz, usePermissionCheck, } from '../../../../hooks'; -import { - PLUGIN_ID, - INTEGRATIONS_PLUGIN_ID, - INTEGRATIONS_ROUTING_PATHS, - pagePathGetters, -} from '../../../../constants'; +import { INTEGRATIONS_ROUTING_PATHS } from '../../../../constants'; import { useGetPackageInfoByKey, useLink, useAgentPolicyContext } from '../../../../hooks'; import { pkgKeyFromPackageInfo } from '../../../../services'; -import type { - CreatePackagePolicyRouteState, - DetailViewPanelName, - PackageInfo, -} from '../../../../types'; +import type { DetailViewPanelName, PackageInfo } from '../../../../types'; import { InstallStatus } from '../../../../types'; -import { Error, EuiButtonWithTooltip, Loading } from '../../../../components'; +import { Error, Loading } from '../../../../components'; import type { WithHeaderLayoutProps } from '../../../../layouts'; import { WithHeaderLayout } from '../../../../layouts'; import { RELEASE_BADGE_DESCRIPTION, RELEASE_BADGE_LABEL } from '../../components/release_badge'; -import { IntegrationAgentPolicyCount, UpdateIcon, IconPanel, LoadingIconPanel } from './components'; +import { getInstallPkgRouteOptions } from './utils'; + +import { + IntegrationAgentPolicyCount, + UpdateIcon, + IconPanel, + LoadingIconPanel, + AddIntegrationButton, +} from './components'; import { AssetsPage } from './assets'; import { OverviewPage } from './overview'; import { PackagePoliciesPage } from './policies'; @@ -257,7 +256,6 @@ export function Detail() { const handleAddIntegrationPolicyClick = useCallback( (ev) => { ev.preventDefault(); - // The object below, given to `createHref` is explicitly accessing keys of `location` in order // to ensure that dependencies to this `useCallback` is set correctly (because `location` is mutable) const currentPath = history.createHref({ @@ -266,65 +264,14 @@ export function Detail() { hash, }); - const path = pagePathGetters.add_integration_to_policy({ + const navigateOptions = getInstallPkgRouteOptions({ + currentPath, + integration, + agentPolicyId: agentPolicyIdFromContext, pkgkey, - ...(integration ? { integration } : {}), - ...(agentPolicyIdFromContext ? { agentPolicyId: agentPolicyIdFromContext } : {}), - })[1]; - - let redirectToPath: CreatePackagePolicyRouteState['onSaveNavigateTo'] & - CreatePackagePolicyRouteState['onCancelNavigateTo']; - let onSaveQueryParams: CreatePackagePolicyRouteState['onSaveQueryParams']; - if (agentPolicyIdFromContext) { - redirectToPath = [ - PLUGIN_ID, - { - path: pagePathGetters.policy_details({ - policyId: agentPolicyIdFromContext, - })[1], - }, - ]; - - onSaveQueryParams = { - showAddAgentHelp: true, - openEnrollmentFlyout: true, - }; - } else { - redirectToPath = [ - INTEGRATIONS_PLUGIN_ID, - { - path: pagePathGetters.integration_details_policies({ - pkgkey, - ...(integration ? { integration } : {}), - })[1], - }, - ]; - - onSaveQueryParams = { - showAddAgentHelp: { renameKey: 'showAddAgentHelpForPolicyId', policyIdAsValue: true }, - openEnrollmentFlyout: { renameKey: 'addAgentToPolicyId', policyIdAsValue: true }, - }; - } - - const redirectBackRouteState: CreatePackagePolicyRouteState = { - onSaveNavigateTo: redirectToPath, - onSaveQueryParams, - onCancelNavigateTo: [ - INTEGRATIONS_PLUGIN_ID, - { - path: pagePathGetters.integration_details_overview({ - pkgkey, - ...(integration ? { integration } : {}), - })[1], - }, - ], - onCancelUrl: currentPath, - }; - - services.application.navigateToApp(PLUGIN_ID, { - path, - state: redirectBackRouteState, }); + + services.application.navigateToApp(...navigateOptions); }, [ history, @@ -375,10 +322,8 @@ export function Detail() { { isDivider: true }, { content: ( - - ) : ( - - ), - } - : undefined - } - > - - + /> ), }, ].map((item, index) => ( diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.test.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.test.ts new file mode 100644 index 0000000000000..4ee67874e61c1 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.test.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getInstallPkgRouteOptions } from '.'; + +// this is always the same +const expectedOnCancelNavigateTo = [ + 'integrations', + { + path: '/detail/myintegration-1.0.0/overview?integration=myintegration', + }, +]; + +describe('getInstallPkgRouteOptions', () => { + it('should redirect to integrations app on save if no agentPolicyId present', () => { + const opts = { + currentPath: 'currentPath', + integration: 'myintegration', + pkgkey: 'myintegration-1.0.0', + }; + + const expectedRedirectURl = '/detail/myintegration-1.0.0/policies?integration=myintegration'; + + const expectedOptions = { + path: '/integrations/myintegration-1.0.0/add-integration/myintegration', + state: { + onCancelUrl: 'currentPath', + onCancelNavigateTo: expectedOnCancelNavigateTo, + onSaveNavigateTo: ['integrations', { path: expectedRedirectURl }], + onSaveQueryParams: { + showAddAgentHelp: { renameKey: 'showAddAgentHelpForPolicyId', policyIdAsValue: true }, + openEnrollmentFlyout: { renameKey: 'addAgentToPolicyId', policyIdAsValue: true }, + }, + }, + }; + + expect(getInstallPkgRouteOptions(opts)).toEqual(['fleet', expectedOptions]); + }); + + it('should redirect to fleet app on save if agentPolicyId present', () => { + const opts = { + currentPath: 'currentPath', + integration: 'myintegration', + pkgkey: 'myintegration-1.0.0', + agentPolicyId: '12345', + }; + + const expectedRedirectURl = '/policies/12345'; + + const expectedOptions = { + path: '/integrations/myintegration-1.0.0/add-integration/myintegration?policyId=12345', + state: { + onCancelUrl: 'currentPath', + onCancelNavigateTo: expectedOnCancelNavigateTo, + onSaveNavigateTo: ['fleet', { path: expectedRedirectURl }], + onSaveQueryParams: { + showAddAgentHelp: true, + openEnrollmentFlyout: true, + }, + }, + }; + + expect(getInstallPkgRouteOptions(opts)).toEqual(['fleet', expectedOptions]); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.ts new file mode 100644 index 0000000000000..dc34ac91447f6 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.ts @@ -0,0 +1,82 @@ +/* + * 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 type { CreatePackagePolicyRouteState } from '../../../../../types'; +import { PLUGIN_ID, INTEGRATIONS_PLUGIN_ID, pagePathGetters } from '../../../../../constants'; + +/* + * When the install package button is pressed, this fn decides which page to navigate to + * by generating the options to be passed to `services.application.navigateToApp`. + */ +export const getInstallPkgRouteOptions = ({ + currentPath, + integration, + agentPolicyId, + pkgkey, +}: { + currentPath: string; + integration: string | null; + agentPolicyId?: string; + pkgkey: string; +}): [string, { path: string; state: unknown }] => { + const integrationOpts: { integration?: string } = integration ? { integration } : {}; + const path = pagePathGetters.add_integration_to_policy({ + pkgkey, + ...integrationOpts, + ...(agentPolicyId ? { agentPolicyId } : {}), + })[1]; + + let redirectToPath: CreatePackagePolicyRouteState['onSaveNavigateTo'] & + CreatePackagePolicyRouteState['onCancelNavigateTo']; + let onSaveQueryParams: CreatePackagePolicyRouteState['onSaveQueryParams']; + if (agentPolicyId) { + redirectToPath = [ + PLUGIN_ID, + { + path: pagePathGetters.policy_details({ + policyId: agentPolicyId, + })[1], + }, + ]; + + onSaveQueryParams = { + showAddAgentHelp: true, + openEnrollmentFlyout: true, + }; + } else { + redirectToPath = [ + INTEGRATIONS_PLUGIN_ID, + { + path: pagePathGetters.integration_details_policies({ + pkgkey, + ...integrationOpts, + })[1], + }, + ]; + + onSaveQueryParams = { + showAddAgentHelp: { renameKey: 'showAddAgentHelpForPolicyId', policyIdAsValue: true }, + openEnrollmentFlyout: { renameKey: 'addAgentToPolicyId', policyIdAsValue: true }, + }; + } + + const state: CreatePackagePolicyRouteState = { + onSaveNavigateTo: redirectToPath, + onSaveQueryParams, + onCancelNavigateTo: [ + INTEGRATIONS_PLUGIN_ID, + { + path: pagePathGetters.integration_details_overview({ + pkgkey, + ...integrationOpts, + })[1], + }, + ], + onCancelUrl: currentPath, + }; + + return [PLUGIN_ID, { path, state }]; +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/index.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/index.ts new file mode 100644 index 0000000000000..020e115f2309a --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { getInstallPkgRouteOptions } from './get_install_route_options'; From 12fdfd63adfd196528b1a61b1f3a07d2d484bcc0 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 27 Apr 2022 12:13:25 +0300 Subject: [PATCH 16/78] [Cases] Revert preconfigured connectors support for Cases (#130372) * Fix bug with deprecated connectors * Add integration test * Improve integration tests * Fix grammar * Fix CI Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../servicenow_itsm_case_fields.test.tsx | 19 ++ .../servicenow_sir_case_fields.test.tsx | 19 ++ .../connectors/servicenow/validator.test.ts | 13 +- .../connectors/servicenow/validator.ts | 16 +- .../cases/public/components/utils.test.ts | 11 ++ .../plugins/cases/public/components/utils.ts | 8 +- .../server/client/configure/client.test.ts | 162 ++++++++++++++---- .../cases/server/client/configure/client.ts | 4 +- .../cases_api_integration/common/config.ts | 14 +- .../cases_api_integration/common/lib/utils.ts | 13 ++ .../tests/basic/configure/create_connector.ts | 20 --- .../configure/get_connectors.ts | 15 +- .../security_and_spaces/tests/basic/index.ts | 1 + .../security_and_spaces/tests/common/index.ts | 1 - .../tests/trial/configure/get_connectors.ts | 60 ++----- .../tests/trial/configure/get_connectors.ts | 62 ++++--- 16 files changed, 300 insertions(+), 138 deletions(-) delete mode 100644 x-pack/test/cases_api_integration/security_and_spaces/tests/basic/configure/create_connector.ts rename x-pack/test/cases_api_integration/security_and_spaces/tests/{common => basic}/configure/get_connectors.ts (64%) diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.test.tsx b/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.test.tsx index 66ff8c751c461..cfc16f1fb6e8b 100644 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.test.tsx +++ b/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.test.tsx @@ -146,6 +146,25 @@ describe('ServiceNowITSM Fields', () => { expect(screen.queryByTestId('deprecated-connector-warning-callout')).not.toBeInTheDocument(); }); + it('does not show the deprecated callout when the connector is preconfigured', async () => { + render( + + ); + expect(screen.queryByTestId('deprecated-connector-warning-callout')).not.toBeInTheDocument(); + }); + + it('does not show the deprecated callout when the config of the connector is undefined', async () => { + render( + // @ts-expect-error + + ); + expect(screen.queryByTestId('deprecated-connector-warning-callout')).not.toBeInTheDocument(); + }); + it('should hide subcategory if selecting a category without subcategories', async () => { // Failed Login doesn't have defined subcategories const customFields = { diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.test.tsx b/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.test.tsx index 279cab7e6f879..a2c61ac78be0b 100644 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.test.tsx +++ b/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.test.tsx @@ -180,6 +180,25 @@ describe('ServiceNowSIR Fields', () => { expect(screen.queryByTestId('deprecated-connector-warning-callout')).not.toBeInTheDocument(); }); + it('does not show the deprecated callout when the connector is preconfigured', async () => { + render( + + ); + expect(screen.queryByTestId('deprecated-connector-warning-callout')).not.toBeInTheDocument(); + }); + + it('does not show the deprecated callout when the config of the connector is undefined', async () => { + render( + // @ts-expect-error + + ); + expect(screen.queryByTestId('deprecated-connector-warning-callout')).not.toBeInTheDocument(); + }); + test('it should hide subcategory if selecting a category without subcategories', async () => { // Failed Login doesn't have defined subcategories const customFields = { diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/validator.test.ts b/x-pack/plugins/cases/public/components/connectors/servicenow/validator.test.ts index aa643191ac62e..ab21a6b5c779c 100644 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/validator.test.ts +++ b/x-pack/plugins/cases/public/components/connectors/servicenow/validator.test.ts @@ -22,7 +22,7 @@ describe('ServiceNow validator', () => { expect(connectorValidator(invalidConnector)).toEqual({ message: 'Deprecated connector' }); }); - test('it does not returns an error message if the connector does not uses the table API', () => { + test('it does not return an error message if the connector does not uses the table API', () => { const invalidConnector = { ...connector, config: { @@ -33,5 +33,16 @@ describe('ServiceNow validator', () => { expect(connectorValidator(invalidConnector)).toBeFalsy(); }); + + test('it does not return an error message if the config of the connector is undefined', () => { + const { config, ...invalidConnector } = connector; + + // @ts-expect-error + expect(connectorValidator(invalidConnector)).toBeFalsy(); + }); + + test('it does not return an error message if the config of the connector is preconfigured', () => { + expect(connectorValidator({ ...connector, isPreconfigured: true })).toBeFalsy(); + }); }); }); diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/validator.ts b/x-pack/plugins/cases/public/components/connectors/servicenow/validator.ts index 7d56163c48350..fed2900715527 100644 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/validator.ts +++ b/x-pack/plugins/cases/public/components/connectors/servicenow/validator.ts @@ -15,10 +15,18 @@ import { CaseActionConnector } from '../../types'; export const connectorValidator = ( connector: CaseActionConnector ): ReturnType => { - const { - config: { usesTableApi }, - } = connector; - if (usesTableApi) { + /** + * It is not possible to know if a preconfigured connector + * is deprecated or not as the config property of a + * preconfigured connector is not returned by the + * actions framework + */ + + if (connector.isPreconfigured || connector.config == null) { + return; + } + + if (connector.config?.usesTableApi) { return { message: 'Deprecated connector', }; diff --git a/x-pack/plugins/cases/public/components/utils.test.ts b/x-pack/plugins/cases/public/components/utils.test.ts index 71218492dec95..278bb28b86627 100644 --- a/x-pack/plugins/cases/public/components/utils.test.ts +++ b/x-pack/plugins/cases/public/components/utils.test.ts @@ -83,5 +83,16 @@ describe('Utils', () => { }) ).toBe(true); }); + + it('returns false if the connector preconfigured', () => { + expect(isDeprecatedConnector({ ...connector, isPreconfigured: true })).toBe(false); + }); + + it('returns false if the config is undefined', () => { + expect( + // @ts-expect-error + isDeprecatedConnector({ ...connector, config: undefined }) + ).toBe(false); + }); }); }); diff --git a/x-pack/plugins/cases/public/components/utils.ts b/x-pack/plugins/cases/public/components/utils.ts index 5ff675a31ce61..34ebffb4eacb4 100644 --- a/x-pack/plugins/cases/public/components/utils.ts +++ b/x-pack/plugins/cases/public/components/utils.ts @@ -74,7 +74,13 @@ export const getConnectorIcon = ( // TODO: Remove when the applications are certified export const isDeprecatedConnector = (connector?: CaseActionConnector): boolean => { - if (connector == null) { + /** + * It is not possible to know if a preconfigured connector + * is deprecated or not as the config property of a + * preconfigured connector is not returned by the + * actions framework + */ + if (connector == null || connector.config == null || connector.isPreconfigured) { return false; } diff --git a/x-pack/plugins/cases/server/client/configure/client.test.ts b/x-pack/plugins/cases/server/client/configure/client.test.ts index f96f55a823aac..2889f00b6d28b 100644 --- a/x-pack/plugins/cases/server/client/configure/client.test.ts +++ b/x-pack/plugins/cases/server/client/configure/client.test.ts @@ -5,11 +5,10 @@ * 2.0. */ -import { CasesClientArgs } from '../types'; import { loggingSystemMock } from '@kbn/core/server/mocks'; -import { getConnectors } from './client'; import { actionsClientMock } from '@kbn/actions-plugin/server/mocks'; -import { ActionType } from '@kbn/actions-plugin/common/types'; +import { CasesClientArgs } from '../types'; +import { getConnectors } from './client'; describe('client', () => { describe('getConnectors', () => { @@ -18,68 +17,163 @@ describe('client', () => { const args = { actionsClient, logger } as unknown as CasesClientArgs; - const jiraType: ActionType = { - id: '.jira', - name: '1', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - minimumLicenseRequired: 'basic', - }; + const actionTypes = [ + { + id: '.jira', + name: '1', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic' as const, + }, + { + id: '.servicenow', + name: '2', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic' as const, + }, + { + id: '.unsupported', + name: '3', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic' as const, + }, + { + id: '.swimlane', + name: 'swimlane', + enabled: true, + enabledInConfig: true, + enabledInLicense: false, + minimumLicenseRequired: 'basic' as const, + }, + ]; + + const connectors = [ + { + id: '1', + actionTypeId: '.jira', + name: '1', + config: {}, + isPreconfigured: false, + isDeprecated: false, + referencedByCount: 1, + }, + { + id: '2', + actionTypeId: '.servicenow', + name: '2', + config: {}, + isPreconfigured: false, + isDeprecated: false, + referencedByCount: 1, + }, + { + id: '3', + actionTypeId: '.unsupported', + name: '3', + config: {}, + isPreconfigured: false, + isDeprecated: false, + referencedByCount: 1, + }, + ]; beforeEach(() => { jest.clearAllMocks(); }); - it('removes connectors without a config field defined', async () => { - actionsClient.listTypes.mockImplementation(async () => [jiraType]); + it('remove unsupported connectors', async () => { + actionsClient.listTypes.mockImplementation(async () => actionTypes); + actionsClient.getAll.mockImplementation(async () => connectors); - actionsClient.getAll.mockImplementation(async () => [ + expect(await getConnectors(args)).toEqual([ { id: '1', actionTypeId: '.jira', name: '1', + config: {}, + isPreconfigured: false, + isDeprecated: false, + referencedByCount: 1, + }, + { + id: '2', + actionTypeId: '.servicenow', + name: '2', + config: {}, isPreconfigured: false, isDeprecated: false, referencedByCount: 1, }, ]); - - expect(await getConnectors(args)).toEqual([]); }); - it('removes connectors that are pre configured', async () => { - actionsClient.listTypes.mockImplementation(async () => [jiraType]); - + it('returns preconfigured connectors', async () => { + actionsClient.listTypes.mockImplementation(async () => actionTypes); actionsClient.getAll.mockImplementation(async () => [ + ...connectors, + { + id: '4', + actionTypeId: '.servicenow', + name: 'sn-preconfigured', + config: {}, + isPreconfigured: true, + isDeprecated: false, + referencedByCount: 1, + }, + ]); + + expect(await getConnectors(args)).toEqual([ { id: '1', actionTypeId: '.jira', name: '1', config: {}, + isPreconfigured: false, + isDeprecated: false, + referencedByCount: 1, + }, + { + id: '2', + actionTypeId: '.servicenow', + name: '2', + config: {}, + isPreconfigured: false, + isDeprecated: false, + referencedByCount: 1, + }, + { + id: '4', + actionTypeId: '.servicenow', + name: 'sn-preconfigured', + config: {}, isPreconfigured: true, isDeprecated: false, referencedByCount: 1, }, ]); - - expect(await getConnectors(args)).toEqual([]); }); - it('includes connectors that have a config and are not pre configured', async () => { - actionsClient.listTypes.mockImplementation(async () => [ - jiraType, + it('filter out connectors that are unsupported by the current license', async () => { + actionsClient.listTypes.mockImplementation(async () => actionTypes); + actionsClient.getAll.mockImplementation(async () => [ + ...connectors, { - id: '.servicenow', - name: '2', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - minimumLicenseRequired: 'basic', + id: '4', + actionTypeId: '.swimlane', + name: 'swimlane', + config: {}, + isPreconfigured: false, + isDeprecated: false, + referencedByCount: 1, }, ]); - const connectors = [ + expect(await getConnectors(args)).toEqual([ { id: '1', actionTypeId: '.jira', @@ -98,11 +192,7 @@ describe('client', () => { isDeprecated: false, referencedByCount: 1, }, - ]; - - actionsClient.getAll.mockImplementation(async () => connectors); - - expect(await getConnectors(args)).toEqual(connectors); + ]); }); }); }); diff --git a/x-pack/plugins/cases/server/client/configure/client.ts b/x-pack/plugins/cases/server/client/configure/client.ts index c4b07019627e4..9bb6b83316264 100644 --- a/x-pack/plugins/cases/server/client/configure/client.ts +++ b/x-pack/plugins/cases/server/client/configure/client.ts @@ -223,9 +223,7 @@ function isConnectorSupported( ): boolean { return ( SUPPORTED_CONNECTORS.includes(action.actionTypeId) && - actionTypes[action.actionTypeId]?.enabledInLicense && - action.config != null && - !action.isPreconfigured + actionTypes[action.actionTypeId]?.enabledInLicense ); } diff --git a/x-pack/test/cases_api_integration/common/config.ts b/x-pack/test/cases_api_integration/common/config.ts index 8f82cbf84f671..89dd19ae74897 100644 --- a/x-pack/test/cases_api_integration/common/config.ts +++ b/x-pack/test/cases_api_integration/common/config.ts @@ -20,7 +20,6 @@ interface CreateTestConfigOptions { testFiles?: string[]; } -// test.not-enabled is specifically not enabled const enabledActionTypes = [ '.email', '.index', @@ -139,6 +138,19 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) (pluginDir) => `--plugin-path=${path.resolve(__dirname, 'fixtures', 'plugins', pluginDir)}` ), + `--xpack.actions.preconfigured=${JSON.stringify({ + 'preconfigured-servicenow': { + name: 'preconfigured-servicenow', + actionTypeId: '.servicenow', + config: { + apiUrl: 'https://example.com', + }, + secrets: { + username: 'elastic', + password: 'elastic', + }, + }, + })}`, `--server.xsrf.allowlist=${JSON.stringify(getAllExternalServiceSimulatorPaths())}`, ...(ssl ? [ diff --git a/x-pack/test/cases_api_integration/common/lib/utils.ts b/x-pack/test/cases_api_integration/common/lib/utils.ts index a433f5da1d74a..322bc3567211d 100644 --- a/x-pack/test/cases_api_integration/common/lib/utils.ts +++ b/x-pack/test/cases_api_integration/common/lib/utils.ts @@ -289,6 +289,19 @@ export const getWebhookConnector = () => ({ }, }); +export const getEmailConnector = () => ({ + name: 'An email action', + connector_type_id: '.email', + config: { + service: '__json', + from: 'bob@example.com', + }, + secrets: { + user: 'bob', + password: 'supersecret', + }, +}); + interface CommonSavedObjectAttributes { id?: string | null; created_at?: string | null; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/configure/create_connector.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/configure/create_connector.ts deleted file mode 100644 index fe8e311b5e4f6..0000000000000 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/configure/create_connector.ts +++ /dev/null @@ -1,20 +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 { createConnector, getServiceNowConnector } from '../../../../common/lib/utils'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function serviceNow({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - - describe('create service now action', () => { - it('should return 403 when creating a service now action', async () => { - await createConnector({ supertest, req: getServiceNowConnector(), expectedHttpCode: 403 }); - }); - }); -} diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/configure/get_connectors.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/configure/get_connectors.ts similarity index 64% rename from x-pack/test/cases_api_integration/security_and_spaces/tests/common/configure/get_connectors.ts rename to x-pack/test/cases_api_integration/security_and_spaces/tests/basic/configure/get_connectors.ts index 46f712ff84aa3..57854075c20fb 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/configure/get_connectors.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/configure/get_connectors.ts @@ -15,13 +15,18 @@ export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); describe('get_connectors', () => { - it('should return an empty find body correctly if no connectors are loaded', async () => { + /** + * A ServiceNow preconfigured connector is registered here + * x-pack/test/cases_api_integration/common/config.ts + * + * The license for this test is set to basic. ServiceNow connectors + * needs license >= platinum. The test below ensures + * that connectors without valid license are being filtered correctly + */ + it('should return an empty list of connectors', async () => { const connectors = await getCaseConnectors({ supertest }); - expect(connectors).to.eql([]); - }); - it.skip('filters out connectors that are not enabled in license', async () => { - // TODO: Should find a way to downgrade license to gold and upgrade back to trial + expect(connectors).to.eql([]); }); }); }; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/index.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/index.ts index ce2f59a115e69..b618cf5b4df68 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/index.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/index.ts @@ -24,6 +24,7 @@ export default ({ loadTestFile, getService }: FtrProviderContext): void => { // Basic loadTestFile(require.resolve('./cases/push_case')); + loadTestFile(require.resolve('./configure/get_connectors')); // Common loadTestFile(require.resolve('../common')); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/index.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/index.ts index f70c8593d3c94..25f39164f7c28 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/index.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/index.ts @@ -31,7 +31,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./cases/tags/get_tags')); loadTestFile(require.resolve('./user_actions/get_all_user_actions')); loadTestFile(require.resolve('./configure/get_configure')); - loadTestFile(require.resolve('./configure/get_connectors')); loadTestFile(require.resolve('./configure/patch_configure')); loadTestFile(require.resolve('./configure/post_configure')); loadTestFile(require.resolve('./metrics/get_case_metrics')); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts index 020159253d086..26df77bfbc924 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts @@ -6,7 +6,6 @@ */ import expect from '@kbn/expect'; -import { CASE_CONFIGURE_CONNECTORS_URL } from '@kbn/cases-plugin/common/constants'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { ObjectRemover as ActionsRemover } from '../../../../../alerting_api_integration/common/lib'; @@ -16,6 +15,8 @@ import { getResilientConnector, createConnector, getServiceNowSIRConnector, + getEmailConnector, + getCaseConnectors, } from '../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export @@ -29,41 +30,10 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should return the correct connectors', async () => { - const { body: snConnector } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'true') - .send(getServiceNowConnector()) - .expect(200); - - const { body: emailConnector } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'true') - .send({ - name: 'An email action', - connector_type_id: '.email', - config: { - service: '__json', - from: 'bob@example.com', - }, - secrets: { - user: 'bob', - password: 'supersecret', - }, - }) - .expect(200); - - const { body: jiraConnector } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'true') - .send(getJiraConnector()) - .expect(200); - - const { body: resilientConnector } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'true') - .send(getResilientConnector()) - .expect(200); - + const snConnector = await createConnector({ supertest, req: getServiceNowConnector() }); + const emailConnector = await createConnector({ supertest, req: getEmailConnector() }); + const jiraConnector = await createConnector({ supertest, req: getJiraConnector() }); + const resilientConnector = await createConnector({ supertest, req: getResilientConnector() }); const sir = await createConnector({ supertest, req: getServiceNowSIRConnector() }); actionsRemover.add('default', sir.id, 'action', 'actions'); @@ -72,11 +42,7 @@ export default ({ getService }: FtrProviderContext): void => { actionsRemover.add('default', jiraConnector.id, 'action', 'actions'); actionsRemover.add('default', resilientConnector.id, 'action', 'actions'); - const { body: connectors } = await supertest - .get(`${CASE_CONFIGURE_CONNECTORS_URL}/_find`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + const connectors = await getCaseConnectors({ supertest }); expect(connectors).to.eql([ { @@ -92,6 +58,18 @@ export default ({ getService }: FtrProviderContext): void => { isMissingSecrets: false, referencedByCount: 0, }, + /** + * Preconfigured connectors are being registered here: + * x-pack/test/cases_api_integration/common/config.ts + */ + { + actionTypeId: '.servicenow', + id: 'preconfigured-servicenow', + isPreconfigured: true, + isDeprecated: false, + name: 'preconfigured-servicenow', + referencedByCount: 0, + }, { id: resilientConnector.id, actionTypeId: '.resilient', diff --git a/x-pack/test/cases_api_integration/spaces_only/tests/trial/configure/get_connectors.ts b/x-pack/test/cases_api_integration/spaces_only/tests/trial/configure/get_connectors.ts index 2abe2996d5e21..c4115b5c4902d 100644 --- a/x-pack/test/cases_api_integration/spaces_only/tests/trial/configure/get_connectors.ts +++ b/x-pack/test/cases_api_integration/spaces_only/tests/trial/configure/get_connectors.ts @@ -18,6 +18,7 @@ import { getAuthWithSuperUser, getCaseConnectors, getActionsSpace, + getEmailConnector, } from '../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export @@ -38,32 +39,25 @@ export default ({ getService }: FtrProviderContext): void => { req: getServiceNowConnector(), auth: authSpace1, }); + const emailConnector = await createConnector({ supertest, - req: { - name: 'An email action', - connector_type_id: '.email', - config: { - service: '__json', - from: 'bob@example.com', - }, - secrets: { - user: 'bob', - password: 'supersecret', - }, - }, + req: getEmailConnector(), auth: authSpace1, }); + const jiraConnector = await createConnector({ supertest, req: getJiraConnector(), auth: authSpace1, }); + const resilientConnector = await createConnector({ supertest, req: getResilientConnector(), auth: authSpace1, }); + const sir = await createConnector({ supertest, req: getServiceNowSIRConnector(), @@ -92,6 +86,18 @@ export default ({ getService }: FtrProviderContext): void => { isMissingSecrets: false, referencedByCount: 0, }, + /** + * Preconfigured connectors are being registered here: + * x-pack/test/cases_api_integration/common/config.ts + */ + { + actionTypeId: '.servicenow', + id: 'preconfigured-servicenow', + isPreconfigured: true, + isDeprecated: false, + name: 'preconfigured-servicenow', + referencedByCount: 0, + }, { id: resilientConnector.id, actionTypeId: '.resilient', @@ -140,32 +146,25 @@ export default ({ getService }: FtrProviderContext): void => { req: getServiceNowConnector(), auth: authSpace1, }); + const emailConnector = await createConnector({ supertest, - req: { - name: 'An email action', - connector_type_id: '.email', - config: { - service: '__json', - from: 'bob@example.com', - }, - secrets: { - user: 'bob', - password: 'supersecret', - }, - }, + req: getEmailConnector(), auth: authSpace1, }); + const jiraConnector = await createConnector({ supertest, req: getJiraConnector(), auth: authSpace1, }); + const resilientConnector = await createConnector({ supertest, req: getResilientConnector(), auth: authSpace1, }); + const sir = await createConnector({ supertest, req: getServiceNowSIRConnector(), @@ -183,7 +182,20 @@ export default ({ getService }: FtrProviderContext): void => { auth: getAuthWithSuperUser('space2'), }); - expect(connectors).to.eql([]); + expect(connectors).to.eql([ + /** + * Preconfigured connectors are being registered here: + * x-pack/test/cases_api_integration/common/config.ts + */ + { + actionTypeId: '.servicenow', + id: 'preconfigured-servicenow', + isPreconfigured: true, + isDeprecated: false, + name: 'preconfigured-servicenow', + referencedByCount: 0, + }, + ]); }); }); }; From 6fccbacd7d6395a12be5b231c96e6d451036dec8 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 27 Apr 2022 11:44:01 +0200 Subject: [PATCH 17/78] [Lens] Stabilize a11y test (#131035) * stabilize a11y test * fix bug --- x-pack/test/accessibility/apps/lens.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/test/accessibility/apps/lens.ts b/x-pack/test/accessibility/apps/lens.ts index f99ff08f86bb8..8a46d662a61cf 100644 --- a/x-pack/test/accessibility/apps/lens.ts +++ b/x-pack/test/accessibility/apps/lens.ts @@ -119,8 +119,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('change chart type', async () => { - await PageObjects.lens.switchToVisualization('line'); + await PageObjects.lens.openChartSwitchPopover(); + await PageObjects.lens.waitForSearchInputValue('line'); await a11y.testAppSnapshot(); + await testSubjects.click('lnsChartSwitchPopover_line'); }); it('change chart type via suggestions', async () => { From f04d75f26ed6f7c4bbf49772e473010b03b34fd0 Mon Sep 17 00:00:00 2001 From: Tomasz Ciecierski Date: Wed, 27 Apr 2022 12:36:29 +0200 Subject: [PATCH 18/78] [Osquery] Fix osquery action permission and dropdown visibility (#130944) --- .../integration/all/live_query.spec.ts | 12 +- .../integration/roles/alert_test.spec.ts | 60 ++++++ .../integration/roles/t2_analyst.spec.ts | 12 +- x-pack/plugins/osquery/cypress/test/index.ts | 1 + .../public/assets/use_assets_status.ts | 1 + .../public/live_queries/form/index.tsx | 9 +- .../osquery/public/live_queries/index.tsx | 6 +- .../osquery/public/results/results_table.tsx | 8 +- .../public/routes/saved_queries/edit/tabs.tsx | 8 +- .../saved_queries/saved_query_flyout.tsx | 12 +- .../osquery_action/index.tsx | 88 +++------ .../osquery_action/osquery_action.test.tsx | 171 ++++++++++++++++++ .../osquery_action/translations.ts | 29 +++ .../use_is_osquery_available.ts | 15 +- .../roles_users/alert_test/delete_user.sh | 11 ++ .../roles_users/alert_test/get_role.sh | 11 ++ .../scripts/roles_users/alert_test/index.ts | 11 ++ .../roles_users/alert_test/post_role.sh | 14 ++ .../roles_users/alert_test/post_user.sh | 14 ++ .../scripts/roles_users/alert_test/role.json | 33 ++++ .../scripts/roles_users/alert_test/user.json | 6 + .../osquery/scripts/roles_users/index.ts | 1 + .../components/osquery/osquery_flyout.tsx | 2 +- 23 files changed, 443 insertions(+), 92 deletions(-) create mode 100644 x-pack/plugins/osquery/cypress/integration/roles/alert_test.spec.ts create mode 100644 x-pack/plugins/osquery/public/shared_components/osquery_action/osquery_action.test.tsx create mode 100644 x-pack/plugins/osquery/public/shared_components/osquery_action/translations.ts create mode 100755 x-pack/plugins/osquery/scripts/roles_users/alert_test/delete_user.sh create mode 100755 x-pack/plugins/osquery/scripts/roles_users/alert_test/get_role.sh create mode 100644 x-pack/plugins/osquery/scripts/roles_users/alert_test/index.ts create mode 100755 x-pack/plugins/osquery/scripts/roles_users/alert_test/post_role.sh create mode 100755 x-pack/plugins/osquery/scripts/roles_users/alert_test/post_user.sh create mode 100644 x-pack/plugins/osquery/scripts/roles_users/alert_test/role.json create mode 100644 x-pack/plugins/osquery/scripts/roles_users/alert_test/user.json diff --git a/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts index 1ddec794f41be..6a9d102784a58 100644 --- a/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts @@ -42,10 +42,10 @@ describe('ALL - Live Query', () => { cy.contains('View in Lens').should('exist'); cy.react(RESULTS_TABLE_CELL_WRRAPER, { props: { id: 'osquery.days.number', index: 1 }, - }); + }).should('exist'); cy.react(RESULTS_TABLE_CELL_WRRAPER, { props: { id: 'osquery.hours.number', index: 2 }, - }); + }).should('exist'); getAdvancedButton().click(); typeInECSFieldInput('message{downArrow}{enter}'); @@ -58,9 +58,13 @@ describe('ALL - Live Query', () => { }); cy.react(RESULTS_TABLE_CELL_WRRAPER, { props: { id: 'message', index: 1 }, - }); + }).should('exist'); cy.react(RESULTS_TABLE_CELL_WRRAPER, { props: { id: 'osquery.days.number', index: 2 }, - }).react('EuiIconIndexMapping'); + }).within(() => { + cy.get('.euiToolTipAnchor').within(() => { + cy.get('svg').should('exist'); + }); + }); }); }); diff --git a/x-pack/plugins/osquery/cypress/integration/roles/alert_test.spec.ts b/x-pack/plugins/osquery/cypress/integration/roles/alert_test.spec.ts new file mode 100644 index 0000000000000..5d25b6599b13c --- /dev/null +++ b/x-pack/plugins/osquery/cypress/integration/roles/alert_test.spec.ts @@ -0,0 +1,60 @@ +/* + * 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 { ROLES } from '../../test'; +import { ArchiverMethod, runKbnArchiverScript } from '../../tasks/archiver'; +import { login } from '../../tasks/login'; +import { findAndClickButton, findFormFieldByRowsLabelAndType } from '../../tasks/live_query'; +import { preparePack } from '../../tasks/packs'; +import { closeModalIfVisible } from '../../tasks/integrations'; +import { navigateTo } from '../../tasks/navigation'; + +describe('Alert_Test', () => { + before(() => { + runKbnArchiverScript(ArchiverMethod.LOAD, 'pack'); + runKbnArchiverScript(ArchiverMethod.LOAD, 'rule'); + }); + beforeEach(() => { + login(ROLES.alert_test); + }); + + after(() => { + runKbnArchiverScript(ArchiverMethod.UNLOAD, 'pack'); + runKbnArchiverScript(ArchiverMethod.UNLOAD, 'rule'); + }); + + it('should be able to run live query', () => { + const PACK_NAME = 'testpack'; + const RULE_NAME = 'Test-rule'; + navigateTo('/app/osquery'); + preparePack(PACK_NAME); + findAndClickButton('Edit'); + cy.contains(`Edit ${PACK_NAME}`); + findFormFieldByRowsLabelAndType( + 'Scheduled agent policies (optional)', + 'fleet server {downArrow}{enter}' + ); + findAndClickButton('Update pack'); + closeModalIfVisible(); + cy.contains(PACK_NAME); + cy.visit('/app/security/rules'); + cy.contains(RULE_NAME).click(); + cy.wait(2000); + cy.getBySel('ruleSwitch').should('have.attr', 'aria-checked', 'true'); + cy.getBySel('ruleSwitch').click(); + cy.getBySel('ruleSwitch').should('have.attr', 'aria-checked', 'false'); + cy.getBySel('ruleSwitch').click(); + cy.getBySel('ruleSwitch').should('have.attr', 'aria-checked', 'true'); + cy.visit('/app/security/alerts'); + cy.getBySel('expand-event').first().click(); + cy.getBySel('take-action-dropdown-btn').click(); + cy.getBySel('osquery-action-item').click(); + + cy.contains('Run Osquery'); + cy.contains('Permission denied'); + }); +}); diff --git a/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts b/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts index 805eb134a44f5..619865e50bb6b 100644 --- a/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts @@ -74,10 +74,10 @@ describe('T2 Analyst - READ + Write Live/Saved + runSavedQueries ', () => { cy.contains('View in Lens').should('not.exist'); cy.react('EuiDataGridHeaderCellWrapper', { props: { id: 'osquery.days.number', index: 1 }, - }); + }).should('exist'); cy.react('EuiDataGridHeaderCellWrapper', { props: { id: 'osquery.hours.number', index: 2 }, - }); + }).should('exist'); cy.react('EuiAccordion', { props: { buttonContent: 'Advanced' } }).click(); typeInECSFieldInput('message{downArrow}{enter}'); @@ -87,10 +87,14 @@ describe('T2 Analyst - READ + Write Live/Saved + runSavedQueries ', () => { checkResults(); cy.react('EuiDataGridHeaderCellWrapper', { props: { id: 'message', index: 1 }, - }); + }).should('exist'); cy.react('EuiDataGridHeaderCellWrapper', { props: { id: 'osquery.days.number', index: 2 }, - }).react('EuiIconIndexMapping'); + }).within(() => { + cy.get('.euiToolTipAnchor').within(() => { + cy.get('svg').should('exist'); + }); + }); }); it('to click the edit button and edit pack', () => { navigateTo('/app/osquery/saved_queries'); diff --git a/x-pack/plugins/osquery/cypress/test/index.ts b/x-pack/plugins/osquery/cypress/test/index.ts index 53261d54e84b0..11cca6c93c553 100644 --- a/x-pack/plugins/osquery/cypress/test/index.ts +++ b/x-pack/plugins/osquery/cypress/test/index.ts @@ -15,4 +15,5 @@ export enum ROLES { rule_author = 'rule_author', platform_engineer = 'platform_engineer', detections_admin = 'detections_admin', + alert_test = 'alert_test', } diff --git a/x-pack/plugins/osquery/public/assets/use_assets_status.ts b/x-pack/plugins/osquery/public/assets/use_assets_status.ts index fd6a2fecb2793..a3ae65c964cca 100644 --- a/x-pack/plugins/osquery/public/assets/use_assets_status.ts +++ b/x-pack/plugins/osquery/public/assets/use_assets_status.ts @@ -18,6 +18,7 @@ export const useAssetsStatus = () => { () => http.get('/internal/osquery/assets'), { keepPreviousData: true, + retry: false, } ); }; diff --git a/x-pack/plugins/osquery/public/live_queries/form/index.tsx b/x-pack/plugins/osquery/public/live_queries/form/index.tsx index f082ca0baeebe..e63ad38aaa695 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/index.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/index.tsx @@ -62,7 +62,7 @@ interface LiveQueryFormProps { ecsMappingField?: boolean; formType?: FormType; enabled?: boolean; - hideFullscreen?: true; + isExternal?: true; } const LiveQueryFormComponent: React.FC = ({ @@ -73,7 +73,7 @@ const LiveQueryFormComponent: React.FC = ({ ecsMappingField = true, formType = 'steps', enabled = true, - hideFullscreen, + isExternal, }) => { const ecsFieldRef = useRef(); const permissions = useKibana().services.application.capabilities.osquery; @@ -393,10 +393,10 @@ const LiveQueryFormComponent: React.FC = ({ actionId={actionId} endDate={data?.actions[0].expiration} agentIds={agentIds} - hideFullscreen={hideFullscreen} + isExternal={isExternal} /> ) : null, - [actionId, agentIds, data?.actions, hideFullscreen] + [actionId, agentIds, data?.actions, isExternal] ); const formSteps: EuiContainedStepProps[] = useMemo( @@ -467,6 +467,7 @@ const LiveQueryFormComponent: React.FC = ({ {showSavedQueryFlyout ? ( diff --git a/x-pack/plugins/osquery/public/live_queries/index.tsx b/x-pack/plugins/osquery/public/live_queries/index.tsx index fdde03d6076a6..56cac9cdf5ec7 100644 --- a/x-pack/plugins/osquery/public/live_queries/index.tsx +++ b/x-pack/plugins/osquery/public/live_queries/index.tsx @@ -28,7 +28,7 @@ interface LiveQueryProps { ecsMappingField?: boolean; enabled?: boolean; formType?: 'steps' | 'simple'; - hideFullscreen?: true; + isExternal?: true; } const LiveQueryComponent: React.FC = ({ @@ -45,7 +45,7 @@ const LiveQueryComponent: React.FC = ({ ecsMappingField, formType, enabled, - hideFullscreen, + isExternal, }) => { const { data: hasActionResultsPrivileges, isLoading } = useActionResultsPrivileges(); @@ -115,7 +115,7 @@ const LiveQueryComponent: React.FC = ({ onSuccess={onSuccess} formType={formType} enabled={enabled} - hideFullscreen={hideFullscreen} + isExternal={isExternal} /> ); }; diff --git a/x-pack/plugins/osquery/public/results/results_table.tsx b/x-pack/plugins/osquery/public/results/results_table.tsx index dec1d08a0d012..9b3dc144a7e3f 100644 --- a/x-pack/plugins/osquery/public/results/results_table.tsx +++ b/x-pack/plugins/osquery/public/results/results_table.tsx @@ -46,7 +46,7 @@ interface ResultsTableComponentProps { agentIds?: string[]; endDate?: string; startDate?: string; - hideFullscreen?: true; + isExternal?: true; } const ResultsTableComponent: React.FC = ({ @@ -54,7 +54,7 @@ const ResultsTableComponent: React.FC = ({ agentIds, startDate, endDate, - hideFullscreen, + isExternal, }) => { const [isLive, setIsLive] = useState(true); const { data: hasActionResultsPrivileges } = useActionResultsPrivileges(); @@ -312,7 +312,7 @@ const ResultsTableComponent: React.FC = ({ const toolbarVisibility = useMemo( () => ({ showDisplaySelector: false, - showFullScreenSelector: !hideFullscreen, + showFullScreenSelector: !isExternal, additionalControls: ( <> = ({ ), }), - [actionId, endDate, startDate, hideFullscreen] + [actionId, endDate, startDate, isExternal] ); useEffect( diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx index d0a48aa99781f..0956dc6528a7f 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx @@ -17,7 +17,7 @@ interface ResultTabsProps { agentIds?: string[]; startDate?: string; endDate?: string; - hideFullscreen?: true; + isExternal?: true; } const ResultTabsComponent: React.FC = ({ @@ -25,7 +25,7 @@ const ResultTabsComponent: React.FC = ({ agentIds, endDate, startDate, - hideFullscreen, + isExternal, }) => { const tabs = useMemo( () => [ @@ -40,7 +40,7 @@ const ResultTabsComponent: React.FC = ({ agentIds={agentIds} startDate={startDate} endDate={endDate} - hideFullscreen={hideFullscreen} + isExternal={isExternal} /> ), @@ -60,7 +60,7 @@ const ResultTabsComponent: React.FC = ({ ), }, ], - [actionId, agentIds, endDate, startDate, hideFullscreen] + [actionId, agentIds, endDate, startDate, isExternal] ); return ( diff --git a/x-pack/plugins/osquery/public/saved_queries/saved_query_flyout.tsx b/x-pack/plugins/osquery/public/saved_queries/saved_query_flyout.tsx index 69fe9442a1cbb..899c27a1ef8fd 100644 --- a/x-pack/plugins/osquery/public/saved_queries/saved_query_flyout.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/saved_query_flyout.tsx @@ -28,9 +28,16 @@ import { useCreateSavedQuery } from './use_create_saved_query'; interface AddQueryFlyoutProps { defaultValue: unknown; onClose: () => void; + isExternal?: true; } -const SavedQueryFlyoutComponent: React.FC = ({ defaultValue, onClose }) => { +const additionalZIndexStyle = { style: 'z-index: 6000' }; + +const SavedQueryFlyoutComponent: React.FC = ({ + defaultValue, + onClose, + isExternal, +}) => { const savedQueryFormRef = useRef(null); const createSavedQueryMutation = useCreateSavedQuery({ withRedirect: false }); @@ -53,8 +60,7 @@ const SavedQueryFlyoutComponent: React.FC = ({ defaultValue ownFocus onClose={onClose} aria-labelledby="flyoutTitle" - // eslint-disable-next-line react-perf/jsx-no-new-object-as-prop - maskProps={{ style: 'z-index: 6000' }} // For an edge case to display above the alerts flyout + maskProps={isExternal && additionalZIndexStyle} // For an edge case to display above the alerts flyout > diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx b/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx index a0ee8bf314e57..5fbc6caedcd15 100644 --- a/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx +++ b/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx @@ -8,7 +8,13 @@ import { EuiErrorBoundary, EuiLoadingContent, EuiEmptyPrompt, EuiCode } from '@elastic/eui'; import React, { useMemo } from 'react'; import { QueryClientProvider } from 'react-query'; -import { i18n } from '@kbn/i18n'; +import { + AGENT_STATUS_ERROR, + EMPTY_PROMPT, + NOT_AVAILABLE, + PERMISSION_DENIED, + SHORT_EMPTY_TITLE, +} from './translations'; import { KibanaContextProvider, useKibana } from '../../common/lib/kibana'; import { LiveQuery } from '../../live_queries'; @@ -20,36 +26,19 @@ import { useIsOsqueryAvailable } from './use_is_osquery_available'; interface OsqueryActionProps { agentId?: string; formType: 'steps' | 'simple'; - hideFullscreen?: true; + isExternal?: true; } -const OsqueryActionComponent: React.FC = ({ - agentId, - formType = 'simple', - hideFullscreen, -}) => { +const OsqueryActionComponent: React.FC = ({ agentId, formType = 'simple' }) => { const permissions = useKibana().services.application.capabilities.osquery; const emptyPrompt = useMemo( () => ( } - title={ -

- {i18n.translate('xpack.osquery.action.shortEmptyTitle', { - defaultMessage: 'Osquery is not available', - })} -

- } + title={

{SHORT_EMPTY_TITLE}

} titleSize="xs" - body={ -

- {i18n.translate('xpack.osquery.action.empty', { - defaultMessage: - 'An Elastic Agent is not installed on this host. To run queries, install Elastic Agent on the host, and then add the Osquery Manager integration to the agent policy in Fleet.', - })} -

- } + body={

{EMPTY_PROMPT}

} /> ), [] @@ -61,17 +50,14 @@ const OsqueryActionComponent: React.FC = ({ return emptyPrompt; } - if (!(permissions.runSavedQueries || permissions.writeLiveQueries)) { + if ( + (!permissions.runSavedQueries || !permissions.readSavedQueries) && + !permissions.writeLiveQueries + ) { return ( } - title={ -

- {i18n.translate('xpack.osquery.action.permissionDenied', { - defaultMessage: 'Permission denied', - })} -

- } + title={

{PERMISSION_DENIED}

} titleSize="xs" body={

@@ -95,22 +81,9 @@ const OsqueryActionComponent: React.FC = ({ return ( } - title={ -

- {i18n.translate('xpack.osquery.action.shortEmptyTitle', { - defaultMessage: 'Osquery is not available', - })} -

- } + title={

{SHORT_EMPTY_TITLE}

} titleSize="xs" - body={ -

- {i18n.translate('xpack.osquery.action.unavailable', { - defaultMessage: - 'The Osquery Manager integration is not added to the agent policy. To run queries on the host, add the Osquery Manager integration to the agent policy in Fleet.', - })} -

- } + body={

{NOT_AVAILABLE}

} /> ); } @@ -119,38 +92,25 @@ const OsqueryActionComponent: React.FC = ({ return ( } - title={ -

- {i18n.translate('xpack.osquery.action.shortEmptyTitle', { - defaultMessage: 'Osquery is not available', - })} -

- } + title={

{SHORT_EMPTY_TITLE}

} titleSize="xs" - body={ -

- {i18n.translate('xpack.osquery.action.agentStatus', { - defaultMessage: - 'To run queries on this host, the Elastic Agent must be active. Check the status of this agent in Fleet.', - })} -

- } + body={

{AGENT_STATUS_ERROR}

} /> ); } - return ; + return ; }; -const OsqueryAction = React.memo(OsqueryActionComponent); +export const OsqueryAction = React.memo(OsqueryActionComponent); // @ts-expect-error update types -const OsqueryActionWrapperComponent = ({ services, agentId, formType, hideFullscreen }) => ( +const OsqueryActionWrapperComponent = ({ services, agentId, formType, isExternal }) => ( - + diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_action/osquery_action.test.tsx b/x-pack/plugins/osquery/public/shared_components/osquery_action/osquery_action.test.tsx new file mode 100644 index 0000000000000..f6128642ba645 --- /dev/null +++ b/x-pack/plugins/osquery/public/shared_components/osquery_action/osquery_action.test.tsx @@ -0,0 +1,171 @@ +/* + * 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 React from 'react'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { render } from '@testing-library/react'; +import { QueryClientProvider } from 'react-query'; + +import { OsqueryAction } from '.'; +import { queryClient } from '../../query_client'; +import * as hooks from './use_is_osquery_available'; +import { useKibana } from '../../common/lib/kibana'; +import { AGENT_STATUS_ERROR, EMPTY_PROMPT, NOT_AVAILABLE, PERMISSION_DENIED } from './translations'; + +jest.mock('../../common/lib/kibana'); + +const useKibanaMock = useKibana as jest.MockedFunction; + +const defaultUseOsqueryAvailableResult = { + osqueryAvailable: true, + agentFetched: true, + isLoading: false, + policyFetched: true, + policyLoading: false, +}; + +const spyUseIsOsqueryAvailable = jest + .spyOn(hooks, 'useIsOsqueryAvailable') + .mockImplementation(() => ({ + ...defaultUseOsqueryAvailableResult, + agentData: {}, + })); + +const defaultPermissions = { + osquery: { + runSavedQueries: false, + readSavedQueries: false, + }, +}; + +const mockKibana = (permissionType: unknown = defaultPermissions) => { + useKibanaMock.mockReturnValue({ + services: { + application: { + capabilities: permissionType, + }, + }, + } as unknown as ReturnType); +}; + +const spyOsquery = (data: Record = {}) => { + spyUseIsOsqueryAvailable.mockImplementation(() => ({ + ...defaultUseOsqueryAvailableResult, + ...data, + })); +}; + +const properPermissions = { + osquery: { + runSavedQueries: true, + writeLiveQueries: true, + }, +}; + +const renderWithContext = (Element: React.ReactElement) => + render( + + {Element} + + ); + +describe('Osquery Action', () => { + it('should return empty prompt when agentFetched and no agentData', async () => { + spyOsquery(); + mockKibana(); + + const { getByText } = renderWithContext( + + ); + expect(getByText(EMPTY_PROMPT)).toBeInTheDocument(); + }); + it('should return empty prompt when no agentId', async () => { + spyOsquery(); + mockKibana(); + + const { getByText } = renderWithContext( + + ); + expect(getByText(EMPTY_PROMPT)).toBeInTheDocument(); + }); + it('should return permission denied when agentFetched and agentData available', async () => { + spyOsquery({ agentData: {} }); + mockKibana(); + + const { getByText } = renderWithContext( + + ); + expect(getByText(PERMISSION_DENIED)).toBeInTheDocument(); + }); + it('should return agent status error when permissions are ok and agent status is wrong', async () => { + spyOsquery({ agentData: {} }); + mockKibana(properPermissions); + const { getByText } = renderWithContext( + + ); + expect(getByText(AGENT_STATUS_ERROR)).toBeInTheDocument(); + }); + it('should return permission denied if just one permission (runSavedQueries) is available', async () => { + spyOsquery({ agentData: {} }); + mockKibana({ + osquery: { + runSavedQueries: true, + }, + }); + const { getByText } = renderWithContext( + + ); + expect(getByText(PERMISSION_DENIED)).toBeInTheDocument(); + }); + it('should return permission denied if just one permission (readSavedQueries) is available', async () => { + spyOsquery({ agentData: {} }); + mockKibana({ + osquery: { + readSavedQueries: true, + }, + }); + const { getByText } = renderWithContext( + + ); + expect(getByText(PERMISSION_DENIED)).toBeInTheDocument(); + }); + it('should return permission denied if no writeLiveQueries', async () => { + spyOsquery({ agentData: {} }); + mockKibana({ + osquery: { + writeLiveQueries: true, + }, + }); + const { getByText } = renderWithContext( + + ); + expect(getByText(AGENT_STATUS_ERROR)).toBeInTheDocument(); + }); + it('should return not available prompt if osquery is not available', async () => { + spyOsquery({ agentData: {}, osqueryAvailable: false }); + mockKibana({ + osquery: { + writeLiveQueries: true, + }, + }); + const { getByText } = renderWithContext( + + ); + expect(getByText(NOT_AVAILABLE)).toBeInTheDocument(); + }); + it('should not return any errors when all data is ok', async () => { + spyOsquery({ agentData: { status: 'online' } }); + mockKibana(properPermissions); + + const { queryByText } = renderWithContext( + + ); + expect(queryByText(EMPTY_PROMPT)).not.toBeInTheDocument(); + expect(queryByText(PERMISSION_DENIED)).not.toBeInTheDocument(); + expect(queryByText(AGENT_STATUS_ERROR)).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_action/translations.ts b/x-pack/plugins/osquery/public/shared_components/osquery_action/translations.ts new file mode 100644 index 0000000000000..cd49b6de7b7c0 --- /dev/null +++ b/x-pack/plugins/osquery/public/shared_components/osquery_action/translations.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const SHORT_EMPTY_TITLE = i18n.translate('xpack.osquery.action.shortEmptyTitle', { + defaultMessage: 'Osquery is not available', +}); + +export const EMPTY_PROMPT = i18n.translate('xpack.osquery.action.empty', { + defaultMessage: + 'An Elastic Agent is not installed on this host. To run queries, install Elastic Agent on the host, and then add the Osquery Manager integration to the agent policy in Fleet.', +}); +export const PERMISSION_DENIED = i18n.translate('xpack.osquery.action.permissionDenied', { + defaultMessage: 'Permission denied', +}); + +export const NOT_AVAILABLE = i18n.translate('xpack.osquery.action.unavailable', { + defaultMessage: + 'The Osquery Manager integration is not added to the agent policy. To run queries on the host, add the Osquery Manager integration to the agent policy in Fleet.', +}); +export const AGENT_STATUS_ERROR = i18n.translate('xpack.osquery.action.agentStatus', { + defaultMessage: + 'To run queries on this host, the Elastic Agent must be active. Check the status of this agent in Fleet.', +}); diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_action/use_is_osquery_available.ts b/x-pack/plugins/osquery/public/shared_components/osquery_action/use_is_osquery_available.ts index 5cd84739cd7d1..4fa52dcb75f01 100644 --- a/x-pack/plugins/osquery/public/shared_components/osquery_action/use_is_osquery_available.ts +++ b/x-pack/plugins/osquery/public/shared_components/osquery_action/use_is_osquery_available.ts @@ -7,11 +7,24 @@ import { useMemo } from 'react'; import { find } from 'lodash'; +import { AgentStatus } from '@kbn/fleet-plugin/common'; import { useAgentDetails } from '../../agents/use_agent_details'; import { useAgentPolicy } from '../../agent_policies'; import { OSQUERY_INTEGRATION_NAME } from '../../../common'; -export const useIsOsqueryAvailable = (agentId?: string) => { +interface IIsOsqueryAvailable { + osqueryAvailable: boolean; + agentFetched: boolean; + isLoading: boolean; + policyFetched: boolean; + policyLoading: boolean; + agentData?: { + status?: AgentStatus; + policy_id?: string; + }; +} + +export const useIsOsqueryAvailable = (agentId?: string): IIsOsqueryAvailable => { const { data: agentData, isFetched: agentFetched, diff --git a/x-pack/plugins/osquery/scripts/roles_users/alert_test/delete_user.sh b/x-pack/plugins/osquery/scripts/roles_users/alert_test/delete_user.sh new file mode 100755 index 0000000000000..f9198c39bdbc5 --- /dev/null +++ b/x-pack/plugins/osquery/scripts/roles_users/alert_test/delete_user.sh @@ -0,0 +1,11 @@ + +# +# 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. +# + +curl -v -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-XDELETE ${ELASTICSEARCH_URL}/_security/user/alert_test diff --git a/x-pack/plugins/osquery/scripts/roles_users/alert_test/get_role.sh b/x-pack/plugins/osquery/scripts/roles_users/alert_test/get_role.sh new file mode 100755 index 0000000000000..06400b4e74876 --- /dev/null +++ b/x-pack/plugins/osquery/scripts/roles_users/alert_test/get_role.sh @@ -0,0 +1,11 @@ + +# +# 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. +# + +curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-XGET ${KIBANA_URL}/api/security/role/alert_test | jq -S . diff --git a/x-pack/plugins/osquery/scripts/roles_users/alert_test/index.ts b/x-pack/plugins/osquery/scripts/roles_users/alert_test/index.ts new file mode 100644 index 0000000000000..7f79ece04cccc --- /dev/null +++ b/x-pack/plugins/osquery/scripts/roles_users/alert_test/index.ts @@ -0,0 +1,11 @@ +/* + * 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 * as alertTestUser from './user.json'; +import * as alertTestRole from './role.json'; + +export { alertTestUser, alertTestRole }; diff --git a/x-pack/plugins/osquery/scripts/roles_users/alert_test/post_role.sh b/x-pack/plugins/osquery/scripts/roles_users/alert_test/post_role.sh new file mode 100755 index 0000000000000..6150119851abe --- /dev/null +++ b/x-pack/plugins/osquery/scripts/roles_users/alert_test/post_role.sh @@ -0,0 +1,14 @@ + +# +# 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. +# + +ROLE_CONFIG=(${@:-./detections_role.json}) + +curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-XPUT ${KIBANA_URL}/api/security/role/alert_test \ +-d @${ROLE_CONFIG} diff --git a/x-pack/plugins/osquery/scripts/roles_users/alert_test/post_user.sh b/x-pack/plugins/osquery/scripts/roles_users/alert_test/post_user.sh new file mode 100755 index 0000000000000..c58aad8f74516 --- /dev/null +++ b/x-pack/plugins/osquery/scripts/roles_users/alert_test/post_user.sh @@ -0,0 +1,14 @@ + +# +# 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. +# + +USER=(${@:-./detections_user.json}) + +curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + ${ELASTICSEARCH_URL}/_security/user/alert_test \ +-d @${USER} diff --git a/x-pack/plugins/osquery/scripts/roles_users/alert_test/role.json b/x-pack/plugins/osquery/scripts/roles_users/alert_test/role.json new file mode 100644 index 0000000000000..4b97c372a1d40 --- /dev/null +++ b/x-pack/plugins/osquery/scripts/roles_users/alert_test/role.json @@ -0,0 +1,33 @@ +{ + "elasticsearch": { + "cluster": ["manage"], + "indices": [ + { + "names": [".items-*", ".lists-*", ".alerts-security.alerts-*", ".siem-signals-*"], + "privileges": ["manage", "read", "write", "view_index_metadata", "maintenance"] + }, + { + "names": ["*"], + "privileges": ["read"] + }, + { + "names": ["logs-osquery_manager*"], + "privileges": ["read"] + } + ] + }, + "kibana": [ + { + "feature": { + "discover": ["read"], + "infrastructure": ["read"], + "ml": ["all"], + "siem": ["all"], + "osquery": ["read", "packs_all"], + "visualize": ["read"] + }, + "spaces": ["*"] + } + ] +} + diff --git a/x-pack/plugins/osquery/scripts/roles_users/alert_test/user.json b/x-pack/plugins/osquery/scripts/roles_users/alert_test/user.json new file mode 100644 index 0000000000000..92eb767f964ba --- /dev/null +++ b/x-pack/plugins/osquery/scripts/roles_users/alert_test/user.json @@ -0,0 +1,6 @@ +{ + "password": "changeme", + "roles": ["alert_test"], + "full_name": "Alert Test", + "email": "osquery@example.com" +} diff --git a/x-pack/plugins/osquery/scripts/roles_users/index.ts b/x-pack/plugins/osquery/scripts/roles_users/index.ts index 755b09387770d..2bb4c94108373 100644 --- a/x-pack/plugins/osquery/scripts/roles_users/index.ts +++ b/x-pack/plugins/osquery/scripts/roles_users/index.ts @@ -10,3 +10,4 @@ export * from './reader'; export * from './t1_analyst'; export * from './t2_analyst'; export * from './soc_manager'; +export * from './alert_test'; diff --git a/x-pack/plugins/security_solution/public/detections/components/osquery/osquery_flyout.tsx b/x-pack/plugins/security_solution/public/detections/components/osquery/osquery_flyout.tsx index 0001c2966c1bc..3262fc36abf75 100644 --- a/x-pack/plugins/security_solution/public/detections/components/osquery/osquery_flyout.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/osquery/osquery_flyout.tsx @@ -45,7 +45,7 @@ export const OsqueryFlyout: React.FC = ({ agentId, onClose }
- + From eaf47aeb1af9b935685fe12f5591be498999a9ab Mon Sep 17 00:00:00 2001 From: Muhammad Ibragimov <53621505+mibragimov@users.noreply.github.com> Date: Wed, 27 Apr 2022 15:46:57 +0500 Subject: [PATCH 19/78] [Console] Handle multiple request exceptions (#129443) * Add handling multiple request exceptions - Fix code styling for send_request.test.ts - Extract a way of getting content-type to a separate function Co-authored-by: Muhammad Ibragimov --- .../send_request.test.ts | 59 +++++++++++-------- .../use_send_current_request/send_request.ts | 23 ++++++-- 2 files changed, 51 insertions(+), 31 deletions(-) diff --git a/src/plugins/console/public/application/hooks/use_send_current_request/send_request.test.ts b/src/plugins/console/public/application/hooks/use_send_current_request/send_request.test.ts index 60ced085c6891..841633cded51e 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request/send_request.test.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request/send_request.test.ts @@ -49,34 +49,43 @@ describe('sendRequest', () => { expect(mockedSendRequest).toHaveBeenCalledTimes(1); }); - it('should send multiple requests', async () => { - mockedSendRequest.mockResolvedValue([ - { - response: { - statusCode: 200, + describe('with multiple requests', () => { + it('should return results with exceptions', async () => { + mockedSendRequest.mockResolvedValue([ + { + response: { + statusCode: 200, + }, }, - }, - { - response: { - statusCode: 200, + { + response: { + statusCode: 200, + }, }, - }, - ]); - - const args = { - http: mockContextValue.services.http, - requests: [ - { method: 'GET', url: 'test-1', data: [] }, - { method: 'GET', url: 'test-2', data: [] }, - ], - }; - const results = await sendRequest(args); + { + response: { + statusCode: 400, + }, + }, + ]); - const [firstRequest, secondRequest] = results; - expect(firstRequest.response.statusCode).toEqual(200); - expect(secondRequest.response.statusCode).toEqual(200); - expect(mockedSendRequest).toHaveBeenCalledWith(args); - expect(mockedSendRequest).toHaveBeenCalledTimes(1); + const args = { + http: mockContextValue.services.http, + requests: [ + { method: 'GET', url: 'success', data: [] }, + { method: 'GET', url: 'success', data: [] }, + { method: 'GET', url: 'fail', data: [] }, + ], + }; + const results = await sendRequest(args); + + const [firstCall, secondCall, thirdCall] = results; + expect(firstCall.response.statusCode).toEqual(200); + expect(secondCall.response.statusCode).toEqual(200); + expect(thirdCall.response.statusCode).toEqual(400); + expect(mockedSendRequest).toHaveBeenCalledWith(args); + expect(mockedSendRequest).toHaveBeenCalledTimes(1); + }); }); it('should handle errors', async () => { diff --git a/src/plugins/console/public/application/hooks/use_send_current_request/send_request.ts b/src/plugins/console/public/application/hooks/use_send_current_request/send_request.ts index 1247f3f78aa68..1ac47df30fca5 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request/send_request.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request/send_request.ts @@ -33,6 +33,9 @@ export interface RequestResult { response: ResponseObject; } +const getContentType = (response: Response | undefined) => + (response?.headers.get('Content-Type') as BaseResponseType) ?? ''; + let CURRENT_REQ_ID = 0; export function sendRequest(args: RequestArgs): Promise { const requests = args.requests.slice(); @@ -111,7 +114,7 @@ export function sendRequest(args: RequestArgs): Promise { timeMs: Date.now() - startTime, statusCode: response.status, statusText: response.statusText, - contentType: response.headers.get('Content-Type') as BaseResponseType, + contentType: getContentType(response), value, }, request: { @@ -128,9 +131,8 @@ export function sendRequest(args: RequestArgs): Promise { } catch (error) { let value; const { response, body } = error as IHttpFetchError; - const contentType = response?.headers.get('Content-Type') ?? ''; const statusCode = response?.status ?? 500; - const statusText = error?.response?.statusText ?? 'error'; + const statusText = response?.statusText ?? 'error'; if (body) { value = JSON.stringify(body, null, 2); @@ -142,10 +144,10 @@ export function sendRequest(args: RequestArgs): Promise { value = '# ' + req.method + ' ' + req.url + '\n' + value; } - reject({ + const result = { response: { value, - contentType, + contentType: getContentType(response), timeMs: Date.now() - startTime, statusCode, statusText, @@ -155,7 +157,16 @@ export function sendRequest(args: RequestArgs): Promise { method, path, }, - }); + }; + + // Reject on unknown errors + if (!response) { + reject(result); + } + + // Add error to the list of results + results.push(result); + await sendNextRequest(); } }; From c6be790d89a877f5ff8e542c77e9f2214f655ee7 Mon Sep 17 00:00:00 2001 From: Dominique Clarke Date: Wed, 27 Apr 2022 08:22:49 -0400 Subject: [PATCH 20/78] [Trigger Actions UI] allow rule owners to specify default alert recovery messages (#130925) * trigger actions ui - allow rule owners to specify default alert recovery messages * adjust documentation * Update x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx * Revert "Update x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx" This reverts commit 8648d80f7d9a364dc7652b43e3d145e70aae72ca. * Update x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx * Update x-pack/plugins/triggers_actions_ui/README.md Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/triggers_actions_ui/README.md | 3 +- .../sections/rule_form/rule_form.test.tsx | 149 +++++++++++++++++- .../sections/rule_form/rule_form.tsx | 3 +- .../triggers_actions_ui/public/types.ts | 1 + 4 files changed, 152 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index f4a1f79da1546..7fde1f1512302 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -252,7 +252,8 @@ Each alert type should be defined as `RuleTypeModel` object with the these prope |iconClass|Icon of the alert type that will be displayed on the select card in the UI.| |validate|Validation function for the alert params.| |ruleParamsExpression| A lazy loaded React component for building UI of the current alert type params.| -|defaultActionMessage|Optional property for providing default message for all added actions with `message` property.| +|defaultActionMessage|Optional property for providing default messages for all added actions, excluding the Recovery action group, with `message` property. | +|defaultRecoveryMessage|Optional property for providing a default message for all added actions with `message` property for the Recovery action group.| |requiresAppContext|Define if alert type is enabled for create and edit in the alerting management UI.| IMPORTANT: The current UI supports a single action group only. diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.test.tsx index f64a3c011187a..cf6a1350d389d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.test.tsx @@ -11,10 +11,12 @@ import { ReactWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { ruleTypeRegistryMock } from '../../rule_type_registry.mock'; +import { ActionForm } from '../action_connector_form'; import { ValidationResult, Rule, RuleType, + RuleTypeModel, ConnectorValidationResult, GenericValidationResult, } from '../../../types'; @@ -30,6 +32,12 @@ jest.mock('../../hooks/use_load_rule_types', () => ({ useLoadRuleTypes: jest.fn(), })); jest.mock('../../../common/lib/kibana'); +jest.mock('../../lib/capabilities', () => ({ + hasAllPrivilege: jest.fn(() => true), + hasSaveRulesCapability: jest.fn(() => true), + hasShowActionsCapability: jest.fn(() => true), + hasExecuteActionsCapability: jest.fn(() => true), +})); describe('rule_form', () => { const ruleType = { @@ -91,6 +99,144 @@ describe('rule_form', () => { const useKibanaMock = useKibana as jest.Mocked; + describe('rule recovery message', () => { + let wrapper: ReactWrapper; + const defaultRecoveryMessage = 'Sample default recovery message'; + + async function setup(enforceMinimum = false, schedule = '1m') { + const mocks = coreMock.createSetup(); + const { useLoadRuleTypes } = jest.requireMock('../../hooks/use_load_rule_types'); + const myRuleModel = { + id: 'my-rule-type', + description: 'Sample rule type model', + iconClass: 'sampleIconClass', + defaultActionMessage: 'Sample default action message', + defaultRecoveryMessage, + requiresAppContext: false, + }; + const myRule = { + id: 'my-rule-type', + name: 'Test', + actionGroups: [ + { + id: 'testActionGroup', + name: 'Test Action Group', + }, + { + id: 'recovered', + name: 'Recovered', + }, + ], + defaultActionGroupId: 'testActionGroup', + minimumLicenseRequired: 'basic', + recoveryActionGroup: RecoveredActionGroup, + producer: ALERTS_FEATURE_ID, + authorizedConsumers: { + [ALERTS_FEATURE_ID]: { read: true, all: true }, + test: { read: true, all: true }, + }, + actionVariables: { + params: [], + state: [], + }, + enabledInLicense: true, + }; + const disabledByLicenseRule = { + id: 'disabled-by-license', + name: 'Test', + actionGroups: [ + { + id: 'testActionGroup', + name: 'Test Action Group', + }, + ], + defaultActionGroupId: 'testActionGroup', + minimumLicenseRequired: 'gold', + recoveryActionGroup: RecoveredActionGroup, + producer: ALERTS_FEATURE_ID, + authorizedConsumers: { + [ALERTS_FEATURE_ID]: { read: true, all: true }, + test: { read: true, all: true }, + }, + actionVariables: { + params: [], + state: [], + }, + enabledInLicense: false, + }; + useLoadRuleTypes.mockReturnValue({ + ruleTypes: [myRule, disabledByLicenseRule], + ruleTypeIndex: new Map([ + [myRule.id, myRule], + [disabledByLicenseRule.id, disabledByLicenseRule], + ]), + }); + const [ + { + application: { capabilities }, + }, + ] = await mocks.getStartServices(); + // eslint-disable-next-line react-hooks/rules-of-hooks + useKibanaMock().services.application.capabilities = { + ...capabilities, + rules: { + show: true, + save: true, + delete: true, + }, + }; + ruleTypeRegistry.list.mockReturnValue([ + ruleType, + ruleTypeNonEditable, + disabledByLicenseRuleType, + ]); + ruleTypeRegistry.has.mockReturnValue(true); + ruleTypeRegistry.get.mockReturnValue(myRuleModel as RuleTypeModel); + actionTypeRegistry.list.mockReturnValue([actionType]); + actionTypeRegistry.has.mockReturnValue(true); + actionTypeRegistry.get.mockReturnValue(actionType); + const initialRule = { + name: 'test', + params: {}, + consumer: ALERTS_FEATURE_ID, + schedule: { + interval: schedule, + }, + actions: [], + tags: [], + muteAll: false, + enabled: false, + mutedInstanceIds: [], + ruleTypeId: 'my-rule-type', + } as unknown as Rule; + + wrapper = mountWithIntl( + {}} + errors={{ name: [], 'schedule.interval': [], ruleTypeId: [], actionConnectors: [] }} + operation="create" + actionTypeRegistry={actionTypeRegistry} + ruleTypeRegistry={ruleTypeRegistry} + /> + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + } + + it('renders defaultRecoveryMessage for recovery action when specified', async () => { + await setup(); + const actionForm = wrapper.find(ActionForm); + expect(actionForm.first().prop('actionGroups')?.[1]).toEqual( + expect.objectContaining({ defaultActionMessage: defaultRecoveryMessage }) + ); + }); + }); + describe('rule_form create rule', () => { let wrapper: ReactWrapper; @@ -282,7 +428,6 @@ describe('rule_form', () => { async function setup() { const { useLoadRuleTypes } = jest.requireMock('../../hooks/use_load_rule_types'); - useLoadRuleTypes.mockReturnValue({ ruleTypes: [ { @@ -384,7 +529,7 @@ describe('rule_form', () => { rule={initialRule} config={{ minimumScheduleInterval: { value: '1m', enforce: false } }} dispatch={() => {}} - errors={{ name: [], 'schedule.interval': [], ruleTypeId: [] }} + errors={{ name: [], 'schedule.interval': [], ruleTypeId: [], actionConnectors: [] }} operation="create" actionTypeRegistry={actionTypeRegistry} ruleTypeRegistry={ruleTypeRegistry} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx index 80923617da064..d765fa225ef0c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx @@ -560,7 +560,8 @@ export const RuleForm = ({ omitMessageVariables: selectedRuleType.doesSetRecoveryContext ? 'keepContext' : 'all', - defaultActionMessage: recoveredActionGroupMessage, + defaultActionMessage: + ruleTypeModel?.defaultRecoveryMessage || recoveredActionGroupMessage, } : { ...actionGroup, defaultActionMessage: ruleTypeModel?.defaultActionMessage } )} diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index cb68aab1899be..7038e2eaa0d01 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -304,6 +304,7 @@ export interface RuleTypeModel { | React.LazyExoticComponent>>; requiresAppContext: boolean; defaultActionMessage?: string; + defaultRecoveryMessage?: string; } export interface IErrorObject { From 29d6e3e975c41534a713a18117ec35e1a382e8d6 Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Wed, 27 Apr 2022 15:49:47 +0300 Subject: [PATCH 21/78] [Cloud Posture] New resource tab display (#130955) --- .../__snapshots__/resource_tab.test.ts.snap | 100 +++++++++++++++ .../{ => findings_flyout}/findings_flyout.tsx | 115 ++++++------------ .../findings_flyout/resource_tab.test.ts | 24 ++++ .../findings/findings_flyout/resource_tab.tsx | 102 ++++++++++++++++ .../public/pages/findings/findings_table.tsx | 2 +- .../public/pages/findings/translations.ts | 2 +- .../public/pages/findings/types.ts | 2 + 7 files changed, 270 insertions(+), 77 deletions(-) create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/__snapshots__/resource_tab.test.ts.snap rename x-pack/plugins/cloud_security_posture/public/pages/findings/{ => findings_flyout}/findings_flyout.tsx (77%) create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.test.ts create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.tsx diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/__snapshots__/resource_tab.test.ts.snap b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/__snapshots__/resource_tab.test.ts.snap new file mode 100644 index 0000000000000..ffa5d91e2d677 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/__snapshots__/resource_tab.test.ts.snap @@ -0,0 +1,100 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`prepareDescriptionList create description lists accordingly 1`] = ` +Array [ + Object { + "description": + string + , + "title": + + a + + , + }, + Object { + "description": + 123 + , + "title": + + b + + , + }, + Object { + "description": "undefined", + "title": + + c + + , + }, + Object { + "description": + null + , + "title": + + d + + , + }, + Object { + "description": + true + , + "title": + + e + + , + }, + Object { + "description": + false + , + "title": + + f + + , + }, + Object { + "description": + [ + { + "a": "another string", + "b": 123 + } +] + , + "title": + + g + + , + }, +] +`; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx similarity index 77% rename from x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout.tsx rename to x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx index 65493bd493342..2902d8fd7736c 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx @@ -25,11 +25,12 @@ import { type PropsOf, } from '@elastic/eui'; import { assertNever } from '@kbn/std'; -import type { CspFinding } from './types'; -import { CspEvaluationBadge } from '../../components/csp_evaluation_badge'; -import * as TEXT from './translations'; -import cisLogoIcon from '../../assets/icons/cis_logo.svg'; -import k8sLogoIcon from '../../assets/icons/k8s_logo.svg'; +import type { CspFinding } from '../types'; +import { CspEvaluationBadge } from '../../../components/csp_evaluation_badge'; +import * as TEXT from '../translations'; +import cisLogoIcon from '../../../assets/icons/cis_logo.svg'; +import k8sLogoIcon from '../../../assets/icons/k8s_logo.svg'; +import { ResourceTab } from './resource_tab'; const tabs = ['remediation', 'resource', 'general'] as const; @@ -51,8 +52,42 @@ interface FindingFlyoutProps { findings: CspFinding; } +const Cards = ({ data }: { data: Card[] }) => ( + + {data.map((card) => ( + + + ({ title: v[0], description: v[1] }))} + style={{ flexFlow: 'column' }} + descriptionProps={{ + style: { width: '100%' }, + }} + /> + + + ))} + +); + +const FindingsTab = ({ tab, findings }: { findings: CspFinding; tab: FindingsTab }) => { + switch (tab) { + case 'remediation': + return ; + case 'resource': + return ; + case 'general': + return ; + default: + assertNever(tab); + } +}; + export const FindingsRuleFlyout = ({ onClose, findings }: FindingFlyoutProps) => { const [tab, setTab] = useState('remediation'); + return ( @@ -89,76 +124,6 @@ export const FindingsRuleFlyout = ({ onClose, findings }: FindingFlyoutProps) => ); }; -const Cards = ({ data }: { data: Card[] }) => ( - - {data.map((card) => ( - - - ({ title: v[0], description: v[1] }))} - style={{ flexFlow: 'column' }} - descriptionProps={{ - style: { width: '100%' }, - }} - /> - - - ))} - -); - -const FindingsTab = ({ tab, findings }: { findings: CspFinding; tab: FindingsTab }) => { - switch (tab) { - case 'remediation': - return ; - case 'resource': - return ; - case 'general': - return ; - default: - assertNever(tab); - } -}; - -const getResourceCards = ({ resource, host }: CspFinding): Card[] => [ - { - title: TEXT.RESOURCE, - listItems: [ - [TEXT.FILENAME, {resource.filename}], - [TEXT.MODE, resource.mode], - [TEXT.PATH, {resource.path}], - [TEXT.TYPE, resource.type], - [TEXT.UID, resource.uid], - ], - }, - { - title: TEXT.HOST, - listItems: [ - [TEXT.ARCHITECTURE, host.architecture], - [TEXT.CONTAINERIZED, host.containerized ? 'true' : 'false'], - [TEXT.HOSTNAME, host.hostname], - [TEXT.ID, {host.id}], - [TEXT.IP, {host.ip.join(', ')}], - [TEXT.MAC, {host.mac.join(', ')}], - [TEXT.NAME, host.name], - ], - }, - { - title: TEXT.OS, - listItems: [ - [TEXT.CODENAME, host.os.codename], - [TEXT.FAMILY, host.os.family], - [TEXT.KERNEL, host.os.kernel], - [TEXT.NAME, host.os.name], - [TEXT.PLATFORM, host.os.platform], - [TEXT.TYPE, host.os.type], - [TEXT.VERSION, host.os.version], - ], - }, -]; - const getGeneralCards = ({ rule }: CspFinding): Card[] => [ { title: TEXT.RULE, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.test.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.test.ts new file mode 100644 index 0000000000000..94886e8fb254d --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.test.ts @@ -0,0 +1,24 @@ +/* + * 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 { prepareDescriptionList } from './resource_tab'; + +const mockData = { + a: 'string', + b: 123, + c: undefined, + d: null, + e: true, + f: false, + g: [{ a: 'another string', b: 123 }], +}; + +describe('prepareDescriptionList', () => { + it('create description lists accordingly', () => { + const descriptionList = prepareDescriptionList(mockData); + expect(descriptionList).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.tsx new file mode 100644 index 0000000000000..7919b836a3a73 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.tsx @@ -0,0 +1,102 @@ +/* + * 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 { + EuiAccordion, + EuiCode, + EuiCodeBlock, + EuiDescriptionList, + EuiPanel, + EuiSpacer, + EuiText, + useEuiTheme, +} from '@elastic/eui'; +import React, { useMemo } from 'react'; +import { getFlattenedObject } from '@kbn/std'; +import { CspFinding } from '../types'; +import * as TEXT from '../translations'; + +const getDescriptionDisplay = (value: unknown) => { + if (value === undefined) return 'undefined'; + if (typeof value === 'boolean' || value === null) { + return {JSON.stringify(value)}; + } + + if (typeof value === 'object') { + return ( + + {JSON.stringify(value, null, 2)} + + ); + } + + return {value as string}; +}; + +export const prepareDescriptionList = (data: Record) => + Object.entries(getFlattenedObject(data)) + .sort((a, b) => a[0].localeCompare(b[0])) + .map(([key, value]) => ({ + title: ( + + {key} + + ), + description: getDescriptionDisplay(value), + })); + +export const ResourceTab = ({ data }: { data: CspFinding }) => { + const { euiTheme } = useEuiTheme(); + + const accordions = useMemo( + () => [ + { + title: TEXT.RESOURCE, + id: 'resourceAccordion', + listItems: prepareDescriptionList(data.resource), + }, + { + title: TEXT.HOST, + id: 'hostAccordion', + listItems: prepareDescriptionList(data.host), + }, + ], + [data.host, data.resource] + ); + + return ( + <> + {accordions.map((accordion) => ( + + + + {accordion.title} + + } + arrowDisplay="right" + initialIsOpen + > + + + + + + ))} + + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_table.tsx index 189cf42994382..aa3ea9586cac9 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_table.tsx @@ -23,7 +23,7 @@ import * as TEXT from './translations'; import type { CspFinding } from './types'; import { CspEvaluationBadge } from '../../components/csp_evaluation_badge'; import type { FindingsGroupByNoneQuery, CspFindingsResult } from './use_findings'; -import { FindingsRuleFlyout } from './findings_flyout'; +import { FindingsRuleFlyout } from './findings_flyout/findings_flyout'; interface BaseFindingsTableProps extends FindingsGroupByNoneQuery { setQuery(query: Partial): void; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/translations.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/translations.ts index 5ffa474e7a63d..72048b060a224 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/translations.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/translations.ts @@ -144,7 +144,7 @@ export const ID = i18n.translate('xpack.csp.findings.idLabel', { }); export const HOST = i18n.translate('xpack.csp.findings.hostLabel', { - defaultMessage: 'HOST', + defaultMessage: 'Host', }); export const ARCHITECTURE = i18n.translate('xpack.csp.findings.architectureLabel', { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts index 79d8f3507c896..405ea9f231375 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts @@ -69,6 +69,7 @@ interface CspFindingResource { mode: string; path: string; type: string; + [other_keys: string]: unknown; } interface CspFindingHost { @@ -88,6 +89,7 @@ interface CspFindingHost { family: string; name: string; }; + [other_keys: string]: unknown; } interface CspFindingAgent { From 37686c3f038768b11b8739259c9ccc1020d88bfa Mon Sep 17 00:00:00 2001 From: Kristof C Date: Wed, 27 Apr 2022 08:32:50 -0500 Subject: [PATCH 22/78] [Security Solution][Detection & Response] Hosts and Users by severity (#128335) * First draft at possible implementation. tests need to be added, and imports,comments, logs cleaned up * Further tweaks to alerts counters * Add tests for alerts_counters hook * Working on vulnerable hosts * Add useVulnerableHostsCounters hook along with tests * add Vulnerable users and tests * Move files to components folder and wire up to detections overview page * Add translations * add querytoggle and navigation to both tables * fix bug for toggleQuery * update button navigation * remove alerts by status, as Angela built instead * Working on changing test files * test files for host and user hooks complete * Components complete * bug fixes from PR * failing tests * Undo bad edit to useRuleAlerts test * Fix show inspect on hover, and use HostDetailsLink component * missed in last commit * more fixes from PR review Co-authored-by: Kristof-Pierre Cummings Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../alerts_by_status/alerts_by_status.tsx | 4 +- .../host_alerts_table.test.tsx | 103 +++++++++ .../host_alerts_table/host_alerts_table.tsx | 150 +++++++++++++ .../host_alerts_table/index.ts | 8 + .../host_alerts_table/mock_data.ts | 125 +++++++++++ .../use_host_alerts_items.test.ts | 127 +++++++++++ .../use_host_alerts_items.ts | 203 ++++++++++++++++++ .../components/detection_response/index.ts | 9 + .../rule_alerts_table.test.tsx | 6 +- .../rule_alerts_table/rule_alerts_table.tsx | 2 + .../use_rule_alerts_items.test.ts | 19 +- .../detection_response/translations.ts | 53 ++++- .../user_alerts_table/index.ts | 8 + .../user_alerts_table/mock_data.ts | 125 +++++++++++ .../use_user_alerts_items.test.ts | 126 +++++++++++ .../use_user_alerts_items.ts | 202 +++++++++++++++++ .../user_alerts_table.test.tsx | 103 +++++++++ .../user_alerts_table/user_alerts_table.tsx | 150 +++++++++++++ .../overview/pages/detection_response.tsx | 12 +- 19 files changed, 1510 insertions(+), 25 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.test.tsx create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/index.ts create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/mock_data.ts create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.test.ts create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.ts create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/index.ts create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/index.ts create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/mock_data.ts create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.test.ts create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.ts create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.test.tsx create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx index 963b12a35dbf0..16c06f4c9d0f8 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx @@ -20,7 +20,7 @@ import { LegendItem } from '../../../../common/components/charts/legend_item'; import { useAlertsByStatus } from './use_alerts_by_status'; import { ALERTS, - ALERTS_TITLE, + ALERTS_TEXT, STATUS_ACKNOWLEDGED, STATUS_CLOSED, STATUS_CRITICAL_LABEL, @@ -130,7 +130,7 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { )} } inspectMultiple toggleStatus={toggleStatus} diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.test.tsx new file mode 100644 index 0000000000000..4294f23f796bd --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.test.tsx @@ -0,0 +1,103 @@ +/* + * 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 React from 'react'; + +import { render } from '@testing-library/react'; + +import { TestProviders } from '../../../../common/mock'; +import { parsedVulnerableHostsAlertsResult } from './mock_data'; +import { UseHostAlertsItems } from './use_host_alerts_items'; +import { HostAlertsTable } from './host_alerts_table'; + +const mockGetAppUrl = jest.fn(); +jest.mock('../../../../common/lib/kibana/hooks', () => { + const original = jest.requireActual('../../../../common/lib/kibana/hooks'); + return { + ...original, + useNavigation: () => ({ + getAppUrl: mockGetAppUrl, + }), + }; +}); + +type UseHostAlertsItemsReturn = ReturnType; +const defaultUseHostAlertsItemsReturn: UseHostAlertsItemsReturn = { + items: [], + isLoading: false, + updatedAt: Date.now(), +}; +const mockUseHostAlertsItems = jest.fn(() => defaultUseHostAlertsItemsReturn); +const mockUseHostAlertsItemsReturn = (overrides: Partial) => { + mockUseHostAlertsItems.mockReturnValueOnce({ ...defaultUseHostAlertsItemsReturn, ...overrides }); +}; + +jest.mock('./use_host_alerts_items', () => ({ + useHostAlertsItems: () => mockUseHostAlertsItems(), +})); + +const renderComponent = () => + render( + + + + ); + +describe('HostAlertsTable', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should render empty table', () => { + const { getByText, getByTestId } = renderComponent(); + + expect(getByTestId('severityHostAlertsPanel')).toBeInTheDocument(); + expect(getByText('No alerts to display')).toBeInTheDocument(); + expect(getByTestId('severityHostAlertsButton')).toBeInTheDocument(); + }); + + it('should render a loading table', () => { + mockUseHostAlertsItemsReturn({ isLoading: true }); + const { getByText, getByTestId } = renderComponent(); + + expect(getByText('Updating...')).toBeInTheDocument(); + expect(getByTestId('severityHostAlertsButton')).toBeInTheDocument(); + expect(getByTestId('severityHostAlertsTable')).toHaveClass('euiBasicTable-loading'); + }); + + it('should render the updated at subtitle', () => { + mockUseHostAlertsItemsReturn({ isLoading: false }); + const { getByText } = renderComponent(); + + expect(getByText('Updated now')).toBeInTheDocument(); + }); + + it('should render the table columns', () => { + mockUseHostAlertsItemsReturn({ items: parsedVulnerableHostsAlertsResult }); + const { getAllByRole } = renderComponent(); + + const columnHeaders = getAllByRole('columnheader'); + expect(columnHeaders.at(0)).toHaveTextContent('Host name'); + expect(columnHeaders.at(1)).toHaveTextContent('Alerts'); + expect(columnHeaders.at(2)).toHaveTextContent('Critical'); + expect(columnHeaders.at(3)).toHaveTextContent('High'); + expect(columnHeaders.at(4)).toHaveTextContent('Medium'); + expect(columnHeaders.at(5)).toHaveTextContent('Low'); + }); + + it('should render the table items', () => { + mockUseHostAlertsItemsReturn({ items: [parsedVulnerableHostsAlertsResult[0]] }); + const { getByTestId } = renderComponent(); + + expect(getByTestId('hostSeverityAlertsTable-hostName')).toHaveTextContent('Host-342m5gl1g2'); + expect(getByTestId('hostSeverityAlertsTable-totalAlerts')).toHaveTextContent('100'); + expect(getByTestId('hostSeverityAlertsTable-critical')).toHaveTextContent('5'); + expect(getByTestId('hostSeverityAlertsTable-high')).toHaveTextContent('50'); + expect(getByTestId('hostSeverityAlertsTable-medium')).toHaveTextContent('5'); + expect(getByTestId('hostSeverityAlertsTable-low')).toHaveTextContent('40'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx new file mode 100644 index 0000000000000..4fe5bf7785bc7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx @@ -0,0 +1,150 @@ +/* + * 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 React, { useCallback, useMemo } from 'react'; + +import { + EuiBasicTable, + EuiBasicTableColumn, + EuiButton, + EuiEmptyPrompt, + EuiHealth, + EuiPanel, + EuiSpacer, +} from '@elastic/eui'; + +import { SecurityPageName } from '../../../../app/types'; +import { FormattedCount } from '../../../../common/components/formatted_number'; +import { HeaderSection } from '../../../../common/components/header_section'; +import { HoverVisibilityContainer } from '../../../../common/components/hover_visibility_container'; +import { BUTTON_CLASS as INPECT_BUTTON_CLASS } from '../../../../common/components/inspect'; +import { HostDetailsLink } from '../../../../common/components/links'; +import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { useNavigation, NavigateTo, GetAppUrl } from '../../../../common/lib/kibana'; +import * as i18n from '../translations'; +import { LastUpdatedAt, SEVERITY_COLOR } from '../util'; +import { HostAlertsItem, useHostAlertsItems } from './use_host_alerts_items'; + +type GetTableColumns = (params: { + getAppUrl: GetAppUrl; + navigateTo: NavigateTo; +}) => Array>; + +interface HostAlertsTableProps { + signalIndexName: string | null; +} + +const DETECTION_RESPONSE_HOST_SEVERITY_QUERY_ID = 'vulnerableHostsBySeverityQuery'; + +export const HostAlertsTable = React.memo(({ signalIndexName }: HostAlertsTableProps) => { + const { getAppUrl, navigateTo } = useNavigation(); + const { toggleStatus, setToggleStatus } = useQueryToggle( + DETECTION_RESPONSE_HOST_SEVERITY_QUERY_ID + ); + + const { items, isLoading, updatedAt } = useHostAlertsItems({ + skip: !toggleStatus, + queryId: DETECTION_RESPONSE_HOST_SEVERITY_QUERY_ID, + signalIndexName, + }); + + const navigateToHosts = useCallback(() => { + navigateTo({ deepLinkId: SecurityPageName.hosts }); + }, [navigateTo]); + + const columns = useMemo( + () => getTableColumns({ getAppUrl, navigateTo }), + [getAppUrl, navigateTo] + ); + + return ( + + + } + titleSize="s" + toggleQuery={setToggleStatus} + toggleStatus={toggleStatus} + /> + {toggleStatus && ( + <> + {i18n.NO_ALERTS_FOUND}} titleSize="xs" /> + } + /> + + + {i18n.VIEW_ALL_HOST_ALERTS} + + + )} + + + ); +}); + +HostAlertsTable.displayName = 'HostAlertsTable'; + +const getTableColumns: GetTableColumns = ({ getAppUrl, navigateTo }) => [ + { + field: 'hostName', + name: i18n.HOST_ALERTS_HOSTNAME_COLUMN, + truncateText: true, + textOnly: true, + 'data-test-subj': 'hostSeverityAlertsTable-hostName', + render: (hostName: string) => , + }, + { + field: 'totalAlerts', + name: i18n.ALERTS_TEXT, + 'data-test-subj': 'hostSeverityAlertsTable-totalAlerts', + render: (totalAlerts: number) => , + }, + { + field: 'critical', + name: i18n.STATUS_CRITICAL_LABEL, + render: (count: number) => ( + + + + ), + }, + { + field: 'high', + name: i18n.STATUS_HIGH_LABEL, + render: (count: number) => ( + + + + ), + }, + { + field: 'medium', + name: i18n.STATUS_MEDIUM_LABEL, + render: (count: number) => ( + + + + ), + }, + { + field: 'low', + name: i18n.STATUS_LOW_LABEL, + render: (count: number) => ( + + + + ), + }, +]; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/index.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/index.ts new file mode 100644 index 0000000000000..62423931bbc4f --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { HostAlertsTable } from './host_alerts_table'; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/mock_data.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/mock_data.ts new file mode 100644 index 0000000000000..d46d1b5401a9e --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/mock_data.ts @@ -0,0 +1,125 @@ +/* + * 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 { buildVulnerableHostAggregationQuery } from './use_host_alerts_items'; + +export const mockVulnerableHostsBySeverityResult = { + aggregations: { + hostsBySeverity: { + buckets: [ + { + key: 'Host-342m5gl1g2', + doc_count: 100, + high: { + doc_count: 50, + }, + critical: { + doc_count: 5, + }, + low: { + doc_count: 40, + }, + medium: { + doc_count: 5, + }, + }, + { + key: 'Host-vns3hyykhu', + doc_count: 104, + high: { + doc_count: 100, + }, + critical: { + doc_count: 4, + }, + low: { + doc_count: 0, + }, + medium: { + doc_count: 0, + }, + }, + { + key: 'Host-awafztonav', + doc_count: 108, + high: { + doc_count: 50, + }, + critical: { + doc_count: 4, + }, + low: { + doc_count: 50, + }, + medium: { + doc_count: 4, + }, + }, + { + key: 'Host-56k7zf5kne', + doc_count: 128, + high: { + doc_count: 6, + }, + critical: { + doc_count: 1, + }, + low: { + doc_count: 59, + }, + medium: { + doc_count: 62, + }, + }, + ], + }, + }, +}; + +export const parsedVulnerableHostsAlertsResult = [ + { + hostName: 'Host-342m5gl1g2', + totalAlerts: 100, + critical: 5, + high: 50, + low: 40, + medium: 5, + }, + { + hostName: 'Host-vns3hyykhu', + totalAlerts: 104, + critical: 4, + high: 100, + low: 0, + medium: 0, + }, + { + hostName: 'Host-awafztonav', + totalAlerts: 108, + critical: 4, + high: 50, + low: 50, + medium: 4, + }, + { + hostName: 'Host-56k7zf5kne', + totalAlerts: 128, + critical: 1, + high: 6, + low: 59, + medium: 62, + }, +]; + +export const mockQuery = () => ({ + query: buildVulnerableHostAggregationQuery({ + from: '2020-07-07T08:20:18.966Z', + to: '2020-07-08T08:20:18.966Z', + }), + indexName: 'signal-alerts', + skip: false, +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.test.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.test.ts new file mode 100644 index 0000000000000..42568a390f686 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.test.ts @@ -0,0 +1,127 @@ +/* + * 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 { renderHook } from '@testing-library/react-hooks'; + +import { + mockQuery, + mockVulnerableHostsBySeverityResult, + parsedVulnerableHostsAlertsResult, +} from './mock_data'; +import { useHostAlertsItems } from './use_host_alerts_items'; + +import type { UseHostAlertsItems, UseHostAlertsItemsProps } from './use_host_alerts_items'; + +const signalIndexName = 'signal-alerts'; + +const dateNow = new Date('2022-04-15T12:00:00.000Z').valueOf(); +const mockDateNow = jest.fn().mockReturnValue(dateNow); +Date.now = jest.fn(() => mockDateNow()) as unknown as DateConstructor['now']; + +const defaultUseQueryAlertsReturn = { + loading: false, + data: null, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, +}; + +const mockUseQueryAlerts = jest.fn().mockReturnValue(defaultUseQueryAlertsReturn); +jest.mock('../../../../detections/containers/detection_engine/alerts/use_query', () => { + return { + useQueryAlerts: (...props: unknown[]) => mockUseQueryAlerts(...props), + }; +}); + +const from = '2020-07-07T08:20:18.966Z'; +const to = '2020-07-08T08:20:18.966Z'; + +const mockUseGlobalTime = jest + .fn() + .mockReturnValue({ from, to, setQuery: jest.fn(), deleteQuery: jest.fn() }); +jest.mock('../../../../common/containers/use_global_time', () => { + return { + useGlobalTime: (...props: unknown[]) => mockUseGlobalTime(...props), + }; +}); + +const renderUseHostAlertsItems = (overrides: Partial = {}) => + renderHook>(() => + useHostAlertsItems({ + skip: false, + signalIndexName, + queryId: 'testing', + ...overrides, + }) + ); + +describe('useVulnerableHostsCounters', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockDateNow.mockReturnValue(dateNow); + mockUseQueryAlerts.mockReturnValue(defaultUseQueryAlertsReturn); + }); + + it('should return default values', () => { + const { result } = renderUseHostAlertsItems(); + + expect(result.current).toEqual({ + items: [], + isLoading: false, + updatedAt: dateNow, + }); + + expect(mockUseQueryAlerts).toBeCalledWith(mockQuery()); + }); + + it('should return parsed items', () => { + mockUseQueryAlerts.mockReturnValue({ + ...defaultUseQueryAlertsReturn, + data: mockVulnerableHostsBySeverityResult, + }); + + const { result } = renderUseHostAlertsItems(); + + expect(result.current).toEqual({ + items: parsedVulnerableHostsAlertsResult, + isLoading: false, + updatedAt: dateNow, + }); + }); + + it('should return new updatedAt', () => { + const newDateNow = new Date('2022-04-08T14:00:00.000Z').valueOf(); + mockDateNow.mockReturnValue(newDateNow); // setUpdatedAt call + mockDateNow.mockReturnValueOnce(dateNow); // initialization call + + mockUseQueryAlerts.mockReturnValue({ + ...defaultUseQueryAlertsReturn, + data: mockVulnerableHostsBySeverityResult, + }); + + const { result } = renderUseHostAlertsItems(); + expect(mockDateNow).toHaveBeenCalled(); + expect(result.current).toEqual({ + items: parsedVulnerableHostsAlertsResult, + isLoading: false, + updatedAt: newDateNow, + }); + }); + + it('should skip the query', () => { + const { result } = renderUseHostAlertsItems({ skip: true }); + + expect(mockUseQueryAlerts).toBeCalledWith({ ...mockQuery(), skip: true }); + + expect(result.current).toEqual({ + items: [], + isLoading: false, + updatedAt: dateNow, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.ts new file mode 100644 index 0000000000000..ebd7af1b49cc2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.ts @@ -0,0 +1,203 @@ +/* + * 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 { useCallback, useEffect, useState } from 'react'; + +import { useQueryInspector } from '../../../../common/components/page/manage_query'; +import { useGlobalTime } from '../../../../common/containers/use_global_time'; +import { GenericBuckets } from '../../../../../common/search_strategy'; +import { useQueryAlerts } from '../../../../detections/containers/detection_engine/alerts/use_query'; + +const HOSTS_BY_SEVERITY_AGG = 'hostsBySeverity'; + +interface TimeRange { + from: string; + to: string; +} + +export interface UseHostAlertsItemsProps { + skip: boolean; + queryId: string; + signalIndexName: string | null; +} +export interface HostAlertsItem { + hostName: string; + totalAlerts: number; + low: number; + medium: number; + high: number; + critical: number; +} + +export type UseHostAlertsItems = (props: UseHostAlertsItemsProps) => { + items: HostAlertsItem[]; + isLoading: boolean; + updatedAt: number; +}; + +export const useHostAlertsItems: UseHostAlertsItems = ({ skip, queryId, signalIndexName }) => { + const [updatedAt, setUpdatedAt] = useState(Date.now()); + const [items, setItems] = useState([]); + + const { to, from, setQuery: setGlobalQuery, deleteQuery } = useGlobalTime(); + + const { + data, + request, + response, + setQuery, + loading, + refetch: refetchQuery, + } = useQueryAlerts<{}, AlertCountersBySeverityAndHostAggregation>({ + query: buildVulnerableHostAggregationQuery({ from, to }), + indexName: signalIndexName, + skip, + }); + + useEffect(() => { + setQuery(buildVulnerableHostAggregationQuery({ from, to })); + }, [setQuery, from, to]); + + useEffect(() => { + if (data == null || !data.aggregations) { + setItems([]); + } else { + setItems(parseHostsData(data.aggregations)); + } + setUpdatedAt(Date.now()); + }, [data]); + + const refetch = useCallback(() => { + if (!skip && refetchQuery) { + refetchQuery(); + } + }, [skip, refetchQuery]); + + useQueryInspector({ + deleteQuery, + inspect: { + dsl: [request], + response: [response], + }, + refetch, + setQuery: setGlobalQuery, + queryId, + loading, + }); + return { items, isLoading: loading, updatedAt }; +}; + +export const buildVulnerableHostAggregationQuery = ({ from, to }: TimeRange) => ({ + query: { + bool: { + filter: [ + { + term: { + 'kibana.alert.workflow_status': 'open', + }, + }, + { + range: { + '@timestamp': { + gte: from, + lte: to, + }, + }, + }, + ], + }, + }, + size: 0, + aggs: { + [HOSTS_BY_SEVERITY_AGG]: { + terms: { + field: 'host.name', + order: [ + { + 'critical.doc_count': 'desc', + }, + { + 'high.doc_count': 'desc', + }, + { + 'medium.doc_count': 'desc', + }, + { + 'low.doc_count': 'desc', + }, + ], + size: 4, + }, + aggs: { + critical: { + filter: { + term: { + 'kibana.alert.severity': 'critical', + }, + }, + }, + high: { + filter: { + term: { + 'kibana.alert.severity': 'high', + }, + }, + }, + medium: { + filter: { + term: { + 'kibana.alert.severity': 'medium', + }, + }, + }, + low: { + filter: { + term: { + 'kibana.alert.severity': 'low', + }, + }, + }, + }, + }, + }, +}); + +interface SeverityContainer { + doc_count: number; +} +interface AlertBySeverityBucketData extends GenericBuckets { + low: SeverityContainer; + medium: SeverityContainer; + high: SeverityContainer; + critical: SeverityContainer; +} + +interface AlertCountersBySeverityAndHostAggregation { + [HOSTS_BY_SEVERITY_AGG]: { + buckets: AlertBySeverityBucketData[]; + }; +} + +function parseHostsData( + rawAggregation: AlertCountersBySeverityAndHostAggregation +): HostAlertsItem[] { + const buckets = rawAggregation?.[HOSTS_BY_SEVERITY_AGG].buckets ?? []; + + return buckets.reduce((accumalatedAlertsByHost, currentHost) => { + return [ + ...accumalatedAlertsByHost, + { + hostName: currentHost.key, + totalAlerts: currentHost.doc_count, + low: currentHost.low.doc_count, + medium: currentHost.medium.doc_count, + high: currentHost.high.doc_count, + critical: currentHost.critical.doc_count, + }, + ]; + }, []); +} diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/index.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/index.ts new file mode 100644 index 0000000000000..8ab93de825305 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { HostAlertsTable } from './host_alerts_table'; +export { UserAlertsTable } from './user_alerts_table'; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.test.tsx index f41e05a2c04dc..dafd1ca965a3f 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.test.tsx @@ -5,13 +5,15 @@ * 2.0. */ +import moment from 'moment'; import React from 'react'; + import { render } from '@testing-library/react'; + +import { SecurityPageName } from '../../../../../common/constants'; import { TestProviders } from '../../../../common/mock'; import { RuleAlertsTable, RuleAlertsTableProps } from './rule_alerts_table'; import { RuleAlertsItem, UseRuleAlertsItems } from './use_rule_alerts_items'; -import moment from 'moment'; -import { SecurityPageName } from '../../../../../common/constants'; const mockGetAppUrl = jest.fn(); jest.mock('../../../../common/lib/kibana/hooks', () => { diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx index a5ddfe25dd985..550e04753e886 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx @@ -30,6 +30,7 @@ import { SecurityPageName } from '../../../../../common/constants'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; import { HoverVisibilityContainer } from '../../../../common/components/hover_visibility_container'; import { BUTTON_CLASS as INPECT_BUTTON_CLASS } from '../../../../common/components/inspect'; +import { FormattedCount } from '../../../../common/components/formatted_number'; export interface RuleAlertsTableProps { signalIndexName: string | null; @@ -78,6 +79,7 @@ export const getTableColumns: GetTableColumns = ({ getAppUrl, navigateTo }) => [ field: 'alert_count', name: i18n.RULE_ALERTS_COLUMN_ALERT_COUNT, 'data-test-subj': 'severityRuleAlertsTable-alertCount', + render: (alertCount: number) => , }, { field: 'severity', diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/use_rule_alerts_items.test.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/use_rule_alerts_items.test.ts index 85d4c8e5b6a93..264c1cf404219 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/use_rule_alerts_items.test.ts +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/use_rule_alerts_items.test.ts @@ -6,7 +6,7 @@ */ import { renderHook } from '@testing-library/react-hooks'; -import { TestProviders } from '../../../../common/mock'; + import { from, mockSeverityRuleAlertsResponse, @@ -50,16 +50,12 @@ jest.mock('../../../../common/containers/use_global_time', () => { // helper function to render the hook const renderUseRuleAlertsItems = (props: Partial = {}) => - renderHook>( - () => - useRuleAlertsItems({ - queryId: 'test', - signalIndexName: 'signal-alerts', - ...props, - }), - { - wrapper: TestProviders, - } + renderHook>(() => + useRuleAlertsItems({ + queryId: 'test', + signalIndexName: 'signal-alerts', + ...props, + }) ); describe('useRuleAlertsItems', () => { @@ -103,7 +99,6 @@ describe('useRuleAlertsItems', () => { const newDateNow = new Date('2022-04-08T14:00:00.000Z').valueOf(); mockDateNow.mockReturnValue(newDateNow); // setUpdatedAt call mockDateNow.mockReturnValueOnce(dateNow); // initialization call - mockUseQueryAlerts.mockReturnValue({ ...defaultUseQueryAlertsReturn, data: mockSeverityRuleAlertsResponse, diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts index c2c5b412f1a9d..2afc356e15c8b 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts @@ -60,24 +60,37 @@ export const ALERTS = (totalAlerts: number) => values: { totalAlerts }, defaultMessage: 'total {totalAlerts, plural, =1 {alert} other {alerts}}', }); -export const ALERTS_TITLE = i18n.translate( - 'xpack.securitySolution.detectionResponse.alertsByStatus.title', - { - defaultMessage: 'Alerts', - } -); +export const ALERTS_TEXT = i18n.translate('xpack.securitySolution.detectionResponse.alerts', { + defaultMessage: 'Alerts', +}); export const UPDATING = i18n.translate('xpack.securitySolution.detectionResponse.updating', { defaultMessage: 'Updating...', }); export const UPDATED = i18n.translate('xpack.securitySolution.detectionResponse.updated', { defaultMessage: 'Updated', }); + export const RULE_ALERTS_SECTION_TITLE = i18n.translate( 'xpack.securitySolution.detectionResponse.ruleAlertsSectionTitle', { defaultMessage: 'Open alerts by rule', } ); + +export const HOST_ALERTS_SECTION_TITLE = i18n.translate( + 'xpack.securitySolution.detectionResponse.hostAlertsSectionTitle', + { + defaultMessage: 'Vulnerable hosts by severity', + } +); + +export const USER_ALERTS_SECTION_TITLE = i18n.translate( + 'xpack.securitySolution.detectionResponse.userAlertsSectionTitle', + { + defaultMessage: 'Vulnerable users by severity', + } +); + export const NO_ALERTS_FOUND = i18n.translate( 'xpack.securitySolution.detectionResponse.noRuleAlerts', { @@ -127,3 +140,31 @@ export const OPEN_ALL_ALERTS_BUTTON = i18n.translate( defaultMessage: 'View all open alerts', } ); + +export const VIEW_ALL_USER_ALERTS = i18n.translate( + 'xpack.securitySolution.detectionResponse.viewAllUserAlerts', + { + defaultMessage: 'View all other user alerts', + } +); + +export const VIEW_ALL_HOST_ALERTS = i18n.translate( + 'xpack.securitySolution.detectionResponse.viewAllHostAlerts', + { + defaultMessage: 'View all other host alerts', + } +); + +export const HOST_ALERTS_HOSTNAME_COLUMN = i18n.translate( + 'xpack.securitySolution.detectionResponse.hostAlertsHostName', + { + defaultMessage: 'Host name', + } +); + +export const USER_ALERTS_USERNAME_COLUMN = i18n.translate( + 'xpack.securitySolution.detectionResponse.userAlertsUserName', + { + defaultMessage: 'User name', + } +); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/index.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/index.ts new file mode 100644 index 0000000000000..b14cd15e55f18 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { UserAlertsTable } from './user_alerts_table'; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/mock_data.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/mock_data.ts new file mode 100644 index 0000000000000..e10bf1b05f032 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/mock_data.ts @@ -0,0 +1,125 @@ +/* + * 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 { buildVulnerableUserAggregationQuery } from './use_user_alerts_items'; + +export const mockVulnerableUsersBySeverityResult = { + aggregations: { + usersBySeverity: { + buckets: [ + { + key: 'crffn20qcs', + doc_count: 4, + high: { + doc_count: 1, + }, + critical: { + doc_count: 4, + }, + low: { + doc_count: 1, + }, + medium: { + doc_count: 1, + }, + }, + { + key: 'd058hziijl', + doc_count: 4, + high: { + doc_count: 11, + }, + critical: { + doc_count: 1, + }, + low: { + doc_count: 1, + }, + medium: { + doc_count: 1, + }, + }, + { + key: 'nenha4bdhv', + doc_count: 4, + high: { + doc_count: 1, + }, + critical: { + doc_count: 1, + }, + low: { + doc_count: 3, + }, + medium: { + doc_count: 3, + }, + }, + { + key: 'u68nq414uw', + doc_count: 2, + high: { + doc_count: 1, + }, + critical: { + doc_count: 0, + }, + low: { + doc_count: 10, + }, + medium: { + doc_count: 0, + }, + }, + ], + }, + }, +}; + +export const parsedVulnerableUserAlertsResult = [ + { + totalAlerts: 4, + critical: 4, + high: 1, + userName: 'crffn20qcs', + low: 1, + medium: 1, + }, + { + totalAlerts: 4, + critical: 1, + high: 11, + userName: 'd058hziijl', + low: 1, + medium: 1, + }, + { + totalAlerts: 4, + critical: 1, + high: 1, + userName: 'nenha4bdhv', + low: 3, + medium: 3, + }, + { + totalAlerts: 2, + critical: 0, + high: 1, + userName: 'u68nq414uw', + low: 10, + medium: 0, + }, +]; + +export const mockQuery = () => ({ + query: buildVulnerableUserAggregationQuery({ + from: '2020-07-07T08:20:18.966Z', + to: '2020-07-08T08:20:18.966Z', + }), + indexName: 'signal-alerts', + skip: false, +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.test.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.test.ts new file mode 100644 index 0000000000000..22ade5d3341c7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.test.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 { renderHook } from '@testing-library/react-hooks'; + +import { + mockQuery, + mockVulnerableUsersBySeverityResult, + parsedVulnerableUserAlertsResult, +} from './mock_data'; +import { useUserAlertsItems } from './use_user_alerts_items'; + +import type { UseUserAlertsItems, UseUserAlertsItemsProps } from './use_user_alerts_items'; + +const signalIndexName = 'signal-alerts'; + +const dateNow = new Date('2022-04-08T12:00:00.000Z').valueOf(); +const mockDateNow = jest.fn().mockReturnValue(dateNow); +Date.now = jest.fn(() => mockDateNow()) as unknown as DateConstructor['now']; + +const defaultUseQueryAlertsReturn = { + loading: false, + data: null, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, +}; +const mockUseQueryAlerts = jest.fn().mockReturnValue(defaultUseQueryAlertsReturn); +jest.mock('../../../../detections/containers/detection_engine/alerts/use_query', () => { + return { + useQueryAlerts: (...props: unknown[]) => mockUseQueryAlerts(...props), + }; +}); + +const from = '2020-07-07T08:20:18.966Z'; +const to = '2020-07-08T08:20:18.966Z'; + +const mockUseGlobalTime = jest + .fn() + .mockReturnValue({ from, to, setQuery: jest.fn(), deleteQuery: jest.fn() }); +jest.mock('../../../../common/containers/use_global_time', () => { + return { + useGlobalTime: (...props: unknown[]) => mockUseGlobalTime(...props), + }; +}); + +const renderUseUserAlertsItems = (overrides: Partial = {}) => + renderHook>(() => + useUserAlertsItems({ + skip: false, + signalIndexName, + queryId: 'testing', + ...overrides, + }) + ); + +describe('useUserAlertsItems', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockDateNow.mockReturnValue(dateNow); + mockUseQueryAlerts.mockReturnValue(defaultUseQueryAlertsReturn); + }); + + it('should return default values', () => { + const { result } = renderUseUserAlertsItems(); + + expect(result.current).toEqual({ + items: [], + isLoading: false, + updatedAt: dateNow, + }); + + expect(mockUseQueryAlerts).toBeCalledWith(mockQuery()); + }); + + it('should return parsed items', () => { + mockUseQueryAlerts.mockReturnValue({ + ...defaultUseQueryAlertsReturn, + data: mockVulnerableUsersBySeverityResult, + }); + + const { result } = renderUseUserAlertsItems(); + + expect(result.current).toEqual({ + items: parsedVulnerableUserAlertsResult, + isLoading: false, + updatedAt: dateNow, + }); + }); + + it('should return new updatedAt', () => { + const newDateNow = new Date('2022-04-08T14:00:00.000Z').valueOf(); + mockDateNow.mockReturnValue(newDateNow); // setUpdatedAt call + mockDateNow.mockReturnValueOnce(dateNow); // initialization call + + mockUseQueryAlerts.mockReturnValue({ + ...defaultUseQueryAlertsReturn, + data: mockVulnerableUsersBySeverityResult, + }); + + const { result } = renderUseUserAlertsItems(); + expect(mockDateNow).toHaveBeenCalled(); + expect(result.current).toEqual({ + items: parsedVulnerableUserAlertsResult, + isLoading: false, + updatedAt: newDateNow, + }); + }); + + it('should skip the query', () => { + const { result } = renderUseUserAlertsItems({ skip: true }); + + expect(mockUseQueryAlerts).toBeCalledWith({ ...mockQuery(), skip: true }); + + expect(result.current).toEqual({ + items: [], + isLoading: false, + updatedAt: dateNow, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.ts new file mode 100644 index 0000000000000..e86ee2f2aef95 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.ts @@ -0,0 +1,202 @@ +/* + * 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 { useCallback, useEffect, useState } from 'react'; + +import { useQueryInspector } from '../../../../common/components/page/manage_query'; +import { useGlobalTime } from '../../../../common/containers/use_global_time'; +import { GenericBuckets } from '../../../../../common/search_strategy'; +import { useQueryAlerts } from '../../../../detections/containers/detection_engine/alerts/use_query'; + +const USERS_BY_SEVERITY_AGG = 'usersBySeverity'; + +interface TimeRange { + from: string; + to: string; +} + +export interface UseUserAlertsItemsProps { + skip: boolean; + queryId: string; + signalIndexName: string | null; +} + +export interface UserAlertsItem { + userName: string; + totalAlerts: number; + low: number; + medium: number; + high: number; + critical: number; +} + +export type UseUserAlertsItems = (props: UseUserAlertsItemsProps) => { + items: UserAlertsItem[]; + isLoading: boolean; + updatedAt: number; +}; + +export const useUserAlertsItems: UseUserAlertsItems = ({ skip, queryId, signalIndexName }) => { + const [updatedAt, setUpdatedAt] = useState(Date.now()); + const [items, setItems] = useState([]); + + const { to, from, setQuery: setGlobalQuery, deleteQuery } = useGlobalTime(); + + const { + setQuery, + data, + loading, + request, + response, + refetch: refetchQuery, + } = useQueryAlerts<{}, AlertCountersBySeverityAggregation>({ + query: buildVulnerableUserAggregationQuery({ from, to }), + indexName: signalIndexName, + skip, + }); + + useEffect(() => { + setQuery(buildVulnerableUserAggregationQuery({ from, to })); + }, [setQuery, from, to]); + + useEffect(() => { + if (data == null || !data.aggregations) { + setItems([]); + } else { + setItems(parseUsersData(data.aggregations)); + } + setUpdatedAt(Date.now()); + }, [data]); + + const refetch = useCallback(() => { + if (!skip && refetchQuery) { + refetchQuery(); + } + }, [skip, refetchQuery]); + + useQueryInspector({ + deleteQuery, + inspect: { + dsl: [request], + response: [response], + }, + refetch, + setQuery: setGlobalQuery, + queryId, + loading, + }); + return { items, isLoading: loading, updatedAt }; +}; + +export const buildVulnerableUserAggregationQuery = ({ from, to }: TimeRange) => ({ + query: { + bool: { + filter: [ + { + term: { + 'kibana.alert.workflow_status': 'open', + }, + }, + { + range: { + '@timestamp': { + gte: from, + lte: to, + }, + }, + }, + ], + }, + }, + size: 0, + aggs: { + [USERS_BY_SEVERITY_AGG]: { + terms: { + field: 'user.name', + order: [ + { + 'critical.doc_count': 'desc', + }, + { + 'high.doc_count': 'desc', + }, + { + 'medium.doc_count': 'desc', + }, + { + 'low.doc_count': 'desc', + }, + ], + size: 4, + }, + aggs: { + critical: { + filter: { + term: { + 'kibana.alert.severity': 'critical', + }, + }, + }, + high: { + filter: { + term: { + 'kibana.alert.severity': 'high', + }, + }, + }, + medium: { + filter: { + term: { + 'kibana.alert.severity': 'medium', + }, + }, + }, + low: { + filter: { + term: { + 'kibana.alert.severity': 'low', + }, + }, + }, + }, + }, + }, +}); + +interface SeverityContainer { + doc_count: number; +} +interface AlertBySeverityBucketData extends GenericBuckets { + low: SeverityContainer; + medium: SeverityContainer; + high: SeverityContainer; + critical: SeverityContainer; +} + +interface AlertCountersBySeverityAggregation { + [USERS_BY_SEVERITY_AGG]: { + buckets: AlertBySeverityBucketData[]; + }; +} + +function parseUsersData(rawAggregation: AlertCountersBySeverityAggregation): UserAlertsItem[] { + const buckets = rawAggregation?.[USERS_BY_SEVERITY_AGG].buckets ?? []; + + return buckets.reduce((accumalatedAlertsByUser, currentUser) => { + return [ + ...accumalatedAlertsByUser, + { + userName: currentUser.key, + totalAlerts: currentUser.doc_count, + low: currentUser.low.doc_count, + medium: currentUser.medium.doc_count, + high: currentUser.high.doc_count, + critical: currentUser.critical.doc_count, + }, + ]; + }, []); +} diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.test.tsx new file mode 100644 index 0000000000000..74c266243dc56 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.test.tsx @@ -0,0 +1,103 @@ +/* + * 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 React from 'react'; + +import { render } from '@testing-library/react'; + +import { TestProviders } from '../../../../common/mock'; +import { parsedVulnerableUserAlertsResult } from './mock_data'; +import { UseUserAlertsItems } from './use_user_alerts_items'; +import { UserAlertsTable } from './user_alerts_table'; + +const mockGetAppUrl = jest.fn(); +jest.mock('../../../../common/lib/kibana/hooks', () => { + const original = jest.requireActual('../../../../common/lib/kibana/hooks'); + return { + ...original, + useNavigation: () => ({ + getAppUrl: mockGetAppUrl, + }), + }; +}); + +type UseUserAlertsItemsReturn = ReturnType; +const defaultUseUserAlertsItemsReturn: UseUserAlertsItemsReturn = { + items: [], + isLoading: false, + updatedAt: Date.now(), +}; +const mockUseUserAlertsItems = jest.fn(() => defaultUseUserAlertsItemsReturn); +const mockUseUserAlertsItemsReturn = (overrides: Partial) => { + mockUseUserAlertsItems.mockReturnValueOnce({ ...defaultUseUserAlertsItemsReturn, ...overrides }); +}; + +jest.mock('./use_user_alerts_items', () => ({ + useUserAlertsItems: () => mockUseUserAlertsItems(), +})); + +const renderComponent = () => + render( + + + + ); + +describe('UserAlertsTable', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should render empty table', () => { + const { getByText, getByTestId } = renderComponent(); + + expect(getByTestId('severityUserAlertsPanel')).toBeInTheDocument(); + expect(getByText('No alerts to display')).toBeInTheDocument(); + expect(getByTestId('severityUserAlertsButton')).toBeInTheDocument(); + }); + + it('should render a loading table', () => { + mockUseUserAlertsItemsReturn({ isLoading: true }); + const { getByText, getByTestId } = renderComponent(); + + expect(getByText('Updating...')).toBeInTheDocument(); + expect(getByTestId('severityUserAlertsButton')).toBeInTheDocument(); + expect(getByTestId('severityUserAlertsTable')).toHaveClass('euiBasicTable-loading'); + }); + + it('should render the updated at subtitle', () => { + mockUseUserAlertsItemsReturn({ isLoading: false }); + const { getByText } = renderComponent(); + + expect(getByText('Updated now')).toBeInTheDocument(); + }); + + it('should render the table columns', () => { + mockUseUserAlertsItemsReturn({ items: parsedVulnerableUserAlertsResult }); + const { getAllByRole } = renderComponent(); + + const columnHeaders = getAllByRole('columnheader'); + expect(columnHeaders.at(0)).toHaveTextContent('User name'); + expect(columnHeaders.at(1)).toHaveTextContent('Alerts'); + expect(columnHeaders.at(2)).toHaveTextContent('Critical'); + expect(columnHeaders.at(3)).toHaveTextContent('High'); + expect(columnHeaders.at(4)).toHaveTextContent('Medium'); + expect(columnHeaders.at(5)).toHaveTextContent('Low'); + }); + + it('should render the table items', () => { + mockUseUserAlertsItemsReturn({ items: [parsedVulnerableUserAlertsResult[0]] }); + const { getByTestId } = renderComponent(); + + expect(getByTestId('userSeverityAlertsTable-userName')).toHaveTextContent('crffn20qcs'); + expect(getByTestId('userSeverityAlertsTable-totalAlerts')).toHaveTextContent('4'); + expect(getByTestId('userSeverityAlertsTable-critical')).toHaveTextContent('4'); + expect(getByTestId('userSeverityAlertsTable-high')).toHaveTextContent('1'); + expect(getByTestId('userSeverityAlertsTable-medium')).toHaveTextContent('1'); + expect(getByTestId('userSeverityAlertsTable-low')).toHaveTextContent('1'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx new file mode 100644 index 0000000000000..e54ef27c0db34 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx @@ -0,0 +1,150 @@ +/* + * 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 React, { useCallback, useMemo } from 'react'; + +import { + EuiBasicTable, + EuiBasicTableColumn, + EuiButton, + EuiEmptyPrompt, + EuiHealth, + EuiPanel, + EuiSpacer, +} from '@elastic/eui'; + +import { SecurityPageName } from '../../../../app/types'; +import { FormattedCount } from '../../../../common/components/formatted_number'; +import { HeaderSection } from '../../../../common/components/header_section'; +import { HoverVisibilityContainer } from '../../../../common/components/hover_visibility_container'; +import { BUTTON_CLASS as INPECT_BUTTON_CLASS } from '../../../../common/components/inspect'; +import { UserDetailsLink } from '../../../../common/components/links'; +import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { useNavigation, NavigateTo, GetAppUrl } from '../../../../common/lib/kibana'; +import * as i18n from '../translations'; +import { LastUpdatedAt, SEVERITY_COLOR } from '../util'; +import { UserAlertsItem, useUserAlertsItems } from './use_user_alerts_items'; + +export interface UserAlertsTableProps { + signalIndexName: string | null; +} + +type GetTableColumns = (params: { + getAppUrl: GetAppUrl; + navigateTo: NavigateTo; +}) => Array>; + +const DETECTION_RESPONSE_USER_SEVERITY_QUERY_ID = 'vulnerableUsersBySeverityQuery'; + +export const UserAlertsTable = React.memo(({ signalIndexName }: UserAlertsTableProps) => { + const { getAppUrl, navigateTo } = useNavigation(); + const { toggleStatus, setToggleStatus } = useQueryToggle( + DETECTION_RESPONSE_USER_SEVERITY_QUERY_ID + ); + const { items, isLoading, updatedAt } = useUserAlertsItems({ + skip: !toggleStatus, + queryId: DETECTION_RESPONSE_USER_SEVERITY_QUERY_ID, + signalIndexName, + }); + + const navigateToAlerts = useCallback(() => { + navigateTo({ deepLinkId: SecurityPageName.users }); + }, [navigateTo]); + + const columns = useMemo( + () => getTableColumns({ getAppUrl, navigateTo }), + [getAppUrl, navigateTo] + ); + + return ( + + + } + /> + + {toggleStatus && ( + <> + {i18n.NO_ALERTS_FOUND}} titleSize="xs" /> + } + /> + + + {i18n.VIEW_ALL_USER_ALERTS} + + + )} + + + ); +}); + +UserAlertsTable.displayName = 'UserAlertsTable'; + +const getTableColumns: GetTableColumns = ({ getAppUrl, navigateTo }) => [ + { + field: 'userName', + name: i18n.USER_ALERTS_USERNAME_COLUMN, + truncateText: true, + textOnly: true, + 'data-test-subj': 'userSeverityAlertsTable-userName', + render: (userName: string) => , + }, + { + field: 'totalAlerts', + name: i18n.ALERTS_TEXT, + 'data-test-subj': 'userSeverityAlertsTable-totalAlerts', + render: (totalAlerts: number) => , + }, + { + field: 'critical', + name: i18n.STATUS_CRITICAL_LABEL, + render: (count: number) => ( + + + + ), + }, + { + field: 'high', + name: i18n.STATUS_HIGH_LABEL, + render: (count: number) => ( + + + + ), + }, + { + field: 'medium', + name: i18n.STATUS_MEDIUM_LABEL, + render: (count: number) => ( + + {count} + + ), + }, + { + field: 'low', + name: i18n.STATUS_LOW_LABEL, + render: (count: number) => ( + + + + ), + }, +]; diff --git a/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx b/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx index 2e0030bafa00e..6561c2886ae38 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx @@ -14,8 +14,10 @@ import { useSourcererDataView } from '../../common/containers/sourcerer'; import { useUserInfo } from '../../detections/components/user_info'; import { HeaderPage } from '../../common/components/header_page'; import { useKibana, useGetUserCasesPermissions } from '../../common/lib/kibana'; -import { RuleAlertsTable } from '../components/detection_response/rule_alerts_table'; +import { HostAlertsTable, UserAlertsTable } from '../components/detection_response'; + import { LandingPageComponent } from '../../common/components/landing_page'; +import { RuleAlertsTable } from '../components/detection_response/rule_alerts_table'; import * as i18n from './translations'; import { EmptyPage } from '../../common/components/empty_page'; import { AlertsByStatus } from '../components/detection_response/alerts_by_status'; @@ -88,8 +90,12 @@ const DetectionResponseComponent = () => { {canReadAlerts && ( - {'[hosts table]'} - {'[users table]'} + + + + + + )} From 30c8f586d2df9813d1f6facce35a56fd0c18610f Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Wed, 27 Apr 2022 14:57:20 +0100 Subject: [PATCH 23/78] [Fleet] Add experimental features (feature flags) config to fleet plugin (#130253) * Add experimental features support * fix imports * PR feedback --- .../fleet/common/experimental_features.ts | 52 +++++++++++++++++++ x-pack/plugins/fleet/common/index.ts | 1 + x-pack/plugins/fleet/common/types/index.ts | 1 + x-pack/plugins/fleet/public/plugin.ts | 12 ++++- .../public/services/experimental_features.ts | 28 ++++++++++ x-pack/plugins/fleet/public/services/index.ts | 1 + x-pack/plugins/fleet/server/index.ts | 24 +++++++++ x-pack/plugins/fleet/server/mocks/index.ts | 3 +- x-pack/plugins/fleet/server/plugin.ts | 8 ++- .../fleet/server/services/app_context.ts | 11 +++- 10 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 x-pack/plugins/fleet/common/experimental_features.ts create mode 100644 x-pack/plugins/fleet/public/services/experimental_features.ts diff --git a/x-pack/plugins/fleet/common/experimental_features.ts b/x-pack/plugins/fleet/common/experimental_features.ts new file mode 100644 index 0000000000000..3398de60c0786 --- /dev/null +++ b/x-pack/plugins/fleet/common/experimental_features.ts @@ -0,0 +1,52 @@ +/* + * 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. + */ + +export type ExperimentalFeatures = typeof allowedExperimentalValues; + +/** + * A list of allowed values that can be used in `xpack.fleet.enableExperimental`. + * This object is then used to validate and parse the value entered. + */ +export const allowedExperimentalValues = Object.freeze({ + addIntegrationStepsPage: false, +}); + +type ExperimentalConfigKeys = Array; +type Mutable = { -readonly [P in keyof T]: T[P] }; + +const FleetInvalidExperimentalValue = class extends Error {}; +const allowedKeys = Object.keys(allowedExperimentalValues) as Readonly; + +/** + * Parses the string value used in `xpack.fleet.enableExperimental` kibana configuration, + * which should be a string of values delimited by a comma (`,`) + * + * @param configValue + * @throws FleetInvalidExperimentalValue + */ +export const parseExperimentalConfigValue = (configValue: string[]): ExperimentalFeatures => { + const enabledFeatures: Mutable> = {}; + + for (const value of configValue) { + if (!isValidExperimentalValue(value)) { + throw new FleetInvalidExperimentalValue(`[${value}] is not a supported experimental feature`); + } + + enabledFeatures[value as keyof ExperimentalFeatures] = true; + } + + return { + ...allowedExperimentalValues, + ...enabledFeatures, + }; +}; + +export const isValidExperimentalValue = (value: string) => { + return allowedKeys.includes(value as keyof ExperimentalFeatures); +}; + +export const getExperimentalAllowedValues = (): string[] => [...allowedKeys]; diff --git a/x-pack/plugins/fleet/common/index.ts b/x-pack/plugins/fleet/common/index.ts index 46a8e2d01fc96..60bbcafc48842 100644 --- a/x-pack/plugins/fleet/common/index.ts +++ b/x-pack/plugins/fleet/common/index.ts @@ -11,6 +11,7 @@ export * from './constants'; export * from './services'; export * from './types'; +export * from './experimental_features'; export type { FleetAuthz } from './authz'; export { calculateAuthz } from './authz'; export { createFleetAuthzMock } from './mocks'; diff --git a/x-pack/plugins/fleet/common/types/index.ts b/x-pack/plugins/fleet/common/types/index.ts index d04855480dd62..ce1cb5a294f80 100644 --- a/x-pack/plugins/fleet/common/types/index.ts +++ b/x-pack/plugins/fleet/common/types/index.ts @@ -32,6 +32,7 @@ export interface FleetConfigType { packages?: PreconfiguredPackage[]; outputs?: PreconfiguredOutput[]; agentIdVerificationEnabled?: boolean; + enableExperimental?: string[]; developer?: { disableRegistryVersionCheck?: boolean; allowAgentUpgradeSourceUri?: boolean; diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index c94f2ce139ab6..34fa9d2cbcfc8 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -49,8 +49,14 @@ import { setupRouteService, appRoutesService, calculateAuthz, + parseExperimentalConfigValue, +} from '../common'; +import type { + CheckPermissionsResponse, + PostFleetSetupResponse, + FleetAuthz, + ExperimentalFeatures, } from '../common'; -import type { CheckPermissionsResponse, PostFleetSetupResponse, FleetAuthz } from '../common'; import type { FleetConfigType } from '../common/types'; @@ -60,6 +66,7 @@ import { setHttpClient } from './hooks/use_request'; import { createPackageSearchProvider } from './search_provider'; import { TutorialDirectoryHeaderLink, TutorialModuleNotice } from './components/home_integration'; import { createExtensionRegistrationCallback } from './services/ui_extensions'; +import { ExperimentalFeaturesService } from './services/experimental_features'; import type { UIExtensionRegistrationCallback, UIExtensionsStorage } from './types'; import { LazyCustomLogsAssetsExtension } from './lazy_custom_logs_assets_extension'; @@ -113,10 +120,12 @@ export class FleetPlugin implements Plugin(); + this.experimentalFeatures = parseExperimentalConfigValue(this.config.enableExperimental || []); this.kibanaVersion = initializerContext.env.packageInfo.version; } @@ -251,6 +260,7 @@ export class FleetPlugin implements Plugin core.http.get(appRoutesService.getCheckPermissionsPath()) diff --git a/x-pack/plugins/fleet/public/services/experimental_features.ts b/x-pack/plugins/fleet/public/services/experimental_features.ts new file mode 100644 index 0000000000000..afe5d0d65e3fd --- /dev/null +++ b/x-pack/plugins/fleet/public/services/experimental_features.ts @@ -0,0 +1,28 @@ +/* + * 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 type { ExperimentalFeatures } from '../../common/experimental_features'; + +export class ExperimentalFeaturesService { + private static experimentalFeatures?: ExperimentalFeatures; + + public static init(experimentalFeatures: ExperimentalFeatures) { + this.experimentalFeatures = experimentalFeatures; + } + + public static get(): ExperimentalFeatures { + if (!this.experimentalFeatures) { + this.throwUninitializedError(); + } + + return this.experimentalFeatures; + } + + private static throwUninitializedError(): never { + throw new Error('Experimental features services not initialized'); + } +} diff --git a/x-pack/plugins/fleet/public/services/index.ts b/x-pack/plugins/fleet/public/services/index.ts index 9de918d01d707..2c1bedeaef82c 100644 --- a/x-pack/plugins/fleet/public/services/index.ts +++ b/x-pack/plugins/fleet/public/services/index.ts @@ -12,6 +12,7 @@ export type { PackagePolicyConfigValidationResults, PackagePolicyInputValidationResults, } from '../../common'; +export { ExperimentalFeaturesService } from './experimental_features'; export { AgentStatusKueryHelper, agentPolicyRouteService, diff --git a/x-pack/plugins/fleet/server/index.ts b/x-pack/plugins/fleet/server/index.ts index 3228c03959322..97ff8ade05859 100644 --- a/x-pack/plugins/fleet/server/index.ts +++ b/x-pack/plugins/fleet/server/index.ts @@ -11,6 +11,9 @@ import { schema } from '@kbn/config-schema'; import type { TypeOf } from '@kbn/config-schema'; import type { PluginConfigDescriptor, PluginInitializerContext } from '@kbn/core/server'; +import { getExperimentalAllowedValues, isValidExperimentalValue } from '../common'; +const allowedExperimentalValues = getExperimentalAllowedValues(); + import { PreconfiguredPackagesSchema, PreconfiguredAgentPoliciesSchema, @@ -139,6 +142,27 @@ export const config: PluginConfigDescriptor = { allowAgentUpgradeSourceUri: schema.boolean({ defaultValue: false }), bundledPackageLocation: schema.string({ defaultValue: DEFAULT_BUNDLED_PACKAGE_LOCATION }), }), + /** + * For internal use. A list of string values (comma delimited) that will enable experimental + * type of functionality that is not yet released. + * + * @example + * xpack.fleet.enableExperimental: + * - feature1 + * - feature2 + */ + enableExperimental: schema.arrayOf(schema.string(), { + defaultValue: () => [], + validate(list) { + for (const key of list) { + if (!isValidExperimentalValue(key)) { + return `[${key}] is not allowed. Allowed values are: ${allowedExperimentalValues.join( + ', ' + )}`; + } + } + }, + }), }), }; diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 01fe5b48f9e31..f3423fb3f1c0f 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -22,7 +22,7 @@ import type { PackagePolicyServiceInterface } from '../services/package_policy'; import type { AgentPolicyServiceInterface } from '../services'; import type { FleetAppContext } from '../plugin'; import { createMockTelemetryEventsSender } from '../telemetry/__mocks__'; -import type { FleetConfigType } from '../../common'; +import type { FleetConfigType, ExperimentalFeatures } from '../../common'; import { createFleetAuthzMock } from '../../common'; import { agentServiceMock } from '../services/agents/agent_service.mock'; import type { FleetRequestHandlerContext } from '../types'; @@ -61,6 +61,7 @@ export const createAppContextStartContractMock = ( securitySetup: securityMock.createSetup(), securityStart: securityMock.createStart(), logger: loggingSystemMock.create().get(), + experimentalFeatures: {} as ExperimentalFeatures, isProductionMode: true, configInitialValue: { agents: { enabled: true, elasticsearch: {} }, diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index c9e3b9388087d..ea38068a7613c 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -42,8 +42,8 @@ import type { CloudSetup } from '@kbn/cloud-plugin/server'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/server'; -import type { FleetConfigType, FleetAuthz } from '../common'; -import { INTEGRATIONS_PLUGIN_ID } from '../common'; +import type { FleetConfigType, FleetAuthz, ExperimentalFeatures } from '../common'; +import { INTEGRATIONS_PLUGIN_ID, parseExperimentalConfigValue } from '../common'; import { PLUGIN_ID, @@ -119,6 +119,7 @@ export interface FleetAppContext { securityStart: SecurityPluginStart; config$?: Observable; configInitialValue: FleetConfigType; + experimentalFeatures: ExperimentalFeatures; savedObjects: SavedObjectsServiceStart; isProductionMode: PluginInitializerContext['env']['mode']['prod']; kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; @@ -392,6 +393,9 @@ export class FleetPlugin securityStart: plugins.security, configInitialValue: this.configInitialValue, config$: this.config$, + experimentalFeatures: parseExperimentalConfigValue( + this.configInitialValue.enableExperimental || [] + ), savedObjects: core.savedObjects, isProductionMode: this.isProductionMode, kibanaVersion: this.kibanaVersion, diff --git a/x-pack/plugins/fleet/server/services/app_context.ts b/x-pack/plugins/fleet/server/services/app_context.ts index 71409f8d115c6..088e4d7c8b3f0 100644 --- a/x-pack/plugins/fleet/server/services/app_context.ts +++ b/x-pack/plugins/fleet/server/services/app_context.ts @@ -26,7 +26,7 @@ import type { SecurityPluginStart, SecurityPluginSetup } from '@kbn/security-plu import type { CloudSetup } from '@kbn/cloud-plugin/server'; -import type { FleetConfigType } from '../../common'; +import type { FleetConfigType, ExperimentalFeatures } from '../../common'; import type { ExternalCallback, ExternalCallbacksStorage, @@ -43,6 +43,7 @@ class AppContextService { private encryptedSavedObjectsSetup: EncryptedSavedObjectsPluginSetup | undefined; private data: DataPluginStart | undefined; private esClient: ElasticsearchClient | undefined; + private experimentalFeatures?: ExperimentalFeatures; private securitySetup: SecurityPluginSetup | undefined; private securityStart: SecurityPluginStart | undefined; private config$?: Observable; @@ -65,6 +66,7 @@ class AppContextService { this.securitySetup = appContext.securitySetup; this.securityStart = appContext.securityStart; this.savedObjects = appContext.savedObjects; + this.experimentalFeatures = appContext.experimentalFeatures; this.isProductionMode = appContext.isProductionMode; this.cloud = appContext.cloud; this.logger = appContext.logger; @@ -126,6 +128,13 @@ class AppContextService { return this.config$; } + public getExperimentalFeatures() { + if (!this.experimentalFeatures) { + throw new Error('experimentalFeatures not set.'); + } + return this.experimentalFeatures; + } + public getSavedObjects() { if (!this.savedObjects) { throw new Error('Saved objects start service not set.'); From 2079f747add5da961d28265baf000abfebd44c8d Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 27 Apr 2022 17:00:52 +0300 Subject: [PATCH 24/78] [Cases] Add duration (#130448) * Migrate duration * Add mapping * Change duration when change status * Add integration tests * Fix tests * Improve tests * PR feedback * Fix test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/cases/common/api/cases/case.ts | 1 + x-pack/plugins/cases/common/ui/types.ts | 29 +--- .../all_cases/all_cases_list.test.tsx | 1 + .../plugins/cases/public/containers/mock.ts | 3 + .../cases/server/client/cases/update.ts | 33 ++--- .../cases/server/client/cases/utils.test.ts | 118 +++++++++++++++- .../cases/server/client/cases/utils.ts | 62 +++++++++ .../plugins/cases/server/client/utils.test.ts | 1 + .../plugins/cases/server/common/utils.test.ts | 8 ++ x-pack/plugins/cases/server/common/utils.ts | 1 + .../api/__fixtures__/mock_saved_objects.ts | 4 + .../cases/server/saved_object_types/cases.ts | 3 + .../migrations/cases.test.ts | 127 ++++++++++++++++- .../saved_object_types/migrations/cases.ts | 31 ++++- .../cases/server/services/cases/index.test.ts | 2 + .../cases/server/services/test_utils.ts | 1 + .../cases_api_integration/common/lib/mock.ts | 1 + .../cases_api_integration/common/lib/utils.ts | 21 +++ .../tests/common/cases/migrations.ts | 45 ++++++ .../tests/common/cases/patch_cases.ts | 67 ++++++++- .../cases/8.2.0/cases_duration.json | 129 ++++++++++++++++++ .../cases/8.2.0/cases_various_dates.json | 12 +- 22 files changed, 642 insertions(+), 58 deletions(-) create mode 100644 x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_duration.json diff --git a/x-pack/plugins/cases/common/api/cases/case.ts b/x-pack/plugins/cases/common/api/cases/case.ts index 251c02d931fcd..3f42e5b5c875c 100644 --- a/x-pack/plugins/cases/common/api/cases/case.ts +++ b/x-pack/plugins/cases/common/api/cases/case.ts @@ -95,6 +95,7 @@ export const CaseFullExternalServiceRt = rt.union([CaseExternalServiceBasicRt, r export const CaseAttributesRt = rt.intersection([ CaseBasicRt, rt.type({ + duration: rt.union([rt.number, rt.null]), closed_at: rt.union([rt.string, rt.null]), closed_by: rt.union([UserRT, rt.null]), created_at: rt.string, diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index c0c68f5d9964a..280cbfbfb2cd5 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -7,8 +7,6 @@ import type { SavedObjectsResolveResponse } from '@kbn/core/public'; import { - CaseAttributes, - CaseConnector, CasePatchRequest, CaseStatuses, User, @@ -17,6 +15,7 @@ import { CaseUserActionResponse, CaseMetricsResponse, CommentResponse, + CaseResponse, CommentResponseAlertsType, } from '../api'; import { SnakeToCamelCase } from '../types'; @@ -61,31 +60,7 @@ export type Comment = SnakeToCamelCase; export type AlertComment = SnakeToCamelCase; export type CaseUserActions = SnakeToCamelCase; export type CaseExternalService = SnakeToCamelCase; - -interface BasicCase { - id: string; - owner: string; - closedAt: string | null; - closedBy: ElasticUser | null; - comments: Comment[]; - createdAt: string; - createdBy: ElasticUser; - status: CaseStatuses; - title: string; - totalAlerts: number; - totalComment: number; - updatedAt: string | null; - updatedBy: ElasticUser | null; - version: string; -} - -export interface Case extends BasicCase { - connector: CaseConnector; - description: string; - externalService: CaseExternalService | null; - settings: CaseAttributes['settings']; - tags: string[]; -} +export type Case = Omit, 'comments'> & { comments: Comment[] }; export interface ResolvedCase { case: Case; diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx index ef6d789e97a36..87d53aae14e28 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx @@ -560,6 +560,7 @@ describe('AllCasesListGeneric', () => { username: 'lknope', }, description: 'Security banana Issue', + duration: null, externalService: { connectorId: '123', connectorName: 'connector name', diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index 69931629a77cb..97572b535fc01 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -154,6 +154,7 @@ export const basicCase: Case = { fields: null, }, description: 'Security banana Issue', + duration: null, externalService: null, status: CaseStatuses.open, tags, @@ -245,6 +246,7 @@ export const mockCase: Case = { type: ConnectorTypes.none, fields: null, }, + duration: null, description: 'Security banana Issue', externalService: null, status: CaseStatuses.open, @@ -383,6 +385,7 @@ export const basicCaseSnake: CaseResponse = { connector: { id: 'none', name: 'My Connector', type: ConnectorTypes.none, fields: null }, created_at: basicCreatedAt, created_by: elasticUserSnake, + duration: null, external_service: null, updated_at: basicUpdatedAt, updated_by: elasticUserSnake, diff --git a/x-pack/plugins/cases/server/client/cases/update.ts b/x-pack/plugins/cases/server/client/cases/update.ts index ae53cb03c28a7..6569dcd3f52b2 100644 --- a/x-pack/plugins/cases/server/client/cases/update.ts +++ b/x-pack/plugins/cases/server/client/cases/update.ts @@ -50,6 +50,7 @@ import { import { UpdateAlertRequest } from '../alerts/types'; import { CasesClientArgs } from '..'; import { Operations, OwnerEntity } from '../../authorization'; +import { getClosedInfoForUpdate, getDurationForUpdate } from './utils'; /** * Throws an error if any of the requests attempt to update the owner of a case. @@ -311,37 +312,29 @@ export const update = async ( throwIfUpdateOwner(updateCases); throwIfTitleIsInvalid(updateCases); - // eslint-disable-next-line @typescript-eslint/naming-convention - const { username, full_name, email } = user; const updatedDt = new Date().toISOString(); const updatedCases = await caseService.patchCases({ cases: updateCases.map(({ updateReq, originalCase }) => { // intentionally removing owner from the case so that we don't accidentally allow it to be updated const { id: caseId, version, owner, ...updateCaseAttributes } = updateReq; - let closedInfo = {}; - if (updateCaseAttributes.status && updateCaseAttributes.status === CaseStatuses.closed) { - closedInfo = { - closed_at: updatedDt, - closed_by: { email, full_name, username }, - }; - } else if ( - updateCaseAttributes.status && - (updateCaseAttributes.status === CaseStatuses.open || - updateCaseAttributes.status === CaseStatuses['in-progress']) - ) { - closedInfo = { - closed_at: null, - closed_by: null, - }; - } + return { caseId, originalCase, updatedAttributes: { ...updateCaseAttributes, - ...closedInfo, + ...getClosedInfoForUpdate({ + user, + closedDate: updatedDt, + status: updateCaseAttributes.status, + }), + ...getDurationForUpdate({ + status: updateCaseAttributes.status, + closedAt: updatedDt, + createdAt: originalCase.attributes.created_at, + }), updated_at: updatedDt, - updated_by: { email, full_name, username }, + updated_by: user, }, version, }; diff --git a/x-pack/plugins/cases/server/client/cases/utils.test.ts b/x-pack/plugins/cases/server/client/cases/utils.test.ts index 3dca8014957da..4832ffe5b2eaf 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.test.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.test.ts @@ -24,13 +24,15 @@ import { import { createIncident, + getClosedInfoForUpdate, + getDurationForUpdate, getLatestPushInfo, prepareFieldsForTransformation, transformComments, transformers, transformFields, } from './utils'; -import { Actions } from '../../../common/api'; +import { Actions, CaseStatuses } from '../../../common/api'; import { flattenCaseSavedObject } from '../../common/utils'; import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; import { casesConnectors } from '../../connectors'; @@ -836,5 +838,119 @@ describe('utils', () => { }); }); }); + + describe('getClosedInfoForUpdate', () => { + const date = '2021-02-03T17:41:26.108Z'; + const user = { full_name: 'Elastic', username: 'elastic', email: 'elastic@elastic.co' }; + + it('returns the correct closed info when the case closes', async () => { + expect( + getClosedInfoForUpdate({ status: CaseStatuses.closed, closedDate: date, user }) + ).toEqual({ + closed_at: date, + closed_by: user, + }); + }); + + it.each([[CaseStatuses.open], [CaseStatuses['in-progress']]])( + 'returns the correct closed info when the case %s', + async (status) => { + expect(getClosedInfoForUpdate({ status, closedDate: date, user })).toEqual({ + closed_at: null, + closed_by: null, + }); + } + ); + + it('returns undefined if the status is not provided', async () => { + expect(getClosedInfoForUpdate({ closedDate: date, user })).toBe(undefined); + }); + }); + + describe('getDurationForUpdate', () => { + const createdAt = '2021-11-23T19:00:00Z'; + const closedAt = '2021-11-23T19:02:00Z'; + + it('returns the correct duration when the case closes', () => { + expect(getDurationForUpdate({ status: CaseStatuses.closed, closedAt, createdAt })).toEqual({ + duration: 120, + }); + }); + + it.each([[CaseStatuses.open], [CaseStatuses['in-progress']]])( + 'returns the correct duration when the case %s', + (status) => { + expect(getDurationForUpdate({ status, closedAt, createdAt })).toEqual({ + duration: null, + }); + } + ); + + it('returns undefined if the status is not provided', async () => { + expect(getDurationForUpdate({ closedAt, createdAt })).toBe(undefined); + }); + + it.each([['invalid'], [null]])( + 'returns undefined if the createdAt date is %s', + (createdAtInvalid) => { + expect( + getDurationForUpdate({ + status: CaseStatuses.closed, + closedAt, + // @ts-expect-error + createdAt: createdAtInvalid, + }) + ).toBe(undefined); + } + ); + + it.each([['invalid'], [null]])( + 'returns undefined if the closedAt date is %s', + (closedAtInvalid) => { + expect( + getDurationForUpdate({ + status: CaseStatuses.closed, + // @ts-expect-error + closedAt: closedAtInvalid, + createdAt, + }) + ).toBe(undefined); + } + ); + + it('returns undefined if created_at > closed_at', async () => { + expect( + getDurationForUpdate({ + status: CaseStatuses.closed, + closedAt: '2021-11-23T19:00:00Z', + createdAt: '2021-11-23T19:05:00Z', + }) + ).toBe(undefined); + }); + + it('rounds the seconds correctly', () => { + expect( + getDurationForUpdate({ + status: CaseStatuses.closed, + createdAt: '2022-04-11T15:56:00.087Z', + closedAt: '2022-04-11T15:58:56.187Z', + }) + ).toEqual({ + duration: 176, + }); + }); + + it('rounds the zero correctly', () => { + expect( + getDurationForUpdate({ + status: CaseStatuses.closed, + createdAt: '2022-04-11T15:56:00.087Z', + closedAt: '2022-04-11T15:56:00.187Z', + }) + ).toEqual({ + duration: 0, + }); + }); + }); }); }); diff --git a/x-pack/plugins/cases/server/client/cases/utils.ts b/x-pack/plugins/cases/server/client/cases/utils.ts index 42a9a54466906..01c1a9ab897b5 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.ts @@ -23,6 +23,9 @@ import { CommentRequestAlertType, CommentRequestActionsType, ActionTypes, + CaseStatuses, + User, + CaseAttributes, } from '../../../common/api'; import { CasesClientGetAlertsResponse } from '../alerts/types'; import { @@ -405,3 +408,62 @@ export const getCommentContextFromAttributes = ( }; } }; + +export const getClosedInfoForUpdate = ({ + user, + status, + closedDate, +}: { + closedDate: string; + user: User; + status?: CaseStatuses; +}): Pick | undefined => { + if (status && status === CaseStatuses.closed) { + return { + closed_at: closedDate, + closed_by: user, + }; + } + + if (status && (status === CaseStatuses.open || status === CaseStatuses['in-progress'])) { + return { + closed_at: null, + closed_by: null, + }; + } +}; + +export const getDurationForUpdate = ({ + status, + closedAt, + createdAt, +}: { + closedAt: string; + createdAt: CaseAttributes['created_at']; + status?: CaseStatuses; +}): Pick | undefined => { + if (status && status === CaseStatuses.closed) { + try { + if (createdAt != null && closedAt != null) { + const createdAtMillis = new Date(createdAt).getTime(); + const closedAtMillis = new Date(closedAt).getTime(); + + if ( + !isNaN(createdAtMillis) && + !isNaN(closedAtMillis) && + closedAtMillis >= createdAtMillis + ) { + return { duration: Math.floor((closedAtMillis - createdAtMillis) / 1000) }; + } + } + } catch (err) { + // Silence date errors + } + } + + if (status && (status === CaseStatuses.open || status === CaseStatuses['in-progress'])) { + return { + duration: null, + }; + } +}; diff --git a/x-pack/plugins/cases/server/client/utils.test.ts b/x-pack/plugins/cases/server/client/utils.test.ts index 0210ce9eaf3d4..e550655882d55 100644 --- a/x-pack/plugins/cases/server/client/utils.test.ts +++ b/x-pack/plugins/cases/server/client/utils.test.ts @@ -87,6 +87,7 @@ describe('utils', () => { "username": "elastic", }, "description": "A description", + "duration": null, "external_service": null, "owner": "securitySolution", "settings": Object { diff --git a/x-pack/plugins/cases/server/common/utils.test.ts b/x-pack/plugins/cases/server/common/utils.test.ts index 47af4b11a0a96..974c36bd0d8a6 100644 --- a/x-pack/plugins/cases/server/common/utils.test.ts +++ b/x-pack/plugins/cases/server/common/utils.test.ts @@ -103,6 +103,7 @@ describe('common utils', () => { "username": "elastic", }, "description": "This is a brand new case of a bad meanie defacing data", + "duration": null, "external_service": null, "id": "mock-id-1", "owner": "securitySolution", @@ -141,6 +142,7 @@ describe('common utils', () => { "username": "elastic", }, "description": "Oh no, a bad meanie destroying data!", + "duration": null, "external_service": null, "id": "mock-id-2", "owner": "securitySolution", @@ -183,6 +185,7 @@ describe('common utils', () => { "username": "elastic", }, "description": "Oh no, a bad meanie going LOLBins all over the place!", + "duration": null, "external_service": null, "id": "mock-id-3", "owner": "securitySolution", @@ -229,6 +232,7 @@ describe('common utils', () => { "username": "elastic", }, "description": "Oh no, a bad meanie going LOLBins all over the place!", + "duration": null, "external_service": null, "id": "mock-id-4", "owner": "securitySolution", @@ -292,6 +296,7 @@ describe('common utils', () => { "username": "elastic", }, "description": "Oh no, a bad meanie going LOLBins all over the place!", + "duration": null, "external_service": null, "id": "mock-id-3", "owner": "securitySolution", @@ -346,6 +351,7 @@ describe('common utils', () => { "username": "elastic", }, "description": "Oh no, a bad meanie going LOLBins all over the place!", + "duration": null, "external_service": null, "id": "mock-id-3", "owner": "securitySolution", @@ -423,6 +429,7 @@ describe('common utils', () => { "username": "elastic", }, "description": "Oh no, a bad meanie going LOLBins all over the place!", + "duration": null, "external_service": null, "id": "mock-id-3", "owner": "securitySolution", @@ -475,6 +482,7 @@ describe('common utils', () => { "username": "elastic", }, "description": "This is a brand new case of a bad meanie defacing data", + "duration": null, "external_service": null, "id": "mock-id-1", "owner": "securitySolution", diff --git a/x-pack/plugins/cases/server/common/utils.ts b/x-pack/plugins/cases/server/common/utils.ts index 9385e83c948c9..11e77c5eb4579 100644 --- a/x-pack/plugins/cases/server/common/utils.ts +++ b/x-pack/plugins/cases/server/common/utils.ts @@ -55,6 +55,7 @@ export const transformNewCase = ({ newCase: CasePostRequest; }): CaseAttributes => ({ ...newCase, + duration: null, closed_at: null, closed_by: null, created_at: new Date().toISOString(), diff --git a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts b/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts index cf5de90f4f61f..cc45ef0e2d069 100644 --- a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts +++ b/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts @@ -34,6 +34,7 @@ export const mockCases: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, + duration: null, description: 'This is a brand new case of a bad meanie defacing data', external_service: null, title: 'Super Bad Security Issue', @@ -72,6 +73,7 @@ export const mockCases: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, + duration: null, description: 'Oh no, a bad meanie destroying data!', external_service: null, title: 'Damaging Data Destruction Detected', @@ -110,6 +112,7 @@ export const mockCases: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, + duration: null, description: 'Oh no, a bad meanie going LOLBins all over the place!', external_service: null, title: 'Another bad one', @@ -152,6 +155,7 @@ export const mockCases: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, + duration: null, description: 'Oh no, a bad meanie going LOLBins all over the place!', external_service: null, status: CaseStatuses.closed, diff --git a/x-pack/plugins/cases/server/saved_object_types/cases.ts b/x-pack/plugins/cases/server/saved_object_types/cases.ts index 441bd818189ae..e1df2366247ec 100644 --- a/x-pack/plugins/cases/server/saved_object_types/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/cases.ts @@ -59,6 +59,9 @@ export const createCaseSavedObjectType = ( }, }, }, + duration: { + type: 'unsigned_long', + }, description: { type: 'text', }, diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts index bdc6b6ca18e64..70e0e91caa57f 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts @@ -15,7 +15,7 @@ import { import { CASE_SAVED_OBJECT } from '../../../common/constants'; import { getNoneCaseConnector } from '../../common/utils'; import { createExternalService, ESCaseConnectorWithId } from '../../services/test_utils'; -import { caseConnectorIdMigration, removeCaseType } from './cases'; +import { addDuration, caseConnectorIdMigration, removeCaseType } from './cases'; // eslint-disable-next-line @typescript-eslint/naming-convention const create_7_14_0_case = ({ @@ -371,4 +371,129 @@ describe('case migrations', () => { }); }); }); + + describe('addDuration', () => { + it('adds the duration correctly', () => { + const doc = { + id: '123', + attributes: { + created_at: '2021-11-23T19:00:00Z', + closed_at: '2021-11-23T19:02:00Z', + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + + expect(addDuration(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + duration: 120, + }, + }); + }); + + it.each([['invalid'], [null]])( + 'returns null if the createdAt date is %s', + (createdAtInvalid) => { + const doc = { + id: '123', + attributes: { + created_at: createdAtInvalid, + closed_at: '2021-11-23T19:02:00Z', + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + + expect(addDuration(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + duration: null, + }, + }); + } + ); + + it.each([['invalid'], [null]])('returns null if the closedAt date is %s', (closedAtInvalid) => { + const doc = { + id: '123', + attributes: { + created_at: '2021-11-23T19:02:00Z', + closed_at: closedAtInvalid, + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + + expect(addDuration(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + duration: null, + }, + }); + }); + + it('returns null if created_at > closed_at', () => { + const doc = { + id: '123', + attributes: { + created_at: '2021-11-23T19:05:00Z', + closed_at: '2021-11-23T19:00:00Z', + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + + expect(addDuration(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + duration: null, + }, + }); + }); + + it('rounds the seconds correctly', () => { + const doc = { + id: '123', + attributes: { + created_at: '2022-04-11T15:56:00.087Z', + closed_at: '2022-04-11T15:58:56.187Z', + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + + expect(addDuration(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + duration: 176, + }, + }); + }); + + it('rounds to zero correctly', () => { + const doc = { + id: '123', + attributes: { + created_at: '2022-04-11T15:56:00.087Z', + closed_at: '2022-04-11T15:56:00.187Z', + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + + expect(addDuration(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + duration: 0, + }, + }); + }); + }); }); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts index eaa93a585015f..91a462c5c8053 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts @@ -11,7 +11,7 @@ import { cloneDeep, unset } from 'lodash'; import { SavedObjectUnsanitizedDoc, SavedObjectSanitizedDoc } from '@kbn/core/server'; import { addOwnerToSO, SanitizedCaseOwner } from '.'; import { ESConnectorFields } from '../../services'; -import { ConnectorTypes } from '../../../common/api'; +import { CaseAttributes, ConnectorTypes } from '../../../common/api'; import { CONNECTOR_ID_REFERENCE_NAME, PUSH_CONNECTOR_ID_REFERENCE_NAME, @@ -86,6 +86,34 @@ export const removeCaseType = ( return { ...docCopy, references: doc.references ?? [] }; }; +export const addDuration = ( + doc: SavedObjectUnsanitizedDoc> +): SavedObjectSanitizedDoc => { + let duration = null; + + try { + const createdAt = doc.attributes.created_at; + const closedAt = doc.attributes.closed_at; + + if (createdAt != null && closedAt != null) { + const createdAtMillis = new Date(createdAt).getTime(); + const closedAtMillis = new Date(closedAt).getTime(); + + if (!isNaN(createdAtMillis) && !isNaN(closedAtMillis) && closedAtMillis >= createdAtMillis) { + duration = Math.floor((closedAtMillis - createdAtMillis) / 1000); + } + } + } catch (err) { + // Silence date errors + } + + /** + * Duration is the time from the creation of the case to the close of the case in seconds + * If an error occurs or the case has not been closed then the duration is set to null + */ + return { ...doc, attributes: { ...doc.attributes, duration }, references: doc.references ?? [] }; +}; + export const caseMigrations = { '7.10.0': ( doc: SavedObjectUnsanitizedDoc @@ -147,4 +175,5 @@ export const caseMigrations = { }, '7.15.0': caseConnectorIdMigration, '8.1.0': removeCaseType, + '8.3.0': addDuration, }; diff --git a/x-pack/plugins/cases/server/services/cases/index.test.ts b/x-pack/plugins/cases/server/services/cases/index.test.ts index 7e576b83404db..5666b102dda55 100644 --- a/x-pack/plugins/cases/server/services/cases/index.test.ts +++ b/x-pack/plugins/cases/server/services/cases/index.test.ts @@ -160,6 +160,7 @@ describe('CasesService', () => { "username": "elastic", }, "description": "This is a brand new case of a bad meanie defacing data", + "duration": null, "owner": "securitySolution", "settings": Object { "syncAlerts": true, @@ -500,6 +501,7 @@ describe('CasesService', () => { "username": "elastic", }, "description": "This is a brand new case of a bad meanie defacing data", + "duration": null, "external_service": Object { "connector_name": ".jira", "external_id": "100", diff --git a/x-pack/plugins/cases/server/services/test_utils.ts b/x-pack/plugins/cases/server/services/test_utils.ts index 16c6445af774f..617dedd368ab3 100644 --- a/x-pack/plugins/cases/server/services/test_utils.ts +++ b/x-pack/plugins/cases/server/services/test_utils.ts @@ -105,6 +105,7 @@ export const basicCaseFields = { email: 'testemail@elastic.co', username: 'elastic', }, + duration: null, description: 'This is a brand new case of a bad meanie defacing data', title: 'Super Bad Security Issue', status: CaseStatuses.open, diff --git a/x-pack/test/cases_api_integration/common/lib/mock.ts b/x-pack/test/cases_api_integration/common/lib/mock.ts index ce025a5681b4f..34f1d4a9273d2 100644 --- a/x-pack/test/cases_api_integration/common/lib/mock.ts +++ b/x-pack/test/cases_api_integration/common/lib/mock.ts @@ -85,6 +85,7 @@ export const postCaseResp = ( ...req, ...(id != null ? { id } : {}), comments: [], + duration: null, totalAlerts: 0, totalComment: 0, closed_by: null, diff --git a/x-pack/test/cases_api_integration/common/lib/utils.ts b/x-pack/test/cases_api_integration/common/lib/utils.ts index 322bc3567211d..7714c85b11d9a 100644 --- a/x-pack/test/cases_api_integration/common/lib/utils.ts +++ b/x-pack/test/cases_api_integration/common/lib/utils.ts @@ -1229,3 +1229,24 @@ export const createCaseAndBulkCreateAttachments = async ({ return { theCase: patchedCase, attachments }; }; + +export const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +export const calculateDuration = (closedAt: string | null, createdAt: string | null): number => { + if (closedAt == null || createdAt == null) { + throw new Error('Dates are null'); + } + + const createdAtMillis = new Date(createdAt).getTime(); + const closedAtMillis = new Date(closedAt).getTime(); + + if (isNaN(createdAtMillis) || isNaN(closedAtMillis)) { + throw new Error('Dates are invalid'); + } + + if (closedAtMillis < createdAtMillis) { + throw new Error('Closed date is earlier than created date'); + } + + return Math.floor(Math.abs((closedAtMillis - createdAtMillis) / 1000)); +}; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/migrations.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/migrations.ts index d844cbae1d79e..3c43ac1932986 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/migrations.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/migrations.ts @@ -368,5 +368,50 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(caseInfo).not.to.have.property('type'); }); }); + + describe('8.3.0 adding duration', () => { + before(async () => { + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_duration.json' + ); + }); + + after(async () => { + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_duration.json' + ); + await deleteAllCaseItems(es); + }); + + it('calculates the correct duration for closed cases', async () => { + const caseInfo = await getCase({ + supertest, + caseId: '4537b380-a512-11ec-b92f-859b9e89e434', + }); + + expect(caseInfo).to.have.property('duration'); + expect(caseInfo.duration).to.be(120); + }); + + it('sets the duration to null to open cases', async () => { + const caseInfo = await getCase({ + supertest, + caseId: '7537b580-a512-11ec-b94f-85979e89e434', + }); + + expect(caseInfo).to.have.property('duration'); + expect(caseInfo.duration).to.be(null); + }); + + it('sets the duration to null to in-progress cases', async () => { + const caseInfo = await getCase({ + supertest, + caseId: '1537b580-a512-11ec-b94f-85979e89e434', + }); + + expect(caseInfo).to.have.property('duration'); + expect(caseInfo.duration).to.be(null); + }); + }); }); } diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts index 8e25ee9225493..9ef1c3d5655e4 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts @@ -34,6 +34,8 @@ import { removeServerGeneratedPropertiesFromUserAction, findCases, superUserSpace1Auth, + delay, + calculateDuration, } from '../../../../common/lib/utils'; import { createSignalsIndex, @@ -111,9 +113,11 @@ export default ({ getService }: FtrProviderContext): void => { const userActions = await getCaseUserActions({ supertest, caseID: postedCase.id }); const statusUserAction = removeServerGeneratedPropertiesFromUserAction(userActions[1]); const data = removeServerGeneratedPropertiesFromCase(patchedCases[0]); + const { duration, ...dataWithoutDuration } = data; + const { duration: resDuration, ...resWithoutDuration } = postCaseResp(); - expect(data).to.eql({ - ...postCaseResp(), + expect(dataWithoutDuration).to.eql({ + ...resWithoutDuration, status: CaseStatuses.closed, closed_by: defaultUser, updated_by: defaultUser, @@ -198,6 +202,65 @@ export default ({ getService }: FtrProviderContext): void => { updated_by: defaultUser, }); }); + + describe('duration', () => { + it('updates the duration correctly when the case closes', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await delay(1000); + + const patchedCases = await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + status: CaseStatuses.closed, + }, + ], + }, + }); + + const duration = calculateDuration(patchedCases[0].closed_at, postedCase.created_at); + expect(duration).to.be(patchedCases[0].duration); + }); + + for (const status of [CaseStatuses.open, CaseStatuses['in-progress']]) { + it(`sets the duration to null when the case status changes to ${status}`, async () => { + const postedCase = await createCase(supertest, postCaseReq); + + const closedCases = await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + status: CaseStatuses.closed, + }, + ], + }, + }); + + expect(closedCases[0].duration).to.not.be(null); + + const openCases = await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: closedCases[0].version, + status, + }, + ], + }, + }); + + expect(openCases[0].duration).to.be(null); + }); + } + }); }); describe('unhappy path', () => { diff --git a/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_duration.json b/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_duration.json new file mode 100644 index 0000000000000..ba8bab9227fbb --- /dev/null +++ b/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_duration.json @@ -0,0 +1,129 @@ +{ + "attributes": { + "closed_at": "2022-03-25T10:18:00.000Z", + "closed_by": { + "email": "", + "full_name": "", + "username": "elastic" + }, + "connector": { + "fields": null, + "name": "none", + "type": ".none" + }, + "created_at": "2022-03-25T10:16:00.000Z", + "created_by": { + "email": "", + "full_name": "", + "username": "elastic" + }, + "description": "test 2", + "external_service": null, + "owner": "securitySolutionFixture", + "settings": { + "syncAlerts": false + }, + "status": "closed", + "tags": [], + "title": "stack", + "updated_at": "2022-03-29T10:33:09.754Z", + "updated_by": { + "email": "", + "full_name": "", + "username": "elastic" + } + }, + "coreMigrationVersion": "8.2.0", + "id": "4537b380-a512-11ec-b92f-859b9e89e434", + "migrationVersion": { + "cases": "8.1.0" + }, + "references": [], + "type": "cases", + "updated_at": "2022-03-29T10:33:09.754Z", + "version": "WzE2OTYyNCwxNF0=" +} + +{ + "attributes": { + "closed_at": null, + "closed_by": null, + "connector": { + "fields": null, + "name": "none", + "type": ".none" + }, + "created_at": "2022-03-20T10:16:56.252Z", + "created_by": { + "email": "", + "full_name": "", + "username": "elastic" + }, + "description": "test 2", + "external_service": null, + "owner": "observabilityFixture", + "settings": { + "syncAlerts": false + }, + "status": "open", + "tags": [], + "title": "stack", + "updated_at": "2022-03-29T10:33:09.754Z", + "updated_by": { + "email": "", + "full_name": "", + "username": "elastic" + } + }, + "coreMigrationVersion": "8.2.0", + "id": "7537b580-a512-11ec-b94f-85979e89e434", + "migrationVersion": { + "cases": "8.1.0" + }, + "references": [], + "type": "cases", + "updated_at": "2022-03-29T10:33:09.754Z", + "version": "WzE2OTYyNCwxNF0=" +} + +{ + "attributes": { + "closed_at": null, + "closed_by": null, + "connector": { + "fields": null, + "name": "none", + "type": ".none" + }, + "created_at": "2022-03-20T10:16:56.252Z", + "created_by": { + "email": "", + "full_name": "", + "username": "elastic" + }, + "description": "test 2", + "external_service": null, + "owner": "observabilityFixture", + "settings": { + "syncAlerts": false + }, + "status": "in-progress", + "tags": [], + "title": "stack", + "updated_at": "2022-03-29T10:33:09.754Z", + "updated_by": { + "email": "", + "full_name": "", + "username": "elastic" + } + }, + "coreMigrationVersion": "8.2.0", + "id": "1537b580-a512-11ec-b94f-85979e89e434", + "migrationVersion": { + "cases": "8.1.0" + }, + "references": [], + "type": "cases", + "updated_at": "2022-03-29T10:33:09.754Z", + "version": "WzE2OTYyNCwxNF0=" +} diff --git a/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json b/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json index ffdfef08735fd..cd87b6c44de1b 100644 --- a/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json +++ b/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json @@ -11,7 +11,7 @@ "created_by": { "email": "", "full_name": "", - "username": "cnasikas" + "username": "elastic" }, "description": "test", "external_service": null, @@ -26,7 +26,7 @@ "updated_by": { "email": "", "full_name": "", - "username": "cnasikas" + "username": "elastic" } }, "coreMigrationVersion": "8.2.0", @@ -53,7 +53,7 @@ "created_by": { "email": "", "full_name": "", - "username": "cnasikas" + "username": "elastic" }, "description": "test 2", "external_service": null, @@ -68,7 +68,7 @@ "updated_by": { "email": "", "full_name": "", - "username": "cnasikas" + "username": "elastic" } }, "coreMigrationVersion": "8.2.0", @@ -95,7 +95,7 @@ "created_by": { "email": "", "full_name": "", - "username": "cnasikas" + "username": "elastic" }, "description": "test 2", "external_service": null, @@ -110,7 +110,7 @@ "updated_by": { "email": "", "full_name": "", - "username": "cnasikas" + "username": "elastic" } }, "coreMigrationVersion": "8.2.0", From fc46fd6a8db908a1c5aa259fc0a304b382668869 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 27 Apr 2022 17:24:23 +0300 Subject: [PATCH 25/78] [Cases] Escape KQL in range queries (#131058) --- .../plugins/cases/server/client/utils.test.ts | 59 +++++++++++++++---- x-pack/plugins/cases/server/client/utils.ts | 12 +++- .../tests/common/cases/find_cases.ts | 11 +++- .../tests/common/cases/status/get_status.ts | 13 +++- 4 files changed, 76 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/cases/server/client/utils.test.ts b/x-pack/plugins/cases/server/client/utils.test.ts index e550655882d55..24e1135020a88 100644 --- a/x-pack/plugins/cases/server/client/utils.test.ts +++ b/x-pack/plugins/cases/server/client/utils.test.ts @@ -118,18 +118,6 @@ describe('utils', () => { expect(node).toBeFalsy(); }); - it('returns undefined if the from is malformed', () => { - expect(() => buildRangeFilter({ from: '<' })).toThrowError( - 'Invalid "from" and/or "to" query parameters' - ); - }); - - it('returns undefined if the to is malformed', () => { - expect(() => buildRangeFilter({ to: '<' })).toThrowError( - 'Invalid "from" and/or "to" query parameters' - ); - }); - it('creates a range filter with only the from correctly', () => { const node = buildRangeFilter({ from: 'now-1M' }); expect(toElasticsearchQuery(node!)).toMatchInlineSnapshot(` @@ -217,6 +205,7 @@ describe('utils', () => { field: 'test', savedObjectType: 'test-type', }); + expect(toElasticsearchQuery(node!)).toMatchInlineSnapshot(` Object { "bool": Object { @@ -254,5 +243,51 @@ describe('utils', () => { } `); }); + + it('escapes the query correctly', () => { + const node = buildRangeFilter({ + from: '2022-04-27T12:55:47.576Z', + to: '2022-04-27T12:56:47.576Z', + field: '= ${from}` : undefined; - const toKQL = to != null ? `${savedObjectType}.attributes.${field} <= ${to}` : undefined; + const fromKQL = + from != null + ? `${escapeKuery(savedObjectType)}.attributes.${escapeKuery(field)} >= ${escapeKuery(from)}` + : undefined; + const toKQL = + to != null + ? `${escapeKuery(savedObjectType)}.attributes.${escapeKuery(field)} <= ${escapeKuery(to)}` + : undefined; const rangeKQLQuery = `${fromKQL != null ? fromKQL : ''} ${ fromKQL != null && toKQL != null ? 'and' : '' diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts index 657424e644be5..ddf0425fb5386 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts @@ -517,8 +517,15 @@ export default ({ getService }: FtrProviderContext): void => { } }); - it('returns a bad request on malformed parameter', async () => { - await findCases({ supertest, query: { from: '<' }, expectedHttpCode: 400 }); + it('escapes correctly', async () => { + const cases = await findCases({ + supertest, + query: { from: '2022-03-15T10:16:56.252Z', to: '2022-03-20T10:16:56.252' }, + }); + + expect(cases.total).to.be(2); + expect(cases.count_open_cases).to.be(2); + expect(cases.cases.length).to.be(2); }); }); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts index 1a9671385a36c..4b7a11166ed16 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts @@ -113,8 +113,17 @@ export default ({ getService }: FtrProviderContext): void => { } }); - it('returns a bad request on malformed parameter', async () => { - await getAllCasesStatuses({ supertest, query: { from: '<' }, expectedHttpCode: 400 }); + it('escapes correctly', async () => { + const statuses = await getAllCasesStatuses({ + supertest, + query: { from: '2022-03-15T10:16:56.252Z', to: '2022-03-20T10:16:56.252' }, + }); + + expect(statuses).to.eql({ + count_open_cases: 2, + count_closed_cases: 0, + count_in_progress_cases: 0, + }); }); }); From d4427f2e34c8b296b1d73c8d8c9895b45f0ef96c Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 27 Apr 2022 07:38:32 -0700 Subject: [PATCH 26/78] [ci-stats/timings] limit metadata strings to 2k length (#131027) Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../src/ci_stats_reporter.ts | 21 ++++++++++++++++++- packages/kbn-pm/dist/index.js | 15 ++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts b/packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts index d709927787b0e..933f4ccdde0a3 100644 --- a/packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts +++ b/packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts @@ -23,6 +23,18 @@ import type { CiStatsTestGroupInfo, CiStatsTestRun } from './ci_stats_test_group const BASE_URL = 'https://ci-stats.kibana.dev'; +function limitMetaStrings(meta: CiStatsMetadata) { + return Object.fromEntries( + Object.entries(meta).map(([key, value]) => { + if (typeof value === 'string' && value.length > 2000) { + return [key, value.slice(0, 2000)]; + } + + return [key, value]; + }) + ); +} + /** A ci-stats metric record */ export interface CiStatsMetric { /** Top-level categorization for the metric, e.g. "page load bundle size" */ @@ -138,7 +150,14 @@ export class CiStatsReporter { } const buildId = this.config?.buildId; - const timings = options.timings; + const timings = options.timings.map((timing) => + timing.meta + ? { + ...timing, + meta: limitMetaStrings(timing.meta), + } + : timing + ); const upstreamBranch = options.upstreamBranch ?? this.getUpstreamBranch(); const kibanaUuid = options.kibanaUuid === undefined ? this.getKibanaUuid() : options.kibanaUuid; let email; diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 32171e030d91a..df1391fe55dd8 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -1580,8 +1580,19 @@ var _ciStatsCore = __webpack_require__("../../node_modules/@kbn/ci-stats-core/ta */ // @ts-expect-error not "public", but necessary to prevent Jest shimming from breaking things const BASE_URL = 'https://ci-stats.kibana.dev'; + +function limitMetaStrings(meta) { + return Object.fromEntries(Object.entries(meta).map(([key, value]) => { + if (typeof value === 'string' && value.length > 2000) { + return [key, value.slice(0, 2000)]; + } + + return [key, value]; + })); +} /** A ci-stats metric record */ + /** Object that helps report data to the ci-stats service */ class CiStatsReporter { /** @@ -1631,7 +1642,9 @@ class CiStatsReporter { } const buildId = (_this$config3 = this.config) === null || _this$config3 === void 0 ? void 0 : _this$config3.buildId; - const timings = options.timings; + const timings = options.timings.map(timing => timing.meta ? { ...timing, + meta: limitMetaStrings(timing.meta) + } : timing); const upstreamBranch = (_options$upstreamBran = options.upstreamBranch) !== null && _options$upstreamBran !== void 0 ? _options$upstreamBran : this.getUpstreamBranch(); const kibanaUuid = options.kibanaUuid === undefined ? this.getKibanaUuid() : options.kibanaUuid; let email; From 88bad4ed00e164740396edbff2cd9dd5bf0936ca Mon Sep 17 00:00:00 2001 From: Colleen McGinnis Date: Wed, 27 Apr 2022 09:41:51 -0500 Subject: [PATCH 27/78] remove source config settings (#130931) --- .../general-infra-logs-ui-settings.asciidoc | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/docs/settings/general-infra-logs-ui-settings.asciidoc b/docs/settings/general-infra-logs-ui-settings.asciidoc index 91a6c2ef91a23..af84b92829dfd 100644 --- a/docs/settings/general-infra-logs-ui-settings.asciidoc +++ b/docs/settings/general-infra-logs-ui-settings.asciidoc @@ -1,28 +1,7 @@ -`xpack.infra.sources.default.logAlias`:: -Index pattern for matching indices that contain log data. Defaults to `filebeat-*,kibana_sample_data_logs*`. To match multiple wildcard patterns, use a comma to separate the names, with no space after the comma. For example, `logstash-app1-*,default-logs-*`. - -`xpack.infra.sources.default.metricAlias`:: -Index pattern for matching indices that contain Metricbeat data. Defaults to `metricbeat-*`. To match multiple wildcard patterns, use a comma to separate the names, with no space after the comma. For example, `logstash-app1-*,default-logs-*`. - -`xpack.infra.sources.default.fields.timestamp`:: -Timestamp used to sort log entries. Defaults to `@timestamp`. - `xpack.infra.sources.default.fields.message`:: Fields used to display messages in the Logs app. Defaults to `['message', '@message']`. -`xpack.infra.sources.default.fields.tiebreaker`:: -Field used to break ties between two entries with the same timestamp. Defaults to `_doc`. - -`xpack.infra.sources.default.fields.host`:: -Field used to identify hosts. Defaults to `host.name`. - -`xpack.infra.sources.default.fields.container`:: -Field used to identify Docker containers. Defaults to `container.id`. - -`xpack.infra.sources.default.fields.pod`:: -Field used to identify Kubernetes pods. Defaults to `kubernetes.pod.uid`. - `xpack.infra.alerting.inventory_threshold.group_by_page_size`:: Controls the size of the composite aggregations used by the Inventory Threshold to retrieve all the hosts. Defaults to `10_000`. From 308a4aa492e567fa1d1fde19d31a14628bffdb6a Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Wed, 27 Apr 2022 17:07:19 +0200 Subject: [PATCH 28/78] [ML] Functional tests - re-enable permission tests in functional_basic (#131049) This PR re-enables the full_ml_access permission tests in functional_basic. --- .../functional_basic/apps/ml/permissions/full_ml_access.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/test/functional_basic/apps/ml/permissions/full_ml_access.ts b/x-pack/test/functional_basic/apps/ml/permissions/full_ml_access.ts index 6db82801a5bdd..b44c5f08bdbc6 100644 --- a/x-pack/test/functional_basic/apps/ml/permissions/full_ml_access.ts +++ b/x-pack/test/functional_basic/apps/ml/permissions/full_ml_access.ts @@ -20,9 +20,7 @@ export default function ({ getService }: FtrProviderContext) { { user: USER.ML_POWERUSER_SPACES, discoverAvailable: false }, ]; - // FLAKY: https://github.com/elastic/kibana/issues/124413 - // FLAKY: https://github.com/elastic/kibana/issues/122838 - describe.skip('for user with full ML access', function () { + describe('for user with full ML access', function () { for (const testUser of testUsers) { describe(`(${testUser.user})`, function () { const ecIndexPattern = 'ft_module_sample_ecommerce'; From a145dffe87580428ef3901f38d5cec4b41460a8a Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Wed, 27 Apr 2022 17:07:41 +0200 Subject: [PATCH 29/78] [ML] Functional tests - re-enable trained models tests (#131051) This PR re-enables the trained models functional tests. --- x-pack/test/functional/apps/ml/model_management/model_list.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/ml/model_management/model_list.ts b/x-pack/test/functional/apps/ml/model_management/model_list.ts index 811ae280e0780..08fb3b7124aec 100644 --- a/x-pack/test/functional/apps/ml/model_management/model_list.ts +++ b/x-pack/test/functional/apps/ml/model_management/model_list.ts @@ -10,8 +10,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); - // Failing: See https://github.com/elastic/kibana/issues/125455 - describe.skip('trained models', function () { + describe('trained models', function () { before(async () => { await ml.trainedModels.createTestTrainedModels('classification', 15, true); await ml.trainedModels.createTestTrainedModels('regression', 15); From ba7580b78634599bf04265ad7562a5d79ff47ee3 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Wed, 27 Apr 2022 17:08:04 +0200 Subject: [PATCH 30/78] [ML] Functional tests - re-enable outlier detection saved search tests (#131052) This PR re-enables the outlier detection saved search tests. --- .../outlier_detection_creation_saved_search.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts index 75ad646a26ac9..861be18591a11 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts @@ -163,8 +163,7 @@ export default function ({ getService }: FtrProviderContext) { ]; for (const testData of testDataList) { - // FLAKY: https://github.com/elastic/kibana/issues/126112 - describe.skip(`${testData.suiteTitle}`, function () { + describe(`${testData.suiteTitle}`, function () { after(async () => { await ml.api.deleteIndices(testData.destinationIndex); await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); From 73706d1867188d3c7960a68ad595a68fbfde59e3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 27 Apr 2022 10:14:36 -0500 Subject: [PATCH 31/78] Update dependency @types/selenium-webdriver to ^4.0.19 (#131018) Co-authored-by: Renovate Bot Co-authored-by: Jonathan Budzenski --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 4c7db193368a6..24c03762b1bed 100644 --- a/package.json +++ b/package.json @@ -740,7 +740,7 @@ "@types/redux-logger": "^3.0.8", "@types/resolve": "^1.20.1", "@types/seedrandom": ">=2.0.0 <4.0.0", - "@types/selenium-webdriver": "^4.0.18", + "@types/selenium-webdriver": "^4.0.19", "@types/semver": "^7", "@types/set-value": "^2.0.0", "@types/sinon": "^7.0.13", diff --git a/yarn.lock b/yarn.lock index 4d1428bc991a4..b0a457c84d3ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6963,10 +6963,10 @@ resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.28.tgz#9ce8fa048c1e8c85cb71d7fe4d704e000226036f" integrity sha512-SMA+fUwULwK7sd/ZJicUztiPs8F1yCPwF3O23Z9uQ32ME5Ha0NmDK9+QTsYE4O2tHXChzXomSWWeIhCnoN1LqA== -"@types/selenium-webdriver@^4.0.18": - version "4.0.18" - resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-4.0.18.tgz#98f6e1ccd2d92f6fddaccfc7c148d2e158da0f92" - integrity sha512-gkrUo3QldGr8V9im/DjgKkX4UVd1rtflfEBuPG9hPSA1keu7A0rF8h/MQjpTMm2EPVhBCd2K8tn5nlC9Vsd5Xw== +"@types/selenium-webdriver@^4.0.19": + version "4.0.19" + resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-4.0.19.tgz#25699713552a63ee70215effdfd2e5d6dda19f8e" + integrity sha512-Irrh+iKc6Cxj6DwTupi4zgWhSBm1nK+JElOklIUiBVE6rcLYDtT1mwm9oFkHie485BQXNmZRoayjwxhowdInnA== "@types/semver@^7": version "7.3.4" From 2df9e4f58d160dd688de651ed35b6bce03116102 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Wed, 27 Apr 2022 17:15:31 +0200 Subject: [PATCH 32/78] [ML] Functional tests - re-enable data frame analytics results view tests (#131053) This PR stabilizes and re-enables the data frame analytics results view tests. --- .../apps/ml/data_frame_analytics/results_view_content.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts index 57d3922b043a3..2bddf0a7d9512 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts @@ -14,8 +14,7 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const ml = getService('ml'); - // FLAKY: https://github.com/elastic/kibana/issues/126422 - describe.skip('results view content and total feature importance', function () { + describe('results view content and total feature importance', function () { const testDataList: Array<{ suiteTitle: string; archive: string; @@ -284,6 +283,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('should display the total feature importance in the results view', async () => { + await ml.dataFrameAnalyticsResults.expandFeatureImportanceSection(true); await ml.dataFrameAnalyticsResults.assertTotalFeatureImportanceEvaluatePanelExists(); }); From 161e9a274224e39a17cbc7540542eac88ba03f0c Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 27 Apr 2022 08:24:22 -0700 Subject: [PATCH 33/78] [x-pack/functional] restore and fix home tests (#131064) --- .../functional/apps/home/feature_controls/home_security.ts | 4 ++-- x-pack/test/functional/config.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/home/feature_controls/home_security.ts b/x-pack/test/functional/apps/home/feature_controls/home_security.ts index f306b9f553d64..96a3ccbe8ea5d 100644 --- a/x-pack/test/functional/apps/home/feature_controls/home_security.ts +++ b/x-pack/test/functional/apps/home/feature_controls/home_security.ts @@ -78,7 +78,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it('shows the "Manage" action item', async () => { - await testSubjects.existOrFail('homManagementActionItem', { + await testSubjects.existOrFail('homeManage', { timeout: 2000, }); }); @@ -128,7 +128,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it('does not show the "Manage" action item', async () => { - await testSubjects.missingOrFail('homManagementActionItem', { + await testSubjects.missingOrFail('homeManage', { timeout: 2000, }); }); diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 3d1f7d5e3dbd5..8d39f26d4569d 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -30,6 +30,7 @@ export default async function ({ readConfigFile }) { return { // list paths to the files that contain your plugins tests testFiles: [ + resolve(__dirname, './apps/home'), resolve(__dirname, './apps/advanced_settings'), resolve(__dirname, './apps/canvas'), resolve(__dirname, './apps/graph'), From 9e197819c2a4df70110f17d916dd7005c3a5424d Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Wed, 27 Apr 2022 08:32:10 -0700 Subject: [PATCH 34/78] Drafts an open API specification for Kibana ML APIs (#129548) --- docs/api/machine-learning/sync.asciidoc | 6 +- x-pack/plugins/ml/common/openapi/README.md | 20 ++ x-pack/plugins/ml/common/openapi/bundled.json | 225 ++++++++++++++++++ x-pack/plugins/ml/common/openapi/bundled.yaml | 167 +++++++++++++ x-pack/plugins/ml/common/openapi/ml_apis.yaml | 146 ++++++++++++ 5 files changed, 561 insertions(+), 3 deletions(-) create mode 100644 x-pack/plugins/ml/common/openapi/README.md create mode 100644 x-pack/plugins/ml/common/openapi/bundled.json create mode 100644 x-pack/plugins/ml/common/openapi/bundled.yaml create mode 100644 x-pack/plugins/ml/common/openapi/ml_apis.yaml diff --git a/docs/api/machine-learning/sync.asciidoc b/docs/api/machine-learning/sync.asciidoc index ba7e98a12dde6..d210a7826d166 100644 --- a/docs/api/machine-learning/sync.asciidoc +++ b/docs/api/machine-learning/sync.asciidoc @@ -47,9 +47,9 @@ it is added. This list contains the {dfeed} identifiers and indicates whether the synchronization was successful. `datafeedsRemoved`:: -(array) If saved objects exist for {dfeeds} that no longer exist, they are -deleted. This list contains the {dfeed} identifiers and indicates whether the -synchronization was successful. +(array) If a saved object for an anomaly detection job references a datafeed +that no longer exists, it is deleted. This list contains the {dfeed} identifiers +and indicates whether the synchronization was successful. `savedObjectsCreated`:: (array) If saved objects are missing for {ml} jobs or trained models, they are diff --git a/x-pack/plugins/ml/common/openapi/README.md b/x-pack/plugins/ml/common/openapi/README.md new file mode 100644 index 0000000000000..28246e8b43d3c --- /dev/null +++ b/x-pack/plugins/ml/common/openapi/README.md @@ -0,0 +1,20 @@ +# OpenAPI (Experimental) + +The current self-contained spec file can be used for online tools like those found at https://openapi.tools/. This spec is experimental and may be incomplete or change later. + +A guide about the openApi specification can be found at [https://swagger.io/docs/specification/about/](https://swagger.io/docs/specification/about/). + + ## Tools + +It is possible to validate the docs before bundling them by running the following command in the `x-pack/plugins/ml/common/openapi/` folder: + ``` + npx swagger-cli validate ml_apis.yaml + ``` + +Then generate the `bundled` files with the following commands: + + ``` + npx @redocly/openapi-cli bundle --ext yaml --output bundled.yaml ml_apis.yaml + npx @redocly/openapi-cli bundle --ext json --output bundled.json ml_apis.yaml + ``` + diff --git a/x-pack/plugins/ml/common/openapi/bundled.json b/x-pack/plugins/ml/common/openapi/bundled.json new file mode 100644 index 0000000000000..455bf02bbd232 --- /dev/null +++ b/x-pack/plugins/ml/common/openapi/bundled.json @@ -0,0 +1,225 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Machine learning APIs", + "description": "Kibana APIs for the machine learning feature", + "version": "1.0.0", + "license": { + "name": "Elastic License 2.0", + "url": "https://www.elastic.co/licensing/elastic-license" + } + }, + "tags": [ + { + "name": "ml", + "description": "Machine learning" + } + ], + "servers": [ + { + "url": "https://localhost:5601/" + } + ], + "paths": { + "/s/{spaceId}/api/ml/saved_objects/sync": { + "get": { + "description": "Synchronizes Kibana saved objects for machine learning jobs and trained models. You must have `all` privileges for the **Machine Learning** feature in the **Analytics** section of the Kibana feature privileges. This API runs automatically when you start Kibana and periodically thereafter.\n", + "parameters": [ + { + "$ref": "#/components/parameters/spaceParam" + }, + { + "$ref": "#/components/parameters/simulateParam" + } + ], + "responses": { + "200": { + "description": "Indicates a successful call", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/syncResponse" + } + } + } + } + } + } + } + }, + "/api/ml/saved_objects/sync": { + "get": { + "description": "Synchronizes Kibana saved objects for machine learning jobs and trained models. You must have `all` privileges for the **Machine Learning** feature in the **Analytics** section of the Kibana feature privileges. This API runs automatically when you start Kibana and periodically thereafter.\n", + "parameters": [ + { + "$ref": "#/components/parameters/simulateParam" + } + ], + "responses": { + "200": { + "description": "Indicates a successful call", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/syncResponse" + } + } + } + } + } + } + } + } + }, + "components": { + "parameters": { + "spaceParam": { + "in": "path", + "name": "spaceId", + "description": "An identifier for the space.", + "required": true, + "schema": { + "type": "string" + } + }, + "simulateParam": { + "in": "query", + "name": "simulate", + "description": "When true, simulates the synchronization by returning only the list actions that would be performed.", + "required": false, + "schema": { + "type": "boolean" + }, + "example": "true" + } + }, + "securitySchemes": { + "basicAuth": { + "type": "http", + "scheme": "basic" + } + }, + "schemas": { + "syncResponse": { + "type": "object", + "properties": { + "datafeedsAdded": { + "type": "object", + "description": "If a saved object for an anomaly detection job is missing a datafeed identifier, it is added. This list contains the datafeed identifiers and indicates whether the synchronization was successful.", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + } + } + }, + "datafeedsRemoved": { + "type": "object", + "description": "If a saved object for an anomaly detection job references a datafeed that no longer exists, it is deleted. This list contains the datafeed identifiers and indicates whether the synchronization was successful.", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + } + } + }, + "savedObjectsCreated": { + "type": "object", + "description": "If saved objects are missing for machine learning jobs or trained models, they are created. This list contains the job and model identifiers and indicates whether the synchronization was successful.", + "properties": { + "anomaly-detector": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + } + }, + "data-frame-analytics": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + } + }, + "trained-model": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + } + } + } + }, + "savedObjectsDeleted": { + "type": "object", + "description": "If saved objects exist for machine learning jobs or trained models that no longer exist, they are deleted. This list contains the job and model identifiers and indicates whether the synchronization was successful.", + "properties": { + "anomaly-detector": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + } + }, + "data-frame-analytics": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + } + }, + "trained-model": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + } + } + } + } + } + } + } + }, + "security": [ + { + "basicAuth": [] + } + ] +} \ No newline at end of file diff --git a/x-pack/plugins/ml/common/openapi/bundled.yaml b/x-pack/plugins/ml/common/openapi/bundled.yaml new file mode 100644 index 0000000000000..235979ff1651c --- /dev/null +++ b/x-pack/plugins/ml/common/openapi/bundled.yaml @@ -0,0 +1,167 @@ +openapi: 3.0.1 +info: + title: Machine learning APIs + description: Kibana APIs for the machine learning feature + version: 1.0.0 + license: + name: Elastic License 2.0 + url: https://www.elastic.co/licensing/elastic-license +tags: + - name: ml + description: Machine learning +servers: + - url: https://localhost:5601/ +paths: + /s/{spaceId}/api/ml/saved_objects/sync: + get: + description: > + Synchronizes Kibana saved objects for machine learning jobs and trained + models. You must have `all` privileges for the **Machine Learning** + feature in the **Analytics** section of the Kibana feature privileges. + This API runs automatically when you start Kibana and periodically + thereafter. + parameters: + - $ref: '#/components/parameters/spaceParam' + - $ref: '#/components/parameters/simulateParam' + responses: + '200': + description: Indicates a successful call + content: + application/json: + schema: + items: + $ref: '#/components/schemas/syncResponse' + /api/ml/saved_objects/sync: + get: + description: > + Synchronizes Kibana saved objects for machine learning jobs and trained + models. You must have `all` privileges for the **Machine Learning** + feature in the **Analytics** section of the Kibana feature privileges. + This API runs automatically when you start Kibana and periodically + thereafter. + parameters: + - $ref: '#/components/parameters/simulateParam' + responses: + '200': + description: Indicates a successful call + content: + application/json: + schema: + items: + $ref: '#/components/schemas/syncResponse' +components: + parameters: + spaceParam: + in: path + name: spaceId + description: An identifier for the space. + required: true + schema: + type: string + simulateParam: + in: query + name: simulate + description: >- + When true, simulates the synchronization by returning only the list + actions that would be performed. + required: false + schema: + type: boolean + example: 'true' + securitySchemes: + basicAuth: + type: http + scheme: basic + schemas: + syncResponse: + type: object + properties: + datafeedsAdded: + type: object + description: >- + If a saved object for an anomaly detection job is missing a datafeed + identifier, it is added. This list contains the datafeed identifiers + and indicates whether the synchronization was successful. + additionalProperties: + type: object + properties: + success: + type: boolean + datafeedsRemoved: + type: object + description: >- + If a saved object for an anomaly detection job references a datafeed + that no longer exists, it is deleted. This list contains the + datafeed identifiers and indicates whether the synchronization was + successful. + additionalProperties: + type: object + properties: + success: + type: boolean + savedObjectsCreated: + type: object + description: >- + If saved objects are missing for machine learning jobs or trained + models, they are created. This list contains the job and model + identifiers and indicates whether the synchronization was + successful. + properties: + anomaly-detector: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + data-frame-analytics: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + trained-model: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + savedObjectsDeleted: + type: object + description: >- + If saved objects exist for machine learning jobs or trained models + that no longer exist, they are deleted. This list contains the job + and model identifiers and indicates whether the synchronization was + successful. + properties: + anomaly-detector: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + data-frame-analytics: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + trained-model: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true +security: + - basicAuth: [] diff --git a/x-pack/plugins/ml/common/openapi/ml_apis.yaml b/x-pack/plugins/ml/common/openapi/ml_apis.yaml new file mode 100644 index 0000000000000..827aa1075960d --- /dev/null +++ b/x-pack/plugins/ml/common/openapi/ml_apis.yaml @@ -0,0 +1,146 @@ +openapi: 3.0.1 +info: + title: Machine learning APIs + description: Kibana APIs for the machine learning feature + version: "1.0.0" + license: + name: Elastic License 2.0 + url: https://www.elastic.co/licensing/elastic-license +tags: + - name: ml + description: Machine learning +servers: + - url: https://localhost:5601/ +paths: + /s/{spaceId}/api/ml/saved_objects/sync: + get: + description: > + Synchronizes Kibana saved objects for machine learning jobs and trained models. + You must have `all` privileges for the **Machine Learning** feature in the **Analytics** section of the Kibana feature privileges. + This API runs automatically when you start Kibana and periodically thereafter. + parameters: + - $ref: '#/components/parameters/spaceParam' + - $ref: '#/components/parameters/simulateParam' + responses: + '200': + description: Indicates a successful call + content: + application/json: + schema: + items: + $ref: '#/components/schemas/syncResponse' + /api/ml/saved_objects/sync: + get: + description: > + Synchronizes Kibana saved objects for machine learning jobs and trained models. + You must have `all` privileges for the **Machine Learning** feature in the **Analytics** section of the Kibana feature privileges. + This API runs automatically when you start Kibana and periodically thereafter. + parameters: + - $ref: '#/components/parameters/simulateParam' + responses: + '200': + description: Indicates a successful call + content: + application/json: + schema: + items: + $ref: '#/components/schemas/syncResponse' +components: + parameters: + spaceParam: + in: path + name: spaceId + description: An identifier for the space. + required: true + schema: + type: string + simulateParam: + in: query + name: simulate + description: When true, simulates the synchronization by returning only the list actions that would be performed. + required: false + schema: + type: boolean + example: 'true' + securitySchemes: + basicAuth: + type: http + scheme: basic + schemas: + syncResponse: + type: object + properties: + datafeedsAdded: + type: object + description: If a saved object for an anomaly detection job is missing a datafeed identifier, it is added. This list contains the datafeed identifiers and indicates whether the synchronization was successful. + additionalProperties: + type: object + properties: + success: + type: boolean + datafeedsRemoved: + type: object + description: If a saved object for an anomaly detection job references a datafeed that no longer exists, it is deleted. This list contains the datafeed identifiers and indicates whether the synchronization was successful. + additionalProperties: + type: object + properties: + success: + type: boolean + savedObjectsCreated: + type: object + description: If saved objects are missing for machine learning jobs or trained models, they are created. This list contains the job and model identifiers and indicates whether the synchronization was successful. + properties: + anomaly-detector: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + data-frame-analytics: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + trained-model: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + savedObjectsDeleted: + type: object + description: If saved objects exist for machine learning jobs or trained models that no longer exist, they are deleted. This list contains the job and model identifiers and indicates whether the synchronization was successful. + properties: + anomaly-detector: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + data-frame-analytics: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + trained-model: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true +security: + - basicAuth: [] \ No newline at end of file From ac560f33efbbd5f5d04039e32a54c53ec13f0746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Efe=20G=C3=BCrkan=20YALAMAN?= Date: Wed, 27 Apr 2022 17:51:12 +0200 Subject: [PATCH 35/78] [App Search] Fix outcome field on Audit log modal. (#131024) * Change outcome field type to event.outcome * Change message column to a field --- .../engines/components/audit_logs_modal/audit_logs_modal.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.tsx index 3807234fd5c11..ea5abb74d5ac0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.tsx @@ -92,7 +92,7 @@ export const AuditLogsModal: React.FC = () => { }, { type: 'field', - field: 'outcome', + field: 'event.outcome', header: i18n.translate( 'xpack.enterpriseSearch.appSearch.engines.auditLogsModal.headers.outcome', { @@ -101,7 +101,8 @@ export const AuditLogsModal: React.FC = () => { ), }, { - type: 'message', + type: 'field', + field: 'message', width: '50%', }, ]} From 1a05ce5cab240299d1ebe50debae18323308164a Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 27 Apr 2022 09:10:47 -0700 Subject: [PATCH 36/78] [jest] automatically determine run order (#130857) --- .buildkite/package-lock.json | 915 ++++++++++++++++++ .buildkite/package.json | 2 +- .buildkite/pipelines/pull_request/base.yml | 25 +- .buildkite/scripts/lifecycle/pre_command.sh | 16 +- .buildkite/scripts/steps/test/jest_env.sh | 7 + .../scripts/steps/test/jest_parallel.sh | 40 +- .../steps/test/pick_jest_config_run_order.js | 22 + .../steps/test/pick_jest_config_run_order.sh | 9 + .buildkite/yarn.lock | 180 ---- .gitignore | 1 + package.json | 2 - packages/BUILD.bazel | 2 - packages/kbn-bazel-runner/jest.config.js | 13 - packages/kbn-ci-stats-client/BUILD.bazel | 120 --- packages/kbn-ci-stats-client/README.md | 3 - packages/kbn-ci-stats-client/jest.config.js | 13 - packages/kbn-ci-stats-client/package.json | 10 - .../src/ci_stats_client.ts | 88 -- packages/kbn-ci-stats-client/src/index.ts | 9 - packages/kbn-ci-stats-client/tsconfig.json | 17 - packages/kbn-ci-stats-core/README.md | 2 +- packages/kbn-ci-stats-core/jest.config.js | 13 - packages/kbn-ci-stats-reporter/jest.config.js | 13 - .../jest.integration.config.js | 13 - packages/kbn-generate/jest.config.js | 13 - packages/kbn-jest-serializers/jest.config.js | 13 - .../kbn-kibana-json-schema/jest.config.js | 13 - packages/kbn-plugin-discovery/jest.config.js | 13 - .../kbn-shared-ux-services/jest.config.js | 13 - .../kbn-shared-ux-storybook/jest.config.js | 13 - packages/kbn-sort-package-json/jest.config.js | 13 - packages/kbn-stdio-dev-helpers/jest.config.js | 13 - packages/kbn-test/jest-preset.js | 16 +- .../kbn-test/jest_integration/jest-preset.js | 16 +- .../src/jest/run_check_jest_configs_cli.ts | 4 +- src/plugins/event_annotation/jest.config.js | 18 - src/plugins/shared_ux/jest.config.js | 19 - yarn.lock | 8 - 38 files changed, 1005 insertions(+), 715 deletions(-) create mode 100644 .buildkite/package-lock.json create mode 100644 .buildkite/scripts/steps/test/jest_env.sh create mode 100644 .buildkite/scripts/steps/test/pick_jest_config_run_order.js create mode 100644 .buildkite/scripts/steps/test/pick_jest_config_run_order.sh delete mode 100644 .buildkite/yarn.lock delete mode 100644 packages/kbn-bazel-runner/jest.config.js delete mode 100644 packages/kbn-ci-stats-client/BUILD.bazel delete mode 100644 packages/kbn-ci-stats-client/README.md delete mode 100644 packages/kbn-ci-stats-client/jest.config.js delete mode 100644 packages/kbn-ci-stats-client/package.json delete mode 100644 packages/kbn-ci-stats-client/src/ci_stats_client.ts delete mode 100644 packages/kbn-ci-stats-client/src/index.ts delete mode 100644 packages/kbn-ci-stats-client/tsconfig.json delete mode 100644 packages/kbn-ci-stats-core/jest.config.js delete mode 100644 packages/kbn-ci-stats-reporter/jest.config.js delete mode 100644 packages/kbn-eslint-plugin-imports/jest.integration.config.js delete mode 100644 packages/kbn-generate/jest.config.js delete mode 100644 packages/kbn-jest-serializers/jest.config.js delete mode 100644 packages/kbn-kibana-json-schema/jest.config.js delete mode 100644 packages/kbn-plugin-discovery/jest.config.js delete mode 100755 packages/kbn-shared-ux-services/jest.config.js delete mode 100644 packages/kbn-shared-ux-storybook/jest.config.js delete mode 100644 packages/kbn-sort-package-json/jest.config.js delete mode 100644 packages/kbn-stdio-dev-helpers/jest.config.js delete mode 100644 src/plugins/event_annotation/jest.config.js delete mode 100644 src/plugins/shared_ux/jest.config.js diff --git a/.buildkite/package-lock.json b/.buildkite/package-lock.json new file mode 100644 index 0000000000000..7d36cd285a897 --- /dev/null +++ b/.buildkite/package-lock.json @@ -0,0 +1,915 @@ +{ + "name": "kibana-buildkite", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "kibana-buildkite", + "version": "1.0.0", + "dependencies": { + "kibana-buildkite-library": "git+https://git@github.com/elastic/kibana-buildkite-library#0c02e8e8461f3bd5b6d686efe401d579d622395d" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", + "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", + "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", + "dependencies": { + "@octokit/types": "^6.34.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", + "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", + "dependencies": { + "@octokit/types": "^6.34.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/rest": { + "version": "18.12.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", + "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "dependencies": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.34.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", + "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", + "dependencies": { + "@octokit/openapi-types": "^11.2.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/kibana-buildkite-library": { + "version": "1.0.0", + "resolved": "git+https://git@github.com/elastic/kibana-buildkite-library.git#0c02e8e8461f3bd5b6d686efe401d579d622395d", + "integrity": "sha512-WgHQBBWJPu4XVDywfBZyoiIyh5mj14PUK0HmpnSOJmVNdyk3lGOTFbfSuYlEAm1ib6xVWXEf/y6W+Ww40CmAwg==", + "license": "MIT", + "dependencies": { + "@octokit/rest": "^18.10.0", + "axios": "^0.21.4", + "globby": "^11.1.0", + "js-yaml": "^4.1.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + }, + "dependencies": { + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "requires": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", + "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==" + }, + "@octokit/plugin-paginate-rest": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", + "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", + "requires": { + "@octokit/types": "^6.34.0" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "requires": {} + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", + "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", + "requires": { + "@octokit/types": "^6.34.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "18.12.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", + "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "requires": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "@octokit/types": { + "version": "6.34.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", + "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", + "requires": { + "@octokit/openapi-types": "^11.2.0" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + }, + "before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "requires": { + "reusify": "^1.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "kibana-buildkite-library": { + "version": "git+https://git@github.com/elastic/kibana-buildkite-library.git#0c02e8e8461f3bd5b6d686efe401d579d622395d", + "integrity": "sha512-WgHQBBWJPu4XVDywfBZyoiIyh5mj14PUK0HmpnSOJmVNdyk3lGOTFbfSuYlEAm1ib6xVWXEf/y6W+Ww40CmAwg==", + "from": "kibana-buildkite-library@git+https://git@github.com/elastic/kibana-buildkite-library#0c02e8e8461f3bd5b6d686efe401d579d622395d", + "requires": { + "@octokit/rest": "^18.10.0", + "axios": "^0.21.4", + "globby": "^11.1.0", + "js-yaml": "^4.1.0" + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/.buildkite/package.json b/.buildkite/package.json index 385c9f2429f79..5fa386f9b54ec 100644 --- a/.buildkite/package.json +++ b/.buildkite/package.json @@ -3,6 +3,6 @@ "version": "1.0.0", "private": true, "dependencies": { - "kibana-buildkite-library": "elastic/kibana-buildkite-library" + "kibana-buildkite-library": "git+https://git@github.com/elastic/kibana-buildkite-library#0c02e8e8461f3bd5b6d686efe401d579d622395d" } } diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index fc12a96964fb2..4b4104f18c627 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -123,29 +123,14 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/test/jest.sh - label: 'Jest Tests' - parallelism: 8 + - command: .buildkite/scripts/steps/test/pick_jest_config_run_order.sh + label: 'Pick Jest Config Run Order' agents: - queue: n2-4-spot - timeout_in_minutes: 90 - key: jest - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/test/jest_integration.sh - label: 'Jest Integration Tests' - parallelism: 3 - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - key: jest-integration + queue: kibana-default retry: automatic: - - exit_status: '-1' - limit: 3 + - exit_status: '*' + limit: 1 - command: .buildkite/scripts/steps/test/api_integration.sh label: 'API Integration Tests' diff --git a/.buildkite/scripts/lifecycle/pre_command.sh b/.buildkite/scripts/lifecycle/pre_command.sh index e7a176a5c2666..8f3776db3ca6b 100755 --- a/.buildkite/scripts/lifecycle/pre_command.sh +++ b/.buildkite/scripts/lifecycle/pre_command.sh @@ -9,21 +9,7 @@ export BUILDKITE_TOKEN echo '--- Install buildkite dependencies' cd '.buildkite' - -# If this yarn install is terminated early, e.g. if the build is cancelled in buildkite, -# A node module could end up in a bad state that can cause all future builds to fail -# So, let's cache clean and try again to make sure that's not what caused the error -install_deps() { - yarn install --production --pure-lockfile - EXIT=$? - if [[ "$EXIT" != "0" ]]; then - yarn cache clean - fi - return $EXIT -} - -retry 5 15 install_deps - +retry 5 15 npm ci cd .. echo '--- Agent Debug/SSH Info' diff --git a/.buildkite/scripts/steps/test/jest_env.sh b/.buildkite/scripts/steps/test/jest_env.sh new file mode 100644 index 0000000000000..80e88bebba184 --- /dev/null +++ b/.buildkite/scripts/steps/test/jest_env.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# keys used to associate test group data in ci-stats with Jest execution order +export TEST_GROUP_TYPE_UNIT="Jest Unit Tests" +export TEST_GROUP_TYPE_INTEGRATION="Jest Integration Tests" diff --git a/.buildkite/scripts/steps/test/jest_parallel.sh b/.buildkite/scripts/steps/test/jest_parallel.sh index 27eb9e9216365..86c685d82c7e7 100755 --- a/.buildkite/scripts/steps/test/jest_parallel.sh +++ b/.buildkite/scripts/steps/test/jest_parallel.sh @@ -2,36 +2,38 @@ set -uo pipefail -JOB=$BUILDKITE_PARALLEL_JOB -JOB_COUNT=$BUILDKITE_PARALLEL_JOB_COUNT +source .buildkite/scripts/steps/test/jest_env.sh -# a jest failure will result in the script returning an exit code of 10 +export JOB=$BUILDKITE_PARALLEL_JOB -i=0 +# a jest failure will result in the script returning an exit code of 10 exitCode=0 -# run unit tests in parallel if [[ "$1" == 'jest.config.js' ]]; then + # run unit tests in parallel parallelism="-w2" + TEST_TYPE="unit" else + # run integration tests in-band parallelism="--runInBand" + TEST_TYPE="integration" fi +export TEST_TYPE +echo "--- downloading integration test run order" +buildkite-agent artifact download jest_run_order.json . +configs=$(jq -r 'getpath([env.TEST_TYPE]) | .groups[env.JOB | tonumber].names | .[]' jest_run_order.json) + while read -r config; do - if [ "$((i % JOB_COUNT))" -eq "$JOB" ]; then - echo "--- $ node scripts/jest --config $config" - node --max-old-space-size=14336 ./scripts/jest --config="$config" "$parallelism" --coverage=false --passWithNoTests - lastCode=$? - - if [ $lastCode -ne 0 ]; then - exitCode=10 - echo "Jest exited with code $lastCode" - echo "^^^ +++" - fi + echo "--- $ node scripts/jest --config $config" + NODE_OPTIONS="--max-old-space-size=14336" node ./scripts/jest --config="$config" "$parallelism" --coverage=false --passWithNoTests + lastCode=$? + + if [ $lastCode -ne 0 ]; then + exitCode=10 + echo "Jest exited with code $lastCode" + echo "^^^ +++" fi - - ((i=i+1)) -# uses heredoc to avoid the while loop being in a sub-shell thus unable to overwrite exitCode -done <<< "$(find src x-pack packages -name "$1" -not -path "*/__fixtures__/*" | sort)" +done <<< "$configs" exit $exitCode diff --git a/.buildkite/scripts/steps/test/pick_jest_config_run_order.js b/.buildkite/scripts/steps/test/pick_jest_config_run_order.js new file mode 100644 index 0000000000000..a5fa499419ab5 --- /dev/null +++ b/.buildkite/scripts/steps/test/pick_jest_config_run_order.js @@ -0,0 +1,22 @@ +/* + * 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 { CiStats } = require('kibana-buildkite-library'); + +(async () => { + try { + await CiStats.pickTestGroupRunOrder(); + } catch (ex) { + console.error('CI Stats Error', ex.message); + if (ex.response) { + console.error('HTTP Error Response Status', ex.response.status); + console.error('HTTP Error Response Body', ex.response.data); + } + process.exit(1); + } +})(); diff --git a/.buildkite/scripts/steps/test/pick_jest_config_run_order.sh b/.buildkite/scripts/steps/test/pick_jest_config_run_order.sh new file mode 100644 index 0000000000000..37d4e629c90b0 --- /dev/null +++ b/.buildkite/scripts/steps/test/pick_jest_config_run_order.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh +source .buildkite/scripts/steps/test/jest_env.sh + +echo '--- Pick Jest Config Run Order' +node "$(dirname "${0}")/pick_jest_config_run_order.js" diff --git a/.buildkite/yarn.lock b/.buildkite/yarn.lock deleted file mode 100644 index c5a4e404ba970..0000000000000 --- a/.buildkite/yarn.lock +++ /dev/null @@ -1,180 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@octokit/auth-token@^2.4.4": - version "2.5.0" - resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz" - integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== - dependencies: - "@octokit/types" "^6.0.3" - -"@octokit/core@^3.5.1": - version "3.5.1" - resolved "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz" - integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.6.0" - "@octokit/request-error" "^2.0.5" - "@octokit/types" "^6.0.3" - before-after-hook "^2.2.0" - universal-user-agent "^6.0.0" - -"@octokit/endpoint@^6.0.1": - version "6.0.12" - resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz" - integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== - dependencies: - "@octokit/types" "^6.0.3" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/graphql@^4.5.8": - version "4.8.0" - resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz" - integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== - dependencies: - "@octokit/request" "^5.6.0" - "@octokit/types" "^6.0.3" - universal-user-agent "^6.0.0" - -"@octokit/openapi-types@^11.2.0": - version "11.2.0" - resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz" - integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== - -"@octokit/plugin-paginate-rest@^2.16.8": - version "2.17.0" - resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz" - integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== - dependencies: - "@octokit/types" "^6.34.0" - -"@octokit/plugin-request-log@^1.0.4": - version "1.0.4" - resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz" - integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== - -"@octokit/plugin-rest-endpoint-methods@^5.12.0": - version "5.13.0" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz" - integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== - dependencies: - "@octokit/types" "^6.34.0" - deprecation "^2.3.1" - -"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz" - integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== - dependencies: - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^5.6.0": - version "5.6.2" - resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz" - integrity sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.1.0" - "@octokit/types" "^6.16.1" - is-plain-object "^5.0.0" - node-fetch "^2.6.1" - universal-user-agent "^6.0.0" - -"@octokit/rest@^18.10.0": - version "18.12.0" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz" - integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== - dependencies: - "@octokit/core" "^3.5.1" - "@octokit/plugin-paginate-rest" "^2.16.8" - "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^5.12.0" - -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": - version "6.34.0" - resolved "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz" - integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== - dependencies: - "@octokit/openapi-types" "^11.2.0" - -axios@^0.21.4: - version "0.21.4" - resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== - dependencies: - follow-redirects "^1.14.0" - -before-after-hook@^2.2.0: - version "2.2.2" - resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz" - integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== - -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - -follow-redirects@^1.14.0: - version "1.14.5" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz" - integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA== - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - -kibana-buildkite-library@elastic/kibana-buildkite-library: - version "1.0.0" - resolved "https://codeload.github.com/elastic/kibana-buildkite-library/tar.gz/bd0bec4c7af5f64a12c781d03cedb9fb2386bfbd" - dependencies: - "@octokit/rest" "^18.10.0" - axios "^0.21.4" - -node-fetch@^2.6.1: - version "2.6.6" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz" - integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA== - dependencies: - whatwg-url "^5.0.0" - -once@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= diff --git a/.gitignore b/.gitignore index 7c20367dfe6de..fac261b2a97ea 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ selenium *.swo *.out package-lock.json +!/.buildkite/package-lock.json .yo-rc.json .vscode *.sublime-* diff --git a/package.json b/package.json index 24c03762b1bed..d6d4a2225a08c 100644 --- a/package.json +++ b/package.json @@ -484,7 +484,6 @@ "@kbn/babel-preset": "link:bazel-bin/packages/kbn-babel-preset", "@kbn/bazel-packages": "link:bazel-bin/packages/kbn-bazel-packages", "@kbn/bazel-runner": "link:bazel-bin/packages/kbn-bazel-runner", - "@kbn/ci-stats-client": "link:bazel-bin/packages/kbn-ci-stats-client", "@kbn/ci-stats-core": "link:bazel-bin/packages/kbn-ci-stats-core", "@kbn/ci-stats-reporter": "link:bazel-bin/packages/kbn-ci-stats-reporter", "@kbn/cli-dev-mode": "link:bazel-bin/packages/kbn-cli-dev-mode", @@ -613,7 +612,6 @@ "@types/kbn__axe-config": "link:bazel-bin/packages/kbn-axe-config/npm_module_types", "@types/kbn__bazel-packages": "link:bazel-bin/packages/kbn-bazel-packages/npm_module_types", "@types/kbn__bazel-runner": "link:bazel-bin/packages/kbn-bazel-runner/npm_module_types", - "@types/kbn__ci-stats-client": "link:bazel-bin/packages/kbn-ci-stats-client/npm_module_types", "@types/kbn__ci-stats-core": "link:bazel-bin/packages/kbn-ci-stats-core/npm_module_types", "@types/kbn__ci-stats-reporter": "link:bazel-bin/packages/kbn-ci-stats-reporter/npm_module_types", "@types/kbn__cli-dev-mode": "link:bazel-bin/packages/kbn-cli-dev-mode/npm_module_types", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 567e5dd65d664..43c80fc882159 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -26,7 +26,6 @@ filegroup( "//packages/kbn-babel-preset:build", "//packages/kbn-bazel-packages:build", "//packages/kbn-bazel-runner:build", - "//packages/kbn-ci-stats-client:build", "//packages/kbn-ci-stats-core:build", "//packages/kbn-ci-stats-reporter:build", "//packages/kbn-cli-dev-mode:build", @@ -126,7 +125,6 @@ filegroup( "//packages/kbn-axe-config:build_types", "//packages/kbn-bazel-packages:build_types", "//packages/kbn-bazel-runner:build_types", - "//packages/kbn-ci-stats-client:build_types", "//packages/kbn-ci-stats-core:build_types", "//packages/kbn-ci-stats-reporter:build_types", "//packages/kbn-cli-dev-mode:build_types", diff --git a/packages/kbn-bazel-runner/jest.config.js b/packages/kbn-bazel-runner/jest.config.js deleted file mode 100644 index 4d4f77a8f43f3..0000000000000 --- a/packages/kbn-bazel-runner/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-bazel-runner'], -}; diff --git a/packages/kbn-ci-stats-client/BUILD.bazel b/packages/kbn-ci-stats-client/BUILD.bazel deleted file mode 100644 index 7017adc604416..0000000000000 --- a/packages/kbn-ci-stats-client/BUILD.bazel +++ /dev/null @@ -1,120 +0,0 @@ -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", "pkg_npm", "pkg_npm_types", "ts_project") - -PKG_DIRNAME = "kbn-ci-stats-client" -PKG_REQUIRE_NAME = "@kbn/ci-stats-client" - -SOURCE_FILES = glob( - [ - "src/**/*.ts", - ], - exclude = [ - "**/*.test.*", - ], -) - -SRCS = SOURCE_FILES - -filegroup( - name = "srcs", - srcs = SRCS, -) - -NPM_MODULE_EXTRA_FILES = [ - "package.json", -] - -# In this array place runtime dependencies, including other packages and NPM packages -# which must be available for this code to run. -# -# To reference other packages use: -# "//repo/relative/path/to/package" -# eg. "//packages/kbn-utils" -# -# To reference a NPM package use: -# "@npm//name-of-package" -# eg. "@npm//lodash" -RUNTIME_DEPS = [ - "@npm//axios", - "//packages/kbn-ci-stats-core", - "//packages/kbn-tooling-log", -] - -# In this array place dependencies necessary to build the types, which will include the -# :npm_module_types target of other packages and packages from NPM, including @types/* -# packages. -# -# To reference the types for another package use: -# "//repo/relative/path/to/package:npm_module_types" -# eg. "//packages/kbn-utils:npm_module_types" -# -# References to NPM packages work the same as RUNTIME_DEPS -TYPES_DEPS = [ - "@npm//@types/node", - "@npm//@types/jest", - "@npm//axios", - "//packages/kbn-ci-stats-core:npm_module_types", - "//packages/kbn-tooling-log:npm_module_types", -] - -jsts_transpiler( - name = "target_node", - srcs = SRCS, - build_pkg_name = package_name(), -) - -ts_config( - name = "tsconfig", - src = "tsconfig.json", - deps = [ - "//:tsconfig.base.json", - "//:tsconfig.bazel.json", - ], -) - -ts_project( - name = "tsc_types", - args = ['--pretty'], - srcs = SRCS, - deps = TYPES_DEPS, - declaration = True, - emit_declaration_only = True, - out_dir = "target_types", - root_dir = "src", - tsconfig = ":tsconfig", -) - -js_library( - name = PKG_DIRNAME, - srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], - package_name = PKG_REQUIRE_NAME, - visibility = ["//visibility:public"], -) - -pkg_npm( - name = "npm_module", - deps = [":" + PKG_DIRNAME], -) - -filegroup( - name = "build", - srcs = [":npm_module"], - visibility = ["//visibility:public"], -) - -pkg_npm_types( - name = "npm_module_types", - srcs = SRCS, - deps = [":tsc_types"], - package_name = PKG_REQUIRE_NAME, - tsconfig = ":tsconfig", - visibility = ["//visibility:public"], -) - -filegroup( - name = "build_types", - srcs = [":npm_module_types"], - visibility = ["//visibility:public"], -) diff --git a/packages/kbn-ci-stats-client/README.md b/packages/kbn-ci-stats-client/README.md deleted file mode 100644 index d1f6c59e978c9..0000000000000 --- a/packages/kbn-ci-stats-client/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @kbn/ci-stats-client - -Client for reading data stored at https://ci-stats.kibana.dev \ No newline at end of file diff --git a/packages/kbn-ci-stats-client/jest.config.js b/packages/kbn-ci-stats-client/jest.config.js deleted file mode 100644 index d855d7886d0d7..0000000000000 --- a/packages/kbn-ci-stats-client/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-ci-stats-client'], -}; diff --git a/packages/kbn-ci-stats-client/package.json b/packages/kbn-ci-stats-client/package.json deleted file mode 100644 index 709f6a3454d59..0000000000000 --- a/packages/kbn-ci-stats-client/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "@kbn/ci-stats-client", - "private": true, - "version": "1.0.0", - "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } -} diff --git a/packages/kbn-ci-stats-client/src/ci_stats_client.ts b/packages/kbn-ci-stats-client/src/ci_stats_client.ts deleted file mode 100644 index a7ab6f1cc4cb8..0000000000000 --- a/packages/kbn-ci-stats-client/src/ci_stats_client.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 Axios from 'axios'; -import { ToolingLog } from '@kbn/tooling-log'; - -import { parseConfig, Config, CiStatsMetadata } from '@kbn/ci-stats-core'; - -interface LatestTestGroupStatsOptions { - /** The Kibana branch to get stats for, eg "main" */ - branch: string; - /** The CI job names to filter builds by, eg "kibana-hourly" */ - ciJobNames: string[]; - /** Filter test groups by group type */ - testGroupType?: string; -} - -interface CompleteSuccessBuildSource { - jobName: string; - jobRunner: string; - completedAt: string; - commit: string; - startedAt: string; - branch: string; - result: 'SUCCESS'; - jobId: string; - targetBranch: string | null; - fromKibanaCiProduction: boolean; - requiresValidMetrics: boolean | null; - jobUrl: string; - mergeBase: string | null; -} - -interface TestGroupSource { - '@timestamp': string; - buildId: string; - name: string; - type: string; - startTime: string; - durationMs: number; - meta: CiStatsMetadata; -} - -interface LatestTestGroupStatsResp { - build: CompleteSuccessBuildSource & { id: string }; - testGroups: Array; -} - -export class CiStatsClient { - /** - * Create a CiStatsReporter by inspecting the ENV for the necessary config - */ - static fromEnv(log: ToolingLog) { - return new CiStatsClient(parseConfig(log)); - } - - constructor(private readonly config?: Config) {} - - isEnabled() { - return !!this.config?.apiToken; - } - - async getLatestTestGroupStats(options: LatestTestGroupStatsOptions) { - if (!this.config || !this.config.apiToken) { - throw new Error('No ciStats config available, call `isEnabled()` before using the client'); - } - - const resp = await Axios.request({ - baseURL: 'https://ci-stats.kibana.dev', - url: '/v1/test_group_stats', - params: { - branch: options.branch, - ci_job_name: options.ciJobNames.join(','), - test_group_type: options.testGroupType, - }, - headers: { - Authentication: `token ${this.config.apiToken}`, - }, - }); - - return resp.data; - } -} diff --git a/packages/kbn-ci-stats-client/src/index.ts b/packages/kbn-ci-stats-client/src/index.ts deleted file mode 100644 index ac32c69b9f7b7..0000000000000 --- a/packages/kbn-ci-stats-client/src/index.ts +++ /dev/null @@ -1,9 +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. - */ - -export { CiStatsClient } from './ci_stats_client'; diff --git a/packages/kbn-ci-stats-client/tsconfig.json b/packages/kbn-ci-stats-client/tsconfig.json deleted file mode 100644 index a8cfc2cceb08b..0000000000000 --- a/packages/kbn-ci-stats-client/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tsconfig.bazel.json", - "compilerOptions": { - "declaration": true, - "emitDeclarationOnly": true, - "outDir": "target_types", - "rootDir": "src", - "stripInternal": false, - "types": [ - "jest", - "node" - ] - }, - "include": [ - "src/**/*" - ] -} diff --git a/packages/kbn-ci-stats-core/README.md b/packages/kbn-ci-stats-core/README.md index b2e34a492b745..741728394aa83 100644 --- a/packages/kbn-ci-stats-core/README.md +++ b/packages/kbn-ci-stats-core/README.md @@ -1,3 +1,3 @@ # @kbn/ci-stats-core -Config and types used by `@kbn/ci-stats-client` and `@kbn/ci-stats-reporter`. \ No newline at end of file +Config and types used by `@kbn/ci-stats-reporter`. \ No newline at end of file diff --git a/packages/kbn-ci-stats-core/jest.config.js b/packages/kbn-ci-stats-core/jest.config.js deleted file mode 100644 index 0feb7b4e1b872..0000000000000 --- a/packages/kbn-ci-stats-core/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-ci-stats-core'], -}; diff --git a/packages/kbn-ci-stats-reporter/jest.config.js b/packages/kbn-ci-stats-reporter/jest.config.js deleted file mode 100644 index bf58324f440a3..0000000000000 --- a/packages/kbn-ci-stats-reporter/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-ci-stats-reporter'], -}; diff --git a/packages/kbn-eslint-plugin-imports/jest.integration.config.js b/packages/kbn-eslint-plugin-imports/jest.integration.config.js deleted file mode 100644 index e7d05a6222c88..0000000000000 --- a/packages/kbn-eslint-plugin-imports/jest.integration.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_integration_node', - rootDir: '../..', - roots: ['/packages/kbn-eslint-plugin-imports'], -}; diff --git a/packages/kbn-generate/jest.config.js b/packages/kbn-generate/jest.config.js deleted file mode 100644 index b72f891cfb1a7..0000000000000 --- a/packages/kbn-generate/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-generate'], -}; diff --git a/packages/kbn-jest-serializers/jest.config.js b/packages/kbn-jest-serializers/jest.config.js deleted file mode 100644 index 23fad67c028bf..0000000000000 --- a/packages/kbn-jest-serializers/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-jest-serializers'], -}; diff --git a/packages/kbn-kibana-json-schema/jest.config.js b/packages/kbn-kibana-json-schema/jest.config.js deleted file mode 100644 index 00bc8f55adc57..0000000000000 --- a/packages/kbn-kibana-json-schema/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-kibana-json-schema'], -}; diff --git a/packages/kbn-plugin-discovery/jest.config.js b/packages/kbn-plugin-discovery/jest.config.js deleted file mode 100644 index 37d7a4f2b63a2..0000000000000 --- a/packages/kbn-plugin-discovery/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-plugin-discovery'], -}; diff --git a/packages/kbn-shared-ux-services/jest.config.js b/packages/kbn-shared-ux-services/jest.config.js deleted file mode 100755 index f1ef008d0f62d..0000000000000 --- a/packages/kbn-shared-ux-services/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['/packages/kbn-shared-ux-services'], -}; diff --git a/packages/kbn-shared-ux-storybook/jest.config.js b/packages/kbn-shared-ux-storybook/jest.config.js deleted file mode 100644 index 91285e025f069..0000000000000 --- a/packages/kbn-shared-ux-storybook/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['/packages/kbn-shared-ux-storybook'], -}; diff --git a/packages/kbn-sort-package-json/jest.config.js b/packages/kbn-sort-package-json/jest.config.js deleted file mode 100644 index ae0651be19e61..0000000000000 --- a/packages/kbn-sort-package-json/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-sort-package-json'], -}; diff --git a/packages/kbn-stdio-dev-helpers/jest.config.js b/packages/kbn-stdio-dev-helpers/jest.config.js deleted file mode 100644 index 31a8aab16c7e5..0000000000000 --- a/packages/kbn-stdio-dev-helpers/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-stdio-dev-helpers'], -}; diff --git a/packages/kbn-test/jest-preset.js b/packages/kbn-test/jest-preset.js index 6245532b00e44..fb535c2766cc4 100644 --- a/packages/kbn-test/jest-preset.js +++ b/packages/kbn-test/jest-preset.js @@ -63,12 +63,16 @@ module.exports = { rootDirectory: '.', }, ], - [ - '@kbn/test/target_node/jest/ci_stats_jest_reporter', - { - testGroupType: 'Jest Unit Tests', - }, - ], + ...(process.env.TEST_GROUP_TYPE_UNIT + ? [ + [ + '@kbn/test/target_node/jest/ci_stats_jest_reporter', + { + testGroupType: process.env.TEST_GROUP_TYPE_UNIT, + }, + ], + ] + : []), ], // The paths to modules that run some code to configure or set up the testing environment before each test diff --git a/packages/kbn-test/jest_integration/jest-preset.js b/packages/kbn-test/jest_integration/jest-preset.js index f5593e3f57fb6..1f2626aef532e 100644 --- a/packages/kbn-test/jest_integration/jest-preset.js +++ b/packages/kbn-test/jest_integration/jest-preset.js @@ -28,12 +28,16 @@ module.exports = { reportName: 'Jest Integration Tests', }, ], - [ - '@kbn/test/target_node/jest/ci_stats_jest_reporter', - { - testGroupType: 'Jest Integration Tests', - }, - ], + ...(process.env.TEST_GROUP_TYPE_INTEGRATION + ? [ + [ + '@kbn/test/target_node/jest/ci_stats_jest_reporter', + { + testGroupType: process.env.TEST_GROUP_TYPE_INTEGRATION, + }, + ], + ] + : []), ], coverageReporters: !!process.env.CI ? [['json', { file: 'jest-integration.json' }]] diff --git a/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts b/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts index 42766ad3f5a31..7a50e88b3396d 100644 --- a/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts +++ b/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts @@ -17,14 +17,14 @@ import { getAllRepoRelativeBazelPackageDirs } from '@kbn/bazel-packages'; import { JestConfigs, CONFIG_NAMES } from './configs'; const unitTestingTemplate: string = `module.exports = { - preset: '@kbn/test', + preset: '@kbn/test/jest_node', rootDir: '{{{relToRoot}}}', roots: ['/{{{modulePath}}}'], }; `; const integrationTestingTemplate: string = `module.exports = { - preset: '@kbn/test/jest_integration', + preset: '@kbn/test/jest_integration_node', rootDir: '{{{relToRoot}}}', roots: ['/{{{modulePath}}}'], }; diff --git a/src/plugins/event_annotation/jest.config.js b/src/plugins/event_annotation/jest.config.js deleted file mode 100644 index a6ea4a6b430df..0000000000000 --- a/src/plugins/event_annotation/jest.config.js +++ /dev/null @@ -1,18 +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. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../../..', - roots: ['/src/plugins/event_annotation'], - coverageDirectory: '/target/kibana-coverage/jest/src/plugins/event_annotation', - coverageReporters: ['text', 'html'], - collectCoverageFrom: [ - '/src/plugins/event_annotation/{common,public,server}/**/*.{ts,tsx}', - ], -}; diff --git a/src/plugins/shared_ux/jest.config.js b/src/plugins/shared_ux/jest.config.js deleted file mode 100644 index bc8d67e5ac35b..0000000000000 --- a/src/plugins/shared_ux/jest.config.js +++ /dev/null @@ -1,19 +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. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../../..', - roots: ['/src/plugins/shared_ux'], - transform: { - '^.+\\.stories\\.tsx?$': '@storybook/addon-storyshots/injectFileName', - }, - coverageDirectory: '/target/kibana-coverage/jest/src/plugins/shared_ux', - coverageReporters: ['text', 'html'], - collectCoverageFrom: ['/src/plugins/shared_ux/{common,public,server}/**/*.{js,ts,tsx}'], -}; diff --git a/yarn.lock b/yarn.lock index b0a457c84d3ac..e4461999d72ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2968,10 +2968,6 @@ version "0.0.0" uid "" -"@kbn/ci-stats-client@link:bazel-bin/packages/kbn-ci-stats-client": - version "0.0.0" - uid "" - "@kbn/ci-stats-core@link:bazel-bin/packages/kbn-ci-stats-core": version "0.0.0" uid "" @@ -6064,10 +6060,6 @@ version "0.0.0" uid "" -"@types/kbn__ci-stats-client@link:bazel-bin/packages/kbn-ci-stats-client/npm_module_types": - version "0.0.0" - uid "" - "@types/kbn__ci-stats-core@link:bazel-bin/packages/kbn-ci-stats-core/npm_module_types": version "0.0.0" uid "" From 57c3f1fe2a3535fadce770e6ecc97d1b2c152bde Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Wed, 27 Apr 2022 19:12:24 +0300 Subject: [PATCH 37/78] added json tab (#131065) --- .../findings_flyout/findings_flyout.tsx | 5 +++- .../findings/findings_flyout/json_tab.tsx | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/json_tab.tsx diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx index 2902d8fd7736c..e165677fd45d4 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx @@ -31,8 +31,9 @@ import * as TEXT from '../translations'; import cisLogoIcon from '../../../assets/icons/cis_logo.svg'; import k8sLogoIcon from '../../../assets/icons/k8s_logo.svg'; import { ResourceTab } from './resource_tab'; +import { JsonTab } from './json_tab'; -const tabs = ['remediation', 'resource', 'general'] as const; +const tabs = ['remediation', 'resource', 'general', 'json'] as const; const CodeBlock: React.FC> = (props) => ( @@ -80,6 +81,8 @@ const FindingsTab = ({ tab, findings }: { findings: CspFinding; tab: FindingsTab return ; case 'general': return ; + case 'json': + return ; default: assertNever(tab); } diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/json_tab.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/json_tab.tsx new file mode 100644 index 0000000000000..ed5845abf19bb --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/json_tab.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import { XJsonLang } from '@kbn/monaco'; +import { CspFinding } from '../types'; + +const offsetHeight = 120; + +export const JsonTab = ({ data }: { data: CspFinding }) => ( +
+ +
+); From f149a17871ae6b33d4d92c4e6a883741d51d0d97 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 27 Apr 2022 11:38:19 -0500 Subject: [PATCH 38/78] Revert "[jest] automatically determine run order (#130857)" This reverts commit 1a05ce5cab240299d1ebe50debae18323308164a. --- .buildkite/package-lock.json | 915 ------------------ .buildkite/package.json | 2 +- .buildkite/pipelines/pull_request/base.yml | 25 +- .buildkite/scripts/lifecycle/pre_command.sh | 16 +- .buildkite/scripts/steps/test/jest_env.sh | 7 - .../scripts/steps/test/jest_parallel.sh | 40 +- .../steps/test/pick_jest_config_run_order.js | 22 - .../steps/test/pick_jest_config_run_order.sh | 9 - .buildkite/yarn.lock | 180 ++++ .gitignore | 1 - package.json | 2 + packages/BUILD.bazel | 2 + packages/kbn-bazel-runner/jest.config.js | 13 + packages/kbn-ci-stats-client/BUILD.bazel | 120 +++ packages/kbn-ci-stats-client/README.md | 3 + packages/kbn-ci-stats-client/jest.config.js | 13 + packages/kbn-ci-stats-client/package.json | 10 + .../src/ci_stats_client.ts | 88 ++ packages/kbn-ci-stats-client/src/index.ts | 9 + packages/kbn-ci-stats-client/tsconfig.json | 17 + packages/kbn-ci-stats-core/README.md | 2 +- packages/kbn-ci-stats-core/jest.config.js | 13 + packages/kbn-ci-stats-reporter/jest.config.js | 13 + .../jest.integration.config.js | 13 + packages/kbn-generate/jest.config.js | 13 + packages/kbn-jest-serializers/jest.config.js | 13 + .../kbn-kibana-json-schema/jest.config.js | 13 + packages/kbn-plugin-discovery/jest.config.js | 13 + .../kbn-shared-ux-services/jest.config.js | 13 + .../kbn-shared-ux-storybook/jest.config.js | 13 + packages/kbn-sort-package-json/jest.config.js | 13 + packages/kbn-stdio-dev-helpers/jest.config.js | 13 + packages/kbn-test/jest-preset.js | 16 +- .../kbn-test/jest_integration/jest-preset.js | 16 +- .../src/jest/run_check_jest_configs_cli.ts | 4 +- src/plugins/event_annotation/jest.config.js | 18 + src/plugins/shared_ux/jest.config.js | 19 + yarn.lock | 8 + 38 files changed, 715 insertions(+), 1005 deletions(-) delete mode 100644 .buildkite/package-lock.json delete mode 100644 .buildkite/scripts/steps/test/jest_env.sh delete mode 100644 .buildkite/scripts/steps/test/pick_jest_config_run_order.js delete mode 100644 .buildkite/scripts/steps/test/pick_jest_config_run_order.sh create mode 100644 .buildkite/yarn.lock create mode 100644 packages/kbn-bazel-runner/jest.config.js create mode 100644 packages/kbn-ci-stats-client/BUILD.bazel create mode 100644 packages/kbn-ci-stats-client/README.md create mode 100644 packages/kbn-ci-stats-client/jest.config.js create mode 100644 packages/kbn-ci-stats-client/package.json create mode 100644 packages/kbn-ci-stats-client/src/ci_stats_client.ts create mode 100644 packages/kbn-ci-stats-client/src/index.ts create mode 100644 packages/kbn-ci-stats-client/tsconfig.json create mode 100644 packages/kbn-ci-stats-core/jest.config.js create mode 100644 packages/kbn-ci-stats-reporter/jest.config.js create mode 100644 packages/kbn-eslint-plugin-imports/jest.integration.config.js create mode 100644 packages/kbn-generate/jest.config.js create mode 100644 packages/kbn-jest-serializers/jest.config.js create mode 100644 packages/kbn-kibana-json-schema/jest.config.js create mode 100644 packages/kbn-plugin-discovery/jest.config.js create mode 100755 packages/kbn-shared-ux-services/jest.config.js create mode 100644 packages/kbn-shared-ux-storybook/jest.config.js create mode 100644 packages/kbn-sort-package-json/jest.config.js create mode 100644 packages/kbn-stdio-dev-helpers/jest.config.js create mode 100644 src/plugins/event_annotation/jest.config.js create mode 100644 src/plugins/shared_ux/jest.config.js diff --git a/.buildkite/package-lock.json b/.buildkite/package-lock.json deleted file mode 100644 index 7d36cd285a897..0000000000000 --- a/.buildkite/package-lock.json +++ /dev/null @@ -1,915 +0,0 @@ -{ - "name": "kibana-buildkite", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "kibana-buildkite", - "version": "1.0.0", - "dependencies": { - "kibana-buildkite-library": "git+https://git@github.com/elastic/kibana-buildkite-library#0c02e8e8461f3bd5b6d686efe401d579d622395d" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "dependencies": { - "@octokit/types": "^6.0.3" - } - }, - "node_modules/@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", - "dependencies": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "dependencies": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "dependencies": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", - "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==" - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", - "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", - "dependencies": { - "@octokit/types": "^6.34.0" - }, - "peerDependencies": { - "@octokit/core": ">=2" - } - }, - "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", - "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", - "dependencies": { - "@octokit/types": "^6.34.0", - "deprecation": "^2.3.1" - }, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", - "dependencies": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "node_modules/@octokit/rest": { - "version": "18.12.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", - "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", - "dependencies": { - "@octokit/core": "^3.5.1", - "@octokit/plugin-paginate-rest": "^2.16.8", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^5.12.0" - } - }, - "node_modules/@octokit/types": { - "version": "6.34.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", - "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", - "dependencies": { - "@octokit/openapi-types": "^11.2.0" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dependencies": { - "follow-redirects": "^1.14.0" - } - }, - "node_modules/before-after-hook": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", - "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/kibana-buildkite-library": { - "version": "1.0.0", - "resolved": "git+https://git@github.com/elastic/kibana-buildkite-library.git#0c02e8e8461f3bd5b6d686efe401d579d622395d", - "integrity": "sha512-WgHQBBWJPu4XVDywfBZyoiIyh5mj14PUK0HmpnSOJmVNdyk3lGOTFbfSuYlEAm1ib6xVWXEf/y6W+Ww40CmAwg==", - "license": "MIT", - "dependencies": { - "@octokit/rest": "^18.10.0", - "axios": "^0.21.4", - "globby": "^11.1.0", - "js-yaml": "^4.1.0" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - } - }, - "dependencies": { - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "requires": { - "@octokit/types": "^6.0.3" - } - }, - "@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", - "requires": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "requires": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "requires": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/openapi-types": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", - "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==" - }, - "@octokit/plugin-paginate-rest": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", - "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", - "requires": { - "@octokit/types": "^6.34.0" - } - }, - "@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "requires": {} - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", - "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", - "requires": { - "@octokit/types": "^6.34.0", - "deprecation": "^2.3.1" - } - }, - "@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", - "requires": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "requires": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/rest": { - "version": "18.12.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", - "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", - "requires": { - "@octokit/core": "^3.5.1", - "@octokit/plugin-paginate-rest": "^2.16.8", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^5.12.0" - } - }, - "@octokit/types": { - "version": "6.34.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", - "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", - "requires": { - "@octokit/openapi-types": "^11.2.0" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" - }, - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "requires": { - "follow-redirects": "^1.14.0" - } - }, - "before-after-hook": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", - "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "requires": { - "path-type": "^4.0.0" - } - }, - "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "requires": { - "reusify": "^1.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - }, - "kibana-buildkite-library": { - "version": "git+https://git@github.com/elastic/kibana-buildkite-library.git#0c02e8e8461f3bd5b6d686efe401d579d622395d", - "integrity": "sha512-WgHQBBWJPu4XVDywfBZyoiIyh5mj14PUK0HmpnSOJmVNdyk3lGOTFbfSuYlEAm1ib6xVWXEf/y6W+Ww40CmAwg==", - "from": "kibana-buildkite-library@git+https://git@github.com/elastic/kibana-buildkite-library#0c02e8e8461f3bd5b6d686efe401d579d622395d", - "requires": { - "@octokit/rest": "^18.10.0", - "axios": "^0.21.4", - "globby": "^11.1.0", - "js-yaml": "^4.1.0" - } - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, - "universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - } - } -} diff --git a/.buildkite/package.json b/.buildkite/package.json index 5fa386f9b54ec..385c9f2429f79 100644 --- a/.buildkite/package.json +++ b/.buildkite/package.json @@ -3,6 +3,6 @@ "version": "1.0.0", "private": true, "dependencies": { - "kibana-buildkite-library": "git+https://git@github.com/elastic/kibana-buildkite-library#0c02e8e8461f3bd5b6d686efe401d579d622395d" + "kibana-buildkite-library": "elastic/kibana-buildkite-library" } } diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index 4b4104f18c627..fc12a96964fb2 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -123,14 +123,29 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/test/pick_jest_config_run_order.sh - label: 'Pick Jest Config Run Order' + - command: .buildkite/scripts/steps/test/jest.sh + label: 'Jest Tests' + parallelism: 8 agents: - queue: kibana-default + queue: n2-4-spot + timeout_in_minutes: 90 + key: jest retry: automatic: - - exit_status: '*' - limit: 1 + - exit_status: '-1' + limit: 3 + + - command: .buildkite/scripts/steps/test/jest_integration.sh + label: 'Jest Integration Tests' + parallelism: 3 + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + key: jest-integration + retry: + automatic: + - exit_status: '-1' + limit: 3 - command: .buildkite/scripts/steps/test/api_integration.sh label: 'API Integration Tests' diff --git a/.buildkite/scripts/lifecycle/pre_command.sh b/.buildkite/scripts/lifecycle/pre_command.sh index 8f3776db3ca6b..e7a176a5c2666 100755 --- a/.buildkite/scripts/lifecycle/pre_command.sh +++ b/.buildkite/scripts/lifecycle/pre_command.sh @@ -9,7 +9,21 @@ export BUILDKITE_TOKEN echo '--- Install buildkite dependencies' cd '.buildkite' -retry 5 15 npm ci + +# If this yarn install is terminated early, e.g. if the build is cancelled in buildkite, +# A node module could end up in a bad state that can cause all future builds to fail +# So, let's cache clean and try again to make sure that's not what caused the error +install_deps() { + yarn install --production --pure-lockfile + EXIT=$? + if [[ "$EXIT" != "0" ]]; then + yarn cache clean + fi + return $EXIT +} + +retry 5 15 install_deps + cd .. echo '--- Agent Debug/SSH Info' diff --git a/.buildkite/scripts/steps/test/jest_env.sh b/.buildkite/scripts/steps/test/jest_env.sh deleted file mode 100644 index 80e88bebba184..0000000000000 --- a/.buildkite/scripts/steps/test/jest_env.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# keys used to associate test group data in ci-stats with Jest execution order -export TEST_GROUP_TYPE_UNIT="Jest Unit Tests" -export TEST_GROUP_TYPE_INTEGRATION="Jest Integration Tests" diff --git a/.buildkite/scripts/steps/test/jest_parallel.sh b/.buildkite/scripts/steps/test/jest_parallel.sh index 86c685d82c7e7..27eb9e9216365 100755 --- a/.buildkite/scripts/steps/test/jest_parallel.sh +++ b/.buildkite/scripts/steps/test/jest_parallel.sh @@ -2,38 +2,36 @@ set -uo pipefail -source .buildkite/scripts/steps/test/jest_env.sh - -export JOB=$BUILDKITE_PARALLEL_JOB +JOB=$BUILDKITE_PARALLEL_JOB +JOB_COUNT=$BUILDKITE_PARALLEL_JOB_COUNT # a jest failure will result in the script returning an exit code of 10 + +i=0 exitCode=0 +# run unit tests in parallel if [[ "$1" == 'jest.config.js' ]]; then - # run unit tests in parallel parallelism="-w2" - TEST_TYPE="unit" else - # run integration tests in-band parallelism="--runInBand" - TEST_TYPE="integration" fi -export TEST_TYPE -echo "--- downloading integration test run order" -buildkite-agent artifact download jest_run_order.json . -configs=$(jq -r 'getpath([env.TEST_TYPE]) | .groups[env.JOB | tonumber].names | .[]' jest_run_order.json) - while read -r config; do - echo "--- $ node scripts/jest --config $config" - NODE_OPTIONS="--max-old-space-size=14336" node ./scripts/jest --config="$config" "$parallelism" --coverage=false --passWithNoTests - lastCode=$? - - if [ $lastCode -ne 0 ]; then - exitCode=10 - echo "Jest exited with code $lastCode" - echo "^^^ +++" + if [ "$((i % JOB_COUNT))" -eq "$JOB" ]; then + echo "--- $ node scripts/jest --config $config" + node --max-old-space-size=14336 ./scripts/jest --config="$config" "$parallelism" --coverage=false --passWithNoTests + lastCode=$? + + if [ $lastCode -ne 0 ]; then + exitCode=10 + echo "Jest exited with code $lastCode" + echo "^^^ +++" + fi fi -done <<< "$configs" + + ((i=i+1)) +# uses heredoc to avoid the while loop being in a sub-shell thus unable to overwrite exitCode +done <<< "$(find src x-pack packages -name "$1" -not -path "*/__fixtures__/*" | sort)" exit $exitCode diff --git a/.buildkite/scripts/steps/test/pick_jest_config_run_order.js b/.buildkite/scripts/steps/test/pick_jest_config_run_order.js deleted file mode 100644 index a5fa499419ab5..0000000000000 --- a/.buildkite/scripts/steps/test/pick_jest_config_run_order.js +++ /dev/null @@ -1,22 +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. - */ - -const { CiStats } = require('kibana-buildkite-library'); - -(async () => { - try { - await CiStats.pickTestGroupRunOrder(); - } catch (ex) { - console.error('CI Stats Error', ex.message); - if (ex.response) { - console.error('HTTP Error Response Status', ex.response.status); - console.error('HTTP Error Response Body', ex.response.data); - } - process.exit(1); - } -})(); diff --git a/.buildkite/scripts/steps/test/pick_jest_config_run_order.sh b/.buildkite/scripts/steps/test/pick_jest_config_run_order.sh deleted file mode 100644 index 37d4e629c90b0..0000000000000 --- a/.buildkite/scripts/steps/test/pick_jest_config_run_order.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/common/util.sh -source .buildkite/scripts/steps/test/jest_env.sh - -echo '--- Pick Jest Config Run Order' -node "$(dirname "${0}")/pick_jest_config_run_order.js" diff --git a/.buildkite/yarn.lock b/.buildkite/yarn.lock new file mode 100644 index 0000000000000..c5a4e404ba970 --- /dev/null +++ b/.buildkite/yarn.lock @@ -0,0 +1,180 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@octokit/auth-token@^2.4.4": + version "2.5.0" + resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz" + integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== + dependencies: + "@octokit/types" "^6.0.3" + +"@octokit/core@^3.5.1": + version "3.5.1" + resolved "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz" + integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== + dependencies: + "@octokit/auth-token" "^2.4.4" + "@octokit/graphql" "^4.5.8" + "@octokit/request" "^5.6.0" + "@octokit/request-error" "^2.0.5" + "@octokit/types" "^6.0.3" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + +"@octokit/endpoint@^6.0.1": + version "6.0.12" + resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz" + integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== + dependencies: + "@octokit/types" "^6.0.3" + is-plain-object "^5.0.0" + universal-user-agent "^6.0.0" + +"@octokit/graphql@^4.5.8": + version "4.8.0" + resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz" + integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== + dependencies: + "@octokit/request" "^5.6.0" + "@octokit/types" "^6.0.3" + universal-user-agent "^6.0.0" + +"@octokit/openapi-types@^11.2.0": + version "11.2.0" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz" + integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== + +"@octokit/plugin-paginate-rest@^2.16.8": + version "2.17.0" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz" + integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== + dependencies: + "@octokit/types" "^6.34.0" + +"@octokit/plugin-request-log@^1.0.4": + version "1.0.4" + resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz" + integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== + +"@octokit/plugin-rest-endpoint-methods@^5.12.0": + version "5.13.0" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz" + integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== + dependencies: + "@octokit/types" "^6.34.0" + deprecation "^2.3.1" + +"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz" + integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== + dependencies: + "@octokit/types" "^6.0.3" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request@^5.6.0": + version "5.6.2" + resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz" + integrity sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA== + dependencies: + "@octokit/endpoint" "^6.0.1" + "@octokit/request-error" "^2.1.0" + "@octokit/types" "^6.16.1" + is-plain-object "^5.0.0" + node-fetch "^2.6.1" + universal-user-agent "^6.0.0" + +"@octokit/rest@^18.10.0": + version "18.12.0" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz" + integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== + dependencies: + "@octokit/core" "^3.5.1" + "@octokit/plugin-paginate-rest" "^2.16.8" + "@octokit/plugin-request-log" "^1.0.4" + "@octokit/plugin-rest-endpoint-methods" "^5.12.0" + +"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": + version "6.34.0" + resolved "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz" + integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== + dependencies: + "@octokit/openapi-types" "^11.2.0" + +axios@^0.21.4: + version "0.21.4" + resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + +before-after-hook@^2.2.0: + version "2.2.2" + resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz" + integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== + +deprecation@^2.0.0, deprecation@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + +follow-redirects@^1.14.0: + version "1.14.5" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz" + integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA== + +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +kibana-buildkite-library@elastic/kibana-buildkite-library: + version "1.0.0" + resolved "https://codeload.github.com/elastic/kibana-buildkite-library/tar.gz/bd0bec4c7af5f64a12c781d03cedb9fb2386bfbd" + dependencies: + "@octokit/rest" "^18.10.0" + axios "^0.21.4" + +node-fetch@^2.6.1: + version "2.6.6" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz" + integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA== + dependencies: + whatwg-url "^5.0.0" + +once@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + +universal-user-agent@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" + integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= diff --git a/.gitignore b/.gitignore index fac261b2a97ea..7c20367dfe6de 100644 --- a/.gitignore +++ b/.gitignore @@ -57,7 +57,6 @@ selenium *.swo *.out package-lock.json -!/.buildkite/package-lock.json .yo-rc.json .vscode *.sublime-* diff --git a/package.json b/package.json index d6d4a2225a08c..24c03762b1bed 100644 --- a/package.json +++ b/package.json @@ -484,6 +484,7 @@ "@kbn/babel-preset": "link:bazel-bin/packages/kbn-babel-preset", "@kbn/bazel-packages": "link:bazel-bin/packages/kbn-bazel-packages", "@kbn/bazel-runner": "link:bazel-bin/packages/kbn-bazel-runner", + "@kbn/ci-stats-client": "link:bazel-bin/packages/kbn-ci-stats-client", "@kbn/ci-stats-core": "link:bazel-bin/packages/kbn-ci-stats-core", "@kbn/ci-stats-reporter": "link:bazel-bin/packages/kbn-ci-stats-reporter", "@kbn/cli-dev-mode": "link:bazel-bin/packages/kbn-cli-dev-mode", @@ -612,6 +613,7 @@ "@types/kbn__axe-config": "link:bazel-bin/packages/kbn-axe-config/npm_module_types", "@types/kbn__bazel-packages": "link:bazel-bin/packages/kbn-bazel-packages/npm_module_types", "@types/kbn__bazel-runner": "link:bazel-bin/packages/kbn-bazel-runner/npm_module_types", + "@types/kbn__ci-stats-client": "link:bazel-bin/packages/kbn-ci-stats-client/npm_module_types", "@types/kbn__ci-stats-core": "link:bazel-bin/packages/kbn-ci-stats-core/npm_module_types", "@types/kbn__ci-stats-reporter": "link:bazel-bin/packages/kbn-ci-stats-reporter/npm_module_types", "@types/kbn__cli-dev-mode": "link:bazel-bin/packages/kbn-cli-dev-mode/npm_module_types", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 43c80fc882159..567e5dd65d664 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -26,6 +26,7 @@ filegroup( "//packages/kbn-babel-preset:build", "//packages/kbn-bazel-packages:build", "//packages/kbn-bazel-runner:build", + "//packages/kbn-ci-stats-client:build", "//packages/kbn-ci-stats-core:build", "//packages/kbn-ci-stats-reporter:build", "//packages/kbn-cli-dev-mode:build", @@ -125,6 +126,7 @@ filegroup( "//packages/kbn-axe-config:build_types", "//packages/kbn-bazel-packages:build_types", "//packages/kbn-bazel-runner:build_types", + "//packages/kbn-ci-stats-client:build_types", "//packages/kbn-ci-stats-core:build_types", "//packages/kbn-ci-stats-reporter:build_types", "//packages/kbn-cli-dev-mode:build_types", diff --git a/packages/kbn-bazel-runner/jest.config.js b/packages/kbn-bazel-runner/jest.config.js new file mode 100644 index 0000000000000..4d4f77a8f43f3 --- /dev/null +++ b/packages/kbn-bazel-runner/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-bazel-runner'], +}; diff --git a/packages/kbn-ci-stats-client/BUILD.bazel b/packages/kbn-ci-stats-client/BUILD.bazel new file mode 100644 index 0000000000000..7017adc604416 --- /dev/null +++ b/packages/kbn-ci-stats-client/BUILD.bazel @@ -0,0 +1,120 @@ +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", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "kbn-ci-stats-client" +PKG_REQUIRE_NAME = "@kbn/ci-stats-client" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "@npm//axios", + "//packages/kbn-ci-stats-core", + "//packages/kbn-tooling-log", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//axios", + "//packages/kbn-ci-stats-core:npm_module_types", + "//packages/kbn-tooling-log:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-ci-stats-client/README.md b/packages/kbn-ci-stats-client/README.md new file mode 100644 index 0000000000000..d1f6c59e978c9 --- /dev/null +++ b/packages/kbn-ci-stats-client/README.md @@ -0,0 +1,3 @@ +# @kbn/ci-stats-client + +Client for reading data stored at https://ci-stats.kibana.dev \ No newline at end of file diff --git a/packages/kbn-ci-stats-client/jest.config.js b/packages/kbn-ci-stats-client/jest.config.js new file mode 100644 index 0000000000000..d855d7886d0d7 --- /dev/null +++ b/packages/kbn-ci-stats-client/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-ci-stats-client'], +}; diff --git a/packages/kbn-ci-stats-client/package.json b/packages/kbn-ci-stats-client/package.json new file mode 100644 index 0000000000000..709f6a3454d59 --- /dev/null +++ b/packages/kbn-ci-stats-client/package.json @@ -0,0 +1,10 @@ +{ + "name": "@kbn/ci-stats-client", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0", + "kibana": { + "devOnly": true + } +} diff --git a/packages/kbn-ci-stats-client/src/ci_stats_client.ts b/packages/kbn-ci-stats-client/src/ci_stats_client.ts new file mode 100644 index 0000000000000..a7ab6f1cc4cb8 --- /dev/null +++ b/packages/kbn-ci-stats-client/src/ci_stats_client.ts @@ -0,0 +1,88 @@ +/* + * 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 Axios from 'axios'; +import { ToolingLog } from '@kbn/tooling-log'; + +import { parseConfig, Config, CiStatsMetadata } from '@kbn/ci-stats-core'; + +interface LatestTestGroupStatsOptions { + /** The Kibana branch to get stats for, eg "main" */ + branch: string; + /** The CI job names to filter builds by, eg "kibana-hourly" */ + ciJobNames: string[]; + /** Filter test groups by group type */ + testGroupType?: string; +} + +interface CompleteSuccessBuildSource { + jobName: string; + jobRunner: string; + completedAt: string; + commit: string; + startedAt: string; + branch: string; + result: 'SUCCESS'; + jobId: string; + targetBranch: string | null; + fromKibanaCiProduction: boolean; + requiresValidMetrics: boolean | null; + jobUrl: string; + mergeBase: string | null; +} + +interface TestGroupSource { + '@timestamp': string; + buildId: string; + name: string; + type: string; + startTime: string; + durationMs: number; + meta: CiStatsMetadata; +} + +interface LatestTestGroupStatsResp { + build: CompleteSuccessBuildSource & { id: string }; + testGroups: Array; +} + +export class CiStatsClient { + /** + * Create a CiStatsReporter by inspecting the ENV for the necessary config + */ + static fromEnv(log: ToolingLog) { + return new CiStatsClient(parseConfig(log)); + } + + constructor(private readonly config?: Config) {} + + isEnabled() { + return !!this.config?.apiToken; + } + + async getLatestTestGroupStats(options: LatestTestGroupStatsOptions) { + if (!this.config || !this.config.apiToken) { + throw new Error('No ciStats config available, call `isEnabled()` before using the client'); + } + + const resp = await Axios.request({ + baseURL: 'https://ci-stats.kibana.dev', + url: '/v1/test_group_stats', + params: { + branch: options.branch, + ci_job_name: options.ciJobNames.join(','), + test_group_type: options.testGroupType, + }, + headers: { + Authentication: `token ${this.config.apiToken}`, + }, + }); + + return resp.data; + } +} diff --git a/packages/kbn-ci-stats-client/src/index.ts b/packages/kbn-ci-stats-client/src/index.ts new file mode 100644 index 0000000000000..ac32c69b9f7b7 --- /dev/null +++ b/packages/kbn-ci-stats-client/src/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { CiStatsClient } from './ci_stats_client'; diff --git a/packages/kbn-ci-stats-client/tsconfig.json b/packages/kbn-ci-stats-client/tsconfig.json new file mode 100644 index 0000000000000..a8cfc2cceb08b --- /dev/null +++ b/packages/kbn-ci-stats-client/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-ci-stats-core/README.md b/packages/kbn-ci-stats-core/README.md index 741728394aa83..b2e34a492b745 100644 --- a/packages/kbn-ci-stats-core/README.md +++ b/packages/kbn-ci-stats-core/README.md @@ -1,3 +1,3 @@ # @kbn/ci-stats-core -Config and types used by `@kbn/ci-stats-reporter`. \ No newline at end of file +Config and types used by `@kbn/ci-stats-client` and `@kbn/ci-stats-reporter`. \ No newline at end of file diff --git a/packages/kbn-ci-stats-core/jest.config.js b/packages/kbn-ci-stats-core/jest.config.js new file mode 100644 index 0000000000000..0feb7b4e1b872 --- /dev/null +++ b/packages/kbn-ci-stats-core/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-ci-stats-core'], +}; diff --git a/packages/kbn-ci-stats-reporter/jest.config.js b/packages/kbn-ci-stats-reporter/jest.config.js new file mode 100644 index 0000000000000..bf58324f440a3 --- /dev/null +++ b/packages/kbn-ci-stats-reporter/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-ci-stats-reporter'], +}; diff --git a/packages/kbn-eslint-plugin-imports/jest.integration.config.js b/packages/kbn-eslint-plugin-imports/jest.integration.config.js new file mode 100644 index 0000000000000..e7d05a6222c88 --- /dev/null +++ b/packages/kbn-eslint-plugin-imports/jest.integration.config.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test/jest_integration_node', + rootDir: '../..', + roots: ['/packages/kbn-eslint-plugin-imports'], +}; diff --git a/packages/kbn-generate/jest.config.js b/packages/kbn-generate/jest.config.js new file mode 100644 index 0000000000000..b72f891cfb1a7 --- /dev/null +++ b/packages/kbn-generate/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-generate'], +}; diff --git a/packages/kbn-jest-serializers/jest.config.js b/packages/kbn-jest-serializers/jest.config.js new file mode 100644 index 0000000000000..23fad67c028bf --- /dev/null +++ b/packages/kbn-jest-serializers/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-jest-serializers'], +}; diff --git a/packages/kbn-kibana-json-schema/jest.config.js b/packages/kbn-kibana-json-schema/jest.config.js new file mode 100644 index 0000000000000..00bc8f55adc57 --- /dev/null +++ b/packages/kbn-kibana-json-schema/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-kibana-json-schema'], +}; diff --git a/packages/kbn-plugin-discovery/jest.config.js b/packages/kbn-plugin-discovery/jest.config.js new file mode 100644 index 0000000000000..37d7a4f2b63a2 --- /dev/null +++ b/packages/kbn-plugin-discovery/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-plugin-discovery'], +}; diff --git a/packages/kbn-shared-ux-services/jest.config.js b/packages/kbn-shared-ux-services/jest.config.js new file mode 100755 index 0000000000000..f1ef008d0f62d --- /dev/null +++ b/packages/kbn-shared-ux-services/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-shared-ux-services'], +}; diff --git a/packages/kbn-shared-ux-storybook/jest.config.js b/packages/kbn-shared-ux-storybook/jest.config.js new file mode 100644 index 0000000000000..91285e025f069 --- /dev/null +++ b/packages/kbn-shared-ux-storybook/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-shared-ux-storybook'], +}; diff --git a/packages/kbn-sort-package-json/jest.config.js b/packages/kbn-sort-package-json/jest.config.js new file mode 100644 index 0000000000000..ae0651be19e61 --- /dev/null +++ b/packages/kbn-sort-package-json/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-sort-package-json'], +}; diff --git a/packages/kbn-stdio-dev-helpers/jest.config.js b/packages/kbn-stdio-dev-helpers/jest.config.js new file mode 100644 index 0000000000000..31a8aab16c7e5 --- /dev/null +++ b/packages/kbn-stdio-dev-helpers/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-stdio-dev-helpers'], +}; diff --git a/packages/kbn-test/jest-preset.js b/packages/kbn-test/jest-preset.js index fb535c2766cc4..6245532b00e44 100644 --- a/packages/kbn-test/jest-preset.js +++ b/packages/kbn-test/jest-preset.js @@ -63,16 +63,12 @@ module.exports = { rootDirectory: '.', }, ], - ...(process.env.TEST_GROUP_TYPE_UNIT - ? [ - [ - '@kbn/test/target_node/jest/ci_stats_jest_reporter', - { - testGroupType: process.env.TEST_GROUP_TYPE_UNIT, - }, - ], - ] - : []), + [ + '@kbn/test/target_node/jest/ci_stats_jest_reporter', + { + testGroupType: 'Jest Unit Tests', + }, + ], ], // The paths to modules that run some code to configure or set up the testing environment before each test diff --git a/packages/kbn-test/jest_integration/jest-preset.js b/packages/kbn-test/jest_integration/jest-preset.js index 1f2626aef532e..f5593e3f57fb6 100644 --- a/packages/kbn-test/jest_integration/jest-preset.js +++ b/packages/kbn-test/jest_integration/jest-preset.js @@ -28,16 +28,12 @@ module.exports = { reportName: 'Jest Integration Tests', }, ], - ...(process.env.TEST_GROUP_TYPE_INTEGRATION - ? [ - [ - '@kbn/test/target_node/jest/ci_stats_jest_reporter', - { - testGroupType: process.env.TEST_GROUP_TYPE_INTEGRATION, - }, - ], - ] - : []), + [ + '@kbn/test/target_node/jest/ci_stats_jest_reporter', + { + testGroupType: 'Jest Integration Tests', + }, + ], ], coverageReporters: !!process.env.CI ? [['json', { file: 'jest-integration.json' }]] diff --git a/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts b/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts index 7a50e88b3396d..42766ad3f5a31 100644 --- a/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts +++ b/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts @@ -17,14 +17,14 @@ import { getAllRepoRelativeBazelPackageDirs } from '@kbn/bazel-packages'; import { JestConfigs, CONFIG_NAMES } from './configs'; const unitTestingTemplate: string = `module.exports = { - preset: '@kbn/test/jest_node', + preset: '@kbn/test', rootDir: '{{{relToRoot}}}', roots: ['/{{{modulePath}}}'], }; `; const integrationTestingTemplate: string = `module.exports = { - preset: '@kbn/test/jest_integration_node', + preset: '@kbn/test/jest_integration', rootDir: '{{{relToRoot}}}', roots: ['/{{{modulePath}}}'], }; diff --git a/src/plugins/event_annotation/jest.config.js b/src/plugins/event_annotation/jest.config.js new file mode 100644 index 0000000000000..a6ea4a6b430df --- /dev/null +++ b/src/plugins/event_annotation/jest.config.js @@ -0,0 +1,18 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/event_annotation'], + coverageDirectory: '/target/kibana-coverage/jest/src/plugins/event_annotation', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/src/plugins/event_annotation/{common,public,server}/**/*.{ts,tsx}', + ], +}; diff --git a/src/plugins/shared_ux/jest.config.js b/src/plugins/shared_ux/jest.config.js new file mode 100644 index 0000000000000..bc8d67e5ac35b --- /dev/null +++ b/src/plugins/shared_ux/jest.config.js @@ -0,0 +1,19 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/shared_ux'], + transform: { + '^.+\\.stories\\.tsx?$': '@storybook/addon-storyshots/injectFileName', + }, + coverageDirectory: '/target/kibana-coverage/jest/src/plugins/shared_ux', + coverageReporters: ['text', 'html'], + collectCoverageFrom: ['/src/plugins/shared_ux/{common,public,server}/**/*.{js,ts,tsx}'], +}; diff --git a/yarn.lock b/yarn.lock index e4461999d72ab..b0a457c84d3ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2968,6 +2968,10 @@ version "0.0.0" uid "" +"@kbn/ci-stats-client@link:bazel-bin/packages/kbn-ci-stats-client": + version "0.0.0" + uid "" + "@kbn/ci-stats-core@link:bazel-bin/packages/kbn-ci-stats-core": version "0.0.0" uid "" @@ -6060,6 +6064,10 @@ version "0.0.0" uid "" +"@types/kbn__ci-stats-client@link:bazel-bin/packages/kbn-ci-stats-client/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__ci-stats-core@link:bazel-bin/packages/kbn-ci-stats-core/npm_module_types": version "0.0.0" uid "" From f5f035645e1454143f09619cfb67ae310ae22ef8 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Wed, 27 Apr 2022 10:07:25 -0700 Subject: [PATCH 39/78] [Security Solution][Cypress] - Update cypress tests to undo a util change that was redundant (#130722) Update cypress tests to undo a util change that was redundant. --- .../cypress/integration/exceptions/add_exception.spec.ts | 7 +++---- .../security_solution/cypress/tasks/api_calls/rules.ts | 9 ++------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/add_exception.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/add_exception.spec.ts index 6d43ed81cd68e..d41e86fb9c96d 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/add_exception.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/add_exception.spec.ts @@ -11,7 +11,7 @@ import { getNewRule } from '../../objects/rule'; import { ALERTS_COUNT, EMPTY_ALERT_TABLE, NUMBER_OF_ALERTS } from '../../screens/alerts'; import { addExceptionFromFirstAlert, goToClosedAlerts, goToOpenedAlerts } from '../../tasks/alerts'; -import { createCustomRule } from '../../tasks/api_calls/rules'; +import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; @@ -39,11 +39,10 @@ describe('Adds rule exception', () => { beforeEach(() => { deleteAlertsAndRules(); - createCustomRule( + createCustomRuleEnabled( { ...getNewRule(), customQuery: 'agent.name:*', index: ['exceptions*'] }, 'rule_testing', - '5s', - true + '1s' ); visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); goToRuleDetails(); diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts index ab58d60044a21..405c118140395 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts @@ -7,12 +7,7 @@ import { CustomRule, ThreatIndicatorRule } from '../../objects/rule'; -export const createCustomRule = ( - rule: CustomRule, - ruleId = 'rule_testing', - interval = '100m', - enabled = false -) => +export const createCustomRule = (rule: CustomRule, ruleId = 'rule_testing', interval = '100m') => cy.request({ method: 'POST', url: 'api/detection_engine/rules', @@ -28,7 +23,7 @@ export const createCustomRule = ( index: rule.index, query: rule.customQuery, language: 'kuery', - enabled, + enabled: false, exceptions_list: rule.exceptionLists ?? [], }, headers: { 'kbn-xsrf': 'cypress-creds' }, From 56d0112a22a4dd3b61cc1c9d074abb44c02eea06 Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Wed, 27 Apr 2022 18:18:07 +0100 Subject: [PATCH 40/78] [SecuritySolution] Cases by status (#130670) * init cases by status * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * cases by status * tickLabel * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * unit tests * unit tests * rm unused dependency * disable dashboard * dependency * enable dashboard * styling * styling * unit tests * styling * unit tests * disable dashboard * fix types * fix types * unit tests * unit test * unit tests * unit tests * unit tests * unit tests * unit tests * update i18n * remove cases api hack Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../components/charts/barchart.test.tsx | 44 +++- .../common/components/charts/barchart.tsx | 44 +++- .../common/components/charts/common.tsx | 11 + .../alerts_by_status/alerts_by_status.tsx | 33 ++- .../cases_by_status/cases_by_status.test.tsx | 92 ++++++++ .../cases_by_status/cases_by_status.tsx | 200 ++++++++++++++++++ .../cases_by_status/index.tsx | 8 + .../use_cases_by_status.test.tsx | 123 +++++++++++ .../cases_by_status/use_cases_by_status.tsx | 93 ++++++++ .../detection_response/translations.ts | 30 ++- .../pages/detection_response.test.tsx | 24 ++- .../overview/pages/detection_response.tsx | 7 +- 12 files changed, 664 insertions(+), 45 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.test.tsx create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.tsx create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/index.tsx create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.test.tsx create mode 100644 x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx index c24e41d096546..2315401b3c760 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { Chart, BarSeries, Axis, ScaleType } from '@elastic/charts'; +import { Chart, BarSeries, Axis, ScaleType, AxisStyle } from '@elastic/charts'; import { mount, ReactWrapper, shallow, ShallowWrapper } from 'enzyme'; import React from 'react'; @@ -166,13 +166,43 @@ describe('BarChartBaseComponent', () => { describe('render with customized configs', () => { const mockNumberFormatter = jest.fn(); + const mockXAxisStyle = { + tickLine: { + size: 0, + }, + tickLabel: { + padding: 16, + fontSize: 10.5, + }, + } as Partial; + const mockYAxisStyle = { + tickLine: { + size: 0, + }, + tickLabel: { + padding: 16, + fontSize: 14, + }, + } as Partial; const configs = { series: { xScaleType: ScaleType.Ordinal, yScaleType: ScaleType.Linear, + barSeriesStyle: { + rect: { + widthPixel: 22, + opacity: 1, + }, + }, }, axis: { yTickFormatter: mockNumberFormatter, + bottom: { + style: mockXAxisStyle, + }, + left: { + style: mockYAxisStyle, + }, }, }; @@ -203,12 +233,22 @@ describe('BarChartBaseComponent', () => { ); }); + it('should render BarSeries with given barSeriesStyle', () => { + expect(shallowWrapper.find(BarSeries).first().prop('barSeriesStyle')).toEqual( + configs.series.barSeriesStyle + ); + }); + it('should render xAxis with given tick formatter', () => { expect(shallowWrapper.find(Axis).first().prop('tickFormat')).toBeUndefined(); }); + it('should render xAxis style', () => { + expect(shallowWrapper.find(Axis).first().prop('style')).toEqual(mockXAxisStyle); + }); + it('should render yAxis with given tick formatter', () => { - expect(shallowWrapper.find(Axis).last().prop('tickFormat')).toEqual(mockNumberFormatter); + expect(shallowWrapper.find(Axis).last().prop('style')).toEqual(mockYAxisStyle); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/barchart.tsx b/x-pack/plugins/security_solution/public/common/components/charts/barchart.tsx index 55a4cc5840259..fcea5c8d77dc9 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/barchart.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/barchart.tsx @@ -87,6 +87,34 @@ export const BarChartBaseComponent = ({ ...deepmerge(get('configs.settings', chartConfigs), { theme }), }; + const xAxisStyle = useMemo( + () => + deepmerge( + { + tickLine: { + size: tickSize, + }, + }, + getOr({}, 'configs.axis.bottom.style', chartConfigs) + ), + [chartConfigs, tickSize] + ); + + const yAxisStyle = useMemo( + () => + deepmerge( + { + tickLine: { + size: tickSize, + }, + }, + getOr({}, 'configs.axis.left.style', chartConfigs) + ), + [chartConfigs, tickSize] + ); + + const xAxisLabelFormat = get('configs.axis.bottom.labelFormat', chartConfigs); + return chartConfigs.width && chartConfigs.height ? ( @@ -106,6 +134,7 @@ export const BarChartBaseComponent = ({ data={series.value ?? []} stackAccessors={get('configs.series.stackAccessors', chartConfigs)} color={series.color ? series.color : undefined} + barSeriesStyle={get('configs.series.barSeriesStyle', chartConfigs)} /> ) : null; })} @@ -114,22 +143,15 @@ export const BarChartBaseComponent = ({ id={xAxisId} position={Position.Bottom} showOverlappingTicks={false} - style={{ - tickLine: { - size: tickSize, - }, - }} + style={xAxisStyle} tickFormat={xTickFormatter} + labelFormat={xAxisLabelFormat} /> @@ -143,7 +165,7 @@ export const BarChartBase = React.memo(BarChartBaseComponent); BarChartBase.displayName = 'BarChartBase'; -interface BarChartComponentProps { +export interface BarChartComponentProps { barChart: ChartSeriesData[] | null | undefined; configs?: ChartSeriesConfigs | undefined; stackByField?: string; diff --git a/x-pack/plugins/security_solution/public/common/components/charts/common.tsx b/x-pack/plugins/security_solution/public/common/components/charts/common.tsx index 85dbd66d0c05e..b96d016d9b186 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/common.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/common.tsx @@ -17,6 +17,8 @@ import { TickFormatter, Position, BrushEndListener, + AxisStyle, + BarSeriesStyle, } from '@elastic/charts'; import React, { useMemo } from 'react'; import styled from 'styled-components'; @@ -45,11 +47,20 @@ export interface ChartSeriesConfigs { xScaleType?: ScaleType | undefined; yScaleType?: ScaleType | undefined; stackAccessors?: string[] | undefined; + barSeriesStyle?: Partial; }; axis?: { xTickFormatter?: TickFormatter | undefined; yTickFormatter?: TickFormatter | undefined; tickSize?: number | undefined; + left?: { + style?: Partial; + labelFormat?: (d: unknown) => string; + }; + bottom?: { + style?: Partial; + labelFormat?: (d: unknown) => string; + }; }; yAxisTitle?: string | undefined; settings?: SettingsProps; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx index 16c06f4c9d0f8..2e1b96447f3df 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx @@ -105,12 +105,12 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { [] ); + const openCount = donutData?.open?.total ?? 0; + const acknowledgedCount = donutData?.acknowledged?.total ?? 0; + const closedCount = donutData?.closed?.total ?? 0; + const totalAlerts = - loading || donutData == null - ? 0 - : (donutData?.open?.total ?? 0) + - (donutData?.acknowledged?.total ?? 0) + - (donutData?.closed?.total ?? 0); + loading || donutData == null ? 0 : openCount + acknowledgedCount + closedCount; const fillColor: FillColor = useCallback((d: ShapeTreeNode) => { return chartConfigs.find((cfg) => cfg.label === d.dataName)?.color ?? emptyDonutColor; @@ -152,10 +152,8 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { <> - - {loading ? ( - - ) : ( + {totalAlerts !== 0 && ( + <> @@ -163,9 +161,8 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { <> {ALERTS(totalAlerts)} - )} - - + + )} @@ -174,8 +171,8 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { fillColor={fillColor} height={donutHeight} label={STATUS_OPEN} - title={} - totalCount={donutData?.open?.total ?? 0} + title={} + totalCount={openCount} /> @@ -184,8 +181,8 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { fillColor={fillColor} height={donutHeight} label={STATUS_ACKNOWLEDGED} - title={} - totalCount={donutData?.acknowledged?.total ?? 0} + title={} + totalCount={acknowledgedCount} /> @@ -194,8 +191,8 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { fillColor={fillColor} height={donutHeight} label={STATUS_CLOSED} - title={} - totalCount={donutData?.closed?.total ?? 0} + title={} + totalCount={closedCount} /> diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.test.tsx new file mode 100644 index 0000000000000..18ac775dc5792 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.test.tsx @@ -0,0 +1,92 @@ +/* + * 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 { render, screen } from '@testing-library/react'; +import React from 'react'; +import { BarChartComponentProps } from '../../../../common/components/charts/barchart'; +import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { TestProviders } from '../../../../common/mock'; +import { CasesByStatus } from './cases_by_status'; +jest.mock('../../../../common/components/link_to'); +jest.mock('../../../../common/containers/query_toggle'); +jest.mock('./use_cases_by_status', () => ({ + useCasesByStatus: jest.fn().mockReturnValue({ + closed: 1, + inProgress: 2, + isLoading: false, + open: 3, + totalCounts: 6, + updatedAt: new Date('2022-04-08T12:00:00.000Z').valueOf(), + }), +})); +jest.mock('../../../../common/lib/kibana', () => { + const actual = jest.requireActual('../../../../common/lib/kibana'); + return { + ...actual, + useNavigation: jest.fn().mockReturnValue({ + getAppUrl: jest.fn(), + navigateTo: jest.fn(), + }), + }; +}); +jest.mock('../../../../common/components/charts/barchart', () => ({ + BarChart: jest.fn((props: BarChartComponentProps) =>
), +})); + +const mockSetToggle = jest.fn(); +(useQueryToggle as jest.Mock).mockReturnValue({ + toggleStatus: true, + setToggleStatus: mockSetToggle, +}); + +describe('CasesByStatus', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('renders title', () => { + render( + + + + ); + expect(screen.getByTestId('header-section-title')).toHaveTextContent('Cases'); + }); + + test('renders toggleQuery', () => { + render( + + + + ); + expect(screen.getByTestId('query-toggle-header')).toBeInTheDocument(); + }); + + test('renders BarChart', () => { + render( + + + + ); + expect(screen.getByTestId('chart-wrapper')).toBeInTheDocument(); + expect(screen.queryByTestId('bar-chart-mask')).not.toBeInTheDocument(); + }); + + test('collapses content', () => { + (useQueryToggle as jest.Mock).mockReturnValueOnce({ + toggleStatus: false, + setToggleStatus: mockSetToggle, + }); + render( + + + + ); + + expect(screen.queryByTestId('chart-wrapper')).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.tsx new file mode 100644 index 0000000000000..13a2459b44d68 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.tsx @@ -0,0 +1,200 @@ +/* + * 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 React, { useCallback, useMemo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiPanel, EuiText } from '@elastic/eui'; +import { AxisStyle, Rotation, ScaleType } from '@elastic/charts'; +import styled from 'styled-components'; +import { FormattedNumber } from '@kbn/i18n-react'; +import numeral from '@elastic/numeral'; +import { BarChart } from '../../../../common/components/charts/barchart'; +import { LastUpdatedAt } from '../util'; +import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { HeaderSection } from '../../../../common/components/header_section'; +import { + CASES, + CASES_BY_STATUS_SECTION_TITLE, + STATUS_CLOSED, + STATUS_IN_PROGRESS, + STATUS_OPEN, + VIEW_CASES, +} from '../translations'; +import { LinkButton } from '../../../../common/components/links'; +import { useCasesByStatus } from './use_cases_by_status'; +import { SecurityPageName } from '../../../../../common/constants'; +import { useFormatUrl } from '../../../../common/components/link_to'; +import { appendSearch } from '../../../../common/components/link_to/helpers'; +import { useNavigation } from '../../../../common/lib/kibana'; + +const CASES_BY_STATUS_ID = 'casesByStatus'; + +export const numberFormatter = (value: string | number): string => value.toLocaleString(); + +export const barchartConfigs = { + series: { + xScaleType: ScaleType.Ordinal, + yScaleType: ScaleType.Linear, + stackAccessors: ['g'], + barSeriesStyle: { + rect: { + widthPixel: 22, + opacity: 1, + }, + }, + }, + axis: { + xTickFormatter: numberFormatter, + left: { + style: { + tickLine: { + size: 0, + }, + tickLabel: { + padding: 16, + fontSize: 14, + }, + } as Partial, + }, + bottom: { + style: { + tickLine: { + size: 0, + }, + tickLabel: { + padding: 16, + fontSize: 10.5, + }, + } as Partial, + labelFormat: (d: unknown) => numeral(d).format('0'), + }, + }, + settings: { + rotation: 90 as Rotation, + }, + customHeight: 146, +}; + +const barColors = { + empty: 'rgba(105, 112, 125, 0.1)', + open: '#79aad9', + 'in-progress': '#f1d86f', + closed: '#d3dae6', +}; + +export const emptyChartSettings = [ + { + key: 'open', + value: [{ y: 20, x: STATUS_OPEN, g: STATUS_OPEN }], + color: barColors.empty, + }, + { + key: 'in-progress', + value: [{ y: 20, x: STATUS_IN_PROGRESS, g: STATUS_IN_PROGRESS }], + color: barColors.empty, + }, + { + key: 'closed', + value: [{ y: 20, x: STATUS_CLOSED, g: STATUS_CLOSED }], + color: barColors.empty, + }, +]; + +const StyledEuiFlexItem = styled(EuiFlexItem)` + align-items: center; + width: 70%; +`; + +const Wrapper = styled.div` + width: 100%; +`; + +const CasesByStatusComponent: React.FC = () => { + const { toggleStatus, setToggleStatus } = useQueryToggle(CASES_BY_STATUS_ID); + const { getAppUrl, navigateTo } = useNavigation(); + const { search } = useFormatUrl(SecurityPageName.case); + const caseUrl = getAppUrl({ deepLinkId: SecurityPageName.case, path: appendSearch(search) }); + + const goToCases = useCallback( + (ev) => { + ev.preventDefault(); + navigateTo({ url: caseUrl }); + }, + [caseUrl, navigateTo] + ); + const { closed, inProgress, isLoading, open, totalCounts, updatedAt } = useCasesByStatus({ + skip: !toggleStatus, + }); + + const chartData = useMemo( + () => [ + { + key: 'open', + value: [{ y: open, x: STATUS_OPEN, g: STATUS_OPEN }], + color: barColors.open, + }, + { + key: 'in-progress', + value: [{ y: inProgress, x: STATUS_IN_PROGRESS, g: STATUS_IN_PROGRESS }], + color: barColors['in-progress'], + }, + { + key: 'closed', + value: [{ y: closed, x: STATUS_CLOSED, g: STATUS_CLOSED }], + color: barColors.closed, + }, + ], + [closed, inProgress, open] + ); + + return ( + + } + showInspectButton={false} + > + + + + {VIEW_CASES} + + + + + {!isLoading && toggleStatus && ( + + + {totalCounts !== 0 && ( + + <> + + + + <> + + {CASES(totalCounts)} + + + + )} + + + + + + + + )} + + ); +}; + +export const CasesByStatus = React.memo(CasesByStatusComponent); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/index.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/index.tsx new file mode 100644 index 0000000000000..4e1aceb20f4ed --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/index.tsx @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { CasesByStatus } from './cases_by_status'; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.test.tsx new file mode 100644 index 0000000000000..eb5daeeb374c2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.test.tsx @@ -0,0 +1,123 @@ +/* + * 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 { renderHook, act } from '@testing-library/react-hooks'; +import { mockCasesContract } from '@kbn/cases-plugin/public/mocks'; +import { useKibana } from '../../../../common/lib/kibana'; +import { TestProviders } from '../../../../common/mock'; +import { + useCasesByStatus, + UseCasesByStatusProps, + UseCasesByStatusResults, +} from './use_cases_by_status'; + +const dateNow = new Date('2022-04-08T12:00:00.000Z').valueOf(); +const mockDateNow = jest.fn().mockReturnValue(dateNow); +Date.now = jest.fn(() => mockDateNow()) as unknown as DateConstructor['now']; + +jest.mock('../../../../common/containers/use_global_time', () => { + return { + useGlobalTime: jest + .fn() + .mockReturnValue({ from: '2022-04-05T12:00:00.000Z', to: '2022-04-08T12:00:00.000Z' }), + }; +}); +jest.mock('../../../../common/lib/kibana'); + +const mockGetAllCasesMetrics = jest.fn(); +mockGetAllCasesMetrics.mockResolvedValue({ + count_open_cases: 1, + count_in_progress_cases: 2, + count_closed_cases: 3, +}); +mockGetAllCasesMetrics.mockResolvedValueOnce({ + count_open_cases: 0, + count_in_progress_cases: 0, + count_closed_cases: 0, +}); + +const mockUseKibana = { + services: { + cases: { + ...mockCasesContract(), + api: { + cases: { + getAllCasesMetrics: mockGetAllCasesMetrics, + }, + }, + }, + }, +}; + +(useKibana as jest.Mock).mockReturnValue(mockUseKibana); + +describe('useCasesByStatus', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + test('init', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook< + UseCasesByStatusProps, + UseCasesByStatusResults + >(() => useCasesByStatus({ skip: false }), { + wrapper: TestProviders, + }); + await waitForNextUpdate(); + expect(result.current).toEqual({ + closed: 0, + inProgress: 0, + isLoading: true, + open: 0, + totalCounts: 0, + updatedAt: dateNow, + }); + }); + }); + + test('fetch data', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook< + UseCasesByStatusProps, + UseCasesByStatusResults + >(() => useCasesByStatus({ skip: false }), { + wrapper: TestProviders, + }); + await waitForNextUpdate(); + await waitForNextUpdate(); + expect(result.current).toEqual({ + closed: 3, + inProgress: 2, + isLoading: false, + open: 1, + totalCounts: 6, + updatedAt: dateNow, + }); + }); + }); + + test('skip', async () => { + const abortSpy = jest.spyOn(AbortController.prototype, 'abort'); + await act(async () => { + const localProps = { skip: false }; + + const { rerender, waitForNextUpdate } = renderHook< + UseCasesByStatusProps, + UseCasesByStatusResults + >(() => useCasesByStatus(localProps), { + wrapper: TestProviders, + }); + await waitForNextUpdate(); + await waitForNextUpdate(); + + localProps.skip = true; + act(() => rerender()); + act(() => rerender()); + expect(abortSpy).toHaveBeenCalledTimes(2); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.tsx new file mode 100644 index 0000000000000..813fcaa848115 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.tsx @@ -0,0 +1,93 @@ +/* + * 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 { useState, useEffect } from 'react'; +import { APP_ID } from '../../../../../common/constants'; +import { useGlobalTime } from '../../../../common/containers/use_global_time'; +import { useKibana } from '../../../../common/lib/kibana'; + +export interface CasesCounts { + count_open_cases?: number; + count_in_progress_cases?: number; + count_closed_cases?: number; +} + +export interface UseCasesByStatusProps { + skip?: boolean; +} + +export interface UseCasesByStatusResults { + closed: number; + inProgress: number; + isLoading: boolean; + open: number; + totalCounts: number; + updatedAt: number; +} + +export const useCasesByStatus = ({ skip = false }) => { + const { + services: { cases }, + } = useKibana(); + const { to, from } = useGlobalTime(); + + const [updatedAt, setUpdatedAt] = useState(Date.now()); + const [isLoading, setIsLoading] = useState(true); + const [casesCounts, setCasesCounts] = useState(null); + + useEffect(() => { + let isSubscribed = true; + const abortCtrl = new AbortController(); + const fetchCases = async () => { + try { + const casesResponse = await cases.api.cases.getAllCasesMetrics({ + from, + to, + owner: APP_ID, + }); + if (isSubscribed) { + setCasesCounts(casesResponse); + } + } catch (error) { + if (isSubscribed) { + setCasesCounts({}); + } + } + if (isSubscribed) { + setIsLoading(false); + setUpdatedAt(Date.now()); + } + }; + + if (!skip) { + fetchCases(); + } + + if (skip) { + setIsLoading(false); + isSubscribed = false; + abortCtrl.abort(); + } + + return () => { + isSubscribed = false; + abortCtrl.abort(); + }; + }, [cases.api.cases, from, skip, to]); + + return { + closed: casesCounts?.count_closed_cases ?? 0, + inProgress: casesCounts?.count_in_progress_cases ?? 0, + isLoading, + open: casesCounts?.count_open_cases ?? 0, + totalCounts: + (casesCounts?.count_closed_cases ?? 0) + + (casesCounts?.count_in_progress_cases ?? 0) + + (casesCounts?.count_open_cases ?? 0), + updatedAt, + }; +}; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts index 2afc356e15c8b..8f37c2e641c7a 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts @@ -31,26 +31,23 @@ export const STATUS_LOW_LABEL = i18n.translate( defaultMessage: 'Low', } ); -export const STATUS_OPEN = i18n.translate( - 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.title.open', - { - defaultMessage: 'Open', - } -); export const STATUS_ACKNOWLEDGED = i18n.translate( - 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.title.acknowledged', + 'xpack.securitySolution.detectionResponse.alertsByStatus.status.acknowledged', { defaultMessage: 'Acknowledged', } ); +export const STATUS_OPEN = i18n.translate('xpack.securitySolution.detectionResponse.status.open', { + defaultMessage: 'Open', +}); export const STATUS_CLOSED = i18n.translate( - 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.title.closed', + 'xpack.securitySolution.detectionResponse.status.closed', { defaultMessage: 'Closed', } ); export const STATUS_IN_PROGRESS = i18n.translate( - 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.title.inProgress', + 'xpack.securitySolution.detectionResponse.status.inProgress', { defaultMessage: 'In progress', } @@ -69,6 +66,21 @@ export const UPDATING = i18n.translate('xpack.securitySolution.detectionResponse export const UPDATED = i18n.translate('xpack.securitySolution.detectionResponse.updated', { defaultMessage: 'Updated', }); +export const CASES = (totalCases: number) => + i18n.translate('xpack.securitySolution.detectionResponse.casesByStatus.totalCases', { + values: { totalCases }, + defaultMessage: 'total {totalCases, plural, =1 {case} other {cases}}', + }); +export const CASES_BY_STATUS_SECTION_TITLE = i18n.translate( + 'xpack.securitySolution.detectionResponse.casesByStatusSectionTitle', + { + defaultMessage: 'Cases', + } +); + +export const VIEW_CASES = i18n.translate('xpack.securitySolution.detectionResponse.viewCases', { + defaultMessage: 'View cases', +}); export const RULE_ALERTS_SECTION_TITLE = i18n.translate( 'xpack.securitySolution.detectionResponse.ruleAlertsSectionTitle', diff --git a/x-pack/plugins/security_solution/public/overview/pages/detection_response.test.tsx b/x-pack/plugins/security_solution/public/overview/pages/detection_response.test.tsx index 8cd387978e97d..4ad2eeac35240 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/detection_response.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/detection_response.test.tsx @@ -14,7 +14,14 @@ import { TestProviders } from '../../common/mock'; jest.mock('../components/detection_response/rule_alerts_table', () => ({ RuleAlertsTable: () =>
, })); -// TODO: add all sections mocks + +jest.mock('../components/detection_response/alerts_by_status', () => ({ + AlertsByStatus: () =>
, +})); + +jest.mock('../components/detection_response/cases_by_status', () => ({ + CasesByStatus: () =>
, +})); jest.mock('../../common/components/search_bar', () => ({ SiemSearchBar: () =>
, @@ -128,9 +135,12 @@ describe('DetectionResponse', () => { ); expect(result.queryByTestId('detectionResponsePage')).toBeInTheDocument(); - expect(result.queryByTestId('mock_RuleAlertsTable')).not.toBeInTheDocument(); // TODO: assert other alert sections are not in the document + expect(result.queryByTestId('mock_RuleAlertsTable')).not.toBeInTheDocument(); + expect(result.queryByTestId('mock_AlertsByStatus')).not.toBeInTheDocument(); + // TODO: assert cases sections are in the document + expect(result.queryByTestId('mock_CasesByStatus')).toBeInTheDocument(); }); it('should not render alerts data sections if user has not kibana read permission', () => { @@ -148,9 +158,13 @@ describe('DetectionResponse', () => { ); expect(result.queryByTestId('detectionResponsePage')).toBeInTheDocument(); - expect(result.queryByTestId('mock_RuleAlertsTable')).not.toBeInTheDocument(); + // TODO: assert all alert sections are not in the document + expect(result.queryByTestId('mock_RuleAlertsTable')).not.toBeInTheDocument(); + expect(result.queryByTestId('mock_AlertsByStatus')).not.toBeInTheDocument(); + // TODO: assert all cases sections are in the document + expect(result.queryByTestId('mock_CasesByStatus')).toBeInTheDocument(); }); it('should not render cases data sections if user has not cases read permission', () => { @@ -165,9 +179,11 @@ describe('DetectionResponse', () => { ); expect(result.queryByTestId('detectionResponsePage')).toBeInTheDocument(); - expect(result.queryByTestId('mock_RuleAlertsTable')).toBeInTheDocument(); // TODO: assert all alert sections are in the document + expect(result.queryByTestId('mock_RuleAlertsTable')).toBeInTheDocument(); + expect(result.queryByTestId('mock_AlertsByStatus')).toBeInTheDocument(); // TODO: assert all cases sections are not in the document + expect(result.queryByTestId('mock_CasesByStatus')).not.toBeInTheDocument(); }); it('should render page permissions message if user has any read permission', () => { diff --git a/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx b/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx index 6561c2886ae38..5b9d298bf915c 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx @@ -20,6 +20,7 @@ import { LandingPageComponent } from '../../common/components/landing_page'; import { RuleAlertsTable } from '../components/detection_response/rule_alerts_table'; import * as i18n from './translations'; import { EmptyPage } from '../../common/components/empty_page'; +import { CasesByStatus } from '../components/detection_response/cases_by_status'; import { AlertsByStatus } from '../components/detection_response/alerts_by_status'; const NoPrivilegePage: React.FC = () => { @@ -75,7 +76,11 @@ const DetectionResponseComponent = () => { )} - {canReadCases && {'[cases chart]'}} + {canReadCases && ( + + + + )} From 7fd166176d21e044374f2cb1335c20b11a2896bd Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 27 Apr 2022 11:16:55 -0700 Subject: [PATCH 41/78] [ci-stats-reporter] use v2 test group APIs (#131001) * [ci-stats-reporter] use v2 test group APIs * fix bazel deps * [CI] Auto-commit changed files from 'yarn kbn run build -i @kbn/pm' * avoid importing kbn/std in kbn-pm * removed kbn/std dependency Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../src/ci_stats_reporter.ts | 51 ++++++++++++++---- packages/kbn-pm/dist/index.js | 52 ++++++++++++++++--- 2 files changed, 85 insertions(+), 18 deletions(-) diff --git a/packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts b/packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts index 933f4ccdde0a3..73eecbda2ff3e 100644 --- a/packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts +++ b/packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts @@ -96,10 +96,8 @@ export interface CiStatsReportTestsOptions { } /* @internal */ -interface ReportTestsResponse { - buildId: string; +interface ReportTestGroupResponse { groupId: string; - testRunCount: number; } /* @internal */ @@ -257,18 +255,51 @@ export class CiStatsReporter { ); } - return await this.req({ + const groupResp = await this.req({ auth: true, - path: '/v1/test_group', + path: '/v2/test_group', query: { buildId: this.config?.buildId, }, - bodyDesc: `[${group.name}/${group.type}] test groups with ${testRuns.length} tests`, - body: [ - JSON.stringify({ group }), - ...testRuns.map((testRun) => JSON.stringify({ testRun })), - ].join('\n'), + bodyDesc: `[${group.name}/${group.type}] test group`, + body: group, }); + + if (!groupResp) { + return; + } + + let bufferBytes = 0; + const buffer: string[] = []; + const flushBuffer = async () => { + await this.req<{ testRunCount: number }>({ + auth: true, + path: '/v2/test_runs', + query: { + buildId: this.config?.buildId, + groupId: groupResp.groupId, + groupType: group.type, + }, + bodyDesc: `[${group.name}/${group.type}] Chunk of ${bufferBytes} bytes`, + body: buffer.join('\n'), + }); + buffer.length = 0; + bufferBytes = 0; + }; + + // send test runs in chunks of ~500kb + for (const testRun of testRuns) { + const json = JSON.stringify(testRun); + bufferBytes += json.length; + buffer.push(json); + if (bufferBytes >= 450000) { + await flushBuffer(); + } + } + + if (bufferBytes) { + await flushBuffer(); + } } /** diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index df1391fe55dd8..b0aabefb1b0f2 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -1753,19 +1753,55 @@ class CiStatsReporter { throw new Error('unable to report tests unless buildId is configured and auth config available'); } - return await this.req({ + const groupResp = await this.req({ auth: true, - path: '/v1/test_group', + path: '/v2/test_group', query: { buildId: (_this$config7 = this.config) === null || _this$config7 === void 0 ? void 0 : _this$config7.buildId }, - bodyDesc: `[${group.name}/${group.type}] test groups with ${testRuns.length} tests`, - body: [JSON.stringify({ - group - }), ...testRuns.map(testRun => JSON.stringify({ - testRun - }))].join('\n') + bodyDesc: `[${group.name}/${group.type}] test group`, + body: group }); + + if (!groupResp) { + return; + } + + let bufferBytes = 0; + const buffer = []; + + const flushBuffer = async () => { + var _this$config8; + + await this.req({ + auth: true, + path: '/v2/test_runs', + query: { + buildId: (_this$config8 = this.config) === null || _this$config8 === void 0 ? void 0 : _this$config8.buildId, + groupId: groupResp.groupId, + groupType: group.type + }, + bodyDesc: `[${group.name}/${group.type}] Chunk of ${bufferBytes} bytes`, + body: buffer.join('\n') + }); + buffer.length = 0; + bufferBytes = 0; + }; // send test runs in chunks of ~500kb + + + for (const testRun of testRuns) { + const json = JSON.stringify(testRun); + bufferBytes += json.length; + buffer.push(json); + + if (bufferBytes >= 450000) { + await flushBuffer(); + } + } + + if (bufferBytes) { + await flushBuffer(); + } } /** * In order to allow this code to run before @kbn/utils is built, @kbn/pm will pass From 0b37272c8eee5d0aa75b071aacb639352c23083f Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 27 Apr 2022 11:18:15 -0700 Subject: [PATCH 42/78] [jest] automatically determine run order (attempt 2) (#131091) --- .buildkite/package-lock.json | 915 ++++++++++++++++++ .buildkite/package.json | 2 +- .buildkite/pipelines/es_snapshots/verify.yml | 11 +- .buildkite/pipelines/on_merge.yml | 25 +- .buildkite/pipelines/pull_request/base.yml | 25 +- .buildkite/scripts/lifecycle/pre_command.sh | 16 +- .buildkite/scripts/steps/test/jest_env.sh | 7 + .../scripts/steps/test/jest_parallel.sh | 40 +- .../steps/test/pick_jest_config_run_order.js | 22 + .../steps/test/pick_jest_config_run_order.sh | 9 + .buildkite/yarn.lock | 180 ---- .gitignore | 1 + package.json | 2 - packages/BUILD.bazel | 2 - packages/kbn-bazel-runner/jest.config.js | 13 - packages/kbn-ci-stats-client/BUILD.bazel | 120 --- packages/kbn-ci-stats-client/README.md | 3 - packages/kbn-ci-stats-client/jest.config.js | 13 - packages/kbn-ci-stats-client/package.json | 10 - .../src/ci_stats_client.ts | 88 -- packages/kbn-ci-stats-client/src/index.ts | 9 - packages/kbn-ci-stats-client/tsconfig.json | 17 - packages/kbn-ci-stats-core/README.md | 2 +- packages/kbn-ci-stats-core/jest.config.js | 13 - packages/kbn-ci-stats-reporter/jest.config.js | 13 - .../jest.integration.config.js | 13 - packages/kbn-generate/jest.config.js | 13 - packages/kbn-jest-serializers/jest.config.js | 13 - .../kbn-kibana-json-schema/jest.config.js | 13 - packages/kbn-plugin-discovery/jest.config.js | 13 - .../kbn-shared-ux-services/jest.config.js | 13 - .../kbn-shared-ux-storybook/jest.config.js | 13 - packages/kbn-sort-package-json/jest.config.js | 13 - packages/kbn-stdio-dev-helpers/jest.config.js | 13 - packages/kbn-test/jest-preset.js | 16 +- .../kbn-test/jest_integration/jest-preset.js | 16 +- .../src/jest/run_check_jest_configs_cli.ts | 4 +- src/plugins/event_annotation/jest.config.js | 18 - src/plugins/shared_ux/jest.config.js | 19 - yarn.lock | 8 - 40 files changed, 1015 insertions(+), 741 deletions(-) create mode 100644 .buildkite/package-lock.json create mode 100644 .buildkite/scripts/steps/test/jest_env.sh create mode 100644 .buildkite/scripts/steps/test/pick_jest_config_run_order.js create mode 100644 .buildkite/scripts/steps/test/pick_jest_config_run_order.sh delete mode 100644 .buildkite/yarn.lock delete mode 100644 packages/kbn-bazel-runner/jest.config.js delete mode 100644 packages/kbn-ci-stats-client/BUILD.bazel delete mode 100644 packages/kbn-ci-stats-client/README.md delete mode 100644 packages/kbn-ci-stats-client/jest.config.js delete mode 100644 packages/kbn-ci-stats-client/package.json delete mode 100644 packages/kbn-ci-stats-client/src/ci_stats_client.ts delete mode 100644 packages/kbn-ci-stats-client/src/index.ts delete mode 100644 packages/kbn-ci-stats-client/tsconfig.json delete mode 100644 packages/kbn-ci-stats-core/jest.config.js delete mode 100644 packages/kbn-ci-stats-reporter/jest.config.js delete mode 100644 packages/kbn-eslint-plugin-imports/jest.integration.config.js delete mode 100644 packages/kbn-generate/jest.config.js delete mode 100644 packages/kbn-jest-serializers/jest.config.js delete mode 100644 packages/kbn-kibana-json-schema/jest.config.js delete mode 100644 packages/kbn-plugin-discovery/jest.config.js delete mode 100755 packages/kbn-shared-ux-services/jest.config.js delete mode 100644 packages/kbn-shared-ux-storybook/jest.config.js delete mode 100644 packages/kbn-sort-package-json/jest.config.js delete mode 100644 packages/kbn-stdio-dev-helpers/jest.config.js delete mode 100644 src/plugins/event_annotation/jest.config.js delete mode 100644 src/plugins/shared_ux/jest.config.js diff --git a/.buildkite/package-lock.json b/.buildkite/package-lock.json new file mode 100644 index 0000000000000..cfbcb9ec1aae8 --- /dev/null +++ b/.buildkite/package-lock.json @@ -0,0 +1,915 @@ +{ + "name": "kibana-buildkite", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "kibana-buildkite", + "version": "1.0.0", + "dependencies": { + "kibana-buildkite-library": "git+https://git@github.com/elastic/kibana-buildkite-library#0f95579ace8100de9f1ad4cc16976b9ec6d5841e" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", + "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", + "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", + "dependencies": { + "@octokit/types": "^6.34.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", + "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", + "dependencies": { + "@octokit/types": "^6.34.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/rest": { + "version": "18.12.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", + "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "dependencies": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.34.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", + "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", + "dependencies": { + "@octokit/openapi-types": "^11.2.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/kibana-buildkite-library": { + "version": "1.0.0", + "resolved": "git+https://git@github.com/elastic/kibana-buildkite-library.git#0f95579ace8100de9f1ad4cc16976b9ec6d5841e", + "integrity": "sha512-Ayiyy3rAE/jOWcR65vxiv4zacMlpxuRZ+WKvly6magfClWTWIUTcW1aiOH2/PYWP3faiCbIDHOyxLeGGajk5dQ==", + "license": "MIT", + "dependencies": { + "@octokit/rest": "^18.10.0", + "axios": "^0.21.4", + "globby": "^11.1.0", + "js-yaml": "^4.1.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + }, + "dependencies": { + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "requires": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", + "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==" + }, + "@octokit/plugin-paginate-rest": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", + "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", + "requires": { + "@octokit/types": "^6.34.0" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "requires": {} + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", + "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", + "requires": { + "@octokit/types": "^6.34.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "18.12.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", + "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "requires": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "@octokit/types": { + "version": "6.34.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", + "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", + "requires": { + "@octokit/openapi-types": "^11.2.0" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + }, + "before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "requires": { + "reusify": "^1.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "kibana-buildkite-library": { + "version": "git+https://git@github.com/elastic/kibana-buildkite-library.git#0f95579ace8100de9f1ad4cc16976b9ec6d5841e", + "integrity": "sha512-Ayiyy3rAE/jOWcR65vxiv4zacMlpxuRZ+WKvly6magfClWTWIUTcW1aiOH2/PYWP3faiCbIDHOyxLeGGajk5dQ==", + "from": "kibana-buildkite-library@git+https://git@github.com/elastic/kibana-buildkite-library#0f95579ace8100de9f1ad4cc16976b9ec6d5841e", + "requires": { + "@octokit/rest": "^18.10.0", + "axios": "^0.21.4", + "globby": "^11.1.0", + "js-yaml": "^4.1.0" + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/.buildkite/package.json b/.buildkite/package.json index 385c9f2429f79..551def1fa1800 100644 --- a/.buildkite/package.json +++ b/.buildkite/package.json @@ -3,6 +3,6 @@ "version": "1.0.0", "private": true, "dependencies": { - "kibana-buildkite-library": "elastic/kibana-buildkite-library" + "kibana-buildkite-library": "git+https://git@github.com/elastic/kibana-buildkite-library#0f95579ace8100de9f1ad4cc16976b9ec6d5841e" } } diff --git a/.buildkite/pipelines/es_snapshots/verify.yml b/.buildkite/pipelines/es_snapshots/verify.yml index a4a6b1303f70d..729d0d1b37b5f 100755 --- a/.buildkite/pipelines/es_snapshots/verify.yml +++ b/.buildkite/pipelines/es_snapshots/verify.yml @@ -53,13 +53,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/test/jest_integration.sh - label: 'Jest Integration Tests' - parallelism: 3 + - command: .buildkite/scripts/steps/test/pick_jest_config_run_order.sh + label: 'Pick Jest Config Run Order' agents: - queue: n2-4 - timeout_in_minutes: 120 - key: jest-integration + queue: kibana-default + env: + FILTER_JEST_CONFIG_TYPE: integration retry: automatic: - exit_status: '*' diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index 8b94f4d7c22c7..1c89c4e90893b 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -157,29 +157,14 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/test/jest.sh - label: 'Jest Tests' - parallelism: 8 + - command: .buildkite/scripts/steps/test/pick_jest_config_run_order.sh + label: 'Pick Jest Config Run Order' agents: - queue: n2-4-spot - timeout_in_minutes: 90 - key: jest - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/test/jest_integration.sh - label: 'Jest Integration Tests' - parallelism: 3 - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - key: jest-integration + queue: kibana-default retry: automatic: - - exit_status: '-1' - limit: 3 + - exit_status: '*' + limit: 1 - command: .buildkite/scripts/steps/test/api_integration.sh label: 'API Integration Tests' diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index fc12a96964fb2..4b4104f18c627 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -123,29 +123,14 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/test/jest.sh - label: 'Jest Tests' - parallelism: 8 + - command: .buildkite/scripts/steps/test/pick_jest_config_run_order.sh + label: 'Pick Jest Config Run Order' agents: - queue: n2-4-spot - timeout_in_minutes: 90 - key: jest - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/test/jest_integration.sh - label: 'Jest Integration Tests' - parallelism: 3 - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - key: jest-integration + queue: kibana-default retry: automatic: - - exit_status: '-1' - limit: 3 + - exit_status: '*' + limit: 1 - command: .buildkite/scripts/steps/test/api_integration.sh label: 'API Integration Tests' diff --git a/.buildkite/scripts/lifecycle/pre_command.sh b/.buildkite/scripts/lifecycle/pre_command.sh index e7a176a5c2666..8f3776db3ca6b 100755 --- a/.buildkite/scripts/lifecycle/pre_command.sh +++ b/.buildkite/scripts/lifecycle/pre_command.sh @@ -9,21 +9,7 @@ export BUILDKITE_TOKEN echo '--- Install buildkite dependencies' cd '.buildkite' - -# If this yarn install is terminated early, e.g. if the build is cancelled in buildkite, -# A node module could end up in a bad state that can cause all future builds to fail -# So, let's cache clean and try again to make sure that's not what caused the error -install_deps() { - yarn install --production --pure-lockfile - EXIT=$? - if [[ "$EXIT" != "0" ]]; then - yarn cache clean - fi - return $EXIT -} - -retry 5 15 install_deps - +retry 5 15 npm ci cd .. echo '--- Agent Debug/SSH Info' diff --git a/.buildkite/scripts/steps/test/jest_env.sh b/.buildkite/scripts/steps/test/jest_env.sh new file mode 100644 index 0000000000000..80e88bebba184 --- /dev/null +++ b/.buildkite/scripts/steps/test/jest_env.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# keys used to associate test group data in ci-stats with Jest execution order +export TEST_GROUP_TYPE_UNIT="Jest Unit Tests" +export TEST_GROUP_TYPE_INTEGRATION="Jest Integration Tests" diff --git a/.buildkite/scripts/steps/test/jest_parallel.sh b/.buildkite/scripts/steps/test/jest_parallel.sh index 27eb9e9216365..86c685d82c7e7 100755 --- a/.buildkite/scripts/steps/test/jest_parallel.sh +++ b/.buildkite/scripts/steps/test/jest_parallel.sh @@ -2,36 +2,38 @@ set -uo pipefail -JOB=$BUILDKITE_PARALLEL_JOB -JOB_COUNT=$BUILDKITE_PARALLEL_JOB_COUNT +source .buildkite/scripts/steps/test/jest_env.sh -# a jest failure will result in the script returning an exit code of 10 +export JOB=$BUILDKITE_PARALLEL_JOB -i=0 +# a jest failure will result in the script returning an exit code of 10 exitCode=0 -# run unit tests in parallel if [[ "$1" == 'jest.config.js' ]]; then + # run unit tests in parallel parallelism="-w2" + TEST_TYPE="unit" else + # run integration tests in-band parallelism="--runInBand" + TEST_TYPE="integration" fi +export TEST_TYPE +echo "--- downloading integration test run order" +buildkite-agent artifact download jest_run_order.json . +configs=$(jq -r 'getpath([env.TEST_TYPE]) | .groups[env.JOB | tonumber].names | .[]' jest_run_order.json) + while read -r config; do - if [ "$((i % JOB_COUNT))" -eq "$JOB" ]; then - echo "--- $ node scripts/jest --config $config" - node --max-old-space-size=14336 ./scripts/jest --config="$config" "$parallelism" --coverage=false --passWithNoTests - lastCode=$? - - if [ $lastCode -ne 0 ]; then - exitCode=10 - echo "Jest exited with code $lastCode" - echo "^^^ +++" - fi + echo "--- $ node scripts/jest --config $config" + NODE_OPTIONS="--max-old-space-size=14336" node ./scripts/jest --config="$config" "$parallelism" --coverage=false --passWithNoTests + lastCode=$? + + if [ $lastCode -ne 0 ]; then + exitCode=10 + echo "Jest exited with code $lastCode" + echo "^^^ +++" fi - - ((i=i+1)) -# uses heredoc to avoid the while loop being in a sub-shell thus unable to overwrite exitCode -done <<< "$(find src x-pack packages -name "$1" -not -path "*/__fixtures__/*" | sort)" +done <<< "$configs" exit $exitCode diff --git a/.buildkite/scripts/steps/test/pick_jest_config_run_order.js b/.buildkite/scripts/steps/test/pick_jest_config_run_order.js new file mode 100644 index 0000000000000..a5fa499419ab5 --- /dev/null +++ b/.buildkite/scripts/steps/test/pick_jest_config_run_order.js @@ -0,0 +1,22 @@ +/* + * 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 { CiStats } = require('kibana-buildkite-library'); + +(async () => { + try { + await CiStats.pickTestGroupRunOrder(); + } catch (ex) { + console.error('CI Stats Error', ex.message); + if (ex.response) { + console.error('HTTP Error Response Status', ex.response.status); + console.error('HTTP Error Response Body', ex.response.data); + } + process.exit(1); + } +})(); diff --git a/.buildkite/scripts/steps/test/pick_jest_config_run_order.sh b/.buildkite/scripts/steps/test/pick_jest_config_run_order.sh new file mode 100644 index 0000000000000..37d4e629c90b0 --- /dev/null +++ b/.buildkite/scripts/steps/test/pick_jest_config_run_order.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh +source .buildkite/scripts/steps/test/jest_env.sh + +echo '--- Pick Jest Config Run Order' +node "$(dirname "${0}")/pick_jest_config_run_order.js" diff --git a/.buildkite/yarn.lock b/.buildkite/yarn.lock deleted file mode 100644 index c5a4e404ba970..0000000000000 --- a/.buildkite/yarn.lock +++ /dev/null @@ -1,180 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@octokit/auth-token@^2.4.4": - version "2.5.0" - resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz" - integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== - dependencies: - "@octokit/types" "^6.0.3" - -"@octokit/core@^3.5.1": - version "3.5.1" - resolved "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz" - integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.6.0" - "@octokit/request-error" "^2.0.5" - "@octokit/types" "^6.0.3" - before-after-hook "^2.2.0" - universal-user-agent "^6.0.0" - -"@octokit/endpoint@^6.0.1": - version "6.0.12" - resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz" - integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== - dependencies: - "@octokit/types" "^6.0.3" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/graphql@^4.5.8": - version "4.8.0" - resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz" - integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== - dependencies: - "@octokit/request" "^5.6.0" - "@octokit/types" "^6.0.3" - universal-user-agent "^6.0.0" - -"@octokit/openapi-types@^11.2.0": - version "11.2.0" - resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz" - integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== - -"@octokit/plugin-paginate-rest@^2.16.8": - version "2.17.0" - resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz" - integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== - dependencies: - "@octokit/types" "^6.34.0" - -"@octokit/plugin-request-log@^1.0.4": - version "1.0.4" - resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz" - integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== - -"@octokit/plugin-rest-endpoint-methods@^5.12.0": - version "5.13.0" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz" - integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== - dependencies: - "@octokit/types" "^6.34.0" - deprecation "^2.3.1" - -"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz" - integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== - dependencies: - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^5.6.0": - version "5.6.2" - resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz" - integrity sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.1.0" - "@octokit/types" "^6.16.1" - is-plain-object "^5.0.0" - node-fetch "^2.6.1" - universal-user-agent "^6.0.0" - -"@octokit/rest@^18.10.0": - version "18.12.0" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz" - integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== - dependencies: - "@octokit/core" "^3.5.1" - "@octokit/plugin-paginate-rest" "^2.16.8" - "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^5.12.0" - -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": - version "6.34.0" - resolved "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz" - integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== - dependencies: - "@octokit/openapi-types" "^11.2.0" - -axios@^0.21.4: - version "0.21.4" - resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== - dependencies: - follow-redirects "^1.14.0" - -before-after-hook@^2.2.0: - version "2.2.2" - resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz" - integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== - -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - -follow-redirects@^1.14.0: - version "1.14.5" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz" - integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA== - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - -kibana-buildkite-library@elastic/kibana-buildkite-library: - version "1.0.0" - resolved "https://codeload.github.com/elastic/kibana-buildkite-library/tar.gz/bd0bec4c7af5f64a12c781d03cedb9fb2386bfbd" - dependencies: - "@octokit/rest" "^18.10.0" - axios "^0.21.4" - -node-fetch@^2.6.1: - version "2.6.6" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz" - integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA== - dependencies: - whatwg-url "^5.0.0" - -once@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= diff --git a/.gitignore b/.gitignore index 7c20367dfe6de..fac261b2a97ea 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ selenium *.swo *.out package-lock.json +!/.buildkite/package-lock.json .yo-rc.json .vscode *.sublime-* diff --git a/package.json b/package.json index 24c03762b1bed..d6d4a2225a08c 100644 --- a/package.json +++ b/package.json @@ -484,7 +484,6 @@ "@kbn/babel-preset": "link:bazel-bin/packages/kbn-babel-preset", "@kbn/bazel-packages": "link:bazel-bin/packages/kbn-bazel-packages", "@kbn/bazel-runner": "link:bazel-bin/packages/kbn-bazel-runner", - "@kbn/ci-stats-client": "link:bazel-bin/packages/kbn-ci-stats-client", "@kbn/ci-stats-core": "link:bazel-bin/packages/kbn-ci-stats-core", "@kbn/ci-stats-reporter": "link:bazel-bin/packages/kbn-ci-stats-reporter", "@kbn/cli-dev-mode": "link:bazel-bin/packages/kbn-cli-dev-mode", @@ -613,7 +612,6 @@ "@types/kbn__axe-config": "link:bazel-bin/packages/kbn-axe-config/npm_module_types", "@types/kbn__bazel-packages": "link:bazel-bin/packages/kbn-bazel-packages/npm_module_types", "@types/kbn__bazel-runner": "link:bazel-bin/packages/kbn-bazel-runner/npm_module_types", - "@types/kbn__ci-stats-client": "link:bazel-bin/packages/kbn-ci-stats-client/npm_module_types", "@types/kbn__ci-stats-core": "link:bazel-bin/packages/kbn-ci-stats-core/npm_module_types", "@types/kbn__ci-stats-reporter": "link:bazel-bin/packages/kbn-ci-stats-reporter/npm_module_types", "@types/kbn__cli-dev-mode": "link:bazel-bin/packages/kbn-cli-dev-mode/npm_module_types", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 567e5dd65d664..43c80fc882159 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -26,7 +26,6 @@ filegroup( "//packages/kbn-babel-preset:build", "//packages/kbn-bazel-packages:build", "//packages/kbn-bazel-runner:build", - "//packages/kbn-ci-stats-client:build", "//packages/kbn-ci-stats-core:build", "//packages/kbn-ci-stats-reporter:build", "//packages/kbn-cli-dev-mode:build", @@ -126,7 +125,6 @@ filegroup( "//packages/kbn-axe-config:build_types", "//packages/kbn-bazel-packages:build_types", "//packages/kbn-bazel-runner:build_types", - "//packages/kbn-ci-stats-client:build_types", "//packages/kbn-ci-stats-core:build_types", "//packages/kbn-ci-stats-reporter:build_types", "//packages/kbn-cli-dev-mode:build_types", diff --git a/packages/kbn-bazel-runner/jest.config.js b/packages/kbn-bazel-runner/jest.config.js deleted file mode 100644 index 4d4f77a8f43f3..0000000000000 --- a/packages/kbn-bazel-runner/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-bazel-runner'], -}; diff --git a/packages/kbn-ci-stats-client/BUILD.bazel b/packages/kbn-ci-stats-client/BUILD.bazel deleted file mode 100644 index 7017adc604416..0000000000000 --- a/packages/kbn-ci-stats-client/BUILD.bazel +++ /dev/null @@ -1,120 +0,0 @@ -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", "pkg_npm", "pkg_npm_types", "ts_project") - -PKG_DIRNAME = "kbn-ci-stats-client" -PKG_REQUIRE_NAME = "@kbn/ci-stats-client" - -SOURCE_FILES = glob( - [ - "src/**/*.ts", - ], - exclude = [ - "**/*.test.*", - ], -) - -SRCS = SOURCE_FILES - -filegroup( - name = "srcs", - srcs = SRCS, -) - -NPM_MODULE_EXTRA_FILES = [ - "package.json", -] - -# In this array place runtime dependencies, including other packages and NPM packages -# which must be available for this code to run. -# -# To reference other packages use: -# "//repo/relative/path/to/package" -# eg. "//packages/kbn-utils" -# -# To reference a NPM package use: -# "@npm//name-of-package" -# eg. "@npm//lodash" -RUNTIME_DEPS = [ - "@npm//axios", - "//packages/kbn-ci-stats-core", - "//packages/kbn-tooling-log", -] - -# In this array place dependencies necessary to build the types, which will include the -# :npm_module_types target of other packages and packages from NPM, including @types/* -# packages. -# -# To reference the types for another package use: -# "//repo/relative/path/to/package:npm_module_types" -# eg. "//packages/kbn-utils:npm_module_types" -# -# References to NPM packages work the same as RUNTIME_DEPS -TYPES_DEPS = [ - "@npm//@types/node", - "@npm//@types/jest", - "@npm//axios", - "//packages/kbn-ci-stats-core:npm_module_types", - "//packages/kbn-tooling-log:npm_module_types", -] - -jsts_transpiler( - name = "target_node", - srcs = SRCS, - build_pkg_name = package_name(), -) - -ts_config( - name = "tsconfig", - src = "tsconfig.json", - deps = [ - "//:tsconfig.base.json", - "//:tsconfig.bazel.json", - ], -) - -ts_project( - name = "tsc_types", - args = ['--pretty'], - srcs = SRCS, - deps = TYPES_DEPS, - declaration = True, - emit_declaration_only = True, - out_dir = "target_types", - root_dir = "src", - tsconfig = ":tsconfig", -) - -js_library( - name = PKG_DIRNAME, - srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], - package_name = PKG_REQUIRE_NAME, - visibility = ["//visibility:public"], -) - -pkg_npm( - name = "npm_module", - deps = [":" + PKG_DIRNAME], -) - -filegroup( - name = "build", - srcs = [":npm_module"], - visibility = ["//visibility:public"], -) - -pkg_npm_types( - name = "npm_module_types", - srcs = SRCS, - deps = [":tsc_types"], - package_name = PKG_REQUIRE_NAME, - tsconfig = ":tsconfig", - visibility = ["//visibility:public"], -) - -filegroup( - name = "build_types", - srcs = [":npm_module_types"], - visibility = ["//visibility:public"], -) diff --git a/packages/kbn-ci-stats-client/README.md b/packages/kbn-ci-stats-client/README.md deleted file mode 100644 index d1f6c59e978c9..0000000000000 --- a/packages/kbn-ci-stats-client/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @kbn/ci-stats-client - -Client for reading data stored at https://ci-stats.kibana.dev \ No newline at end of file diff --git a/packages/kbn-ci-stats-client/jest.config.js b/packages/kbn-ci-stats-client/jest.config.js deleted file mode 100644 index d855d7886d0d7..0000000000000 --- a/packages/kbn-ci-stats-client/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-ci-stats-client'], -}; diff --git a/packages/kbn-ci-stats-client/package.json b/packages/kbn-ci-stats-client/package.json deleted file mode 100644 index 709f6a3454d59..0000000000000 --- a/packages/kbn-ci-stats-client/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "@kbn/ci-stats-client", - "private": true, - "version": "1.0.0", - "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } -} diff --git a/packages/kbn-ci-stats-client/src/ci_stats_client.ts b/packages/kbn-ci-stats-client/src/ci_stats_client.ts deleted file mode 100644 index a7ab6f1cc4cb8..0000000000000 --- a/packages/kbn-ci-stats-client/src/ci_stats_client.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 Axios from 'axios'; -import { ToolingLog } from '@kbn/tooling-log'; - -import { parseConfig, Config, CiStatsMetadata } from '@kbn/ci-stats-core'; - -interface LatestTestGroupStatsOptions { - /** The Kibana branch to get stats for, eg "main" */ - branch: string; - /** The CI job names to filter builds by, eg "kibana-hourly" */ - ciJobNames: string[]; - /** Filter test groups by group type */ - testGroupType?: string; -} - -interface CompleteSuccessBuildSource { - jobName: string; - jobRunner: string; - completedAt: string; - commit: string; - startedAt: string; - branch: string; - result: 'SUCCESS'; - jobId: string; - targetBranch: string | null; - fromKibanaCiProduction: boolean; - requiresValidMetrics: boolean | null; - jobUrl: string; - mergeBase: string | null; -} - -interface TestGroupSource { - '@timestamp': string; - buildId: string; - name: string; - type: string; - startTime: string; - durationMs: number; - meta: CiStatsMetadata; -} - -interface LatestTestGroupStatsResp { - build: CompleteSuccessBuildSource & { id: string }; - testGroups: Array; -} - -export class CiStatsClient { - /** - * Create a CiStatsReporter by inspecting the ENV for the necessary config - */ - static fromEnv(log: ToolingLog) { - return new CiStatsClient(parseConfig(log)); - } - - constructor(private readonly config?: Config) {} - - isEnabled() { - return !!this.config?.apiToken; - } - - async getLatestTestGroupStats(options: LatestTestGroupStatsOptions) { - if (!this.config || !this.config.apiToken) { - throw new Error('No ciStats config available, call `isEnabled()` before using the client'); - } - - const resp = await Axios.request({ - baseURL: 'https://ci-stats.kibana.dev', - url: '/v1/test_group_stats', - params: { - branch: options.branch, - ci_job_name: options.ciJobNames.join(','), - test_group_type: options.testGroupType, - }, - headers: { - Authentication: `token ${this.config.apiToken}`, - }, - }); - - return resp.data; - } -} diff --git a/packages/kbn-ci-stats-client/src/index.ts b/packages/kbn-ci-stats-client/src/index.ts deleted file mode 100644 index ac32c69b9f7b7..0000000000000 --- a/packages/kbn-ci-stats-client/src/index.ts +++ /dev/null @@ -1,9 +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. - */ - -export { CiStatsClient } from './ci_stats_client'; diff --git a/packages/kbn-ci-stats-client/tsconfig.json b/packages/kbn-ci-stats-client/tsconfig.json deleted file mode 100644 index a8cfc2cceb08b..0000000000000 --- a/packages/kbn-ci-stats-client/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tsconfig.bazel.json", - "compilerOptions": { - "declaration": true, - "emitDeclarationOnly": true, - "outDir": "target_types", - "rootDir": "src", - "stripInternal": false, - "types": [ - "jest", - "node" - ] - }, - "include": [ - "src/**/*" - ] -} diff --git a/packages/kbn-ci-stats-core/README.md b/packages/kbn-ci-stats-core/README.md index b2e34a492b745..741728394aa83 100644 --- a/packages/kbn-ci-stats-core/README.md +++ b/packages/kbn-ci-stats-core/README.md @@ -1,3 +1,3 @@ # @kbn/ci-stats-core -Config and types used by `@kbn/ci-stats-client` and `@kbn/ci-stats-reporter`. \ No newline at end of file +Config and types used by `@kbn/ci-stats-reporter`. \ No newline at end of file diff --git a/packages/kbn-ci-stats-core/jest.config.js b/packages/kbn-ci-stats-core/jest.config.js deleted file mode 100644 index 0feb7b4e1b872..0000000000000 --- a/packages/kbn-ci-stats-core/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-ci-stats-core'], -}; diff --git a/packages/kbn-ci-stats-reporter/jest.config.js b/packages/kbn-ci-stats-reporter/jest.config.js deleted file mode 100644 index bf58324f440a3..0000000000000 --- a/packages/kbn-ci-stats-reporter/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-ci-stats-reporter'], -}; diff --git a/packages/kbn-eslint-plugin-imports/jest.integration.config.js b/packages/kbn-eslint-plugin-imports/jest.integration.config.js deleted file mode 100644 index e7d05a6222c88..0000000000000 --- a/packages/kbn-eslint-plugin-imports/jest.integration.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_integration_node', - rootDir: '../..', - roots: ['/packages/kbn-eslint-plugin-imports'], -}; diff --git a/packages/kbn-generate/jest.config.js b/packages/kbn-generate/jest.config.js deleted file mode 100644 index b72f891cfb1a7..0000000000000 --- a/packages/kbn-generate/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-generate'], -}; diff --git a/packages/kbn-jest-serializers/jest.config.js b/packages/kbn-jest-serializers/jest.config.js deleted file mode 100644 index 23fad67c028bf..0000000000000 --- a/packages/kbn-jest-serializers/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-jest-serializers'], -}; diff --git a/packages/kbn-kibana-json-schema/jest.config.js b/packages/kbn-kibana-json-schema/jest.config.js deleted file mode 100644 index 00bc8f55adc57..0000000000000 --- a/packages/kbn-kibana-json-schema/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-kibana-json-schema'], -}; diff --git a/packages/kbn-plugin-discovery/jest.config.js b/packages/kbn-plugin-discovery/jest.config.js deleted file mode 100644 index 37d7a4f2b63a2..0000000000000 --- a/packages/kbn-plugin-discovery/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-plugin-discovery'], -}; diff --git a/packages/kbn-shared-ux-services/jest.config.js b/packages/kbn-shared-ux-services/jest.config.js deleted file mode 100755 index f1ef008d0f62d..0000000000000 --- a/packages/kbn-shared-ux-services/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['/packages/kbn-shared-ux-services'], -}; diff --git a/packages/kbn-shared-ux-storybook/jest.config.js b/packages/kbn-shared-ux-storybook/jest.config.js deleted file mode 100644 index 91285e025f069..0000000000000 --- a/packages/kbn-shared-ux-storybook/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['/packages/kbn-shared-ux-storybook'], -}; diff --git a/packages/kbn-sort-package-json/jest.config.js b/packages/kbn-sort-package-json/jest.config.js deleted file mode 100644 index ae0651be19e61..0000000000000 --- a/packages/kbn-sort-package-json/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-sort-package-json'], -}; diff --git a/packages/kbn-stdio-dev-helpers/jest.config.js b/packages/kbn-stdio-dev-helpers/jest.config.js deleted file mode 100644 index 31a8aab16c7e5..0000000000000 --- a/packages/kbn-stdio-dev-helpers/jest.config.js +++ /dev/null @@ -1,13 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-stdio-dev-helpers'], -}; diff --git a/packages/kbn-test/jest-preset.js b/packages/kbn-test/jest-preset.js index 6245532b00e44..fb535c2766cc4 100644 --- a/packages/kbn-test/jest-preset.js +++ b/packages/kbn-test/jest-preset.js @@ -63,12 +63,16 @@ module.exports = { rootDirectory: '.', }, ], - [ - '@kbn/test/target_node/jest/ci_stats_jest_reporter', - { - testGroupType: 'Jest Unit Tests', - }, - ], + ...(process.env.TEST_GROUP_TYPE_UNIT + ? [ + [ + '@kbn/test/target_node/jest/ci_stats_jest_reporter', + { + testGroupType: process.env.TEST_GROUP_TYPE_UNIT, + }, + ], + ] + : []), ], // The paths to modules that run some code to configure or set up the testing environment before each test diff --git a/packages/kbn-test/jest_integration/jest-preset.js b/packages/kbn-test/jest_integration/jest-preset.js index f5593e3f57fb6..1f2626aef532e 100644 --- a/packages/kbn-test/jest_integration/jest-preset.js +++ b/packages/kbn-test/jest_integration/jest-preset.js @@ -28,12 +28,16 @@ module.exports = { reportName: 'Jest Integration Tests', }, ], - [ - '@kbn/test/target_node/jest/ci_stats_jest_reporter', - { - testGroupType: 'Jest Integration Tests', - }, - ], + ...(process.env.TEST_GROUP_TYPE_INTEGRATION + ? [ + [ + '@kbn/test/target_node/jest/ci_stats_jest_reporter', + { + testGroupType: process.env.TEST_GROUP_TYPE_INTEGRATION, + }, + ], + ] + : []), ], coverageReporters: !!process.env.CI ? [['json', { file: 'jest-integration.json' }]] diff --git a/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts b/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts index 42766ad3f5a31..7a50e88b3396d 100644 --- a/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts +++ b/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts @@ -17,14 +17,14 @@ import { getAllRepoRelativeBazelPackageDirs } from '@kbn/bazel-packages'; import { JestConfigs, CONFIG_NAMES } from './configs'; const unitTestingTemplate: string = `module.exports = { - preset: '@kbn/test', + preset: '@kbn/test/jest_node', rootDir: '{{{relToRoot}}}', roots: ['/{{{modulePath}}}'], }; `; const integrationTestingTemplate: string = `module.exports = { - preset: '@kbn/test/jest_integration', + preset: '@kbn/test/jest_integration_node', rootDir: '{{{relToRoot}}}', roots: ['/{{{modulePath}}}'], }; diff --git a/src/plugins/event_annotation/jest.config.js b/src/plugins/event_annotation/jest.config.js deleted file mode 100644 index a6ea4a6b430df..0000000000000 --- a/src/plugins/event_annotation/jest.config.js +++ /dev/null @@ -1,18 +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. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../../..', - roots: ['/src/plugins/event_annotation'], - coverageDirectory: '/target/kibana-coverage/jest/src/plugins/event_annotation', - coverageReporters: ['text', 'html'], - collectCoverageFrom: [ - '/src/plugins/event_annotation/{common,public,server}/**/*.{ts,tsx}', - ], -}; diff --git a/src/plugins/shared_ux/jest.config.js b/src/plugins/shared_ux/jest.config.js deleted file mode 100644 index bc8d67e5ac35b..0000000000000 --- a/src/plugins/shared_ux/jest.config.js +++ /dev/null @@ -1,19 +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. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../../..', - roots: ['/src/plugins/shared_ux'], - transform: { - '^.+\\.stories\\.tsx?$': '@storybook/addon-storyshots/injectFileName', - }, - coverageDirectory: '/target/kibana-coverage/jest/src/plugins/shared_ux', - coverageReporters: ['text', 'html'], - collectCoverageFrom: ['/src/plugins/shared_ux/{common,public,server}/**/*.{js,ts,tsx}'], -}; diff --git a/yarn.lock b/yarn.lock index b0a457c84d3ac..e4461999d72ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2968,10 +2968,6 @@ version "0.0.0" uid "" -"@kbn/ci-stats-client@link:bazel-bin/packages/kbn-ci-stats-client": - version "0.0.0" - uid "" - "@kbn/ci-stats-core@link:bazel-bin/packages/kbn-ci-stats-core": version "0.0.0" uid "" @@ -6064,10 +6060,6 @@ version "0.0.0" uid "" -"@types/kbn__ci-stats-client@link:bazel-bin/packages/kbn-ci-stats-client/npm_module_types": - version "0.0.0" - uid "" - "@types/kbn__ci-stats-core@link:bazel-bin/packages/kbn-ci-stats-core/npm_module_types": version "0.0.0" uid "" From 1bdbecbcb49c52b6153ac413d0c3fa5ec9cd6acf Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 27 Apr 2022 11:31:25 -0700 Subject: [PATCH 43/78] [ci-stats/test-groups] report overall result of test group (#131094) --- .../src/ci_stats_test_group_types.ts | 4 ++++ .../lib/mocha/reporter/ci_stats_ftr_reporter.ts | 7 +++++++ .../kbn-test/src/jest/ci_stats_jest_reporter.ts | 16 ++++++++++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts b/packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts index 223273ca82cd3..298b46498aff4 100644 --- a/packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts +++ b/packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts @@ -83,6 +83,10 @@ export interface CiStatsTestGroupInfo { * The name of this specific group (within the "type") */ name: string; + /** + * Overall result of this test group + */ + result: 'fail' | 'pass' | 'skip'; /** * Arbitrary metadata associated with this group. We currently look for a ciGroup metadata property for highlighting that when appropriate */ diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/reporter/ci_stats_ftr_reporter.ts b/packages/kbn-test/src/functional_test_runner/lib/mocha/reporter/ci_stats_ftr_reporter.ts index 838b8ee9b9aa5..ee993122d7d9c 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/reporter/ci_stats_ftr_reporter.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/reporter/ci_stats_ftr_reporter.ts @@ -80,6 +80,7 @@ export function setupCiStatsFtrTestGroupReporter({ durationMs: 0, type: config.path.startsWith('x-pack') ? 'X-Pack Functional Tests' : 'Functional Tests', name: Path.relative(REPO_ROOT, config.path), + result: 'skip', meta: { ciGroup: config.get('suiteTags.include').find((t: string) => t.startsWith('ciGroup')), tags: [ @@ -117,6 +118,11 @@ export function setupCiStatsFtrTestGroupReporter({ errors.set(test, error); }); + let passCount = 0; + let failCount = 0; + runner.on('pass', () => (passCount += 1)); + runner.on('fail', () => (failCount += 1)); + runner.on('hook end', (hook: Runnable) => { if (hook.isFailed()) { const error = errors.get(hook); @@ -150,6 +156,7 @@ export function setupCiStatsFtrTestGroupReporter({ // update the durationMs group.durationMs = Date.now() - startMs; + group.result = failCount ? 'fail' : passCount ? 'pass' : 'skip'; }); lifecycle.cleanup.add(async () => { diff --git a/packages/kbn-test/src/jest/ci_stats_jest_reporter.ts b/packages/kbn-test/src/jest/ci_stats_jest_reporter.ts index 6294594ff6cf5..3ac4a64c1f3f7 100644 --- a/packages/kbn-test/src/jest/ci_stats_jest_reporter.ts +++ b/packages/kbn-test/src/jest/ci_stats_jest_reporter.ts @@ -39,6 +39,8 @@ export default class CiStatsJestReporter extends BaseReporter { private readonly reportName: string; private readonly rootDir: string; private startTime: number | undefined; + private passCount = 0; + private failCount = 0; private group: CiStatsReportTestsOptions['group'] | undefined; private readonly testRuns: CiStatsReportTestsOptions['testRuns'] = []; @@ -79,6 +81,7 @@ export default class CiStatsJestReporter extends BaseReporter { startTime: new Date(this.startTime).toJSON(), meta: {}, durationMs: 0, + result: 'skip', }; } @@ -89,6 +92,14 @@ export default class CiStatsJestReporter extends BaseReporter { let elapsedTime = 0; for (const t of testResult.testResults) { + const result = t.status === 'failed' ? 'fail' : t.status === 'passed' ? 'pass' : 'skip'; + + if (result === 'fail') { + this.failCount += 1; + } else if (result === 'pass') { + this.passCount += 1; + } + const startTime = new Date(testResult.perfStats.start + elapsedTime).toJSON(); elapsedTime += t.duration ?? 0; this.testRuns.push({ @@ -97,7 +108,7 @@ export default class CiStatsJestReporter extends BaseReporter { seq: this.testRuns.length + 1, file: Path.relative(this.rootDir, testResult.testFilePath), name: t.title, - result: t.status === 'failed' ? 'fail' : t.status === 'passed' ? 'pass' : 'skip', + result, suites: t.ancestorTitles, type: 'test', error: t.failureMessages.join('\n\n'), @@ -107,11 +118,12 @@ export default class CiStatsJestReporter extends BaseReporter { } async onRunComplete() { - if (!this.reporter || !this.group || !this.testRuns.length || !this.startTime) { + if (!this.reporter || !this.group || !this.startTime) { return; } this.group.durationMs = Date.now() - this.startTime; + this.group.result = this.failCount ? 'fail' : this.passCount ? 'pass' : 'skip'; await this.reporter.reportTests({ group: this.group, From 27b5a7f85b3be79b8886ec00d267bfdb2b5c3056 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Wed, 27 Apr 2022 16:01:06 -0300 Subject: [PATCH 44/78] [Session View] fix CSS and Dark Mode issues (#131020) * fix Session View timestamps format * updating back to investigated alert button * updating session view background * removing max width for alert and status interaction * updating buttons strategy * updatin toolbar * updating search bar * refining use_eui_theme hook * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * fixing alert flex content disposition * updating snapshot Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../back_to_investigated_alert/index.tsx | 2 +- .../back_to_investigated_alert/styles.ts | 21 +++-- .../public/components/process_tree/index.tsx | 2 +- .../public/components/process_tree/styles.ts | 14 +-- .../__snapshots__/index.test.tsx.snap | 18 ++-- .../components/process_tree_alert/index.tsx | 7 +- .../components/process_tree_alert/styles.ts | 21 +---- .../components/process_tree_node/buttons.tsx | 19 ++-- .../process_tree_node/index.test.tsx | 4 +- .../components/process_tree_node/index.tsx | 8 +- .../components/process_tree_node/styles.ts | 25 +++-- .../process_tree_node/use_button_styles.ts | 94 ++++++++++--------- .../public/components/session_view/index.tsx | 7 +- .../public/components/session_view/styles.ts | 24 ++--- .../session_view_search_bar/index.test.tsx | 4 +- .../session_view_search_bar/index.tsx | 2 +- .../session_view_search_bar/styles.ts | 13 +-- .../session_view/public/hooks/index.ts | 8 ++ .../public/hooks/use_eui_theme.ts | 44 +++++++++ 19 files changed, 190 insertions(+), 147 deletions(-) create mode 100644 x-pack/plugins/session_view/public/hooks/index.ts create mode 100644 x-pack/plugins/session_view/public/hooks/use_eui_theme.ts diff --git a/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.tsx b/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.tsx index 6252335c29f61..78ce2f4d871ea 100644 --- a/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.tsx +++ b/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.tsx @@ -29,7 +29,7 @@ export const BackToInvestigatedAlert = ({ return (
{ - const { euiTheme } = useEuiTheme(); + const { euiTheme, euiVars } = useEuiTheme(); const cached = useMemo(() => { - const { size, colors, font } = euiTheme; + const { size, font } = euiTheme; - const buttonBackgroundColor = colors.primary; + const buttonStyle = { + color: euiLightVars.euiColorEmptyShade, + backgroundColor: euiLightVars.euiColorPrimaryText, + }; const container: CSSObject = { position: 'absolute', @@ -41,18 +44,18 @@ export const useStyles = ({ isDisplayedAbove }: StylesDeps) => { if (isDisplayedAbove) { container.top = 0; - container.background = `linear-gradient(180deg, ${theme.euiColorLightestShade} 0%, transparent 100%)`; + container.background = `linear-gradient(180deg, ${euiVars.euiColorLightestShade} 0%, transparent 100%)`; } else { container.bottom = 0; - container.background = `linear-gradient(360deg, ${theme.euiColorLightestShade} 0%, transparent 100%)`; + container.background = `linear-gradient(360deg, ${euiVars.euiColorLightestShade} 0%, transparent 100%)`; } return { container, jumpBackBadge, - buttonBackgroundColor, + buttonStyle, }; - }, [isDisplayedAbove, euiTheme]); + }, [isDisplayedAbove, euiTheme, euiVars]); return cached; }; diff --git a/x-pack/plugins/session_view/public/components/process_tree/index.tsx b/x-pack/plugins/session_view/public/components/process_tree/index.tsx index f2b5fef85002c..8a06ec769b7a9 100644 --- a/x-pack/plugins/session_view/public/components/process_tree/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree/index.tsx @@ -160,7 +160,7 @@ export const ProcessTree = ({ <>
{sessionLeader && ( diff --git a/x-pack/plugins/session_view/public/components/process_tree/styles.ts b/x-pack/plugins/session_view/public/components/process_tree/styles.ts index 490829cad440b..04f8a22890c28 100644 --- a/x-pack/plugins/session_view/public/components/process_tree/styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree/styles.ts @@ -6,23 +6,23 @@ */ import { useMemo } from 'react'; -import { transparentize, useEuiTheme } from '@elastic/eui'; +import { transparentize } from '@elastic/eui'; import { CSSObject } from '@emotion/react'; -import { euiLightVars } from '@kbn/ui-theme'; // using this temporarily until the euiTheme hook is updated to include proper hex values +import { useEuiTheme } from '../../hooks'; export const useStyles = () => { - const { euiTheme } = useEuiTheme(); + const { euiTheme, euiVars } = useEuiTheme(); const cached = useMemo(() => { const { colors, font, size } = euiTheme; const defaultSelectionColor = colors.primary; - const scroller: CSSObject = { + const sessionViewProcessTree: CSSObject = { position: 'relative', fontFamily: font.familyCode, overflow: 'auto', height: '100%', - backgroundColor: euiLightVars.euiColorLightestShade, + backgroundColor: euiVars.euiColorLightestShade, paddingTop: size.base, paddingLeft: size.s, }; @@ -43,12 +43,12 @@ export const useStyles = () => { const alertSelected = transparentize(colors.danger, 0.008); return { - scroller, + sessionViewProcessTree, selectionArea, defaultSelected, alertSelected, }; - }, [euiTheme]); + }, [euiTheme, euiVars]); return cached; }; diff --git a/x-pack/plugins/session_view/public/components/process_tree_alert/__snapshots__/index.test.tsx.snap b/x-pack/plugins/session_view/public/components/process_tree_alert/__snapshots__/index.test.tsx.snap index e9f66ea10d66e..9614bb1267ea5 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_alert/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/session_view/public/components/process_tree_alert/__snapshots__/index.test.tsx.snap @@ -6,13 +6,13 @@ Object { "baseElement":
cmd test alert
@@ -53,13 +52,13 @@ Object { , "container":
cmd test alert
diff --git a/x-pack/plugins/session_view/public/components/process_tree_alert/index.tsx b/x-pack/plugins/session_view/public/components/process_tree_alert/index.tsx index 95618123d709f..954492782ab93 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_alert/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_alert/index.tsx @@ -69,14 +69,11 @@ export const ProcessTreeAlert = ({ - - - {dataOrDash(name)} - + + {dataOrDash(name)} {dataOrDash(status)} diff --git a/x-pack/plugins/session_view/public/components/process_tree_alert/styles.ts b/x-pack/plugins/session_view/public/components/process_tree_alert/styles.ts index bcd47edf56db8..db06be06cc2aa 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_alert/styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree_alert/styles.ts @@ -43,6 +43,7 @@ export const useStyles = ({ isInvestigated, isSelected }: StylesDeps) => { const alert: CSSObject = { fontFamily: font.family, display: 'flex', + gap: size.s, alignItems: 'center', minHeight: '20px', padding: `${size.xs} ${size.base}`, @@ -55,34 +56,18 @@ export const useStyles = ({ isInvestigated, isSelected }: StylesDeps) => { '&:hover': { background: hoverBgColor, }, - }; - - const alertRowItem: CSSObject = { - '&:first-of-type': { - marginRight: size.m, - }, - '&:not(:first-of-type)': { + button: { + flexShrink: 0, marginRight: size.s, }, }; - const alertRuleName: CSSObject = { - ...alertRowItem, - maxWidth: '70%', - }; - const alertStatus: CSSObject = { - ...alertRowItem, textTransform: 'capitalize', - '&, span': { - cursor: 'pointer !important', - }, }; return { alert, - alertRowItem, - alertRuleName, alertStatus, }; }, [euiTheme, isInvestigated, isSelected]); diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/buttons.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/buttons.tsx index b4a2472f11dab..9cc842aac7668 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/buttons.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_node/buttons.tsx @@ -9,6 +9,8 @@ import { EuiButton, EuiIcon } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { useButtonStyles } from './use_button_styles'; +const MAX_ALERT_COUNT = 99; + export const ChildrenProcessesButton = ({ onToggle, isExpanded, @@ -16,17 +18,18 @@ export const ChildrenProcessesButton = ({ onToggle: () => void; isExpanded: boolean; }) => { - const { button, buttonArrow, expandedIcon } = useButtonStyles({ isExpanded }); + const { button, buttonArrow } = useButtonStyles(); return ( - + ); }; @@ -40,12 +43,11 @@ export const AlertButton = ({ onToggle: () => void; alertsCount: number; }) => { - const { alertButton, alertsCountNumber, buttonArrow, expandedIcon } = useButtonStyles({ - isExpanded, - }); + const { alertButton, buttonArrow } = useButtonStyles(); return ( )} - {alertsCount > 1 && ( - ({alertsCount > 99 ? '99+' : alertsCount}) - )} - + {alertsCount > 1 && + (alertsCount > MAX_ALERT_COUNT ? ` (${MAX_ALERT_COUNT}+)` : ` (${alertsCount})`)} + ); }; diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx index a5e0305595187..7daceaa366c43 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx @@ -216,7 +216,7 @@ describe('ProcessTreeNode component', () => { expect(renderResult.queryByTestId('processTreeNodeAlertButton')).toBeTruthy(); expect(renderResult.queryByTestId('processTreeNodeAlertButton')?.textContent).toBe( - `Alerts(${sessionViewAlertProcessMock.getAlerts().length})` + `Alerts (${sessionViewAlertProcessMock.getAlerts().length})` ); }); it('renders Alerts button with 99+ when process has more than 99 alerts', async () => { @@ -234,7 +234,7 @@ describe('ProcessTreeNode component', () => { expect(renderResult.queryByTestId('processTreeNodeAlertButton')).toBeTruthy(); expect(renderResult.queryByTestId('processTreeNodeAlertButton')?.textContent).toBe( - 'Alerts(99+)' + 'Alerts (99+)' ); }); it('toggle Alert Details button when Alert button is clicked', async () => { diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx index d1b0fded615f6..3897f3c75c45f 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx @@ -105,7 +105,7 @@ export function ProcessTreeNode({ ); const isSelected = selectedProcess?.id === process.id; const styles = useStyles({ depth, hasAlerts, hasInvestigatedAlert, isSelected }); - const buttonStyles = useButtonStyles({}); + const buttonStyles = useButtonStyles(); const nodeRef = useVisible({ viewPortEl: scrollerRef.current, @@ -256,7 +256,7 @@ export function ProcessTreeNode({ > {isSessionLeader ? ( <> - {' '} + {' '} {dataOrDash(name || args?.[0])}{' '} {' '} {dataOrDash(user?.name)} @@ -269,7 +269,7 @@ export function ProcessTreeNode({ )} - + {' '} {dataOrDash(workingDirectory)}  @@ -308,7 +308,7 @@ export function ProcessTreeNode({ diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts b/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts index 6503f373240ad..55b5cee88eb44 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts @@ -6,8 +6,9 @@ */ import { useMemo } from 'react'; -import { useEuiTheme, transparentize } from '@elastic/eui'; +import { transparentize } from '@elastic/eui'; import { CSSObject } from '@emotion/react'; +import { useEuiTheme } from '../../hooks'; interface StylesDeps { depth: number; @@ -17,7 +18,7 @@ interface StylesDeps { } export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected }: StylesDeps) => { - const { euiTheme } = useEuiTheme(); + const { euiTheme, euiVars } = useEuiTheme(); const cached = useMemo(() => { const { colors, border, size, font } = euiTheme; @@ -40,12 +41,16 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected } borderLeft: border.editable, }; + const icon: CSSObject = { + color: euiVars.euiColorDarkShade, + }; + /** * gets border, bg and hover colors for a process */ const getHighlightColors = () => { let bgColor = 'none'; - const hoverColor = transparentize(colors.primary, 0.04); + let hoverColor = transparentize(colors.primary, 0.04); let borderColor = 'transparent'; let searchResColor = transparentize(colors.warning, 0.32); @@ -55,11 +60,16 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected } if (isSelected) { searchResColor = colors.warning; - bgColor = `${transparentize(colors.primary, 0.1)}!important`; + bgColor = transparentize(colors.primary, 0.08); + hoverColor = transparentize(colors.primary, 0.12); } if (hasInvestigatedAlert) { - bgColor = `${transparentize(colors.danger, 0.04)}!important`; + bgColor = transparentize(colors.danger, 0.04); + hoverColor = transparentize(colors.danger, 0.12); + if (isSelected) { + bgColor = transparentize(colors.danger, 0.08); + } } return { bgColor, borderColor, hoverColor, searchResColor }; @@ -99,7 +109,7 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected } paddingLeft: size.s, position: 'relative', verticalAlign: 'middle', - color: colors.mediumShade, + color: euiVars.euiTextSubduedColor, wordBreak: 'break-all', minHeight: `calc(${size.l} - ${size.xxs})`, lineHeight: `calc(${size.l} - ${size.xxs})`, @@ -138,8 +148,9 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected } workingDir, timeStamp, alertDetails, + icon, }; - }, [depth, euiTheme, hasAlerts, hasInvestigatedAlert, isSelected]); + }, [depth, euiTheme, hasAlerts, hasInvestigatedAlert, isSelected, euiVars]); return cached; }; diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/use_button_styles.ts b/x-pack/plugins/session_view/public/components/process_tree_node/use_button_styles.ts index 67883b12e2bba..04d3eb793bfda 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/use_button_styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree_node/use_button_styles.ts @@ -6,36 +6,48 @@ */ import { useMemo } from 'react'; -import { useEuiTheme, transparentize, shade } from '@elastic/eui'; -import { euiLightVars as theme } from '@kbn/ui-theme'; +import { transparentize } from '@elastic/eui'; import { CSSObject } from '@emotion/react'; +import { useEuiTheme } from '../../hooks'; -interface ButtonStylesDeps { - isExpanded?: boolean; -} - -export const useButtonStyles = ({ isExpanded }: ButtonStylesDeps) => { - const { euiTheme } = useEuiTheme(); +export const useButtonStyles = () => { + const { euiTheme, euiVars } = useEuiTheme(); const cached = useMemo(() => { - const { colors, border, size, font } = euiTheme; + const { border, colors, size, font } = euiTheme; const button: CSSObject = { - background: transparentize(theme.euiColorVis6, 0.04), - border: `${border.width.thin} solid ${transparentize(theme.euiColorVis6, 0.48)}`, lineHeight: '18px', height: '20px', fontSize: size.m, fontFamily: font.family, fontWeight: font.weight.medium, borderRadius: border.radius.small, - color: shade(theme.euiColorVis6, 0.25), marginLeft: size.xs, marginRight: size.xs, minWidth: 0, padding: `${size.s} ${size.xxs}`, - span: { - padding: `0px ${size.xxs} !important`, + color: euiVars.euiColorVis6_asText, + background: transparentize(euiVars.euiColorVis6, 0.04), + border: `${border.width.thin} solid ${transparentize(euiVars.euiColorVis6, 0.48)}`, + '&& > span': { + padding: `0px ${size.xxs}`, + svg: { + transition: `transform ${euiTheme.animation.extraFast}`, + }, + }, + '&&:hover, &&:focus': { + background: transparentize(euiVars.euiColorVis6, 0.04), + }, + '&.isExpanded > span svg': { + transform: `rotate(180deg)`, + }, + '&.isExpanded': { + color: colors.ghost, + background: euiVars.euiColorVis6, + '&:hover, &:focus': { + background: euiVars.euiColorVis6, + }, }, }; @@ -45,52 +57,48 @@ export const useButtonStyles = ({ isExpanded }: ButtonStylesDeps) => { const alertButton: CSSObject = { ...button, - color: colors.dangerText, - background: transparentize(colors.dangerText, 0.04), - border: `${border.width.thin} solid ${transparentize(colors.dangerText, 0.48)}`, - }; - - const alertsCountNumber: CSSObject = { - paddingLeft: size.xs, + color: euiVars.euiColorDanger, + background: transparentize(euiVars.euiColorDanger, 0.04), + border: `${border.width.thin} solid ${transparentize(euiVars.euiColorDanger, 0.48)}`, + '&&:hover, &&:focus': { + background: transparentize(euiVars.euiColorDanger, 0.04), + }, + '&.isExpanded': { + color: colors.ghost, + background: euiVars.euiColorDanger, + '&:hover, &:focus': { + background: `${euiVars.euiColorDanger}`, + }, + }, }; - if (isExpanded) { - button.color = colors.ghost; - button.background = theme.euiColorVis6; - button['&:hover, &:focus'] = { - backgroundColor: `${theme.euiColorVis6} !important`, - }; - - alertButton.color = colors.ghost; - alertButton.background = colors.dangerText; - alertButton['&:hover, &:focus'] = { - backgroundColor: `${colors.dangerText} !important`, - }; - } - const userChangedButton: CSSObject = { ...button, - color: theme.euiColorVis3, - background: transparentize(theme.euiColorVis3, 0.04), - border: `${border.width.thin} solid ${transparentize(theme.euiColorVis3, 0.48)}`, + cursor: 'default', + color: euiVars.euiColorGhost, + background: euiVars.euiColorVis3, + border: `${border.width.thin} solid ${transparentize(euiVars.euiColorVis3, 0.48)}`, + '&&:hover, &&:focus': { + color: euiVars.euiColorGhost, + background: euiVars.euiColorVis3, + textDecoration: 'none', + transform: 'none', + animation: 'none', + }, }; const buttonSize: CSSObject = { padding: `0px ${euiTheme.size.xs}`, }; - const expandedIcon = isExpanded ? 'arrowUp' : 'arrowDown'; - return { buttonArrow, button, alertButton, - alertsCountNumber, userChangedButton, buttonSize, - expandedIcon, }; - }, [euiTheme, isExpanded]); + }, [euiTheme, euiVars]); return cached; }; diff --git a/x-pack/plugins/session_view/public/components/session_view/index.tsx b/x-pack/plugins/session_view/public/components/session_view/index.tsx index 7d11096ff18bd..5fe3e2365cc58 100644 --- a/x-pack/plugins/session_view/public/components/session_view/index.tsx +++ b/x-pack/plugins/session_view/public/components/session_view/index.tsx @@ -200,15 +200,12 @@ export const SessionView = ({ return ( <>
- + - + { - const { euiTheme } = useEuiTheme(); + const { euiTheme, euiVars } = useEuiTheme(); const cached = useMemo(() => { const { border, colors, size } = euiTheme; @@ -45,21 +44,14 @@ export const useStyles = ({ height = 500, isFullScreen }: StylesDeps) => { flexGrow: 0, alignItems: 'stretch', }; - const searchBar: CSSObject = { - position: 'relative', - input: { - backgroundColor: colors.emptyShade, - }, - }; const sessionViewerComponent: CSSObject = { border: border.thin, borderRadius: border.radius.medium, - }; - - const toolBar: CSSObject = { - backgroundColor: `${euiLightVars.euiFormBackgroundDisabledColor} !important`, // important used since euipanel overrides this - padding: `${size.m} ${size.base}`, + '.sessionViewerToolbar': { + backgroundColor: `${euiVars.euiFormBackgroundDisabledColor}`, + padding: `${size.m} ${size.base}`, + }, }; const betaBadge: CSSObject = { @@ -71,12 +63,10 @@ export const useStyles = ({ height = 500, isFullScreen }: StylesDeps) => { detailPanel, nonGrowGroup, resizeHandle, - searchBar, sessionViewerComponent, - toolBar, betaBadge, }; - }, [euiTheme, isFullScreen, height]); + }, [euiTheme, isFullScreen, height, euiVars]); return cached; }; diff --git a/x-pack/plugins/session_view/public/components/session_view_search_bar/index.test.tsx b/x-pack/plugins/session_view/public/components/session_view_search_bar/index.test.tsx index 67347f2138f0a..dd758529530ba 100644 --- a/x-pack/plugins/session_view/public/components/session_view_search_bar/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/session_view_search_bar/index.test.tsx @@ -34,7 +34,7 @@ describe('SessionViewSearchBar component', () => { /> ); - const searchInput = renderResult.getByTestId('sessionView:searchInput').querySelector('input'); + const searchInput = renderResult.getByTestId('sessionView:searchBar').querySelector('input'); expect(searchInput?.value).toEqual('ls'); @@ -73,7 +73,7 @@ describe('SessionViewSearchBar component', () => { userEvent.click(renderResult.getByTestId('pagination-button-next')); expect(searchPagination.querySelector(paginationTextClass)?.textContent).toEqual('2 of 3'); - const searchInput = renderResult.getByTestId('sessionView:searchInput').querySelector('input'); + const searchInput = renderResult.getByTestId('sessionView:searchBar').querySelector('input'); if (searchInput) { userEvent.type(searchInput, ' -la'); diff --git a/x-pack/plugins/session_view/public/components/session_view_search_bar/index.tsx b/x-pack/plugins/session_view/public/components/session_view_search_bar/index.tsx index 9c24f6b94199c..0cac43f39a8ec 100644 --- a/x-pack/plugins/session_view/public/components/session_view_search_bar/index.tsx +++ b/x-pack/plugins/session_view/public/components/session_view_search_bar/index.tsx @@ -65,7 +65,7 @@ export const SessionViewSearchBar = ({ }, [searchResults, onProcessSelected, selectedResult]); return ( -
+
{noResults && {NO_RESULTS}} {showPagination && ( diff --git a/x-pack/plugins/session_view/public/components/session_view_search_bar/styles.ts b/x-pack/plugins/session_view/public/components/session_view_search_bar/styles.ts index ebb1b8f7b340b..6e7c717e2816b 100644 --- a/x-pack/plugins/session_view/public/components/session_view_search_bar/styles.ts +++ b/x-pack/plugins/session_view/public/components/session_view_search_bar/styles.ts @@ -6,15 +6,15 @@ */ import { useMemo } from 'react'; -import { useEuiTheme } from '@elastic/eui'; import { CSSObject } from '@emotion/react'; +import { useEuiTheme } from '../../hooks'; interface StylesDeps { hasSearchResults: boolean; } export const useStyles = ({ hasSearchResults }: StylesDeps) => { - const { euiTheme } = useEuiTheme(); + const { euiTheme, euiVars } = useEuiTheme(); const cached = useMemo(() => { const pagination: CSSObject = { @@ -36,19 +36,20 @@ export const useStyles = ({ hasSearchResults }: StylesDeps) => { right: euiTheme.size.xxl, }; - const searchBarWithResult: CSSObject = { + const searchBar: CSSObject = { position: 'relative', - 'input.euiFieldSearch.euiFieldSearch-isClearable': { + backgroundColor: euiVars.euiFormBackgroundColor, + input: { paddingRight: hasSearchResults ? '200px' : euiTheme.size.xxl, }, }; return { pagination, - searchBarWithResult, + searchBar, noResults, }; - }, [euiTheme, hasSearchResults]); + }, [euiTheme, euiVars, hasSearchResults]); return cached; }; diff --git a/x-pack/plugins/session_view/public/hooks/index.ts b/x-pack/plugins/session_view/public/hooks/index.ts new file mode 100644 index 0000000000000..8db231cd2bc64 --- /dev/null +++ b/x-pack/plugins/session_view/public/hooks/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { useEuiTheme } from './use_eui_theme'; diff --git a/x-pack/plugins/session_view/public/hooks/use_eui_theme.ts b/x-pack/plugins/session_view/public/hooks/use_eui_theme.ts new file mode 100644 index 0000000000000..f7021f6146f09 --- /dev/null +++ b/x-pack/plugins/session_view/public/hooks/use_eui_theme.ts @@ -0,0 +1,44 @@ +/* + * 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 { shade, useEuiTheme as useEuiThemeHook } from '@elastic/eui'; +import { euiLightVars, euiDarkVars } from '@kbn/ui-theme'; +import { useMemo } from 'react'; + +type EuiThemeProps = Parameters; +type ExtraEuiVars = { + // eslint-disable-next-line @typescript-eslint/naming-convention + euiColorVis6_asText: string; +}; +type EuiVars = typeof euiLightVars & ExtraEuiVars; +type EuiThemeReturn = ReturnType & { euiVars: EuiVars }; + +// Not all Eui Tokens were fully migrated to @elastic/eui/useEuiTheme yet, so +// this hook overrides the default useEuiTheme hook to provide a custom hook that +// allows the use the euiVars tokens from the euiLightVars and euiDarkVars +export const useEuiTheme = (...props: EuiThemeProps): EuiThemeReturn => { + const euiThemeHook = useEuiThemeHook(...props); + + const euiVars = useMemo(() => { + const themeVars = euiThemeHook.colorMode === 'DARK' ? euiDarkVars : euiLightVars; + + const extraEuiVars: ExtraEuiVars = { + // eslint-disable-next-line @typescript-eslint/naming-convention + euiColorVis6_asText: shade(themeVars.euiColorVis6, 0.335), + }; + + return { + ...themeVars, + ...extraEuiVars, + }; + }, [euiThemeHook.colorMode]); + + return { + ...euiThemeHook, + euiVars, + }; +}; From 8771527334c5949e2ca772fd3d7202daed79ffdb Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Wed, 27 Apr 2022 21:42:55 +0200 Subject: [PATCH 45/78] adding new dependencies (#125187) --- .../pipelines/pull_request/response_ops.yml | 4 +-- .../pull_request/response_ops_cases.yml | 11 ++++++++ .../pipelines/pull_request/pipeline.js | 20 +++++++++++--- .../scripts/steps/functional/response_ops.sh | 17 ++++++++++++ x-pack/plugins/security_solution/package.json | 1 + .../response_ops_cli_config.ts | 19 +++++++++++++ .../test/security_solution_cypress/runner.ts | 27 +++++++++++++++++++ 7 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 .buildkite/pipelines/pull_request/response_ops_cases.yml create mode 100755 .buildkite/scripts/steps/functional/response_ops.sh create mode 100644 x-pack/test/security_solution_cypress/response_ops_cli_config.ts diff --git a/.buildkite/pipelines/pull_request/response_ops.yml b/.buildkite/pipelines/pull_request/response_ops.yml index 846477170409b..87f6a60c85c5a 100644 --- a/.buildkite/pipelines/pull_request/response_ops.yml +++ b/.buildkite/pipelines/pull_request/response_ops.yml @@ -1,6 +1,6 @@ steps: - - command: .buildkite/scripts/steps/functional/response_ops_cases.sh - label: 'Cases Cypress Tests on Security Solution' + - command: .buildkite/scripts/steps/functional/response_ops.sh + label: 'Rules, Alerts and Exceptions ResponseOps Cypress Tests on Security Solution' agents: queue: ci-group-6 depends_on: build diff --git a/.buildkite/pipelines/pull_request/response_ops_cases.yml b/.buildkite/pipelines/pull_request/response_ops_cases.yml new file mode 100644 index 0000000000000..846477170409b --- /dev/null +++ b/.buildkite/pipelines/pull_request/response_ops_cases.yml @@ -0,0 +1,11 @@ +steps: + - command: .buildkite/scripts/steps/functional/response_ops_cases.sh + label: 'Cases Cypress Tests on Security Solution' + agents: + queue: ci-group-6 + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '*' + limit: 1 diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.js b/.buildkite/scripts/pipelines/pull_request/pipeline.js index fa167d9f324b4..0f8e46842fd39 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.js +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.js @@ -64,12 +64,12 @@ const uploadPipeline = (pipelineContent) => { if ( (await doAnyChangesMatch([ - /^x-pack\/plugins\/security_solution/, /^x-pack\/plugins\/lists/, + /^x-pack\/plugins\/security_solution/, /^x-pack\/plugins\/timelines/, - /^x-pack\/test\/security_solution_cypress/, /^x-pack\/plugins\/triggers_actions_ui\/public\/application\/sections\/action_connector_form/, /^x-pack\/plugins\/triggers_actions_ui\/public\/application\/context\/actions_connectors_context\.tsx/, + /^x-pack\/test\/security_solution_cypress/, ])) || process.env.GITHUB_PR_LABELS.includes('ci:all-cypress-suites') ) { @@ -77,12 +77,26 @@ const uploadPipeline = (pipelineContent) => { } if ( - (await doAnyChangesMatch([/^x-pack\/plugins\/cases/])) || + (await doAnyChangesMatch([ + /^src\/plugins\/data/, + /^x-pack\/plugins\/actions/, + /^x-pack\/plugins\/alerting/, + /^x-pack\/plugins\/event_log/, + /^x-pack\/plugins\/rule_registry/, + /^x-pack\/plugins\/task_manager/, + ])) || process.env.GITHUB_PR_LABELS.includes('ci:all-cypress-suites') ) { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/response_ops.yml')); } + if ( + (await doAnyChangesMatch([/^x-pack\/plugins\/cases/])) || + process.env.GITHUB_PR_LABELS.includes('ci:all-cypress-suites') + ) { + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/response_ops_cases.yml')); + } + if ( (await doAnyChangesMatch([/^x-pack\/plugins\/apm/])) || process.env.GITHUB_PR_LABELS.includes('ci:all-cypress-suites') diff --git a/.buildkite/scripts/steps/functional/response_ops.sh b/.buildkite/scripts/steps/functional/response_ops.sh new file mode 100755 index 0000000000000..d8484854ed29e --- /dev/null +++ b/.buildkite/scripts/steps/functional/response_ops.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/steps/functional/common.sh + +export JOB=kibana-security-solution-chrome + +echo "--- Response Ops Cypress Tests on Security Solution" + +cd "$XPACK_DIR" + +checks-reporter-with-killswitch "Response Ops Cypress Tests on Security Solution" \ + node scripts/functional_tests \ + --debug --bail \ + --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ + --config test/security_solution_cypress/response_ops_cli_config.ts diff --git a/x-pack/plugins/security_solution/package.json b/x-pack/plugins/security_solution/package.json index fb0a9a7ed1d78..b62b6d08fd892 100644 --- a/x-pack/plugins/security_solution/package.json +++ b/x-pack/plugins/security_solution/package.json @@ -16,6 +16,7 @@ "cypress:run:cases": "yarn cypress:run:reporter --browser chrome --spec './cypress/integration/cases/*.spec.ts'; status=$?; yarn junit:merge && exit $status", "cypress:run:firefox": "yarn cypress:run:reporter --browser firefox --spec './cypress/integration/**/*.spec.ts'; status=$?; yarn junit:merge && exit $status", "cypress:run:reporter": "yarn cypress run --config-file ./cypress/cypress.json --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json", + "cypress:run:respops": "yarn cypress:run:reporter --browser chrome --spec ./cypress/integration/detection_alerts/*.spec.ts,./cypress/integration/detection_rules/*.spec.ts,./cypress/integration/exceptions/*.spec.ts; status=$?; yarn junit:merge && exit $status", "cypress:run:ccs": "yarn cypress:run:reporter --browser chrome --config integrationFolder=./cypress/ccs_integration; status=$?; yarn junit:merge && exit $status", "cypress:run-as-ci": "node --max-old-space-size=2048 ../../../scripts/functional_tests --config ../../test/security_solution_cypress/cli_config.ts", "cypress:run-as-ci:firefox": "node --max-old-space-size=2048 ../../../scripts/functional_tests --config ../../test/security_solution_cypress/config.firefox.ts", diff --git a/x-pack/test/security_solution_cypress/response_ops_cli_config.ts b/x-pack/test/security_solution_cypress/response_ops_cli_config.ts new file mode 100644 index 0000000000000..d70837bff55e7 --- /dev/null +++ b/x-pack/test/security_solution_cypress/response_ops_cli_config.ts @@ -0,0 +1,19 @@ +/* + * 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 { FtrConfigProviderContext } from '@kbn/test'; + +import { SecuritySolutionCypressCliResponseOpsTestRunner } from './runner'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const securitySolutionCypressConfig = await readConfigFile(require.resolve('./config.ts')); + return { + ...securitySolutionCypressConfig.getAll(), + + testRunner: SecuritySolutionCypressCliResponseOpsTestRunner, + }; +} diff --git a/x-pack/test/security_solution_cypress/runner.ts b/x-pack/test/security_solution_cypress/runner.ts index 21404b8b2e47f..6ae5812c56658 100644 --- a/x-pack/test/security_solution_cypress/runner.ts +++ b/x-pack/test/security_solution_cypress/runner.ts @@ -38,6 +38,33 @@ export async function SecuritySolutionCypressCliTestRunner({ getService }: FtrPr }); } +export async function SecuritySolutionCypressCliResponseOpsTestRunner({ + getService, +}: FtrProviderContext) { + const log = getService('log'); + const config = getService('config'); + const esArchiver = getService('esArchiver'); + + await esArchiver.load('x-pack/test/security_solution_cypress/es_archives/auditbeat'); + + await withProcRunner(log, async (procs) => { + await procs.run('cypress', { + cmd: 'yarn', + args: ['cypress:run:respops'], + cwd: resolve(__dirname, '../../plugins/security_solution'), + env: { + FORCE_COLOR: '1', + CYPRESS_BASE_URL: Url.format(config.get('servers.kibana')), + CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')), + CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'), + CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'), + ...process.env, + }, + wait: true, + }); + }); +} + export async function SecuritySolutionCypressCliCasesTestRunner({ getService, }: FtrProviderContext) { From 219a72fd634d9332b08dccc8871da02025281152 Mon Sep 17 00:00:00 2001 From: Jiawei Wu <74562234+JiaweiWu@users.noreply.github.com> Date: Wed, 27 Apr 2022 13:38:18 -0700 Subject: [PATCH 46/78] [RAM] Add shareable tags badge (#130721) * shareable tags badge Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../rule_tag_badge_sandbox.tsx | 32 +++++++ .../shareable_components_sandbox.tsx | 2 + .../public/application/sections/index.tsx | 3 + .../components/rule_tag_badge.test.tsx | 59 ++++++++++++ .../rules_list/components/rule_tag_badge.tsx | 89 +++++++++++++++++++ .../rules_list/components/rules_list.test.tsx | 2 +- .../rules_list/components/rules_list.tsx | 42 ++------- .../public/common/get_rule_tag_badge.tsx | 14 +++ .../triggers_actions_ui/public/mocks.ts | 4 + .../triggers_actions_ui/public/plugin.ts | 6 ++ .../triggers_actions_ui/public/types.ts | 2 + .../apps/triggers_actions_ui/index.ts | 1 + .../triggers_actions_ui/rule_tag_badge.ts | 42 +++++++++ 13 files changed, 261 insertions(+), 37 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rule_tag_badge_sandbox.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/common/get_rule_tag_badge.tsx create mode 100644 x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rule_tag_badge.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rule_tag_badge_sandbox.tsx b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rule_tag_badge_sandbox.tsx new file mode 100644 index 0000000000000..097fb00969d27 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rule_tag_badge_sandbox.tsx @@ -0,0 +1,32 @@ +/* + * 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. + */ + +/** + * Note: This exists only temporarily as we refactor to remove the sandboxes + * to its own plugin. Ideally we do not want to use an internal page within + * the triggers_actions_ui plugin + */ + +import React, { useState } from 'react'; +import { getRuleTagBadgeLazy } from '../../../common/get_rule_tag_badge'; + +const tags = ['tag1', 'tag2', 'tag3', 'tag4']; + +export const RuleTagBadgeSandbox = () => { + const [isOpen, setIsOpen] = useState(false); + + return ( +
+ {getRuleTagBadgeLazy({ + isOpen, + tags, + onClick: () => setIsOpen(true), + onClose: () => setIsOpen(false), + })} +
+ ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/shareable_components_sandbox.tsx b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/shareable_components_sandbox.tsx index 97366832bda0e..d756804bbd406 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/shareable_components_sandbox.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/shareable_components_sandbox.tsx @@ -7,11 +7,13 @@ import React from 'react'; import { RuleStatusDropdownSandbox } from './rule_status_dropdown_sandbox'; +import { RuleTagBadgeSandbox } from './rule_tag_badge_sandbox'; export const InternalShareableComponentsSandbox: React.FC<{}> = () => { return ( <> + ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx index 0aaa3195b7c52..0e59e3c8ca38f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx @@ -32,3 +32,6 @@ export const ActionForm = suspendedComponentWithProps( export const RuleStatusDropdown = suspendedComponentWithProps( lazy(() => import('./rules_list/components/rule_status_dropdown')) ); +export const RuleTagBadge = suspendedComponentWithProps( + lazy(() => import('./rules_list/components/rule_tag_badge')) +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.test.tsx new file mode 100644 index 0000000000000..606d60ff6bfeb --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.test.tsx @@ -0,0 +1,59 @@ +/* + * 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 React from 'react'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { RuleTagBadge } from './rule_tag_badge'; + +const onClickMock = jest.fn(); +const onCloseMock = jest.fn(); + +const tags = ['a', 'b', 'c']; + +describe('RuleTagBadge', () => { + beforeEach(() => { + onClickMock.mockReset(); + onCloseMock.mockReset(); + }); + + it('renders the initial badge count correctly', () => { + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find('[data-test-subj="ruleTagBadge"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="ruleTagBadge"]').first().text()).toEqual( + `${tags.length}` + ); + }); + + it('can open and close the popover', () => { + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-a"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-b"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-c"]').exists()).toBeFalsy(); + + wrapper.find('[data-test-subj="ruleTagBadge"]').at(1).simulate('click'); + + expect(onClickMock).toHaveBeenCalledTimes(1); + + wrapper.setProps({ + isOpen: true, + }); + + expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-a"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-b"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-c"]').exists()).toBeTruthy(); + + wrapper.find('[data-test-subj="ruleTagBadge"]').at(1).simulate('click'); + + expect(onClickMock).toHaveBeenCalledTimes(2); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.tsx new file mode 100644 index 0000000000000..c7da398d14403 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.tsx @@ -0,0 +1,89 @@ +/* + * 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 React, { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiPopover, EuiBadge, EuiPopoverTitle } from '@elastic/eui'; + +const tagTitle = i18n.translate( + 'xpack.triggersActionsUI.sections.rules_list.rules_tag_badge.tagTitle', + { + defaultMessage: 'Tag', + } +); + +export interface RuleTagBadgeProps { + isOpen: boolean; + tags: string[]; + onClick: React.MouseEventHandler; + onClose: () => void; + badgeDataTestSubj?: string; + titleDataTestSubj?: string; + tagItemDataTestSubj?: (tag: string) => string; +} + +const containerStyle = { + width: '300px', +}; + +const getTagItemDataTestSubj = (tag: string) => `ruleTagBadgeItem-${tag}`; + +export const RuleTagBadge = (props: RuleTagBadgeProps) => { + const { + isOpen = false, + tags = [], + onClick, + onClose, + badgeDataTestSubj = 'ruleTagBadge', + titleDataTestSubj = 'ruleTagPopoverTitle', + tagItemDataTestSubj = getTagItemDataTestSubj, + } = props; + + const badge = useMemo(() => { + return ( + + {tags.length} + + ); + }, [tags, badgeDataTestSubj, onClick]); + + const tagBadges = useMemo( + () => + tags.map((tag, index) => ( + + {tag} + + )), + [tags, tagItemDataTestSubj] + ); + + return ( + + {tagTitle} +
{tagBadges}
+
+ ); +}; + +// eslint-disable-next-line import/no-default-export +export { RuleTagBadge as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx index f87cee4c6547f..098bef2b5af47 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx @@ -439,7 +439,7 @@ describe('rules_list component with items', () => { wrapper.find('EuiTableRowCell[data-test-subj="rulesTableCell-tagsPopover"]').length ).toEqual(mockedRulesData.length); // only show tags popover if tags exist on rule - const tagsBadges = wrapper.find('EuiBadge[data-test-subj="ruleTagsBadge"]'); + const tagsBadges = wrapper.find('EuiBadge[data-test-subj="ruleTagBadge"]'); expect(tagsBadges.length).toEqual( mockedRulesData.filter((data) => data.tags.length > 0).length ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx index 59515ca3c3622..9b85334932a99 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx @@ -14,7 +14,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React, { useEffect, useState, useMemo, ReactNode, useCallback } from 'react'; import { EuiBasicTable, - EuiBadge, EuiButton, EuiFieldSearch, EuiFlexGroup, @@ -30,8 +29,6 @@ import { EuiTableSortingType, EuiButtonIcon, EuiHorizontalRule, - EuiPopover, - EuiPopoverTitle, EuiSelectableOption, EuiIcon, EuiScreenReaderOnly, @@ -95,6 +92,7 @@ import { CenterJustifiedSpinner } from '../../../components/center_justified_spi import { ManageLicenseModal } from './manage_license_modal'; import { checkRuleTypeEnabled } from '../../../lib/check_rule_type_enabled'; import { RuleStatusDropdown } from './rule_status_dropdown'; +import { RuleTagBadge } from './rule_tag_badge'; import { PercentileSelectablePopover } from './percentile_selectable_popover'; import { RuleDurationFormat } from './rule_duration_format'; import { shouldShowDurationWarning } from '../../../lib/execution_duration_utils'; @@ -590,40 +588,12 @@ export const RulesList: React.FunctionComponent = () => { 'data-test-subj': 'rulesTableCell-tagsPopover', render: (tags: string[], item: RuleTableItem) => { return tags.length > 0 ? ( - setTagPopoverOpenIndex(item.index)} - onClickAriaLabel="Tags" - iconOnClick={() => setTagPopoverOpenIndex(item.index)} - iconOnClickAriaLabel="Tags" - > - {tags.length} - - } - anchorPosition="upCenter" + setTagPopoverOpenIndex(-1)} - > - Tags -
- {tags.map((tag: string, index: number) => ( - - {tag} - - ))} - + tags={tags} + onClick={() => setTagPopoverOpenIndex(item.index)} + onClose={() => setTagPopoverOpenIndex(-1)} + /> ) : null; }, }, diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_rule_tag_badge.tsx b/x-pack/plugins/triggers_actions_ui/public/common/get_rule_tag_badge.tsx new file mode 100644 index 0000000000000..dc889402ab3c5 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/get_rule_tag_badge.tsx @@ -0,0 +1,14 @@ +/* + * 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 React from 'react'; +import { RuleTagBadge } from '../application/sections'; +import type { RuleTagBadgeProps } from '../application/sections/rules_list/components/rule_tag_badge'; + +export const getRuleTagBadgeLazy = (props: RuleTagBadgeProps) => { + return ; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/mocks.ts b/x-pack/plugins/triggers_actions_ui/public/mocks.ts index 007b906e8747b..959d959ef855a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/mocks.ts +++ b/x-pack/plugins/triggers_actions_ui/public/mocks.ts @@ -26,6 +26,7 @@ import { } from './types'; import { getAlertsTableLazy } from './common/get_alerts_table'; import { getRuleStatusDropdownLazy } from './common/get_rule_status_dropdown'; +import { getRuleTagBadgeLazy } from './common/get_rule_tag_badge'; function createStartMock(): TriggersAndActionsUIPublicPluginStart { const actionTypeRegistry = new TypeRegistry(); @@ -64,6 +65,9 @@ function createStartMock(): TriggersAndActionsUIPublicPluginStart { getRuleStatusDropdown: (props) => { return getRuleStatusDropdownLazy(props); }, + getRuleTagBadge: (props) => { + return getRuleTagBadgeLazy(props); + }, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index 7997552a81023..e2c3be96271b9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -31,6 +31,7 @@ import { getAddAlertFlyoutLazy } from './common/get_add_alert_flyout'; import { getEditAlertFlyoutLazy } from './common/get_edit_alert_flyout'; import { getAlertsTableLazy } from './common/get_alerts_table'; import { getRuleStatusDropdownLazy } from './common/get_rule_status_dropdown'; +import { getRuleTagBadgeLazy } from './common/get_rule_tag_badge'; import { ExperimentalFeaturesService } from './common/experimental_features_service'; import { ExperimentalFeatures, @@ -46,6 +47,7 @@ import type { ConnectorEditFlyoutProps, AlertsTableProps, RuleStatusDropdownProps, + RuleTagBadgeProps, AlertsTableConfigurationRegistry, } from './types'; import { TriggersActionsUiConfigType } from '../common/types'; @@ -76,6 +78,7 @@ export interface TriggersAndActionsUIPublicPluginStart { ) => ReactElement; getAlertsTable: (props: AlertsTableProps) => ReactElement; getRuleStatusDropdown: (props: RuleStatusDropdownProps) => ReactElement; + getRuleTagBadge: (props: RuleTagBadgeProps) => ReactElement; } interface PluginsSetup { @@ -249,6 +252,9 @@ export class Plugin getRuleStatusDropdown: (props: RuleStatusDropdownProps) => { return getRuleStatusDropdownLazy(props); }, + getRuleTagBadge: (props: RuleTagBadgeProps) => { + return getRuleTagBadgeLazy(props); + }, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 7038e2eaa0d01..9ca0f672b4f99 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -48,6 +48,7 @@ import { import { RuleRegistrySearchRequestPagination } from '@kbn/rule-registry-plugin/common'; import { TypeRegistry } from './application/type_registry'; import type { ComponentOpts as RuleStatusDropdownProps } from './application/sections/rules_list/components/rule_status_dropdown'; +import { RuleTagBadgeProps } from './application/sections/rules_list/components/rule_tag_badge'; // In Triggers and Actions we treat all `Alert`s as `SanitizedRule` // so the `Params` is a black-box of Record @@ -80,6 +81,7 @@ export type { ResolvedRule, SanitizedRule, RuleStatusDropdownProps, + RuleTagBadgeProps, }; export type { ActionType, AsApiContract }; export { diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/index.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/index.ts index 179cd0c7ab8e9..270232d1aa0fd 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/index.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/index.ts @@ -17,5 +17,6 @@ export default ({ loadTestFile, getService }: FtrProviderContext) => { loadTestFile(require.resolve('./connectors')); loadTestFile(require.resolve('./alerts_table')); loadTestFile(require.resolve('./rule_status_dropdown')); + loadTestFile(require.resolve('./rule_tag_badge')); }); }; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rule_tag_badge.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rule_tag_badge.ts new file mode 100644 index 0000000000000..0743896fbcbdb --- /dev/null +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rule_tag_badge.ts @@ -0,0 +1,42 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']); + const esArchiver = getService('esArchiver'); + + describe('Rule tag badge', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); + await PageObjects.common.navigateToUrlWithBrowserHistory( + 'triggersActions', + '/__components_sandbox' + ); + }); + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); + }); + + it('should load from the shareable lazy loader', async () => { + await testSubjects.find('ruleTagBadge'); + const exists = await testSubjects.exists('ruleTagBadge'); + expect(exists).to.be(true); + }); + + it('should open and display tags', async () => { + await testSubjects.click('ruleTagBadge'); + expect(await testSubjects.exists('ruleTagBadgeItem-tag1')).to.be(true); + expect(await testSubjects.exists('ruleTagBadgeItem-tag2')).to.be(true); + expect(await testSubjects.exists('ruleTagBadgeItem-tag3')).to.be(true); + expect(await testSubjects.exists('ruleTagBadgeItem-tag4')).to.be(true); + }); + }); +}; From 2c9f2aa903a6289df789b5c031c0936d1f855695 Mon Sep 17 00:00:00 2001 From: liza-mae Date: Wed, 27 Apr 2022 14:55:41 -0600 Subject: [PATCH 47/78] [8.x] Security solutions upgrade test fixes (#130750) * Fix upgrade tests * More fixes * Remove unused * Comment out ones missing data-test-subj * Remove participant verification * Remove unused vars * brings back assertions * Revert "brings back assertions" This reverts commit 5783eae53204c98bb0d50686155c491a414e534c. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Gloria Hornero --- .../cypress/screens/alerts.ts | 3 +- .../cypress/screens/alerts_details.ts | 5 ++- .../cypress/tasks/alerts_detection_rules.ts | 2 +- .../detection_rules/custom_query_rule.spec.ts | 13 +++--- .../detection_rules/threshold_rule.spec.ts | 40 ++++++++++--------- .../threat_hunting/cases/import_case.spec.ts | 24 +++++------ .../timeline/import_timeline.spec.ts | 9 ++++- 7 files changed, 53 insertions(+), 43 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts.ts b/x-pack/plugins/security_solution/cypress/screens/alerts.ts index d85755ff5238e..b37cc82e7108b 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts.ts @@ -65,7 +65,8 @@ export const OPENED_ALERTS_FILTER_BTN = '[data-test-subj="openAlerts"]'; export const PROCESS_NAME_COLUMN = '[data-test-subj="dataGridHeaderCell-process.name"]'; 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 REASON = + '[data-test-subj="dataGridRowCell"][data-gridcell-column-id="kibana.alert.reason"]'; export const RISK_SCORE = '[data-test-subj^=formatted-field][data-test-subj$=risk_score]'; 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 ddb100367b242..95c0017d6a6ef 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts @@ -30,13 +30,14 @@ 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_HOST_NAME = '[data-test-subj="event-field-host.name"]'; export const OVERVIEW_RISK_SCORE = '[data-test-subj="eventDetails"] [data-test-subj="riskScore"]'; export const OVERVIEW_RULE = '[data-test-subj="eventDetails"] [data-test-subj="ruleName"]'; +export const OVERVIEW_RULE_TYPE = '[data-test-subj="event-field-kibana.alert.rule.type"]'; + export const OVERVIEW_SEVERITY = '[data-test-subj="eventDetails"] [data-test-subj="severity"]'; export const OVERVIEW_STATUS = '[data-test-subj="eventDetails"] [data-test-subj="alertStatus"]'; 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 afe3981219217..9edba01beb7a8 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 @@ -154,7 +154,7 @@ export const goToRuleDetails = () => { }; export const goToTheRuleDetailsOf = (ruleName: string) => { - cy.get(RULE_NAME).should('contain', ruleName).contains(ruleName).click(); + cy.get(RULE_NAME).contains(ruleName).click({ force: true }); }; export const loadPrebuiltDetectionRules = () => { diff --git a/x-pack/plugins/security_solution/cypress/upgrade_integration/detections/detection_rules/custom_query_rule.spec.ts b/x-pack/plugins/security_solution/cypress/upgrade_integration/detections/detection_rules/custom_query_rule.spec.ts index 4c965c1f00b14..6a3c19aeabfcf 100644 --- a/x-pack/plugins/security_solution/cypress/upgrade_integration/detections/detection_rules/custom_query_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/upgrade_integration/detections/detection_rules/custom_query_rule.spec.ts @@ -6,6 +6,7 @@ */ import semver from 'semver'; import { + ALERT_GRID_CELL, DESTINATION_IP, HOST_NAME, PROCESS_NAME_COLUMN, @@ -52,6 +53,7 @@ const alert = { riskScore: '7', 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.', + reasonAlt: '—', hostName: 'security-solution.local', username: 'Security Solution', processName: 'test', @@ -106,16 +108,15 @@ describe('After an upgrade, the custom query rule', () => { }); it('Displays the alert details at the tgrid', () => { - let expectedReason; - if (semver.gt(Cypress.env('ORIGINAL_VERSION'), '7.15.0')) { - expectedReason = alert.reason; - } else { - expectedReason = '-'; + let expectedReason = alert.reason; + if (semver.lt(Cypress.env('ORIGINAL_VERSION'), '7.15.0')) { + expectedReason = alert.reasonAlt; } + cy.get(ALERT_GRID_CELL).first().focus(); 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', expectedReason).type('{rightarrow}'); + cy.get(REASON).contains(expectedReason); cy.get(HOST_NAME).should('have.text', alert.hostName); cy.get(USER_NAME).should('have.text', alert.username); cy.get(PROCESS_NAME_COLUMN).eq(0).scrollIntoView(); diff --git a/x-pack/plugins/security_solution/cypress/upgrade_integration/detections/detection_rules/threshold_rule.spec.ts b/x-pack/plugins/security_solution/cypress/upgrade_integration/detections/detection_rules/threshold_rule.spec.ts index 1a4bdb388aa35..78c34dfd43a7e 100644 --- a/x-pack/plugins/security_solution/cypress/upgrade_integration/detections/detection_rules/threshold_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/upgrade_integration/detections/detection_rules/threshold_rule.spec.ts @@ -5,7 +5,7 @@ * 2.0. */ import semver from 'semver'; -import { HOST_NAME, REASON, RISK_SCORE, RULE_NAME, SEVERITY } from '../../../screens/alerts'; +import { REASON, RISK_SCORE, RULE_NAME, SEVERITY } from '../../../screens/alerts'; import { SERVER_SIDE_EVENT_COUNT } from '../../../screens/alerts_detection_rules'; import { ADDITIONAL_LOOK_BACK_DETAILS, @@ -35,23 +35,21 @@ import { login, visit } 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, + OVERVIEW_RULE_TYPE, } from '../../../screens/alerts_details'; const EXPECTED_NUMBER_OF_ALERTS = '1'; const alert = { rule: 'Threshold rule', - severity: 'Medium', + severity: 'medium', riskScore: '17', reason: 'event created medium alert Threshold rule.', + reasonAlt: '—', hostName: 'security-solution.local', thresholdCount: '2', }; @@ -67,8 +65,9 @@ const rule = { runsEvery: '24h', lookBack: '49976h', timeline: 'None', + ruleType: 'threshold', thresholdField: 'host.name', - threholdValue: '1', + thresholdValue: '1', }; describe('After an upgrade, the threshold rule', () => { @@ -98,7 +97,7 @@ describe('After an upgrade, the threshold rule', () => { getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', rule.timeline); getDetails(THRESHOLD_DETAILS).should( 'have.text', - `Results aggregated by ${rule.thresholdField} >= ${rule.threholdValue}` + `Results aggregated by ${rule.thresholdField} >= ${rule.thresholdValue}` ); }); cy.get(SCHEDULE_DETAILS).within(() => { @@ -108,17 +107,17 @@ describe('After an upgrade, the threshold rule', () => { }); it('Displays the alert details in the TGrid', () => { - let expectedReason; - if (semver.gt(Cypress.env('ORIGINAL_VERSION'), '7.15.0')) { - expectedReason = alert.reason; - } else { - expectedReason = '-'; + let expectedReason = alert.reason; + if (semver.lt(Cypress.env('ORIGINAL_VERSION'), '7.15.0')) { + expectedReason = alert.reasonAlt; } + cy.scrollTo('bottom'); 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', expectedReason); - cy.get(HOST_NAME).should('have.text', alert.hostName); + cy.get(REASON).contains(expectedReason); + // TODO: Needs data-test-subj + // cy.get(HOST_NAME).should('have.text', alert.hostName); }); it('Displays the Overview alert details in the alert flyout', () => { @@ -128,9 +127,12 @@ describe('After an upgrade, the threshold rule', () => { cy.get(OVERVIEW_RULE).should('have.text', alert.rule); cy.get(OVERVIEW_SEVERITY).contains(alert.severity, { matchCase: false }); 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]`); + // TODO: Find out what this is + // cy.get(OVERVIEW_HOST_NAME).should('have.text', alert.hostName); + // TODO: Needs data-test-subj + // cy.get(OVERVIEW_THRESHOLD_COUNT).should('have.text', alert.thresholdCount); + cy.get(OVERVIEW_RULE_TYPE).should('have.text', rule.ruleType); + // TODO: Needs data-test-subj + // cy.get(OVERVIEW_THRESHOLD_VALUE).should('have.text', rule.thresholdValue); }); }); diff --git a/x-pack/plugins/security_solution/cypress/upgrade_integration/threat_hunting/cases/import_case.spec.ts b/x-pack/plugins/security_solution/cypress/upgrade_integration/threat_hunting/cases/import_case.spec.ts index 79a6e1ebd90b0..d9ad058c2ec65 100644 --- a/x-pack/plugins/security_solution/cypress/upgrade_integration/threat_hunting/cases/import_case.spec.ts +++ b/x-pack/plugins/security_solution/cypress/upgrade_integration/threat_hunting/cases/import_case.spec.ts @@ -45,7 +45,7 @@ const importedCase = { user: 'glo', reporter: 'glo@test.co', tags: 'export case', - numberOfAlerts: '2', + numberOfAlerts: '1', numberOfComments: '2', description: "This is the description of the 7.16 case that I'm going to import in future versions.", @@ -72,11 +72,7 @@ const FIRST_ALERT_UPDATE = 1; const SECOND_ALERT_UPDATE = 2; const INCIDENT_MANAGEMENT_SYSTEM_UPDATE = 3; const EXPECTED_NUMBER_OF_UPDATES = 4; -const EXPECTED_NUMBER_OF_PARTICIPANTS = 4; const REPORTER = 0; -const FIRST_PARTICIPANT = 1; -const SECOND_PARTICIPANT = 2; -const THIRD_PARTICIPANT = 3; describe('Import case after upgrade', () => { before(() => { @@ -141,15 +137,17 @@ describe('Import case after upgrade', () => { .eq(INCIDENT_MANAGEMENT_SYSTEM_UPDATE) .invoke('text') .should('match', incidentManagementSystemRegex); - cy.get(CASE_DETAILS_USERNAMES).should('have.length', EXPECTED_NUMBER_OF_PARTICIPANTS); + // TODO: Needs data-test-subj + // cy.get(CASE_DETAILS_USERNAMES).should('have.length', EXPECTED_NUMBER_OF_PARTICIPANTS); + // TODO: Investigate why this changes, not reliable to verify + // cy.get(CASE_DETAILS_USERNAMES).eq(FIRST_PARTICIPANT).should('have.text', importedCase.user); + // cy.get(CASE_DETAILS_USERNAMES) + // .eq(SECOND_PARTICIPANT) + // .should('have.text', importedCase.participants[0]); + // cy.get(CASE_DETAILS_USERNAMES) + // .eq(THIRD_PARTICIPANT) + // .should('have.text', importedCase.participants[1]); cy.get(CASE_DETAILS_USERNAMES).eq(REPORTER).should('have.text', importedCase.user); - cy.get(CASE_DETAILS_USERNAMES).eq(FIRST_PARTICIPANT).should('have.text', importedCase.user); - cy.get(CASE_DETAILS_USERNAMES) - .eq(SECOND_PARTICIPANT) - .should('have.text', importedCase.participants[0]); - cy.get(CASE_DETAILS_USERNAMES) - .eq(THIRD_PARTICIPANT) - .should('have.text', importedCase.participants[1]); cy.get(CASES_TAGS(importedCase.tags)).should('exist'); cy.get(CASE_CONNECTOR).should('have.text', importedCase.connector); }); diff --git a/x-pack/plugins/security_solution/cypress/upgrade_integration/threat_hunting/timeline/import_timeline.spec.ts b/x-pack/plugins/security_solution/cypress/upgrade_integration/threat_hunting/timeline/import_timeline.spec.ts index 145c031bcd7cd..476ad34652a63 100644 --- a/x-pack/plugins/security_solution/cypress/upgrade_integration/threat_hunting/timeline/import_timeline.spec.ts +++ b/x-pack/plugins/security_solution/cypress/upgrade_integration/threat_hunting/timeline/import_timeline.spec.ts @@ -5,6 +5,7 @@ * 2.0. */ +import semver from 'semver'; import { CORRELATION_EVENT_TABLE_CELL, DATA_PROVIDERS, @@ -56,6 +57,7 @@ const timelineDetails = { dateStart: 'Oct 10, 2020 @ 22:00:00.000', dateEnd: 'Oct 11, 2030 @ 15:13:15.851', queryTab: 'Query4', + queryTabAlt: 'Query2', correlationTab: 'Correlation', analyzerTab: 'Analyzer', notesTab: 'Notes2', @@ -119,6 +121,11 @@ describe('Import timeline after upgrade', () => { }); it('Displays the correct timeline details inside the query tab', () => { + let expectedQueryTab = timelineDetails.queryTab; + if (semver.lt(Cypress.env('ORIGINAL_VERSION'), '7.10.0')) { + expectedQueryTab = timelineDetails.queryTabAlt; + } + openTimeline(); cy.readFile(`cypress/fixtures/${timeline}`).then((file) => { @@ -143,7 +150,7 @@ describe('Import timeline after upgrade', () => { 'have.text', timelineJson.kqlQuery.filterQuery.kuery.expression ); - cy.get(QUERY_TAB_BUTTON).should('have.text', timelineDetails.queryTab); + cy.get(QUERY_TAB_BUTTON).should('have.text', expectedQueryTab); cy.get(TIMELINE_CORRELATION_TAB).should('have.text', timelineDetails.correlationTab); cy.get(GRAPH_TAB_BUTTON).should('have.text', timelineDetails.analyzerTab).and('be.disabled'); cy.get(NOTES_TAB_BUTTON).should('have.text', timelineDetails.notesTab); From 4ade036958598f2ff6c04f42e0d470e22878c198 Mon Sep 17 00:00:00 2001 From: Melissa Burpo Date: Wed, 27 Apr 2022 17:02:01 -0500 Subject: [PATCH 48/78] Osquery 8.2 updates (#130195) * add exported fields reference docs * move advanced content to main osquery page * add info about prebuilt packs * fix number formatting * test table formatting fix * simplify table styling * edit column widths * update column options * Update fields formatting * add steps for copying prebuilt pack * add a section header for fields list * Add redirect for removed advanced-osquery page Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Aleksandr Maus --- docs/osquery/advanced-osquery.asciidoc | 103 - .../exported-fields-reference.asciidoc | 6451 ++++++++++++++++- docs/osquery/osquery.asciidoc | 152 +- docs/redirects.asciidoc | 5 + 4 files changed, 6604 insertions(+), 107 deletions(-) delete mode 100644 docs/osquery/advanced-osquery.asciidoc diff --git a/docs/osquery/advanced-osquery.asciidoc b/docs/osquery/advanced-osquery.asciidoc deleted file mode 100644 index 4f03a6d5fe5eb..0000000000000 --- a/docs/osquery/advanced-osquery.asciidoc +++ /dev/null @@ -1,103 +0,0 @@ -[[advanced-osquery]] -== Advanced Osquery - -[float] -[[osquery-map-fields]] -=== Map result fields to ECS - -When you save queries or add queries to a pack, you can optionally map Osquery results or static values to fields in -the {ecs-ref}/ecs-reference.html[Elastic Common Schema] (ECS). -This standardizes your Osquery data for use across detections, machine learning, -and any other areas that rely on ECS-compliant data. -When the query is run, the results include the original `osquery.` -and the mapped ECS fields. For example, if you update a query to map `osquery.name` to `user.name`, the query results include both fields. - -. Edit saved queries or queries in a pack to map fields: - -* For *Saved queries*: Open the *Saved queries* tab, and then click the edit icon for the query that you want to map. - -* For *packs*: Open the *Packs* tab, edit a pack, and then click the edit icon for the query that you want to map. - -. In the **ECS mapping** section, select an **ECS field** to map. - -. In the **Value** column, use the dropdown on the left to choose what type of value to map to the ECS field: - -** **Osquery value**: Select an Osquery field. The fields available are based on the SQL query entered, and only include fields that the query returns. When the query runs, the ECS field is set dynamically to the value of the Osquery field selected. - -** **Static value**: Enter a static value. When the query runs, the ECS field is set to the value entered. For example, static fields can be used to apply `tags` or your preferred `event.category` to the query results. - -. Map more fields, as needed. To remove any mapped rows, click the delete icon. - -. Save your changes. - -[NOTE] -========================= - -* Some ECS fields are restricted and cannot be mapped. These are not available in the ECS dropdown. - -* Some ECS fields are restricted to a set of allowed values, like {ecs-ref}/ecs-event.html#field-event-category[event.category]. Use the {ecs-ref}/ecs-field-reference.html[ECS Field Reference] for help when mapping fields. - -* Osquery date fields have a variety of data types (including integer, text, or bigint). When mapping an Osquery date field to an ECS date field, you might need to use SQL operators in the query to get an {es}-compatible -{ref}/date.html[date] type. -========================= - - -[float] -[[osquery-extended-tables]] -=== Extended tables for Kubernetes queries -In addition to the Osquery schema, the Elastic-provided version of Osquery also includes the following tables to support Kubernetes containers. These can be queried with live or scheduled queries. - -* `host_users` - -* `host_groups` - -* `host_processes` - -When querying these tables, the expectation is that the `/etc/passwd`, `/etc/group`, and `/proc` are available in the container under `/hostfs` as: -`/hostfs/etc/passwd`, `/hostfs/etc/group`, and `/hostfs/proc`. For information about the fields available in these tables, see the -https://docs.elastic.co/en/integrations/osquery_manager#exported-fields[exported fields] reference. - -[float] -[[osquery-status]] -=== Osquery status - -A query can have the following status: - -[cols="2*<"] -|=== -| Successful | The query successfully completed. -| Failed | The query encountered a problem, such as an issue with the query or the agent was disconnected, and might have failed. -| Not yet responded | The query has not been sent to the agent. -| Expired | The action request timed out. The agent may be offline. -|=== - -NOTE: If an agent is offline, the request status remains **pending** as {kib} retries the request. -By default, a query request times out after five minutes. The time out applies to the time it takes -to deliver the action request to an agent to run a query. If the action completes after the timeout period, -the results are still returned. - - -[float] -[[osquery-results]] -=== Osquery results -When you run live or scheduled queries, the results are automatically -stored in an {es} index, so that you can search, analyze, and visualize this data in {kib}. -For a list of the Osquery fields that can be returned in query results, -refer to https://docs.elastic.co/en/integrations/osquery_manager#exported-fields[exported fields]. -Query results can also include ECS fields, if the query has a defined ECS mapping. - -Osquery responses include the following information: - -* Everything prefaced with `osquery.` is part of the query response. These fields are not mapped to ECS by default. - -* Results include some ECS fields by default, such as `host.*` and `agent.*`, which provide information about the host that was queried. - -* For live queries, the `action_data.query` is the query that was sent. - -* For scheduled queries in a pack, the `action_id` has the format `pack__`. You can use this information to look up the query that was run. - -* By default, all query results are https://osquery.readthedocs.io/en/stable/deployment/logging/#snapshot-logs[snapshot logs] -that represent a point in time with a set of results, with no -https://osquery.readthedocs.io/en/stable/deployment/logging/#differential-logs[differentials]. - -* Osquery data is stored in the `logs-osquery_manager.result-` datastream, and the result row data is under the `osquery` property in the document. diff --git a/docs/osquery/exported-fields-reference.asciidoc b/docs/osquery/exported-fields-reference.asciidoc index d359c484e4439..600751620c6d9 100644 --- a/docs/osquery/exported-fields-reference.asciidoc +++ b/docs/osquery/exported-fields-reference.asciidoc @@ -1,4 +1,6453 @@ [[exported-fields-osquery]] == Exported fields reference -_Content coming soon._ +The following fields can be returned in osquery results. Note the following about osquery fields: + +* Some fields list multiple descriptions because the one that applies depends on which table was queried. For example, a result stored in the `osquery.autoupdate` field may represent a response from the `firefox_addons` table or the `windows_security_center` table. +* In the cases where a field name is associated with more than one osquery table, we have made a best guess at what the data `type` should be. In the cases where it is unknown, the data type is set as a `keyword` object. + +For more information about osquery tables, see the https://osquery.io/schema[osquery schema documentation]. + +[float] +[[osquery-fields]] +=== Fields + +*UUID* - keyword, text.text + +* _system_extensions.UUID_ - Extension unique id + +*abi* - keyword, text.text + +* _elf_info.abi_ - Section type + +*action* - keyword, text.text + +* _disk_events.action_ - Appear or disappear +* _example.action_ - Action performed in generation +* _file_events.action_ - Change action (UPDATE, REMOVE, etc) +* _hardware_events.action_ - Remove, insert, change properties, etc +* _ntfs_journal_events.action_ - Change action (Write, Delete, etc) +* _scheduled_tasks.action_ - Actions executed by the scheduled task +* _socket_events.action_ - The socket action (bind, listen, close) +* _yara_events.action_ - Change action (UPDATE, REMOVE, etc) + +*activated* - keyword, number.long + +* _tpm_info.activated_ - TPM is activated + +*active* - keyword, number.long + +* _firefox_addons.active_ - 1 If the addon is active else 0 +* _memory_info.active_ - The total amount of buffer or page cache memory, in bytes, that is in active use +* _osquery_events.active_ - 1 if the publisher or subscriber is active else 0 +* _osquery_packs.active_ - Whether this pack is active (the version, platform and discovery queries match) yes=1, no=0. +* _osquery_registry.active_ - 1 If this plugin is active else 0 +* _virtual_memory_info.active_ - Total number of active pages. + +*active_disks* - keyword, number.long + +* _md_devices.active_disks_ - Number of active disks in array + +*active_state* - keyword, text.text + +* _systemd_units.active_state_ - The high-level unit activation state, i.e. generalization of SUB + +*actual* - keyword, number.long + +* _fan_speed_sensors.actual_ - Actual speed + +*additional_product_id* - keyword, text.text + +* _smart_drive_info.additional_product_id_ - An additional drive identifier if any + +*addr* - keyword, number.long + +* _elf_symbols.addr_ - Symbol address (value) + +*address* - keyword, text.text + +* _arp_cache.address_ - IPv4 address target +* _dns_resolvers.address_ - Resolver IP/IPv6 address +* _etc_hosts.address_ - IP address mapping +* _fbsd_kmods.address_ - Kernel module address +* _interface_addresses.address_ - Specific address for interface +* _kernel_modules.address_ - Kernel module address +* _listening_ports.address_ - Specific address for bind +* _platform_info.address_ - Relative address of firmware mapping +* _user_events.address_ - The Internet protocol address or family ID + +*address_width* - keyword, text.text + +* _cpu_info.address_width_ - The width of the CPU address bus. + +*algorithm* - keyword, text.text + +* _authorized_keys.algorithm_ - algorithm of key + +*alias* - keyword, text.text + +* _etc_protocols.alias_ - Protocol alias +* _time_machine_destinations.alias_ - Human readable name of drive + +*aliases* - keyword, text.text + +* _etc_services.aliases_ - Optional space separated list of other names for a service +* _lxd_images.aliases_ - Comma-separated list of image aliases + +*align* - keyword, number.long + +* _elf_sections.align_ - Segment alignment +* _elf_segments.align_ - Segment alignment + +*allow_maximum* - keyword, number.long + +* _shared_resources.allow_maximum_ - Number of concurrent users for this resource has been limited. If True, the value in the MaximumAllowed property is ignored. + +*allow_root* - keyword, text.text + +* _authorizations.allow_root_ - Label top-level key + +*allow_signed_enabled* - keyword, number.long + +* _alf.allow_signed_enabled_ - 1 If allow signed mode is enabled else 0 + +*ami_id* - keyword, text.text + +* _ec2_instance_metadata.ami_id_ - AMI ID used to launch this EC2 instance + +*amperage* - keyword, number.long + +* _battery.amperage_ - The battery's current amperage in mA + +*anonymous* - keyword, number.long + +* _virtual_memory_info.anonymous_ - Total number of anonymous pages. + +*antispyware* - keyword, text.text + +* _windows_security_center.antispyware_ - The health of the monitored Antispyware solution (see windows_security_products) + +*antivirus* - keyword, text.text + +* _windows_security_center.antivirus_ - The health of the monitored Antivirus solution (see windows_security_products) + +*api_version* - keyword, text.text + +* _docker_version.api_version_ - API version + +*apparmor* - keyword, text.text + +* _apparmor_events.apparmor_ - Apparmor Status like ALLOWED, DENIED etc. + +*applescript_enabled* - keyword, text.text + +* _apps.applescript_enabled_ - Info properties NSAppleScriptEnabled label + +*application* - keyword, text.text + +* _office_mru.application_ - Associated Office application + +*arch* - keyword, text.text + +* _deb_packages.arch_ - Package architecture +* _docker_version.arch_ - Hardware architecture +* _os_version.arch_ - OS Architecture +* _pkg_packages.arch_ - Architecture(s) supported +* _rpm_packages.arch_ - Architecture(s) supported +* _seccomp_events.arch_ - Information about the CPU architecture +* _signature.arch_ - If applicable, the arch of the signed code + +*architecture* - keyword, text.text + +* _docker_info.architecture_ - Hardware architecture +* _ec2_instance_metadata.architecture_ - Hardware architecture of this EC2 instance +* _lxd_images.architecture_ - Target architecture for the image +* _lxd_instances.architecture_ - Instance architecture + +*architectures* - keyword, text.text + +* _apt_sources.architectures_ - Repository architectures + +*args* - keyword, text.text + +* _startup_items.args_ - Arguments provided to startup executable + +*arguments* - keyword, text.text + +* _kernel_info.arguments_ - Kernel arguments + +*array_handle* - keyword, text.text + +* _memory_devices.array_handle_ - The memory array that the device is attached to + +*assessments_enabled* - keyword, number.long + +* _gatekeeper.assessments_enabled_ - 1 If a Gatekeeper is enabled else 0 + +*asset_tag* - keyword, text.text + +* _memory_devices.asset_tag_ - Manufacturer specific asset tag of memory device + +*ata_version* - keyword, text.text + +* _smart_drive_info.ata_version_ - ATA version of drive + +*atime* - keyword, number.long + +* _device_file.atime_ - Last access time +* _file.atime_ - Last access time +* _file_events.atime_ - Last access time +* _process_events.atime_ - File last access in UNIX time +* _shared_memory.atime_ - Attached time + +*attach* - keyword, text.text + +* _apparmor_profiles.attach_ - Which executable(s) a profile will attach to. + +*attached* - keyword, number.long + +* _shared_memory.attached_ - Number of attached processes + +*attributes* - keyword, text.text + +* _file.attributes_ - File attrib string. See: https://ss64.com/nt/attrib.html + +*audible_alarm* - keyword, text.text + +* _chassis_info.audible_alarm_ - If TRUE, the frame is equipped with an audible alarm. + +*auid* - keyword + +* _process_events.auid_ - Audit User ID at process start +* _process_file_events.auid_ - Audit user ID of the process using the file +* _seccomp_events.auid_ - Audit user ID (loginuid) of the user who started the analyzed process +* _socket_events.auid_ - Audit User ID +* _user_events.auid_ - Audit User ID + +*authenticate_user* - keyword, text.text + +* _authorizations.authenticate_user_ - Label top-level key + +*authentication_package* - keyword, text.text + +* _logon_sessions.authentication_package_ - The authentication package used to authenticate the owner of the logon session. + +*author* - keyword, text.text + +* _chocolatey_packages.author_ - Optional package author +* _chrome_extensions.author_ - Optional extension author +* _npm_packages.author_ - Package author name +* _python_packages.author_ - Optional package author +* _safari_extensions.author_ - Optional extension author + +*authority* - keyword, text.text + +* _signature.authority_ - Certificate Common Name + +*authority_key_id* - keyword, text.text + +* _certificates.authority_key_id_ - AKID an optionally included SHA1 + +*authority_key_identifier* - keyword, text.text + +* _curl_certificate.authority_key_identifier_ - Authority Key Identifier + +*authorizations* - keyword, text.text + +* _keychain_acls.authorizations_ - A space delimited set of authorization attributes + +*auto_login* - keyword, number.long + +* _wifi_networks.auto_login_ - 1 if auto login is enabled, 0 otherwise + +*auto_update* - keyword, number.long + +* _lxd_images.auto_update_ - Whether the image auto-updates (1) or not (0) + +*autoupdate* - keyword + +* _firefox_addons.autoupdate_ - 1 If the addon applies background updates else 0 +* _windows_security_center.autoupdate_ - The health of the Windows Autoupdate feature + +*availability* - keyword, text.text + +* _cpu_info.availability_ - The availability and status of the CPU. + +*availability_zone* - keyword, text.text + +* _ec2_instance_metadata.availability_zone_ - Availability zone in which this instance launched + +*average* - keyword, text.text + +* _load_average.average_ - Load average over the specified period. + +*average_memory* - keyword, number.long + +* _osquery_schedule.average_memory_ - Average private memory left after executing + +*avg_disk_bytes_per_read* - keyword, number.long + +* _physical_disk_performance.avg_disk_bytes_per_read_ - Average number of bytes transferred from the disk during read operations + +*avg_disk_bytes_per_write* - keyword, number.long + +* _physical_disk_performance.avg_disk_bytes_per_write_ - Average number of bytes transferred to the disk during write operations + +*avg_disk_read_queue_length* - keyword, number.long + +* _physical_disk_performance.avg_disk_read_queue_length_ - Average number of read requests that were queued for the selected disk during the sample interval + +*avg_disk_sec_per_read* - keyword, number.long + +* _physical_disk_performance.avg_disk_sec_per_read_ - Average time, in seconds, of a read operation of data from the disk + +*avg_disk_sec_per_write* - keyword, number.long + +* _physical_disk_performance.avg_disk_sec_per_write_ - Average time, in seconds, of a write operation of data to the disk + +*avg_disk_write_queue_length* - keyword, number.long + +* _physical_disk_performance.avg_disk_write_queue_length_ - Average number of write requests that were queued for the selected disk during the sample interval + +*backup_date* - keyword, number.long + +* _time_machine_backups.backup_date_ - Backup Date + +*bank_locator* - keyword, text.text + +* _memory_devices.bank_locator_ - String number of the string that identifies the physically-labeled bank where the memory device is located + +*base64* - keyword, number.long + +* _extended_attributes.base64_ - 1 if the value is base64 encoded else 0 + +*base_image* - keyword, text.text + +* _lxd_instances.base_image_ - ID of image used to launch this instance + +*base_uri* - keyword, text.text + +* _apt_sources.base_uri_ - Repository base URI + +*baseurl* - keyword, text.text + +* _yum_sources.baseurl_ - Repository base URL + +*basic_constraint* - keyword, text.text + +* _curl_certificate.basic_constraint_ - Basic Constraints + +*binary_queue* - keyword, number.long + +* _carbon_black_info.binary_queue_ - Size in bytes of binaries waiting to be sent to Carbon Black server + +*binding* - keyword, text.text + +* _elf_symbols.binding_ - Binding type + +*bitmap_chunk_size* - keyword, text.text + +* _md_devices.bitmap_chunk_size_ - Bitmap chunk size + +*bitmap_external_file* - keyword, text.text + +* _md_devices.bitmap_external_file_ - External referenced bitmap file + +*bitmap_on_mem* - keyword, text.text + +* _md_devices.bitmap_on_mem_ - Pages allocated in in-memory bitmap, if enabled + +*block* - keyword, text.text + +* _ssh_configs.block_ - The host or match block + +*block_size* - keyword, number.long + +* _block_devices.block_size_ - Block size in bytes +* _device_file.block_size_ - Block size of filesystem +* _file.block_size_ - Block size of filesystem + +*blocks* - keyword, number.long + +* _device_partitions.blocks_ - Number of blocks +* _mounts.blocks_ - Mounted device used blocks + +*blocks_available* - keyword, number.long + +* _mounts.blocks_available_ - Mounted device available blocks + +*blocks_free* - keyword, number.long + +* _mounts.blocks_free_ - Mounted device free blocks + +*blocks_size* - keyword, number.long + +* _device_partitions.blocks_size_ - Byte size of each block +* _mounts.blocks_size_ - Block size in bytes + +*bluetooth_sharing* - keyword, number.long + +* _sharing_preferences.bluetooth_sharing_ - 1 If bluetooth sharing is enabled for any user else 0 + +*board_model* - keyword, text.text + +* _system_info.board_model_ - Board model + +*board_serial* - keyword, text.text + +* _system_info.board_serial_ - Board serial number + +*board_vendor* - keyword, text.text + +* _system_info.board_vendor_ - Board vendor + +*board_version* - keyword, text.text + +* _system_info.board_version_ - Board version + +*boot_partition* - keyword, number.long + +* _logical_drives.boot_partition_ - True if Windows booted from this drive. + +*boot_uuid* - keyword, text.text + +* _ibridge_info.boot_uuid_ - Boot UUID of the iBridge controller + +*bp_microcode_disabled* - keyword, number.long + +* _kva_speculative_info.bp_microcode_disabled_ - Branch Predictions are disabled due to lack of microcode update. + +*bp_mitigations* - keyword, number.long + +* _kva_speculative_info.bp_mitigations_ - Branch Prediction mitigations are enabled. + +*bp_system_pol_disabled* - keyword, number.long + +* _kva_speculative_info.bp_system_pol_disabled_ - Branch Predictions are disabled via system policy. + +*breach_description* - keyword, text.text + +* _chassis_info.breach_description_ - If provided, gives a more detailed description of a detected security breach. + +*bridge_nf_ip6tables* - keyword, number.long + +* _docker_info.bridge_nf_ip6tables_ - 1 if bridge netfilter ip6tables is enabled. 0 otherwise + +*bridge_nf_iptables* - keyword, number.long + +* _docker_info.bridge_nf_iptables_ - 1 if bridge netfilter iptables is enabled. 0 otherwise + +*broadcast* - keyword, text.text + +* _interface_addresses.broadcast_ - Broadcast address for the interface + +*browser_type* - keyword, text.text + +* _chrome_extension_content_scripts.browser_type_ - The browser type (Valid values: chrome, chromium, opera, yandex, brave) +* _chrome_extensions.browser_type_ - The browser type (Valid values: chrome, chromium, opera, yandex, brave, edge, edge_beta) + +*bsd_flags* - keyword, text.text + +* _file.bsd_flags_ - The BSD file flags (chflags). Possible values: NODUMP, UF_IMMUTABLE, UF_APPEND, OPAQUE, HIDDEN, ARCHIVED, SF_IMMUTABLE, SF_APPEND + +*bssid* - keyword, text.text + +* _wifi_status.bssid_ - The current basic service set identifier +* _wifi_survey.bssid_ - The current basic service set identifier + +*btime* - keyword, number.long + +* _file.btime_ - (B)irth or (cr)eate time +* _process_events.btime_ - File creation in UNIX time + +*buffers* - keyword, number.long + +* _memory_info.buffers_ - The amount of physical RAM, in bytes, used for file buffers + +*build* - keyword, text.text + +* _os_version.build_ - Optional build-specific or variant string + +*build_distro* - keyword, text.text + +* _osquery_info.build_distro_ - osquery toolkit platform distribution name (os version) + +*build_id* - keyword, text.text + +* _sandboxes.build_id_ - Sandbox-specific identifier + +*build_number* - keyword, number.long + +* _windows_crashes.build_number_ - Windows build number of the crashing machine + +*build_platform* - keyword, text.text + +* _osquery_info.build_platform_ - osquery toolkit build platform + +*build_time* - keyword, text.text + +* _docker_version.build_time_ - Build time +* _portage_packages.build_time_ - Unix time when package was built + +*bundle_executable* - keyword, text.text + +* _apps.bundle_executable_ - Info properties CFBundleExecutable label + +*bundle_identifier* - keyword, text.text + +* _apps.bundle_identifier_ - Info properties CFBundleIdentifier label +* _running_apps.bundle_identifier_ - The bundle identifier of the application + +*bundle_name* - keyword, text.text + +* _apps.bundle_name_ - Info properties CFBundleName label + +*bundle_package_type* - keyword, text.text + +* _apps.bundle_package_type_ - Info properties CFBundlePackageType label + +*bundle_path* - keyword, text.text + +* _sandboxes.bundle_path_ - Application bundle used by the sandbox +* _system_extensions.bundle_path_ - System extension bundle path + +*bundle_short_version* - keyword, text.text + +* _apps.bundle_short_version_ - Info properties CFBundleShortVersionString label + +*bundle_version* - keyword, text.text + +* _apps.bundle_version_ - Info properties CFBundleVersion label + +*busy_state* - keyword, number.long + +* _iokit_devicetree.busy_state_ - 1 if the device is in a busy state else 0 +* _iokit_registry.busy_state_ - 1 if the node is in a busy state else 0 + +*bytes* - keyword, number.long + +* _curl.bytes_ - Number of bytes in the response +* _iptables.bytes_ - Number of matching bytes for this rule. + +*bytes_available* - keyword, number.long + +* _time_machine_destinations.bytes_available_ - Bytes available on volume + +*bytes_received* - keyword, number.long + +* _lxd_networks.bytes_received_ - Number of bytes received on this network + +*bytes_sent* - keyword, number.long + +* _lxd_networks.bytes_sent_ - Number of bytes sent on this network + +*bytes_used* - keyword, number.long + +* _time_machine_destinations.bytes_used_ - Bytes used on volume + +*ca* - keyword, number.long + +* _certificates.ca_ - 1 if CA: true (certificate is an authority) else 0 + +*cache_path* - keyword, text.text + +* _quicklook_cache.cache_path_ - Path to cache data + +*cached* - keyword, number.long + +* _lxd_images.cached_ - Whether image is cached (1) or not (0) +* _memory_info.cached_ - The amount of physical RAM, in bytes, used as cache memory + +*capability* - keyword, number.long + +* _apparmor_events.capability_ - Capability number + +*capname* - keyword, text.text + +* _apparmor_events.capname_ - Capability requested by the process + +*caption* - keyword, text.text + +* _patches.caption_ - Short description of the patch. +* _windows_optional_features.caption_ - Caption of feature in settings UI + +*captive_portal* - keyword, number.long + +* _wifi_networks.captive_portal_ - 1 if this network has a captive portal, 0 otherwise + +*carve* - keyword, number.long + +* _carves.carve_ - Set this value to '1' to start a file carve + +*carve_guid* - keyword, text.text + +* _carves.carve_guid_ - Identifying value of the carve session + +*category* - keyword, text.text + +* _apps.category_ - The UTI that categorizes the app for the App Store +* _file_events.category_ - The category of the file defined in the config +* _ntfs_journal_events.category_ - The category that the event originated from +* _power_sensors.category_ - The sensor category: currents, voltage, wattage +* _system_extensions.category_ - System extension category +* _yara_events.category_ - The category of the file + +*cdhash* - keyword, text.text + +* _es_process_events.cdhash_ - Codesigning hash of the process +* _signature.cdhash_ - Hash of the application Code Directory + +*celsius* - keyword, number.double + +* _temperature_sensors.celsius_ - Temperature in Celsius + +*certificate* - keyword, text.text + +* _lxd_certificates.certificate_ - Certificate content + +*cgroup_driver* - keyword, text.text + +* _docker_info.cgroup_driver_ - Control groups driver + +*cgroup_namespace* - keyword, text.text + +* _docker_containers.cgroup_namespace_ - cgroup namespace +* _process_namespaces.cgroup_namespace_ - cgroup namespace inode + +*chain* - keyword, text.text + +* _iptables.chain_ - Size of module content. + +*change_type* - keyword, text.text + +* _docker_container_fs_changes.change_type_ - Type of change: C:Modified, A:Added, D:Deleted + +*channel* - keyword + +* _wifi_status.channel_ - Channel number +* _wifi_survey.channel_ - Channel number +* _windows_eventlog.channel_ - Source or channel of the event + +*channel_band* - keyword, number.long + +* _wifi_status.channel_band_ - Channel band +* _wifi_survey.channel_band_ - Channel band + +*channel_width* - keyword, number.long + +* _wifi_status.channel_width_ - Channel width +* _wifi_survey.channel_width_ - Channel width + +*charged* - keyword, number.long + +* _battery.charged_ - 1 if the battery is currently completely charged. 0 otherwise + +*charging* - keyword, number.long + +* _battery.charging_ - 1 if the battery is currently being charged by a power source. 0 otherwise + +*chassis_bridge_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_bridge_capability_available_ - Chassis bridge capability availability + +*chassis_bridge_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_bridge_capability_enabled_ - Is chassis bridge capability enabled. + +*chassis_docsis_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_docsis_capability_available_ - Chassis DOCSIS capability availability + +*chassis_docsis_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_docsis_capability_enabled_ - Chassis DOCSIS capability enabled + +*chassis_id* - keyword, text.text + +* _lldp_neighbors.chassis_id_ - Neighbor chassis ID value + +*chassis_id_type* - keyword, text.text + +* _lldp_neighbors.chassis_id_type_ - Neighbor chassis ID type + +*chassis_mgmt_ips* - keyword, text.text + +* _lldp_neighbors.chassis_mgmt_ips_ - Comma delimited list of chassis management IPS + +*chassis_other_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_other_capability_available_ - Chassis other capability availability + +*chassis_other_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_other_capability_enabled_ - Chassis other capability enabled + +*chassis_repeater_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_repeater_capability_available_ - Chassis repeater capability availability + +*chassis_repeater_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_repeater_capability_enabled_ - Chassis repeater capability enabled + +*chassis_router_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_router_capability_available_ - Chassis router capability availability + +*chassis_router_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_router_capability_enabled_ - Chassis router capability enabled + +*chassis_station_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_station_capability_available_ - Chassis station capability availability + +*chassis_station_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_station_capability_enabled_ - Chassis station capability enabled + +*chassis_sys_description* - keyword, number.long + +* _lldp_neighbors.chassis_sys_description_ - Max number of CPU physical cores + +*chassis_sysname* - keyword, text.text + +* _lldp_neighbors.chassis_sysname_ - CPU brand string, contains vendor and model + +*chassis_tel_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_tel_capability_available_ - Chassis telephone capability availability + +*chassis_tel_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_tel_capability_enabled_ - Chassis telephone capability enabled + +*chassis_types* - keyword, text.text + +* _chassis_info.chassis_types_ - A comma-separated list of chassis types, such as Desktop or Laptop. + +*chassis_wlan_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_wlan_capability_available_ - Chassis wlan capability availability + +*chassis_wlan_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_wlan_capability_enabled_ - Chassis wlan capability enabled + +*check_array_finish* - keyword, text.text + +* _md_devices.check_array_finish_ - Estimated duration of the check array activity + +*check_array_progress* - keyword, text.text + +* _md_devices.check_array_progress_ - Progress of the check array activity + +*check_array_speed* - keyword, text.text + +* _md_devices.check_array_speed_ - Speed of the check array activity + +*checksum* - keyword, text.text + +* _disk_events.checksum_ - UDIF Master checksum if available (CRC32) + +*child_pid* - keyword, number.long + +* _es_process_events.child_pid_ - Process ID of a child process in case of a fork event + +*chunk_size* - keyword, number.long + +* _md_devices.chunk_size_ - chunk size in bytes + +*cid* - keyword, number.long + +* _bpf_process_events.cid_ - Cgroup ID +* _bpf_socket_events.cid_ - Cgroup ID + +*class* - keyword, text.text + +* _authorizations.class_ - Label top-level key +* _drivers.class_ - Device/driver class name +* _elf_dynamic.class_ - Class (32 or 64) +* _elf_info.class_ - Class type, 32 or 64bit +* _iokit_devicetree.class_ - Best matching device class (most-specific category) +* _iokit_registry.class_ - Best matching device class (most-specific category) +* _usb_devices.class_ - USB Device class +* _wmi_cli_event_consumers.class_ - The name of the class. +* _wmi_event_filters.class_ - The name of the class. +* _wmi_filter_consumer_binding.class_ - The name of the class. +* _wmi_script_event_consumers.class_ - The name of the class. + +*client_site_name* - keyword, text.text + +* _ntdomains.client_site_name_ - The name of the site where the domain controller is configured. + +*cmdline* - keyword, text.text + +* _bpf_process_events.cmdline_ - Command line arguments +* _docker_container_processes.cmdline_ - Complete argv +* _es_process_events.cmdline_ - Command line arguments (argv) +* _process_events.cmdline_ - Command line arguments (argv) +* _processes.cmdline_ - Complete argv + +*cmdline_count* - keyword, number.long + +* _es_process_events.cmdline_count_ - Number of command line arguments + +*cmdline_size* - keyword, number.long + +* _process_events.cmdline_size_ - Actual size (bytes) of command line arguments + +*code* - keyword, text.text + +* _seccomp_events.code_ - The seccomp action + +*code_integrity_policy_enforcement_status* - keyword, text.text + +* _hvci_status.code_integrity_policy_enforcement_status_ - The status of the code integrity policy enforcement settings. Returns UNKNOWN if an error is encountered. + +*codename* - keyword, text.text + +* _os_version.codename_ - OS version codename + +*collect_cross_processes* - keyword, number.long + +* _carbon_black_info.collect_cross_processes_ - If the sensor is configured to cross process events + +*collect_data_file_writes* - keyword, number.long + +* _carbon_black_info.collect_data_file_writes_ - If the sensor is configured to collect non binary file writes + +*collect_emet_events* - keyword, number.long + +* _carbon_black_info.collect_emet_events_ - If the sensor is configured to EMET events + +*collect_file_mods* - keyword, number.long + +* _carbon_black_info.collect_file_mods_ - If the sensor is configured to collect file modification events + +*collect_module_info* - keyword, number.long + +* _carbon_black_info.collect_module_info_ - If the sensor is configured to collect metadata of binaries + +*collect_module_loads* - keyword, number.long + +* _carbon_black_info.collect_module_loads_ - If the sensor is configured to capture module loads + +*collect_net_conns* - keyword, number.long + +* _carbon_black_info.collect_net_conns_ - If the sensor is configured to collect network connections + +*collect_process_user_context* - keyword, number.long + +* _carbon_black_info.collect_process_user_context_ - If the sensor is configured to collect the user running a process + +*collect_processes* - keyword, number.long + +* _carbon_black_info.collect_processes_ - If the sensor is configured to process events + +*collect_reg_mods* - keyword, number.long + +* _carbon_black_info.collect_reg_mods_ - If the sensor is configured to collect registry modification events + +*collect_sensor_operations* - keyword, number.long + +* _carbon_black_info.collect_sensor_operations_ - Unknown + +*collect_store_files* - keyword, number.long + +* _carbon_black_info.collect_store_files_ - If the sensor is configured to send back binaries to the Carbon Black server + +*collisions* - keyword, number.long + +* _interface_details.collisions_ - Packet Collisions detected + +*color_depth* - keyword, number.long + +* _video_info.color_depth_ - The amount of bits per pixel to represent color. + +*comm* - keyword, text.text + +* _apparmor_events.comm_ - Command-line name of the command that was used to invoke the analyzed process +* _seccomp_events.comm_ - Command-line name of the command that was used to invoke the analyzed process + +*command* - keyword, text.text + +* _crontab.command_ - Raw command string +* _docker_containers.command_ - Command with arguments +* _shell_history.command_ - Unparsed date/line/command history line + +*command_args* - keyword, text.text + +* _shortcut_files.command_args_ - Command args passed to lnk file. + +*command_line* - keyword, text.text + +* _windows_crashes.command_line_ - Command-line string passed to the crashed process + +*command_line_template* - keyword, text.text + +* _wmi_cli_event_consumers.command_line_template_ - Standard string template that specifies the process to be started. This property can be NULL, and the ExecutablePath property is used as the command line. + +*comment* - keyword, text.text + +* _authorizations.comment_ - Label top-level key +* _docker_image_history.comment_ - Instruction comment +* _etc_protocols.comment_ - Comment with protocol description +* _etc_services.comment_ - Optional comment for a service. +* _groups.comment_ - Remarks or comments associated with the group +* _keychain_items.comment_ - Optional keychain comment + +*common_name* - keyword, text.text + +* _certificates.common_name_ - Certificate CommonName +* _curl_certificate.common_name_ - Common name of company issued to + +*common_path* - keyword, text.text + +* _shortcut_files.common_path_ - Common system path to target file. + +*compat* - keyword, number.long + +* _seccomp_events.compat_ - Is system call in compatibility mode + +*compiler* - keyword, text.text + +* _apps.compiler_ - Info properties DTCompiler label + +*completed_time* - keyword, number.long + +* _cups_jobs.completed_time_ - When the job completed printing + +*components* - keyword, text.text + +* _apt_sources.components_ - Repository components + +*compressed* - keyword, number.long + +* _virtual_memory_info.compressed_ - The total number of pages that have been compressed by the VM compressor. + +*compressor* - keyword, number.long + +* _virtual_memory_info.compressor_ - The number of pages used to store compressed VM pages. + +*computer_name* - keyword, text.text + +* _system_info.computer_name_ - Friendly computer name (optional) +* _windows_eventlog.computer_name_ - Hostname of system where event was generated +* _windows_events.computer_name_ - Hostname of system where event was generated + +*condition* - keyword, text.text + +* _battery.condition_ - One of the following: "Normal" indicates the condition of the battery is within normal tolerances, "Service Needed" indicates that the battery should be checked out by a licensed Mac repair service, "Permanent Failure" indicates the battery needs replacement + +*config_entrypoint* - keyword, text.text + +* _docker_containers.config_entrypoint_ - Container entrypoint(s) + +*config_flag* - keyword, text.text + +* _sip_config.config_flag_ - The System Integrity Protection config flag + +*config_hash* - keyword, text.text + +* _osquery_info.config_hash_ - Hash of the working configuration state + +*config_name* - keyword, text.text + +* _carbon_black_info.config_name_ - Sensor group + +*config_valid* - keyword, number.long + +* _osquery_info.config_valid_ - 1 if the config was loaded and considered valid, else 0 + +*config_value* - keyword, text.text + +* _system_controls.config_value_ - The MIB value set in /etc/sysctl.conf + +*configured_clock_speed* - keyword, number.long + +* _memory_devices.configured_clock_speed_ - Configured speed of memory device in megatransfers per second (MT/s) + +*configured_voltage* - keyword, number.long + +* _memory_devices.configured_voltage_ - Configured operating voltage of device in millivolts + +*connection_id* - keyword, text.text + +* _interface_details.connection_id_ - Name of the network connection as it appears in the Network Connections Control Panel program. + +*connection_status* - keyword, text.text + +* _interface_details.connection_status_ - State of the network adapter connection to the network. + +*consistency_scan_date* - keyword, number.long + +* _time_machine_destinations.consistency_scan_date_ - Consistency scan date + +*consumer* - keyword, text.text + +* _wmi_filter_consumer_binding.consumer_ - Reference to an instance of __EventConsumer that represents the object path to a logical consumer, the recipient of an event. + +*containers* - keyword, number.long + +* _docker_info.containers_ - Total number of containers + +*containers_paused* - keyword, number.long + +* _docker_info.containers_paused_ - Number of containers in paused state + +*containers_running* - keyword, number.long + +* _docker_info.containers_running_ - Number of containers currently running + +*containers_stopped* - keyword, number.long + +* _docker_info.containers_stopped_ - Number of containers in stopped state + +*content* - keyword, text.text + +* _disk_events.content_ - Disk event content + +*content_caching* - keyword, number.long + +* _sharing_preferences.content_caching_ - 1 If content caching is enabled else 0 + +*content_type* - keyword, text.text + +* _package_install_history.content_type_ - Package content_type (optional) + +*conversion_status* - keyword, number.long + +* _bitlocker_info.conversion_status_ - The bitlocker conversion status of the drive. + +*coprocessor_version* - keyword, text.text + +* _ibridge_info.coprocessor_version_ - The manufacturer and chip version + +*copy* - keyword, number.long + +* _virtual_memory_info.copy_ - Total number of copy-on-write pages. + +*copyright* - keyword, text.text + +* _apps.copyright_ - Info properties NSHumanReadableCopyright label + +*core* - keyword, number.long + +* _cpu_time.core_ - Name of the cpu (core) + +*cosine_similarity* - keyword, number.double + +* _powershell_events.cosine_similarity_ - How similar the Powershell script is to a provided 'normal' character frequency + +*count* - keyword, number.long + +* _userassist.count_ - Number of times the application has been executed. +* _yara.count_ - Number of YARA matches +* _yara_events.count_ - Number of YARA matches + +*country_code* - keyword, text.text + +* _wifi_status.country_code_ - The country code (ISO/IEC 3166-1:1997) for the network +* _wifi_survey.country_code_ - The country code (ISO/IEC 3166-1:1997) for the network + +*cpu* - keyword, number.double + +* _docker_container_processes.cpu_ - CPU utilization as percentage + +*cpu_brand* - keyword, text.text + +* _system_info.cpu_brand_ - CPU brand string, contains vendor and model + +*cpu_cfs_period* - keyword, number.long + +* _docker_info.cpu_cfs_period_ - 1 if CPU Completely Fair Scheduler (CFS) period support is enabled. 0 otherwise + +*cpu_cfs_quota* - keyword, number.long + +* _docker_info.cpu_cfs_quota_ - 1 if CPU Completely Fair Scheduler (CFS) quota support is enabled. 0 otherwise + +*cpu_kernelmode_usage* - keyword, number.long + +* _docker_container_stats.cpu_kernelmode_usage_ - CPU kernel mode usage + +*cpu_logical_cores* - keyword, number.long + +* _system_info.cpu_logical_cores_ - Number of logical CPU cores available to the system + +*cpu_microcode* - keyword, text.text + +* _system_info.cpu_microcode_ - Microcode version + +*cpu_physical_cores* - keyword, number.long + +* _system_info.cpu_physical_cores_ - Number of physical CPU cores in to the system + +*cpu_pred_cmd_supported* - keyword, number.long + +* _kva_speculative_info.cpu_pred_cmd_supported_ - PRED_CMD MSR supported by CPU Microcode. + +*cpu_set* - keyword, number.long + +* _docker_info.cpu_set_ - 1 if CPU set selection support is enabled. 0 otherwise + +*cpu_shares* - keyword, number.long + +* _docker_info.cpu_shares_ - 1 if CPU share weighting support is enabled. 0 otherwise + +*cpu_spec_ctrl_supported* - keyword, number.long + +* _kva_speculative_info.cpu_spec_ctrl_supported_ - SPEC_CTRL MSR supported by CPU Microcode. + +*cpu_status* - keyword, number.long + +* _cpu_info.cpu_status_ - The current operating status of the CPU. + +*cpu_subtype* - keyword + +* _processes.cpu_subtype_ - Indicates the specific processor on which an entry may be used. +* _system_info.cpu_subtype_ - CPU subtype + +*cpu_total_usage* - keyword, number.long + +* _docker_container_stats.cpu_total_usage_ - Total CPU usage + +*cpu_type* - keyword + +* _processes.cpu_type_ - Indicates the specific processor designed for installation. +* _system_info.cpu_type_ - CPU type + +*cpu_usermode_usage* - keyword, number.long + +* _docker_container_stats.cpu_usermode_usage_ - CPU user mode usage + +*cpus* - keyword, number.long + +* _docker_info.cpus_ - Number of CPUs + +*crash_path* - keyword, text.text + +* _crashes.crash_path_ - Location of log file +* _windows_crashes.crash_path_ - Path of the log file + +*crashed_thread* - keyword, number.long + +* _crashes.crashed_thread_ - Thread ID which crashed + +*created* - keyword, text.text + +* _authorizations.created_ - Label top-level key +* _docker_containers.created_ - Time of creation as UNIX time +* _docker_image_history.created_ - Time of creation as UNIX time +* _docker_images.created_ - Time of creation as UNIX time +* _docker_networks.created_ - Time of creation as UNIX time +* _keychain_items.created_ - Data item was created + +*created_at* - keyword, text.text + +* _lxd_images.created_at_ - ISO time of image creation +* _lxd_instances.created_at_ - ISO time of creation + +*created_by* - keyword, text.text + +* _docker_image_history.created_by_ - Created by instruction + +*created_time* - keyword, number.long + +* _shellbags.created_time_ - Directory Created time. + +*creation_time* - keyword + +* _account_policy_data.creation_time_ - When the account was first created +* _cups_jobs.creation_time_ - When the print request was initiated + +*creator* - keyword, text.text + +* _firefox_addons.creator_ - Addon-supported creator string + +*creator_pid* - keyword, number.long + +* _shared_memory.creator_pid_ - Process ID that created the segment + +*creator_uid* - keyword, number.long + +* _shared_memory.creator_uid_ - User ID of creator process + +*csname* - keyword, text.text + +* _patches.csname_ - The name of the host the patch is installed on. + +*ctime* - keyword + +* _device_file.ctime_ - Creation time +* _file.ctime_ - Last status change time +* _file_events.ctime_ - Last status change time +* _gatekeeper_approved_apps.ctime_ - Last change time +* _process_events.ctime_ - File last metadata change in UNIX time +* _shared_memory.ctime_ - Changed time + +*current_capacity* - keyword, number.long + +* _battery.current_capacity_ - The battery's current charged capacity in mAh + +*current_clock_speed* - keyword, number.long + +* _cpu_info.current_clock_speed_ - The current frequency of the CPU. + +*current_directory* - keyword, text.text + +* _windows_crashes.current_directory_ - Current working directory of the crashed process + +*current_disk_queue_length* - keyword, number.long + +* _physical_disk_performance.current_disk_queue_length_ - Number of requests outstanding on the disk at the time the performance data is collected + +*current_locale* - keyword, text.text + +* _chrome_extensions.current_locale_ - Current locale supported by extension + +*current_value* - keyword, text.text + +* _system_controls.current_value_ - Value of setting + +*cwd* - keyword, text.text + +* _bpf_process_events.cwd_ - Current working directory +* _es_process_events.cwd_ - The process current working directory +* _process_events.cwd_ - The process current working directory +* _process_file_events.cwd_ - The current working directory of the process +* _processes.cwd_ - Process current working directory + +*cycle_count* - keyword, number.long + +* _battery.cycle_count_ - The number of charge/discharge cycles + +*data* - keyword, text.text + +* _magic.data_ - Magic number data from libmagic +* _registry.data_ - Data content of registry value +* _windows_eventlog.data_ - Data associated with the event +* _windows_events.data_ - Data associated with the event + +*data_width* - keyword, number.long + +* _memory_devices.data_width_ - Data width, in bits, of this memory device + +*database* - keyword, number.long + +* _lxd_cluster_members.database_ - Whether the server is a database node (1) or not (0) + +*date* - keyword + +* _drivers.date_ - Driver date +* _platform_info.date_ - Self-reported platform code update date + +*datetime* - keyword, text.text + +* _crashes.datetime_ - Date/Time at which the crash occurred +* _powershell_events.datetime_ - System time at which the Powershell script event occurred +* _syslog_events.datetime_ - Time known to syslog +* _time.datetime_ - Current date and time (ISO format) in the system +* _windows_crashes.datetime_ - Timestamp (log format) of the crash +* _windows_eventlog.datetime_ - System time at which the event occurred +* _windows_events.datetime_ - System time at which the event occurred + +*day* - keyword, number.long + +* _time.day_ - Current day in the system + +*day_of_month* - keyword, text.text + +* _crontab.day_of_month_ - The day of the month for the job + +*day_of_week* - keyword, text.text + +* _crontab.day_of_week_ - The day of the week for the job + +*days* - keyword, number.long + +* _uptime.days_ - Days of uptime + +*dc_site_name* - keyword, text.text + +* _ntdomains.dc_site_name_ - The name of the site where the domain controller is located. + +*decompressed* - keyword, number.long + +* _virtual_memory_info.decompressed_ - The total number of pages that have been decompressed by the VM compressor. + +*default_locale* - keyword, text.text + +* _chrome_extensions.default_locale_ - Default locale supported by extension + +*default_value* - keyword, text.text + +* _osquery_flags.default_value_ - Flag default value + +*denied_mask* - keyword, text.text + +* _apparmor_events.denied_mask_ - Denied permissions for the process + +*denylisted* - keyword, number.long + +* _osquery_schedule.denylisted_ - 1 if the query is denylisted else 0 + +*dependencies* - keyword, text.text + +* _kernel_panics.dependencies_ - Module dependencies existing in crashed module's backtrace + +*depth* - keyword, number.long + +* _iokit_devicetree.depth_ - Device nested depth +* _iokit_registry.depth_ - Node nested depth + +*description* - keyword, text.text + +* _appcompat_shims.description_ - Description of the SDB. +* _atom_packages.description_ - Package supplied description +* _browser_plugins.description_ - Plugin description text +* _chassis_info.description_ - An extended description of the chassis if available. +* _chrome_extensions.description_ - Extension-optional description +* _disk_info.description_ - The OS's description of the disk. +* _drivers.description_ - Driver description +* _firefox_addons.description_ - Addon-supplied description string +* _interface_details.description_ - Short description of the object a one-line string. +* _keychain_acls.description_ - The description included with the ACL entry +* _keychain_items.description_ - Optional item description +* _logical_drives.description_ - The canonical description of the drive, e.g. 'Logical Fixed Disk', 'CD-ROM Disk'. +* _lxd_images.description_ - Image description +* _lxd_instances.description_ - Instance description +* _npm_packages.description_ - Package supplied description +* _osquery_flags.description_ - Flag description +* _patches.description_ - Fuller description of the patch. +* _safari_extensions.description_ - Optional extension description text +* _services.description_ - Service Description +* _shared_resources.description_ - A textual description of the object +* _shortcut_files.description_ - Lnk file description. +* _smbios_tables.description_ - Table entry description +* _systemd_units.description_ - Unit description +* _users.description_ - Optional user description +* _ycloud_instance_metadata.description_ - Description of the VM + +*designed_capacity* - keyword, number.long + +* _battery.designed_capacity_ - The battery's designed capacity in mAh + +*dest_path* - keyword, text.text + +* _process_file_events.dest_path_ - The canonical path associated with the event + +*destination* - keyword, text.text + +* _cups_jobs.destination_ - The printer the job was sent to +* _docker_container_mounts.destination_ - Destination path inside container +* _routes.destination_ - Destination IP address + +*destination_id* - keyword, text.text + +* _time_machine_backups.destination_id_ - Time Machine destination ID +* _time_machine_destinations.destination_id_ - Time Machine destination ID + +*dev_id_enabled* - keyword, number.long + +* _gatekeeper.dev_id_enabled_ - 1 If a Gatekeeper allows execution from identified developers else 0 + +*developer_id* - keyword, text.text + +* _safari_extensions.developer_id_ - Optional developer identifier +* _xprotect_meta.developer_id_ - Developer identity (SHA1) of extension + +*development_region* - keyword, text.text + +* _apps.development_region_ - Info properties CFBundleDevelopmentRegion label +* _browser_plugins.development_region_ - Plugin language-localization + +*device* - keyword, text.text + +* _device_file.device_ - Absolute file path to device node +* _device_firmware.device_ - The device name +* _device_hash.device_ - Absolute file path to device node +* _device_partitions.device_ - Absolute file path to device node +* _disk_events.device_ - Disk event BSD name +* _file.device_ - Device ID (optional) +* _kernel_info.device_ - Kernel device identifier +* _lxd_instance_devices.device_ - Name of the device +* _mounts.device_ - Mounted device +* _process_memory_map.device_ - MA:MI Major/minor device ID + +*device_alias* - keyword, text.text + +* _mounts.device_alias_ - Mounted device alias + +*device_error_address* - keyword, text.text + +* _memory_error_info.device_error_address_ - 32 bit physical address of the error relative to the start of the failing memory address, in bytes + +*device_id* - keyword, text.text + +* _bitlocker_info.device_id_ - ID of the encrypted drive. +* _cpu_info.device_id_ - The DeviceID of the CPU. +* _drivers.device_id_ - Device ID +* _logical_drives.device_id_ - The drive id, usually the drive name, e.g., 'C:'. + +*device_locator* - keyword, text.text + +* _memory_devices.device_locator_ - String number of the string that identifies the physically-labeled socket or board position where the memory device is located + +*device_model* - keyword, text.text + +* _smart_drive_info.device_model_ - Device Model + +*device_name* - keyword, text.text + +* _drivers.device_name_ - Device name +* _md_devices.device_name_ - md device name +* _smart_drive_info.device_name_ - Name of block device + +*device_path* - keyword, text.text + +* _iokit_devicetree.device_path_ - Device tree path + +*device_type* - keyword, text.text + +* _lxd_instance_devices.device_type_ - Device type +* _shortcut_files.device_type_ - Device containing the target file. + +*dhcp_enabled* - keyword, number.long + +* _interface_details.dhcp_enabled_ - If TRUE, the dynamic host configuration protocol (DHCP) server automatically assigns an IP address to the computer system when establishing a network connection. + +*dhcp_lease_expires* - keyword, text.text + +* _interface_details.dhcp_lease_expires_ - Expiration date and time for a leased IP address that was assigned to the computer by the dynamic host configuration protocol (DHCP) server. + +*dhcp_lease_obtained* - keyword, text.text + +* _interface_details.dhcp_lease_obtained_ - Date and time the lease was obtained for the IP address assigned to the computer by the dynamic host configuration protocol (DHCP) server. + +*dhcp_server* - keyword, text.text + +* _interface_details.dhcp_server_ - IP address of the dynamic host configuration protocol (DHCP) server. + +*directory* - keyword, text.text + +* _extended_attributes.directory_ - Directory of file(s) +* _file.directory_ - Directory of file(s) +* _hash.directory_ - Must provide a path or directory +* _npm_packages.directory_ - Node module's directory where this package is located +* _python_packages.directory_ - Directory where Python modules are located +* _users.directory_ - User's home directory + +*disabled* - keyword + +* _browser_plugins.disabled_ - Is the plugin disabled. 1 = Disabled +* _firefox_addons.disabled_ - 1 If the addon is application-disabled else 0 +* _launchd.disabled_ - Skip loading this daemon or agent on boot +* _wifi_networks.disabled_ - 1 if this network is disabled, 0 otherwise + +*disc_sharing* - keyword, number.long + +* _sharing_preferences.disc_sharing_ - 1 If CD or DVD sharing is enabled else 0 + +*disconnected* - keyword, number.long + +* _connectivity.disconnected_ - True if the all interfaces are not connected to any network + +*discovery_cache_hits* - keyword, number.long + +* _osquery_packs.discovery_cache_hits_ - The number of times that the discovery query used cached values since the last time the config was reloaded + +*discovery_executions* - keyword, number.long + +* _osquery_packs.discovery_executions_ - The number of times that the discovery queries have been executed since the last time the config was reloaded + +*disk_bytes_read* - keyword, number.long + +* _processes.disk_bytes_read_ - Bytes read from disk + +*disk_bytes_written* - keyword, number.long + +* _processes.disk_bytes_written_ - Bytes written to disk + +*disk_id* - keyword, number.long + +* _smart_drive_info.disk_id_ - Physical slot number of device, only exists when hardware storage controller exists + +*disk_index* - keyword, number.long + +* _disk_info.disk_index_ - Physical drive number of the disk. + +*disk_read* - keyword, number.long + +* _docker_container_stats.disk_read_ - Total disk read bytes + +*disk_size* - keyword, number.long + +* _disk_info.disk_size_ - Size of the disk. + +*disk_write* - keyword, number.long + +* _docker_container_stats.disk_write_ - Total disk write bytes + +*display_name* - keyword, text.text + +* _apps.display_name_ - Info properties CFBundleDisplayName label +* _services.display_name_ - Service Display name + +*dns_domain* - keyword, text.text + +* _interface_details.dns_domain_ - Organization name followed by a period and an extension that indicates the type of organization, such as 'microsoft.com'. + +*dns_domain_name* - keyword, text.text + +* _logon_sessions.dns_domain_name_ - The DNS name for the owner of the logon session. + +*dns_domain_suffix_search_order* - keyword, text.text + +* _interface_details.dns_domain_suffix_search_order_ - Array of DNS domain suffixes to be appended to the end of host names during name resolution. + +*dns_forest_name* - keyword, text.text + +* _ntdomains.dns_forest_name_ - The name of the root of the DNS tree. + +*dns_host_name* - keyword, text.text + +* _interface_details.dns_host_name_ - Host name used to identify the local computer for authentication by some utilities. + +*dns_server_search_order* - keyword, text.text + +* _interface_details.dns_server_search_order_ - Array of server IP addresses to be used in querying for DNS servers. + +*domain* - keyword, text.text + +* _ad_config.domain_ - Active Directory trust domain +* _managed_policies.domain_ - System or manager-chosen domain key +* _preferences.domain_ - Application ID usually in com.name.product format + +*domain_controller_address* - keyword, text.text + +* _ntdomains.domain_controller_address_ - The IP Address of the discovered domain controller.. + +*domain_controller_name* - keyword, text.text + +* _ntdomains.domain_controller_name_ - The name of the discovered domain controller. + +*domain_name* - keyword, text.text + +* _ntdomains.domain_name_ - The name of the domain. + +*drive_letter* - keyword, text.text + +* _bitlocker_info.drive_letter_ - Drive letter of the encrypted drive. +* _ntfs_journal_events.drive_letter_ - The drive letter identifying the source journal + +*drive_name* - keyword, text.text + +* _md_drives.drive_name_ - Drive device name + +*driver* - keyword, text.text + +* _docker_container_mounts.driver_ - Driver providing the mount +* _docker_networks.driver_ - Network driver +* _docker_volumes.driver_ - Volume driver +* _hardware_events.driver_ - Driver claiming the device +* _lxd_storage_pools.driver_ - Storage driver +* _pci_devices.driver_ - PCI Device used driver +* _video_info.driver_ - The driver of the device. + +*driver_date* - keyword, number.long + +* _video_info.driver_date_ - The date listed on the installed driver. + +*driver_key* - keyword, text.text + +* _drivers.driver_key_ - Driver key + +*driver_type* - keyword, text.text + +* _smart_drive_info.driver_type_ - The explicit device type used to retrieve the SMART information + +*driver_version* - keyword, text.text + +* _video_info.driver_version_ - The version of the installed driver. + +*dst_ip* - keyword, text.text + +* _iptables.dst_ip_ - Destination IP address. + +*dst_mask* - keyword, text.text + +* _iptables.dst_mask_ - Destination IP address mask. + +*dst_port* - keyword, text.text + +* _iptables.dst_port_ - Protocol destination port(s). + +*dtime* - keyword, number.long + +* _shared_memory.dtime_ - Detached time + +*dump_certificate* - keyword, number.long + +* _curl_certificate.dump_certificate_ - Set this value to '1' to dump certificate + +*duration* - keyword, number.long + +* _bpf_process_events.duration_ - How much time was spent inside the syscall (nsecs) +* _bpf_socket_events.duration_ - How much time was spent inside the syscall (nsecs) + +*eapi* - keyword, number.long + +* _portage_packages.eapi_ - The eapi for the ebuild + +*egid* - keyword + +* _docker_container_processes.egid_ - Effective group ID +* _es_process_events.egid_ - Effective Group ID of the process +* _process_events.egid_ - Effective group ID at process start +* _process_file_events.egid_ - Effective group ID of the process using the file +* _processes.egid_ - Unsigned effective group ID + +*eid* - keyword, text.text + +* _apparmor_events.eid_ - Event ID +* _bpf_process_events.eid_ - Event ID +* _bpf_socket_events.eid_ - Event ID +* _disk_events.eid_ - Event ID +* _es_process_events.eid_ - Event ID +* _file_events.eid_ - Event ID +* _hardware_events.eid_ - Event ID +* _ntfs_journal_events.eid_ - Event ID +* _process_events.eid_ - Event ID +* _process_file_events.eid_ - Event ID +* _selinux_events.eid_ - Event ID +* _socket_events.eid_ - Event ID +* _syslog_events.eid_ - Event ID +* _user_events.eid_ - Event ID +* _windows_events.eid_ - Event ID +* _yara_events.eid_ - Event ID + +*ejectable* - keyword, number.long + +* _disk_events.ejectable_ - 1 if ejectable, 0 if not + +*elapsed_time* - keyword, number.long + +* _processes.elapsed_time_ - Elapsed time in seconds this process has been running. + +*element* - keyword, text.text + +* _apps.element_ - Does the app identify as a background agent + +*elevated_token* - keyword, number.long + +* _processes.elevated_token_ - Process uses elevated token yes=1, no=0 + +*enable_ipv6* - keyword, number.long + +* _docker_networks.enable_ipv6_ - 1 if IPv6 is enabled on this network. 0 otherwise + +*enabled* - keyword + +* _app_schemes.enabled_ - 1 if this handler is the OS default, else 0 +* _event_taps.enabled_ - Is the Event Tap enabled +* _interface_details.enabled_ - Indicates whether the adapter is enabled or not. +* _location_services.enabled_ - 1 if Location Services are enabled, else 0 +* _lxd_cluster.enabled_ - Whether clustering enabled (1) or not (0) on this node +* _sandboxes.enabled_ - Application sandboxings enabled on container +* _scheduled_tasks.enabled_ - Whether or not the scheduled task is enabled +* _screenlock.enabled_ - 1 If a password is required after sleep or the screensaver begins; else 0 +* _sip_config.enabled_ - 1 if this configuration is enabled, otherwise 0 +* _tpm_info.enabled_ - TPM is enabled +* _yum_sources.enabled_ - Whether the repository is used + +*enabled_nvram* - keyword, number.long + +* _sip_config.enabled_nvram_ - 1 if this configuration is enabled, otherwise 0 + +*encrypted* - keyword, number.long + +* _disk_encryption.encrypted_ - 1 If encrypted: true (disk is encrypted), else 0 +* _user_ssh_keys.encrypted_ - 1 if key is encrypted, 0 otherwise + +*encryption* - keyword, text.text + +* _time_machine_destinations.encryption_ - Last known encrypted state + +*encryption_method* - keyword, text.text + +* _bitlocker_info.encryption_method_ - The encryption type of the device. + +*encryption_status* - keyword, text.text + +* _disk_encryption.encryption_status_ - Disk encryption status with one of following values: encrypted | not encrypted | undefined + +*end* - keyword, text.text + +* _memory_map.end_ - End address of memory region +* _process_memory_map.end_ - Virtual end address (hex) + +*ending_address* - keyword, text.text + +* _memory_array_mapped_addresses.ending_address_ - Physical ending address of last kilobyte of a range of memory mapped to physical memory array +* _memory_device_mapped_addresses.ending_address_ - Physical ending address of last kilobyte of a range of memory mapped to physical memory array + +*endpoint_id* - keyword, text.text + +* _docker_container_networks.endpoint_id_ - Endpoint ID + +*entry* - keyword, text.text + +* _authorization_mechanisms.entry_ - The whole string entry +* _elf_info.entry_ - Entry point address +* _shimcache.entry_ - Execution order. + +*env* - keyword, text.text + +* _es_process_events.env_ - Environment variables delimited by spaces +* _process_events.env_ - Environment variables delimited by spaces + +*env_count* - keyword, number.long + +* _es_process_events.env_count_ - Number of environment variables +* _process_events.env_count_ - Number of environment variables + +*env_size* - keyword, number.long + +* _process_events.env_size_ - Actual size (bytes) of environment list + +*env_variables* - keyword, text.text + +* _docker_containers.env_variables_ - Container environmental variables + +*environment* - keyword, text.text + +* _apps.environment_ - Application-set environment variables + +*ephemeral* - keyword, number.long + +* _lxd_instances.ephemeral_ - Whether the instance is ephemeral(1) or not(0) + +*epoch* - keyword, number.long + +* _rpm_packages.epoch_ - Package epoch value + +*error* - keyword, text.text + +* _apparmor_events.error_ - Error information + +*error_granularity* - keyword, text.text + +* _memory_error_info.error_granularity_ - Granularity to which the error can be resolved + +*error_operation* - keyword, text.text + +* _memory_error_info.error_operation_ - Memory access operation that caused the error + +*error_resolution* - keyword, text.text + +* _memory_error_info.error_resolution_ - Range, in bytes, within which this error can be determined, when an error address is given + +*error_type* - keyword, text.text + +* _memory_error_info.error_type_ - type of error associated with current error status for array or device + +*euid* - keyword + +* _docker_container_processes.euid_ - Effective user ID +* _es_process_events.euid_ - Effective User ID of the process +* _process_events.euid_ - Effective user ID at process start +* _process_file_events.euid_ - Effective user ID of the process using the file +* _processes.euid_ - Unsigned effective user ID + +*event* - keyword, text.text + +* _crontab.event_ - The job @event name (rare) + +*event_queue* - keyword, number.long + +* _carbon_black_info.event_queue_ - Size in bytes of Carbon Black event files on disk + +*event_tap_id* - keyword, number.long + +* _event_taps.event_tap_id_ - Unique ID for the Tap + +*event_tapped* - keyword, text.text + +* _event_taps.event_tapped_ - The mask that identifies the set of events to be observed. + +*event_type* - keyword, text.text + +* _es_process_events.event_type_ - Type of EndpointSecurity event + +*eventid* - keyword, number.long + +* _windows_eventlog.eventid_ - Event ID of the event +* _windows_events.eventid_ - Event ID of the event + +*events* - keyword, number.long + +* _osquery_events.events_ - Number of events emitted or received since osquery started + +*exception_address* - keyword, text.text + +* _windows_crashes.exception_address_ - Address (in hex) where the exception occurred + +*exception_code* - keyword, text.text + +* _windows_crashes.exception_code_ - The Windows exception code + +*exception_codes* - keyword, text.text + +* _crashes.exception_codes_ - Exception codes from the crash + +*exception_message* - keyword, text.text + +* _windows_crashes.exception_message_ - The NTSTATUS error message associated with the exception code + +*exception_notes* - keyword, text.text + +* _crashes.exception_notes_ - Exception notes from the crash + +*exception_type* - keyword, text.text + +* _crashes.exception_type_ - Exception type of the crash + +*exe* - keyword, text.text + +* _seccomp_events.exe_ - The path to the executable that was used to invoke the analyzed process + +*executable* - keyword, text.text + +* _appcompat_shims.executable_ - Name of the executable that is being shimmed. This is pulled from the registry. +* _process_file_events.executable_ - The executable path + +*executable_path* - keyword, text.text + +* _wmi_cli_event_consumers.executable_path_ - Module to execute. The string can specify the full path and file name of the module to execute, or it can specify a partial name. If a partial name is specified, the current drive and current directory are assumed. + +*execution_flag* - keyword, number.long + +* _shimcache.execution_flag_ - Boolean Execution flag, 1 for execution, 0 for no execution, -1 for missing (this flag does not exist on Windows 10 and higher). + +*executions* - keyword, number.long + +* _osquery_schedule.executions_ - Number of times the query was executed + +*exit_code* - keyword, text.text + +* _bpf_process_events.exit_code_ - Exit code of the system call +* _bpf_socket_events.exit_code_ - Exit code of the system call +* _es_process_events.exit_code_ - Exit code of a process in case of an exit event + +*expand* - keyword, number.long + +* _default_environment.expand_ - 1 if the variable needs expanding, 0 otherwise + +*expire* - keyword, number.long + +* _shadow.expire_ - Number of days since UNIX epoch date until account is disabled + +*expires_at* - keyword, text.text + +* _lxd_images.expires_at_ - ISO time of image expiration + +*extended_key_usage* - keyword, text.text + +* _curl_certificate.extended_key_usage_ - Extended usage of key in certificate + +*extensions* - keyword, text.text + +* _osquery_info.extensions_ - osquery extensions status + +*external* - keyword, number.long + +* _app_schemes.external_ - 1 if this handler does NOT exist on OS X by default, else 0 + +*extra* - keyword, text.text + +* _asl.extra_ - Extra columns, in JSON format. Queries against this column are performed entirely in SQLite, so do not benefit from efficient querying via asl.h. +* _platform_info.extra_ - Platform-specific additional information + +*facility* - keyword, text.text + +* _asl.facility_ - Sender's facility. Default is 'user'. +* _syslog_events.facility_ - Syslog facility + +*fahrenheit* - keyword, number.double + +* _temperature_sensors.fahrenheit_ - Temperature in Fahrenheit + +*failed_disks* - keyword, number.long + +* _md_devices.failed_disks_ - Number of failed disks in array + +*failed_login_count* - keyword, number.long + +* _account_policy_data.failed_login_count_ - The number of failed login attempts using an incorrect password. Count resets after a correct password is entered. + +*failed_login_timestamp* - keyword, number.double + +* _account_policy_data.failed_login_timestamp_ - The time of the last failed login attempt. Resets after a correct password is entered + +*family* - keyword, number.long + +* _bpf_socket_events.family_ - The Internet protocol family ID +* _listening_ports.family_ - Network protocol (IPv4, IPv6) +* _process_open_sockets.family_ - Network protocol (IPv4, IPv6) +* _socket_events.family_ - The Internet protocol family ID + +*fan* - keyword, text.text + +* _fan_speed_sensors.fan_ - Fan number + +*faults* - keyword, number.long + +* _virtual_memory_info.faults_ - Total number of calls to vm_faults. + +*fd* - keyword, text.text + +* _bpf_socket_events.fd_ - The file description for the process socket +* _listening_ports.fd_ - Socket file descriptor number +* _process_open_files.fd_ - Process-specific file descriptor number +* _process_open_pipes.fd_ - File descriptor +* _process_open_sockets.fd_ - Socket file descriptor number +* _socket_events.fd_ - The file description for the process socket + +*feature* - keyword, text.text + +* _cpuid.feature_ - Present feature flags + +*feature_control* - keyword, number.long + +* _msr.feature_control_ - Bitfield controlling enabled features. + +*field_name* - keyword, text.text + +* _system_controls.field_name_ - Specific attribute of opaque type + +*file_attributes* - keyword, text.text + +* _ntfs_journal_events.file_attributes_ - File attributes + +*file_backed* - keyword, number.long + +* _virtual_memory_info.file_backed_ - Total number of file backed pages. + +*file_id* - keyword, text.text + +* _file.file_id_ - file ID + +*file_sharing* - keyword, number.long + +* _sharing_preferences.file_sharing_ - 1 If file sharing is enabled else 0 + +*file_system* - keyword, text.text + +* _logical_drives.file_system_ - The file system of the drive. + +*file_version* - keyword, text.text + +* _file.file_version_ - File version + +*filename* - keyword, text.text + +* _device_file.filename_ - Name portion of file path +* _file.filename_ - Name portion of file path +* _lxd_images.filename_ - Filename of the image file +* _prefetch.filename_ - Executable filename. +* _xprotect_entries.filename_ - Use this file name to match + +*filepath* - keyword, text.text + +* _package_bom.filepath_ - Package file or directory + +*filesystem* - keyword, text.text + +* _disk_events.filesystem_ - Filesystem if available + +*filetype* - keyword, text.text + +* _xprotect_entries.filetype_ - Use this file type to match + +*filevault_status* - keyword, text.text + +* _disk_encryption.filevault_status_ - FileVault status with one of following values: on | off | unknown + +*filter* - keyword, text.text + +* _wmi_filter_consumer_binding.filter_ - Reference to an instance of __EventFilter that represents the object path to an event filter which is a query that specifies the type of event to be received. + +*filter_name* - keyword, text.text + +* _iptables.filter_name_ - Packet matching filter table name. + +*fingerprint* - keyword, text.text + +* _lxd_certificates.fingerprint_ - SHA256 hash of the certificate + +*finished_at* - keyword, text.text + +* _docker_containers.finished_at_ - Container finish time as string + +*firewall* - keyword, text.text + +* _windows_security_center.firewall_ - The health of the monitored Firewall (see windows_security_products) + +*firewall_unload* - keyword, number.long + +* _alf.firewall_unload_ - 1 If firewall unloading enabled else 0 + +*firmware_version* - keyword, text.text + +* _ibridge_info.firmware_version_ - The build version of the firmware +* _smart_drive_info.firmware_version_ - Drive firmware version + +*fix_comments* - keyword, text.text + +* _patches.fix_comments_ - Additional comments about the patch. + +*flag* - keyword, number.long + +* _shadow.flag_ - Reserved + +*flags* - keyword + +* _device_partitions.flags_ - +* _dns_cache.flags_ - DNS record flags +* _elf_info.flags_ - ELF header flags +* _elf_sections.flags_ - Section attributes +* _elf_segments.flags_ - Segment attributes +* _interface_details.flags_ - Flags (netdevice) for the device +* _mounts.flags_ - Mounted device flags +* _pipes.flags_ - The flags indicating whether this pipe connection is a server or client end, and if the pipe for sending messages or bytes +* _routes.flags_ - Flags to describe route + +*flatsize* - keyword, number.long + +* _pkg_packages.flatsize_ - Package size in bytes + +*folder_id* - keyword, text.text + +* _ycloud_instance_metadata.folder_id_ - Folder identifier for the VM + +*following* - keyword, text.text + +* _systemd_units.following_ - The name of another unit that this unit follows in state + +*forced* - keyword, number.long + +* _preferences.forced_ - 1 if the value is forced/managed, else 0 + +*form_factor* - keyword, text.text + +* _memory_devices.form_factor_ - Implementation form factor for this memory device +* _smart_drive_info.form_factor_ - Form factor if reported + +*format* - keyword, text.text + +* _cups_jobs.format_ - The format of the print job + +*forwarding_enabled* - keyword, number.long + +* _interface_ipv6.forwarding_enabled_ - Enable IP forwarding + +*fragment_path* - keyword, text.text + +* _systemd_units.fragment_path_ - The unit file path this unit was read from, if there is any + +*frame_backtrace* - keyword, text.text + +* _kernel_panics.frame_backtrace_ - Backtrace of the crashed module + +*free* - keyword, number.long + +* _virtual_memory_info.free_ - Total number of free pages. + +*free_space* - keyword, number.long + +* _logical_drives.free_space_ - The amount of free space, in bytes, of the drive (-1 on failure). + +*friendly_name* - keyword, text.text + +* _interface_addresses.friendly_name_ - The friendly display name of the interface. +* _interface_details.friendly_name_ - The friendly display name of the interface. + +*from_webstore* - keyword, text.text + +* _chrome_extensions.from_webstore_ - True if this extension was installed from the web store + +*fs_id* - keyword, text.text + +* _quicklook_cache.fs_id_ - Quicklook file fs_id key + +*fsgid* - keyword + +* _process_events.fsgid_ - Filesystem group ID at process start +* _process_file_events.fsgid_ - Filesystem group ID of the process using the file + +*fsuid* - keyword + +* _apparmor_events.fsuid_ - Filesystem user ID +* _process_events.fsuid_ - Filesystem user ID at process start +* _process_file_events.fsuid_ - Filesystem user ID of the process using the file + +*gateway* - keyword, text.text + +* _docker_container_networks.gateway_ - Gateway +* _docker_networks.gateway_ - Network gateway +* _routes.gateway_ - Route gateway + +*gid* - keyword + +* _asl.gid_ - GID that sent the log message (set by the server). +* _bpf_process_events.gid_ - Group ID +* _bpf_socket_events.gid_ - Group ID +* _device_file.gid_ - Owning group ID +* _docker_container_processes.gid_ - Group ID +* _es_process_events.gid_ - Group ID of the process +* _file.gid_ - Owning group ID +* _file_events.gid_ - Owning group ID +* _groups.gid_ - Unsigned int64 group ID +* _package_bom.gid_ - Expected group of file or directory +* _process_events.gid_ - Group ID at process start +* _process_file_events.gid_ - The gid of the process performing the action +* _processes.gid_ - Unsigned group ID +* _seccomp_events.gid_ - Group ID of the user who started the analyzed process +* _user_groups.gid_ - Group ID +* _users.gid_ - Group ID (unsigned) + +*gid_signed* - keyword, number.long + +* _groups.gid_signed_ - A signed int64 version of gid +* _users.gid_signed_ - Default group ID as int64 signed (Apple) + +*git_commit* - keyword, text.text + +* _docker_version.git_commit_ - Docker build git commit + +*global_seq_num* - keyword, number.long + +* _es_process_events.global_seq_num_ - Global sequence number + +*global_state* - keyword, number.long + +* _alf.global_state_ - 1 If the firewall is enabled with exceptions, 2 if the firewall is configured to block all incoming connections, else 0 + +*go_version* - keyword, text.text + +* _docker_version.go_version_ - Go version + +*gpgcheck* - keyword, text.text + +* _yum_sources.gpgcheck_ - Whether packages are GPG checked + +*gpgkey* - keyword, text.text + +* _yum_sources.gpgkey_ - URL to GPG key + +*grace_period* - keyword, number.long + +* _screenlock.grace_period_ - The amount of time in seconds the screen must be asleep or the screensaver on before a password is required on-wake. 0 = immediately; -1 = no password is required on-wake + +*group_sid* - keyword, text.text + +* _groups.group_sid_ - Unique group ID + +*groupname* - keyword, text.text + +* _groups.groupname_ - Canonical local group name +* _launchd.groupname_ - Run this daemon or agent as this group +* _rpm_package_files.groupname_ - File default groupname from info DB +* _suid_bin.groupname_ - Binary owner group + +*guest* - keyword, number.long + +* _cpu_time.guest_ - Time spent running a virtual CPU for a guest OS under the control of the Linux kernel + +*guest_nice* - keyword, number.long + +* _cpu_time.guest_nice_ - Time spent running a niced guest + +*handle* - keyword, text.text + +* _memory_array_mapped_addresses.handle_ - Handle, or instance number, associated with the structure +* _memory_arrays.handle_ - Handle, or instance number, associated with the array +* _memory_device_mapped_addresses.handle_ - Handle, or instance number, associated with the structure +* _memory_devices.handle_ - Handle, or instance number, associated with the structure in SMBIOS +* _memory_error_info.handle_ - Handle, or instance number, associated with the structure +* _oem_strings.handle_ - Handle, or instance number, associated with the Type 11 structure +* _smbios_tables.handle_ - Table entry handle + +*handle_count* - keyword, number.long + +* _processes.handle_count_ - Total number of handles that the process has open. This number is the sum of the handles currently opened by each thread in the process. + +*handler* - keyword, text.text + +* _app_schemes.handler_ - Application label for the handler + +*hard_limit* - keyword, text.text + +* _ulimit_info.hard_limit_ - Maximum limit value + +*hard_links* - keyword, number.long + +* _device_file.hard_links_ - Number of hard links +* _file.hard_links_ - Number of hard links + +*hardware_model* - keyword, text.text + +* _disk_info.hardware_model_ - Hard drive model. +* _system_info.hardware_model_ - Hardware model + +*hardware_serial* - keyword, text.text + +* _system_info.hardware_serial_ - Device serial number + +*hardware_vendor* - keyword, text.text + +* _system_info.hardware_vendor_ - Hardware vendor + +*hardware_version* - keyword, text.text + +* _system_info.hardware_version_ - Hardware version + +*has_expired* - keyword, number.long + +* _curl_certificate.has_expired_ - 1 if the certificate has expired, 0 otherwise + +*hash* - keyword, text.text + +* _prefetch.hash_ - Prefetch CRC hash. + +*hash_alg* - keyword, text.text + +* _shadow.hash_alg_ - Password hashing algorithm + +*hash_resources* - keyword, number.long + +* _signature.hash_resources_ - Set to 1 to also hash resources, or 0 otherwise. Default is 1 + +*hashed* - keyword, number.long + +* _file_events.hashed_ - 1 if the file was hashed, 0 if not, -1 if hashing failed + +*header* - keyword, text.text + +* _sudoers.header_ - Symbol for given rule + +*header_size* - keyword, number.long + +* _smbios_tables.header_size_ - Header size in bytes + +*health* - keyword, text.text + +* _battery.health_ - One of the following: "Good" describes a well-performing battery, "Fair" describes a functional battery with limited capacity, or "Poor" describes a battery that's not capable of providing power + +*hidden* - keyword, number.long + +* _scheduled_tasks.hidden_ - Whether or not the task is visible in the UI +* _smc_keys.hidden_ - 1 if this key is normally hidden, otherwise 0 + +*history_file* - keyword, text.text + +* _shell_history.history_file_ - Path to the .*_history for this user + +*hit_count* - keyword, text.text + +* _quicklook_cache.hit_count_ - Number of cache hits on thumbnail + +*home_directory* - keyword, text.text + +* _logon_sessions.home_directory_ - The home directory for the logon session. + +*home_directory_drive* - keyword, text.text + +* _logon_sessions.home_directory_drive_ - The drive location of the home directory of the logon session. + +*homepage* - keyword, text.text + +* _atom_packages.homepage_ - Package supplied homepage + +*hop_limit* - keyword, number.long + +* _interface_ipv6.hop_limit_ - Current Hop Limit + +*hopcount* - keyword, number.long + +* _routes.hopcount_ - Max hops expected + +*host* - keyword, text.text + +* _asl.host_ - Sender's address (set by the server). +* _last.host_ - Entry hostname +* _logged_in_users.host_ - Remote hostname +* _preferences.host_ - 'current' or 'any' host, where 'current' takes precedence +* _syslog_events.host_ - Hostname configured for syslog + +*host_ip* - keyword, text.text + +* _docker_container_ports.host_ip_ - Host IP address on which public port is listening + +*host_port* - keyword, number.long + +* _docker_container_ports.host_port_ - Host port + +*hostname* - keyword, text.text + +* _curl_certificate.hostname_ - Hostname (domain[:port]) to CURL +* _shortcut_files.hostname_ - Optional hostname of the target file. +* _system_info.hostname_ - Network hostname including domain +* _ycloud_instance_metadata.hostname_ - Hostname of the VM + +*hostnames* - keyword, text.text + +* _etc_hosts.hostnames_ - Raw hosts mapping + +*hotfix_id* - keyword, text.text + +* _patches.hotfix_id_ - The KB ID of the patch. + +*hour* - keyword, text.text + +* _crontab.hour_ - The hour of the day for the job +* _time.hour_ - Current hour in the system + +*hours* - keyword, number.long + +* _uptime.hours_ - Hours of uptime + +*http_proxy* - keyword, text.text + +* _docker_info.http_proxy_ - HTTP proxy + +*https_proxy* - keyword, text.text + +* _docker_info.https_proxy_ - HTTPS proxy + +*hwaddr* - keyword, text.text + +* _lxd_networks.hwaddr_ - Hardware address for this network + +*iam_arn* - keyword, text.text + +* _ec2_instance_metadata.iam_arn_ - If there is an IAM role associated with the instance, contains instance profile ARN + +*ibrs_support_enabled* - keyword, number.long + +* _kva_speculative_info.ibrs_support_enabled_ - Windows uses IBRS. + +*ibytes* - keyword, number.long + +* _interface_details.ibytes_ - Input bytes + +*icon_mode* - keyword, number.long + +* _quicklook_cache.icon_mode_ - Thumbnail icon mode + +*icon_path* - keyword, text.text + +* _shortcut_files.icon_path_ - Lnk file icon location. + +*id* - keyword, text.text + +* _disk_info.id_ - The unique identifier of the drive on the system. +* _dns_resolvers.id_ - Address type index or order +* _docker_container_fs_changes.id_ - Container ID +* _docker_container_labels.id_ - Container ID +* _docker_container_mounts.id_ - Container ID +* _docker_container_networks.id_ - Container ID +* _docker_container_ports.id_ - Container ID +* _docker_container_processes.id_ - Container ID +* _docker_container_stats.id_ - Container ID +* _docker_containers.id_ - Container ID +* _docker_image_history.id_ - Image ID +* _docker_image_labels.id_ - Image ID +* _docker_image_layers.id_ - Image ID +* _docker_images.id_ - Image ID +* _docker_info.id_ - Docker system ID +* _docker_network_labels.id_ - Network ID +* _docker_networks.id_ - Network ID +* _example.id_ - An index of some sort +* _iokit_devicetree.id_ - IOKit internal registry ID +* _iokit_registry.id_ - IOKit internal registry ID +* _lxd_images.id_ - Image ID +* _systemd_units.id_ - Unique unit identifier + +*identifier* - keyword, text.text + +* _browser_plugins.identifier_ - Plugin identifier +* _chrome_extension_content_scripts.identifier_ - Extension identifier +* _chrome_extensions.identifier_ - Extension identifier, computed from its manifest. Empty in case of error. +* _crashes.identifier_ - Identifier of the crashed process +* _firefox_addons.identifier_ - Addon identifier +* _safari_extensions.identifier_ - Extension identifier +* _signature.identifier_ - The signing identifier sealed into the signature +* _system_extensions.identifier_ - Identifier name +* _xprotect_meta.identifier_ - Browser plugin or extension identifier + +*identifying_number* - keyword, text.text + +* _programs.identifying_number_ - Product identification such as a serial number on software, or a die number on a hardware chip. + +*identity* - keyword, text.text + +* _xprotect_entries.identity_ - XProtect identity (SHA1) of content + +*idle* - keyword, number.long + +* _cpu_time.idle_ - Time spent in the idle task + +*idrops* - keyword, number.long + +* _interface_details.idrops_ - Input drops + +*idx* - keyword, number.long + +* _kernel_extensions.idx_ - Extension load tag or index + +*ierrors* - keyword, number.long + +* _interface_details.ierrors_ - Input errors + +*image* - keyword, text.text + +* _docker_containers.image_ - Docker image (name) used to launch this container +* _drivers.image_ - Path to driver image file + +*image_id* - keyword, text.text + +* _docker_containers.image_id_ - Docker image ID + +*images* - keyword, number.long + +* _docker_info.images_ - Number of images + +*in_smartctl_db* - keyword, number.long + +* _smart_drive_info.in_smartctl_db_ - Boolean value for if drive is recognized + +*inactive* - keyword, number.long + +* _memory_info.inactive_ - The total amount of buffer or page cache memory, in bytes, that are free and available +* _shadow.inactive_ - Number of days after password expires until account is blocked +* _virtual_memory_info.inactive_ - Total number of inactive pages. + +*inetd_compatibility* - keyword, text.text + +* _launchd.inetd_compatibility_ - Run this daemon or agent as it was launched from inetd + +*inf* - keyword, text.text + +* _drivers.inf_ - Associated inf file + +*info* - keyword, text.text + +* _apparmor_events.info_ - Additional information + +*info_access* - keyword, text.text + +* _curl_certificate.info_access_ - Authority Information Access + +*info_string* - keyword, text.text + +* _apps.info_string_ - Info properties CFBundleGetInfoString label + +*inherited_from* - keyword, text.text + +* _ntfs_acl_permissions.inherited_from_ - The inheritance policy of the ACE. + +*iniface* - keyword, text.text + +* _iptables.iniface_ - Input interface for the rule. + +*iniface_mask* - keyword, text.text + +* _iptables.iniface_mask_ - Input interface mask for the rule. + +*inode* - keyword, number.long + +* _device_file.inode_ - Filesystem inode number +* _device_hash.inode_ - Filesystem inode number +* _file.inode_ - Filesystem inode number +* _file_events.inode_ - Filesystem inode number +* _process_memory_map.inode_ - Mapped path inode, 0 means uninitialized (BSS) +* _process_open_pipes.inode_ - Pipe inode number +* _quicklook_cache.inode_ - Parsed file ID (inode) from fs_id + +*inodes* - keyword, number.long + +* _device_partitions.inodes_ - Number of meta nodes +* _mounts.inodes_ - Mounted device used inodes + +*inodes_free* - keyword, number.long + +* _mounts.inodes_free_ - Mounted device free inodes + +*inodes_total* - keyword, number.long + +* _lxd_storage_pools.inodes_total_ - Total number of inodes available in this storage pool + +*inodes_used* - keyword, number.long + +* _lxd_storage_pools.inodes_used_ - Number of inodes used + +*input_eax* - keyword, text.text + +* _cpuid.input_eax_ - Value of EAX used + +*install_date* - keyword + +* _os_version.install_date_ - The install date of the OS. +* _patches.install_date_ - Indicates when the patch was installed. Lack of a value does not indicate that the patch was not installed. +* _programs.install_date_ - Date that this product was installed on the system. +* _shared_resources.install_date_ - Indicates when the object was installed. Lack of a value does not indicate that the object is not installed. + +*install_location* - keyword, text.text + +* _programs.install_location_ - The installation location directory of the product. + +*install_source* - keyword, text.text + +* _programs.install_source_ - The installation source of the product. + +*install_time* - keyword + +* _appcompat_shims.install_time_ - Install time of the SDB +* _chrome_extensions.install_time_ - Extension install time, in its original Webkit format +* _package_receipts.install_time_ - Timestamp of install time +* _rpm_packages.install_time_ - When the package was installed + +*install_timestamp* - keyword, number.long + +* _chrome_extensions.install_timestamp_ - Extension install time, converted to unix time + +*installed_by* - keyword, text.text + +* _patches.installed_by_ - The system context in which the patch as installed. + +*installed_on* - keyword, text.text + +* _patches.installed_on_ - The date when the patch was installed. + +*installer_name* - keyword, text.text + +* _package_receipts.installer_name_ - Name of installer process + +*instance_id* - keyword, text.text + +* _ec2_instance_metadata.instance_id_ - EC2 instance ID +* _ec2_instance_tags.instance_id_ - EC2 instance ID +* _osquery_info.instance_id_ - Unique, long-lived ID per instance of osquery +* _ycloud_instance_metadata.instance_id_ - Unique identifier for the VM + +*instance_identifier* - keyword, text.text + +* _hvci_status.instance_identifier_ - The instance ID of Device Guard. + +*instance_type* - keyword, text.text + +* _ec2_instance_metadata.instance_type_ - EC2 instance type + +*instances* - keyword, number.long + +* _pipes.instances_ - Number of instances of the named pipe + +*interface* - keyword, text.text + +* _arp_cache.interface_ - Interface of the network for the MAC +* _interface_addresses.interface_ - Interface name +* _interface_details.interface_ - Interface name +* _interface_ipv6.interface_ - Interface name +* _lldp_neighbors.interface_ - Interface name +* _routes.interface_ - Route local interface +* _wifi_status.interface_ - Name of the interface +* _wifi_survey.interface_ - Name of the interface + +*interleave_data_depth* - keyword, number.long + +* _memory_device_mapped_addresses.interleave_data_depth_ - The max number of consecutive rows from memory device that are accessed in a single interleave transfer; 0 indicates device is non-interleave + +*interleave_position* - keyword, number.long + +* _memory_device_mapped_addresses.interleave_position_ - The position of the device in a interleave, i.e. 0 indicates non-interleave, 1 indicates 1st interleave, 2 indicates 2nd interleave, etc. + +*internal* - keyword, number.long + +* _osquery_registry.internal_ - 1 If the plugin is internal else 0 + +*internet_settings* - keyword, text.text + +* _windows_security_center.internet_settings_ - The health of the Internet Settings + +*internet_sharing* - keyword, number.long + +* _sharing_preferences.internet_sharing_ - 1 If internet sharing is enabled else 0 + +*interval* - keyword, number.long + +* _docker_container_stats.interval_ - Difference between read and preread in nano-seconds +* _osquery_schedule.interval_ - The interval in seconds to run this query, not an exact interval + +*iowait* - keyword, number.long + +* _cpu_time.iowait_ - Time spent waiting for I/O to complete + +*ip* - keyword, text.text + +* _seccomp_events.ip_ - Instruction pointer value + +*ip_address* - keyword, text.text + +* _docker_container_networks.ip_address_ - IP address + +*ip_prefix_len* - keyword, number.long + +* _docker_container_networks.ip_prefix_len_ - IP subnet prefix length + +*ipackets* - keyword, number.long + +* _interface_details.ipackets_ - Input packets + +*ipc_namespace* - keyword, text.text + +* _docker_containers.ipc_namespace_ - IPC namespace +* _process_namespaces.ipc_namespace_ - ipc namespace inode + +*ipv4_address* - keyword, text.text + +* _lxd_networks.ipv4_address_ - IPv4 address + +*ipv4_forwarding* - keyword, number.long + +* _docker_info.ipv4_forwarding_ - 1 if IPv4 forwarding is enabled. 0 otherwise + +*ipv4_internet* - keyword, number.long + +* _connectivity.ipv4_internet_ - True if any interface is connected to the Internet via IPv4 + +*ipv4_local_network* - keyword, number.long + +* _connectivity.ipv4_local_network_ - True if any interface is connected to a routed network via IPv4 + +*ipv4_no_traffic* - keyword, number.long + +* _connectivity.ipv4_no_traffic_ - True if any interface is connected via IPv4, but has seen no traffic + +*ipv4_subnet* - keyword, number.long + +* _connectivity.ipv4_subnet_ - True if any interface is connected to the local subnet via IPv4 + +*ipv6_address* - keyword, text.text + +* _docker_container_networks.ipv6_address_ - IPv6 address +* _lxd_networks.ipv6_address_ - IPv6 address + +*ipv6_gateway* - keyword, text.text + +* _docker_container_networks.ipv6_gateway_ - IPv6 gateway + +*ipv6_internet* - keyword, number.long + +* _connectivity.ipv6_internet_ - True if any interface is connected to the Internet via IPv6 + +*ipv6_local_network* - keyword, number.long + +* _connectivity.ipv6_local_network_ - True if any interface is connected to a routed network via IPv6 + +*ipv6_no_traffic* - keyword, number.long + +* _connectivity.ipv6_no_traffic_ - True if any interface is connected via IPv6, but has seen no traffic + +*ipv6_prefix_len* - keyword, number.long + +* _docker_container_networks.ipv6_prefix_len_ - IPv6 subnet prefix length + +*ipv6_subnet* - keyword, number.long + +* _connectivity.ipv6_subnet_ - True if any interface is connected to the local subnet via IPv6 + +*irq* - keyword, number.long + +* _cpu_time.irq_ - Time spent servicing interrupts + +*is_active* - keyword, number.long + +* _running_apps.is_active_ - 1 if the application is in focus, 0 otherwise + +*is_hidden* - keyword, number.long + +* _groups.is_hidden_ - IsHidden attribute set in OpenDirectory +* _users.is_hidden_ - IsHidden attribute set in OpenDirectory + +*iso_8601* - keyword, text.text + +* _time.iso_8601_ - Current time (ISO format) in the system + +*issuer* - keyword, text.text + +* _certificates.issuer_ - Certificate issuer distinguished name + +*issuer_alternative_names* - keyword, text.text + +* _curl_certificate.issuer_alternative_names_ - Issuer Alternative Name + +*issuer_common_name* - keyword, text.text + +* _curl_certificate.issuer_common_name_ - Issuer common name + +*issuer_name* - keyword, text.text + +* _authenticode.issuer_name_ - The certificate issuer name + +*issuer_organization* - keyword, text.text + +* _curl_certificate.issuer_organization_ - Issuer organization + +*issuer_organization_unit* - keyword, text.text + +* _curl_certificate.issuer_organization_unit_ - Issuer organization unit + +*job_id* - keyword, number.long + +* _systemd_units.job_id_ - Next queued job id + +*job_path* - keyword, text.text + +* _systemd_units.job_path_ - The object path for the job + +*job_type* - keyword, text.text + +* _systemd_units.job_type_ - Job type + +*json_cmdline* - keyword, text.text + +* _bpf_process_events.json_cmdline_ - Command line arguments, in JSON format + +*keep_alive* - keyword, text.text + +* _launchd.keep_alive_ - Should the process be restarted if killed + +*kernel_memory* - keyword, number.long + +* _docker_info.kernel_memory_ - 1 if kernel memory limit support is enabled. 0 otherwise + +*kernel_version* - keyword, text.text + +* _docker_info.kernel_version_ - Kernel version +* _docker_version.kernel_version_ - Kernel version +* _kernel_panics.kernel_version_ - Version of the system kernel + +*key* - keyword, text.text + +* _authorized_keys.key_ - parsed authorized keys line +* _azure_instance_tags.key_ - The tag key +* _chrome_extensions.key_ - The extension key, from the manifest file +* _docker_container_labels.key_ - Label key +* _docker_image_labels.key_ - Label key +* _docker_network_labels.key_ - Label key +* _docker_volume_labels.key_ - Label key +* _ec2_instance_tags.key_ - Tag key +* _extended_attributes.key_ - Name of the value generated from the extended attribute +* _known_hosts.key_ - parsed authorized keys line +* _launchd_overrides.key_ - Name of the override key +* _lxd_instance_config.key_ - Configuration parameter name +* _lxd_instance_devices.key_ - Device info param name +* _mdls.key_ - Name of the metadata key +* _plist.key_ - Preference top-level key +* _power_sensors.key_ - The SMC key on OS X +* _preferences.key_ - Preference top-level key +* _process_envs.key_ - Environment variable name +* _registry.key_ - Name of the key to search for +* _selinux_settings.key_ - Key or class name. +* _smc_keys.key_ - 4-character key +* _temperature_sensors.key_ - The SMC key on OS X + +*key_algorithm* - keyword, text.text + +* _certificates.key_algorithm_ - Key algorithm used + +*key_file* - keyword, text.text + +* _authorized_keys.key_file_ - Path to the authorized_keys file +* _known_hosts.key_file_ - Path to known_hosts file + +*key_strength* - keyword, text.text + +* _certificates.key_strength_ - Key size used for RSA/DSA, or curve name + +*key_type* - keyword, text.text + +* _user_ssh_keys.key_type_ - The type of the private key. One of [rsa, dsa, dh, ec, hmac, cmac], or the empty string. + +*key_usage* - keyword, text.text + +* _certificates.key_usage_ - Certificate key usage and extended key usage +* _curl_certificate.key_usage_ - Usage of key in certificate + +*keychain_path* - keyword, text.text + +* _keychain_acls.keychain_path_ - The path of the keychain + +*keyword* - keyword, text.text + +* _portage_keywords.keyword_ - The keyword applied to the package + +*keywords* - keyword, text.text + +* _windows_eventlog.keywords_ - A bitmask of the keywords defined in the event +* _windows_events.keywords_ - A bitmask of the keywords defined in the event + +*kva_shadow_enabled* - keyword, number.long + +* _kva_speculative_info.kva_shadow_enabled_ - Kernel Virtual Address shadowing is enabled. + +*kva_shadow_inv_pcid* - keyword, number.long + +* _kva_speculative_info.kva_shadow_inv_pcid_ - Kernel VA INVPCID is enabled. + +*kva_shadow_pcid* - keyword, number.long + +* _kva_speculative_info.kva_shadow_pcid_ - Kernel VA PCID flushing optimization is enabled. + +*kva_shadow_user_global* - keyword, number.long + +* _kva_speculative_info.kva_shadow_user_global_ - User pages are marked as global. + +*label* - keyword, text.text + +* _apparmor_events.label_ - AppArmor label +* _augeas.label_ - The label of the configuration item +* _authorization_mechanisms.label_ - Label of the authorization right +* _authorizations.label_ - Item name, usually in reverse domain format +* _block_devices.label_ - Block device label string +* _device_partitions.label_ - +* _keychain_acls.label_ - An optional label tag that may be included with the keychain entry +* _keychain_items.label_ - Generic item name +* _launchd.label_ - Daemon or agent service name +* _launchd_overrides.label_ - Daemon or agent service name +* _quicklook_cache.label_ - Parsed version 'gen' field +* _sandboxes.label_ - UTI-format bundle or label ID + +*language* - keyword, text.text + +* _programs.language_ - The language of the product. + +*last_change* - keyword, number.long + +* _interface_details.last_change_ - Time of last device modification (optional) +* _shadow.last_change_ - Date of last password change (starting from UNIX epoch date) + +*last_connected* - keyword, number.long + +* _wifi_networks.last_connected_ - Last time this netword was connected to as a unix_time + +*last_executed* - keyword, number.long + +* _osquery_schedule.last_executed_ - UNIX time stamp in seconds of the last completed execution + +*last_execution_time* - keyword, number.long + +* _background_activities_moderator.last_execution_time_ - Most recent time application was executed. +* _userassist.last_execution_time_ - Most recent time application was executed. + +*last_hit_date* - keyword, number.long + +* _quicklook_cache.last_hit_date_ - Apple date format for last thumbnail cache hit + +*last_loaded* - keyword, text.text + +* _kernel_panics.last_loaded_ - Last loaded module before panic + +*last_opened_time* - keyword + +* _apps.last_opened_time_ - The time that the app was last used +* _office_mru.last_opened_time_ - Most recent opened time file was opened + +*last_run_code* - keyword, text.text + +* _scheduled_tasks.last_run_code_ - Exit status code of the last task run + +*last_run_message* - keyword, text.text + +* _scheduled_tasks.last_run_message_ - Exit status message of the last task run + +*last_run_time* - keyword, number.long + +* _prefetch.last_run_time_ - Most recent time application was run. +* _scheduled_tasks.last_run_time_ - Timestamp the task last ran + +*last_unloaded* - keyword, text.text + +* _kernel_panics.last_unloaded_ - Last unloaded module before panic + +*last_used_at* - keyword, text.text + +* _lxd_images.last_used_at_ - ISO time for the most recent use of this image in terms of container spawn + +*launch_type* - keyword, text.text + +* _xprotect_entries.launch_type_ - Launch services content type + +*layer_id* - keyword, text.text + +* _docker_image_layers.layer_id_ - Layer ID + +*layer_order* - keyword, number.long + +* _docker_image_layers.layer_order_ - Layer Order (1 = base layer) + +*level* - keyword, number.long + +* _asl.level_ - Log level number. See levels in asl.h. +* _windows_eventlog.level_ - Severity level associated with the event +* _windows_events.level_ - The severity level associated with the event + +*license* - keyword, text.text + +* _atom_packages.license_ - License for package +* _chocolatey_packages.license_ - License under which package is launched +* _npm_packages.license_ - License for package +* _python_packages.license_ - License under which package is launched + +*link* - keyword, text.text + +* _elf_sections.link_ - Link to other section + +*link_speed* - keyword, number.long + +* _interface_details.link_speed_ - Interface speed in Mb/s + +*linked_against* - keyword, text.text + +* _kernel_extensions.linked_against_ - Indexes of extensions this extension is linked against + +*load_state* - keyword, text.text + +* _systemd_units.load_state_ - Reflects whether the unit definition was properly loaded + +*local_address* - keyword, text.text + +* _bpf_socket_events.local_address_ - Local address associated with socket +* _process_open_sockets.local_address_ - Socket local address +* _socket_events.local_address_ - Local address associated with socket + +*local_hostname* - keyword, text.text + +* _ec2_instance_metadata.local_hostname_ - Private IPv4 DNS hostname of the first interface of this instance +* _system_info.local_hostname_ - Local hostname (optional) + +*local_ipv4* - keyword, text.text + +* _ec2_instance_metadata.local_ipv4_ - Private IPv4 address of the first interface of this instance + +*local_path* - keyword, text.text + +* _shortcut_files.local_path_ - Local system path to target file. + +*local_port* - keyword, number.long + +* _bpf_socket_events.local_port_ - Local network protocol port number +* _process_open_sockets.local_port_ - Socket local port +* _socket_events.local_port_ - Local network protocol port number + +*local_time* - keyword, number.long + +* _time.local_time_ - Current local UNIX time in the system + +*local_timezone* - keyword, text.text + +* _time.local_timezone_ - Current local timezone in the system + +*location* - keyword, text.text + +* _azure_instance_metadata.location_ - Azure Region the VM is running in +* _firefox_addons.location_ - Global, profile location +* _memory_arrays.location_ - Physical location of the memory array +* _package_receipts.location_ - Optional relative install path on volume + +*lock* - keyword, text.text + +* _chassis_info.lock_ - If TRUE, the frame is equipped with a lock. + +*lock_status* - keyword, number.long + +* _bitlocker_info.lock_status_ - The accessibility status of the drive from Windows. + +*locked* - keyword, number.long + +* _shared_memory.locked_ - 1 if segment is locked else 0 + +*log_file_disk_quota_mb* - keyword, number.long + +* _carbon_black_info.log_file_disk_quota_mb_ - Event file disk quota in MB + +*log_file_disk_quota_percentage* - keyword, number.long + +* _carbon_black_info.log_file_disk_quota_percentage_ - Event file disk quota in a percentage + +*logging_driver* - keyword, text.text + +* _docker_info.logging_driver_ - Logging driver + +*logging_enabled* - keyword, number.long + +* _alf.logging_enabled_ - 1 If logging mode is enabled else 0 + +*logging_option* - keyword, number.long + +* _alf.logging_option_ - Firewall logging option + +*logical_processors* - keyword, number.long + +* _cpu_info.logical_processors_ - The number of logical processors of the CPU. + +*logon_domain* - keyword, text.text + +* _logon_sessions.logon_domain_ - The name of the domain used to authenticate the owner of the logon session. + +*logon_id* - keyword, number.long + +* _logon_sessions.logon_id_ - A locally unique identifier (LUID) that identifies a logon session. + +*logon_script* - keyword, text.text + +* _logon_sessions.logon_script_ - The script used for logging on. + +*logon_server* - keyword, text.text + +* _logon_sessions.logon_server_ - The name of the server used to authenticate the owner of the logon session. + +*logon_sid* - keyword, text.text + +* _logon_sessions.logon_sid_ - The user's security identifier (SID). + +*logon_time* - keyword, number.long + +* _logon_sessions.logon_time_ - The time the session owner logged on. + +*logon_type* - keyword, text.text + +* _logon_sessions.logon_type_ - The logon method. + +*lu_wwn_device_id* - keyword, text.text + +* _smart_drive_info.lu_wwn_device_id_ - Device Identifier + +*mac* - keyword, text.text + +* _arp_cache.mac_ - MAC address of broadcasted address +* _ec2_instance_metadata.mac_ - MAC address for the first network interface of this EC2 instance +* _interface_details.mac_ - MAC of interface (optional) + +*mac_address* - keyword, text.text + +* _docker_container_networks.mac_address_ - MAC address + +*machine* - keyword, number.long + +* _elf_info.machine_ - Machine type + +*machine_name* - keyword, text.text + +* _windows_crashes.machine_name_ - Name of the machine where the crash happened + +*magic_db_files* - keyword, text.text + +* _magic.magic_db_files_ - Colon(:) separated list of files where the magic db file can be found. By default one of the following is used: /usr/share/file/magic/magic, /usr/share/misc/magic or /usr/share/misc/magic.mgc + +*maintainer* - keyword, text.text + +* _apt_sources.maintainer_ - Repository maintainer +* _deb_packages.maintainer_ - Package maintainer + +*major* - keyword, number.long + +* _os_version.major_ - Major release version + +*major_version* - keyword, number.long + +* _windows_crashes.major_version_ - Windows major version of the machine + +*managed* - keyword, number.long + +* _lxd_networks.managed_ - 1 if network created by LXD, 0 otherwise + +*manifest_hash* - keyword, text.text + +* _chrome_extensions.manifest_hash_ - The SHA256 hash of the manifest.json file + +*manifest_json* - keyword, text.text + +* _chrome_extensions.manifest_json_ - The manifest file of the extension + +*manual* - keyword, number.long + +* _managed_policies.manual_ - 1 if policy was loaded manually, otherwise 0 + +*manufacture_date* - keyword, number.long + +* _battery.manufacture_date_ - The date the battery was manufactured UNIX Epoch + +*manufacturer* - keyword, text.text + +* _battery.manufacturer_ - The battery manufacturer's name +* _chassis_info.manufacturer_ - The manufacturer of the chassis. +* _cpu_info.manufacturer_ - The manufacturer of the CPU. +* _disk_info.manufacturer_ - The manufacturer of the disk. +* _drivers.manufacturer_ - Device manufacturer +* _interface_details.manufacturer_ - Name of the network adapter's manufacturer. +* _memory_devices.manufacturer_ - Manufacturer ID string +* _video_info.manufacturer_ - The manufacturer of the gpu. + +*manufacturer_id* - keyword, number.long + +* _tpm_info.manufacturer_id_ - TPM manufacturers ID + +*manufacturer_name* - keyword, text.text + +* _tpm_info.manufacturer_name_ - TPM manufacturers name + +*manufacturer_version* - keyword, text.text + +* _tpm_info.manufacturer_version_ - TPM version + +*mask* - keyword, text.text + +* _interface_addresses.mask_ - Interface netmask +* _portage_keywords.mask_ - If the package is masked + +*match* - keyword, text.text + +* _chrome_extension_content_scripts.match_ - The pattern that the script is matched against +* _iptables.match_ - Matching rule that applies. + +*matches* - keyword, text.text + +* _yara.matches_ - List of YARA matches +* _yara_events.matches_ - List of YARA matches + +*max* - keyword, number.long + +* _fan_speed_sensors.max_ - Maximum speed +* _shadow.max_ - Maximum number of days between password changes + +*max_capacity* - keyword, number.long + +* _battery.max_capacity_ - The battery's actual capacity when it is fully charged in mAh +* _memory_arrays.max_capacity_ - Maximum capacity of array in gigabytes + +*max_clock_speed* - keyword, number.long + +* _cpu_info.max_clock_speed_ - The maximum possible frequency of the CPU. + +*max_instances* - keyword, number.long + +* _pipes.max_instances_ - The maximum number of instances creatable for this pipe + +*max_speed* - keyword, number.long + +* _memory_devices.max_speed_ - Max speed of memory device in megatransfers per second (MT/s) + +*max_voltage* - keyword, number.long + +* _memory_devices.max_voltage_ - Maximum operating voltage of device in millivolts + +*maximum_allowed* - keyword, number.long + +* _shared_resources.maximum_allowed_ - Limit on the maximum number of users allowed to use this resource concurrently. The value is only valid if the AllowMaximum property is set to FALSE. + +*md5* - keyword, text.text + +* _acpi_tables.md5_ - MD5 hash of table content +* _device_hash.md5_ - MD5 hash of provided inode data +* _file_events.md5_ - The MD5 of the file after change +* _hash.md5_ - MD5 hash of provided filesystem data +* _smbios_tables.md5_ - MD5 hash of table entry + +*md_device_name* - keyword, text.text + +* _md_drives.md_device_name_ - md device name + +*mdm_managed* - keyword, number.long + +* _system_extensions.mdm_managed_ - 1 if managed by MDM system extension payload configuration, 0 otherwise + +*mechanism* - keyword, text.text + +* _authorization_mechanisms.mechanism_ - Name of the mechanism that will be called + +*med_capability_capabilities* - keyword, number.long + +* _lldp_neighbors.med_capability_capabilities_ - Is MED capabilities enabled + +*med_capability_inventory* - keyword, number.long + +* _lldp_neighbors.med_capability_inventory_ - Is MED inventory capability enabled + +*med_capability_location* - keyword, number.long + +* _lldp_neighbors.med_capability_location_ - Is MED location capability enabled + +*med_capability_mdi_pd* - keyword, number.long + +* _lldp_neighbors.med_capability_mdi_pd_ - Is MED MDI PD capability enabled + +*med_capability_mdi_pse* - keyword, number.long + +* _lldp_neighbors.med_capability_mdi_pse_ - Is MED MDI PSE capability enabled + +*med_capability_policy* - keyword, number.long + +* _lldp_neighbors.med_capability_policy_ - Is MED policy capability enabled + +*med_device_type* - keyword, text.text + +* _lldp_neighbors.med_device_type_ - Chassis MED type + +*med_policies* - keyword, text.text + +* _lldp_neighbors.med_policies_ - Comma delimited list of MED policies + +*media_name* - keyword, text.text + +* _disk_events.media_name_ - Disk event media name string + +*mem* - keyword, number.double + +* _docker_container_processes.mem_ - Memory utilization as percentage + +*member_config_description* - keyword, text.text + +* _lxd_cluster.member_config_description_ - Config description + +*member_config_entity* - keyword, text.text + +* _lxd_cluster.member_config_entity_ - Type of configuration parameter for this node + +*member_config_key* - keyword, text.text + +* _lxd_cluster.member_config_key_ - Config key + +*member_config_name* - keyword, text.text + +* _lxd_cluster.member_config_name_ - Name of configuration parameter + +*member_config_value* - keyword, text.text + +* _lxd_cluster.member_config_value_ - Config value + +*memory* - keyword, number.long + +* _docker_info.memory_ - Total memory + +*memory_array_error_address* - keyword, text.text + +* _memory_error_info.memory_array_error_address_ - 32 bit physical address of the error based on the addressing of the bus to which the memory array is connected + +*memory_array_handle* - keyword, text.text + +* _memory_array_mapped_addresses.memory_array_handle_ - Handle of the memory array associated with this structure + +*memory_array_mapped_address_handle* - keyword, text.text + +* _memory_device_mapped_addresses.memory_array_mapped_address_handle_ - Handle of the memory array mapped address to which this device range is mapped to + +*memory_device_handle* - keyword, text.text + +* _memory_device_mapped_addresses.memory_device_handle_ - Handle of the memory device structure associated with this structure + +*memory_error_correction* - keyword, text.text + +* _memory_arrays.memory_error_correction_ - Primary hardware error correction or detection method supported + +*memory_error_info_handle* - keyword, text.text + +* _memory_arrays.memory_error_info_handle_ - Handle, or instance number, associated with any error that was detected for the array + +*memory_free* - keyword, number.long + +* _memory_info.memory_free_ - The amount of physical RAM, in bytes, left unused by the system + +*memory_limit* - keyword, number.long + +* _docker_container_stats.memory_limit_ - Memory limit +* _docker_info.memory_limit_ - 1 if memory limit support is enabled. 0 otherwise + +*memory_max_usage* - keyword, number.long + +* _docker_container_stats.memory_max_usage_ - Memory maximum usage + +*memory_total* - keyword, number.long + +* _memory_info.memory_total_ - Total amount of physical RAM, in bytes + +*memory_type* - keyword, text.text + +* _memory_devices.memory_type_ - Type of memory used + +*memory_type_details* - keyword, text.text + +* _memory_devices.memory_type_details_ - Additional details for memory device + +*memory_usage* - keyword, number.long + +* _docker_container_stats.memory_usage_ - Memory usage + +*message* - keyword, text.text + +* _apparmor_events.message_ - Raw audit message +* _asl.message_ - Message text. +* _lxd_cluster_members.message_ - Message from the node (Online/Offline) +* _selinux_events.message_ - Message +* _syslog_events.message_ - The syslog message +* _user_events.message_ - Message from the event + +*metadata_endpoint* - keyword, text.text + +* _ycloud_instance_metadata.metadata_endpoint_ - Endpoint used to fetch VM metadata + +*method* - keyword, text.text + +* _curl.method_ - The HTTP method for the request + +*metric* - keyword, number.long + +* _interface_details.metric_ - Metric based on the speed of the interface +* _routes.metric_ - Cost of route. Lowest is preferred + +*metric_name* - keyword, text.text + +* _prometheus_metrics.metric_name_ - Name of collected Prometheus metric + +*metric_value* - keyword, number.double + +* _prometheus_metrics.metric_value_ - Value of collected Prometheus metric + +*mft_entry* - keyword, number.long + +* _shellbags.mft_entry_ - Directory master file table entry. +* _shortcut_files.mft_entry_ - Target mft entry. + +*mft_sequence* - keyword, number.long + +* _shellbags.mft_sequence_ - Directory master file table sequence. +* _shortcut_files.mft_sequence_ - Target mft sequence. + +*mime_encoding* - keyword, text.text + +* _magic.mime_encoding_ - MIME encoding data from libmagic + +*mime_type* - keyword, text.text + +* _magic.mime_type_ - MIME type data from libmagic + +*min* - keyword, number.long + +* _fan_speed_sensors.min_ - Minimum speed +* _shadow.min_ - Minimal number of days between password changes + +*min_api_version* - keyword, text.text + +* _docker_version.min_api_version_ - Minimum API version supported + +*min_version* - keyword, text.text + +* _xprotect_meta.min_version_ - The minimum allowed plugin version. + +*min_voltage* - keyword, number.long + +* _memory_devices.min_voltage_ - Minimum operating voltage of device in millivolts + +*minimum_system_version* - keyword, text.text + +* _apps.minimum_system_version_ - Minimum version of OS X required for the app to run + +*minor* - keyword, number.long + +* _os_version.minor_ - Minor release version + +*minor_version* - keyword, number.long + +* _windows_crashes.minor_version_ - Windows minor version of the machine + +*minute* - keyword, text.text + +* _crontab.minute_ - The exact minute for the job + +*minutes* - keyword, number.long + +* _time.minutes_ - Current minutes in the system +* _uptime.minutes_ - Minutes of uptime + +*minutes_to_full_charge* - keyword, number.long + +* _battery.minutes_to_full_charge_ - The number of minutes until the battery is fully charged. This value is -1 if this time is still being calculated + +*minutes_until_empty* - keyword, number.long + +* _battery.minutes_until_empty_ - The number of minutes until the battery is fully depleted. This value is -1 if this time is still being calculated + +*mnt_namespace* - keyword, text.text + +* _docker_containers.mnt_namespace_ - Mount namespace +* _process_namespaces.mnt_namespace_ - mnt namespace inode + +*mode* - keyword, text.text + +* _apparmor_profiles.mode_ - How the policy is applied. +* _device_file.mode_ - Permission bits +* _docker_container_mounts.mode_ - Mount options (rw, ro) +* _file.mode_ - Permission bits +* _file_events.mode_ - Permission bits +* _package_bom.mode_ - Expected permissions +* _process_events.mode_ - File mode permissions +* _process_open_pipes.mode_ - Pipe open mode (r/w) +* _rpm_package_files.mode_ - File permissions mode from info DB +* _wifi_status.mode_ - The current operating mode for the Wi-Fi interface + +*model* - keyword, text.text + +* _battery.model_ - The battery's model number +* _block_devices.model_ - Block device model string identifier +* _chassis_info.model_ - The model of the chassis. +* _cpu_info.model_ - The model of the CPU. +* _hardware_events.model_ - Hardware device model +* _pci_devices.model_ - PCI Device model +* _usb_devices.model_ - USB Device model string +* _video_info.model_ - The model of the gpu. + +*model_family* - keyword, text.text + +* _smart_drive_info.model_family_ - Drive model family + +*model_id* - keyword, text.text + +* _hardware_events.model_id_ - Hex encoded Hardware model identifier +* _pci_devices.model_id_ - Hex encoded PCI Device model identifier +* _usb_devices.model_id_ - Hex encoded USB Device model identifier + +*modified* - keyword, text.text + +* _authorizations.modified_ - Label top-level key +* _keychain_items.modified_ - Date of last modification + +*modified_time* - keyword, number.long + +* _package_bom.modified_time_ - Timestamp the file was installed +* _shellbags.modified_time_ - Directory Modified time. +* _shimcache.modified_time_ - File Modified time. + +*module* - keyword, text.text + +* _windows_crashes.module_ - Path of the crashed module within the process + +*module_backtrace* - keyword, text.text + +* _kernel_panics.module_backtrace_ - Modules appearing in the crashed module's backtrace + +*module_path* - keyword, text.text + +* _services.module_path_ - Path to ServiceDll + +*month* - keyword, text.text + +* _crontab.month_ - The month of the year for the job +* _time.month_ - Current month in the system + +*mount_namespace_id* - keyword, text.text + +* _deb_packages.mount_namespace_id_ - Mount namespace id +* _file.mount_namespace_id_ - Mount namespace id +* _hash.mount_namespace_id_ - Mount namespace id +* _npm_packages.mount_namespace_id_ - Mount namespace id +* _os_version.mount_namespace_id_ - Mount namespace id +* _rpm_packages.mount_namespace_id_ - Mount namespace id + +*mount_point* - keyword, text.text + +* _docker_volumes.mount_point_ - Mount point + +*mountable* - keyword, number.long + +* _disk_events.mountable_ - 1 if mountable, 0 if not + +*msize* - keyword, number.long + +* _elf_segments.msize_ - Segment offset in memory + +*mtime* - keyword + +* _device_file.mtime_ - Last modification time +* _file.mtime_ - Last modification time +* _file_events.mtime_ - Last modification time +* _gatekeeper_approved_apps.mtime_ - Last modification time +* _process_events.mtime_ - File modification in UNIX time +* _quicklook_cache.mtime_ - Parsed version date field +* _registry.mtime_ - timestamp of the most recent registry write + +*mtu* - keyword, number.long + +* _interface_details.mtu_ - Network MTU +* _lxd_networks.mtu_ - MTU size +* _routes.mtu_ - Maximum Transmission Unit for the route + +*name* - keyword, text.text + +* _acpi_tables.name_ - ACPI table name +* _ad_config.name_ - The OS X-specific configuration name +* _apparmor_events.name_ - Process name +* _apparmor_profiles.name_ - Policy name. +* _apps.name_ - Name of the Name.app folder +* _apt_sources.name_ - Repository name +* _atom_packages.name_ - Package display name +* _autoexec.name_ - Name of the program +* _azure_instance_metadata.name_ - Name of the VM +* _block_devices.name_ - Block device name +* _browser_plugins.name_ - Plugin display name +* _chocolatey_packages.name_ - Package display name +* _chrome_extensions.name_ - Extension display name +* _cups_destinations.name_ - Name of the printer +* _deb_packages.name_ - Package name +* _disk_encryption.name_ - Disk name +* _disk_events.name_ - Disk event name +* _disk_info.name_ - The label of the disk object. +* _dns_cache.name_ - DNS record name +* _docker_container_mounts.name_ - Optional mount name +* _docker_container_networks.name_ - Network name +* _docker_container_processes.name_ - The process path or shorthand argv[0] +* _docker_container_stats.name_ - Container name +* _docker_containers.name_ - Container name +* _docker_info.name_ - Name of the docker host +* _docker_networks.name_ - Network name +* _docker_volume_labels.name_ - Volume name +* _docker_volumes.name_ - Volume name +* _elf_sections.name_ - Section name +* _elf_segments.name_ - Segment type/name +* _elf_symbols.name_ - Symbol name +* _etc_protocols.name_ - Protocol name +* _etc_services.name_ - Service name +* _example.name_ - Description for name column +* _fan_speed_sensors.name_ - Fan name +* _fbsd_kmods.name_ - Module name +* _firefox_addons.name_ - Addon display name +* _homebrew_packages.name_ - Package name +* _ie_extensions.name_ - Extension display name +* _iokit_devicetree.name_ - Device node name +* _iokit_registry.name_ - Default name of the node +* _kernel_extensions.name_ - Extension label +* _kernel_modules.name_ - Module name +* _kernel_panics.name_ - Process name corresponding to crashed thread +* _launchd.name_ - File name of plist (used by launchd) +* _lxd_certificates.name_ - Name of the certificate +* _lxd_instance_config.name_ - Instance name +* _lxd_instance_devices.name_ - Instance name +* _lxd_instances.name_ - Instance name +* _lxd_networks.name_ - Name of the network +* _lxd_storage_pools.name_ - Name of the storage pool +* _managed_policies.name_ - Policy key name +* _md_personalities.name_ - Name of personality supported by kernel +* _memory_map.name_ - Region name +* _npm_packages.name_ - Package display name +* _ntdomains.name_ - The label by which the object is known. +* _nvram.name_ - Variable name +* _os_version.name_ - Distribution or product name +* _osquery_events.name_ - Event publisher or subscriber name +* _osquery_extensions.name_ - Extension's name +* _osquery_flags.name_ - Flag name +* _osquery_packs.name_ - The given name for this query pack +* _osquery_registry.name_ - Name of the plugin item +* _osquery_schedule.name_ - The given name for this query +* _package_install_history.name_ - Package display name +* _physical_disk_performance.name_ - Name of the physical disk +* _pipes.name_ - Name of the pipe +* _pkg_packages.name_ - Package name +* _power_sensors.name_ - Name of power source +* _processes.name_ - The process path or shorthand argv[0] +* _programs.name_ - Commonly used product name. +* _python_packages.name_ - Package display name +* _registry.name_ - Name of the registry value entry +* _rpm_packages.name_ - RPM package name +* _safari_extensions.name_ - Extension display name +* _scheduled_tasks.name_ - Name of the scheduled task +* _services.name_ - Service name +* _shared_folders.name_ - The shared name of the folder as it appears to other users +* _shared_resources.name_ - Alias given to a path set up as a share on a computer system running Windows. +* _startup_items.name_ - Name of startup item +* _system_controls.name_ - Full sysctl MIB name +* _temperature_sensors.name_ - Name of temperature source +* _windows_optional_features.name_ - Name of the feature +* _windows_security_products.name_ - Name of product +* _wmi_bios_info.name_ - Name of the Bios setting +* _wmi_cli_event_consumers.name_ - Unique name of a consumer. +* _wmi_event_filters.name_ - Unique identifier of an event filter. +* _wmi_script_event_consumers.name_ - Unique identifier for the event consumer. +* _xprotect_entries.name_ - Description of XProtected malware +* _xprotect_reports.name_ - Description of XProtected malware +* _ycloud_instance_metadata.name_ - Name of the VM +* _yum_sources.name_ - Repository name + +*name_constraints* - keyword, text.text + +* _curl_certificate.name_constraints_ - Name Constraints + +*namespace* - keyword, text.text + +* _apparmor_events.namespace_ - AppArmor namespace + +*native* - keyword, number.long + +* _browser_plugins.native_ - Plugin requires native execution +* _firefox_addons.native_ - 1 If the addon includes binary components else 0 + +*net_namespace* - keyword, text.text + +* _docker_containers.net_namespace_ - Network namespace +* _listening_ports.net_namespace_ - The inode number of the network namespace +* _process_namespaces.net_namespace_ - net namespace inode +* _process_open_sockets.net_namespace_ - The inode number of the network namespace + +*netmask* - keyword, text.text + +* _dns_resolvers.netmask_ - Address (sortlist) netmask length +* _routes.netmask_ - Netmask length + +*network_id* - keyword, text.text + +* _docker_container_networks.network_id_ - Network ID + +*network_name* - keyword, text.text + +* _wifi_networks.network_name_ - Name of the network +* _wifi_status.network_name_ - Name of the network +* _wifi_survey.network_name_ - Name of the network + +*network_rx_bytes* - keyword, number.long + +* _docker_container_stats.network_rx_bytes_ - Total network bytes read + +*network_tx_bytes* - keyword, number.long + +* _docker_container_stats.network_tx_bytes_ - Total network bytes transmitted + +*next_run_time* - keyword, number.long + +* _scheduled_tasks.next_run_time_ - Timestamp the task is scheduled to run next + +*nice* - keyword, number.long + +* _cpu_time.nice_ - Time spent in user mode with low priority (nice) +* _docker_container_processes.nice_ - Process nice level (-20 to 20, default 0) +* _processes.nice_ - Process nice level (-20 to 20, default 0) + +*no_proxy* - keyword, text.text + +* _docker_info.no_proxy_ - Comma-separated list of domain extensions proxy should not be used for + +*node* - keyword, text.text + +* _augeas.node_ - The node path of the configuration item + +*node_ref_number* - keyword, text.text + +* _ntfs_journal_events.node_ref_number_ - The ordinal that associates a journal record with a filename + +*noise* - keyword, number.long + +* _wifi_status.noise_ - The current noise measurement (dBm) +* _wifi_survey.noise_ - The current noise measurement (dBm) + +*not_valid_after* - keyword, text.text + +* _certificates.not_valid_after_ - Certificate expiration data + +*not_valid_before* - keyword, text.text + +* _certificates.not_valid_before_ - Lower bound of valid date + +*nr_raid_disks* - keyword, number.long + +* _md_devices.nr_raid_disks_ - Number of partitions or disk devices to comprise the array + +*ntime* - keyword, text.text + +* _bpf_process_events.ntime_ - The nsecs uptime timestamp as obtained from BPF +* _bpf_socket_events.ntime_ - The nsecs uptime timestamp as obtained from BPF + +*num_procs* - keyword, number.long + +* _docker_container_stats.num_procs_ - Number of processors + +*number* - keyword, number.long + +* _etc_protocols.number_ - Protocol number +* _oem_strings.number_ - The string index of the structure +* _smbios_tables.number_ - Table entry number + +*number_memory_devices* - keyword, number.long + +* _memory_arrays.number_memory_devices_ - Number of memory devices on array + +*number_of_cores* - keyword, text.text + +* _cpu_info.number_of_cores_ - The number of cores of the CPU. + +*object_name* - keyword, text.text + +* _winbaseobj.object_name_ - Object Name + +*object_path* - keyword, text.text + +* _systemd_units.object_path_ - The object path for this unit + +*object_type* - keyword, text.text + +* _winbaseobj.object_type_ - Object Type + +*obytes* - keyword, number.long + +* _interface_details.obytes_ - Output bytes + +*odrops* - keyword, number.long + +* _interface_details.odrops_ - Output drops + +*oerrors* - keyword, number.long + +* _interface_details.oerrors_ - Output errors + +*offer* - keyword, text.text + +* _azure_instance_metadata.offer_ - Offer information for the VM image (Azure image gallery VMs only) + +*offset* - keyword, number.long + +* _device_partitions.offset_ - +* _elf_sections.offset_ - Offset of section in file +* _elf_segments.offset_ - Segment offset in file +* _elf_symbols.offset_ - Section table index +* _process_memory_map.offset_ - Offset into mapped path + +*oid* - keyword, text.text + +* _system_controls.oid_ - Control MIB + +*old_path* - keyword, text.text + +* _ntfs_journal_events.old_path_ - Old path (renames only) + +*on_demand* - keyword, text.text + +* _launchd.on_demand_ - Deprecated key, replaced by keep_alive + +*on_disk* - keyword, number.long + +* _processes.on_disk_ - The process path exists yes=1, no=0, unknown=-1 + +*online_cpus* - keyword, number.long + +* _docker_container_stats.online_cpus_ - Online CPUs + +*oom_kill_disable* - keyword, number.long + +* _docker_info.oom_kill_disable_ - 1 if Out-of-memory kill is disabled. 0 otherwise + +*opackets* - keyword, number.long + +* _interface_details.opackets_ - Output packets + +*opaque_version* - keyword, text.text + +* _gatekeeper.opaque_version_ - Version of Gatekeeper's gkopaque.bundle + +*operation* - keyword, text.text + +* _apparmor_events.operation_ - Permission requested by the process +* _process_file_events.operation_ - Operation type + +*option* - keyword, text.text + +* _ad_config.option_ - Canonical name of option +* _ssh_configs.option_ - The option and value + +*option_name* - keyword, text.text + +* _cups_destinations.option_name_ - Option name + +*option_value* - keyword, text.text + +* _cups_destinations.option_value_ - Option value + +*optional* - keyword, number.long + +* _xprotect_entries.optional_ - Match any of the identities/patterns for this XProtect name + +*optional_permissions* - keyword, text.text + +* _chrome_extensions.optional_permissions_ - The permissions optionally required by the extensions + +*optional_permissions_json* - keyword, text.text + +* _chrome_extensions.optional_permissions_json_ - The JSON-encoded permissions optionally required by the extensions + +*options* - keyword + +* _dns_resolvers.options_ - Resolver options +* _nfs_shares.options_ - Options string set on the export share + +*organization* - keyword, text.text + +* _curl_certificate.organization_ - Organization issued to + +*organization_unit* - keyword, text.text + +* _curl_certificate.organization_unit_ - Organization unit issued to + +*original_parent* - keyword, number.long + +* _es_process_events.original_parent_ - Original parent process ID in case of reparenting + +*original_program_name* - keyword, text.text + +* _authenticode.original_program_name_ - The original program name that the publisher has signed + +*os* - keyword, text.text + +* _docker_info.os_ - Operating system +* _docker_version.os_ - Operating system +* _lxd_images.os_ - OS on which image is based +* _lxd_instances.os_ - The OS of this instance + +*os_type* - keyword, text.text + +* _azure_instance_metadata.os_type_ - Linux or Windows +* _docker_info.os_type_ - Operating system type + +*os_version* - keyword, text.text + +* _kernel_panics.os_version_ - Version of the operating system + +*other* - keyword, text.text + +* _md_devices.other_ - Other information associated with array from /proc/mdstat + +*other_run_times* - keyword, text.text + +* _prefetch.other_run_times_ - Other execution times in prefetch file. + +*ouid* - keyword, number.long + +* _apparmor_events.ouid_ - Object owner's user ID + +*outiface* - keyword, text.text + +* _iptables.outiface_ - Output interface for the rule. + +*outiface_mask* - keyword, text.text + +* _iptables.outiface_mask_ - Output interface mask for the rule. + +*output_bit* - keyword, number.long + +* _cpuid.output_bit_ - Bit in register value for feature value + +*output_register* - keyword, text.text + +* _cpuid.output_register_ - Register used to for feature value + +*output_size* - keyword, number.long + +* _osquery_schedule.output_size_ - Total number of bytes generated by the query + +*overflows* - keyword, text.text + +* _process_events.overflows_ - List of structures that overflowed + +*owned* - keyword, number.long + +* _tpm_info.owned_ - TPM is ownned + +*owner_gid* - keyword, number.long + +* _process_events.owner_gid_ - File owner group ID + +*owner_uid* - keyword, number.long + +* _process_events.owner_uid_ - File owner user ID +* _shared_memory.owner_uid_ - User ID of owning process + +*owner_uuid* - keyword, number.long + +* _osquery_registry.owner_uuid_ - Extension route UUID (0 for core) + +*package* - keyword, text.text + +* _portage_keywords.package_ - Package name +* _portage_packages.package_ - Package name +* _portage_use.package_ - Package name +* _rpm_package_files.package_ - RPM package name + +*package_filename* - keyword, text.text + +* _package_receipts.package_filename_ - Filename of original .pkg file + +*package_group* - keyword, text.text + +* _rpm_packages.package_group_ - Package group + +*package_id* - keyword, text.text + +* _package_install_history.package_id_ - Label packageIdentifiers +* _package_receipts.package_id_ - Package domain identifier + +*packet_device_type* - keyword, text.text + +* _smart_drive_info.packet_device_type_ - Packet device type + +*packets* - keyword, number.long + +* _iptables.packets_ - Number of matching packets for this rule. + +*packets_received* - keyword, number.long + +* _lxd_networks.packets_received_ - Number of packets received on this network + +*packets_sent* - keyword, number.long + +* _lxd_networks.packets_sent_ - Number of packets sent on this network + +*page_ins* - keyword, number.long + +* _virtual_memory_info.page_ins_ - The total number of requests for pages from a pager. + +*page_outs* - keyword, number.long + +* _virtual_memory_info.page_outs_ - Total number of pages paged out. + +*parent* - keyword + +* _apparmor_events.parent_ - Parent process PID +* _block_devices.parent_ - Block device parent name +* _bpf_process_events.parent_ - Parent process ID +* _bpf_socket_events.parent_ - Parent process ID +* _crashes.parent_ - Parent PID of the crashed process +* _docker_container_processes.parent_ - Process parent's PID +* _es_process_events.parent_ - Parent process ID +* _iokit_devicetree.parent_ - Parent device registry ID +* _iokit_registry.parent_ - Parent registry ID +* _process_events.parent_ - Process parent's PID, or -1 if cannot be determined. +* _processes.parent_ - Process parent's PID + +*parent_ref_number* - keyword, text.text + +* _ntfs_journal_events.parent_ref_number_ - The ordinal that associates a journal record with a filename's parent directory + +*part_number* - keyword, text.text + +* _memory_devices.part_number_ - Manufacturer specific serial number of memory device + +*partial* - keyword + +* _ntfs_journal_events.partial_ - Set to 1 if either path or old_path only contains the file or folder name +* _process_file_events.partial_ - True if this is a partial event (i.e.: this process existed before we started osquery) + +*partition* - keyword, text.text + +* _device_file.partition_ - A partition number +* _device_hash.partition_ - A partition number +* _device_partitions.partition_ - A partition number or description + +*partition_row_position* - keyword, number.long + +* _memory_device_mapped_addresses.partition_row_position_ - Identifies the position of the referenced memory device in a row of the address partition + +*partition_width* - keyword, number.long + +* _memory_array_mapped_addresses.partition_width_ - Number of memory devices that form a single row of memory for the address partition of this structure + +*partitions* - keyword, number.long + +* _disk_info.partitions_ - Number of detected partitions on disk. + +*partner_fd* - keyword, number.long + +* _process_open_pipes.partner_fd_ - File descriptor of shared pipe at partner's end + +*partner_mode* - keyword, text.text + +* _process_open_pipes.partner_mode_ - Mode of shared pipe at partner's end + +*partner_pid* - keyword, number.long + +* _process_open_pipes.partner_pid_ - Process ID of partner process sharing a particular pipe + +*passpoint* - keyword, number.long + +* _wifi_networks.passpoint_ - 1 if Passpoint is supported, 0 otherwise + +*password_last_set_time* - keyword, number.double + +* _account_policy_data.password_last_set_time_ - The time the password was last changed + +*password_status* - keyword, text.text + +* _shadow.password_status_ - Password status + +*patch* - keyword, number.long + +* _os_version.patch_ - Optional patch release + +*path* - keyword, text.text + +* _alf_exceptions.path_ - Path to the executable that is excepted +* _apparmor_profiles.path_ - Unique, aa-status compatible, policy identifier. +* _appcompat_shims.path_ - This is the path to the SDB database. +* _apps.path_ - Absolute and full Name.app path +* _atom_packages.path_ - Package's package.json path +* _augeas.path_ - The path to the configuration file +* _authenticode.path_ - Must provide a path or directory +* _autoexec.path_ - Path to the executable +* _background_activities_moderator.path_ - Application file path. +* _bpf_process_events.path_ - Binary path +* _bpf_socket_events.path_ - Path of executed file +* _browser_plugins.path_ - Path to plugin bundle +* _carves.path_ - The path of the requested carve +* _certificates.path_ - Path to Keychain or PEM bundle +* _chocolatey_packages.path_ - Path at which this package resides +* _chrome_extension_content_scripts.path_ - Path to extension folder +* _chrome_extensions.path_ - Path to extension folder +* _crashes.path_ - Path to the crashed process +* _crontab.path_ - File parsed +* _device_file.path_ - A logical path within the device node +* _disk_events.path_ - Path of the DMG file accessed +* _docker_container_fs_changes.path_ - FIle or directory path relative to rootfs +* _docker_containers.path_ - Container path +* _elf_dynamic.path_ - Path to ELF file +* _elf_info.path_ - Path to ELF file +* _elf_sections.path_ - Path to ELF file +* _elf_segments.path_ - Path to ELF file +* _elf_symbols.path_ - Path to ELF file +* _es_process_events.path_ - Path of executed file +* _example.path_ - Path of example +* _extended_attributes.path_ - Absolute file path +* _file.path_ - Absolute file path +* _firefox_addons.path_ - Path to plugin bundle +* _gatekeeper_approved_apps.path_ - Path of executable allowed to run +* _hardware_events.path_ - Local device path assigned (optional) +* _hash.path_ - Must provide a path or directory +* _homebrew_packages.path_ - Package install path +* _ie_extensions.path_ - Path to executable +* _kernel_extensions.path_ - Optional path to extension bundle +* _kernel_info.path_ - Kernel path +* _kernel_panics.path_ - Location of log file +* _keychain_acls.path_ - The path of the authorized application +* _keychain_items.path_ - Path to keychain containing item +* _launchd.path_ - Path to daemon or agent plist +* _launchd_overrides.path_ - Path to daemon or agent plist +* _listening_ports.path_ - Path for UNIX domain sockets +* _magic.path_ - Absolute path to target file +* _mdfind.path_ - Path of the file returned from spotlight +* _mdls.path_ - Path of the file +* _mounts.path_ - Mounted device path +* _npm_packages.path_ - Module's package.json path +* _ntfs_acl_permissions.path_ - Path to the file or directory. +* _ntfs_journal_events.path_ - Path +* _office_mru.path_ - File path +* _osquery_extensions.path_ - Path of the extension's Thrift connection or library path +* _package_bom.path_ - Path of package bom +* _package_receipts.path_ - Path of receipt plist +* _plist.path_ - (required) read preferences from a plist +* _prefetch.path_ - Prefetch file path. +* _process_events.path_ - Path of executed file +* _process_file_events.path_ - The path associated with the event +* _process_memory_map.path_ - Path to mapped file or mapped type +* _process_open_files.path_ - Filesystem path of descriptor +* _process_open_sockets.path_ - For UNIX sockets (family=AF_UNIX), the domain path +* _processes.path_ - Path to executed binary +* _python_packages.path_ - Path at which this module resides +* _quicklook_cache.path_ - Path of file +* _registry.path_ - Full path to the value +* _rpm_package_files.path_ - File path within the package +* _safari_extensions.path_ - Path to extension XAR bundle +* _sandboxes.path_ - Path to sandbox container directory +* _scheduled_tasks.path_ - Path to the executable to be run +* _services.path_ - Path to Service Executable +* _shared_folders.path_ - Absolute path of shared folder on the local system +* _shared_resources.path_ - Local path of the Windows share. +* _shellbags.path_ - Directory name. +* _shimcache.path_ - This is the path to the executed file. +* _shortcut_files.path_ - Directory name. +* _signature.path_ - Must provide a path or directory +* _socket_events.path_ - Path of executed file +* _startup_items.path_ - Path of startup item +* _suid_bin.path_ - Binary path +* _system_extensions.path_ - Original path of system extension +* _user_events.path_ - Supplied path from event +* _user_ssh_keys.path_ - Path to key file +* _userassist.path_ - Application file path. +* _windows_crashes.path_ - Path of the executable file for the crashed process +* _yara.path_ - The path scanned + +*pci_class* - keyword, text.text + +* _pci_devices.pci_class_ - PCI Device class + +*pci_class_id* - keyword, text.text + +* _pci_devices.pci_class_id_ - PCI Device class ID in hex format + +*pci_slot* - keyword, text.text + +* _interface_details.pci_slot_ - PCI slot number +* _pci_devices.pci_slot_ - PCI Device used slot + +*pci_subclass* - keyword, text.text + +* _pci_devices.pci_subclass_ - PCI Device subclass + +*pci_subclass_id* - keyword, text.text + +* _pci_devices.pci_subclass_id_ - PCI Device subclass in hex format + +*pem* - keyword, text.text + +* _curl_certificate.pem_ - Certificate PEM format + +*percent_disk_read_time* - keyword, number.long + +* _physical_disk_performance.percent_disk_read_time_ - Percentage of elapsed time that the selected disk drive is busy servicing read requests + +*percent_disk_time* - keyword, number.long + +* _physical_disk_performance.percent_disk_time_ - Percentage of elapsed time that the selected disk drive is busy servicing read or write requests + +*percent_disk_write_time* - keyword, number.long + +* _physical_disk_performance.percent_disk_write_time_ - Percentage of elapsed time that the selected disk drive is busy servicing write requests + +*percent_idle_time* - keyword, number.long + +* _physical_disk_performance.percent_idle_time_ - Percentage of time during the sample interval that the disk was idle + +*percent_processor_time* - keyword, number.long + +* _processes.percent_processor_time_ - Returns elapsed time that all of the threads of this process used the processor to execute instructions in 100 nanoseconds ticks. + +*percent_remaining* - keyword, number.long + +* _battery.percent_remaining_ - The percentage of battery remaining before it is drained + +*percentage_encrypted* - keyword, number.long + +* _bitlocker_info.percentage_encrypted_ - The percentage of the drive that is encrypted. + +*perf_ctl* - keyword, number.long + +* _msr.perf_ctl_ - Performance setting for the processor. + +*perf_status* - keyword, number.long + +* _msr.perf_status_ - Performance status for the processor. + +*period* - keyword, text.text + +* _load_average.period_ - Period over which the average is calculated. + +*permanent* - keyword, text.text + +* _arp_cache.permanent_ - 1 for true, 0 for false + +*permissions* - keyword, text.text + +* _chrome_extensions.permissions_ - The permissions required by the extension +* _process_memory_map.permissions_ - r=read, w=write, x=execute, p=private (cow) +* _shared_memory.permissions_ - Memory segment permissions +* _suid_bin.permissions_ - Binary permissions + +*permissions_json* - keyword, text.text + +* _chrome_extensions.permissions_json_ - The JSON-encoded permissions required by the extension + +*persistent* - keyword, number.long + +* _chrome_extensions.persistent_ - 1 If extension is persistent across all tabs else 0 + +*persistent_volume_id* - keyword, text.text + +* _bitlocker_info.persistent_volume_id_ - Persistent ID of the drive. + +*pgroup* - keyword, number.long + +* _docker_container_processes.pgroup_ - Process group +* _processes.pgroup_ - Process group + +*physical_adapter* - keyword, number.long + +* _interface_details.physical_adapter_ - Indicates whether the adapter is a physical or a logical adapter. + +*physical_memory* - keyword, number.long + +* _system_info.physical_memory_ - Total physical memory in bytes + +*physical_presence_version* - keyword, text.text + +* _tpm_info.physical_presence_version_ - Version of the Physical Presence Interface + +*pid* - keyword, number.long + +* _apparmor_events.pid_ - Process ID +* _asl.pid_ - Sending process ID encoded as a string. Set automatically. +* _bpf_process_events.pid_ - Process ID +* _bpf_socket_events.pid_ - Process ID +* _crashes.pid_ - Process (or thread) ID of the crashed process +* _docker_container_processes.pid_ - Process ID +* _docker_containers.pid_ - Identifier of the initial process +* _es_process_events.pid_ - Process (or thread) ID +* _last.pid_ - Process (or thread) ID +* _listening_ports.pid_ - Process (or thread) ID +* _logged_in_users.pid_ - Process (or thread) ID +* _lxd_instances.pid_ - Instance's process ID +* _osquery_info.pid_ - Process (or thread/handle) ID +* _pipes.pid_ - Process ID of the process to which the pipe belongs +* _process_envs.pid_ - Process (or thread) ID +* _process_events.pid_ - Process (or thread) ID +* _process_file_events.pid_ - Process ID +* _process_memory_map.pid_ - Process (or thread) ID +* _process_namespaces.pid_ - Process (or thread) ID +* _process_open_files.pid_ - Process (or thread) ID +* _process_open_pipes.pid_ - Process ID +* _process_open_sockets.pid_ - Process (or thread) ID +* _processes.pid_ - Process (or thread) ID +* _running_apps.pid_ - The pid of the application +* _seccomp_events.pid_ - Process ID +* _services.pid_ - the Process ID of the service +* _shared_memory.pid_ - Process ID to last use the segment +* _socket_events.pid_ - Process (or thread) ID +* _user_events.pid_ - Process (or thread) ID +* _windows_crashes.pid_ - Process ID of the crashed process +* _windows_eventlog.pid_ - Process ID which emitted the event record + +*pid_namespace* - keyword, text.text + +* _docker_containers.pid_namespace_ - PID namespace +* _process_namespaces.pid_namespace_ - pid namespace inode + +*pid_with_namespace* - keyword, number.long + +* _apt_sources.pid_with_namespace_ - Pids that contain a namespace +* _authorized_keys.pid_with_namespace_ - Pids that contain a namespace +* _crontab.pid_with_namespace_ - Pids that contain a namespace +* _deb_packages.pid_with_namespace_ - Pids that contain a namespace +* _dns_resolvers.pid_with_namespace_ - Pids that contain a namespace +* _etc_hosts.pid_with_namespace_ - Pids that contain a namespace +* _file.pid_with_namespace_ - Pids that contain a namespace +* _groups.pid_with_namespace_ - Pids that contain a namespace +* _hash.pid_with_namespace_ - Pids that contain a namespace +* _npm_packages.pid_with_namespace_ - Pids that contain a namespace +* _os_version.pid_with_namespace_ - Pids that contain a namespace +* _python_packages.pid_with_namespace_ - Pids that contain a namespace +* _rpm_packages.pid_with_namespace_ - Pids that contain a namespace +* _suid_bin.pid_with_namespace_ - Pids that contain a namespace +* _user_ssh_keys.pid_with_namespace_ - Pids that contain a namespace +* _users.pid_with_namespace_ - Pids that contain a namespace +* _yum_sources.pid_with_namespace_ - Pids that contain a namespace + +*pids* - keyword + +* _docker_container_stats.pids_ - Number of processes +* _lldp_neighbors.pids_ - Comma delimited list of PIDs + +*placement_group_id* - keyword, text.text + +* _azure_instance_metadata.placement_group_id_ - Placement group for the VM scale set + +*platform* - keyword, text.text + +* _os_version.platform_ - OS Platform or ID +* _osquery_packs.platform_ - Platforms this query is supported on + +*platform_binary* - keyword, number.long + +* _es_process_events.platform_binary_ - Indicates if the binary is Apple signed binary (1) or not (0) + +*platform_fault_domain* - keyword, text.text + +* _azure_instance_metadata.platform_fault_domain_ - Fault domain the VM is running in + +*platform_info* - keyword, number.long + +* _msr.platform_info_ - Platform information. + +*platform_like* - keyword, text.text + +* _os_version.platform_like_ - Closely related platforms + +*platform_mask* - keyword, number.long + +* _osquery_info.platform_mask_ - The osquery platform bitmask + +*platform_update_domain* - keyword, text.text + +* _azure_instance_metadata.platform_update_domain_ - Update domain the VM is running in + +*plugin* - keyword, text.text + +* _authorization_mechanisms.plugin_ - Authorization plugin name + +*pnp_device_id* - keyword, text.text + +* _disk_info.pnp_device_id_ - The unique identifier of the drive on the system. + +*point_to_point* - keyword, text.text + +* _interface_addresses.point_to_point_ - PtP address for the interface + +*points* - keyword, number.long + +* _example.points_ - This is a signed SQLite int column + +*policies* - keyword, text.text + +* _curl_certificate.policies_ - Certificate Policies + +*policy* - keyword, text.text + +* _iptables.policy_ - Policy that applies for this rule. + +*policy_constraints* - keyword, text.text + +* _curl_certificate.policy_constraints_ - Policy Constraints + +*policy_mappings* - keyword, text.text + +* _curl_certificate.policy_mappings_ - Policy Mappings + +*port* - keyword, number.long + +* _docker_container_ports.port_ - Port inside the container +* _etc_services.port_ - Service port number +* _listening_ports.port_ - Transport layer port + +*port_aggregation_id* - keyword, text.text + +* _lldp_neighbors.port_aggregation_id_ - Port aggregation ID + +*port_autoneg_1000baset_fd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_1000baset_fd_enabled_ - 1000Base-T FD auto negotiation enabled + +*port_autoneg_1000baset_hd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_1000baset_hd_enabled_ - 1000Base-T HD auto negotiation enabled + +*port_autoneg_1000basex_fd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_1000basex_fd_enabled_ - 1000Base-X FD auto negotiation enabled + +*port_autoneg_1000basex_hd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_1000basex_hd_enabled_ - 1000Base-X HD auto negotiation enabled + +*port_autoneg_100baset2_fd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_100baset2_fd_enabled_ - 100Base-T2 FD auto negotiation enabled + +*port_autoneg_100baset2_hd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_100baset2_hd_enabled_ - 100Base-T2 HD auto negotiation enabled + +*port_autoneg_100baset4_fd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_100baset4_fd_enabled_ - 100Base-T4 FD auto negotiation enabled + +*port_autoneg_100baset4_hd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_100baset4_hd_enabled_ - 100Base-T4 HD auto negotiation enabled + +*port_autoneg_100basetx_fd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_100basetx_fd_enabled_ - 100Base-TX FD auto negotiation enabled + +*port_autoneg_100basetx_hd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_100basetx_hd_enabled_ - 100Base-TX HD auto negotiation enabled + +*port_autoneg_10baset_fd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_10baset_fd_enabled_ - 10Base-T FD auto negotiation enabled + +*port_autoneg_10baset_hd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_10baset_hd_enabled_ - 10Base-T HD auto negotiation enabled + +*port_autoneg_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_enabled_ - Is auto negotiation enabled + +*port_autoneg_supported* - keyword, number.long + +* _lldp_neighbors.port_autoneg_supported_ - Auto negotiation supported + +*port_description* - keyword, text.text + +* _lldp_neighbors.port_description_ - Port description + +*port_id* - keyword, text.text + +* _lldp_neighbors.port_id_ - Port ID value + +*port_id_type* - keyword, text.text + +* _lldp_neighbors.port_id_type_ - Port ID type + +*port_mau_type* - keyword, text.text + +* _lldp_neighbors.port_mau_type_ - MAU type + +*port_mfs* - keyword, number.long + +* _lldp_neighbors.port_mfs_ - Port max frame size + +*port_ttl* - keyword, number.long + +* _lldp_neighbors.port_ttl_ - Age of neighbor port + +*possibly_hidden* - keyword, number.long + +* _wifi_networks.possibly_hidden_ - 1 if network is possibly a hidden network, 0 otherwise + +*power_8023at_enabled* - keyword, number.long + +* _lldp_neighbors.power_8023at_enabled_ - Is 802.3at enabled + +*power_8023at_power_allocated* - keyword, text.text + +* _lldp_neighbors.power_8023at_power_allocated_ - 802.3at power allocated + +*power_8023at_power_priority* - keyword, text.text + +* _lldp_neighbors.power_8023at_power_priority_ - 802.3at power priority + +*power_8023at_power_requested* - keyword, text.text + +* _lldp_neighbors.power_8023at_power_requested_ - 802.3at power requested + +*power_8023at_power_source* - keyword, text.text + +* _lldp_neighbors.power_8023at_power_source_ - 802.3at power source + +*power_8023at_power_type* - keyword, text.text + +* _lldp_neighbors.power_8023at_power_type_ - 802.3at power type + +*power_class* - keyword, text.text + +* _lldp_neighbors.power_class_ - Power class + +*power_device_type* - keyword, text.text + +* _lldp_neighbors.power_device_type_ - Dot3 power device type + +*power_mdi_enabled* - keyword, number.long + +* _lldp_neighbors.power_mdi_enabled_ - Is MDI power enabled + +*power_mdi_supported* - keyword, number.long + +* _lldp_neighbors.power_mdi_supported_ - MDI power supported + +*power_mode* - keyword, text.text + +* _smart_drive_info.power_mode_ - Device power mode + +*power_paircontrol_enabled* - keyword, number.long + +* _lldp_neighbors.power_paircontrol_enabled_ - Is power pair control enabled + +*power_pairs* - keyword, text.text + +* _lldp_neighbors.power_pairs_ - Dot3 power pairs + +*ppid* - keyword, number.long + +* _process_file_events.ppid_ - Parent process ID + +*ppvids_enabled* - keyword, text.text + +* _lldp_neighbors.ppvids_enabled_ - Comma delimited list of enabled PPVIDs + +*ppvids_supported* - keyword, text.text + +* _lldp_neighbors.ppvids_supported_ - Comma delimited list of supported PPVIDs + +*pre_cpu_kernelmode_usage* - keyword, number.long + +* _docker_container_stats.pre_cpu_kernelmode_usage_ - Last read CPU kernel mode usage + +*pre_cpu_total_usage* - keyword, number.long + +* _docker_container_stats.pre_cpu_total_usage_ - Last read total CPU usage + +*pre_cpu_usermode_usage* - keyword, number.long + +* _docker_container_stats.pre_cpu_usermode_usage_ - Last read CPU user mode usage + +*pre_online_cpus* - keyword, number.long + +* _docker_container_stats.pre_online_cpus_ - Last read online CPUs + +*pre_system_cpu_usage* - keyword, number.long + +* _docker_container_stats.pre_system_cpu_usage_ - Last read CPU system usage + +*prefix* - keyword, text.text + +* _homebrew_packages.prefix_ - Homebrew install prefix + +*preread* - keyword, number.long + +* _docker_container_stats.preread_ - UNIX time when stats were last read + +*principal* - keyword, text.text + +* _ntfs_acl_permissions.principal_ - User or group to which the ACE applies. + +*printer_sharing* - keyword, number.long + +* _sharing_preferences.printer_sharing_ - 1 If printer sharing is enabled else 0 + +*priority* - keyword, text.text + +* _deb_packages.priority_ - Package priority + +*privileged* - keyword, text.text + +* _authorization_mechanisms.privileged_ - If privileged it will run as root, else as an anonymous user +* _docker_containers.privileged_ - Is the container privileged + +*probe_error* - keyword, number.long + +* _bpf_process_events.probe_error_ - Set to 1 if one or more buffers could not be captured +* _bpf_socket_events.probe_error_ - Set to 1 if one or more buffers could not be captured + +*process* - keyword, text.text + +* _alf_explicit_auths.process_ - Process name explicitly allowed + +*process_being_tapped* - keyword, number.long + +* _event_taps.process_being_tapped_ - The process ID of the target application + +*process_type* - keyword, text.text + +* _launchd.process_type_ - Key describes the intended purpose of the job + +*process_uptime* - keyword, number.long + +* _windows_crashes.process_uptime_ - Uptime of the process in seconds + +*processes* - keyword, number.long + +* _lxd_instances.processes_ - Number of processes running inside this instance + +*processing_time* - keyword, number.long + +* _cups_jobs.processing_time_ - How long the job took to process + +*processor_number* - keyword, number.long + +* _msr.processor_number_ - The processor number as reported in /proc/cpuinfo + +*processor_type* - keyword, text.text + +* _cpu_info.processor_type_ - The processor type, such as Central, Math, or Video. + +*product_name* - keyword, text.text + +* _tpm_info.product_name_ - Product name of the TPM + +*product_version* - keyword, text.text + +* _file.product_version_ - File product version + +*profile* - keyword, text.text + +* _apparmor_events.profile_ - Apparmor profile name +* _chrome_extensions.profile_ - The name of the Chrome profile that contains this extension + +*profile_path* - keyword, text.text + +* _chrome_extension_content_scripts.profile_path_ - The profile path +* _chrome_extensions.profile_path_ - The profile path +* _logon_sessions.profile_path_ - The home directory for the logon session. + +*program* - keyword, text.text + +* _launchd.program_ - Path to target program + +*program_arguments* - keyword, text.text + +* _launchd.program_arguments_ - Command line arguments passed to program + +*propagation* - keyword, text.text + +* _docker_container_mounts.propagation_ - Mount propagation + +*protected* - keyword, number.long + +* _app_schemes.protected_ - 1 if this handler is protected (reserved) by OS X, else 0 + +*protection_disabled* - keyword, number.long + +* _carbon_black_info.protection_disabled_ - If the sensor is configured to report tamper events + +*protection_status* - keyword, number.long + +* _bitlocker_info.protection_status_ - The bitlocker protection status of the drive. + +*protection_type* - keyword, text.text + +* _processes.protection_type_ - The protection type of the process + +*protocol* - keyword + +* _bpf_socket_events.protocol_ - The network protocol ID +* _etc_services.protocol_ - Transport protocol (TCP/UDP) +* _iptables.protocol_ - Protocol number identification. +* _listening_ports.protocol_ - Transport protocol (TCP/UDP) +* _process_open_sockets.protocol_ - Transport protocol (TCP/UDP) +* _socket_events.protocol_ - The network protocol ID +* _usb_devices.protocol_ - USB Device protocol + +*provider* - keyword, text.text + +* _drivers.provider_ - Driver provider + +*provider_guid* - keyword, text.text + +* _windows_eventlog.provider_guid_ - Provider guid of the event +* _windows_events.provider_guid_ - Provider guid of the event + +*provider_name* - keyword, text.text + +* _windows_eventlog.provider_name_ - Provider name of the event +* _windows_events.provider_name_ - Provider name of the event + +*pseudo* - keyword, number.long + +* _process_memory_map.pseudo_ - 1 If path is a pseudo path, else 0 + +*psize* - keyword, number.long + +* _elf_segments.psize_ - Size of segment in file + +*public* - keyword, number.long + +* _lxd_images.public_ - Whether image is public (1) or not (0) + +*publisher* - keyword, text.text + +* _azure_instance_metadata.publisher_ - Publisher of the VM image +* _osquery_events.publisher_ - Name of the associated publisher +* _programs.publisher_ - Name of the product supplier. + +*purgeable* - keyword, number.long + +* _virtual_memory_info.purgeable_ - Total number of purgeable pages. + +*purged* - keyword, number.long + +* _virtual_memory_info.purged_ - Total number of purged pages. + +*pvid* - keyword, text.text + +* _lldp_neighbors.pvid_ - Primary VLAN id + +*query* - keyword, text.text + +* _mdfind.query_ - The query that was run to find the file +* _osquery_schedule.query_ - The exact query to run +* _wmi_event_filters.query_ - Windows Management Instrumentation Query Language (WQL) event query that specifies the set of events for consumer notification, and the specific conditions for notification. + +*query_language* - keyword, text.text + +* _wmi_event_filters.query_language_ - Query language that the query is written in. + +*queue_directories* - keyword, text.text + +* _launchd.queue_directories_ - Similar to watch_paths but only with non-empty directories + +*raid_disks* - keyword, number.long + +* _md_devices.raid_disks_ - Number of configured RAID disks in array + +*raid_level* - keyword, number.long + +* _md_devices.raid_level_ - Current raid level of the array + +*rapl_energy_status* - keyword, number.long + +* _msr.rapl_energy_status_ - Run Time Average Power Limiting energy status. + +*rapl_power_limit* - keyword, number.long + +* _msr.rapl_power_limit_ - Run Time Average Power Limiting power limit. + +*rapl_power_units* - keyword, number.long + +* _msr.rapl_power_units_ - Run Time Average Power Limiting power units. + +*reactivated* - keyword, number.long + +* _virtual_memory_info.reactivated_ - Total number of reactivated pages. + +*read* - keyword, number.long + +* _docker_container_stats.read_ - UNIX time when stats were read + +*read_device_identity_failure* - keyword, text.text + +* _smart_drive_info.read_device_identity_failure_ - Error string for device id read, if any + +*readonly* - keyword, number.long + +* _nfs_shares.readonly_ - 1 if the share is exported readonly else 0 + +*readonly_rootfs* - keyword, number.long + +* _docker_containers.readonly_rootfs_ - Is the root filesystem mounted as read only + +*record_timestamp* - keyword, text.text + +* _ntfs_journal_events.record_timestamp_ - Journal record timestamp + +*record_usn* - keyword, text.text + +* _ntfs_journal_events.record_usn_ - The update sequence number that identifies the journal record + +*recovery_finish* - keyword, text.text + +* _md_devices.recovery_finish_ - Estimated duration of recovery activity + +*recovery_progress* - keyword, text.text + +* _md_devices.recovery_progress_ - Progress of the recovery activity + +*recovery_speed* - keyword, text.text + +* _md_devices.recovery_speed_ - Speed of recovery activity + +*redirect_accept* - keyword, number.long + +* _interface_ipv6.redirect_accept_ - Accept ICMP redirect messages + +*ref_pid* - keyword, number.long + +* _asl.ref_pid_ - Reference PID for messages proxied by launchd + +*ref_proc* - keyword, text.text + +* _asl.ref_proc_ - Reference process for messages proxied by launchd + +*referenced* - keyword, number.long + +* _chrome_extension_content_scripts.referenced_ - 1 if this extension is referenced by the Preferences file of the profile +* _chrome_extensions.referenced_ - 1 if this extension is referenced by the Preferences file of the profile + +*referenced_identifier* - keyword, text.text + +* _chrome_extensions.referenced_identifier_ - Extension identifier, as specified by the preferences file. Empty if the extension is not in the profile. + +*refreshes* - keyword, number.long + +* _osquery_events.refreshes_ - Publisher only: number of runloop restarts + +*refs* - keyword, number.long + +* _fbsd_kmods.refs_ - Module reverse dependencies +* _kernel_extensions.refs_ - Reference count + +*region* - keyword, text.text + +* _ec2_instance_metadata.region_ - AWS region in which this instance launched + +*registers* - keyword, text.text + +* _crashes.registers_ - The value of the system registers +* _kernel_panics.registers_ - A space delimited line of register:value pairs +* _windows_crashes.registers_ - The values of the system registers + +*registry* - keyword, text.text + +* _osquery_registry.registry_ - Name of the osquery registry + +*registry_hive* - keyword, text.text + +* _logged_in_users.registry_hive_ - HKEY_USERS registry hive + +*registry_path* - keyword, text.text + +* _ie_extensions.registry_path_ - Extension identifier + +*relative_path* - keyword, text.text + +* _shortcut_files.relative_path_ - Relative path to target file from lnk file. +* _wmi_cli_event_consumers.relative_path_ - Relative path to the class or instance. +* _wmi_event_filters.relative_path_ - Relative path to the class or instance. +* _wmi_filter_consumer_binding.relative_path_ - Relative path to the class or instance. +* _wmi_script_event_consumers.relative_path_ - Relative path to the class or instance. + +*release* - keyword, text.text + +* _apt_sources.release_ - Release name +* _lxd_images.release_ - OS release version on which the image is based +* _rpm_packages.release_ - Package release + +*remediation_path* - keyword, text.text + +* _windows_security_products.remediation_path_ - Remediation path + +*remote_address* - keyword, text.text + +* _bpf_socket_events.remote_address_ - Remote address associated with socket +* _process_open_sockets.remote_address_ - Socket remote address +* _socket_events.remote_address_ - Remote address associated with socket + +*remote_apple_events* - keyword, number.long + +* _sharing_preferences.remote_apple_events_ - 1 If remote apple events are enabled else 0 + +*remote_login* - keyword, number.long + +* _sharing_preferences.remote_login_ - 1 If remote login is enabled else 0 + +*remote_management* - keyword, number.long + +* _sharing_preferences.remote_management_ - 1 If remote management is enabled else 0 + +*remote_port* - keyword, number.long + +* _bpf_socket_events.remote_port_ - Remote network protocol port number +* _process_open_sockets.remote_port_ - Socket remote port +* _socket_events.remote_port_ - Remote network protocol port number + +*removable* - keyword, number.long + +* _usb_devices.removable_ - 1 If USB device is removable else 0 + +*repository* - keyword, text.text + +* _portage_packages.repository_ - From which repository the ebuild was used + +*request_id* - keyword, text.text + +* _carves.request_id_ - Identifying value of the carve request (e.g., scheduled query name, distributed request, etc) + +*requested_mask* - keyword, text.text + +* _apparmor_events.requested_mask_ - Requested access mask + +*requirement* - keyword, text.text + +* _gatekeeper_approved_apps.requirement_ - Code signing requirement language + +*reservation_id* - keyword, text.text + +* _ec2_instance_metadata.reservation_id_ - ID of the reservation + +*reshape_finish* - keyword, text.text + +* _md_devices.reshape_finish_ - Estimated duration of reshape activity + +*reshape_progress* - keyword, text.text + +* _md_devices.reshape_progress_ - Progress of the reshape activity + +*reshape_speed* - keyword, text.text + +* _md_devices.reshape_speed_ - Speed of reshape activity + +*resident_size* - keyword, number.long + +* _docker_container_processes.resident_size_ - Bytes of private memory used by process +* _processes.resident_size_ - Bytes of private memory used by process + +*resource_group_name* - keyword, text.text + +* _azure_instance_metadata.resource_group_name_ - Resource group for the VM + +*response_code* - keyword, number.long + +* _curl.response_code_ - The HTTP status code for the response + +*responsible* - keyword, text.text + +* _crashes.responsible_ - Process responsible for the crashed process + +*result* - keyword, text.text + +* _authenticode.result_ - The signature check result +* _curl.result_ - The HTTP response body + +*resync_finish* - keyword, text.text + +* _md_devices.resync_finish_ - Estimated duration of resync activity + +*resync_progress* - keyword, text.text + +* _md_devices.resync_progress_ - Progress of the resync activity + +*resync_speed* - keyword, text.text + +* _md_devices.resync_speed_ - Speed of resync activity + +*retain_count* - keyword, number.long + +* _iokit_devicetree.retain_count_ - The device reference count +* _iokit_registry.retain_count_ - The node reference count + +*revision* - keyword, text.text + +* _deb_packages.revision_ - Package revision +* _hardware_events.revision_ - Device revision (optional) +* _platform_info.revision_ - BIOS major and minor revision + +*rid* - keyword, number.long + +* _lldp_neighbors.rid_ - Neighbor chassis index + +*roaming* - keyword, number.long + +* _wifi_networks.roaming_ - 1 if roaming is supported, 0 otherwise + +*roaming_profile* - keyword, text.text + +* _wifi_networks.roaming_profile_ - Describe the roaming profile, usually one of Single, Dual or Multi + +*root* - keyword, text.text + +* _processes.root_ - Process virtual root directory + +*root_dir* - keyword, text.text + +* _docker_info.root_dir_ - Docker root directory + +*root_directory* - keyword, text.text + +* _launchd.root_directory_ - Key used to specify a directory to chroot to before launch + +*root_volume_uuid* - keyword, text.text + +* _time_machine_destinations.root_volume_uuid_ - Root UUID of backup volume + +*rotation_rate* - keyword, text.text + +* _smart_drive_info.rotation_rate_ - Drive RPM + +*round_trip_time* - keyword, number.long + +* _curl.round_trip_time_ - Time taken to complete the request + +*rowid* - keyword, number.long + +* _quicklook_cache.rowid_ - Quicklook file rowid key + +*rssi* - keyword, number.long + +* _wifi_status.rssi_ - The current received signal strength indication (dbm) +* _wifi_survey.rssi_ - The current received signal strength indication (dbm) + +*rtadv_accept* - keyword, number.long + +* _interface_ipv6.rtadv_accept_ - Accept ICMP Router Advertisement + +*rule_details* - keyword, text.text + +* _sudoers.rule_details_ - Rule definition + +*run_at_load* - keyword, text.text + +* _launchd.run_at_load_ - Should the program run on launch load + +*run_count* - keyword, number.long + +* _prefetch.run_count_ - Number of times the application has been run. + +*rw* - keyword, number.long + +* _docker_container_mounts.rw_ - 1 if read/write. 0 otherwise + +*sata_version* - keyword, text.text + +* _smart_drive_info.sata_version_ - SATA version, if any + +*scheme* - keyword, text.text + +* _app_schemes.scheme_ - Name of the scheme/protocol + +*scope* - keyword, text.text + +* _selinux_settings.scope_ - Where the key is located inside the SELinuxFS mount point. + +*screen_sharing* - keyword, number.long + +* _sharing_preferences.screen_sharing_ - 1 If screen sharing is enabled else 0 + +*script* - keyword, text.text + +* _chrome_extension_content_scripts.script_ - The content script used by the extension + +*script_block_count* - keyword, number.long + +* _powershell_events.script_block_count_ - The total number of script blocks for this script + +*script_block_id* - keyword, text.text + +* _powershell_events.script_block_id_ - The unique GUID of the powershell script to which this block belongs + +*script_file_name* - keyword, text.text + +* _wmi_script_event_consumers.script_file_name_ - Name of the file from which the script text is read, intended as an alternative to specifying the text of the script in the ScriptText property. + +*script_name* - keyword, text.text + +* _powershell_events.script_name_ - The name of the Powershell script + +*script_path* - keyword, text.text + +* _powershell_events.script_path_ - The path for the Powershell script + +*script_text* - keyword, text.text + +* _powershell_events.script_text_ - The text content of the Powershell script +* _wmi_script_event_consumers.script_text_ - Text of the script that is expressed in a language known to the scripting engine. This property must be NULL if the ScriptFileName property is not NULL. + +*scripting_engine* - keyword, text.text + +* _wmi_script_event_consumers.scripting_engine_ - Name of the scripting engine to use, for example, 'VBScript'. This property cannot be NULL. + +*sdb_id* - keyword, text.text + +* _appcompat_shims.sdb_id_ - Unique GUID of the SDB. + +*sdk* - keyword, text.text + +* _browser_plugins.sdk_ - Build SDK used to compile plugin +* _safari_extensions.sdk_ - Bundle SDK used to compile extension + +*sdk_version* - keyword, text.text + +* _osquery_extensions.sdk_version_ - osquery SDK version used to build the extension + +*seconds* - keyword, number.long + +* _time.seconds_ - Current seconds in the system +* _uptime.seconds_ - Seconds of uptime + +*section* - keyword, text.text + +* _deb_packages.section_ - Package section + +*sector_sizes* - keyword, text.text + +* _smart_drive_info.sector_sizes_ - Bytes of drive sector sizes + +*secure_boot* - keyword, number.long + +* _secureboot.secure_boot_ - Whether secure boot is enabled + +*secure_process* - keyword, number.long + +* _processes.secure_process_ - Process is secure (IUM) yes=1, no=0 + +*security_breach* - keyword, text.text + +* _chassis_info.security_breach_ - The physical status of the chassis such as Breach Successful, Breach Attempted, etc. + +*security_groups* - keyword, text.text + +* _ec2_instance_metadata.security_groups_ - Comma separated list of security group names + +*security_options* - keyword, text.text + +* _docker_containers.security_options_ - List of container security options + +*security_type* - keyword, text.text + +* _wifi_networks.security_type_ - Type of security on this network +* _wifi_status.security_type_ - Type of security on this network + +*self_signed* - keyword, number.long + +* _certificates.self_signed_ - 1 if self-signed, else 0 + +*sender* - keyword, text.text + +* _asl.sender_ - Sender's identification string. Default is process name. + +*sensor_backend_server* - keyword, text.text + +* _carbon_black_info.sensor_backend_server_ - Carbon Black server + +*sensor_id* - keyword, number.long + +* _carbon_black_info.sensor_id_ - Sensor ID of the Carbon Black sensor + +*sensor_ip_addr* - keyword, text.text + +* _carbon_black_info.sensor_ip_addr_ - IP address of the sensor + +*seq_num* - keyword, number.long + +* _es_process_events.seq_num_ - Per event sequence number + +*serial* - keyword, text.text + +* _certificates.serial_ - Certificate serial number +* _chassis_info.serial_ - The serial number of the chassis. +* _disk_info.serial_ - The serial number of the disk. +* _hardware_events.serial_ - Device serial (optional) +* _usb_devices.serial_ - USB Device serial connection + +*serial_number* - keyword, text.text + +* _authenticode.serial_number_ - The certificate serial number +* _battery.serial_number_ - The battery's unique serial number +* _curl_certificate.serial_number_ - Certificate serial number +* _memory_devices.serial_number_ - Serial number of memory device +* _smart_drive_info.serial_number_ - Device serial number + +*serial_port_enabled* - keyword, text.text + +* _ycloud_instance_metadata.serial_port_enabled_ - Indicates if serial port is enabled for the VM + +*series* - keyword, text.text + +* _video_info.series_ - The series of the gpu. + +*server_name* - keyword, text.text + +* _lxd_cluster.server_name_ - Name of the LXD server node +* _lxd_cluster_members.server_name_ - Name of the LXD server node + +*server_version* - keyword, text.text + +* _docker_info.server_version_ - Server version + +*service* - keyword, text.text + +* _drivers.service_ - Driver service name, if one exists +* _interface_details.service_ - The name of the service the network adapter uses. +* _iokit_devicetree.service_ - 1 if the device conforms to IOService else 0 + +*service_exit_code* - keyword, number.long + +* _services.service_exit_code_ - The service-specific error code that the service returns when an error occurs while the service is starting or stopping + +*service_key* - keyword, text.text + +* _drivers.service_key_ - Driver service registry key + +*service_type* - keyword, text.text + +* _services.service_type_ - Service Type: OWN_PROCESS, SHARE_PROCESS and maybe Interactive (can interact with the desktop) + +*ses* - keyword, number.long + +* _seccomp_events.ses_ - Session ID of the session from which the analyzed process was invoked + +*session_id* - keyword, number.long + +* _logon_sessions.session_id_ - The Terminal Services session identifier. +* _winbaseobj.session_id_ - Terminal Services Session Id + +*session_owner* - keyword, text.text + +* _authorizations.session_owner_ - Label top-level key + +*set* - keyword, number.long + +* _memory_devices.set_ - Identifies if memory device is one of a set of devices. A value of 0 indicates no set affiliation. + +*setup_mode* - keyword, number.long + +* _secureboot.setup_mode_ - Whether setup mode is enabled + +*severity* - keyword, number.long + +* _syslog_events.severity_ - Syslog severity + +*sgid* - keyword + +* _docker_container_processes.sgid_ - Saved group ID +* _process_events.sgid_ - Saved group ID at process start +* _process_file_events.sgid_ - Saved group ID of the process using the file +* _processes.sgid_ - Unsigned saved group ID + +*sha1* - keyword, text.text + +* _apparmor_profiles.sha1_ - A unique hash that identifies this policy. +* _certificates.sha1_ - SHA1 hash of the raw certificate contents +* _device_hash.sha1_ - SHA1 hash of provided inode data +* _file_events.sha1_ - The SHA1 of the file after change +* _hash.sha1_ - SHA1 hash of provided filesystem data +* _rpm_packages.sha1_ - SHA1 hash of the package contents + +*sha1_fingerprint* - keyword, text.text + +* _curl_certificate.sha1_fingerprint_ - SHA1 fingerprint + +*sha256* - keyword, text.text + +* _carves.sha256_ - A SHA256 sum of the carved archive +* _device_hash.sha256_ - SHA256 hash of provided inode data +* _file_events.sha256_ - The SHA256 of the file after change +* _hash.sha256_ - SHA256 hash of provided filesystem data +* _rpm_package_files.sha256_ - SHA256 file digest from RPM info DB + +*sha256_fingerprint* - keyword, text.text + +* _curl_certificate.sha256_fingerprint_ - SHA-256 fingerprint + +*shard* - keyword, number.long + +* _osquery_packs.shard_ - Shard restriction limit, 1-100, 0 meaning no restriction + +*share* - keyword, text.text + +* _nfs_shares.share_ - Filesystem path to the share + +*share_name* - keyword, text.text + +* _shortcut_files.share_name_ - Share name of the target file. + +*shared* - keyword, text.text + +* _authorizations.shared_ - Label top-level key + +*shell* - keyword, text.text + +* _users.shell_ - User's configured default shell + +*shell_only* - keyword, number.long + +* _osquery_flags.shell_only_ - Is the flag shell only? + +*shmid* - keyword, number.long + +* _shared_memory.shmid_ - Shared memory segment ID + +*sid* - keyword, text.text + +* _background_activities_moderator.sid_ - User SID. +* _certificates.sid_ - SID +* _logged_in_users.sid_ - The user's unique security identifier +* _office_mru.sid_ - User SID +* _shellbags.sid_ - User SID +* _userassist.sid_ - User SID. + +*sig* - keyword, number.long + +* _seccomp_events.sig_ - Signal value sent to process by seccomp + +*sig_group* - keyword, text.text + +* _yara.sig_group_ - Signature group used + +*sigfile* - keyword, text.text + +* _yara.sigfile_ - Signature file used + +*signature* - keyword, text.text + +* _curl_certificate.signature_ - Signature + +*signature_algorithm* - keyword, text.text + +* _curl_certificate.signature_algorithm_ - Signature Algorithm + +*signatures_up_to_date* - keyword, number.long + +* _windows_security_products.signatures_up_to_date_ - 1 if product signatures are up to date, else 0 + +*signed* - keyword, number.long + +* _drivers.signed_ - Whether the driver is signed or not +* _signature.signed_ - 1 If the file is signed else 0 + +*signing_algorithm* - keyword, text.text + +* _certificates.signing_algorithm_ - Signing algorithm used + +*signing_id* - keyword, text.text + +* _es_process_events.signing_id_ - Signature identifier of the process + +*sigrule* - keyword, text.text + +* _yara.sigrule_ - Signature strings used + +*sigurl* - keyword, text.text + +* _yara.sigurl_ - Signature url + +*size* - keyword + +* _acpi_tables.size_ - Size of compiled table data +* _block_devices.size_ - Block device size in blocks +* _carves.size_ - Size of the carved archive +* _cups_jobs.size_ - The size of the print job +* _deb_packages.size_ - Package size in bytes +* _device_file.size_ - Size of file in bytes +* _disk_events.size_ - Size of partition in bytes +* _docker_image_history.size_ - Size of instruction in bytes +* _elf_sections.size_ - Size of section +* _elf_symbols.size_ - Size of object +* _example.size_ - This is a signed SQLite bigint column +* _fbsd_kmods.size_ - Size of module content +* _file.size_ - Size of file in bytes +* _file_events.size_ - Size of file in bytes +* _kernel_extensions.size_ - Bytes of wired memory used by extension +* _kernel_modules.size_ - Size of module content +* _logical_drives.size_ - The total amount of space, in bytes, of the drive (-1 on failure). +* _lxd_images.size_ - Size of image in bytes +* _lxd_storage_pools.size_ - Size of the storage pool +* _md_devices.size_ - size of the array in blocks +* _memory_devices.size_ - Size of memory device in Megabyte +* _package_bom.size_ - Expected file size +* _platform_info.size_ - Size in bytes of firmware +* _portage_packages.size_ - The size of the package +* _prefetch.size_ - Application file size. +* _quicklook_cache.size_ - Parsed version size field +* _rpm_package_files.size_ - Expected file size in bytes from RPM info DB +* _rpm_packages.size_ - Package size in bytes +* _shared_memory.size_ - Size in bytes +* _smbios_tables.size_ - Table entry size in bytes +* _smc_keys.size_ - Reported size of data in bytes + +*size_bytes* - keyword, number.long + +* _docker_images.size_bytes_ - Size of image in bytes + +*sku* - keyword, text.text + +* _azure_instance_metadata.sku_ - SKU for the VM image +* _chassis_info.sku_ - The Stock Keeping Unit number if available. + +*slot* - keyword + +* _md_drives.slot_ - Slot position of disk +* _portage_packages.slot_ - The slot used by package + +*smart_enabled* - keyword, text.text + +* _smart_drive_info.smart_enabled_ - SMART enabled status + +*smart_supported* - keyword, text.text + +* _smart_drive_info.smart_supported_ - SMART support status + +*smbios_tag* - keyword, text.text + +* _chassis_info.smbios_tag_ - The assigned asset tag number of the chassis. + +*socket* - keyword + +* _listening_ports.socket_ - Socket handle or inode number +* _process_open_sockets.socket_ - Socket handle or inode number +* _socket_events.socket_ - The local path (UNIX domain socket only) + +*socket_designation* - keyword, text.text + +* _cpu_info.socket_designation_ - The assigned socket on the board for the given CPU. + +*soft_limit* - keyword, text.text + +* _ulimit_info.soft_limit_ - Current limit value + +*softirq* - keyword, number.long + +* _cpu_time.softirq_ - Time spent servicing softirqs + +*source* - keyword, text.text + +* _apt_sources.source_ - Source file +* _autoexec.source_ - Source table of the autoexec item +* _deb_packages.source_ - Package source +* _docker_container_mounts.source_ - Source path on host +* _lxd_storage_pools.source_ - Storage pool source +* _package_install_history.source_ - Install source: usually the installer process name +* _routes.source_ - Route source +* _rpm_packages.source_ - Source RPM package name (optional) +* _shellbags.source_ - Shellbags source Registry file +* _startup_items.source_ - Directory or plist containing startup item +* _sudoers.source_ - Source file containing the given rule +* _windows_events.source_ - Source or channel of the event + +*source_path* - keyword, text.text + +* _systemd_units.source_path_ - Path to the (possibly generated) unit configuration file + +*source_url* - keyword, text.text + +* _firefox_addons.source_url_ - URL that installed the addon + +*space_total* - keyword, number.long + +* _lxd_storage_pools.space_total_ - Total available storage space in bytes for this storage pool + +*space_used* - keyword, number.long + +* _lxd_storage_pools.space_used_ - Storage space used in bytes + +*spare_disks* - keyword, number.long + +* _md_devices.spare_disks_ - Number of idle disks in array + +*spec_version* - keyword, text.text + +* _tpm_info.spec_version_ - Trusted Computing Group specification that the TPM supports + +*speculative* - keyword, number.long + +* _virtual_memory_info.speculative_ - Total number of speculative pages. + +*speed* - keyword, number.long + +* _interface_details.speed_ - Estimate of the current bandwidth in bits per second. + +*src_ip* - keyword, text.text + +* _iptables.src_ip_ - Source IP address. + +*src_mask* - keyword, text.text + +* _iptables.src_mask_ - Source IP address mask. + +*src_port* - keyword, text.text + +* _iptables.src_port_ - Protocol source port(s). + +*ssdeep* - keyword, text.text + +* _hash.ssdeep_ - ssdeep hash of provided filesystem data + +*ssh_config_file* - keyword, text.text + +* _ssh_configs.ssh_config_file_ - Path to the ssh_config file + +*ssh_public_key* - keyword, text.text + +* _ec2_instance_metadata.ssh_public_key_ - SSH public key. Only available if supplied at instance launch time +* _ycloud_instance_metadata.ssh_public_key_ - SSH public key. Only available if supplied at instance launch time + +*ssid* - keyword, text.text + +* _wifi_networks.ssid_ - SSID octets of the network +* _wifi_status.ssid_ - SSID octets of the network +* _wifi_survey.ssid_ - SSID octets of the network + +*stack_trace* - keyword, text.text + +* _crashes.stack_trace_ - Most recent frame from the stack trace +* _windows_crashes.stack_trace_ - Multiple stack frames from the stack trace + +*start* - keyword, text.text + +* _memory_map.start_ - Start address of memory region +* _process_memory_map.start_ - Virtual start address (hex) + +*start_interval* - keyword, text.text + +* _launchd.start_interval_ - Frequency to run in seconds + +*start_on_mount* - keyword, text.text + +* _launchd.start_on_mount_ - Run daemon or agent every time a filesystem is mounted + +*start_time* - keyword, number.long + +* _docker_container_processes.start_time_ - Process start in seconds since boot (non-sleeping) +* _osquery_info.start_time_ - UNIX time in seconds when the process started +* _processes.start_time_ - Process start time in seconds since Epoch, in case of error -1 + +*start_type* - keyword, text.text + +* _services.start_type_ - Service start type: BOOT_START, SYSTEM_START, AUTO_START, DEMAND_START, DISABLED + +*started_at* - keyword, text.text + +* _docker_containers.started_at_ - Container start time as string + +*starting_address* - keyword, text.text + +* _memory_array_mapped_addresses.starting_address_ - Physical stating address, in kilobytes, of a range of memory mapped to physical memory array +* _memory_device_mapped_addresses.starting_address_ - Physical stating address, in kilobytes, of a range of memory mapped to physical memory array + +*state* - keyword + +* _alf_exceptions.state_ - Firewall exception state +* _battery.state_ - One of the following: "AC Power" indicates the battery is connected to an external power source, "Battery Power" indicates that the battery is drawing internal power, "Off Line" indicates the battery is off-line or no longer connected +* _chrome_extensions.state_ - 1 if this extension is enabled +* _docker_container_processes.state_ - Process state +* _docker_containers.state_ - Container state (created, restarting, running, removing, paused, exited, dead) +* _lxd_networks.state_ - Network status +* _md_drives.state_ - State of the drive +* _process_open_sockets.state_ - TCP socket state +* _processes.state_ - Process state +* _scheduled_tasks.state_ - State of the scheduled task +* _system_extensions.state_ - System extension state +* _windows_optional_features.state_ - Installation state value. 1 == Enabled, 2 == Disabled, 3 == Absent +* _windows_security_products.state_ - State of protection + +*state_timestamp* - keyword, text.text + +* _windows_security_products.state_timestamp_ - Timestamp for the product state + +*stateful* - keyword, number.long + +* _lxd_instances.stateful_ - Whether the instance is stateful(1) or not(0) + +*statename* - keyword, text.text + +* _windows_optional_features.statename_ - Installation state name. 'Enabled','Disabled','Absent' + +*status* - keyword, text.text + +* _carves.status_ - Status of the carve, can be STARTING, PENDING, SUCCESS, or FAILED +* _chassis_info.status_ - If available, gives various operational or nonoperational statuses such as OK, Degraded, and Pred Fail. +* _deb_packages.status_ - Package status +* _docker_containers.status_ - Container status information +* _kernel_modules.status_ - Kernel module status +* _lxd_cluster_members.status_ - Status of the node (Online/Offline) +* _lxd_instances.status_ - Instance state (running, stopped, etc.) +* _md_devices.status_ - Current state of the array +* _ntdomains.status_ - The current status of the domain object. +* _process_events.status_ - OpenBSM Attribute: Status of the process +* _services.status_ - Service Current status: STOPPED, START_PENDING, STOP_PENDING, RUNNING, CONTINUE_PENDING, PAUSE_PENDING, PAUSED +* _shared_memory.status_ - Destination/attach status +* _shared_resources.status_ - String that indicates the current status of the object. +* _socket_events.status_ - Either 'succeeded', 'failed', 'in_progress' (connect() on non-blocking socket) or 'no_client' (null accept() on non-blocking socket) +* _startup_items.status_ - Startup status; either enabled or disabled + +*stderr_path* - keyword, text.text + +* _launchd.stderr_path_ - Pipe stderr to a target path + +*stdout_path* - keyword, text.text + +* _launchd.stdout_path_ - Pipe stdout to a target path + +*steal* - keyword, number.long + +* _cpu_time.steal_ - Time spent in other operating systems when running in a virtualized environment + +*stealth_enabled* - keyword, number.long + +* _alf.stealth_enabled_ - 1 If stealth mode is enabled else 0 + +*stibp_support_enabled* - keyword, number.long + +* _kva_speculative_info.stibp_support_enabled_ - Windows uses STIBP. + +*storage_driver* - keyword, text.text + +* _docker_info.storage_driver_ - Storage driver + +*store* - keyword, text.text + +* _certificates.store_ - Certificate system store + +*store_id* - keyword, text.text + +* _certificates.store_id_ - Exists for service/user stores. Contains raw store id provided by WinAPI. + +*store_location* - keyword, text.text + +* _certificates.store_location_ - Certificate system store location + +*strings* - keyword, text.text + +* _yara.strings_ - Matching strings +* _yara_events.strings_ - Matching strings + +*sub_state* - keyword, text.text + +* _systemd_units.sub_state_ - The low-level unit activation state, values depend on unit type + +*subclass* - keyword, text.text + +* _usb_devices.subclass_ - USB Device subclass + +*subject* - keyword, text.text + +* _certificates.subject_ - Certificate distinguished name + +*subject_alternative_names* - keyword, text.text + +* _curl_certificate.subject_alternative_names_ - Subject Alternative Name + +*subject_info_access* - keyword, text.text + +* _curl_certificate.subject_info_access_ - Subject Information Access + +*subject_key_id* - keyword, text.text + +* _certificates.subject_key_id_ - SKID an optionally included SHA1 + +*subject_key_identifier* - keyword, text.text + +* _curl_certificate.subject_key_identifier_ - Subject Key Identifier + +*subject_name* - keyword, text.text + +* _authenticode.subject_name_ - The certificate subject name + +*subkey* - keyword, text.text + +* _plist.subkey_ - Intermediate key path, includes lists/dicts +* _preferences.subkey_ - Intemediate key path, includes lists/dicts + +*subnet* - keyword, text.text + +* _docker_networks.subnet_ - Network subnet + +*subscription_id* - keyword, text.text + +* _azure_instance_metadata.subscription_id_ - Azure subscription for the VM + +*subscriptions* - keyword, number.long + +* _osquery_events.subscriptions_ - Number of subscriptions the publisher received or subscriber used + +*subsystem* - keyword, text.text + +* _system_controls.subsystem_ - Subsystem ID, control type + +*subsystem_model* - keyword, text.text + +* _pci_devices.subsystem_model_ - Device description of PCI device subsystem + +*subsystem_model_id* - keyword, text.text + +* _pci_devices.subsystem_model_id_ - Model ID of PCI device subsystem + +*subsystem_vendor* - keyword, text.text + +* _pci_devices.subsystem_vendor_ - Vendor of PCI device subsystem + +*subsystem_vendor_id* - keyword, text.text + +* _pci_devices.subsystem_vendor_id_ - Vendor ID of PCI device subsystem + +*success* - keyword, number.long + +* _socket_events.success_ - Deprecated. Use the 'status' column instead + +*suid* - keyword + +* _docker_container_processes.suid_ - Saved user ID +* _process_events.suid_ - Saved user ID at process start +* _process_file_events.suid_ - Saved user ID of the process using the file +* _processes.suid_ - Unsigned saved user ID + +*summary* - keyword, text.text + +* _chocolatey_packages.summary_ - Package-supplied summary +* _python_packages.summary_ - Package-supplied summary + +*superblock_state* - keyword, text.text + +* _md_devices.superblock_state_ - State of the superblock + +*superblock_update_time* - keyword, number.long + +* _md_devices.superblock_update_time_ - Unix timestamp of last update + +*superblock_version* - keyword, text.text + +* _md_devices.superblock_version_ - Version of the superblock + +*swap_cached* - keyword, number.long + +* _memory_info.swap_cached_ - The amount of swap, in bytes, used as cache memory + +*swap_free* - keyword, number.long + +* _memory_info.swap_free_ - The total amount of swap free, in bytes + +*swap_ins* - keyword, number.long + +* _virtual_memory_info.swap_ins_ - The total number of compressed pages that have been swapped out to disk. + +*swap_limit* - keyword, number.long + +* _docker_info.swap_limit_ - 1 if swap limit support is enabled. 0 otherwise + +*swap_outs* - keyword, number.long + +* _virtual_memory_info.swap_outs_ - The total number of compressed pages that have been swapped back in from disk. + +*swap_total* - keyword, number.long + +* _memory_info.swap_total_ - The total amount of swap available, in bytes + +*symlink* - keyword, number.long + +* _file.symlink_ - 1 if the path is a symlink, otherwise 0 + +*syscall* - keyword, text.text + +* _bpf_process_events.syscall_ - System call name +* _bpf_socket_events.syscall_ - System call name +* _process_events.syscall_ - Syscall name: fork, vfork, clone, execve, execveat +* _seccomp_events.syscall_ - Type of the system call + +*system* - keyword, number.long + +* _cpu_time.system_ - Time spent in system mode + +*system_cpu_usage* - keyword, number.long + +* _docker_container_stats.system_cpu_usage_ - CPU system usage + +*system_model* - keyword, text.text + +* _kernel_panics.system_model_ - Physical system model, for example 'MacBookPro12,1 (Mac-E43C1C25D4880AD6)' + +*system_time* - keyword, number.long + +* _osquery_schedule.system_time_ - Total system time spent executing +* _processes.system_time_ - CPU time in milliseconds spent in kernel space + +*table* - keyword, text.text + +* _elf_symbols.table_ - Table name containing symbol + +*tag* - keyword + +* _elf_dynamic.tag_ - Tag ID +* _syslog_events.tag_ - The syslog tag + +*tags* - keyword, text.text + +* _docker_image_history.tags_ - Comma-separated list of tags +* _docker_images.tags_ - Comma-separated list of repository tags +* _yara.tags_ - Matching tags +* _yara_events.tags_ - Matching tags + +*tapping_process* - keyword, number.long + +* _event_taps.tapping_process_ - The process ID of the application that created the event tap. + +*target* - keyword + +* _fan_speed_sensors.target_ - Target speed +* _iptables.target_ - Target that applies for this rule. + +*target_accessed* - keyword, number.long + +* _shortcut_files.target_accessed_ - Target Accessed time. + +*target_created* - keyword, number.long + +* _shortcut_files.target_created_ - Target Created time. + +*target_modified* - keyword, number.long + +* _shortcut_files.target_modified_ - Target Modified time. + +*target_name* - keyword, text.text + +* _prometheus_metrics.target_name_ - Address of prometheus target + +*target_path* - keyword, text.text + +* _file_events.target_path_ - The path associated with the event +* _shortcut_files.target_path_ - Target file path +* _yara_events.target_path_ - The path scanned + +*target_size* - keyword, number.long + +* _shortcut_files.target_size_ - Size of target file. + +*task* - keyword, number.long + +* _windows_eventlog.task_ - Task value associated with the event +* _windows_events.task_ - Task value associated with the event + +*team* - keyword, text.text + +* _system_extensions.team_ - Signing team ID + +*team_id* - keyword, text.text + +* _es_process_events.team_id_ - Team identifier of thd process + +*team_identifier* - keyword, text.text + +* _signature.team_identifier_ - The team signing identifier sealed into the signature + +*temporarily_disabled* - keyword, number.long + +* _wifi_networks.temporarily_disabled_ - 1 if this network is temporarily disabled, 0 otherwise + +*terminal* - keyword, text.text + +* _user_events.terminal_ - The network protocol ID + +*threads* - keyword, number.long + +* _docker_container_processes.threads_ - Number of threads used by process +* _processes.threads_ - Number of threads used by process + +*throttled* - keyword, number.long + +* _virtual_memory_info.throttled_ - Total number of throttled pages. + +*tid* - keyword, number.long + +* _bpf_process_events.tid_ - Thread ID +* _bpf_socket_events.tid_ - Thread ID +* _windows_crashes.tid_ - Thread ID of the crashed thread +* _windows_eventlog.tid_ - Thread ID which emitted the event record + +*time* - keyword + +* _apparmor_events.time_ - Time of execution in UNIX time +* _asl.time_ - Unix timestamp. Set automatically +* _bpf_process_events.time_ - Time of execution in UNIX time +* _bpf_socket_events.time_ - Time of execution in UNIX time +* _carves.time_ - Time at which the carve was kicked off +* _disk_events.time_ - Time of appearance/disappearance in UNIX time +* _docker_container_processes.time_ - Cumulative CPU time. [DD-]HH:MM:SS format +* _es_process_events.time_ - Time of execution in UNIX time +* _file_events.time_ - Time of file event +* _hardware_events.time_ - Time of hardware event +* _kernel_panics.time_ - Formatted time of the event +* _last.time_ - Entry timestamp +* _logged_in_users.time_ - Time entry was made +* _ntfs_journal_events.time_ - Time of file event +* _package_install_history.time_ - Label date as UNIX timestamp +* _powershell_events.time_ - Timestamp the event was received by the osquery event publisher +* _process_events.time_ - Time of execution in UNIX time +* _process_file_events.time_ - Time of execution in UNIX time +* _seccomp_events.time_ - Time of execution in UNIX time +* _selinux_events.time_ - Time of execution in UNIX time +* _shell_history.time_ - Entry timestamp. It could be absent, default value is 0. +* _socket_events.time_ - Time of execution in UNIX time +* _syslog_events.time_ - Current unix epoch time +* _user_events.time_ - Time of execution in UNIX time +* _user_interaction_events.time_ - Time +* _windows_events.time_ - Timestamp the event was received +* _xprotect_reports.time_ - Quarantine alert time +* _yara_events.time_ - Time of the scan + +*time_nano_sec* - keyword, number.long + +* _asl.time_nano_sec_ - Nanosecond time. + +*time_range* - keyword, text.text + +* _windows_eventlog.time_range_ - System time to selectively filter the events + +*timeout* - keyword, text.text + +* _authorizations.timeout_ - Label top-level key +* _curl_certificate.timeout_ - Set this value to the timeout in seconds to complete the TLS handshake (default 4s, use 0 for no timeout) + +*timestamp* - keyword, text.text + +* _time.timestamp_ - Current timestamp (log format) in the system +* _windows_eventlog.timestamp_ - Timestamp to selectively filter the events + +*timestamp_ms* - keyword, number.long + +* _prometheus_metrics.timestamp_ms_ - Unix timestamp of collected data in MS + +*timezone* - keyword, text.text + +* _time.timezone_ - Current timezone in the system + +*title* - keyword, text.text + +* _cups_jobs.title_ - Title of the printed job + +*total_seconds* - keyword, number.long + +* _uptime.total_seconds_ - Total uptime seconds + +*total_size* - keyword, number.long + +* _docker_container_processes.total_size_ - Total virtual memory size +* _processes.total_size_ - Total virtual memory size + +*total_width* - keyword, number.long + +* _memory_devices.total_width_ - Total width, in bits, of this memory device, including any check or error-correction bits + +*transaction_id* - keyword, number.long + +* _file_events.transaction_id_ - ID used during bulk update +* _yara_events.transaction_id_ - ID used during bulk update + +*transmit_rate* - keyword, text.text + +* _wifi_status.transmit_rate_ - The current transmit rate + +*transport_type* - keyword, text.text + +* _smart_drive_info.transport_type_ - Drive transport type + +*tries* - keyword, text.text + +* _authorizations.tries_ - Label top-level key + +*tty* - keyword, text.text + +* _last.tty_ - Entry terminal +* _logged_in_users.tty_ - Device name + +*turbo_disabled* - keyword, number.long + +* _msr.turbo_disabled_ - Whether the turbo feature is disabled. + +*turbo_ratio_limit* - keyword, number.long + +* _msr.turbo_ratio_limit_ - The turbo feature ratio limit. + +*type* - keyword, text.text + +* _apparmor_events.type_ - Event type +* _appcompat_shims.type_ - Type of the SDB database. +* _block_devices.type_ - Block device type string +* _bpf_socket_events.type_ - The socket type +* _crashes.type_ - Type of crash log +* _device_file.type_ - File status +* _device_firmware.type_ - Type of device +* _device_partitions.type_ - +* _disk_encryption.type_ - Description of cipher type and mode if available +* _disk_info.type_ - The interface type of the disk. +* _dns_cache.type_ - DNS record type +* _dns_resolvers.type_ - Address type: sortlist, nameserver, search +* _docker_container_mounts.type_ - Type of mount (bind, volume) +* _docker_container_ports.type_ - Protocol (tcp, udp) +* _docker_volumes.type_ - Volume type +* _elf_info.type_ - Offset of section in file +* _elf_sections.type_ - Section type +* _elf_symbols.type_ - Symbol type +* _file.type_ - File status +* _firefox_addons.type_ - Extension, addon, webapp +* _hardware_events.type_ - Type of hardware and hardware event +* _interface_addresses.type_ - Type of address. One of dhcp, manual, auto, other, unknown +* _interface_details.type_ - Interface type (includes virtual) +* _keychain_items.type_ - Keychain item type (class) +* _last.type_ - Entry type, according to ut_type types (utmp.h) +* _logged_in_users.type_ - Login type +* _logical_drives.type_ - Deprecated (always 'Unknown'). +* _lxd_certificates.type_ - Type of the certificate +* _lxd_networks.type_ - Type of network +* _mounts.type_ - Mounted device type +* _ntfs_acl_permissions.type_ - Type of access mode for the access control entry. +* _nvram.type_ - Data type (CFData, CFString, etc) +* _osquery_events.type_ - Either publisher or subscriber +* _osquery_extensions.type_ - SDK extension type: extension or module +* _osquery_flags.type_ - Flag type +* _process_open_pipes.type_ - Pipe Type: named vs unnamed/anonymous +* _registry.type_ - Type of the registry value, or 'subkey' if item is a subkey +* _routes.type_ - Type of route +* _selinux_events.type_ - Event type +* _shared_resources.type_ - Type of resource being shared. Types include: disk drives, print queues, interprocess communications (IPC), and general devices. +* _smbios_tables.type_ - Table entry type +* _smc_keys.type_ - SMC-reported type literal type +* _startup_items.type_ - Startup Item or Login Item +* _system_controls.type_ - Data type +* _ulimit_info.type_ - System resource to be limited +* _user_events.type_ - The file description for the process socket +* _users.type_ - Whether the account is roaming (domain), local, or a system profile +* _windows_crashes.type_ - Type of crash log +* _windows_security_products.type_ - Type of security product +* _xprotect_meta.type_ - Either plugin or extension + +*type_name* - keyword, text.text + +* _last.type_name_ - Entry type name, according to ut_type types (utmp.h) + +*uid* - keyword + +* _account_policy_data.uid_ - User ID +* _asl.uid_ - UID that sent the log message (set by the server). +* _atom_packages.uid_ - The local user that owns the plugin +* _authorized_keys.uid_ - The local owner of authorized_keys file +* _bpf_process_events.uid_ - User ID +* _bpf_socket_events.uid_ - User ID +* _browser_plugins.uid_ - The local user that owns the plugin +* _chrome_extension_content_scripts.uid_ - The local user that owns the extension +* _chrome_extensions.uid_ - The local user that owns the extension +* _crashes.uid_ - User ID of the crashed process +* _device_file.uid_ - Owning user ID +* _disk_encryption.uid_ - Currently authenticated user if available +* _docker_container_processes.uid_ - User ID +* _es_process_events.uid_ - User ID of the process +* _file.uid_ - Owning user ID +* _file_events.uid_ - Owning user ID +* _firefox_addons.uid_ - The local user that owns the addon +* _known_hosts.uid_ - The local user that owns the known_hosts file +* _launchd_overrides.uid_ - User ID applied to the override, 0 applies to all +* _package_bom.uid_ - Expected user of file or directory +* _process_events.uid_ - User ID at process start +* _process_file_events.uid_ - The uid of the process performing the action +* _processes.uid_ - Unsigned user ID +* _safari_extensions.uid_ - The local user that owns the extension +* _seccomp_events.uid_ - User ID of the user who started the analyzed process +* _shell_history.uid_ - Shell history owner +* _ssh_configs.uid_ - The local owner of the ssh_config file +* _user_events.uid_ - User ID +* _user_groups.uid_ - User ID +* _user_ssh_keys.uid_ - The local user that owns the key file +* _users.uid_ - User ID + +*uid_signed* - keyword, number.long + +* _users.uid_signed_ - User ID as int64 signed (Apple) + +*umci_policy_status* - keyword, text.text + +* _hvci_status.umci_policy_status_ - The status of the User Mode Code Integrity security settings. Returns UNKNOWN if an error is encountered. + +*uncompressed* - keyword, number.long + +* _virtual_memory_info.uncompressed_ - Total number of uncompressed pages. + +*uninstall_string* - keyword, text.text + +* _programs.uninstall_string_ - Path and filename of the uninstaller. + +*unique_chip_id* - keyword, text.text + +* _ibridge_info.unique_chip_id_ - Unique id of the iBridge controller + +*unix_time* - keyword, number.long + +* _time.unix_time_ - Current UNIX time in the system, converted to UTC if --utc enabled + +*unmask* - keyword, number.long + +* _portage_keywords.unmask_ - If the package is unmasked + +*unused_devices* - keyword, text.text + +* _md_devices.unused_devices_ - Unused devices + +*update_source_alias* - keyword, text.text + +* _lxd_images.update_source_alias_ - Alias of image at update source server + +*update_source_certificate* - keyword, text.text + +* _lxd_images.update_source_certificate_ - Certificate for update source server + +*update_source_protocol* - keyword, text.text + +* _lxd_images.update_source_protocol_ - Protocol used for image information update and image import from source server + +*update_source_server* - keyword, text.text + +* _lxd_images.update_source_server_ - Server for image update + +*update_url* - keyword, text.text + +* _chrome_extensions.update_url_ - Extension-supplied update URI +* _safari_extensions.update_url_ - Extension-supplied update URI + +*upid* - keyword, number.long + +* _processes.upid_ - A 64bit pid that is never reused. Returns -1 if we couldn't gather them from the system. + +*uploaded_at* - keyword, text.text + +* _lxd_images.uploaded_at_ - ISO time of image upload + +*upn* - keyword, text.text + +* _logon_sessions.upn_ - The user principal name (UPN) for the owner of the logon session. + +*uppid* - keyword, number.long + +* _processes.uppid_ - The 64bit parent pid that is never reused. Returns -1 if we couldn't gather them from the system. + +*uptime* - keyword, number.long + +* _apparmor_events.uptime_ - Time of execution in system uptime +* _kernel_panics.uptime_ - System uptime at kernel panic in nanoseconds +* _process_events.uptime_ - Time of execution in system uptime +* _process_file_events.uptime_ - Time of execution in system uptime +* _seccomp_events.uptime_ - Time of execution in system uptime +* _selinux_events.uptime_ - Time of execution in system uptime +* _socket_events.uptime_ - Time of execution in system uptime +* _user_events.uptime_ - Time of execution in system uptime + +*url* - keyword, text.text + +* _curl.url_ - The url for the request +* _lxd_cluster_members.url_ - URL of the node + +*usb_address* - keyword, number.long + +* _usb_devices.usb_address_ - USB Device used address + +*usb_port* - keyword, number.long + +* _usb_devices.usb_port_ - USB Device used port + +*use* - keyword, text.text + +* _memory_arrays.use_ - Function for which the array is used +* _portage_use.use_ - USE flag which has been enabled for package + +*used_by* - keyword, text.text + +* _kernel_modules.used_by_ - Module reverse dependencies +* _lxd_networks.used_by_ - URLs for containers using this network + +*user* - keyword + +* _cpu_time.user_ - Time spent in user mode +* _cups_jobs.user_ - The user who printed the job +* _docker_container_processes.user_ - User name +* _logged_in_users.user_ - User login name +* _logon_sessions.user_ - The account name of the security principal that owns the logon session. +* _sandboxes.user_ - Sandbox owner +* _systemd_units.user_ - The configured user, if any + +*user_account* - keyword, text.text + +* _services.user_account_ - The name of the account that the service process will be logged on as when it runs. This name can be of the form Domain\UserName. If the account belongs to the built-in domain, the name can be of the form .\UserName. + +*user_account_control* - keyword, text.text + +* _windows_security_center.user_account_control_ - The health of the User Account Control (UAC) capability in Windows + +*user_action* - keyword, text.text + +* _xprotect_reports.user_action_ - Action taken by user after prompted + +*user_agent* - keyword, text.text + +* _curl.user_agent_ - The user-agent string to use for the request + +*user_capacity* - keyword, text.text + +* _smart_drive_info.user_capacity_ - Bytes of drive capacity + +*user_namespace* - keyword, text.text + +* _docker_containers.user_namespace_ - User namespace +* _process_namespaces.user_namespace_ - user namespace inode + +*user_time* - keyword, number.long + +* _osquery_schedule.user_time_ - Total user time spent executing +* _processes.user_time_ - CPU time in milliseconds spent in user space + +*user_uuid* - keyword, text.text + +* _disk_encryption.user_uuid_ - UUID of authenticated user if available + +*username* - keyword, text.text + +* _certificates.username_ - Username +* _es_process_events.username_ - Username +* _last.username_ - Entry username +* _launchd.username_ - Run this daemon or agent as this username +* _managed_policies.username_ - Policy applies only this user +* _preferences.username_ - (optional) read preferences for a specific user +* _rpm_package_files.username_ - File default username from info DB +* _shadow.username_ - Username +* _startup_items.username_ - The user associated with the startup item +* _suid_bin.username_ - Binary owner username +* _users.username_ - Username +* _windows_crashes.username_ - Username of the user who ran the crashed process + +*uses_pattern* - keyword, number.long + +* _xprotect_entries.uses_pattern_ - Uses a match pattern instead of identity + +*uts_namespace* - keyword, text.text + +* _docker_containers.uts_namespace_ - UTS namespace +* _process_namespaces.uts_namespace_ - uts namespace inode + +*uuid* - keyword, text.text + +* _block_devices.uuid_ - Block device Universally Unique Identifier +* _disk_encryption.uuid_ - Disk Universally Unique Identifier +* _disk_events.uuid_ - UUID of the volume inside DMG if available +* _managed_policies.uuid_ - Optional UUID assigned to policy set +* _osquery_extensions.uuid_ - The transient ID assigned for communication +* _osquery_info.uuid_ - Unique ID provided by the system +* _system_info.uuid_ - Unique ID provided by the system +* _users.uuid_ - User's UUID (Apple) or SID (Windows) + +*vaddr* - keyword, number.long + +* _elf_sections.vaddr_ - Section virtual address in memory +* _elf_segments.vaddr_ - Segment virtual address in memory + +*valid_from* - keyword, text.text + +* _curl_certificate.valid_from_ - Period of validity start date + +*valid_to* - keyword, text.text + +* _curl_certificate.valid_to_ - Period of validity end date + +*value* - keyword, text.text + +* _ad_config.value_ - Variable typed option value +* _augeas.value_ - The value of the configuration item +* _azure_instance_tags.value_ - The tag value +* _cpuid.value_ - Bit value or string +* _default_environment.value_ - Value of the environment variable +* _docker_container_labels.value_ - Optional label value +* _docker_image_labels.value_ - Optional label value +* _docker_network_labels.value_ - Optional label value +* _docker_volume_labels.value_ - Optional label value +* _ec2_instance_tags.value_ - Tag value +* _elf_dynamic.value_ - Tag value +* _extended_attributes.value_ - The parsed information from the attribute +* _launchd_overrides.value_ - Overridden value +* _lxd_instance_config.value_ - Configuration parameter value +* _lxd_instance_devices.value_ - Device info param value +* _managed_policies.value_ - Policy value +* _mdls.value_ - Value stored in the metadata key +* _nvram.value_ - Raw variable data +* _oem_strings.value_ - The value of the OEM string +* _osquery_flags.value_ - Flag value +* _plist.value_ - String value of most CF types +* _power_sensors.value_ - Power in Watts +* _preferences.value_ - String value of most CF types +* _process_envs.value_ - Environment variable value +* _selinux_settings.value_ - Active value. +* _smc_keys.value_ - A type-encoded representation of the key value +* _wmi_bios_info.value_ - Value of the Bios setting + +*valuetype* - keyword, text.text + +* _mdls.valuetype_ - CoreFoundation type of data stored in value + +*variable* - keyword, text.text + +* _default_environment.variable_ - Name of the environment variable + +*vbs_status* - keyword, text.text + +* _hvci_status.vbs_status_ - The status of the virtualization based security settings. Returns UNKNOWN if an error is encountered. + +*vendor* - keyword, text.text + +* _block_devices.vendor_ - Block device vendor string +* _disk_events.vendor_ - Disk event vendor string +* _hardware_events.vendor_ - Hardware device vendor +* _pci_devices.vendor_ - PCI Device vendor +* _platform_info.vendor_ - Platform code vendor +* _rpm_packages.vendor_ - Package vendor +* _usb_devices.vendor_ - USB Device vendor string + +*vendor_id* - keyword, text.text + +* _hardware_events.vendor_id_ - Hex encoded Hardware vendor identifier +* _pci_devices.vendor_id_ - Hex encoded PCI Device vendor identifier +* _usb_devices.vendor_id_ - Hex encoded USB Device vendor identifier + +*vendor_syndrome* - keyword, text.text + +* _memory_error_info.vendor_syndrome_ - Vendor specific ECC syndrome or CRC data associated with the erroneous access + +*version* - keyword, text.text + +* _alf.version_ - Application Layer Firewall version +* _apt_sources.version_ - Repository source version +* _atom_packages.version_ - Package supplied version +* _authorizations.version_ - Label top-level key +* _azure_instance_metadata.version_ - Version of the VM image +* _bitlocker_info.version_ - The FVE metadata version of the drive. +* _browser_plugins.version_ - Plugin short version +* _chocolatey_packages.version_ - Package-supplied version +* _chrome_extension_content_scripts.version_ - Extension-supplied version +* _chrome_extensions.version_ - Extension-supplied version +* _crashes.version_ - Version info of the crashed process +* _curl_certificate.version_ - Version Number +* _deb_packages.version_ - Package version +* _device_firmware.version_ - Firmware version +* _docker_version.version_ - Docker version +* _drivers.version_ - Driver version +* _elf_info.version_ - Object file version +* _es_process_events.version_ - Version of EndpointSecurity event +* _firefox_addons.version_ - Addon-supplied version string +* _gatekeeper.version_ - Version of Gatekeeper's gke.bundle +* _homebrew_packages.version_ - Current 'linked' version +* _hvci_status.version_ - The version number of the Device Guard build. +* _ie_extensions.version_ - Version of the executable +* _intel_me_info.version_ - Intel ME version +* _kernel_extensions.version_ - Extension version +* _kernel_info.version_ - Kernel version +* _npm_packages.version_ - Package supplied version +* _office_mru.version_ - Office application version number +* _os_version.version_ - Pretty, suitable for presentation, OS version +* _osquery_extensions.version_ - Extension's version +* _osquery_info.version_ - osquery toolkit version +* _osquery_packs.version_ - Minimum osquery version that this query will run on +* _package_install_history.version_ - Package display version +* _package_receipts.version_ - Installed package version +* _pkg_packages.version_ - Package version +* _platform_info.version_ - Platform code version +* _portage_keywords.version_ - The version which are affected by the use flags, empty means all +* _portage_packages.version_ - The version which are affected by the use flags, empty means all +* _portage_use.version_ - The version of the installed package +* _programs.version_ - Product version information. +* _python_packages.version_ - Package-supplied version +* _rpm_packages.version_ - Package version +* _safari_extensions.version_ - Extension long version +* _system_extensions.version_ - System extension version +* _usb_devices.version_ - USB Device version number +* _windows_crashes.version_ - File version info of the crashed process + +*video_mode* - keyword, text.text + +* _video_info.video_mode_ - The current resolution of the display. + +*virtual_process* - keyword, number.long + +* _processes.virtual_process_ - Process is virtual (e.g. System, Registry, vmmem) yes=1, no=0 + +*visible* - keyword, number.long + +* _firefox_addons.visible_ - 1 If the addon is shown in browser else 0 + +*visible_alarm* - keyword, text.text + +* _chassis_info.visible_alarm_ - If TRUE, the frame is equipped with a visual alarm. + +*vlans* - keyword, text.text + +* _lldp_neighbors.vlans_ - Comma delimited list of vlan ids + +*vm_id* - keyword, text.text + +* _azure_instance_metadata.vm_id_ - Unique identifier for the VM +* _azure_instance_tags.vm_id_ - Unique identifier for the VM + +*vm_scale_set_name* - keyword, text.text + +* _azure_instance_metadata.vm_scale_set_name_ - VM scale set name + +*vm_size* - keyword, text.text + +* _azure_instance_metadata.vm_size_ - VM size + +*voltage* - keyword, number.long + +* _battery.voltage_ - The battery's current voltage in mV + +*volume_creation* - keyword, text.text + +* _prefetch.volume_creation_ - Volume creation time. + +*volume_id* - keyword, number.long + +* _quicklook_cache.volume_id_ - Parsed volume ID from fs_id + +*volume_serial* - keyword, text.text + +* _file.volume_serial_ - Volume serial number +* _prefetch.volume_serial_ - Volume serial number. +* _shortcut_files.volume_serial_ - Volume serial number. + +*volume_size* - keyword, number.long + +* _platform_info.volume_size_ - (Optional) size of firmware volume + +*wall_time* - keyword, number.long + +* _osquery_schedule.wall_time_ - Total wall time spent executing + +*warning* - keyword, number.long + +* _shadow.warning_ - Number of days before password expires to warn user about it + +*warnings* - keyword, text.text + +* _smart_drive_info.warnings_ - Warning messages from SMART controller + +*watch_paths* - keyword, text.text + +* _launchd.watch_paths_ - Key that launches daemon or agent if path is modified + +*watcher* - keyword, number.long + +* _osquery_info.watcher_ - Process (or thread/handle) ID of optional watcher process + +*weekday* - keyword, text.text + +* _time.weekday_ - Current weekday in the system + +*win32_exit_code* - keyword, number.long + +* _services.win32_exit_code_ - The error code that the service uses to report an error that occurs when it is starting or stopping + +*win_timestamp* - keyword, number.long + +* _time.win_timestamp_ - Timestamp value in 100 nanosecond units. + +*windows_security_center_service* - keyword, text.text + +* _windows_security_center.windows_security_center_service_ - The health of the Windows Security Center Service + +*wired* - keyword, number.long + +* _virtual_memory_info.wired_ - Total number of wired down pages. + +*wired_size* - keyword, number.long + +* _docker_container_processes.wired_size_ - Bytes of unpageable memory used by process +* _processes.wired_size_ - Bytes of unpageable memory used by process + +*working_directory* - keyword, text.text + +* _launchd.working_directory_ - Key used to specify a directory to chdir to before launch + +*working_disks* - keyword, number.long + +* _md_devices.working_disks_ - Number of working disks in array + +*working_path* - keyword, text.text + +* _shortcut_files.working_path_ - Target file directory. + +*world* - keyword, number.long + +* _portage_packages.world_ - If package is in the world file + +*writable* - keyword, number.long + +* _disk_events.writable_ - 1 if writable, 0 if not + +*xpath* - keyword, text.text + +* _windows_eventlog.xpath_ - The custom query to filter events + +*year* - keyword, number.long + +* _time.year_ - Current year in the system + +*zero_fill* - keyword, number.long + +* _virtual_memory_info.zero_fill_ - Total number of zero filled pages. + +*zone* - keyword, text.text + +* _azure_instance_metadata.zone_ - Availability zone of the VM +* _ycloud_instance_metadata.zone_ - Availability zone of the VM + diff --git a/docs/osquery/osquery.asciidoc b/docs/osquery/osquery.asciidoc index ab8058027934e..e450bcbcb7d6f 100644 --- a/docs/osquery/osquery.asciidoc +++ b/docs/osquery/osquery.asciidoc @@ -16,7 +16,7 @@ With Osquery in {kib}, you can: * View a history of past queries and their results * Save queries and build a library of queries for specific use cases -Osquery is powered by the *Osquery Manager* integration. +Osquery in {kib} is powered by the *Osquery Manager* integration. For information on how to set up *Osquery Manager*, refer to <>. [float] @@ -119,6 +119,51 @@ image::images/scheduled-pack.png[Shows queries in the pack and details about eac . View scheduled query results in <> or the drag-and-drop <> editor. +[float] +[[osquery-prebuilt-packs]] +== Prebuilt Elastic packs + +The Osquery Manager integration includes a set of prebuilt Osquery packs that you can optionally load. Once added, you can then activate and schedule the packs. + +You can modify the scheduled agent policies for a prebuilt pack, but you cannot edit queries in the pack. To edit the queries, you must first create a copy of the pack. + +[float] +[[load-prebuilt-packs]] +=== Load and activate prebuilt Elastic packs + +. Go to *Packs*, and then click *Load Elastic prebuilt packs*. ++ +NOTE: This option is only available if new or updated prebuilt packs are available. + +. For each pack that you want to schedule: + +* Enable the option to make the pack *Active*. + +* Click the pack name, then *Edit*. + +* Update the *Scheduled agent policies* to specify the policies where this pack should run. + +. Click *Update pack*. + +[float] +[[copy-prebuilt-packs]] +=== Copy prebuilt Elastic packs + +To modify queries in prebuilt packs, you must first make a copy of the pack. + +. Go to *Stack Management* -> *Saved Objects*. + +. Search for the Osquery packs you want to modify by name. + +. Select the checkboxes of the packs to export. + +. Click *Export x objects*. + +. Click *Import*. + +. Select the import option *Create new objects with random IDs*, then click *Import* to import the pack. This creates a copy of the pack that you can edit. + + [float] [[osquery-manage-query]] == Save queries @@ -157,9 +202,110 @@ To add or edit saved queries from the *Saved queries* tab: . Click *Save* or *Update*. --- -include::advanced-osquery.asciidoc[] +[float] +[[osquery-map-fields]] +== Map result fields to ECS + +When you save queries or add queries to a pack, you can optionally map Osquery results or static values to fields in +the {ecs-ref}/ecs-reference.html[Elastic Common Schema] (ECS). +This standardizes your Osquery data for use across detections, machine learning, +and any other areas that rely on ECS-compliant data. +When the query is run, the results include the original `osquery.` +and the mapped ECS fields. For example, if you update a query to map `osquery.name` to `user.name`, the query results include both fields. + +. Edit saved queries or queries in a pack to map fields: + +* For *Saved queries*: Open the *Saved queries* tab, and then click the edit icon for the query that you want to map. + +* For *packs*: Open the *Packs* tab, edit a pack, and then click the edit icon for the query that you want to map. + +. In the **ECS mapping** section, select an **ECS field** to map. + +. In the **Value** column, use the dropdown on the left to choose what type of value to map to the ECS field: + +** **Osquery value**: Select an Osquery field. The fields available are based on the SQL query entered, and only include fields that the query returns. When the query runs, the ECS field is set dynamically to the value of the Osquery field selected. + +** **Static value**: Enter a static value. When the query runs, the ECS field is set to the value entered. For example, static fields can be used to apply `tags` or your preferred `event.category` to the query results. + +. Map more fields, as needed. To remove any mapped rows, click the delete icon. + +. Save your changes. + +[NOTE] +========================= + +* Some ECS fields are restricted and cannot be mapped. These are not available in the ECS dropdown. + +* Some ECS fields are restricted to a set of allowed values, like {ecs-ref}/ecs-event.html#field-event-category[event.category]. Use the {ecs-ref}/ecs-field-reference.html[ECS Field Reference] for help when mapping fields. + +* Osquery date fields have a variety of data types (including integer, text, or bigint). When mapping an Osquery date field to an ECS date field, you might need to use SQL operators in the query to get an {es}-compatible +{ref}/date.html[date] type. +========================= + + +[float] +[[osquery-extended-tables]] +== Extended tables for Kubernetes queries +In addition to the Osquery schema, the Elastic-provided version of Osquery also includes the following tables to support Kubernetes containers. These can be queried with live or scheduled queries. + +* `host_users` + +* `host_groups` + +* `host_processes` + +When querying these tables, the expectation is that the `/etc/passwd`, `/etc/group`, and `/proc` are available in the container under `/hostfs` as: +`/hostfs/etc/passwd`, `/hostfs/etc/group`, and `/hostfs/proc`. For information about the fields available in these tables, see the +https://docs.elastic.co/en/integrations/osquery_manager#exported-fields[exported fields] reference. + +[float] +[[osquery-status]] +== Osquery status + +A query can have the following status: + +[cols="2*<"] +|=== +| Successful | The query successfully completed. +| Failed | The query encountered a problem, such as an issue with the query or the agent was disconnected, and might have failed. +| Not yet responded | The query has not been sent to the agent. +| Expired | The action request timed out. The agent may be offline. +|=== + +NOTE: If an agent is offline, the request status remains **pending** as {kib} retries the request. +By default, a query request times out after five minutes. The time out applies to the time it takes +to deliver the action request to an agent to run a query. If the action completes after the timeout period, +the results are still returned. + + +[float] +[[osquery-results]] +== Osquery results +When you run live or scheduled queries, the results are automatically +stored in an {es} index, so that you can search, analyze, and visualize this data in {kib}. +For a list of the Osquery fields that can be returned in query results, +refer to https://docs.elastic.co/en/integrations/osquery_manager#exported-fields[exported fields]. +Query results can also include ECS fields, if the query has a defined ECS mapping. + +Osquery responses include the following information: + +* Everything prefaced with `osquery.` is part of the query response. These fields are not mapped to ECS by default. + +* Results include some ECS fields by default, such as `host.*` and `agent.*`, which provide information about the host that was queried. + +* For live queries, the `action_data.query` is the query that was sent. + +* For scheduled queries in a pack, the `action_id` has the format `pack__`. You can use this information to look up the query that was run. + +* By default, all query results are https://osquery.readthedocs.io/en/stable/deployment/logging/#snapshot-logs[snapshot logs] +that represent a point in time with a set of results, with no +https://osquery.readthedocs.io/en/stable/deployment/logging/#differential-logs[differentials]. + +* Osquery data is stored in the `logs-osquery_manager.result-` datastream, and the result row data is under the `osquery` property in the document. + + +-- include::manage-integration.asciidoc[] diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc index 9a96eec55db32..934ebfc3e4b08 100644 --- a/docs/redirects.asciidoc +++ b/docs/redirects.asciidoc @@ -410,3 +410,8 @@ This page has been deleted. Refer to <>. == View a document This page has been deleted. Refer to <>. + +[role="exclude",id="advanced-osquery"] +== Advanced Osquery + +This page has been deleted. Refer to <>. From e615007273e9d3204c448058f29e182eec817202 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Wed, 27 Apr 2022 17:06:15 -0500 Subject: [PATCH 49/78] Upgrade EUI to v55.0.1 (#130593) * eui to v55.0.0 * remove jest polyfill * i18n tokens * hasFocus prop * eui to v55.0.1 * removed obsolete mixin arg * i18n snapshots * hasFocus snapshots * formRow snapshots * euiAvatar snapshots * euiContextMenu snapshot * update focus * hasFocus snapshots * [Security] wait for popover panel focus before checking for field input focus - possible race condition failure where cypress is clicking the input too quickly and the popover is stealing focus back onto the panel * Fix ML focus issue in Filter settings * Fix jest tests * [ML] Fix stale / loading EuiContextMenuPanel item * Fix ML watched props for EuiContextMenuPanel Co-authored-by: Constance Chen Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Quynh Nguyen --- package.json | 2 +- .../src/jest/setup/polyfills.jsdom.js | 4 - .../__snapshots__/i18n_service.test.tsx.snap | 2 + src/core/public/i18n/i18n_eui_mapping.tsx | 6 + src/dev/license_checker/config.ts | 2 +- .../__snapshots__/indices_list.test.tsx.snap | 5 - .../url/__snapshots__/url.test.tsx.snap | 10 +- .../__snapshots__/cron_editor.test.tsx.snap | 30 +- .../public/typeahead/_suggestion.scss | 2 +- .../extended_template.stories.storyshot | 4 +- .../custom_element_modal.stories.storyshot | 24 +- .../__snapshots__/edit_var.stories.storyshot | 24 +- .../extended_template.stories.storyshot | 42 +- .../extended_template.stories.storyshot | 8 +- .../__snapshots__/settings.test.tsx.snap | 574 +++++++++--------- .../autoplay_settings.stories.storyshot | 6 +- .../components/anomalies_table/links_menu.tsx | 2 +- .../add_item_popover.test.js.snap | 6 + .../add_item_popover/add_item_popover.js | 2 + .../edit_description_popover.test.js.snap | 6 + .../edit_description_popover.js | 2 + .../spaces_popover_list.tsx | 1 - .../nav_control/nav_control_service.test.ts | 2 +- .../cypress/tasks/timeline.ts | 2 + .../__snapshots__/index.test.tsx.snap | 1 - .../__snapshots__/index.test.tsx.snap | 1 - .../context_menu_with_router_support.tsx | 6 +- .../components/resolve_all_conflicts.test.tsx | 1 - .../nav_control/components/spaces_menu.tsx | 1 - .../add_filter_btn.test.tsx | 3 - yarn.lock | 18 +- 31 files changed, 396 insertions(+), 403 deletions(-) diff --git a/package.json b/package.json index d6d4a2225a08c..620e22de5ec1f 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.2.0-canary.2", "@elastic/ems-client": "8.3.0", - "@elastic/eui": "54.0.0", + "@elastic/eui": "55.0.1", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", diff --git a/packages/kbn-test/src/jest/setup/polyfills.jsdom.js b/packages/kbn-test/src/jest/setup/polyfills.jsdom.js index ebe6178dbdd91..5857ca9b99b8a 100644 --- a/packages/kbn-test/src/jest/setup/polyfills.jsdom.js +++ b/packages/kbn-test/src/jest/setup/polyfills.jsdom.js @@ -14,7 +14,3 @@ require('whatwg-fetch'); if (!global.URL.hasOwnProperty('createObjectURL')) { Object.defineProperty(global.URL, 'createObjectURL', { value: () => '' }); } - -// Will be replaced with a better solution in EUI -// https://github.com/elastic/eui/issues/3713 -global._isJest = true; diff --git a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap index 69439d1927745..a3e3ca7a7c207 100644 --- a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap +++ b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap @@ -141,6 +141,8 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiImage.openImage": [Function], "euiLink.external.ariaLabel": "External link", "euiLink.newTarget.screenReaderOnlyText": "(opens in a new tab or window)", + "euiMark.highlightEnd": "highlight end", + "euiMark.highlightStart": "highlight start", "euiMarkdownEditorFooter.closeButton": "Close", "euiMarkdownEditorFooter.errorsTitle": "Errors", "euiMarkdownEditorFooter.mdSyntaxLink": "GitHub flavored markdown", diff --git a/src/core/public/i18n/i18n_eui_mapping.tsx b/src/core/public/i18n/i18n_eui_mapping.tsx index 9969f4ee23f57..5344fddc4fe2e 100644 --- a/src/core/public/i18n/i18n_eui_mapping.tsx +++ b/src/core/public/i18n/i18n_eui_mapping.tsx @@ -618,6 +618,12 @@ export const getEuiContextMapping = (): EuiTokensObject => { defaultMessage: '(opens in a new tab or window)', } ), + 'euiMark.highlightStart': i18n.translate('core.euiMark.highlightStart', { + defaultMessage: 'highlight start', + }), + 'euiMark.highlightEnd': i18n.translate('core.euiMark.highlightEnd', { + defaultMessage: 'highlight end', + }), 'euiMarkdownEditorFooter.closeButton': i18n.translate( 'core.euiMarkdownEditorFooter.closeButton', { diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 1cfcb7b5cf284..bd5fd75e30998 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -77,6 +77,6 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@8.3.0': ['Elastic License 2.0'], - '@elastic/eui@54.0.0': ['SSPL-1.0 OR Elastic License 2.0'], + '@elastic/eui@55.0.1': ['SSPL-1.0 OR Elastic License 2.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry }; diff --git a/src/plugins/data_view_editor/public/components/preview_panel/indices_list/__snapshots__/indices_list.test.tsx.snap b/src/plugins/data_view_editor/public/components/preview_panel/indices_list/__snapshots__/indices_list.test.tsx.snap index 74d803a6ff176..b495cf52657d6 100644 --- a/src/plugins/data_view_editor/public/components/preview_panel/indices_list/__snapshots__/indices_list.test.tsx.snap +++ b/src/plugins/data_view_editor/public/components/preview_panel/indices_list/__snapshots__/indices_list.test.tsx.snap @@ -65,7 +65,6 @@ exports[`IndicesList should change pages 1`] = ` panelPaddingSize="none" >
@@ -1663,7 +1663,7 @@ exports[`CronEditor is rendered with a HOUR frequency 1`] = ` labelType="label" >
@@ -2860,7 +2860,7 @@ exports[`CronEditor is rendered with a MINUTE frequency 1`] = ` labelType="label" >
@@ -4195,7 +4195,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = ` labelType="label" >
@@ -5287,7 +5287,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = ` labelType="label" >
@@ -6078,7 +6078,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = ` labelType="label" >
@@ -7170,7 +7170,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = ` labelType="label" >
@@ -8160,7 +8160,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = ` labelType="label" >
@@ -8610,7 +8610,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = ` labelType="label" >
diff --git a/src/plugins/unified_search/public/typeahead/_suggestion.scss b/src/plugins/unified_search/public/typeahead/_suggestion.scss index 9e0c163f9c94c..e466a52e7fc10 100644 --- a/src/plugins/unified_search/public/typeahead/_suggestion.scss +++ b/src/plugins/unified_search/public/typeahead/_suggestion.scss @@ -18,7 +18,7 @@ $kbnTypeaheadTypes: ( } .kbnTypeahead__popover--bottom { - @include euiBottomShadow($adjustBorders: true); + @include euiBottomShadow; border-bottom-left-radius: $euiBorderRadius; border-bottom-right-radius: $euiBorderRadius; } diff --git a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot index 24d6e94de0ec2..beab512ea62e1 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot +++ b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot @@ -11,7 +11,7 @@ exports[`Storyshots arguments/AxisConfig extended 1`] = ` } >
can navigate Autoplay Settings 1`] = `
-
- - + -
+ + +
@@ -143,54 +141,52 @@ exports[` can navigate Autoplay Settings 2`] = `
-
- - + -
+ + +
can navigate Autoplay Settings 2`] = `
-
+
-
- - - Cycle Slides -
-
-
+ + + Cycle Slides + +
+
+ +
-
- -
+ Set a custom interval + +
+
-
- -
-
-
- Use shorthand notation, like 30s, 10m, or 1h +
+
+ Use shorthand notation, like 30s, 10m, or 1h +
+
+
-
- -
+ +
- -
+
+
@@ -398,54 +392,52 @@ exports[` can navigate Toolbar Settings, closes when activated 1`] =
-
- - + -
+ + +
@@ -498,54 +490,52 @@ exports[` can navigate Toolbar Settings, closes when activated 2`] =
-
- - + -
+ + +
can navigate Toolbar Settings, closes when activated 2`] =
-
+
-
- - - Hide Toolbar -
-
+ - Hide the toolbar when the mouse is not within the Canvas? -
+ Hide Toolbar + +
+
+ Hide the toolbar when the mouse is not within the Canvas?
@@ -643,4 +631,4 @@ exports[` can navigate Toolbar Settings, closes when activated 2`] =
`; -exports[` can navigate Toolbar Settings, closes when activated 3`] = `"

You are in a dialog. To close this dialog, hit escape.

Settings
Hide Toolbar
Hide the toolbar when the mouse is not within the Canvas?
"`; +exports[` can navigate Toolbar Settings, closes when activated 3`] = `"

You are in a dialog. To close this dialog, hit escape.

Settings
Hide Toolbar
Hide the toolbar when the mouse is not within the Canvas?
"`; diff --git a/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__stories__/__snapshots__/autoplay_settings.stories.storyshot b/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__stories__/__snapshots__/autoplay_settings.stories.storyshot index e803600fe8d51..df3d090de3132 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__stories__/__snapshots__/autoplay_settings.stories.storyshot +++ b/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__stories__/__snapshots__/autoplay_settings.stories.storyshot @@ -82,7 +82,7 @@ exports[`Storyshots shareables/Footer/Settings/AutoplaySettings component: off, className="euiFlexItem" >
{ return ( ); diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap b/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap index 969406724537d..45d9296f1752e 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap @@ -25,6 +25,7 @@ exports[`AddItemPopover calls addItems with multiple items on clicking Add butto display="inlineBlock" hasArrow={true} id="add_item_popover" + initialFocus="#filter_list_add_item_input_row" isOpen={false} ownFocus={true} panelClassName="ml-add-filter-item-popover" @@ -37,6 +38,7 @@ exports[`AddItemPopover calls addItems with multiple items on clicking Add butto fullWidth={false} hasChildLabel={true} hasEmptyLabelSpace={false} + id="filter_list_add_item_input_row" label={
{ key={`spcMenuList`} data-search-term={searchTerm} className="spcMenu__spacesList" - hasFocus={this.state.allowSpacesListFocus} initialFocusedItemIndex={this.state.allowSpacesListFocus ? 0 : undefined} items={items} /> diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts b/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts index 3e871c4979804..c801a1f8f4e1b 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts @@ -85,7 +85,7 @@ describe('SecurityNavControlService', () => { >