From dedcce934948165cd9b971c479b0e2b02ccac1bb Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Wed, 7 Oct 2020 10:35:15 -0400 Subject: [PATCH 01/32] set initialWidth for confusion matrix dataGrid columns (#79790) --- .../classification_exploration/column_data.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx index 64fdf161b56150..ef86569bda63de 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx @@ -15,6 +15,8 @@ import { } from '@elastic/eui'; import { ConfusionMatrix } from '../../../../common/analytics'; +const COL_INITIAL_WIDTH = 165; // in pixels + interface ColumnData { actual_class: string; actual_class_doc_count: number; @@ -27,10 +29,11 @@ export const MAX_COLUMNS = 6; export function getColumnData(confusionMatrixData: ConfusionMatrix[]) { const colData: Partial = []; - const columns: Array<{ id: string; display?: any }> = [ + const columns: Array<{ id: string; display?: any; initialWidth?: number }> = [ { id: ACTUAL_CLASS_ID, display: , + initialWidth: COL_INITIAL_WIDTH, }, ]; @@ -51,7 +54,7 @@ export function getColumnData(confusionMatrixData: ConfusionMatrix[]) { const predictedClasses = classData.predicted_classes || []; - columns.push({ id: classData.actual_class }); + columns.push({ id: classData.actual_class, initialWidth: COL_INITIAL_WIDTH }); for (let i = 0; i < predictedClasses.length; i++) { const predictedClass = predictedClasses[i].predicted_class; @@ -63,7 +66,7 @@ export function getColumnData(confusionMatrixData: ConfusionMatrix[]) { }); if (showOther) { - columns.push({ id: OTHER_CLASS_ID }); + columns.push({ id: OTHER_CLASS_ID, initialWidth: COL_INITIAL_WIDTH }); } return { columns, columnData: colData }; From 567100d3b8966e4b86703c59ac18942d6c6006ae Mon Sep 17 00:00:00 2001 From: IgorG <56408662+IgorGuz2000@users.noreply.github.com> Date: Wed, 7 Oct 2020 07:56:09 -0700 Subject: [PATCH 02/32] Resolver Tree Events tests (#79344) * commiting change for the dismiss Banner * Change comments * Change timeout and gziped data file * Fixed banner list fail * Moved dismiss Banner code to the common_page.ts * Remove find from host_page * Remove comments from host_page * Added Expected data to the related Evens Renamed tests from Child events to Related Events Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../overlays/banners/banners_list.test.tsx | 4 +- .../public/overlays/banners/banners_list.tsx | 7 +- test/functional/page_objects/common_page.ts | 10 + .../resolver_tree/events/data.json.gz | Bin 0 -> 9635 bytes .../{ => functions}/data.json.gz | Bin .../apps/endpoint/resolver.ts | 365 ++++++++++-------- .../page_objects/hosts_page.ts | 43 +++ 7 files changed, 274 insertions(+), 155 deletions(-) create mode 100644 x-pack/test/functional/es_archives/endpoint/resolver_tree/events/data.json.gz rename x-pack/test/functional/es_archives/endpoint/resolver_tree/{ => functions}/data.json.gz (100%) diff --git a/src/core/public/overlays/banners/banners_list.test.tsx b/src/core/public/overlays/banners/banners_list.test.tsx index dbee20790fa94e..3850a88699d90e 100644 --- a/src/core/public/overlays/banners/banners_list.test.tsx +++ b/src/core/public/overlays/banners/banners_list.test.tsx @@ -43,7 +43,7 @@ describe('BannersList', () => { ]); expect(mount().html()).toMatchInlineSnapshot( - `"

Hello!

"` + `"

Hello!

"` ); }); @@ -85,7 +85,7 @@ describe('BannersList', () => { // Two new banners should be rendered expect(component.html()).toMatchInlineSnapshot( - `"

First Banner!

Second banner!

"` + `"

First Banner!

Second banner!

"` ); // Original banner should be unmounted expect(unmount).toHaveBeenCalled(); diff --git a/src/core/public/overlays/banners/banners_list.tsx b/src/core/public/overlays/banners/banners_list.tsx index 6503af985f9c85..30acf6abff70b4 100644 --- a/src/core/public/overlays/banners/banners_list.tsx +++ b/src/core/public/overlays/banners/banners_list.tsx @@ -59,6 +59,11 @@ const BannerItem: React.FunctionComponent<{ banner: OverlayBanner }> = ({ banner useEffect(() => banner.mount(element.current!), [banner]); // Only unmount / remount if banner object changed. return ( -
+
); }; diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index 41667e1f26c8e1..cc229ef0c2e08a 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -505,6 +505,16 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo async scrollKibanaBodyTop() { await browser.setScrollToById('kibana-body', 0, 0); } + + /** + * Dismiss Banner if available. + */ + async dismissBanner() { + if (await testSubjects.exists('global-banner-item')) { + const button = await find.byButtonText('Dismiss'); + await button.click(); + } + } } return new CommonPage(); diff --git a/x-pack/test/functional/es_archives/endpoint/resolver_tree/events/data.json.gz b/x-pack/test/functional/es_archives/endpoint/resolver_tree/events/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..c4c8e3d9a5b480a56b55cf8fec1bd7f0dc5d4bf4 GIT binary patch literal 9635 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 { - await esArchiver.load('endpoint/resolver_tree', { useCreate: true }); await pageObjects.hosts.navigateToSecurityHostsPage(); + await pageObjects.common.dismissBanner(); const fromTime = 'Jan 1, 2018 @ 00:00:00.000'; const toTime = 'now'; await pageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - await queryBar.setQuery('event.dataset : endpoint.events.file'); - await queryBar.submitQuery(); - await browser.refresh(); await browser.setWindowSize(1800, 1200); - await testSubjects.click('draggable-content-host.name'); - await testSubjects.existOrFail('header-page-title'); - await testSubjects.click('navigation-events'); - await testSubjects.existOrFail('events-viewer-panel'); - await testSubjects.exists('investigate-in-resolver-button', { timeout: 4000 }); - await (await testSubjects.findAll('investigate-in-resolver-button'))[0].click(); }); + describe('Endpoint Resolver Tree', function () { + before(async () => { + await esArchiver.load('empty_kibana'); + await esArchiver.load('endpoint/resolver_tree/functions', { useCreate: true }); + await pageObjects.hosts.navigateToEventsPanel(); + await pageObjects.hosts.executeQueryAndOpenResolver('event.dataset : endpoint.events.file'); + }); + after(async () => { + await pageObjects.hosts.deleteDataStreams(); + }); - after(async () => { - await pageObjects.hosts.deleteDataStreams(); - }); + it('check that Resolver and Data table is loaded', async () => { + await testSubjects.existOrFail('resolver:graph'); + await testSubjects.existOrFail('tableHeaderCell_name_0'); + await testSubjects.existOrFail('tableHeaderCell_timestamp_1'); + }); - it('check that Resolver and Data table is loaded', async () => { - await testSubjects.existOrFail('resolver:graph'); - await testSubjects.existOrFail('tableHeaderCell_name_0'); - await testSubjects.existOrFail('tableHeaderCell_timestamp_1'); - }); + it('compare resolver Nodes Table data and Data length', async () => { + const nodeData: string[] = []; + const TableData: string[] = []; - it('compare resolver Nodes Table data and Data length', async () => { - const nodeData: string[] = []; - const TableData: string[] = []; - - const Table = await testSubjects.findAll('resolver:node-list:node-link:title'); - for (const value of Table) { - const text = await value._webElement.getText(); - TableData.push(text.split('\n')[0]); - } - await (await testSubjects.find('resolver:graph-controls:zoom-out')).click(); - const Nodes = await testSubjects.findAll('resolver:node:primary-button'); - for (const value of Nodes) { - nodeData.push(await value._webElement.getText()); - } - for (let i = 0; i < nodeData.length; i++) { - expect(TableData[i]).to.eql(nodeData[i]); - } - expect(nodeData.length).to.eql(TableData.length); - await (await testSubjects.find('resolver:graph-controls:zoom-in')).click(); - }); + const Table = await testSubjects.findAll('resolver:node-list:node-link:title'); + for (const value of Table) { + const text = await value._webElement.getText(); + TableData.push(text.split('\n')[0]); + } + await (await testSubjects.find('resolver:graph-controls:zoom-out')).click(); + const Nodes = await testSubjects.findAll('resolver:node:primary-button'); + for (const value of Nodes) { + nodeData.push(await value._webElement.getText()); + } + for (let i = 0; i < nodeData.length; i++) { + expect(TableData[i]).to.eql(nodeData[i]); + } + expect(nodeData.length).to.eql(TableData.length); + await (await testSubjects.find('resolver:graph-controls:zoom-in')).click(); + }); - it('resolver Nodes navigation Up', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await (await testSubjects.find('resolver:graph-controls:north-button')).click(); + it('resolver Nodes navigation Up', async () => { + const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); + await (await testSubjects.find('resolver:graph-controls:north-button')).click(); - const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); - for (let i = 0; i < OriginalNodeDataStyle.length; i++) { - expect(parseFloat(OriginalNodeDataStyle[i].top)).to.lessThan( - parseFloat(NewNodeDataStyle[i].top) - ); - expect(parseFloat(OriginalNodeDataStyle[i].left)).to.equal( - parseFloat(NewNodeDataStyle[i].left) - ); - } - await (await testSubjects.find('resolver:graph-controls:center-button')).click(); - }); + const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); + for (let i = 0; i < OriginalNodeDataStyle.length; i++) { + expect(parseFloat(OriginalNodeDataStyle[i].top)).to.lessThan( + parseFloat(NewNodeDataStyle[i].top) + ); + expect(parseFloat(OriginalNodeDataStyle[i].left)).to.equal( + parseFloat(NewNodeDataStyle[i].left) + ); + } + await (await testSubjects.find('resolver:graph-controls:center-button')).click(); + }); - it('resolver Nodes navigation Down', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await (await testSubjects.find('resolver:graph-controls:south-button')).click(); + it('resolver Nodes navigation Down', async () => { + const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); + await (await testSubjects.find('resolver:graph-controls:south-button')).click(); - const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); - for (let i = 0; i < NewNodeDataStyle.length; i++) { - expect(parseFloat(NewNodeDataStyle[i].top)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].top) - ); - expect(parseFloat(OriginalNodeDataStyle[i].left)).to.equal( - parseFloat(NewNodeDataStyle[i].left) - ); - } - await (await testSubjects.find('resolver:graph-controls:center-button')).click(); - }); + const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); + for (let i = 0; i < NewNodeDataStyle.length; i++) { + expect(parseFloat(NewNodeDataStyle[i].top)).to.lessThan( + parseFloat(OriginalNodeDataStyle[i].top) + ); + expect(parseFloat(OriginalNodeDataStyle[i].left)).to.equal( + parseFloat(NewNodeDataStyle[i].left) + ); + } + await (await testSubjects.find('resolver:graph-controls:center-button')).click(); + }); - it('resolver Nodes navigation Left', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await (await testSubjects.find('resolver:graph-controls:east-button')).click(); + it('resolver Nodes navigation Left', async () => { + const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); + await (await testSubjects.find('resolver:graph-controls:east-button')).click(); - const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); - for (let i = 0; i < OriginalNodeDataStyle.length; i++) { - expect(parseFloat(NewNodeDataStyle[i].left)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].left) - ); - expect(parseFloat(NewNodeDataStyle[i].top)).to.equal( - parseFloat(OriginalNodeDataStyle[i].top) - ); - } - await (await testSubjects.find('resolver:graph-controls:center-button')).click(); - }); + const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); + for (let i = 0; i < OriginalNodeDataStyle.length; i++) { + expect(parseFloat(NewNodeDataStyle[i].left)).to.lessThan( + parseFloat(OriginalNodeDataStyle[i].left) + ); + expect(parseFloat(NewNodeDataStyle[i].top)).to.equal( + parseFloat(OriginalNodeDataStyle[i].top) + ); + } + await (await testSubjects.find('resolver:graph-controls:center-button')).click(); + }); - it('resolver Nodes navigation Right', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await testSubjects.click('resolver:graph-controls:west-button'); - const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); - for (let i = 0; i < NewNodeDataStyle.length; i++) { - expect(parseFloat(OriginalNodeDataStyle[i].left)).to.lessThan( - parseFloat(NewNodeDataStyle[i].left) - ); - expect(parseFloat(NewNodeDataStyle[i].top)).to.equal( - parseFloat(OriginalNodeDataStyle[i].top) - ); - } - await (await testSubjects.find('resolver:graph-controls:center-button')).click(); - }); + it('resolver Nodes navigation Right', async () => { + const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); + await testSubjects.click('resolver:graph-controls:west-button'); + const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); + for (let i = 0; i < NewNodeDataStyle.length; i++) { + expect(parseFloat(OriginalNodeDataStyle[i].left)).to.lessThan( + parseFloat(NewNodeDataStyle[i].left) + ); + expect(parseFloat(NewNodeDataStyle[i].top)).to.equal( + parseFloat(OriginalNodeDataStyle[i].top) + ); + } + await (await testSubjects.find('resolver:graph-controls:center-button')).click(); + }); - it('resolver Nodes navigation Center', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await (await testSubjects.find('resolver:graph-controls:east-button')).click(); - await (await testSubjects.find('resolver:graph-controls:south-button')).click(); + it('resolver Nodes navigation Center', async () => { + const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); + await (await testSubjects.find('resolver:graph-controls:east-button')).click(); + await (await testSubjects.find('resolver:graph-controls:south-button')).click(); - const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); - for (let i = 0; i < NewNodeDataStyle.length; i++) { - expect(parseFloat(NewNodeDataStyle[i].left)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].left) - ); - expect(parseFloat(NewNodeDataStyle[i].top)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].top) - ); - } - await (await testSubjects.find('resolver:graph-controls:center-button')).click(); - const CenterNodeDataStyle = await pageObjects.hosts.parseStyles(); + const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); + for (let i = 0; i < NewNodeDataStyle.length; i++) { + expect(parseFloat(NewNodeDataStyle[i].left)).to.lessThan( + parseFloat(OriginalNodeDataStyle[i].left) + ); + expect(parseFloat(NewNodeDataStyle[i].top)).to.lessThan( + parseFloat(OriginalNodeDataStyle[i].top) + ); + } + await (await testSubjects.find('resolver:graph-controls:center-button')).click(); + const CenterNodeDataStyle = await pageObjects.hosts.parseStyles(); - for (let i = 0; i < CenterNodeDataStyle.length; i++) { - expect(parseFloat(CenterNodeDataStyle[i].left)).to.equal( - parseFloat(OriginalNodeDataStyle[i].left) - ); - expect(parseFloat(CenterNodeDataStyle[i].top)).to.equal( - parseFloat(OriginalNodeDataStyle[i].top) - ); - } - }); + for (let i = 0; i < CenterNodeDataStyle.length; i++) { + expect(parseFloat(CenterNodeDataStyle[i].left)).to.equal( + parseFloat(OriginalNodeDataStyle[i].left) + ); + expect(parseFloat(CenterNodeDataStyle[i].top)).to.equal( + parseFloat(OriginalNodeDataStyle[i].top) + ); + } + }); - it('resolver Nodes navigation zoom in', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await (await testSubjects.find('resolver:graph-controls:zoom-in')).click(); + it('resolver Nodes navigation zoom in', async () => { + const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); + await (await testSubjects.find('resolver:graph-controls:zoom-in')).click(); - const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); - for (let i = 1; i < NewNodeDataStyle.length; i++) { - expect(parseFloat(NewNodeDataStyle[i].left)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].left) - ); - expect(parseFloat(NewNodeDataStyle[i].top)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].top) - ); - expect(parseFloat(OriginalNodeDataStyle[i].width)).to.lessThan( - parseFloat(NewNodeDataStyle[i].width) - ); - expect(parseFloat(OriginalNodeDataStyle[i].height)).to.lessThan( - parseFloat(NewNodeDataStyle[i].height) - ); + const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); + for (let i = 1; i < NewNodeDataStyle.length; i++) { + expect(parseFloat(NewNodeDataStyle[i].left)).to.lessThan( + parseFloat(OriginalNodeDataStyle[i].left) + ); + expect(parseFloat(NewNodeDataStyle[i].top)).to.lessThan( + parseFloat(OriginalNodeDataStyle[i].top) + ); + expect(parseFloat(OriginalNodeDataStyle[i].width)).to.lessThan( + parseFloat(NewNodeDataStyle[i].width) + ); + expect(parseFloat(OriginalNodeDataStyle[i].height)).to.lessThan( + parseFloat(NewNodeDataStyle[i].height) + ); + await (await testSubjects.find('resolver:graph-controls:zoom-out')).click(); + } + }); + + it('resolver Nodes navigation zoom out', async () => { + const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); await (await testSubjects.find('resolver:graph-controls:zoom-out')).click(); - } + const NewNodeDataStyle1 = await pageObjects.hosts.parseStyles(); + for (let i = 1; i < OriginalNodeDataStyle.length; i++) { + expect(parseFloat(OriginalNodeDataStyle[i].left)).to.lessThan( + parseFloat(NewNodeDataStyle1[i].left) + ); + expect(parseFloat(OriginalNodeDataStyle[i].top)).to.lessThan( + parseFloat(NewNodeDataStyle1[i].top) + ); + expect(parseFloat(NewNodeDataStyle1[i].width)).to.lessThan( + parseFloat(OriginalNodeDataStyle[i].width) + ); + expect(parseFloat(NewNodeDataStyle1[i].height)).to.lessThan( + parseFloat(OriginalNodeDataStyle[i].height) + ); + } + await (await testSubjects.find('resolver:graph-controls:zoom-in')).click(); + }); + + it('Check Child events for event.file Node', async () => { + const expectedData = [ + '17 authentication', + '1 registry', + '17 session', + '8 file', + '1 registry', + ]; + await pageObjects.hosts.runNodeEvents(expectedData); + }); }); - it('resolver Nodes navigation zoom out', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await (await testSubjects.find('resolver:graph-controls:zoom-out')).click(); - const NewNodeDataStyle1 = await pageObjects.hosts.parseStyles(); - for (let i = 1; i < OriginalNodeDataStyle.length; i++) { - expect(parseFloat(OriginalNodeDataStyle[i].left)).to.lessThan( - parseFloat(NewNodeDataStyle1[i].left) + describe('Resolver Tree events', function () { + const expectedData = [ + '17 authentication', + '1 registry', + '17 session', + '80 registry', + '8 network', + '60 registry', + ]; + before(async () => { + await esArchiver.load('empty_kibana'); + await esArchiver.load('endpoint/resolver_tree/events', { useCreate: true }); + await queryBar.setQuery(''); + await queryBar.submitQuery(); + }); + after(async () => { + await pageObjects.hosts.deleteDataStreams(); + }); + + it('Check Child events for event.process Node', async () => { + await pageObjects.hosts.navigateToEventsPanel(); + await pageObjects.hosts.executeQueryAndOpenResolver( + 'event.dataset : endpoint.events.process' ); - expect(parseFloat(OriginalNodeDataStyle[i].top)).to.lessThan( - parseFloat(NewNodeDataStyle1[i].top) + await pageObjects.hosts.runNodeEvents(expectedData); + }); + + it('Check Child events for event.security Node', async () => { + await pageObjects.hosts.navigateToEventsPanel(); + await pageObjects.hosts.executeQueryAndOpenResolver( + 'event.dataset : endpoint.events.security' ); - expect(parseFloat(NewNodeDataStyle1[i].width)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].width) + await pageObjects.hosts.runNodeEvents(expectedData); + }); + + it('Check Child events for event.registry Node', async () => { + await pageObjects.hosts.navigateToEventsPanel(); + await pageObjects.hosts.executeQueryAndOpenResolver( + 'event.dataset : endpoint.events.registry' ); - expect(parseFloat(NewNodeDataStyle1[i].height)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].height) + await pageObjects.hosts.runNodeEvents(expectedData); + }); + + it('Check Child events for event.network Node', async () => { + await pageObjects.hosts.navigateToEventsPanel(); + await pageObjects.hosts.executeQueryAndOpenResolver( + 'event.dataset : endpoint.events.network' ); - } - await (await testSubjects.find('resolver:graph-controls:zoom-in')).click(); + await pageObjects.hosts.runNodeEvents(expectedData); + }); }); }); } diff --git a/x-pack/test/security_solution_endpoint/page_objects/hosts_page.ts b/x-pack/test/security_solution_endpoint/page_objects/hosts_page.ts index c5f7d5b5fdf311..3301217e41a90c 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/hosts_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/hosts_page.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import expect from '@kbn/expect'; import { FtrProviderContext } from '../ftr_provider_context'; import { deleteEventsStream } from '../../security_solution_endpoint_api_int/apis/data_stream_helper'; import { deleteAlertsStream } from '../../security_solution_endpoint_api_int/apis/data_stream_helper'; @@ -20,6 +21,7 @@ export interface DataStyle { export function SecurityHostsPageProvider({ getService, getPageObjects }: FtrProviderContext) { const pageObjects = getPageObjects(['common', 'header']); const testSubjects = getService('testSubjects'); + const queryBar = getService('queryBar'); /** * @function parseStyles @@ -105,5 +107,46 @@ export function SecurityHostsPageProvider({ getService, getPageObjects }: FtrPro await deleteMetadataStream(getService); await deleteTelemetryStream(getService); }, + /** + * Runs Nodes Events + */ + async runNodeEvents(expectedData: string[]) { + await testSubjects.exists('resolver:submenu:button', { timeout: 400 }); + const NodeSubmenuButtons = await testSubjects.findAll('resolver:submenu:button'); + for (let b = 0; b < NodeSubmenuButtons.length; b++) { + await (await testSubjects.findAll('resolver:submenu:button'))[b].click(); + } + await testSubjects.exists('resolver:map:node-submenu-item', { timeout: 400 }); + const NodeSubmenuItems = await testSubjects.findAll('resolver:map:node-submenu-item'); + for (let i = 0; i < NodeSubmenuItems.length; i++) { + await (await testSubjects.findAll('resolver:map:node-submenu-item'))[i].click(); + const Events = await testSubjects.findAll('resolver:map:node-submenu-item'); + const EventName = await Events[i]._webElement.getText(); + const LinkText = await testSubjects.find('resolver:breadcrumbs:last'); + const linkText = await LinkText._webElement.getText(); + expect(EventName).to.equal(linkText); + expect(EventName).to.equal(expectedData[i]); + } + }, + /** + * Navigate to Events Panel + */ + async navigateToEventsPanel() { + if (!(await testSubjects.exists('investigate-in-resolver-button', { timeout: 400 }))) { + await (await testSubjects.find('navigation-hosts')).click(); + await testSubjects.click('navigation-events'); + await testSubjects.existOrFail('event'); + } + }, + /** + * execute Query And Open Resolver + */ + async executeQueryAndOpenResolver(query: string) { + await queryBar.setQuery(query); + await queryBar.submitQuery(); + await testSubjects.click('full-screen'); + await testSubjects.click('investigate-in-resolver-button'); + await testSubjects.click('full-screen'); + }, }; } From 56c61225b2651db7a255ebcddeb05a41477bda45 Mon Sep 17 00:00:00 2001 From: Oliver Gupte Date: Wed, 7 Oct 2020 08:07:55 -0700 Subject: [PATCH 03/32] Closes #79796. Fixes bug in the anomaly detection setup header link (#79797) showing an alert when environment: All was selected even when there were anomaly detection jobs --- .../shared/Links/apm/AnomalyDetectionSetupLink.tsx | 10 +++++++--- .../Links/apm/anomaly_detection_setup_link.test.tsx | 11 +++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/AnomalyDetectionSetupLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/AnomalyDetectionSetupLink.tsx index 0c209b0aca9171..368837b3c94115 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/AnomalyDetectionSetupLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/AnomalyDetectionSetupLink.tsx @@ -9,7 +9,10 @@ import { i18n } from '@kbn/i18n'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; import { APIReturnType } from '../../../../services/rest/createCallApmApi'; import { APMLink } from './APMLink'; -import { getEnvironmentLabel } from '../../../../../common/environment_filter_values'; +import { + ENVIRONMENT_ALL, + getEnvironmentLabel, +} from '../../../../../common/environment_filter_values'; import { useUrlParams } from '../../../../hooks/useUrlParams'; import { useFetcher, FETCH_STATUS } from '../../../../hooks/useFetcher'; import { useLicense } from '../../../../hooks/useLicense'; @@ -57,7 +60,8 @@ export function MissingJobsAlert({ environment }: { environment?: string }) { return null; } - const isEnvironmentSelected = !!environment; + const isEnvironmentSelected = + environment && environment !== ENVIRONMENT_ALL.value; // there are jobs for at least one environment if (!isEnvironmentSelected && data.jobs.length > 0) { @@ -80,7 +84,7 @@ export function MissingJobsAlert({ environment }: { environment?: string }) { } function getTooltipText(environment?: string) { - if (!environment) { + if (!environment || environment === ENVIRONMENT_ALL.value) { return i18n.translate('xpack.apm.anomalyDetectionSetup.notEnabledText', { defaultMessage: `Anomaly detection is not yet enabled. Click to continue setup.`, }); diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/anomaly_detection_setup_link.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/anomaly_detection_setup_link.test.tsx index 3f675f494a661b..a53468e2ad06c1 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/anomaly_detection_setup_link.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/anomaly_detection_setup_link.test.tsx @@ -96,4 +96,15 @@ describe('MissingJobsAlert', () => { expect(toolTipText).toBe(undefined); }); }); + + describe('when at least one job exists and all environments are selected', () => { + it('does not show a warning', async () => { + const { toolTipAnchor, toolTipText } = await renderTooltipAnchor({ + jobs: [{ environment: 'ENVIRONMENT_ALL', job_id: 'my_job_id' }], + }); + + expect(toolTipAnchor).not.toBeInTheDocument(); + expect(toolTipText).toBe(undefined); + }); + }); }); From a8c080be283e4a39e1c0f8d4087213dffa1a6e46 Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Wed, 7 Oct 2020 09:18:43 -0600 Subject: [PATCH 04/32] Delay client-side feature usage registration until start (#79365) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- test/scripts/jenkins_xpack_build_plugins.sh | 1 + x-pack/plugins/licensing/public/plugin.ts | 2 +- .../services/feature_usage_service.test.ts | 37 ++++++++++++------- .../public/services/feature_usage_service.ts | 36 +++++++++--------- .../routes/internal/register_feature.ts | 28 ++++++++------ .../ui_actions_service_enhancements.ts | 12 +----- x-pack/test/licensing_plugin/config.public.ts | 1 + .../plugins/test_feature_usage/kibana.json | 7 ++++ .../test_feature_usage/public/index.ts | 9 +++++ .../test_feature_usage/public/plugin.ts | 22 +++++++++++ .../licensing_plugin/public/feature_usage.ts | 23 +----------- 11 files changed, 102 insertions(+), 76 deletions(-) create mode 100644 x-pack/test/licensing_plugin/plugins/test_feature_usage/kibana.json create mode 100644 x-pack/test/licensing_plugin/plugins/test_feature_usage/public/index.ts create mode 100644 x-pack/test/licensing_plugin/plugins/test_feature_usage/public/plugin.ts diff --git a/test/scripts/jenkins_xpack_build_plugins.sh b/test/scripts/jenkins_xpack_build_plugins.sh index 3fd3d02de13046..289e64f66c89bf 100755 --- a/test/scripts/jenkins_xpack_build_plugins.sh +++ b/test/scripts/jenkins_xpack_build_plugins.sh @@ -10,5 +10,6 @@ node scripts/build_kibana_platform_plugins \ --scan-dir "$XPACK_DIR/test/alerting_api_integration/plugins" \ --scan-dir "$XPACK_DIR/test/plugin_api_integration/plugins" \ --scan-dir "$XPACK_DIR/test/plugin_api_perf/plugins" \ + --scan-dir "$XPACK_DIR/test/licensing_plugin/plugins" \ --workers 12 \ --verbose diff --git a/x-pack/plugins/licensing/public/plugin.ts b/x-pack/plugins/licensing/public/plugin.ts index aa0c25364f2c7d..3f945756691b3b 100644 --- a/x-pack/plugins/licensing/public/plugin.ts +++ b/x-pack/plugins/licensing/public/plugin.ts @@ -117,7 +117,7 @@ export class LicensingPlugin implements Plugin { describe('#setup', () => { describe('#register', () => { - it('calls the endpoint with the correct parameters', async () => { - const setup = service.setup({ http }); - await setup.register('my-feature', 'platinum'); + it('calls the endpoint on start with the correct parameters', async () => { + const setup = service.setup(); + setup.register('my-feature', 'platinum'); + setup.register('my-other-feature', 'gold'); + expect(http.post).not.toHaveBeenCalled(); + + service.start({ http }); expect(http.post).toHaveBeenCalledTimes(1); expect(http.post).toHaveBeenCalledWith('/internal/licensing/feature_usage/register', { - body: JSON.stringify({ - featureName: 'my-feature', - licenseType: 'platinum', - }), + body: JSON.stringify([ + { featureName: 'my-feature', licenseType: 'platinum' }, + { featureName: 'my-other-feature', licenseType: 'gold' }, + ]), }); }); - it('does not call endpoint if on anonymous path', async () => { + it('does not call endpoint on start if no registrations', async () => { + service.setup(); + service.start({ http }); + expect(http.post).not.toHaveBeenCalled(); + }); + + it('does not call endpoint on start if on anonymous path', async () => { http.anonymousPaths.isAnonymous.mockReturnValue(true); - const setup = service.setup({ http }); - await setup.register('my-feature', 'platinum'); + const setup = service.setup(); + setup.register('my-feature', 'platinum'); + service.start({ http }); expect(http.post).not.toHaveBeenCalled(); }); }); @@ -42,7 +53,7 @@ describe('FeatureUsageService', () => { describe('#start', () => { describe('#notifyUsage', () => { it('calls the endpoint with the correct parameters', async () => { - service.setup({ http }); + service.setup(); const start = service.start({ http }); await start.notifyUsage('my-feature', 42); @@ -56,7 +67,7 @@ describe('FeatureUsageService', () => { }); it('correctly convert dates', async () => { - service.setup({ http }); + service.setup(); const start = service.start({ http }); const now = new Date(); @@ -74,7 +85,7 @@ describe('FeatureUsageService', () => { it('does not call endpoint if on anonymous path', async () => { http.anonymousPaths.isAnonymous.mockReturnValue(true); - service.setup({ http }); + service.setup(); const start = service.start({ http }); await start.notifyUsage('my-feature', 42); expect(http.post).not.toHaveBeenCalled(); diff --git a/x-pack/plugins/licensing/public/services/feature_usage_service.ts b/x-pack/plugins/licensing/public/services/feature_usage_service.ts index 461246844a33bf..fa9dc0f5b66952 100644 --- a/x-pack/plugins/licensing/public/services/feature_usage_service.ts +++ b/x-pack/plugins/licensing/public/services/feature_usage_service.ts @@ -5,7 +5,7 @@ */ import { isDate } from 'lodash'; -import type { HttpSetup, HttpStart } from 'src/core/public'; +import type { HttpStart } from 'src/core/public'; import { LicenseType } from '../../common/types'; /** @public */ @@ -13,7 +13,7 @@ export interface FeatureUsageServiceSetup { /** * Register a feature to be able to notify of it's usages using the {@link FeatureUsageServiceStart | service start contract}. */ - register(featureName: string, licenseType: LicenseType): Promise; + register(featureName: string, licenseType: LicenseType): void; } /** @public */ @@ -27,10 +27,6 @@ export interface FeatureUsageServiceStart { notifyUsage(featureName: string, usedAt?: Date | number): Promise; } -interface SetupDeps { - http: HttpSetup; -} - interface StartDeps { http: HttpStart; } @@ -39,29 +35,33 @@ interface StartDeps { * @internal */ export class FeatureUsageService { - public setup({ http }: SetupDeps): FeatureUsageServiceSetup { + private readonly registrations: Array<{ featureName: string; licenseType: LicenseType }> = []; + + public setup(): FeatureUsageServiceSetup { return { register: async (featureName, licenseType) => { - // Skip registration if on logged-out page - // NOTE: this only works because the login page does a full-page refresh after logging in - // If this is ever changed, this code will need to buffer registrations and call them after the user logs in. - if (http.anonymousPaths.isAnonymous(window.location.pathname)) return; - - await http.post('/internal/licensing/feature_usage/register', { - body: JSON.stringify({ - featureName, - licenseType, - }), - }); + this.registrations.push({ featureName, licenseType }); }, }; } public start({ http }: StartDeps): FeatureUsageServiceStart { + // Skip registration if on logged-out page + // NOTE: this only works because the login page does a full-page refresh after logging in + // If this is ever changed, this code will need to buffer registrations and call them after the user logs in. + const registrationPromise = + http.anonymousPaths.isAnonymous(window.location.pathname) || this.registrations.length === 0 + ? Promise.resolve() + : http.post('/internal/licensing/feature_usage/register', { + body: JSON.stringify(this.registrations), + }); + return { notifyUsage: async (featureName, usedAt = Date.now()) => { // Skip notification if on logged-out page if (http.anonymousPaths.isAnonymous(window.location.pathname)) return; + // Wait for registrations to complete + await registrationPromise; const lastUsed = isDate(usedAt) ? usedAt.getTime() : usedAt; await http.post('/internal/licensing/feature_usage/notify', { diff --git a/x-pack/plugins/licensing/server/routes/internal/register_feature.ts b/x-pack/plugins/licensing/server/routes/internal/register_feature.ts index 14f7952f86f5ac..418e98fc1b2a8a 100644 --- a/x-pack/plugins/licensing/server/routes/internal/register_feature.ts +++ b/x-pack/plugins/licensing/server/routes/internal/register_feature.ts @@ -16,22 +16,26 @@ export function registerRegisterFeatureRoute( { path: '/internal/licensing/feature_usage/register', validate: { - body: schema.object({ - featureName: schema.string(), - licenseType: schema.string({ - validate: (value) => { - if (!(value in LICENSE_TYPE)) { - return `Invalid license type: ${value}`; - } - }, - }), - }), + body: schema.arrayOf( + schema.object({ + featureName: schema.string(), + licenseType: schema.string({ + validate: (value) => { + if (!(value in LICENSE_TYPE)) { + return `Invalid license type: ${value}`; + } + }, + }), + }) + ), }, }, async (context, request, response) => { - const { featureName, licenseType } = request.body; + const registrations = request.body; - featureUsageSetup.register(featureName, licenseType as LicenseType); + registrations.forEach(({ featureName, licenseType }) => { + featureUsageSetup.register(featureName, licenseType as LicenseType); + }); return response.ok({ body: { diff --git a/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts b/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts index cbbd88e65e8416..0da2dc6da2c259 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts @@ -158,17 +158,7 @@ export class UiActionsServiceEnhancements private registerFeatureUsage = (definition: ActionFactoryDefinition): void => { if (!definition.minimalLicense || !definition.licenseFeatureName) return; - - // Intentionally don't wait for response because - // happens in setup phase and has to be sync - this.deps.featureUsageSetup - .register(definition.licenseFeatureName, definition.minimalLicense) - .catch(() => { - // eslint-disable-next-line no-console - console.warn( - `ActionFactory [actionFactory.id = ${definition.id}] fail to register feature for featureUsage.` - ); - }); + this.deps.featureUsageSetup.register(definition.licenseFeatureName, definition.minimalLicense); }; public readonly telemetry = (state: DynamicActionsState, telemetry: Record = {}) => { diff --git a/x-pack/test/licensing_plugin/config.public.ts b/x-pack/test/licensing_plugin/config.public.ts index 73010c3e88f2aa..1ccdf89bad1404 100644 --- a/x-pack/test/licensing_plugin/config.public.ts +++ b/x-pack/test/licensing_plugin/config.public.ts @@ -23,6 +23,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { KIBANA_ROOT, 'test/plugin_functional/plugins/core_provider_plugin' )}`, + `--plugin-path=${path.resolve(__dirname, 'plugins/test_feature_usage')}`, ], }, }; diff --git a/x-pack/test/licensing_plugin/plugins/test_feature_usage/kibana.json b/x-pack/test/licensing_plugin/plugins/test_feature_usage/kibana.json new file mode 100644 index 00000000000000..aedbf95539736b --- /dev/null +++ b/x-pack/test/licensing_plugin/plugins/test_feature_usage/kibana.json @@ -0,0 +1,7 @@ +{ + "id": "testFeatureUsage", + "version": "kibana", + "server": false, + "ui": true, + "requiredPlugins": ["licensing"] +} diff --git a/x-pack/test/licensing_plugin/plugins/test_feature_usage/public/index.ts b/x-pack/test/licensing_plugin/plugins/test_feature_usage/public/index.ts new file mode 100644 index 00000000000000..85889df20241b9 --- /dev/null +++ b/x-pack/test/licensing_plugin/plugins/test_feature_usage/public/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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { TestFeatureUsagePlugin } from './plugin'; + +export const plugin = () => new TestFeatureUsagePlugin(); diff --git a/x-pack/test/licensing_plugin/plugins/test_feature_usage/public/plugin.ts b/x-pack/test/licensing_plugin/plugins/test_feature_usage/public/plugin.ts new file mode 100644 index 00000000000000..9dd77201ff849c --- /dev/null +++ b/x-pack/test/licensing_plugin/plugins/test_feature_usage/public/plugin.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CoreSetup } from 'src/core/server'; +import { LicensingPluginSetup } from '../../../../../plugins/licensing/public'; + +interface SetupPlugins { + licensing: LicensingPluginSetup; +} + +export class TestFeatureUsagePlugin { + public setup(core: CoreSetup, plugins: SetupPlugins) { + plugins.licensing.featureUsage.register('test-client-A', 'gold'); + plugins.licensing.featureUsage.register('test-client-B', 'enterprise'); + } + + public start() {} + public stop() {} +} diff --git a/x-pack/test/licensing_plugin/public/feature_usage.ts b/x-pack/test/licensing_plugin/public/feature_usage.ts index 15d302d71bfabf..10232d6367062f 100644 --- a/x-pack/test/licensing_plugin/public/feature_usage.ts +++ b/x-pack/test/licensing_plugin/public/feature_usage.ts @@ -5,11 +5,7 @@ */ import expect from '@kbn/expect'; import { FtrProviderContext } from '../services'; -import { - LicensingPluginSetup, - LicensingPluginStart, - LicenseType, -} from '../../../plugins/licensing/public'; +import { LicensingPluginStart, LicenseType } from '../../../plugins/licensing/public'; import '../../../../test/plugin_functional/plugins/core_provider_plugin/types'; interface FeatureUsage { @@ -25,19 +21,6 @@ export default function (ftrContext: FtrProviderContext) { const browser = getService('browser'); const PageObjects = getPageObjects(['common', 'security']); - const registerFeature = async (featureName: string, licenseType: LicenseType) => { - await browser.executeAsync( - async (feature, type, cb) => { - const { setup } = window._coreProvider; - const licensing: LicensingPluginSetup = setup.plugins.licensing; - await licensing.featureUsage.register(feature, type); - cb(); - }, - featureName, - licenseType - ); - }; - const notifyFeatureUsage = async (featureName: string, lastUsed: number) => { await browser.executeAsync( async (feature, time, cb) => { @@ -57,12 +40,10 @@ export default function (ftrContext: FtrProviderContext) { }); it('allows to register features to the server', async () => { - await registerFeature('test-client-A', 'gold'); - await registerFeature('test-client-B', 'enterprise'); - const response = await supertest.get('/api/licensing/feature_usage').expect(200); const features = response.body.features.map(({ name }: FeatureUsage) => name); + // These were registered by the plugin in ../plugins/test_feature_usage expect(features).to.contain('test-client-A'); expect(features).to.contain('test-client-B'); }); From 59d83e6955435368a26e50b541edccb1c632d086 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Wed, 7 Oct 2020 19:41:37 +0300 Subject: [PATCH 05/32] Add support for HDR percentiles in TSVB visualizations (#78306) * Add support for HDR percentiles in TSVB visualizations Closes: #64238 * remove extra console.log * fix CI * fix PR comments * fix layout * remove legacy injectI18n * fix localization issues Co-authored-by: Elastic Machine Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../vis_type_timeseries/common/vis_schema.ts | 1 + .../application/components/aggs/percentile.js | 32 ++- .../components/aggs/percentile_hdr.tsx | 52 +++++ .../aggs/percentile_rank/multi_value_row.tsx | 31 +-- .../aggs/percentile_rank/percentile_rank.tsx | 46 ++-- .../percentile_rank_values.tsx | 23 +- .../components/aggs/percentile_ui.js | 202 ++++++++++-------- .../server/lib/vis_data/get_series_data.js | 1 - .../lib/vis_data/helpers/bucket_transform.js | 16 +- .../series/percentile_rank.js | 4 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 12 files changed, 254 insertions(+), 156 deletions(-) create mode 100644 src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx diff --git a/src/plugins/vis_type_timeseries/common/vis_schema.ts b/src/plugins/vis_type_timeseries/common/vis_schema.ts index b33215934c5dfd..09509384231340 100644 --- a/src/plugins/vis_type_timeseries/common/vis_schema.ts +++ b/src/plugins/vis_type_timeseries/common/vis_schema.ts @@ -104,6 +104,7 @@ export const metricsItems = schema.object({ }) ) ), + numberOfSignificantValueDigits: numberOptional, percentiles: schema.maybe( schema.arrayOf( schema.object({ diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js index f12c0c8f6f465e..a34b74d1064924 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js @@ -24,10 +24,11 @@ import { FieldSelect } from './field_select'; import { AggRow } from './agg_row'; import { createChangeHandler } from '../lib/create_change_handler'; import { createSelectHandler } from '../lib/create_select_handler'; +import { createNumberHandler } from '../lib/create_number_handler'; import { htmlIdGenerator, EuiSpacer, - EuiFlexGroup, + EuiFlexGrid, EuiFlexItem, EuiFormLabel, EuiFormRow, @@ -35,6 +36,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; import { Percentiles, newPercentile } from './percentile_ui'; +import { PercentileHdr } from './percentile_hdr'; const RESTRICT_FIELDS = [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.HISTOGRAM]; @@ -46,6 +48,8 @@ export function PercentileAgg(props) { const handleChange = createChangeHandler(props.onChange, model); const handleSelectChange = createSelectHandler(handleChange); + const handleNumberChange = createNumberHandler(handleChange); + const indexPattern = (series.override_index_pattern && series.series_index_pattern) || panel.index_pattern; @@ -66,7 +70,7 @@ export function PercentileAgg(props) { siblings={props.siblings} dragHandleProps={props.dragHandleProps} > - + - - - - - + + + } + > + + + + + + + ); } diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx new file mode 100644 index 00000000000000..c0ba7d3c6765bc --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { EuiFieldNumber, EuiFormRow, EuiIconTip } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export interface PercentileHdrProps { + value: number | undefined; + onChange: () => void; +} + +export const PercentileHdr = ({ value, onChange }: PercentileHdrProps) => ( + + {' '} + + } + type="questionInCircle" + /> + + } + > + + +); diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx index ef8876a19b1a65..444e11d0d563d8 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx @@ -18,15 +18,8 @@ */ import React, { ChangeEvent } from 'react'; import { get } from 'lodash'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { - htmlIdGenerator, - EuiFieldNumber, - EuiFlexGroup, - EuiFlexItem, - EuiFormLabel, - EuiSpacer, -} from '@elastic/eui'; + +import { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { AddDeleteButtons } from '../../add_delete_buttons'; @@ -50,8 +43,6 @@ export const MultiValueRow = ({ disableAdd, disableDelete, }: MultiValueRowProps) => { - const htmlId = htmlIdGenerator(); - const onFieldNumberChange = (event: ChangeEvent) => onChange({ ...model, @@ -59,17 +50,9 @@ export const MultiValueRow = ({ }); return ( -
- - - - - - - + + + onDelete(model)} disableDelete={disableDelete} disableAdd={disableAdd} + responsive={false} /> - -
+ ); }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx index d02a16ade2bba7..f78df9b1ddef40 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx @@ -20,11 +20,11 @@ import React from 'react'; import { htmlIdGenerator, - EuiFlexGroup, EuiFlexItem, EuiFormLabel, EuiFormRow, EuiSpacer, + EuiFlexGrid, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { AggSelect } from '../agg_select'; @@ -34,12 +34,16 @@ import { FieldSelect } from '../field_select'; import { createChangeHandler } from '../../lib/create_change_handler'; // @ts-ignore import { createSelectHandler } from '../../lib/create_select_handler'; +// @ts-ignore +import { createNumberHandler } from '../../lib/create_number_handler'; + import { AggRow } from '../agg_row'; import { PercentileRankValues } from './percentile_rank_values'; import { IFieldType, KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { MetricsItemsSchema, PanelSchema, SeriesItemsSchema } from '../../../../../common/types'; import { DragHandleProps } from '../../../../types'; +import { PercentileHdr } from '../percentile_hdr'; const RESTRICT_FIELDS = [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.HISTOGRAM]; @@ -67,6 +71,7 @@ export const PercentileRankAgg = (props: PercentileRankAggProps) => { const isTablePanel = panel.type === 'table'; const handleChange = createChangeHandler(props.onChange, model); const handleSelectChange = createSelectHandler(handleChange); + const handleNumberChange = createNumberHandler(handleChange); const handlePercentileRankValuesChange = (values: MetricsItemsSchema['values']) => { handleChange({ @@ -84,7 +89,7 @@ export const PercentileRankAgg = (props: PercentileRankAggProps) => { siblings={props.siblings} dragHandleProps={props.dragHandleProps} > - + { /> - - - {model.values && ( - - )} + + + + } + > + + + + + + + ); }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx index b66d79d67f4277..92ca4dfb706acb 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx @@ -19,7 +19,7 @@ import React from 'react'; import { last } from 'lodash'; -import { EuiFlexGroup } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { MultiValueRow } from './multi_value_row'; interface PercentileRankValuesProps { @@ -52,19 +52,20 @@ export const PercentileRankValues = (props: PercentileRankValuesProps) => { disableDeleteRow: boolean; disableAddRow: boolean; }) => ( - + + + ); return ( - + {showOnlyLastRow && renderRow({ rowModel: { diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js index bd421248a3607b..fb556a053df3fc 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js @@ -19,6 +19,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; +import { i18n } from '@kbn/i18n'; import _ from 'lodash'; import { collectionActions } from '../lib/collection_actions'; import { AddDeleteButtons } from '../add_delete_buttons'; @@ -27,17 +28,19 @@ import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, - EuiFormLabel, EuiComboBox, EuiFieldNumber, + EuiFormRow, + EuiFlexGrid, + EuiPanel, } from '@elastic/eui'; -import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; +import { FormattedMessage } from '@kbn/i18n/react'; export const newPercentile = (opts) => { return _.assign({ id: uuid.v1(), mode: 'line', shade: 0.2 }, opts); }; -class PercentilesUi extends Component { +export class Percentiles extends Component { handleTextChange(item, name) { return (e) => { const handleChange = collectionActions.handleChange.bind(null, this.props); @@ -50,22 +53,31 @@ class PercentilesUi extends Component { renderRow = (row, i, items) => { const defaults = { value: '', percentile: '', shade: '' }; const model = { ...defaults, ...row }; - const { intl, panel } = this.props; + const { panel } = this.props; + const flexItemStyle = { minWidth: 100 }; const percentileFieldNumber = ( - - + + + } + > + + ); @@ -77,99 +89,103 @@ class PercentilesUi extends Component { const handleDelete = collectionActions.handleDelete.bind(null, this.props, model); const modeOptions = [ { - label: intl.formatMessage({ - id: 'visTypeTimeseries.percentile.modeOptions.lineLabel', + label: i18n.translate('visTypeTimeseries.percentile.modeOptions.lineLabel', { defaultMessage: 'Line', }), value: 'line', }, { - label: intl.formatMessage({ - id: 'visTypeTimeseries.percentile.modeOptions.bandLabel', + label: i18n.translate('visTypeTimeseries.percentile.modeOptions.bandLabel', { defaultMessage: 'Band', }), value: 'band', }, ]; - const optionsStyle = {}; + const optionsStyle = { + ...flexItemStyle, + }; if (model.mode === 'line') { optionsStyle.display = 'none'; } - const labelStyle = { marginBottom: 0 }; + const htmlId = htmlIdGenerator(model.id); const selectedModeOption = modeOptions.find((option) => { return model.mode === option.value; }); return ( - - {percentileFieldNumber} - - - - - - - - - - - - - + + + + {percentileFieldNumber} + + + } + > + + + + + + } + > + + + + + + } + > + + + + + + + - - - - - - - - - - - - - - - - - - - + + + ); }; @@ -192,15 +208,13 @@ class PercentilesUi extends Component { } } -PercentilesUi.defaultProps = { +Percentiles.defaultProps = { name: 'percentile', }; -PercentilesUi.propTypes = { +Percentiles.propTypes = { name: PropTypes.string, model: PropTypes.object, panel: PropTypes.object, onChange: PropTypes.func, }; - -export const Percentiles = injectI18n(PercentilesUi); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.js index 1eace13c2e3364..232efc7514a5ac 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.js @@ -43,7 +43,6 @@ export async function getSeriesData(req, panel) { (acc, items) => acc.concat(items), [] ); - const data = await searchStrategy.search(req, searches); const handleResponseBodyFn = handleResponseBody(panel); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js index f033a43806312e..dc2936072165eb 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js @@ -64,6 +64,16 @@ function extendStatsBucket(bucket, metrics) { return body; } +function getPercentileHdrParam(bucket) { + return bucket.numberOfSignificantValueDigits + ? { + hdr: { + number_of_significant_value_digits: bucket.numberOfSignificantValueDigits, + }, + } + : undefined; +} + export const bucketTransform = { count: () => { return { @@ -139,13 +149,14 @@ export const bucketTransform = { bucket.percentiles.filter((p) => p.percentile).map((p) => p.percentile) ); } - const agg = { + + return { percentiles: { field: bucket.field, percents, + ...getPercentileHdrParam(bucket), }, }; - return agg; }, percentile_rank: (bucket) => { @@ -155,6 +166,7 @@ export const bucketTransform = { percentile_ranks: { field: bucket.field, values: (bucket.values || []).map((value) => (isEmpty(value) ? 0 : value)), + ...getPercentileHdrParam(bucket), }, }; }, diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js index a11324f73e611b..c163605af7ac59 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js @@ -32,7 +32,7 @@ export function percentileRank(resp, panel, series, meta) { } getSplits(resp, panel, series, meta).forEach((split) => { - (metric.values || []).forEach((percentileRank) => { + (metric.values || []).forEach((percentileRank, index) => { const data = split.timeseries.buckets.map((bucket) => [ bucket.key, getAggValue(bucket, { @@ -43,7 +43,7 @@ export function percentileRank(resp, panel, series, meta) { results.push({ data, - id: `${split.id}:${percentileRank}`, + id: `${split.id}:${percentileRank}:${index}`, label: `${split.label} (${percentileRank || 0})`, color: split.color, ...getDefaultDecoration(series), diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f9a0463ff58032..edae2d16dde95d 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3817,7 +3817,6 @@ "visTypeTimeseries.movingAverage.period": "期間", "visTypeTimeseries.movingAverage.windowSizeHint": "ウィンドウは、必ず、期間のサイズの 2 倍以上でなければなりません", "visTypeTimeseries.movingAverage.windowSizeLabel": "ウィンドウサイズ", - "visTypeTimeseries.multivalueRow.valueLabel": "値:", "visTypeTimeseries.noButtonLabel": "いいえ", "visTypeTimeseries.noDataDescription": "選択されたメトリックに表示するデータがありません", "visTypeTimeseries.percentile.aggregationLabel": "集約", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 6210f565ca3b48..8ea4577619ac6d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3818,7 +3818,6 @@ "visTypeTimeseries.movingAverage.period": "期间", "visTypeTimeseries.movingAverage.windowSizeHint": "窗口必须始终至少是期间大小的两倍", "visTypeTimeseries.movingAverage.windowSizeLabel": "窗口大小", - "visTypeTimeseries.multivalueRow.valueLabel": "值:", "visTypeTimeseries.noButtonLabel": "否", "visTypeTimeseries.noDataDescription": "所选指标没有可显示的数据", "visTypeTimeseries.percentile.aggregationLabel": "聚合", From cc784a0b5a835b37247bd2dddee7051a8fd5742e Mon Sep 17 00:00:00 2001 From: DeDe Morton Date: Wed, 7 Oct 2020 10:03:56 -0700 Subject: [PATCH 06/32] [docs] Rename ingest manager to fleet (#79731) * [docs] Rename ingest manager to fleet * remove commented out todo --- .../fleet.asciidoc} | 12 +++++++----- docs/fleet/images/fleet-start.png | Bin 0 -> 92286 bytes .../images/ingest-manager-start.png | Bin 75189 -> 0 bytes ...ettings.asciidoc => fleet-settings.asciidoc} | 16 ++++++++-------- docs/settings/settings-xkb.asciidoc | 2 +- docs/setup/settings.asciidoc | 2 +- docs/user/index.asciidoc | 2 +- 7 files changed, 18 insertions(+), 16 deletions(-) rename docs/{ingest_manager/ingest-manager.asciidoc => fleet/fleet.asciidoc} (67%) create mode 100644 docs/fleet/images/fleet-start.png delete mode 100644 docs/ingest_manager/images/ingest-manager-start.png rename docs/settings/{ingest-manager-settings.asciidoc => fleet-settings.asciidoc} (68%) diff --git a/docs/ingest_manager/ingest-manager.asciidoc b/docs/fleet/fleet.asciidoc similarity index 67% rename from docs/ingest_manager/ingest-manager.asciidoc rename to docs/fleet/fleet.asciidoc index 8f6e8036c68cd2..7039468f4b185f 100644 --- a/docs/ingest_manager/ingest-manager.asciidoc +++ b/docs/fleet/fleet.asciidoc @@ -1,11 +1,11 @@ [chapter] [role="xpack"] -[[ingest-manager]] -= {ingest-manager} +[[fleet]] += {fleet} experimental[] -{ingest-manager} in {kib} enables you to add and manage integrations for popular +{fleet} in {kib} enables you to add and manage integrations for popular services and platforms, as well as manage {elastic-agent} installations in standalone or {fleet} mode. @@ -17,11 +17,13 @@ Standalone mode requires you to manually configure and manage the agent locally. * An overview of the data ingest in your {es} cluster. * Multiple integrations to collect and transform data. +//TODO: Redo screen capture. + [role="screenshot"] -image::ingest_manager/images/ingest-manager-start.png[{ingest-manager} app in {kib}] +image::fleet/images/fleet-start.png[{fleet} app in {kib}] [float] == Get started -To get started with {ingest-management}, refer to the +To get started with {fleet}, refer to the {ingest-guide}/index.html[Ingest Management Guide]. diff --git a/docs/fleet/images/fleet-start.png b/docs/fleet/images/fleet-start.png new file mode 100644 index 0000000000000000000000000000000000000000..60e5416fde127e38a5eddb783ac336b62f5c7d79 GIT binary patch literal 92286 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�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 literal 0 HcmV?d00001 diff --git a/docs/ingest_manager/images/ingest-manager-start.png b/docs/ingest_manager/images/ingest-manager-start.png deleted file mode 100644 index 89174686a9768ed560c30a3534fc2f4fe126651b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75189 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 z 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 X7eBf{iQiBCvG*k#M{E4~TMz#Y+A77E diff --git a/docs/settings/ingest-manager-settings.asciidoc b/docs/settings/fleet-settings.asciidoc similarity index 68% rename from docs/settings/ingest-manager-settings.asciidoc rename to docs/settings/fleet-settings.asciidoc index 9fa83fc242b4ad..9c28d280031759 100644 --- a/docs/settings/ingest-manager-settings.asciidoc +++ b/docs/settings/fleet-settings.asciidoc @@ -1,29 +1,29 @@ [role="xpack"] -[[ingest-manager-settings-kb]] -=== {ingest-manager} settings in {kib} +[[fleet-settings-kb]] +=== {fleet} settings in {kib} ++++ -{ingest-manager} settings +{fleet} settings ++++ experimental[] You can configure `xpack.fleet` settings in your `kibana.yml`. -By default, {ingest-manager} is enabled. To use {fleet}, you also need to configure {kib} and {es} hosts. +By default, {fleet} is enabled. To use {fleet}, you also need to configure {kib} and {es} hosts. See the {ingest-guide}/index.html[Ingest Management] docs for more information. -[[general-ingest-manager-settings-kb]] -==== General {ingest-manager} settings +[[general-fleet-settings-kb]] +==== General {fleet} settings [cols="2*<"] |=== | `xpack.fleet.enabled` {ess-icon} - | Set to `true` (default) to enable {ingest-manager}. + | Set to `true` (default) to enable {fleet}. | `xpack.fleet.agents.enabled` {ess-icon} | Set to `true` (default) to enable {fleet}. |=== -[[ingest-manager-data-visualizer-settings]] +[[fleet-data-visualizer-settings]] ==== {package-manager} settings diff --git a/docs/settings/settings-xkb.asciidoc b/docs/settings/settings-xkb.asciidoc index 04fed0d6204b7b..9d9cc924018969 100644 --- a/docs/settings/settings-xkb.asciidoc +++ b/docs/settings/settings-xkb.asciidoc @@ -20,4 +20,4 @@ include::ml-settings.asciidoc[] include::reporting-settings.asciidoc[] include::spaces-settings.asciidoc[] include::i18n-settings.asciidoc[] -include::ingest-manager-settings.asciidoc[] +include::fleet-settings.asciidoc[] diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 903bb59cef380b..2f2c87ca9c7d47 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -638,7 +638,7 @@ include::{kib-repo-dir}/settings/alert-action-settings.asciidoc[] include::{kib-repo-dir}/settings/apm-settings.asciidoc[] include::{kib-repo-dir}/settings/dev-settings.asciidoc[] include::{kib-repo-dir}/settings/graph-settings.asciidoc[] -include::{kib-repo-dir}/settings/ingest-manager-settings.asciidoc[] +include::{kib-repo-dir}/settings/fleet-settings.asciidoc[] include::{kib-repo-dir}/settings/i18n-settings.asciidoc[] include::{kib-repo-dir}/settings/logs-ui-settings.asciidoc[] include::{kib-repo-dir}/settings/infrastructure-ui-settings.asciidoc[] diff --git a/docs/user/index.asciidoc b/docs/user/index.asciidoc index e909626c5779cd..b0f3dbfa0c9e95 100644 --- a/docs/user/index.asciidoc +++ b/docs/user/index.asciidoc @@ -43,7 +43,7 @@ include::monitoring/index.asciidoc[] include::management.asciidoc[] -include::{kib-repo-dir}/ingest_manager/ingest-manager.asciidoc[] +include::{kib-repo-dir}/fleet/fleet.asciidoc[] include::reporting/index.asciidoc[] From 25b1db12fddf76c1d154b4e51d51df482f7f1595 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Wed, 7 Oct 2020 10:07:12 -0700 Subject: [PATCH 07/32] [Search] Functional tests for async search (#78703) * [Search] [WIP] Add shard delay aggregation * Add expression functions * Register function * Fix test * Add comment * [Search] Add async search functional tests * Add import * Add additional test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/search/errors/timeout_error.tsx | 7 +- .../lib/panel/embeddable_error_label.tsx | 6 +- test/common/config.js | 2 + .../dashboard/async_search/async_search.ts | 41 +++ .../apps/dashboard/async_search/index.ts | 26 ++ .../test/functional/apps/dashboard/index.ts | 1 + .../dashboard/async_search/data.json | 197 ++++++++++++++ .../dashboard/async_search/mappings.json | 244 ++++++++++++++++++ 8 files changed, 521 insertions(+), 3 deletions(-) create mode 100644 x-pack/test/functional/apps/dashboard/async_search/async_search.ts create mode 100644 x-pack/test/functional/apps/dashboard/async_search/index.ts create mode 100644 x-pack/test/functional/es_archives/dashboard/async_search/data.json create mode 100644 x-pack/test/functional/es_archives/dashboard/async_search/mappings.json diff --git a/src/plugins/data/public/search/errors/timeout_error.tsx b/src/plugins/data/public/search/errors/timeout_error.tsx index a9ff0c3b38ae6d..007689dd0269d5 100644 --- a/src/plugins/data/public/search/errors/timeout_error.tsx +++ b/src/plugins/data/public/search/errors/timeout_error.tsx @@ -97,7 +97,12 @@ export class SearchTimeoutError extends KbnError { <> - this.onClick(application)} size="s"> + this.onClick(application)} + size="s" + data-test-subj="searchTimeoutError" + > {actionText} diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_error_label.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_error_label.tsx index 1e4604af9dc092..fa4d7f466caeea 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_error_label.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_error_label.tsx @@ -40,8 +40,10 @@ export function EmbeddableErrorLabel(props: Props) { return (
- - {labelText} + + + {labelText} +
diff --git a/test/common/config.js b/test/common/config.js index dbbd75d1f95777..9d6d531ae4b374 100644 --- a/test/common/config.js +++ b/test/common/config.js @@ -48,6 +48,8 @@ export default function () { `--elasticsearch.username=${kibanaServerTestUser.username}`, `--elasticsearch.password=${kibanaServerTestUser.password}`, `--home.disableWelcomeScreen=true`, + // Needed for async search functional tests to introduce a delay + `--data.search.aggs.shardDelay.enabled=true`, `--security.showInsecureClusterWarning=false`, '--telemetry.banner=false', '--telemetry.optIn=false', diff --git a/x-pack/test/functional/apps/dashboard/async_search/async_search.ts b/x-pack/test/functional/apps/dashboard/async_search/async_search.ts new file mode 100644 index 00000000000000..004e51e459caa5 --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/async_search/async_search.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'header', 'dashboard', 'visChart']); + + describe('dashboard with async search', () => { + it('not delayed should load', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.gotoDashboardEditMode('Not Delayed'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.missingOrFail('embeddableErrorLabel'); + const data = await PageObjects.visChart.getBarChartData('Sum of bytes'); + expect(data.length).to.be(5); + }); + + it('delayed should load', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.gotoDashboardEditMode('Delayed 5s'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.missingOrFail('embeddableErrorLabel'); + const data = await PageObjects.visChart.getBarChartData(''); + expect(data.length).to.be(5); + }); + + it('timed out should show error', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.gotoDashboardEditMode('Delayed 15s'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('embeddableErrorLabel'); + await testSubjects.existOrFail('searchTimeoutError'); + }); + }); +} diff --git a/x-pack/test/functional/apps/dashboard/async_search/index.ts b/x-pack/test/functional/apps/dashboard/async_search/index.ts new file mode 100644 index 00000000000000..9c07bff885a11f --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/async_search/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile, getService }: FtrProviderContext) { + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + + describe('async search', function () { + before(async () => { + await esArchiver.loadIfNeeded('logstash_functional'); + await esArchiver.load('dashboard/async_search'); + await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); + await kibanaServer.uiSettings.replace({ 'search:timeout': 10000 }); + }); + + after(async () => { + await esArchiver.unload('dashboard/async_search'); + }); + + loadTestFile(require.resolve('./async_search')); + }); +} diff --git a/x-pack/test/functional/apps/dashboard/index.ts b/x-pack/test/functional/apps/dashboard/index.ts index 4a893d3f62d934..37ba60cea70f06 100644 --- a/x-pack/test/functional/apps/dashboard/index.ts +++ b/x-pack/test/functional/apps/dashboard/index.ts @@ -13,6 +13,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./preserve_url')); loadTestFile(require.resolve('./reporting')); loadTestFile(require.resolve('./drilldowns')); + loadTestFile(require.resolve('./async_search')); loadTestFile(require.resolve('./_async_dashboard')); }); } diff --git a/x-pack/test/functional/es_archives/dashboard/async_search/data.json b/x-pack/test/functional/es_archives/dashboard/async_search/data.json new file mode 100644 index 00000000000000..2990097e88d00c --- /dev/null +++ b/x-pack/test/functional/es_archives/dashboard/async_search/data.json @@ -0,0 +1,197 @@ +{ + "type": "doc", + "value": { + "id": "space:default", + "index": ".kibana", + "source": { + "space": { + "description": "This is the default space!", + "name": "Default" + }, + "type": "space" + } + } +} + +{ + "type": "doc", + "value": { + "id": "visualization:6c9f3830-01e3-11eb-9b63-176d7b28a352", + "index": ".kibana", + "source": { + "type": "visualization", + "visualization": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}" + }, + "title": "Sum of Bytes by Extension (Delayed 5s)", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Sum of Bytes by Extension (Delayed 5s)\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"sum\",\"params\":{\"field\":\"bytes\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"extension.raw\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"segment\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"shard_delay\",\"params\":{\"delay\":\"5s\"},\"schema\":\"group\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Sum of bytes\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Sum of bytes\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"row\":true}}" + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "visualization:14501a50-01e3-11eb-9b63-176d7b28a352", + "index": ".kibana", + "source": { + "type": "visualization", + "visualization": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}" + }, + "title": "Sum of Bytes by Extension", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Sum of Bytes by Extension\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"sum\",\"params\":{\"field\":\"bytes\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"extension.raw\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Sum of bytes\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Sum of bytes\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"row\":true}}" + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "visualization:50a67010-075d-11eb-be70-0bd5e8b57d02", + "index": ".kibana", + "source": { + "type": "visualization", + "visualization": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}" + }, + "title": "Sum of Bytes by Extension (Delayed 15s)", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Sum of Bytes by Extension (Delayed 15s)\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"sum\",\"params\":{\"field\":\"bytes\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"extension.raw\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"segment\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"shard_delay\",\"params\":{\"delay\":\"15s\"},\"schema\":\"group\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Sum of bytes\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Sum of bytes\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"row\":true}}" + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "index-pattern:logstash-*", + "index": ".kibana", + "source": { + "index-pattern": { + "fields": "[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]", + "timeFieldName": "@timestamp", + "title": "logstash-*" + }, + "type": "index-pattern" + } + } +} + +{ + "type": "doc", + "value": { + "id": "dashboard:24f3f950-69d9-11ea-a14d-e341629a29e6", + "index": ".kibana", + "source": { + "dashboard": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "optionsJSON": "{\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[{\"version\":\"7.10.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"f8f7f5b5-b840-443c-a766-d22a8a87c493\"},\"panelIndex\":\"f8f7f5b5-b840-443c-a766-d22a8a87c493\",\"embeddableConfig\":{},\"panelRefName\":\"panel_0\"}]", + "refreshInterval": { "pause": true, "value": 0 }, + "timeFrom": "2015-09-19T17:34:10.297Z", + "timeRestore": true, + "timeTo": "2015-09-23T00:09:17.180Z", + "title" : "Delayed 5s", + "version": 1 + }, + "references": [ + { + "name" : "panel_0", + "type" : "visualization", + "id" : "6c9f3830-01e3-11eb-9b63-176d7b28a352" + } + ], + "type": "dashboard", + "updated_at": "2020-03-19T11:59:53.701Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "dashboard:41e77910-69d9-11ea-a14d-e341629a29e6", + "index": ".kibana", + "source": { + "dashboard": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "optionsJSON": "{\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[{\"version\":\"7.7.0\",\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"8c5df6b2-0cc9-4887-a2d9-6a9a192f3407\"},\"panelIndex\":\"8c5df6b2-0cc9-4887-a2d9-6a9a192f3407\",\"embeddableConfig\":{},\"panelRefName\":\"panel_0\"}]", + "refreshInterval": { "pause": true, "value": 0 }, + "timeFrom": "2015-09-19T17:34:10.297Z", + "timeRestore": true, + "timeTo": "2015-09-23T00:09:17.180Z", + "title": "Not Delayed", + "version": 1 + }, + "references": [ + { + "name" : "panel_0", + "type" : "visualization", + "id" : "14501a50-01e3-11eb-9b63-176d7b28a352" + } + ], + "type": "dashboard", + "updated_at": "2020-03-19T11:59:53.701Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "dashboard:a41c6790-075d-11eb-be70-0bd5e8b57d02", + "index": ".kibana", + "source": { + "dashboard": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "optionsJSON": "{\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[{\"version\":\"7.7.0\",\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"eedeb943-5cfc-4e2c-bc1a-10ce06345cc1\"},\"panelIndex\":\"eedeb943-5cfc-4e2c-bc1a-10ce06345cc1\",\"embeddableConfig\":{},\"panelRefName\":\"panel_0\"}]", + "refreshInterval": { "pause": true, "value": 0 }, + "timeFrom": "2015-09-19T17:34:10.297Z", + "timeRestore": true, + "timeTo": "2015-09-23T00:09:17.180Z", + "title": "Delayed 15s", + "version": 1 + }, + "references": [ + { + "name" : "panel_0", + "type" : "visualization", + "id" : "50a67010-075d-11eb-be70-0bd5e8b57d02" + } + ], + "type": "dashboard", + "updated_at": "2020-03-19T11:59:53.701Z" + } + } +} + + diff --git a/x-pack/test/functional/es_archives/dashboard/async_search/mappings.json b/x-pack/test/functional/es_archives/dashboard/async_search/mappings.json new file mode 100644 index 00000000000000..210fade40c648f --- /dev/null +++ b/x-pack/test/functional/es_archives/dashboard/async_search/mappings.json @@ -0,0 +1,244 @@ +{ + "type": "index", + "value": { + "index": ".kibana", + "mappings": { + "properties": { + "config": { + "dynamic": "true", + "properties": { + "buildNum": { + "type": "keyword" + } + } + }, + "dashboard": { + "dynamic": "strict", + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "optionsJSON": { + "type": "text" + }, + "panelsJSON": { + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "type": "keyword" + }, + "pause": { + "type": "boolean" + }, + "section": { + "type": "integer" + }, + "value": { + "type": "integer" + } + } + }, + "timeFrom": { + "type": "keyword" + }, + "timeRestore": { + "type": "boolean" + }, + "timeTo": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "index-pattern": { + "dynamic": "strict", + "properties": { + "fieldFormatMap": { + "type": "text" + }, + "fields": { + "type": "text" + }, + "intervalName": { + "type": "keyword" + }, + "notExpandable": { + "type": "boolean" + }, + "sourceFilters": { + "type": "text" + }, + "timeFieldName": { + "type": "keyword" + }, + "title": { + "type": "text" + } + } + }, + "search": { + "dynamic": "strict", + "properties": { + "columns": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "sort": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "server": { + "dynamic": "strict", + "properties": { + "uuid": { + "type": "keyword" + } + } + }, + "timelion-sheet": { + "dynamic": "strict", + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "type": { + "type": "keyword" + }, + "url": { + "dynamic": "strict", + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "visualization": { + "dynamic": "strict", + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "savedSearchId": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "type": "text" + } + } + } + } + }, + "settings": { + "index": { + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +} From 4da338a0cbe0d652922f94047bbdef7369583067 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Wed, 7 Oct 2020 13:35:47 -0400 Subject: [PATCH 08/32] [SECURITY_SOLUTION][ENDPOINT] Trusted Apps - fix error for duplicate fields to correctly mention the field at fault (#79853) * Fix error for duplicate fields to correctly mention the field at fault * Add new tests to duplicate field validation --- .../endpoint/schema/trusted_apps.test.ts | 21 +++++++++++++++++-- .../common/endpoint/schema/trusted_apps.ts | 9 +++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts index ab3549c11bef4e..f83496737bcc62 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts @@ -293,11 +293,28 @@ describe('When invoking Trusted Apps Schema', () => { }); it('should validate that `entry.field` is used only once', () => { - const bodyMsg = { + let bodyMsg = { ...getCreateTrustedAppItem(), entries: [getTrustedAppItemEntryItem(), getTrustedAppItemEntryItem()], }; - expect(() => body.validate(bodyMsg)).toThrow(); + expect(() => body.validate(bodyMsg)).toThrow('[Path] field can only be used once'); + + bodyMsg = { + ...getCreateTrustedAppItem(), + entries: [ + { + ...getTrustedAppItemEntryItem(), + field: 'process.hash.*', + value: VALID_HASH_MD5, + }, + { + ...getTrustedAppItemEntryItem(), + field: 'process.hash.*', + value: VALID_HASH_MD5, + }, + ], + }; + expect(() => body.validate(bodyMsg)).toThrow('[Hash] field can only be used once'); }); it('should validate Hash field valid value', () => { diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts index 29957682f72fc8..60672cce972a31 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts @@ -5,6 +5,7 @@ */ import { schema } from '@kbn/config-schema'; +import { TrustedApp } from '../types'; const hashLengths: readonly number[] = [ 32, // MD5 @@ -13,6 +14,12 @@ const hashLengths: readonly number[] = [ ]; const hasInvalidCharacters = /[^0-9a-f]/i; +const entryFieldLabels: { [k in TrustedApp['entries'][0]['field']]: string } = { + 'process.hash.*': 'Hash', + 'process.executable.caseless': 'Path', + 'process.code_signature': 'Signer', +}; + export const DeleteTrustedAppsRequestSchema = { params: schema.object({ id: schema.string(), @@ -47,7 +54,7 @@ export const PostTrustedAppCreateRequestSchema = { const usedFields: string[] = []; for (const { field, value } of entries) { if (usedFields.includes(field)) { - return `[Hash] field can only be used once`; + return `[${entryFieldLabels[field]}] field can only be used once`; } usedFields.push(field); From 82a6ddbc2aefef00c88c0de36406372f7a8bd59a Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Wed, 7 Oct 2020 19:52:11 +0200 Subject: [PATCH 09/32] [Bugfix][Filters] Add strike-through line for disabled filters in warning and error state (#79641) Adds the strikethrough for all disabled filter pills no matter the error/warning states --- src/plugins/data/public/ui/filter_bar/_global_filter_item.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss b/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss index 73ec14de82b43b..b79b7c038f9cc4 100644 --- a/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss +++ b/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss @@ -33,8 +33,6 @@ } .globalFilterItem-isError, .globalFilterItem-isWarning { - text-decoration: none; - .globalFilterLabel__value { font-weight: $euiFontWeightBold; } From d1e1050817f8013821de6e08a1b62d1c5119297c Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Wed, 7 Oct 2020 20:44:44 +0200 Subject: [PATCH 10/32] Allow idleTimeout/lifespan larger than 32-bit signed integer. (#79858) --- .../public/session/session_timeout.test.tsx | 69 +++++++++++++++++++ .../public/session/session_timeout.tsx | 38 +++++++++- 2 files changed, 104 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security/public/session/session_timeout.test.tsx b/x-pack/plugins/security/public/session/session_timeout.test.tsx index 11aadcff377ef7..c49cd8cd4ed9ce 100644 --- a/x-pack/plugins/security/public/session/session_timeout.test.tsx +++ b/x-pack/plugins/security/public/session/session_timeout.test.tsx @@ -109,6 +109,7 @@ describe('Session Timeout', () => { afterEach(async () => { jest.clearAllMocks(); + sessionTimeout.stop(); }); afterAll(() => { @@ -147,6 +148,27 @@ describe('Session Timeout', () => { expect(close).toHaveBeenCalled(); expect(cleanup).toHaveBeenCalled(); }); + + test(`stop works properly for large timeouts`, async () => { + http.fetch.mockResolvedValue({ + ...defaultSessionInfo, + idleTimeoutExpiration: now + 5_000_000_000, + }); + await sessionTimeout.start(); + + // Advance timers far enough to call intermediate `setTimeout` multiple times, but before any + // of the timers is supposed to be triggered. + jest.advanceTimersByTime(5_000_000_000 - (60 + 5 + 2) * 1000); + + sessionTimeout.stop(); + + // Advance timer even further and make sure that timers were properly cleaned up. + jest.runAllTimers(); + + expect(http.fetch).toHaveBeenCalledTimes(1); + expect(sessionExpired.logout).not.toHaveBeenCalled(); + expectNoWarningToast(notifications); + }); }); describe('API calls', () => { @@ -188,6 +210,21 @@ describe('Session Timeout', () => { expectIdleTimeoutWarningToast(notifications); }); + test(`shows idle timeout warning toast even for large timeouts`, async () => { + http.fetch.mockResolvedValue({ + ...defaultSessionInfo, + idleTimeoutExpiration: now + 5_000_000_000, + }); + await sessionTimeout.start(); + + // we display the warning a minute before we expire the the session, which is 5 seconds before it actually expires + jest.advanceTimersByTime(5_000_000_000 - 66 * 1000); + expectNoWarningToast(notifications); + + jest.advanceTimersByTime(1000); + expectIdleTimeoutWarningToast(notifications); + }); + test(`shows lifespan warning toast`, async () => { const sessionInfo = { now, @@ -203,6 +240,23 @@ describe('Session Timeout', () => { expectLifespanWarningToast(notifications); }); + test(`shows lifespan warning toast even for large timeouts`, async () => { + const sessionInfo = { + ...defaultSessionInfo, + idleTimeoutExpiration: null, + lifespanExpiration: now + 5_000_000_000, + }; + http.fetch.mockResolvedValue(sessionInfo); + await sessionTimeout.start(); + + // we display the warning a minute before we expire the the session, which is 5 seconds before it actually expires + jest.advanceTimersByTime(5_000_000_000 - 66 * 1000); + expectNoWarningToast(notifications); + + jest.advanceTimersByTime(1000); + expectLifespanWarningToast(notifications); + }); + test(`extend only results in an HTTP call if a warning is shown`, async () => { await sessionTimeout.start(); expect(http.fetch).toHaveBeenCalledTimes(1); @@ -328,6 +382,21 @@ describe('Session Timeout', () => { expect(sessionExpired.logout).toHaveBeenCalled(); }); + test(`expires the session 5 seconds before it really expires even for large timeouts`, async () => { + http.fetch.mockResolvedValue({ + ...defaultSessionInfo, + idleTimeoutExpiration: now + 5_000_000_000, + }); + + await sessionTimeout.start(); + + jest.advanceTimersByTime(5_000_000_000 - 6000); + expect(sessionExpired.logout).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(1000); + expect(sessionExpired.logout).toHaveBeenCalled(); + }); + test(`extend delays the expiration`, async () => { await sessionTimeout.start(); expect(http.fetch).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/security/public/session/session_timeout.tsx b/x-pack/plugins/security/public/session/session_timeout.tsx index b06d8fffd4b629..858be46f287e91 100644 --- a/x-pack/plugins/security/public/session/session_timeout.tsx +++ b/x-pack/plugins/security/public/session/session_timeout.tsx @@ -140,13 +140,21 @@ export class SessionTimeout implements ISessionTimeout { const timeoutVal = timeout - WARNING_MS - GRACE_PERIOD_MS - SESSION_CHECK_MS; if (timeoutVal > 0 && !isLifespanTimeout) { // we should check for the latest session info before the warning displays - this.fetchTimer = window.setTimeout(this.fetchSessionInfoAndResetTimers, timeoutVal); + this.startTimer( + (timeoutID) => (this.fetchTimer = timeoutID), + this.fetchSessionInfoAndResetTimers, + timeoutVal + ); } - this.warningTimer = window.setTimeout( + + this.startTimer( + (timeoutID) => (this.warningTimer = timeoutID), this.showWarning, Math.max(timeout - WARNING_MS - GRACE_PERIOD_MS, 0) ); - this.expirationTimer = window.setTimeout( + + this.startTimer( + (timeoutID) => (this.expirationTimer = timeoutID), () => this.sessionExpired.logout(), Math.max(timeout - GRACE_PERIOD_MS, 0) ); @@ -206,4 +214,28 @@ export class SessionTimeout implements ISessionTimeout { } this.warningToast = this.notifications.toasts.add(toast); }; + + /** + * Starts a timer that uses a native `setTimeout` under the hood. When `timeout` is larger + * than the maximum supported one then method calls itself recursively as many times as needed. + * @param updater Method that is supposed to update a reference to a native timer ID that can be + * used with native `clearTimeout`. It's essential for the larger timeouts when `setTimeout` is + * called multiple times and timer ID changes. + * when timer ID changes + * @param callback A function to be executed after the timer expires. + * @param timeout The time, in milliseconds the timer should wait before the specified function is + * executed. + */ + private startTimer(updater: (timeoutID: number) => void, callback: () => void, timeout: number) { + // Max timeout is the largest possible 32-bit signed integer or 2,147,483,647 or 0x7fffffff. + const maxTimeout = 0x7fffffff; + updater( + timeout > maxTimeout + ? window.setTimeout( + () => this.startTimer(updater, callback, timeout - maxTimeout), + maxTimeout + ) + : window.setTimeout(callback, timeout) + ); + } } From 8c54b397087b77ae89d1b83fa71b377e7d765532 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Wed, 7 Oct 2020 12:45:27 -0600 Subject: [PATCH 11/32] add --no-validate option to bypass validateYarnLock (#79878) --- packages/kbn-pm/dist/index.js | 12 +++++++++--- packages/kbn-pm/src/cli.ts | 4 +++- packages/kbn-pm/src/commands/bootstrap.ts | 4 +++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 48dba22505232a..2e50f4214beb4d 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -196,6 +196,7 @@ function help() { --oss Do not include the x-pack when running command. --skip-kibana-plugins Filter all plugins in ./plugins and ../kibana-extra when running command. --no-cache Disable the bootstrap cache + --no-validate Disable the bootstrap yarn.lock validation --verbose Set log level to verbose --debug Set log level to debug --quiet Set log level to error @@ -222,9 +223,10 @@ async function run(argv) { i: 'include' }, default: { - cache: true + cache: true, + validate: true }, - boolean: ['prefer-offline', 'frozen-lockfile', 'cache'] + boolean: ['prefer-offline', 'frozen-lockfile', 'cache', 'validate'] }); const args = options._; @@ -8998,7 +9000,11 @@ const BootstrapCommand = { } const yarnLock = await Object(_utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__["readYarnLock"])(kbn); - await Object(_utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__["validateYarnLock"])(kbn, yarnLock); + + if (options.validate) { + await Object(_utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__["validateYarnLock"])(kbn, yarnLock); + } + await Object(_utils_link_project_executables__WEBPACK_IMPORTED_MODULE_0__["linkProjectExecutables"])(projects, projectGraph); /** * At the end of the bootstrapping process we call all `kbn:bootstrap` scripts diff --git a/packages/kbn-pm/src/cli.ts b/packages/kbn-pm/src/cli.ts index 816e84c13bbe96..92ddf3d957cd50 100644 --- a/packages/kbn-pm/src/cli.ts +++ b/packages/kbn-pm/src/cli.ts @@ -47,6 +47,7 @@ function help() { --oss Do not include the x-pack when running command. --skip-kibana-plugins Filter all plugins in ./plugins and ../kibana-extra when running command. --no-cache Disable the bootstrap cache + --no-validate Disable the bootstrap yarn.lock validation --verbose Set log level to verbose --debug Set log level to debug --quiet Set log level to error @@ -80,8 +81,9 @@ export async function run(argv: string[]) { }, default: { cache: true, + validate: true, }, - boolean: ['prefer-offline', 'frozen-lockfile', 'cache'], + boolean: ['prefer-offline', 'frozen-lockfile', 'cache', 'validate'], }); const args = options._; diff --git a/packages/kbn-pm/src/commands/bootstrap.ts b/packages/kbn-pm/src/commands/bootstrap.ts index 7cf89c5f08f96b..0fa3f355ae9d68 100644 --- a/packages/kbn-pm/src/commands/bootstrap.ts +++ b/packages/kbn-pm/src/commands/bootstrap.ts @@ -58,7 +58,9 @@ export const BootstrapCommand: ICommand = { const yarnLock = await readYarnLock(kbn); - await validateYarnLock(kbn, yarnLock); + if (options.validate) { + await validateYarnLock(kbn, yarnLock); + } await linkProjectExecutables(projects, projectGraph); From 4bd58ca2605652439c164f725ab5b6b294dbef14 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 7 Oct 2020 19:51:44 +0100 Subject: [PATCH 12/32] chore(NA): add missing branches into backportrc configuration file (#79848) * chore(NA): add missing branches into backportrc configuration file * chore(NA): fix extra line for defined regexs --- .backportrc.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.backportrc.json b/.backportrc.json index 3f1d639e9a4808..a97c82ca0efa94 100644 --- a/.backportrc.json +++ b/.backportrc.json @@ -3,6 +3,7 @@ "targetBranchChoices": [ { "name": "master", "checked": true }, { "name": "7.x", "checked": true }, + "7.10", "7.9", "7.8", "7.7", @@ -27,7 +28,7 @@ "targetPRLabels": ["backport"], "branchLabelMapping": { "^v8.0.0$": "master", - "^v7.10.0$": "7.x", + "^v7.11.0$": "7.x", "^v(\\d+).(\\d+).\\d+$": "$1.$2" } } From 6ee48d85bfd11ce2a019971c278a1b52630f301f Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 7 Oct 2020 11:50:26 -0700 Subject: [PATCH 13/32] skip flaky tests (#79891) --- packages/kbn-pm/src/commands/bootstrap.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/kbn-pm/src/commands/bootstrap.test.ts b/packages/kbn-pm/src/commands/bootstrap.test.ts index 956c4e72933baa..479a226839b99a 100644 --- a/packages/kbn-pm/src/commands/bootstrap.test.ts +++ b/packages/kbn-pm/src/commands/bootstrap.test.ts @@ -75,7 +75,8 @@ afterEach(() => { jest.restoreAllMocks(); }); -test('handles dependencies of dependencies', async () => { +// FLAKY: https://github.com/elastic/kibana/issues/79891 +test.skip('handles dependencies of dependencies', async () => { const kibana = createProject({ dependencies: { bar: '1.0.0', @@ -141,7 +142,8 @@ test('handles dependencies of dependencies', async () => { `); }); -test('does not run installer if no deps in package', async () => { +// FLAKY: https://github.com/elastic/kibana/issues/79891 +test.skip('does not run installer if no deps in package', async () => { const kibana = createProject({ dependencies: { bar: '1.0.0', From 37f9db4224fb3c5505d1a19cd94bd17628f67656 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 7 Oct 2020 12:12:33 -0700 Subject: [PATCH 14/32] remove skipped snapshots --- .../__snapshots__/bootstrap.test.ts.snap | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap b/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap index be146d710c87a8..0b9e07b00d9066 100644 --- a/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap +++ b/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap @@ -121,15 +121,6 @@ Array [ ] `; -exports[`does not run installer if no deps in package: install in dir 1`] = ` -Array [ - Array [ - "/packages/kbn-pm/src/commands", - Array [], - ], -] -`; - exports[`handles "frozen-lockfile": install in dir 1`] = ` Array [ Array [ @@ -140,20 +131,3 @@ Array [ ], ] `; - -exports[`handles dependencies of dependencies: install in dir 1`] = ` -Array [ - Array [ - "/packages/kbn-pm/src/commands", - Array [], - ], - Array [ - "/packages/kbn-pm/src/commands/packages/bar", - Array [], - ], - Array [ - "/packages/kbn-pm/src/commands/packages/foo", - Array [], - ], -] -`; From deef4c5b4c0c82656773ddb0d352f7f17b9ff2f3 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 7 Oct 2020 12:35:26 -0700 Subject: [PATCH 15/32] [es/mappings] remove doc_values from text fields (#79869) Co-authored-by: spalger --- src/plugins/dashboard/server/saved_objects/dashboard.ts | 6 +++--- src/plugins/discover/server/saved_objects/search.ts | 2 +- .../visualizations/server/saved_objects/visualization.ts | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/plugins/dashboard/server/saved_objects/dashboard.ts b/src/plugins/dashboard/server/saved_objects/dashboard.ts index 850b2470dd475e..a85f67f5ba56ad 100644 --- a/src/plugins/dashboard/server/saved_objects/dashboard.ts +++ b/src/plugins/dashboard/server/saved_objects/dashboard.ts @@ -46,10 +46,10 @@ export const dashboardSavedObjectType: SavedObjectsType = { description: { type: 'text' }, hits: { type: 'integer', index: false, doc_values: false }, kibanaSavedObjectMeta: { - properties: { searchSourceJSON: { type: 'text', index: false, doc_values: false } }, + properties: { searchSourceJSON: { type: 'text', index: false } }, }, - optionsJSON: { type: 'text', index: false, doc_values: false }, - panelsJSON: { type: 'text', index: false, doc_values: false }, + optionsJSON: { type: 'text', index: false }, + panelsJSON: { type: 'text', index: false }, refreshInterval: { properties: { display: { type: 'keyword', index: false, doc_values: false }, diff --git a/src/plugins/discover/server/saved_objects/search.ts b/src/plugins/discover/server/saved_objects/search.ts index c13550e543ab60..a6e42f956a025f 100644 --- a/src/plugins/discover/server/saved_objects/search.ts +++ b/src/plugins/discover/server/saved_objects/search.ts @@ -48,7 +48,7 @@ export const searchSavedObjectType: SavedObjectsType = { hits: { type: 'integer', index: false, doc_values: false }, kibanaSavedObjectMeta: { properties: { - searchSourceJSON: { type: 'text', index: false, doc_values: false }, + searchSourceJSON: { type: 'text', index: false }, }, }, sort: { type: 'keyword', index: false, doc_values: false }, diff --git a/src/plugins/visualizations/server/saved_objects/visualization.ts b/src/plugins/visualizations/server/saved_objects/visualization.ts index ad7618a8640bab..5261b2cac7dcfe 100644 --- a/src/plugins/visualizations/server/saved_objects/visualization.ts +++ b/src/plugins/visualizations/server/saved_objects/visualization.ts @@ -45,13 +45,13 @@ export const visualizationSavedObjectType: SavedObjectsType = { properties: { description: { type: 'text' }, kibanaSavedObjectMeta: { - properties: { searchSourceJSON: { type: 'text', index: false, doc_values: false } }, + properties: { searchSourceJSON: { type: 'text', index: false } }, }, savedSearchRefName: { type: 'keyword', index: false, doc_values: false }, title: { type: 'text' }, - uiStateJSON: { type: 'text', index: false, doc_values: false }, + uiStateJSON: { type: 'text', index: false }, version: { type: 'integer' }, - visState: { type: 'text', index: false, doc_values: false }, + visState: { type: 'text', index: false }, }, }, migrations: visualizationSavedObjectTypeMigrations, From 128485c216e1241d0a318b3d3527624bd81ee316 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 7 Oct 2020 12:51:02 -0700 Subject: [PATCH 16/32] skip flaky suite (#79910) --- src/plugins/data/public/ui/search_bar/search_bar.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/data/public/ui/search_bar/search_bar.test.tsx b/src/plugins/data/public/ui/search_bar/search_bar.test.tsx index a2f6c7e82d1a40..a89b9bb7f91ef8 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.test.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.test.tsx @@ -117,7 +117,8 @@ function wrapSearchBarInContext(testProps: any) { ); } -describe('SearchBar', () => { +// FLAKY: https://github.com/elastic/kibana/issues/79910 +describe.skip('SearchBar', () => { const SEARCH_BAR_TEST_ID = 'globalQueryBar'; const SEARCH_BAR_ROOT = '.globalQueryBar'; const FILTER_BAR = '.globalFilterBar'; From cb1af1b472c4500c3435c419a0287cc985e92230 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Mon, 5 Oct 2020 14:30:49 -0400 Subject: [PATCH 17/32] Skip failing suite (#79522) (cherry picked from commit 9011f42d7ffa8b1ad690d2ef4aa03216bc4401b5) --- .../cypress/integration/alerts_detection_rules_eql.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts index 087c91fda02b89..5745a545f048b0 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts @@ -87,7 +87,8 @@ const expectedNumberOfRules = 1; const expectedNumberOfAlerts = 7; const expectedNumberOfSequenceAlerts = 1; -describe('Detection rules, EQL', () => { +// Failing: See https://github.com/elastic/kibana/issues/79522 +describe.skip('Detection rules, EQL', () => { beforeEach(() => { esArchiverLoad('timeline'); }); From 1062957648fa883d4e9964263b8ea618b967d45d Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 7 Oct 2020 14:11:55 -0700 Subject: [PATCH 18/32] skip flaky suite (#78689) --- test/functional/apps/discover/_field_data.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/discover/_field_data.js b/test/functional/apps/discover/_field_data.js index f0472fb5a3da5f..d9cb09432b26f1 100644 --- a/test/functional/apps/discover/_field_data.js +++ b/test/functional/apps/discover/_field_data.js @@ -27,7 +27,8 @@ export default function ({ getService, getPageObjects }) { const queryBar = getService('queryBar'); const PageObjects = getPageObjects(['common', 'header', 'discover', 'visualize', 'timePicker']); - describe('discover tab', function describeIndexTests() { + // FLAKY: https://github.com/elastic/kibana/issues/78689 + describe.skip('discover tab', function describeIndexTests() { this.tags('includeFirefox'); before(async function () { await esArchiver.loadIfNeeded('logstash_functional'); From 4ce3e1ce03336c8b838f881a77a75d724e469315 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 7 Oct 2020 14:30:12 -0700 Subject: [PATCH 19/32] remove entire suite as partial skips aren't doing the trick --- .../__snapshots__/bootstrap.test.ts.snap | 133 ---------- .../kbn-pm/src/commands/bootstrap.test.ts | 248 ------------------ 2 files changed, 381 deletions(-) delete mode 100644 packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap delete mode 100644 packages/kbn-pm/src/commands/bootstrap.test.ts diff --git a/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap b/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap deleted file mode 100644 index 0b9e07b00d9066..00000000000000 --- a/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap +++ /dev/null @@ -1,133 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`calls "kbn:bootstrap" scripts and links executables after installing deps: link bins 1`] = ` -Array [ - Array [ - Map { - "kibana" => Project { - "allDependencies": Object { - "bar": "1.0.0", - }, - "devDependencies": Object {}, - "isWorkspaceProject": false, - "isWorkspaceRoot": true, - "json": Object { - "dependencies": Object { - "bar": "1.0.0", - }, - "name": "kibana", - "version": "1.0.0", - "workspaces": Object { - "packages": Array [ - "packages/*", - ], - }, - }, - "nodeModulesLocation": "/packages/kbn-pm/src/commands/node_modules", - "packageJsonLocation": "/packages/kbn-pm/src/commands/package.json", - "path": "/packages/kbn-pm/src/commands", - "productionDependencies": Object { - "bar": "1.0.0", - }, - "scripts": Object {}, - "targetLocation": "/packages/kbn-pm/src/commands/target", - "version": "1.0.0", - }, - "bar" => Project { - "allDependencies": Object {}, - "devDependencies": Object {}, - "isWorkspaceProject": false, - "isWorkspaceRoot": false, - "json": Object { - "name": "bar", - "scripts": Object { - "kbn:bootstrap": "node ./bar.js", - }, - "version": "1.0.0", - }, - "nodeModulesLocation": "/packages/kbn-pm/src/commands/packages/bar/node_modules", - "packageJsonLocation": "/packages/kbn-pm/src/commands/packages/bar/package.json", - "path": "/packages/kbn-pm/src/commands/packages/bar", - "productionDependencies": Object {}, - "scripts": Object { - "kbn:bootstrap": "node ./bar.js", - }, - "targetLocation": "/packages/kbn-pm/src/commands/packages/bar/target", - "version": "1.0.0", - }, - }, - Map { - "kibana" => Array [ - Project { - "allDependencies": Object {}, - "devDependencies": Object {}, - "isWorkspaceProject": false, - "isWorkspaceRoot": false, - "json": Object { - "name": "bar", - "scripts": Object { - "kbn:bootstrap": "node ./bar.js", - }, - "version": "1.0.0", - }, - "nodeModulesLocation": "/packages/kbn-pm/src/commands/packages/bar/node_modules", - "packageJsonLocation": "/packages/kbn-pm/src/commands/packages/bar/package.json", - "path": "/packages/kbn-pm/src/commands/packages/bar", - "productionDependencies": Object {}, - "scripts": Object { - "kbn:bootstrap": "node ./bar.js", - }, - "targetLocation": "/packages/kbn-pm/src/commands/packages/bar/target", - "version": "1.0.0", - }, - ], - "bar" => Array [], - }, - ], -] -`; - -exports[`calls "kbn:bootstrap" scripts and links executables after installing deps: script 1`] = ` -Array [ - Array [ - Object { - "args": Array [], - "debug": undefined, - "pkg": Project { - "allDependencies": Object {}, - "devDependencies": Object {}, - "isWorkspaceProject": false, - "isWorkspaceRoot": false, - "json": Object { - "name": "bar", - "scripts": Object { - "kbn:bootstrap": "node ./bar.js", - }, - "version": "1.0.0", - }, - "nodeModulesLocation": "/packages/kbn-pm/src/commands/packages/bar/node_modules", - "packageJsonLocation": "/packages/kbn-pm/src/commands/packages/bar/package.json", - "path": "/packages/kbn-pm/src/commands/packages/bar", - "productionDependencies": Object {}, - "scripts": Object { - "kbn:bootstrap": "node ./bar.js", - }, - "targetLocation": "/packages/kbn-pm/src/commands/packages/bar/target", - "version": "1.0.0", - }, - "script": "kbn:bootstrap", - }, - ], -] -`; - -exports[`handles "frozen-lockfile": install in dir 1`] = ` -Array [ - Array [ - "/packages/kbn-pm/src/commands", - Array [ - "--frozen-lockfile", - ], - ], -] -`; diff --git a/packages/kbn-pm/src/commands/bootstrap.test.ts b/packages/kbn-pm/src/commands/bootstrap.test.ts deleted file mode 100644 index 479a226839b99a..00000000000000 --- a/packages/kbn-pm/src/commands/bootstrap.test.ts +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -jest.mock('../utils/scripts'); -jest.mock('../utils/link_project_executables'); -jest.mock('../utils/validate_yarn_lock'); - -import { resolve } from 'path'; - -import { ToolingLogCollectingWriter } from '@kbn/dev-utils/tooling_log'; - -import { absolutePathSnapshotSerializer, stripAnsiSnapshotSerializer } from '../test_helpers'; -import { linkProjectExecutables } from '../utils/link_project_executables'; -import { IPackageJson } from '../utils/package_json'; -import { Project } from '../utils/project'; -import { buildProjectGraph } from '../utils/projects'; -import { installInDir, runScriptInPackageStreaming, yarnWorkspacesInfo } from '../utils/scripts'; -import { BootstrapCommand } from './bootstrap'; -import { Kibana } from '../utils/kibana'; -import { log } from '../utils/log'; - -const mockInstallInDir = installInDir as jest.Mock; -const mockRunScriptInPackageStreaming = runScriptInPackageStreaming as jest.Mock; -const mockLinkProjectExecutables = linkProjectExecutables as jest.Mock; -const mockYarnWorkspacesInfo = yarnWorkspacesInfo as jest.Mock; - -const logWriter = new ToolingLogCollectingWriter('debug'); -log.setLogLevel('silent'); -log.setWriters([logWriter]); -beforeEach(() => { - logWriter.messages.length = 0; -}); - -const createProject = (packageJson: IPackageJson, path = '.') => { - const project = new Project( - { - name: 'kibana', - version: '1.0.0', - ...packageJson, - }, - resolve(__dirname, path) - ); - - if (packageJson.workspaces) { - project.isWorkspaceRoot = true; - } - - return project; -}; -expect.addSnapshotSerializer(absolutePathSnapshotSerializer); -expect.addSnapshotSerializer(stripAnsiSnapshotSerializer); - -beforeEach(() => { - mockYarnWorkspacesInfo.mockResolvedValue({}); -}); - -afterEach(() => { - jest.resetAllMocks(); - jest.restoreAllMocks(); -}); - -// FLAKY: https://github.com/elastic/kibana/issues/79891 -test.skip('handles dependencies of dependencies', async () => { - const kibana = createProject({ - dependencies: { - bar: '1.0.0', - }, - workspaces: { - packages: ['packages/*'], - }, - }); - const foo = createProject( - { - dependencies: { - bar: 'link:../bar', - }, - name: 'foo', - }, - 'packages/foo' - ); - const bar = createProject( - { - dependencies: { - baz: 'link:../baz', - }, - name: 'bar', - }, - 'packages/bar' - ); - const baz = createProject( - { - name: 'baz', - }, - 'packages/baz' - ); - - const projects = new Map([ - ['kibana', kibana], - ['foo', foo], - ['bar', bar], - ['baz', baz], - ]); - const kbn = new Kibana(projects); - const projectGraph = buildProjectGraph(projects); - - await BootstrapCommand.run(projects, projectGraph, { - extraArgs: [], - options: {}, - rootPath: '', - kbn, - }); - - expect(mockInstallInDir.mock.calls).toMatchSnapshot('install in dir'); - expect(logWriter.messages).toMatchInlineSnapshot(` - Array [ - info [kibana] running yarn, - "", - "", - info [bar] running yarn, - "", - "", - info [foo] running yarn, - "", - "", - ] - `); -}); - -// FLAKY: https://github.com/elastic/kibana/issues/79891 -test.skip('does not run installer if no deps in package', async () => { - const kibana = createProject({ - dependencies: { - bar: '1.0.0', - }, - workspaces: { - packages: ['packages/*'], - }, - }); - // bar has no dependencies - const bar = createProject( - { - name: 'bar', - }, - 'packages/bar' - ); - - const projects = new Map([ - ['kibana', kibana], - ['bar', bar], - ]); - const kbn = new Kibana(projects); - const projectGraph = buildProjectGraph(projects); - - await BootstrapCommand.run(projects, projectGraph, { - extraArgs: [], - options: {}, - rootPath: '', - kbn, - }); - - expect(mockInstallInDir.mock.calls).toMatchSnapshot('install in dir'); - expect(logWriter.messages).toMatchInlineSnapshot(` - Array [ - info [kibana] running yarn, - "", - "", - ] - `); -}); - -test('handles "frozen-lockfile"', async () => { - const kibana = createProject({ - dependencies: { - foo: '2.2.0', - }, - workspaces: { - packages: ['packages/*'], - }, - }); - - const projects = new Map([['kibana', kibana]]); - const kbn = new Kibana(projects); - const projectGraph = buildProjectGraph(projects); - - await BootstrapCommand.run(projects, projectGraph, { - extraArgs: [], - options: { - 'frozen-lockfile': true, - }, - rootPath: '', - kbn, - }); - - expect(mockInstallInDir.mock.calls).toMatchSnapshot('install in dir'); -}); - -test('calls "kbn:bootstrap" scripts and links executables after installing deps', async () => { - const kibana = createProject({ - dependencies: { - bar: '1.0.0', - }, - workspaces: { - packages: ['packages/*'], - }, - }); - const bar = createProject( - { - name: 'bar', - scripts: { - 'kbn:bootstrap': 'node ./bar.js', - }, - }, - 'packages/bar' - ); - - const projects = new Map([ - ['kibana', kibana], - ['bar', bar], - ]); - const kbn = new Kibana(projects); - const projectGraph = buildProjectGraph(projects); - - await BootstrapCommand.run(projects, projectGraph, { - extraArgs: [], - options: {}, - rootPath: '', - kbn, - }); - - expect(mockLinkProjectExecutables.mock.calls).toMatchSnapshot('link bins'); - expect(mockRunScriptInPackageStreaming.mock.calls).toMatchSnapshot('script'); -}); From 8f41c59be0ee1c629a26c9dd3219d0f5defabddf Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 7 Oct 2020 14:47:21 -0700 Subject: [PATCH 20/32] share tslib across bundles (#79915) Co-authored-by: spalger --- packages/kbn-ui-shared-deps/entry.js | 3 +++ packages/kbn-ui-shared-deps/index.js | 5 +++++ packages/kbn-ui-shared-deps/package.json | 1 + 3 files changed, 9 insertions(+) diff --git a/packages/kbn-ui-shared-deps/entry.js b/packages/kbn-ui-shared-deps/entry.js index 966fb65406ac6a..d74b45f973eb13 100644 --- a/packages/kbn-ui-shared-deps/entry.js +++ b/packages/kbn-ui-shared-deps/entry.js @@ -53,5 +53,8 @@ export const ElasticEuiChartsTheme = require('@elastic/eui/dist/eui_charts_theme export const Lodash = require('lodash'); export const LodashFp = require('lodash/fp'); +// runtime deps which don't need to be copied across all bundles +export const TsLib = require('tslib'); + import * as Theme from './theme.ts'; export { Theme }; diff --git a/packages/kbn-ui-shared-deps/index.js b/packages/kbn-ui-shared-deps/index.js index a403ae63a8f70b..8f931fae4f3375 100644 --- a/packages/kbn-ui-shared-deps/index.js +++ b/packages/kbn-ui-shared-deps/index.js @@ -63,5 +63,10 @@ exports.externals = { '@elastic/eui/dist/eui_theme_dark.json': '__kbnSharedDeps__.Theme.euiDarkVars', lodash: '__kbnSharedDeps__.Lodash', 'lodash/fp': '__kbnSharedDeps__.LodashFp', + + /** + * runtime deps which don't need to be copied across all bundles + */ + tslib: '__kbnSharedDeps__.TsLib', }; exports.publicPathLoader = require.resolve('./public_path_loader'); diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index e5ebb874e58aa2..0a154c537fec1e 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -33,6 +33,7 @@ "rxjs": "^6.5.5", "styled-components": "^5.1.0", "symbol-observable": "^1.2.0", + "tslib": "^2.0.0", "whatwg-fetch": "^3.0.0" }, "devDependencies": { From d0dad32526a6d4f6ac72e6881e901798a543188c Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Wed, 7 Oct 2020 15:01:36 -0700 Subject: [PATCH 21/32] Revert "skips test failing promotion (#79777)" (#79904) This reverts commit 92ce8f3040991bd4473a93e9ee89b1f46580b87a. --- .../apps/ml/data_frame_analytics/outlier_detection_creation.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts index e0bd88a6ab6426..b5b0f4c94f2621 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts @@ -11,8 +11,7 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); const editedDescription = 'Edited description'; - // https://github.com/elastic/kibana/issues/79777 - describe.skip('outlier detection creation', function () { + describe('outlier detection creation', function () { before(async () => { await esArchiver.loadIfNeeded('ml/ihp_outlier'); await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier', '@timestamp'); From 3c7529147ebd3d7c69d732453f0496ef6ac0b542 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 7 Oct 2020 15:20:31 -0700 Subject: [PATCH 22/32] [release notes] extract "dev docs" comment too (#79351) Co-authored-by: spalger Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/developer/contributing/index.asciidoc | 2 +- .../src/lib/get_note_from_description.test.ts | 16 ++++++++++------ .../src/lib/get_note_from_description.ts | 5 +++-- packages/kbn-release-notes/src/lib/pr_api.ts | 4 +++- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/docs/developer/contributing/index.asciidoc b/docs/developer/contributing/index.asciidoc index ecb37ffe9c97bf..ba4ab89d17c274 100644 --- a/docs/developer/contributing/index.asciidoc +++ b/docs/developer/contributing/index.asciidoc @@ -49,7 +49,7 @@ The Release Notes summarize what the PRs accomplish in language that is meaningf The text that appears in the Release Notes is pulled directly from your PR title, or a single paragraph of text that you specify in the PR description. -To use a single paragraph of text, enter `Release note:` or a `## Release note` header in the PR description, followed by your text. For example, refer to this https://github.com/elastic/kibana/pull/65796[PR] that uses the `## Release note` header. +To use a single paragraph of text, enter a `Release note:` or `## Release note` header in the PR description ("dev docs" works too), followed by your text. For example, refer to this https://github.com/elastic/kibana/pull/65796[PR] that uses the `## Release note` header. When you create the Release Notes text, use the following best practices: diff --git a/packages/kbn-release-notes/src/lib/get_note_from_description.test.ts b/packages/kbn-release-notes/src/lib/get_note_from_description.test.ts index 23dcb302f090d2..22b9713b78332d 100644 --- a/packages/kbn-release-notes/src/lib/get_note_from_description.test.ts +++ b/packages/kbn-release-notes/src/lib/get_note_from_description.test.ts @@ -35,7 +35,8 @@ it('extracts expected components from html', () => { ## Release Note: Checkout this feature - `) + `), + 'release note' ) ).toMatchInlineSnapshot(`"Checkout this feature"`); @@ -46,10 +47,11 @@ it('extracts expected components from html', () => { Fixes: #1234 - #### Release Note: + #### Dev docs: We fixed an issue - `) + `), + 'dev docs' ) ).toMatchInlineSnapshot(`"We fixed an issue"`); @@ -60,8 +62,9 @@ it('extracts expected components from html', () => { Fixes: #1234 - Release note: Checkout feature foo - `) + OTHER TITLE: Checkout feature foo + `), + 'other title' ) ).toMatchInlineSnapshot(`"Checkout feature foo"`); @@ -73,7 +76,8 @@ it('extracts expected components from html', () => { My PR description release note : bar - `) + `), + 'release note' ) ).toMatchInlineSnapshot(`"bar"`); }); diff --git a/packages/kbn-release-notes/src/lib/get_note_from_description.ts b/packages/kbn-release-notes/src/lib/get_note_from_description.ts index 57df203470a5a4..0d9135c431e361 100644 --- a/packages/kbn-release-notes/src/lib/get_note_from_description.ts +++ b/packages/kbn-release-notes/src/lib/get_note_from_description.ts @@ -19,11 +19,12 @@ import cheerio from 'cheerio'; -export function getNoteFromDescription(descriptionHtml: string) { +export function getNoteFromDescription(descriptionHtml: string, header: string) { + const re = new RegExp(`^(\\s*${header.toLowerCase()}(?:s)?\\s*:?\\s*)`, 'i'); const $ = cheerio.load(descriptionHtml); for (const el of $('p,h1,h2,h3,h4,h5').toArray()) { const text = $(el).text(); - const match = text.match(/^(\s*release note(?:s)?\s*:?\s*)/i); + const match = text.match(re); if (!match) { continue; diff --git a/packages/kbn-release-notes/src/lib/pr_api.ts b/packages/kbn-release-notes/src/lib/pr_api.ts index 1f26aa7ad86c36..5fa3dfdba10efd 100644 --- a/packages/kbn-release-notes/src/lib/pr_api.ts +++ b/packages/kbn-release-notes/src/lib/pr_api.ts @@ -178,7 +178,9 @@ export class PrApi { versions: labels .map((l) => Version.fromLabel(l)) .filter((v): v is Version => v instanceof Version), - note: getNoteFromDescription(node.bodyHTML), + note: + getNoteFromDescription(node.bodyHTML, 'release note') || + getNoteFromDescription(node.bodyHTML, 'dev docs'), }; } From fd066f7b63ee184f33f554dacef3c12e62625a35 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 7 Oct 2020 15:43:14 -0700 Subject: [PATCH 23/32] [kbn/optimizer] report limits with ci metrics (#78205) Co-authored-by: spalger Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../src/ci_stats_reporter/README.md | 15 +- .../ci_stats_reporter/ci_stats_reporter.ts | 8 +- packages/kbn-optimizer/limits.yml | 100 +++++++++++++ packages/kbn-optimizer/package.json | 2 + packages/kbn-optimizer/src/cli.ts | 32 ++++- packages/kbn-optimizer/src/index.ts | 1 + .../basic_optimization.test.ts.snap | 1 + .../basic_optimization.test.ts | 11 +- packages/kbn-optimizer/src/limits.ts | 83 +++++++++++ .../src/optimizer/get_output_stats.ts | 124 ++++++++++++++++ packages/kbn-optimizer/src/optimizer/index.ts | 1 + .../src/optimizer/optimizer_config.test.ts | 4 + .../src/optimizer/optimizer_config.ts | 13 +- .../src/report_optimizer_stats.ts | 135 +++--------------- test/scripts/checks/bundle_limits.sh | 5 + vars/githubPr.groovy | 31 ++-- vars/tasks.groovy | 1 + 17 files changed, 432 insertions(+), 135 deletions(-) create mode 100644 packages/kbn-optimizer/limits.yml create mode 100644 packages/kbn-optimizer/src/limits.ts create mode 100644 packages/kbn-optimizer/src/optimizer/get_output_stats.ts create mode 100755 test/scripts/checks/bundle_limits.sh diff --git a/packages/kbn-dev-utils/src/ci_stats_reporter/README.md b/packages/kbn-dev-utils/src/ci_stats_reporter/README.md index c7b98224c4e570..12fc33dfaffb0f 100644 --- a/packages/kbn-dev-utils/src/ci_stats_reporter/README.md +++ b/packages/kbn-dev-utils/src/ci_stats_reporter/README.md @@ -8,10 +8,23 @@ This class integrates with the `ciStats.trackBuild {}` Jenkins Pipeline function To create an instance of the reporter, import the class and call `CiStatsReporter.fromEnv(log)` (passing it a tooling log). -#### `CiStatsReporter#metrics(metrics: Array<{ group: string, id: string, value: number }>)` +#### `CiStatsReporter#metrics(metrics: Metric[])` Use this method to record metrics in the Kibana CI Stats service. +```ts +interface Metric { + group: string, + id: string, + value: number, + // optional limit, values which exceed the limit will fail PRs + limit?: number + // optional path, relative to the root of the repo, where config values + // are defined. Will be linked to in PRs which have overages. + limitConfigPath?: string +} +``` + Example: ```ts diff --git a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts index b0378ab6c5cd5a..a2f3b63daec50f 100644 --- a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts +++ b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts @@ -29,7 +29,13 @@ interface Config { buildId: string; } -export type CiStatsMetrics = Array<{ group: string; id: string; value: number }>; +export type CiStatsMetrics = Array<{ + group: string; + id: string; + value: number; + limit?: number; + limitConfigPath?: string; +}>; function parseConfig(log: ToolingLog) { const configJson = process.env.KIBANA_CI_STATS_CONFIG; diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml new file mode 100644 index 00000000000000..e395734928a4d9 --- /dev/null +++ b/packages/kbn-optimizer/limits.yml @@ -0,0 +1,100 @@ +pageLoadAssetSize: + advancedSettings: 27_596 + alerts: 106_936 + apm: 64_385 + apmOss: 18_996 + beatsManagement: 188_135 + bfetch: 41_874 + canvas: 1_066_647 + charts: 159_211 + cloud: 21_076 + console: 46_091 + core: 692_106 + crossClusterReplication: 65_408 + dashboard: 374_194 + dashboardEnhanced: 65_646 + dashboardMode: 22_716 + data: 1_170_713 + dataEnhanced: 50_420 + devTools: 38_637 + discover: 105_145 + discoverEnhanced: 42_730 + embeddable: 312_874 + embeddableEnhanced: 41_145 + enterpriseSearch: 35_741 + esUiShared: 326_654 + expressions: 224_136 + features: 31_211 + fileUpload: 24_717 + globalSearch: 43_548 + globalSearchBar: 62_888 + globalSearchProviders: 25_554 + graph: 31_504 + grokdebugger: 26_779 + home: 41_661 + indexLifecycleManagement: 107_090 + indexManagement: 140_608 + indexPatternManagement: 154_222 + infra: 197_873 + ingestManager: 415_829 + ingestPipelines: 58_003 + inputControlVis: 172_675 + inspector: 148_711 + kibanaLegacy: 107_711 + kibanaOverview: 56_279 + kibanaReact: 161_921 + kibanaUtils: 198_829 + lens: 96_624 + licenseManagement: 41_817 + licensing: 39_008 + lists: 183_665 + logstash: 53_548 + management: 46_112 + maps: 183_610 + mapsLegacy: 116_817 + mapsLegacyLicensing: 20_214 + ml: 82_187 + monitoring: 268_612 + navigation: 37_269 + newsfeed: 42_228 + observability: 89_709 + painlessLab: 179_748 + regionMap: 66_098 + remoteClusters: 51_327 + reporting: 183_418 + rollup: 97_204 + savedObjects: 108_518 + savedObjectsManagement: 100_503 + searchprofiler: 67_080 + security: 189_428 + securityOss: 30_806 + securitySolution: 622_387 + share: 99_061 + snapshotRestore: 79_032 + spaces: 387_915 + telemetry: 91_832 + telemetryManagementSection: 52_443 + tileMap: 65_337 + timelion: 29_920 + transform: 41_007 + triggersActionsUi: 170_001 + uiActions: 97_717 + uiActionsEnhanced: 349_511 + upgradeAssistant: 81_241 + uptime: 40_825 + urlDrilldown: 34_174 + urlForwarding: 32_579 + usageCollection: 39_762 + visDefaultEditor: 50_178 + visTypeMarkdown: 30_896 + visTypeMetric: 42_790 + visTypeTable: 94_934 + visTypeTagcloud: 37_575 + visTypeTimelion: 51_933 + visTypeTimeseries: 155_203 + visTypeVega: 153_573 + visTypeVislib: 242_838 + visTypeXy: 20_255 + visualizations: 295_025 + visualize: 57_431 + watcher: 43_598 diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index 52f9349aec696b..5d9a409919db18 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -22,6 +22,7 @@ "cpy": "^8.0.0", "core-js": "^3.6.5", "css-loader": "^3.4.2", + "dedent": "^0.7.0", "del": "^5.1.0", "execa": "^4.0.2", "file-loader": "^4.2.0", @@ -38,6 +39,7 @@ "postcss-loader": "^3.0.0", "raw-loader": "^3.1.0", "rxjs": "^6.5.5", + "js-yaml": "^3.14.0", "sass-loader": "^8.0.2", "source-map-support": "^0.5.19", "style-loader": "^1.1.3", diff --git a/packages/kbn-optimizer/src/cli.ts b/packages/kbn-optimizer/src/cli.ts index dcfb56be66efd8..386a7a50537345 100644 --- a/packages/kbn-optimizer/src/cli.ts +++ b/packages/kbn-optimizer/src/cli.ts @@ -28,6 +28,7 @@ import { logOptimizerState } from './log_optimizer_state'; import { OptimizerConfig } from './optimizer'; import { reportOptimizerStats } from './report_optimizer_stats'; import { runOptimizer } from './run_optimizer'; +import { validateLimitsForAllBundles, updateBundleLimits } from './limits'; run( async ({ log, flags }) => { @@ -93,14 +94,24 @@ run( throw createFlagError('expected --filter to be one or more strings'); } + const validateLimits = flags['validate-limits'] ?? false; + if (typeof validateLimits !== 'boolean') { + throw createFlagError('expected --validate-limits to have no value'); + } + + const updateLimits = flags['update-limits'] ?? false; + if (typeof updateLimits !== 'boolean') { + throw createFlagError('expected --update-limits to have no value'); + } + const config = OptimizerConfig.create({ repoRoot: REPO_ROOT, watch, maxWorkerCount, - oss, - dist, + oss: oss && !(validateLimits || updateLimits), + dist: dist || updateLimits, cache, - examples, + examples: examples && !(validateLimits || updateLimits), profileWebpack, extraPluginScanDirs, inspectWorkers, @@ -108,6 +119,11 @@ run( filter, }); + if (validateLimits) { + validateLimitsForAllBundles(log, config); + return; + } + let update$ = runOptimizer(config); if (reportStats) { @@ -121,6 +137,10 @@ run( } await update$.pipe(logOptimizerState(log, config)).toPromise(); + + if (updateLimits) { + updateBundleLimits(log, config); + } }, { flags: { @@ -134,6 +154,8 @@ run( 'profile', 'inspect-workers', 'report-stats', + 'validate-limits', + 'update-limits', ], string: ['workers', 'scan-dir', 'filter'], default: { @@ -152,10 +174,12 @@ run( --no-cache disable the cache --filter comma-separated list of bundle id filters, results from multiple flags are merged, * and ! are supported --no-examples don't build the example plugins - --dist create bundles that are suitable for inclusion in the Kibana distributable + --dist create bundles that are suitable for inclusion in the Kibana distributable, enabled when running with --update-limits --scan-dir add a directory to the list of directories scanned for plugins (specify as many times as necessary) --no-inspect-workers when inspecting the parent process, don't inspect the workers --report-stats attempt to report stats about this execution of the build to the kibana-ci-stats service using this name + --validate-limits validate the limits.yml config to ensure that there are limits defined for every bundle + --update-limits run a build and rewrite the limits file to include the current bundle sizes +5kb `, }, } diff --git a/packages/kbn-optimizer/src/index.ts b/packages/kbn-optimizer/src/index.ts index 549e4b13a4ac01..c522ff770d3695 100644 --- a/packages/kbn-optimizer/src/index.ts +++ b/packages/kbn-optimizer/src/index.ts @@ -22,3 +22,4 @@ export * from './run_optimizer'; export * from './log_optimizer_state'; export * from './report_optimizer_stats'; export * from './node'; +export * from './limits'; diff --git a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap index 038beca703720c..cb5bb1e8fc529f 100644 --- a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap +++ b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap @@ -57,6 +57,7 @@ OptimizerConfig { "cache": true, "dist": false, "inspectWorkers": false, + "limits": "", "maxWorkerCount": 1, "plugins": Array [ Object { diff --git a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts index de3838eb92975c..3dff034af886c6 100644 --- a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts +++ b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts @@ -27,7 +27,13 @@ import del from 'del'; import { toArray, tap, filter } from 'rxjs/operators'; import { REPO_ROOT } from '@kbn/utils'; import { ToolingLog } from '@kbn/dev-utils'; -import { runOptimizer, OptimizerConfig, OptimizerUpdate, logOptimizerState } from '@kbn/optimizer'; +import { + runOptimizer, + OptimizerConfig, + OptimizerUpdate, + logOptimizerState, + readLimits, +} from '@kbn/optimizer'; const TMP_DIR = Path.resolve(__dirname, '../__fixtures__/__tmp__'); const MOCK_REPO_SRC = Path.resolve(__dirname, '../__fixtures__/mock_repo'); @@ -72,6 +78,9 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { dist: false, }); + expect(config.limits).toEqual(readLimits()); + (config as any).limits = ''; + expect(config).toMatchSnapshot('OptimizerConfig'); const msgs = await runOptimizer(config) diff --git a/packages/kbn-optimizer/src/limits.ts b/packages/kbn-optimizer/src/limits.ts new file mode 100644 index 00000000000000..4040a0c37d3b60 --- /dev/null +++ b/packages/kbn-optimizer/src/limits.ts @@ -0,0 +1,83 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Fs from 'fs'; + +import dedent from 'dedent'; +import Yaml from 'js-yaml'; +import { createFailError, ToolingLog } from '@kbn/dev-utils'; + +import { OptimizerConfig, getMetrics } from './optimizer'; + +const LIMITS_PATH = require.resolve('../limits.yml'); +const DEFAULT_BUDGET = 15000; + +const diff = (a: T[], b: T[]): T[] => a.filter((item) => !b.includes(item)); + +export function readLimits() { + return Yaml.safeLoad(Fs.readFileSync(LIMITS_PATH, 'utf8')); +} + +export function validateLimitsForAllBundles(log: ToolingLog, config: OptimizerConfig) { + const limitBundleIds = Object.keys(config.limits.pageLoadAssetSize); + const configBundleIds = config.bundles.map((b) => b.id); + + const missingBundleIds = diff(configBundleIds, limitBundleIds); + const extraBundleIds = diff(limitBundleIds, configBundleIds); + + const issues = []; + if (missingBundleIds.length) { + issues.push(`missing: ${missingBundleIds.join(', ')}`); + } + if (extraBundleIds.length) { + issues.push(`extra: ${extraBundleIds.join(', ')}`); + } + if (issues.length) { + throw createFailError( + dedent` + The limits defined in packages/kbn-optimizer/limits.yml are outdated. Please update + this file with a limit (in bytes) for every production bundle. + + ${issues.join('\n ')} + + To validate your changes locally, run: + + node scripts/build_kibana_platform_plugins.js --validate-limits + ` + '\n' + ); + } + + log.success('limits.yml file valid'); +} + +export function updateBundleLimits(log: ToolingLog, config: OptimizerConfig) { + const metrics = getMetrics(log, config); + + const number = (input: number) => input.toLocaleString('en').split(',').join('_'); + + let yaml = `pageLoadAssetSize:\n`; + for (const metric of metrics.sort((a, b) => a.id.localeCompare(b.id))) { + if (metric.group === 'page load bundle size') { + yaml += ` ${metric.id}: ${number(metric.value + DEFAULT_BUDGET)}\n`; + } + } + + Fs.writeFileSync(LIMITS_PATH, yaml); + log.success(`wrote updated limits to ${LIMITS_PATH}`); +} diff --git a/packages/kbn-optimizer/src/optimizer/get_output_stats.ts b/packages/kbn-optimizer/src/optimizer/get_output_stats.ts new file mode 100644 index 00000000000000..24847a03edb522 --- /dev/null +++ b/packages/kbn-optimizer/src/optimizer/get_output_stats.ts @@ -0,0 +1,124 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Fs from 'fs'; +import Path from 'path'; + +import { ToolingLog, CiStatsMetrics } from '@kbn/dev-utils'; +import { OptimizerConfig } from './optimizer_config'; + +const flatten = (arr: Array): T[] => + arr.reduce((acc: T[], item) => acc.concat(item), []); + +interface Entry { + relPath: string; + stats: Fs.Stats; +} + +const IGNORED_EXTNAME = ['.map', '.br', '.gz']; + +const getFiles = (dir: string, parent?: string) => + flatten( + Fs.readdirSync(dir).map((name): Entry | Entry[] => { + const absPath = Path.join(dir, name); + const relPath = parent ? Path.join(parent, name) : name; + const stats = Fs.statSync(absPath); + + if (stats.isDirectory()) { + return getFiles(absPath, relPath); + } + + return { + relPath, + stats, + }; + }) + ).filter((file) => { + const filename = Path.basename(file.relPath); + if (filename.startsWith('.')) { + return false; + } + + const ext = Path.extname(filename); + if (IGNORED_EXTNAME.includes(ext)) { + return false; + } + + return true; + }); + +export function getMetrics(log: ToolingLog, config: OptimizerConfig) { + return flatten( + config.bundles.map((bundle) => { + // make the cache read from the cache file since it was likely updated by the worker + bundle.cache.refresh(); + + const outputFiles = getFiles(bundle.outputDir); + const entryName = `${bundle.id}.${bundle.type}.js`; + const entry = outputFiles.find((f) => f.relPath === entryName); + if (!entry) { + throw new Error( + `Unable to find bundle entry named [${entryName}] in [${bundle.outputDir}]` + ); + } + + const chunkPrefix = `${bundle.id}.chunk.`; + const asyncChunks = outputFiles.filter((f) => f.relPath.startsWith(chunkPrefix)); + const miscFiles = outputFiles.filter((f) => f !== entry && !asyncChunks.includes(f)); + + if (asyncChunks.length) { + log.verbose(bundle.id, 'async chunks', asyncChunks); + } + if (miscFiles.length) { + log.verbose(bundle.id, 'misc files', asyncChunks); + } + + const sumSize = (files: Entry[]) => files.reduce((acc: number, f) => acc + f.stats!.size, 0); + + const bundleMetrics: CiStatsMetrics = [ + { + group: `@kbn/optimizer bundle module count`, + id: bundle.id, + value: bundle.cache.getModuleCount() || 0, + }, + { + group: `page load bundle size`, + id: bundle.id, + value: entry.stats!.size, + limit: config.limits.pageLoadAssetSize[bundle.id], + limitConfigPath: `packages/kbn-optimizer/limits.yml`, + }, + { + group: `async chunks size`, + id: bundle.id, + value: sumSize(asyncChunks), + }, + { + group: `miscellaneous assets size`, + id: bundle.id, + value: sumSize(miscFiles), + }, + ]; + + log.debug(bundle.id, 'metrics', bundleMetrics); + + return bundleMetrics; + }) + ); +} diff --git a/packages/kbn-optimizer/src/optimizer/index.ts b/packages/kbn-optimizer/src/optimizer/index.ts index 84fd395e989766..77df112b44351a 100644 --- a/packages/kbn-optimizer/src/optimizer/index.ts +++ b/packages/kbn-optimizer/src/optimizer/index.ts @@ -25,3 +25,4 @@ export * from './watch_bundles_for_changes'; export * from './run_workers'; export * from './bundle_cache'; export * from './handle_optimizer_completion'; +export * from './get_output_stats'; diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts index fd887e8c2c0122..948ba520931e55 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts @@ -22,6 +22,7 @@ jest.mock('./kibana_platform_plugins.ts'); jest.mock('./get_plugin_bundles.ts'); jest.mock('../common/theme_tags.ts'); jest.mock('./filter_by_id.ts'); +jest.mock('../limits.ts'); jest.mock('os', () => { const realOs = jest.requireActual('os'); @@ -385,6 +386,7 @@ describe('OptimizerConfig::create()', () => { .findKibanaPlatformPlugins; const getPluginBundles: jest.Mock = jest.requireMock('./get_plugin_bundles.ts').getPluginBundles; const filterById: jest.Mock = jest.requireMock('./filter_by_id.ts').filterById; + const readLimits: jest.Mock = jest.requireMock('../limits.ts').readLimits; beforeEach(() => { if ('mock' in OptimizerConfig.parseOptions) { @@ -398,6 +400,7 @@ describe('OptimizerConfig::create()', () => { findKibanaPlatformPlugins.mockReturnValue(Symbol('new platform plugins')); getPluginBundles.mockReturnValue([Symbol('bundle1'), Symbol('bundle2')]); filterById.mockReturnValue(Symbol('filtered bundles')); + readLimits.mockReturnValue(Symbol('limits')); jest.spyOn(OptimizerConfig, 'parseOptions').mockImplementation((): { [key in keyof ParsedOptions]: any; @@ -429,6 +432,7 @@ describe('OptimizerConfig::create()', () => { "cache": Symbol(parsed cache), "dist": Symbol(parsed dist), "inspectWorkers": Symbol(parsed inspect workers), + "limits": Symbol(limits), "maxWorkerCount": Symbol(parsed max worker count), "plugins": Symbol(new platform plugins), "profileWebpack": Symbol(parsed profile webpack), diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts index b1ab1ebfe49f23..01a20bec52a042 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts @@ -32,6 +32,13 @@ import { import { findKibanaPlatformPlugins, KibanaPlatformPlugin } from './kibana_platform_plugins'; import { getPluginBundles } from './get_plugin_bundles'; import { filterById } from './filter_by_id'; +import { readLimits } from '../limits'; + +export interface Limits { + pageLoadAssetSize: { + [id: string]: number | undefined; + }; +} function pickMaxWorkerCount(dist: boolean) { // don't break if cpus() returns nothing, or an empty array @@ -238,7 +245,8 @@ export class OptimizerConfig { options.maxWorkerCount, options.dist, options.profileWebpack, - options.themeTags + options.themeTags, + readLimits() ); } @@ -252,7 +260,8 @@ export class OptimizerConfig { public readonly maxWorkerCount: number, public readonly dist: boolean, public readonly profileWebpack: boolean, - public readonly themeTags: ThemeTags + public readonly themeTags: ThemeTags, + public readonly limits: Limits ) {} getWorkerConfig(optimizerCacheKey: unknown): WorkerConfig { diff --git a/packages/kbn-optimizer/src/report_optimizer_stats.ts b/packages/kbn-optimizer/src/report_optimizer_stats.ts index eff2bce0b827e4..a0f59a3505e307 100644 --- a/packages/kbn-optimizer/src/report_optimizer_stats.ts +++ b/packages/kbn-optimizer/src/report_optimizer_stats.ts @@ -17,136 +17,41 @@ * under the License. */ -import Fs from 'fs'; -import Path from 'path'; - import { materialize, mergeMap, dematerialize } from 'rxjs/operators'; -import { CiStatsReporter, CiStatsMetrics, ToolingLog } from '@kbn/dev-utils'; +import { CiStatsReporter, ToolingLog } from '@kbn/dev-utils'; import { OptimizerUpdate$ } from './run_optimizer'; -import { OptimizerState, OptimizerConfig } from './optimizer'; +import { OptimizerConfig, getMetrics } from './optimizer'; import { pipeClosure } from './common'; -const flatten = (arr: Array): T[] => - arr.reduce((acc: T[], item) => acc.concat(item), []); - -interface Entry { - relPath: string; - stats: Fs.Stats; -} - -const IGNORED_EXTNAME = ['.map', '.br', '.gz']; - -const getFiles = (dir: string, parent?: string) => - flatten( - Fs.readdirSync(dir).map((name): Entry | Entry[] => { - const absPath = Path.join(dir, name); - const relPath = parent ? Path.join(parent, name) : name; - const stats = Fs.statSync(absPath); - - if (stats.isDirectory()) { - return getFiles(absPath, relPath); - } - - return { - relPath, - stats, - }; - }) - ).filter((file) => { - const filename = Path.basename(file.relPath); - if (filename.startsWith('.')) { - return false; - } - - const ext = Path.extname(filename); - if (IGNORED_EXTNAME.includes(ext)) { - return false; - } - - return true; - }); - export function reportOptimizerStats( reporter: CiStatsReporter, config: OptimizerConfig, log: ToolingLog ) { - return pipeClosure((update$: OptimizerUpdate$) => { - let lastState: OptimizerState | undefined; - return update$.pipe( + return pipeClosure((update$: OptimizerUpdate$) => + update$.pipe( materialize(), mergeMap(async (n) => { - if (n.kind === 'N' && n.value?.state) { - lastState = n.value?.state; - } - - if (n.kind === 'C' && lastState) { - await reporter.metrics( - flatten( - config.bundles.map((bundle) => { - // make the cache read from the cache file since it was likely updated by the worker - bundle.cache.refresh(); - - const outputFiles = getFiles(bundle.outputDir); - const entryName = `${bundle.id}.${bundle.type}.js`; - const entry = outputFiles.find((f) => f.relPath === entryName); - if (!entry) { - throw new Error( - `Unable to find bundle entry named [${entryName}] in [${bundle.outputDir}]` - ); - } - - const chunkPrefix = `${bundle.id}.chunk.`; - const asyncChunks = outputFiles.filter((f) => f.relPath.startsWith(chunkPrefix)); - const miscFiles = outputFiles.filter( - (f) => f !== entry && !asyncChunks.includes(f) - ); - - if (asyncChunks.length) { - log.verbose(bundle.id, 'async chunks', asyncChunks); - } - if (miscFiles.length) { - log.verbose(bundle.id, 'misc files', asyncChunks); - } - - const sumSize = (files: Entry[]) => - files.reduce((acc: number, f) => acc + f.stats!.size, 0); - - const metrics: CiStatsMetrics = [ - { - group: `@kbn/optimizer bundle module count`, - id: bundle.id, - value: bundle.cache.getModuleCount() || 0, - }, - { - group: `page load bundle size`, - id: bundle.id, - value: entry.stats!.size, - }, - { - group: `async chunks size`, - id: bundle.id, - value: sumSize(asyncChunks), - }, - { - group: `miscellaneous assets size`, - id: bundle.id, - value: sumSize(miscFiles), - }, - ]; - - log.info(bundle.id, 'metrics', metrics); - - return metrics; - }) - ) - ); + if (n.kind === 'C') { + const metrics = getMetrics(log, config); + + await reporter.metrics(metrics); + + for (const metric of metrics) { + if (metric.limit != null && metric.value > metric.limit) { + const value = metric.value.toLocaleString(); + const limit = metric.limit.toLocaleString(); + log.warning( + `Metric [${metric.group}] for [${metric.id}] of [${value}] over the limit of [${limit}]` + ); + } + } } return n; }), dematerialize() - ); - }); + ) + ); } diff --git a/test/scripts/checks/bundle_limits.sh b/test/scripts/checks/bundle_limits.sh new file mode 100755 index 00000000000000..10d9d9343fda4e --- /dev/null +++ b/test/scripts/checks/bundle_limits.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +source src/dev/ci_setup/setup_env.sh + +node scripts/build_kibana_platform_plugins --validate-limits diff --git a/vars/githubPr.groovy b/vars/githubPr.groovy index ec3dbd919fed66..fd5412c905683e 100644 --- a/vars/githubPr.groovy +++ b/vars/githubPr.groovy @@ -87,15 +87,6 @@ def getLatestBuildInfo(comment) { return comment ? getBuildInfoFromComment(comment.body) : null } -def createBuildInfo() { - return [ - status: buildUtils.getBuildStatus(), - url: env.BUILD_URL, - number: env.BUILD_NUMBER, - commit: getCommitHash() - ] -} - def getHistoryText(builds) { if (!builds || builds.size() < 1) { return "" @@ -155,6 +146,16 @@ def getTestFailuresMessage() { return messages.join("\n") } +def getBuildStatusIncludingMetrics() { + def status = buildUtils.getBuildStatus() + + if (status == 'SUCCESS' && !ciStats.getMetricsSuccess()) { + return 'FAILURE' + } + + return status +} + def getNextCommentMessage(previousCommentInfo = [:], isFinal = false) { def info = previousCommentInfo ?: [:] info.builds = previousCommentInfo.builds ?: [] @@ -163,7 +164,10 @@ def getNextCommentMessage(previousCommentInfo = [:], isFinal = false) { info.builds = info.builds.findAll { it.number != env.BUILD_NUMBER } def messages = [] - def status = buildUtils.getBuildStatus() + + def status = isFinal + ? getBuildStatusIncludingMetrics() + : buildUtils.getBuildStatus() if (!isFinal) { def failuresPart = status != 'SUCCESS' ? ', with failures' : '' @@ -228,7 +232,12 @@ def getNextCommentMessage(previousCommentInfo = [:], isFinal = false) { messages << "To update your PR or re-run it, just comment with:\n`@elasticmachine merge upstream`" - info.builds << createBuildInfo() + info.builds << [ + status: status, + url: env.BUILD_URL, + number: env.BUILD_NUMBER, + commit: getCommitHash() + ] messages << """