From 7e344a766ddd3bb397fa2b03c92dd256d3bded08 Mon Sep 17 00:00:00 2001 From: Kyle Pollich Date: Tue, 11 Jun 2024 08:06:40 -0400 Subject: [PATCH 1/3] [Fleet] Add concurrency limit to EPM bulk install API + fix duplicate installations (#185900) ## Summary Prevents large bulk installation requests to the `POST /api/fleet/epm/packages/_bulk` from consuming too much memory by limiting concurrency. This PR also adds logic such that duplicate package names in the request body will be ignored. ## To test Use the following dev tools to test and confirm that bulk installation works as expected, and that there aren't multiple installations attempted in your Kibana logs: ``` POST kbn:/api/fleet/epm/packages/_bulk { "packages": [ "aws", "elastic_agent", "elastic_agent" ] } ``` ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../epm/packages/bulk_install_packages.ts | 67 ++++++++++++------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts b/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts index 08bc17f193bd7..d526ddf0d0f54 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts @@ -7,6 +7,9 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; +import pLimit from 'p-limit'; +import { uniqBy } from 'lodash'; + import type { HTTPAuthorizationHeader } from '../../../../common/http_authorization_header'; import { appContextService } from '../../app_context'; @@ -44,37 +47,49 @@ export async function bulkInstallPackages({ }: BulkInstallPackagesParams): Promise { const logger = appContextService.getLogger(); + const uniquePackages = uniqBy(packagesToInstall, (pkg) => { + if (typeof pkg === 'string') { + return pkg; + } + + return pkg.name; + }); + + const limiter = pLimit(10); + const packagesResults = await Promise.allSettled( - packagesToInstall.map(async (pkg) => { - if (typeof pkg === 'string') { - return Registry.fetchFindLatestPackageOrThrow(pkg, { - prerelease, + uniquePackages.map(async (pkg) => { + return limiter(async () => { + if (typeof pkg === 'string') { + return Registry.fetchFindLatestPackageOrThrow(pkg, { + prerelease, + }).then((pkgRes) => ({ + name: pkgRes.name, + version: pkgRes.version, + prerelease: undefined, + skipDataStreamRollover: undefined, + })); + } + if (pkg.version !== undefined) { + return Promise.resolve( + pkg as { + name: string; + version: string; + prerelease?: boolean; + skipDataStreamRollover?: boolean; + } + ); + } + + return Registry.fetchFindLatestPackageOrThrow(pkg.name, { + prerelease: prerelease || pkg.prerelease, }).then((pkgRes) => ({ name: pkgRes.name, version: pkgRes.version, - prerelease: undefined, - skipDataStreamRollover: undefined, + prerelease: pkg.prerelease, + skipDataStreamRollover: pkg.skipDataStreamRollover, })); - } - if (pkg.version !== undefined) { - return Promise.resolve( - pkg as { - name: string; - version: string; - prerelease?: boolean; - skipDataStreamRollover?: boolean; - } - ); - } - - return Registry.fetchFindLatestPackageOrThrow(pkg.name, { - prerelease: prerelease || pkg.prerelease, - }).then((pkgRes) => ({ - name: pkgRes.name, - version: pkgRes.version, - prerelease: pkg.prerelease, - skipDataStreamRollover: pkg.skipDataStreamRollover, - })); + }); }) ); From 9e52d98d2b8d8fc51a0f30c6e4ca4d2cc9120dfc Mon Sep 17 00:00:00 2001 From: Dominique Clarke Date: Tue, 11 Jun 2024 09:22:14 -0400 Subject: [PATCH 2/3] [APM] alert details - add alert start annotation to all visualizations (#184677) ## Summary Summarize your PR. If it involves visual changes include a screenshot or gif. SLO APM latency alert details page ![image](https://github.com/elastic/kibana/assets/11356435/8a205f5d-00ea-4dce-9d66-a51d284caaa9) SLO APM error rate alert details page ![image](https://github.com/elastic/kibana/assets/11356435/d7ea10d4-d640-4d9e-8ca5-9c7bed2ba279) APM latency alert details page ![image](https://github.com/elastic/kibana/assets/11356435/2b4e0c6c-a481-41f7-848b-87836565905d) ### Testing 1. Create an APM latency rule that fires. Navigate to the alert details page and confirm the annotations appear correctly. 2. Create an SLO burn rate rule for the APM latency SLI that fires. Navigate to the alert details page and confirm the annotations appear correctly 3. Create an SLO burn rate rule for the APM error rate SLI that fires. Navigate to the alert details page and confirm the annotations appear correctly --------- Co-authored-by: Carlos Crespo --- .../failed_transaction_chart.tsx | 33 +++++------------ .../get_alert_start_annotation.tsx | 37 +++++++++++++++++++ .../alert_details_app_section/index.tsx | 8 +++- .../latency_chart.tsx | 37 ++++++++----------- .../throughput_chart.tsx | 25 +++++++++++++ .../alerting_throughput_chart/chart.tsx | 8 +++- 6 files changed, 101 insertions(+), 47 deletions(-) create mode 100644 x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/get_alert_start_annotation.tsx diff --git a/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/failed_transaction_chart.tsx b/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/failed_transaction_chart.tsx index 02273f0f43141..23edfb1822163 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/failed_transaction_chart.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/failed_transaction_chart.tsx @@ -7,7 +7,6 @@ /* Error Rate */ import React from 'react'; -import chroma from 'chroma-js'; import { EuiFlexItem, EuiPanel, @@ -16,13 +15,11 @@ import { EuiIconTip, RecursivePartial, useEuiTheme, - transparentize, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { BoolQuery } from '@kbn/es-query'; import { UI_SETTINGS } from '@kbn/data-plugin/public'; import { Theme } from '@elastic/charts'; -import { AlertActiveTimeRangeAnnotation, AlertAnnotation } from '@kbn/observability-alert-details'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { DEFAULT_DATE_FORMAT } from './constants'; import { useFetcher } from '../../../../hooks/use_fetcher'; @@ -36,6 +33,7 @@ import { usePreferredDataSourceAndBucketSize } from '../../../../hooks/use_prefe import { ApmDocumentType } from '../../../../../common/document_type'; import { TransactionTypeSelect } from './transaction_type_select'; import { ViewInAPMButton } from './view_in_apm_button'; +import { getAlertStartAnnotation } from './get_alert_start_annotation'; type ErrorRate = APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate'>; @@ -75,12 +73,12 @@ function FailedTransactionChart({ environment: string; start: string; end: string; + alertStart?: number; + alertEnd?: number; comparisonChartTheme: RecursivePartial; timeZone: string; kuery?: string; filters?: BoolQuery; - alertStart?: number; - alertEnd?: number; }) { const { euiTheme } = useEuiTheme(); const { @@ -151,25 +149,14 @@ function FailedTransactionChart({ const showTransactionTypeSelect = setTransactionType && transactionTypes; const getFailedTransactionChartAdditionalData = () => { if (alertStart) { - return [ - , - , - ]; + return getAlertStartAnnotation({ + alertStart, + alertEnd, + color: euiTheme.colors.danger, + dateFormat: (uiSettings && uiSettings.get(UI_SETTINGS.DATE_FORMAT)) || DEFAULT_DATE_FORMAT, + }); } + return []; }; return ( diff --git a/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/get_alert_start_annotation.tsx b/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/get_alert_start_annotation.tsx new file mode 100644 index 0000000000000..c259121647175 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/get_alert_start_annotation.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { AlertActiveTimeRangeAnnotation, AlertAnnotation } from '@kbn/observability-alert-details'; + +export function getAlertStartAnnotation({ + alertStart, + alertEnd, + dateFormat, + color, +}: { + alertStart: number; + alertEnd?: number; + dateFormat: string; + color: string; +}) { + return [ + , + , + ]; +} diff --git a/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/index.tsx index 7bea5746e461f..96ed1f8d9128d 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/index.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import moment from 'moment'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { formatAlertEvaluationValue } from '@kbn/observability-plugin/public'; @@ -145,6 +145,8 @@ export function AlertDetailsAppSection({ ); } + const alertEnd = alert.fields[ALERT_END] ? moment(alert.fields[ALERT_END]).valueOf() : undefined; + return ( diff --git a/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart.tsx b/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart.tsx index 82fa091fa6617..7d1686ade2d22 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart.tsx @@ -15,10 +15,8 @@ import { getDurationFormatter } from '@kbn/observability-plugin/common'; import { ALERT_RULE_TYPE_ID, ALERT_EVALUATION_THRESHOLD, ALERT_END } from '@kbn/rule-data-utils'; import type { TopAlert } from '@kbn/observability-plugin/public'; import { - AlertActiveTimeRangeAnnotation, AlertThresholdAnnotation, AlertThresholdTimeRangeRect, - AlertAnnotation, } from '@kbn/observability-alert-details'; import { useEuiTheme } from '@elastic/eui'; import { useKibana } from '@kbn/kibana-react-plugin/public'; @@ -41,6 +39,7 @@ import { usePreferredDataSourceAndBucketSize } from '../../../../hooks/use_prefe import { DEFAULT_DATE_FORMAT } from './constants'; import { TransactionTypeSelect } from './transaction_type_select'; import { ViewInAPMButton } from './view_in_apm_button'; +import { getAlertStartAnnotation } from './get_alert_start_annotation'; function LatencyChart({ alert, @@ -160,26 +159,20 @@ function LatencyChart({ isLatencyThresholdRuleType(alert.fields[ALERT_RULE_TYPE_ID]) || customAlertEvaluationThreshold ) { - return [ - , - , - ...alertEvalThresholdChartData, - ]; + return [...alertEvalThresholdChartData]; + } + return []; + }; + const getLatencyChartAlertStartData = () => { + if (alert.start) { + return getAlertStartAnnotation({ + alertStart: alert.start, + alertEnd, + color: euiTheme.colors.danger, + dateFormat: (uiSettings && uiSettings.get(UI_SETTINGS.DATE_FORMAT)) || DEFAULT_DATE_FORMAT, + }); } + return []; }; const memoizedData = useMemo( () => @@ -247,7 +240,7 @@ function LatencyChart({ ; comparisonEnabled: boolean; offset: string; @@ -62,6 +71,10 @@ function ThroughputChart({ kuery?: string; filters?: BoolQuery; }) { + const { euiTheme } = useEuiTheme(); + const { + services: { uiSettings }, + } = useKibana(); const preferred = usePreferredDataSourceAndBucketSize({ start, end, @@ -129,6 +142,17 @@ function ThroughputChart({ ] : []), ]; + const getThroughputChartAdditionalData = () => { + if (alertStart) { + return getAlertStartAnnotation({ + alertStart, + alertEnd, + color: euiTheme.colors.danger, + dateFormat: (uiSettings && uiSettings.get(UI_SETTINGS.DATE_FORMAT)) || DEFAULT_DATE_FORMAT, + }); + } + return []; + }; const showTransactionTypeSelect = setTransactionType && transactionTypes; @@ -190,6 +214,7 @@ function ThroughputChart({ timeseries={timeseriesThroughput} yLabelFormat={asExactTransactionRate} timeZone={timeZone} + annotations={getThroughputChartAdditionalData()} /> diff --git a/x-pack/plugins/observability_solution/apm/public/embeddable/alerting/alerting_throughput_chart/chart.tsx b/x-pack/plugins/observability_solution/apm/public/embeddable/alerting/alerting_throughput_chart/chart.tsx index d98c27fc52d1d..0a0785497d0dd 100644 --- a/x-pack/plugins/observability_solution/apm/public/embeddable/alerting/alerting_throughput_chart/chart.tsx +++ b/x-pack/plugins/observability_solution/apm/public/embeddable/alerting/alerting_throughput_chart/chart.tsx @@ -4,8 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import moment from 'moment'; import React from 'react'; +import { ALERT_END } from '@kbn/rule-data-utils'; import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; import ThroughputChart from '../../../components/alerting/ui_components/alert_details_app_section/throughput_chart'; import { EmbeddableApmAlertingVizProps } from '../types'; @@ -15,6 +16,7 @@ import { ServiceNameCallout } from '../service_name_callout'; export function APMAlertingThroughputChart({ rule, + alert, rangeFrom = 'now-15m', rangeTo = 'now', transactionName, @@ -46,6 +48,8 @@ export function APMAlertingThroughputChart({ return ; } + const alertEnd = alert.fields[ALERT_END] ? moment(alert.fields[ALERT_END]).valueOf() : undefined; + return ( Date: Tue, 11 Jun 2024 10:09:29 -0400 Subject: [PATCH 3/3] [Observability Onboarding] Update question contents to match new designs (#184866) ## Summary Resolves #184108. Resolves #181855. Updates the o11y onboarding flow's first question to match our latest designs. This includes changing the avatar icon at the beginning of the flow, updating the copy for the first question, adding a list of integration icons that pertain to each question, and including a badge to indicate that the user can add additional integrations for each option. From a technical perspective, I have added a type to indicate the supported icon values. Many of these logos are natively supported by EUI, but I did introduce three new SVG to the plugin's assets folder. ### Before image ### After image --- .../onboarding_flow_form.tsx | 98 +++++++++++++++---- .../public/assets/dotnet.svg | 18 ++++ .../public/assets/java.svg | 3 + .../public/assets/javascript.svg | 10 ++ 4 files changed, 110 insertions(+), 19 deletions(-) create mode 100644 x-pack/plugins/observability_solution/observability_onboarding/public/assets/dotnet.svg create mode 100644 x-pack/plugins/observability_solution/observability_onboarding/public/assets/java.svg create mode 100644 x-pack/plugins/observability_solution/observability_onboarding/public/assets/javascript.svg diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.tsx index 3ed5d1d2a8584..4d5e75abe74c4 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.tsx +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.tsx @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; import type { FunctionComponent } from 'react'; import { EuiAvatar, @@ -20,6 +21,8 @@ import { EuiTitle, useGeneratedHtmlId, useEuiTheme, + EuiBadge, + EuiIcon, } from '@elastic/eui'; import { useSearchParams } from 'react-router-dom-v5-compat'; @@ -33,8 +36,23 @@ interface UseCaseOption { id: Category; label: string; description: React.ReactNode; + logos?: SupportedLogo[]; + showIntegrationsBadge?: boolean; } +type SupportedLogo = + | 'aws' + | 'azure' + | 'docker' + | 'dotnet' + | 'prometheus' + | 'gcp' + | 'java' + | 'javascript' + | 'kubernetes' + | 'nginx' + | 'opentelemetry'; + export const OnboardingFlowForm: FunctionComponent = () => { const options: UseCaseOption[] = [ { @@ -50,6 +68,8 @@ export const OnboardingFlowForm: FunctionComponent = () => { 'Detect patterns, gain insights from logs, get alerted when surpassing error thresholds', } ), + logos: ['azure', 'aws', 'nginx', 'gcp'], + showIntegrationsBadge: true, }, { id: 'apm', @@ -64,6 +84,7 @@ export const OnboardingFlowForm: FunctionComponent = () => { 'Catch application problems, get alerted on performance issues or SLO breaches, expedite root cause analysis and remediation', } ), + logos: ['opentelemetry', 'java', 'javascript', 'dotnet'], }, { id: 'infra', @@ -78,6 +99,8 @@ export const OnboardingFlowForm: FunctionComponent = () => { 'Check my system’s health, get alerted on performance issues or SLO breaches, expedite root cause analysis and remediation', } ), + logos: ['kubernetes', 'prometheus', 'docker', 'opentelemetry'], + showIntegrationsBadge: true, }, ]; @@ -129,12 +152,11 @@ export const OnboardingFlowForm: FunctionComponent = () => { return ( @@ -155,6 +177,27 @@ export const OnboardingFlowForm: FunctionComponent = () => { {option.description} + {(option.logos || option.showIntegrationsBadge) && ( + <> + + + {option.logos?.map((logo) => ( + + + + ))} + {option.showIntegrationsBadge && ( + + + + )} + + + )} } checked={option.id === searchParams.get('category')} @@ -241,22 +284,7 @@ interface TitleWithIconProps { const TitleWithIcon: FunctionComponent = ({ title, iconType }) => ( - + @@ -278,3 +306,35 @@ function scrollIntoViewWithOffset(element: HTMLElement, offset = 0) { top: element.getBoundingClientRect().top - document.body.getBoundingClientRect().top - offset, }); } + +function useIconForLogo(logo?: SupportedLogo): string | undefined { + const { + services: { http }, + } = useKibana(); + switch (logo) { + case 'aws': + return 'logoAWS'; + case 'azure': + return 'logoAzure'; + case 'gcp': + return 'logoGCP'; + case 'kubernetes': + return 'logoKubernetes'; + case 'nginx': + return 'logoNginx'; + case 'prometheus': + return 'logoPrometheus'; + case 'docker': + return 'logoDocker'; + default: + return http?.staticAssets.getPluginAssetHref(`${logo}.svg`); + } +} + +function LogoIcon({ logo }: { logo: SupportedLogo }) { + const iconType = useIconForLogo(logo); + if (iconType) { + return ; + } + return null; +} diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/assets/dotnet.svg b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/dotnet.svg new file mode 100644 index 0000000000000..34d5f874e47af --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/dotnet.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/assets/java.svg b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/java.svg new file mode 100644 index 0000000000000..943e009ec8dfe --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/java.svg @@ -0,0 +1,3 @@ + + + diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/assets/javascript.svg b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/javascript.svg new file mode 100644 index 0000000000000..1369b495404be --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/javascript.svg @@ -0,0 +1,10 @@ + + + + + + + + + +