-
- {i18n.translate('xpack.csp.vulnerabilities.totalVulnerabilities', {
- defaultMessage:
- '{total, plural, one {# Vulnerability} other {# Vulnerabilities}}',
- values: { total: data?.total },
- })}
-
- >
+
+
+
+ {i18n.translate('xpack.csp.vulnerabilities.totalVulnerabilities', {
+ defaultMessage:
+ '{total, plural, one {# Vulnerability} other {# Vulnerabilities}}',
+ values: { total: data?.total },
+ })}
+
+ >
+ ),
+ },
+ right: (
+
+
+
),
},
- right: (
-
-
-
- ),
- },
- }}
- gridStyle={{
- border: 'horizontal',
- cellPadding: 'l',
- stripes: false,
- rowHover: 'none',
- header: 'underline',
- }}
- renderCellValue={renderCellValue}
- inMemory={{ level: 'enhancements' }}
- sorting={{ columns: sort, onSort: onSortHandler }}
- pagination={{
- pageIndex,
- pageSize,
- pageSizeOptions: [10, 25, 100],
- onChangeItemsPerPage,
- onChangePage,
- }}
- />
- {isLastLimitedPage && }
+ }}
+ gridStyle={{
+ border: 'horizontal',
+ cellPadding: 'l',
+ stripes: false,
+ rowHover: 'none',
+ header: 'underline',
+ }}
+ renderCellValue={renderCellValue}
+ inMemory={{ level: 'enhancements' }}
+ sorting={{ columns: sort, onSort: onSortHandler }}
+ pagination={{
+ pageIndex,
+ pageSize,
+ pageSizeOptions: [10, 25, 100],
+ onChangeItemsPerPage,
+ onChangePage,
+ }}
+ virtualizationOptions={{
+ overscanRowCount: 20,
+ }}
+ />
+ {isLastLimitedPage && }
+
{showVulnerabilityFlyout && selectedVulnerability && (
= ({ r
const now = new Date().toISOString();
return [
- {
- '@timestamp': now,
- message: {
- role: MessageRole.System,
- content: `You are logs-gpt, a helpful assistant for logs-based observability. Answer as
- concisely as possible.`,
- },
- },
{
'@timestamp': now,
message: {
diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/process_row.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/process_row.tsx
index 12f6eb066a697e..1ec925d9ce148d 100644
--- a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/process_row.tsx
+++ b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/process_row.tsx
@@ -45,14 +45,6 @@ export const ContextualInsightProcessRow = ({ command }: { command: string }) =>
}
const now = new Date().toISOString();
return [
- {
- '@timestamp': now,
- message: {
- role: MessageRole.System,
- content: `You are infra-gpt, a helpful assistant for metrics-based infrastructure observability. Answer as
- concisely as possible.`,
- },
- },
{
'@timestamp': now,
message: {
diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/processes.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/processes.tsx
index 52bad6238c1037..973ff681ab570c 100644
--- a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/processes.tsx
+++ b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/processes.tsx
@@ -23,11 +23,7 @@ import { parseSearchString } from './parse_search_string';
import { ProcessesTable } from './processes_table';
import { STATE_NAMES } from './states';
import { SummaryTable } from './summary_table';
-import {
- SortBy,
- useProcessList,
- ProcessListContextProvider,
-} from '../../../../pages/metrics/inventory_view/hooks/use_process_list';
+import { SortBy, useProcessList, ProcessListContextProvider } from '../../hooks/use_process_list';
import { getFieldByType } from '../../../../../common/inventory_models';
import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props';
import { useDateRangeProviderContext } from '../../hooks/use_date_range';
diff --git a/x-pack/plugins/infra/public/containers/plugin_config_context.test.tsx b/x-pack/plugins/infra/public/containers/plugin_config_context.test.tsx
index e3d3c3f28de112..70b3cf466f749e 100644
--- a/x-pack/plugins/infra/public/containers/plugin_config_context.test.tsx
+++ b/x-pack/plugins/infra/public/containers/plugin_config_context.test.tsx
@@ -20,9 +20,10 @@ describe('usePluginConfig()', () => {
it('returns the plugin config what was set through the provider', () => {
const config: Partial = {
featureFlags: {
+ customThresholdAlertsEnabled: true,
+ logsUIEnabled: false,
metricsExplorerEnabled: false,
osqueryEnabled: false,
- customThresholdAlertsEnabled: true,
},
};
const { result } = renderHook(() => usePluginConfig(), {
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_process_list.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_process_list.ts
deleted file mode 100644
index 532195ba32cc06..00000000000000
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_process_list.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import createContainter from 'constate';
-import { fold } from 'fp-ts/lib/Either';
-import { identity } from 'fp-ts/lib/function';
-import { pipe } from 'fp-ts/lib/pipeable';
-import { useEffect } from 'react';
-import { ProcessListAPIResponse, ProcessListAPIResponseRT } from '../../../../../common/http_api';
-import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
-import { useHTTPRequest } from '../../../../hooks/use_http_request';
-import { useSourceContext } from '../../../../containers/metrics_source';
-
-export interface SortBy {
- name: string;
- isAscending: boolean;
-}
-
-export function useProcessList(
- hostTerm: Record,
- to: number,
- sortBy: SortBy,
- searchFilter: object
-) {
- const { createDerivedIndexPattern } = useSourceContext();
- const indexPattern = createDerivedIndexPattern().title;
-
- const decodeResponse = (response: any) => {
- return pipe(
- ProcessListAPIResponseRT.decode(response),
- fold(throwErrors(createPlainError), identity)
- );
- };
-
- const parsedSortBy =
- sortBy.name === 'runtimeLength'
- ? {
- ...sortBy,
- name: 'startTime',
- }
- : sortBy;
-
- const { error, loading, response, makeRequest } = useHTTPRequest(
- '/api/metrics/process_list',
- 'POST',
- JSON.stringify({
- hostTerm,
- indexPattern,
- to,
- sortBy: parsedSortBy,
- searchFilter,
- }),
- decodeResponse
- );
-
- useEffect(() => {
- makeRequest();
- }, [makeRequest]);
-
- return {
- error: (error && error.message) || null,
- loading,
- response,
- makeRequest,
- };
-}
-
-function useProcessListParams(props: { hostTerm: Record; to: number }) {
- const { hostTerm, to } = props;
- const { createDerivedIndexPattern } = useSourceContext();
- const indexPattern = createDerivedIndexPattern().title;
- return { hostTerm, indexPattern, to };
-}
-const ProcessListContext = createContainter(useProcessListParams);
-export const [ProcessListContextProvider, useProcessListContext] = ProcessListContext;
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_process_list_row_chart.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_process_list_row_chart.ts
deleted file mode 100644
index 0d718ffbe210c8..00000000000000
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_process_list_row_chart.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { fold } from 'fp-ts/lib/Either';
-import { identity } from 'fp-ts/lib/function';
-import { pipe } from 'fp-ts/lib/pipeable';
-import { useEffect, useState } from 'react';
-import {
- ProcessListAPIChartResponse,
- ProcessListAPIChartResponseRT,
-} from '../../../../../common/http_api';
-import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
-import { useHTTPRequest } from '../../../../hooks/use_http_request';
-import { useProcessListContext } from './use_process_list';
-
-export function useProcessListRowChart(command: string) {
- const [inErrorState, setInErrorState] = useState(false);
- const decodeResponse = (response: any) => {
- return pipe(
- ProcessListAPIChartResponseRT.decode(response),
- fold(throwErrors(createPlainError), identity)
- );
- };
- const { hostTerm, indexPattern, to } = useProcessListContext();
-
- const { error, loading, response, makeRequest } = useHTTPRequest(
- '/api/metrics/process_list/chart',
- 'POST',
- JSON.stringify({
- hostTerm,
- indexPattern,
- to,
- command,
- }),
- decodeResponse
- );
-
- useEffect(() => setInErrorState(true), [error]);
- useEffect(() => setInErrorState(false), [loading]);
-
- useEffect(() => {
- makeRequest();
- }, [makeRequest]);
-
- return {
- error: inErrorState,
- loading,
- response,
- makeRequest,
- };
-}
diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts
index fe6c42ab432489..1d084845465063 100644
--- a/x-pack/plugins/infra/public/plugin.ts
+++ b/x-pack/plugins/infra/public/plugin.ts
@@ -85,18 +85,21 @@ export class Plugin implements InfraClientPluginClass {
pluginsSetup.observability.observabilityRuleTypeRegistry.register(
createMetricThresholdRuleType()
);
- pluginsSetup.observability.dashboard.register({
- appName: 'infra_logs',
- hasData: getLogsHasDataFetcher(core.getStartServices),
- fetchData: getLogsOverviewDataFetcher(core.getStartServices),
- });
+
+ if (this.config.featureFlags.logsUIEnabled) {
+ // fetchData `appLink` redirects to logs/stream
+ pluginsSetup.observability.dashboard.register({
+ appName: 'infra_logs',
+ hasData: getLogsHasDataFetcher(core.getStartServices),
+ fetchData: getLogsOverviewDataFetcher(core.getStartServices),
+ });
+ }
pluginsSetup.observability.dashboard.register({
appName: 'infra_metrics',
hasData: createMetricsHasData(core.getStartServices),
fetchData: createMetricsFetchData(core.getStartServices),
});
-
pluginsSetup.logsShared.logViews.setLogViewsStaticConfig({
messageFields: this.config.sources?.default?.fields?.message,
});
@@ -107,13 +110,6 @@ export class Plugin implements InfraClientPluginClass {
]);
/** !! Need to be kept in sync with the deepLinks in x-pack/plugins/infra/public/plugin.ts */
- const infraEntries = [
- { label: 'Inventory', app: 'metrics', path: '/inventory' },
- ...(this.config.featureFlags.metricsExplorerEnabled
- ? [{ label: 'Metrics Explorer', app: 'metrics', path: '/explorer' }]
- : []),
- { label: 'Hosts', isBetaFeature: true, app: 'metrics', path: '/hosts' },
- ];
pluginsSetup.observabilityShared.navigation.registerSections(
startDep$AndHostViewFlag$.pipe(
map(
@@ -136,9 +132,13 @@ export class Plugin implements InfraClientPluginClass {
path: '/',
isBetaFeature: true,
},
- { label: 'Stream', app: 'logs', path: '/stream' },
- { label: 'Anomalies', app: 'logs', path: '/anomalies' },
- { label: 'Categories', app: 'logs', path: '/log-categories' },
+ ...(this.config.featureFlags.logsUIEnabled
+ ? [
+ { label: 'Stream', app: 'logs', path: '/stream' },
+ { label: 'Anomalies', app: 'logs', path: '/anomalies' },
+ { label: 'Categories', app: 'logs', path: '/log-categories' },
+ ]
+ : []),
],
},
]
@@ -148,7 +148,13 @@ export class Plugin implements InfraClientPluginClass {
{
label: 'Infrastructure',
sortKey: 300,
- entries: infraEntries,
+ entries: [
+ { label: 'Inventory', app: 'metrics', path: '/inventory' },
+ ...(this.config.featureFlags.metricsExplorerEnabled
+ ? [{ label: 'Metrics Explorer', app: 'metrics', path: '/explorer' }]
+ : []),
+ { label: 'Hosts', isBetaFeature: true, app: 'metrics', path: '/hosts' },
+ ],
},
]
: []),
@@ -172,54 +178,56 @@ export class Plugin implements InfraClientPluginClass {
createLogThresholdRuleType(core, logsLocator)
);
- core.application.register({
- id: 'logs',
- title: i18n.translate('xpack.infra.logs.pluginTitle', {
- defaultMessage: 'Logs',
- }),
- euiIconType: 'logoObservability',
- order: 8100,
- appRoute: '/app/logs',
- // !! Need to be kept in sync with the routes in x-pack/plugins/infra/public/pages/logs/page_content.tsx
- deepLinks: [
- {
- id: 'stream',
- title: i18n.translate('xpack.infra.logs.index.streamTabTitle', {
- defaultMessage: 'Stream',
- }),
- path: '/stream',
- },
- {
- id: 'anomalies',
- title: i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', {
- defaultMessage: 'Anomalies',
- }),
- path: '/anomalies',
- },
- {
- id: 'log-categories',
- title: i18n.translate('xpack.infra.logs.index.logCategoriesBetaBadgeTitle', {
- defaultMessage: 'Categories',
- }),
- path: '/log-categories',
- },
- {
- id: 'settings',
- title: i18n.translate('xpack.infra.logs.index.settingsTabTitle', {
- defaultMessage: 'Settings',
- }),
- path: '/settings',
+ if (this.config.featureFlags.logsUIEnabled) {
+ core.application.register({
+ id: 'logs',
+ title: i18n.translate('xpack.infra.logs.pluginTitle', {
+ defaultMessage: 'Logs',
+ }),
+ euiIconType: 'logoObservability',
+ order: 8100,
+ appRoute: '/app/logs',
+ // !! Need to be kept in sync with the routes in x-pack/plugins/infra/public/pages/logs/page_content.tsx
+ deepLinks: [
+ {
+ id: 'stream',
+ title: i18n.translate('xpack.infra.logs.index.streamTabTitle', {
+ defaultMessage: 'Stream',
+ }),
+ path: '/stream',
+ },
+ {
+ id: 'anomalies',
+ title: i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', {
+ defaultMessage: 'Anomalies',
+ }),
+ path: '/anomalies',
+ },
+ {
+ id: 'log-categories',
+ title: i18n.translate('xpack.infra.logs.index.logCategoriesBetaBadgeTitle', {
+ defaultMessage: 'Categories',
+ }),
+ path: '/log-categories',
+ },
+ {
+ id: 'settings',
+ title: i18n.translate('xpack.infra.logs.index.settingsTabTitle', {
+ defaultMessage: 'Settings',
+ }),
+ path: '/settings',
+ },
+ ],
+ category: DEFAULT_APP_CATEGORIES.observability,
+ mount: async (params: AppMountParameters) => {
+ // mount callback should not use setup dependencies, get start dependencies instead
+ const [coreStart, plugins, pluginStart] = await core.getStartServices();
+
+ const { renderApp } = await import('./apps/logs_app');
+ return renderApp(coreStart, plugins, pluginStart, params);
},
- ],
- category: DEFAULT_APP_CATEGORIES.observability,
- mount: async (params: AppMountParameters) => {
- // mount callback should not use setup dependencies, get start dependencies instead
- const [coreStart, plugins, pluginStart] = await core.getStartServices();
-
- const { renderApp } = await import('./apps/logs_app');
- return renderApp(coreStart, plugins, pluginStart, params);
- },
- });
+ });
+ }
// !! Need to be kept in sync with the routes in x-pack/plugins/infra/public/pages/metrics/index.tsx
const infraDeepLinks: AppDeepLink[] = [
@@ -285,14 +293,6 @@ export class Plugin implements InfraClientPluginClass {
},
});
- startDep$AndHostViewFlag$.subscribe(
- ([_startServices]: [[CoreStart, InfraClientStartDeps, InfraClientStartExports], boolean]) => {
- this.appUpdater$.next(() => ({
- deepLinks: infraDeepLinks,
- }));
- }
- );
-
/* This exists purely to facilitate URL redirects from the old App ID ("infra"),
to our new App IDs ("metrics" and "logs"). With version 8.0.0 we can remove this. */
core.application.register({
@@ -307,6 +307,14 @@ export class Plugin implements InfraClientPluginClass {
},
});
+ startDep$AndHostViewFlag$.subscribe(
+ ([_startServices]: [[CoreStart, InfraClientStartDeps, InfraClientStartExports], boolean]) => {
+ this.appUpdater$.next(() => ({
+ deepLinks: infraDeepLinks,
+ }));
+ }
+ );
+
// Setup telemetry events
this.telemetry.setup({ analytics: core.analytics });
diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts
index 0752c75b198c30..b3b82602f11f14 100644
--- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts
+++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts
@@ -1899,8 +1899,9 @@ const createMockStaticConfiguration = (sources: any): InfraConfig => ({
compositeSize: 2000,
},
featureFlags: {
- metricsExplorerEnabled: true,
customThresholdAlertsEnabled: false,
+ logsUIEnabled: true,
+ metricsExplorerEnabled: true,
osqueryEnabled: true,
},
enabled: true,
diff --git a/x-pack/plugins/infra/server/lib/sources/sources.test.ts b/x-pack/plugins/infra/server/lib/sources/sources.test.ts
index 53d7b8979de96c..d9e3e3ee4dbac2 100644
--- a/x-pack/plugins/infra/server/lib/sources/sources.test.ts
+++ b/x-pack/plugins/infra/server/lib/sources/sources.test.ts
@@ -126,8 +126,9 @@ const createMockStaticConfiguration = (sources: any): InfraConfig => ({
compositeSize: 2000,
},
featureFlags: {
- metricsExplorerEnabled: true,
customThresholdAlertsEnabled: false,
+ logsUIEnabled: true,
+ metricsExplorerEnabled: true,
osqueryEnabled: true,
},
sources,
diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts
index 4aeb0971dde8d4..04a57f294303f0 100644
--- a/x-pack/plugins/infra/server/plugin.ts
+++ b/x-pack/plugins/infra/server/plugin.ts
@@ -81,14 +81,18 @@ export const config: PluginConfigDescriptor = {
})
),
featureFlags: schema.object({
- metricsExplorerEnabled: offeringBasedSchema({
- traditional: schema.boolean({ defaultValue: true }),
- serverless: schema.boolean({ defaultValue: false }),
- }),
customThresholdAlertsEnabled: offeringBasedSchema({
traditional: schema.boolean({ defaultValue: false }),
serverless: schema.boolean({ defaultValue: true }),
}),
+ logsUIEnabled: offeringBasedSchema({
+ traditional: schema.boolean({ defaultValue: true }),
+ serverless: schema.boolean({ defaultValue: false }),
+ }),
+ metricsExplorerEnabled: offeringBasedSchema({
+ traditional: schema.boolean({ defaultValue: true }),
+ serverless: schema.boolean({ defaultValue: false }),
+ }),
osqueryEnabled: offeringBasedSchema({
traditional: schema.boolean({ defaultValue: true }),
serverless: schema.boolean({ defaultValue: false }),
@@ -222,14 +226,16 @@ export class InfraServerPlugin
countLogs: () => UsageCollector.countLogs(),
});
- plugins.home.sampleData.addAppLinksToSampleDataset('logs', [
- {
- sampleObject: null, // indicates that there is no sample object associated with this app link's path
- getPath: () => `/app/logs`,
- label: logsSampleDataLinkLabel,
- icon: 'logsApp',
- },
- ]);
+ if (this.config.featureFlags.logsUIEnabled) {
+ plugins.home.sampleData.addAppLinksToSampleDataset('logs', [
+ {
+ sampleObject: null, // indicates that there is no sample object associated with this app link's path
+ getPath: () => `/app/logs`,
+ label: logsSampleDataLinkLabel,
+ icon: 'logsApp',
+ },
+ ]);
+ }
initInfraServer(this.libs);
registerRuleTypes(plugins.alerting, this.libs, plugins.ml);
diff --git a/x-pack/plugins/infra/server/routes/log_alerts/chart_preview_data.ts b/x-pack/plugins/infra/server/routes/log_alerts/chart_preview_data.ts
index 1c6be6bf56c28f..05b4452dc55571 100644
--- a/x-pack/plugins/infra/server/routes/log_alerts/chart_preview_data.ts
+++ b/x-pack/plugins/infra/server/routes/log_alerts/chart_preview_data.ts
@@ -16,6 +16,11 @@ export const initGetLogAlertsChartPreviewDataRoute = ({
framework,
getStartServices,
}: Pick) => {
+ // Replace with the corresponding logs alert rule feature flag
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
+
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies.ts
index e48292a12e95b1..30a9aadda432a5 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies.ts
@@ -16,6 +16,9 @@ import { getLogEntryAnomalies } from '../../../lib/log_analysis';
import { isMlPrivilegesError } from '../../../lib/log_analysis/errors';
export const initGetLogEntryAnomaliesRoute = ({ framework }: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies_datasets.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies_datasets.ts
index 3151c6ac82b4ea..ce10ba09a059f4 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies_datasets.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies_datasets.ts
@@ -15,6 +15,9 @@ import { assertHasInfraMlPlugins } from '../../../utils/request_context';
import { isMlPrivilegesError } from '../../../lib/log_analysis/errors';
export const initGetLogEntryAnomaliesDatasetsRoute = ({ framework }: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts
index 25994b7d14a205..f51f81a846bbb5 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts
@@ -15,6 +15,9 @@ import { assertHasInfraMlPlugins } from '../../../utils/request_context';
import { isMlPrivilegesError } from '../../../lib/log_analysis/errors';
export const initGetLogEntryCategoriesRoute = ({ framework }: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts
index ba9e389b4a48eb..9ed89f1adb05bb 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts
@@ -15,6 +15,9 @@ import { assertHasInfraMlPlugins } from '../../../utils/request_context';
import { isMlPrivilegesError } from '../../../lib/log_analysis/errors';
export const initGetLogEntryCategoryDatasetsRoute = ({ framework }: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts
index ec8589416efb42..a3ea9356a4ac3e 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts
@@ -15,6 +15,9 @@ import { isMlPrivilegesError } from '../../../lib/log_analysis/errors';
import { assertHasInfraMlPlugins } from '../../../utils/request_context';
export const initGetLogEntryCategoryDatasetsStatsRoute = ({ framework }: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts
index 5e9a57768828c4..828912143d4127 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts
@@ -18,6 +18,9 @@ export const initGetLogEntryCategoryExamplesRoute = ({
framework,
getStartServices,
}: Pick) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts
index 8b3b2f0449c58c..df79783a56edc0 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts
@@ -18,6 +18,9 @@ export const initGetLogEntryExamplesRoute = ({
framework,
getStartServices,
}: Pick) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/validation/datasets.ts b/x-pack/plugins/infra/server/routes/log_analysis/validation/datasets.ts
index f76fd3a5173cf3..00ad8b951edd4a 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/validation/datasets.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/validation/datasets.ts
@@ -17,6 +17,9 @@ export const initValidateLogAnalysisDatasetsRoute = ({
framework,
logEntries,
}: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts b/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts
index 06ebd4d98b0f84..096ea2e7dd8ddd 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts
@@ -19,6 +19,10 @@ import { logAnalysisValidationV1 } from '../../../../common/http_api';
const escapeHatch = schema.object({}, { unknowns: 'allow' });
export const initValidateLogAnalysisIndicesRoute = ({ framework }: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
+
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
index 5d0c6123d4dca6..2573ef02aaf014 100644
--- a/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
+++ b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
@@ -39,12 +39,6 @@ import { DataSearchProgress } from '../../data_search_progress';
import { LogEntryActionsMenu } from './log_entry_actions_menu';
import { LogEntryFieldsTable } from './log_entry_fields_table';
-const LOGS_SYSTEM_MESSAGE = {
- content: `You are logs-gpt, a helpful assistant for logs-based observability. Answer as
- concisely as possible.`,
- role: MessageRole.System,
-};
-
export interface LogEntryFlyoutProps {
logEntryId: string | null | undefined;
onCloseFlyout: () => void;
@@ -144,10 +138,6 @@ export const LogEntryFlyout = ({
const now = new Date().toISOString();
return [
- {
- '@timestamp': now,
- message: LOGS_SYSTEM_MESSAGE,
- },
{
'@timestamp': now,
message: {
@@ -170,10 +160,6 @@ export const LogEntryFlyout = ({
const message = logEntry.fields.find((field) => field.field === 'message')?.value[0];
return [
- {
- '@timestamp': now,
- message: LOGS_SYSTEM_MESSAGE,
- },
{
'@timestamp': now,
message: {
diff --git a/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts b/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts
index a1247dbb154a9c..707a594d5eff34 100644
--- a/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts
+++ b/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts
@@ -526,9 +526,9 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu
// TODO - work out how best to display results from detectors with just an over field.
const firstFieldName =
- record.partition_field_name || record.by_field_name || record.over_field_name;
+ record.partition_field_name ?? record.by_field_name ?? record.over_field_name;
const firstFieldValue =
- record.partition_field_value || record.by_field_value || record.over_field_value;
+ record.partition_field_value ?? record.by_field_value ?? record.over_field_value;
if (firstFieldName !== undefined && firstFieldValue !== undefined) {
const groupsForDetector = detectorsForJob[detectorIndex];
@@ -544,7 +544,7 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu
let isSecondSplit = false;
if (record.partition_field_name !== undefined) {
- const splitFieldName = record.over_field_name || record.by_field_name;
+ const splitFieldName = record.over_field_name ?? record.by_field_name;
if (splitFieldName !== undefined) {
isSecondSplit = true;
}
@@ -562,8 +562,8 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu
}
} else {
// Aggregate another level for the over or by field.
- const secondFieldName = record.over_field_name || record.by_field_name;
- const secondFieldValue = record.over_field_value || record.by_field_value;
+ const secondFieldName = record.over_field_name ?? record.by_field_name;
+ const secondFieldValue = record.over_field_value ?? record.by_field_value;
if (secondFieldName !== undefined && secondFieldValue !== undefined) {
if (dataForGroupValue[secondFieldName] === undefined) {
@@ -1044,7 +1044,7 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu
let chartData: ChartPoint[] = [];
if (metricData !== undefined) {
if (records.length > 0) {
- const filterField = records[0].by_field_value || records[0].over_field_value;
+ const filterField = records[0].by_field_value ?? records[0].over_field_value;
if (eventDistribution && eventDistribution.length > 0) {
chartData = eventDistribution.filter((d: { entity: any }) => d.entity !== filterField);
}
@@ -1143,7 +1143,7 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu
chartType === CHART_TYPE.POPULATION_DISTRIBUTION
) {
return chartData.filter((d) => {
- return d.entity === (record && (record.by_field_value || record.over_field_value));
+ return d.entity === (record && (record.by_field_value ?? record.over_field_value));
});
}
diff --git a/x-pack/plugins/observability/server/services/slo/historical_summary_client.ts b/x-pack/plugins/observability/server/services/slo/historical_summary_client.ts
index 2e582fd3eac84f..41ff658c89de53 100644
--- a/x-pack/plugins/observability/server/services/slo/historical_summary_client.ts
+++ b/x-pack/plugins/observability/server/services/slo/historical_summary_client.ts
@@ -279,6 +279,7 @@ function generateSearchQuery(
window: timeWindowDurationInDays * bucketsPerDay,
shift: 1,
script: 'MovingFunctions.sum(values)',
+ gap_policy: 'insert_zeros',
},
},
cumulative_total: {
@@ -287,6 +288,7 @@ function generateSearchQuery(
window: timeWindowDurationInDays * bucketsPerDay,
shift: 1,
script: 'MovingFunctions.sum(values)',
+ gap_policy: 'insert_zeros',
},
},
},
diff --git a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx
index 2b56523d1e879a..a48d53c9420558 100644
--- a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx
+++ b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx
@@ -4,11 +4,11 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { last } from 'lodash';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { AbortError } from '@kbn/kibana-utils-plugin/common';
-import type { Subscription } from 'rxjs';
+import { isObservable, Subscription } from 'rxjs';
import { MessageRole, type Message } from '../../../common/types';
import { ObservabilityAIAssistantChatServiceProvider } from '../../context/observability_ai_assistant_chat_service_provider';
import { useKibana } from '../../hooks/use_kibana';
@@ -41,6 +41,9 @@ function ChatContent({
const chatService = useObservabilityAIAssistantChatService();
const [pendingMessage, setPendingMessage] = useState();
+
+ const [recalledMessages, setRecalledMessages] = useState(undefined);
+
const [loading, setLoading] = useState(false);
const [subscription, setSubscription] = useState();
@@ -56,37 +59,101 @@ function ChatContent({
const conversationTitle = conversationId
? conversation.value?.conversation.title || ''
: defaultTitle;
- const reloadReply = useCallback(() => {
+
+ const controllerRef = useRef(new AbortController());
+
+ const reloadRecalledMessages = useCallback(async () => {
setLoading(true);
+ setDisplayedMessages(initialMessages);
+
+ setRecalledMessages(undefined);
+
+ controllerRef.current.abort();
+
+ const controller = (controllerRef.current = new AbortController());
+
+ let appendedMessages: Message[] = [];
+
+ if (chatService.hasFunction('recall')) {
+ try {
+ // manually execute recall function and append to list of
+ // messages
+ const functionCall = {
+ name: 'recall',
+ args: JSON.stringify({ queries: [], contexts: [] }),
+ };
+
+ const response = await chatService.executeFunction({
+ ...functionCall,
+ messages: initialMessages,
+ signal: controller.signal,
+ connectorId,
+ });
+
+ if (isObservable(response)) {
+ throw new Error('Recall function unexpectedly returned an Observable');
+ }
+
+ appendedMessages = [
+ {
+ '@timestamp': new Date().toISOString(),
+ message: {
+ role: MessageRole.Assistant,
+ content: '',
+ function_call: {
+ name: functionCall.name,
+ arguments: functionCall.args,
+ trigger: MessageRole.User as const,
+ },
+ },
+ },
+ {
+ '@timestamp': new Date().toISOString(),
+ message: {
+ role: MessageRole.User,
+ name: functionCall.name,
+ content: JSON.stringify(response.content),
+ },
+ },
+ ];
+
+ setRecalledMessages(appendedMessages);
+ } catch (err) {
+ // eslint-disable-next-line no-console
+ console.error(err);
+ setRecalledMessages([]);
+ }
+ }
+ }, [chatService, connectorId, initialMessages, setDisplayedMessages]);
+
+ useEffect(() => {
let lastPendingMessage: PendingMessage | undefined;
+ if (recalledMessages === undefined) {
+ // don't do anything, it's loading
+ return;
+ }
+
const nextSubscription = chatService
- .chat({ messages: initialMessages, connectorId, function: 'none' })
+ .chat({ messages: displayedMessages.concat(recalledMessages), connectorId, function: 'none' })
.subscribe({
next: (msg) => {
lastPendingMessage = msg;
setPendingMessage(() => msg);
},
complete: () => {
- setDisplayedMessages((prevMessages) =>
- prevMessages.concat({
- '@timestamp': new Date().toISOString(),
- message: {
- ...lastPendingMessage!.message,
- },
- })
- );
+ setPendingMessage(lastPendingMessage);
setLoading(false);
},
});
setSubscription(nextSubscription);
- }, [initialMessages, setDisplayedMessages, connectorId, chatService]);
+ }, [chatService, connectorId, displayedMessages, setDisplayedMessages, recalledMessages]);
useEffect(() => {
- reloadReply();
- }, [reloadReply]);
+ reloadRecalledMessages();
+ }, [reloadRecalledMessages]);
useEffect(() => {
setDisplayedMessages(initialMessages);
@@ -96,23 +163,25 @@ function ChatContent({
const messagesWithPending = useMemo(() => {
return pendingMessage
- ? displayedMessages.concat({
+ ? displayedMessages.concat(recalledMessages || []).concat({
'@timestamp': new Date().toISOString(),
message: {
...pendingMessage.message,
},
})
- : displayedMessages;
- }, [pendingMessage, displayedMessages]);
+ : displayedMessages.concat(recalledMessages || []);
+ }, [pendingMessage, displayedMessages, recalledMessages]);
- const lastMessage = last(messagesWithPending);
+ const lastAssistantMessage = last(
+ messagesWithPending.filter((message) => message.message.role === MessageRole.Assistant)
+ );
return (
<>
{}}
/>
@@ -147,7 +216,7 @@ function ChatContent({
{
- reloadReply();
+ reloadRecalledMessages();
}}
/>
@@ -168,7 +237,7 @@ function ChatContent({
onClose={() => {
setIsOpen(() => false);
}}
- messages={displayedMessages}
+ messages={messagesWithPending}
conversationId={conversationId}
startedFrom="contextualInsight"
onChatComplete={(nextMessages) => {
diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.ts
index fc65f04c4d1162..965a8b899879ad 100644
--- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.ts
@@ -6,9 +6,10 @@
*/
import { i18n } from '@kbn/i18n';
import { merge, omit } from 'lodash';
-import { Dispatch, SetStateAction, useState } from 'react';
+import { Dispatch, SetStateAction, useMemo, useState } from 'react';
import { type Conversation, type Message } from '../../common';
-import type { ConversationCreateRequest } from '../../common/types';
+import { ConversationCreateRequest, MessageRole } from '../../common/types';
+import { getAssistantSetupMessage } from '../service/get_assistant_setup_message';
import { ObservabilityAIAssistantChatService } from '../types';
import { useAbortableAsync, type AbortableAsyncState } from './use_abortable_async';
import { useKibana } from './use_kibana';
@@ -21,7 +22,7 @@ export function useConversation({
connectorId,
}: {
conversationId?: string;
- chatService?: ObservabilityAIAssistantChatService;
+ chatService?: ObservabilityAIAssistantChatService; // will eventually resolve to a non-nullish value
connectorId: string | undefined;
}): {
conversation: AbortableAsyncState;
@@ -41,6 +42,19 @@ export function useConversation({
const [displayedMessages, setDisplayedMessages] = useState([]);
+ const displayedMessagesWithHardcodedSystemMessage = useMemo(() => {
+ if (!chatService) {
+ return displayedMessages;
+ }
+ const systemMessage = getAssistantSetupMessage({ contexts: chatService?.getContexts() || [] });
+
+ if (displayedMessages[0]?.message.role === MessageRole.User) {
+ return [systemMessage, ...displayedMessages];
+ }
+
+ return [systemMessage, ...displayedMessages.slice(1)];
+ }, [displayedMessages, chatService]);
+
const conversation: AbortableAsyncState =
useAbortableAsync(
({ signal }) => {
@@ -71,7 +85,7 @@ export function useConversation({
return {
conversation,
- displayedMessages,
+ displayedMessages: displayedMessagesWithHardcodedSystemMessage,
setDisplayedMessages,
save: (messages: Message[], handleRefreshConversations?: () => void) => {
const conversationObject = conversation.value!;
diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts
index ded7b4b3822853..299164e6f52e69 100644
--- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts
@@ -171,6 +171,8 @@ describe('useTimeline', () => {
return subject;
}),
executeFunction: jest.fn(),
+ hasFunction: jest.fn(),
+ hasRenderFunction: jest.fn(),
},
onChatUpdate: jest.fn().mockImplementation((messages) => {
props = { ...props, messages };
diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts
index ed6a5a6d2b4815..93f1cec5d6c140 100644
--- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts
@@ -5,13 +5,13 @@
* 2.0.
*/
+import { i18n } from '@kbn/i18n';
import { AbortError } from '@kbn/kibana-utils-plugin/common';
import type { AuthenticatedUser } from '@kbn/security-plugin/common';
import { last } from 'lodash';
import { useEffect, useMemo, useRef, useState } from 'react';
-import { isObservable, Observable, Subscription } from 'rxjs';
import usePrevious from 'react-use/lib/usePrevious';
-import { i18n } from '@kbn/i18n';
+import { isObservable, Observable, Subscription } from 'rxjs';
import {
ContextDefinition,
MessageRole,
@@ -20,8 +20,8 @@ import {
} from '../../common/types';
import type { ChatPromptEditorProps } from '../components/chat/chat_prompt_editor';
import type { ChatTimelineProps } from '../components/chat/chat_timeline';
+import { ChatActionClickType } from '../components/chat/types';
import { EMPTY_CONVERSATION_TITLE } from '../i18n';
-import { getAssistantSetupMessage } from '../service/get_assistant_setup_message';
import type { ObservabilityAIAssistantChatService, PendingMessage } from '../types';
import {
getTimelineItemsfromConversation,
@@ -29,7 +29,6 @@ import {
} from '../utils/get_timeline_items_from_conversation';
import type { UseGenAIConnectorsResult } from './use_genai_connectors';
import { useKibana } from './use_kibana';
-import { ChatActionClickType } from '../components/chat/types';
export function createNewConversation({
contexts,
@@ -38,7 +37,7 @@ export function createNewConversation({
}): ConversationCreateRequest {
return {
'@timestamp': new Date().toISOString(),
- messages: [getAssistantSetupMessage({ contexts })],
+ messages: [],
conversation: {
title: EMPTY_CONVERSATION_TITLE,
},
@@ -114,55 +113,71 @@ export function useTimeline({
): Promise {
const controller = new AbortController();
- return new Promise((resolve, reject) => {
- if (!connectorId) {
- reject(new Error('Can not add a message without a connector'));
- return;
- }
+ return new Promise(async (resolve, reject) => {
+ try {
+ if (!connectorId) {
+ reject(new Error('Can not add a message without a connector'));
+ return;
+ }
- onChatUpdate(nextMessages);
+ const isStartOfConversation =
+ nextMessages.some((message) => message.message.role === MessageRole.Assistant) === false;
- const lastMessage = last(nextMessages);
+ if (isStartOfConversation && chatService.hasFunction('recall')) {
+ nextMessages = nextMessages.concat({
+ '@timestamp': new Date().toISOString(),
+ message: {
+ role: MessageRole.Assistant,
+ content: '',
+ function_call: {
+ name: 'recall',
+ arguments: JSON.stringify({ queries: [], contexts: [] }),
+ trigger: MessageRole.User,
+ },
+ },
+ });
+ }
- if (lastMessage?.message.function_call?.name) {
- // the user has edited a function suggestion, no need to talk to
- resolve(undefined);
- return;
- }
+ onChatUpdate(nextMessages);
+ const lastMessage = last(nextMessages);
+ if (lastMessage?.message.function_call?.name) {
+ // the user has edited a function suggestion, no need to talk to the LLM
+ resolve(undefined);
+ return;
+ }
- response$ =
- response$ ||
- chatService!.chat({
- messages: nextMessages,
- connectorId,
+ response$ =
+ response$ ||
+ chatService!.chat({
+ messages: nextMessages,
+ connectorId,
+ });
+ let pendingMessageLocal = pendingMessage;
+ const nextSubscription = response$.subscribe({
+ next: (nextPendingMessage) => {
+ pendingMessageLocal = nextPendingMessage;
+ setPendingMessage(() => nextPendingMessage);
+ },
+ error: reject,
+ complete: () => {
+ const error = pendingMessageLocal?.error;
+ if (error) {
+ notifications.toasts.addError(error, {
+ title: i18n.translate('xpack.observabilityAiAssistant.failedToLoadResponse', {
+ defaultMessage: 'Failed to load response from the AI Assistant',
+ }),
+ });
+ }
+ resolve(pendingMessageLocal!);
+ },
});
-
- let pendingMessageLocal = pendingMessage;
-
- const nextSubscription = response$.subscribe({
- next: (nextPendingMessage) => {
- pendingMessageLocal = nextPendingMessage;
- setPendingMessage(() => nextPendingMessage);
- },
- error: reject,
- complete: () => {
- const error = pendingMessageLocal?.error;
-
- if (error) {
- notifications.toasts.addError(error, {
- title: i18n.translate('xpack.observabilityAiAssistant.failedToLoadResponse', {
- defaultMessage: 'Failed to load response from the AI Assistant',
- }),
- });
- }
- resolve(pendingMessageLocal!);
- },
- });
-
- setSubscription(() => {
- controllerRef.current = controller;
- return nextSubscription;
- });
+ setSubscription(() => {
+ controllerRef.current = controller;
+ return nextSubscription;
+ });
+ } catch (error) {
+ reject(error);
+ }
}).then(async (reply) => {
if (reply?.error) {
return nextMessages;
diff --git a/x-pack/plugins/observability_ai_assistant/public/service/create_chat_service.ts b/x-pack/plugins/observability_ai_assistant/public/service/create_chat_service.ts
index 269e81101d8aaf..8dc61e6d48449c 100644
--- a/x-pack/plugins/observability_ai_assistant/public/service/create_chat_service.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/service/create_chat_service.ts
@@ -145,6 +145,9 @@ export async function createChatService({
},
getContexts,
getFunctions,
+ hasFunction: (name: string) => {
+ return !!getFunctions().find((fn) => fn.options.name === name);
+ },
hasRenderFunction: (name: string) => {
return !!getFunctions().find((fn) => fn.options.name === name)?.render;
},
diff --git a/x-pack/plugins/observability_ai_assistant/public/service/get_assistant_setup_message.ts b/x-pack/plugins/observability_ai_assistant/public/service/get_assistant_setup_message.ts
index 4f52dfb9b97335..c7e865606aaf06 100644
--- a/x-pack/plugins/observability_ai_assistant/public/service/get_assistant_setup_message.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/service/get_assistant_setup_message.ts
@@ -7,9 +7,9 @@
import { without } from 'lodash';
import { MessageRole } from '../../common';
-import { ContextDefinition } from '../../common/types';
+import type { ContextDefinition, Message } from '../../common/types';
-export function getAssistantSetupMessage({ contexts }: { contexts: ContextDefinition[] }) {
+export function getAssistantSetupMessage({ contexts }: { contexts: ContextDefinition[] }): Message {
const coreContext = contexts.find((context) => context.name === 'core')!;
const otherContexts = without(contexts.concat(), coreContext);
diff --git a/x-pack/plugins/observability_ai_assistant/public/types.ts b/x-pack/plugins/observability_ai_assistant/public/types.ts
index 5e985f5ae5f97a..99853b7b313b7c 100644
--- a/x-pack/plugins/observability_ai_assistant/public/types.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/types.ts
@@ -59,6 +59,7 @@ export interface ObservabilityAIAssistantChatService {
}) => Observable;
getContexts: () => ContextDefinition[];
getFunctions: (options?: { contexts?: string[]; filter?: string }) => FunctionDefinition[];
+ hasFunction: (name: string) => boolean;
hasRenderFunction: (name: string) => boolean;
executeFunction: ({}: {
name: string;
diff --git a/x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx b/x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx
index 3c4dc569ab5e57..68dd784a1f8aa5 100644
--- a/x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx
+++ b/x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx
@@ -34,6 +34,7 @@ const chatService: ObservabilityAIAssistantChatService = {
renderFunction: (name: string, args: string | undefined, response: {}) => (
Hello! {name}
),
+ hasFunction: () => true,
hasRenderFunction: () => true,
};
diff --git a/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts b/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts
index a250a9e8e0915c..19ebdcbaedc95e 100644
--- a/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts
+++ b/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts
@@ -9,7 +9,6 @@ import { IncomingMessage } from 'http';
import * as t from 'io-ts';
import { toBooleanRt } from '@kbn/io-ts-utils';
import type { CreateChatCompletionResponse } from 'openai';
-import { MessageRole } from '../../../common';
import { createObservabilityAIAssistantServerRoute } from '../create_observability_ai_assistant_server_route';
import { messageRt } from '../runtime_types';
@@ -49,25 +48,12 @@ const chatRoute = createObservabilityAIAssistantServerRoute({
}
const {
- body: { messages, connectorId, functions, functionCall: givenFunctionCall },
+ body: { messages, connectorId, functions, functionCall },
query = { stream: true },
} = params;
const stream = query.stream;
- let functionCall = givenFunctionCall;
-
- if (!functionCall) {
- const isStartOfConversation =
- messages.some((message) => message.message.role === MessageRole.Assistant) === false;
-
- const isRecallFunctionAvailable = functions.some((fn) => fn.name === 'recall') === true;
-
- const willUseRecall = isStartOfConversation && isRecallFunctionAvailable;
-
- functionCall = willUseRecall ? 'recall' : undefined;
- }
-
return client.chat({
messages,
connectorId,
diff --git a/x-pack/plugins/profiling/public/components/frame_information_window/frame_information_ai_assistant.tsx b/x-pack/plugins/profiling/public/components/frame_information_window/frame_information_ai_assistant.tsx
index 01f27ef54e72f9..9175115432f81c 100644
--- a/x-pack/plugins/profiling/public/components/frame_information_window/frame_information_ai_assistant.tsx
+++ b/x-pack/plugins/profiling/public/components/frame_information_window/frame_information_ai_assistant.tsx
@@ -30,14 +30,6 @@ export function FrameInformationAIAssistant({ frame }: Props) {
const now = new Date().toISOString();
return [
- {
- '@timestamp': now,
- message: {
- role: MessageRole.System,
- content: `You are perf-gpt, a helpful assistant for performance analysis and optimisation
- of software. Answer as concisely as possible.`,
- },
- },
{
'@timestamp': now,
message: {
diff --git a/x-pack/plugins/security/server/authorization/authorization_service.tsx b/x-pack/plugins/security/server/authorization/authorization_service.tsx
index 6e0fda1fa3d8ea..a2b32a0a6b13ea 100644
--- a/x-pack/plugins/security/server/authorization/authorization_service.tsx
+++ b/x-pack/plugins/security/server/authorization/authorization_service.tsx
@@ -9,6 +9,7 @@ import querystring from 'querystring';
import React from 'react';
import { renderToString } from 'react-dom/server';
import type { Observable, Subscription } from 'rxjs';
+import { switchMap } from 'rxjs';
import type {
CapabilitiesSetup,
@@ -209,18 +210,22 @@ export class AuthorizationService {
validateFeaturePrivileges(allFeatures);
validateReservedPrivileges(allFeatures);
- this.statusSubscription = online$.subscribe(async ({ scheduleRetry }) => {
- try {
- await registerPrivilegesWithCluster(
- this.logger,
- this.privileges,
- this.applicationName,
- clusterClient
- );
- } catch (err) {
- scheduleRetry();
- }
- });
+ this.statusSubscription = online$
+ .pipe(
+ switchMap(async ({ scheduleRetry }) => {
+ try {
+ await registerPrivilegesWithCluster(
+ this.logger,
+ this.privileges,
+ this.applicationName,
+ clusterClient
+ );
+ } catch (err) {
+ scheduleRetry();
+ }
+ })
+ )
+ .subscribe();
}
stop() {
diff --git a/x-pack/plugins/security/server/session_management/session_management_service.ts b/x-pack/plugins/security/server/session_management/session_management_service.ts
index 10395999f502b0..4c3298f69bca25 100644
--- a/x-pack/plugins/security/server/session_management/session_management_service.ts
+++ b/x-pack/plugins/security/server/session_management/session_management_service.ts
@@ -6,6 +6,7 @@
*/
import type { Observable, Subscription } from 'rxjs';
+import { switchMap } from 'rxjs';
import type { ElasticsearchClient, HttpServiceSetup, Logger } from '@kbn/core/server';
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
@@ -90,13 +91,20 @@ export class SessionManagementService {
auditLogger: audit.withoutRequest,
});
- this.statusSubscription = online$.subscribe(async ({ scheduleRetry }) => {
- try {
- await Promise.all([this.sessionIndex.initialize(), this.scheduleCleanupTask(taskManager)]);
- } catch (err) {
- scheduleRetry();
- }
- });
+ this.statusSubscription = online$
+ .pipe(
+ switchMap(async ({ scheduleRetry }) => {
+ try {
+ await Promise.all([
+ this.sessionIndex.initialize(),
+ this.scheduleCleanupTask(taskManager),
+ ]);
+ } catch (err) {
+ scheduleRetry();
+ }
+ })
+ )
+ .subscribe();
return {
session: new Session({
diff --git a/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx b/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx
index 08818172bca5af..fe21d973c86b84 100644
--- a/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx
@@ -167,6 +167,7 @@ export const QueryBar = memo(
savedQuery={savedQuery}
displayStyle={displayStyle}
isDisabled={isDisabled}
+ hideTextBasedRunQueryLabel
/>
);
}
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx
index 0f6a3fad1eb66a..71c41b5dd5c9f8 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx
@@ -88,8 +88,6 @@ const GraphTab = tabWithSuspense(lazy(() => import('../graph_tab_content')));
const NotesTab = tabWithSuspense(lazy(() => import('../notes_tab_content')));
const PinnedTab = tabWithSuspense(lazy(() => import('../pinned_tab_content')));
const SessionTab = tabWithSuspense(lazy(() => import('../session_tab_content')));
-const DiscoverTab = tabWithSuspense(lazy(() => import('../discover_tab_content')));
-
interface BasicTimelineTab {
renderCellValue: (props: CellValueElementProps) => React.ReactNode;
rowRenderers: RowRenderer[];
@@ -134,7 +132,6 @@ const ActiveTimelineTab = memo(
setConversationId,
showTimeline,
}) => {
- const isDiscoverInTimelineEnabled = useIsExperimentalFeatureEnabled('discoverInTimeline');
const { hasAssistantPrivilege } = useAssistantAvailability();
const getTab = useCallback(
(tab: TimelineTabs) => {
@@ -231,14 +228,6 @@ const ActiveTimelineTab = memo(
)}
)}
- {isDiscoverInTimelineEnabled && (
-
-
-
- )}
>
);
}
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/esql.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/esql.ts
index 0de78a4887e688..73a9cbe6b93a9c 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/esql.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/esql.ts
@@ -100,7 +100,7 @@ export const esqlExecutor = async ({
});
const esqlSearchDuration = makeFloatString(performance.now() - esqlSignalSearchStart);
- result.searchAfterTimes = [esqlSearchDuration];
+ result.searchAfterTimes.push(esqlSearchDuration);
ruleExecutionLogger.debug(`ES|QL query request took: ${esqlSearchDuration}ms`);
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/row_to_document.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/row_to_document.test.ts
index cfe59dafa2ed47..7b52018a5e490b 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/row_to_document.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/row_to_document.test.ts
@@ -19,7 +19,6 @@ describe('rowToDocument', () => {
const row = ['abcd', null, '8.8.1', 'packetbeat'];
expect(rowToDocument(columns, row)).toEqual({
_id: 'abcd',
- 'agent.name': null,
'agent.version': '8.8.1',
'agent.type': 'packetbeat',
});
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/row_to_document.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/row_to_document.ts
index 6049f3892fa9ea..976c403205200b 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/row_to_document.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/row_to_document.ts
@@ -18,8 +18,10 @@ export const rowToDocument = (
row: EsqlResultRow
): Record => {
return columns.reduce>((acc, column, i) => {
- acc[column.name] = row[i];
-
+ // skips nulls, as ES|QL return null for each existing mapping field
+ if (row[i] !== null) {
+ acc[column.name] = row[i];
+ }
return acc;
}, {});
};
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.test.ts
index 9c10a317ee17cb..21f9adc96bd602 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.test.ts
@@ -71,6 +71,27 @@ describe('stripNonEcsFields', () => {
]);
});
+ // https://github.com/elastic/sdh-security-team/issues/736
+ describe('fields that exists in the alerts mapping but not in local ECS(ruleRegistry) definition', () => {
+ it('should strip object type "device" field if it is supplied as a keyword', () => {
+ const { result, removed } = stripNonEcsFields({
+ device: 'test',
+ message: 'test message',
+ });
+
+ expect(result).toEqual({
+ message: 'test message',
+ });
+
+ expect(removed).toEqual([
+ {
+ key: 'device',
+ value: 'test',
+ },
+ ]);
+ });
+ });
+
describe('array fields', () => {
it('should not strip arrays of objects when an object is expected', () => {
const { result, removed } = stripNonEcsFields({
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts
index 95261a185526bd..86a3dc4ff6c1f0 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ecsFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/ecs_field_map';
+import { ecsFieldMap } from '@kbn/alerts-as-data-utils';
import { isPlainObject, cloneDeep, isArray } from 'lodash';
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/links/constants.ts b/x-pack/plugins/security_solution_serverless/public/navigation/links/constants.ts
index 565b2d2c97c80b..dca89a4a00111d 100644
--- a/x-pack/plugins/security_solution_serverless/public/navigation/links/constants.ts
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/links/constants.ts
@@ -30,6 +30,8 @@ export const SecurityPagePath = {
* The path should not be used for links displayed in the main left navigation, since highlighting won't work.
**/
export enum ExternalPageName {
+ // Discover
+ discover = 'discover:',
// Osquery
osquery = 'osquery:',
// Analytics
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/links/nav.links.test.ts b/x-pack/plugins/security_solution_serverless/public/navigation/links/nav.links.test.ts
index 0a20899462c204..d06b28df019550 100644
--- a/x-pack/plugins/security_solution_serverless/public/navigation/links/nav.links.test.ts
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/links/nav.links.test.ts
@@ -57,6 +57,11 @@ const projectLinkDevTools: ProjectNavigationLink = {
title: 'Dev tools',
};
+const projectLinkDiscover: ProjectNavigationLink = {
+ id: ExternalPageName.discover,
+ title: 'Discover',
+};
+
const chromeNavLink1: ChromeNavLink = {
id: `${APP_UI_ID}:${link1.id}`,
title: link1.title,
@@ -145,6 +150,7 @@ describe('getProjectNavLinks', () => {
link1,
link2,
{ ...linkMlLanding, categories: mlNavCategories, links: mlNavLinks },
+ projectLinkDiscover,
projectLinkDevTools,
]);
});
@@ -169,6 +175,7 @@ describe('getProjectNavLinks', () => {
expect(value).toEqual([
link1,
{ ...linkAssets, links: [...assetsNavLinks, link2] },
+ projectLinkDiscover,
projectLinkDevTools,
]);
});
@@ -193,6 +200,7 @@ describe('getProjectNavLinks', () => {
expect(value).toEqual([
link1,
{ ...linkInvestigations, links: [link2, ...investigationsNavLinks] },
+ projectLinkDiscover,
projectLinkDevTools,
]);
});
@@ -226,6 +234,7 @@ describe('getProjectNavLinks', () => {
categories: projectSettingsNavCategories,
links: [...expectedProjectSettingsNavLinks, link2],
},
+ projectLinkDiscover,
projectLinkDevTools,
]);
});
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/links/nav_links.ts b/x-pack/plugins/security_solution_serverless/public/navigation/links/nav_links.ts
index 2da3279562191f..76f4e752af6c5b 100644
--- a/x-pack/plugins/security_solution_serverless/public/navigation/links/nav_links.ts
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/links/nav_links.ts
@@ -18,6 +18,7 @@ import {
projectSettingsNavLinks,
} from './sections/project_settings_links';
import { devToolsNavLink } from './sections/dev_tools_links';
+import { discoverNavLink } from './sections/discover_links';
import type { ProjectNavigationLink } from './types';
import { getCloudLinkKey, getCloudUrl, getNavLinkIdFromProjectPageName, isCloudLink } from './util';
import { investigationsNavLinks } from './sections/investigations_links';
@@ -55,6 +56,9 @@ const processNavLinks = (
): ProjectNavigationLink[] => {
const projectNavLinks: ProjectNavigationLink[] = [...securityNavLinks];
+ // Discover. just pushing it
+ projectNavLinks.push(discoverNavLink);
+
// Investigations. injecting external sub-links and categories definition to the landing
const investigationsLinkIndex = projectNavLinks.findIndex(
({ id }) => id === SecurityPageName.investigations
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/links/sections/discover_links.ts b/x-pack/plugins/security_solution_serverless/public/navigation/links/sections/discover_links.ts
new file mode 100644
index 00000000000000..8c0bf26ec2d12f
--- /dev/null
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/links/sections/discover_links.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { ExternalPageName } from '../constants';
+import type { ProjectNavigationLink } from '../types';
+import { DISCOVER_TITLE } from './discover_translations';
+
+export const discoverNavLink: ProjectNavigationLink = {
+ id: ExternalPageName.discover,
+ title: DISCOVER_TITLE,
+};
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/links/sections/discover_translations.ts b/x-pack/plugins/security_solution_serverless/public/navigation/links/sections/discover_translations.ts
new file mode 100644
index 00000000000000..0c1212c187aa2c
--- /dev/null
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/links/sections/discover_translations.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const DISCOVER_TITLE = i18n.translate(
+ 'xpack.securitySolutionServerless.navLinks.discover.title',
+ {
+ defaultMessage: 'Discover',
+ }
+);
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts
index ebefe9b77a70be..e7c9057d950ff7 100644
--- a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts
@@ -15,7 +15,7 @@ import { ExternalPageName } from '../links/constants';
export const CATEGORIES: SeparatorLinkCategory[] = [
{
type: LinkCategoryType.separator,
- linkIds: [SecurityPageName.dashboards],
+ linkIds: [ExternalPageName.discover, SecurityPageName.dashboards],
},
{
type: LinkCategoryType.separator,
diff --git a/x-pack/plugins/security_solution_serverless/server/endpoint/services/set_package_policy_flag.test.ts b/x-pack/plugins/security_solution_serverless/server/endpoint/services/set_package_policy_flag.test.ts
index 54a95ae68a1b7c..fad1317af898bd 100644
--- a/x-pack/plugins/security_solution_serverless/server/endpoint/services/set_package_policy_flag.test.ts
+++ b/x-pack/plugins/security_solution_serverless/server/endpoint/services/set_package_policy_flag.test.ts
@@ -82,6 +82,35 @@ describe('setEndpointPackagePolicyServerlessFlag', () => {
);
});
+ it('updates serverless flag for endpoint policies with the flag already set', async () => {
+ const packagePolicy1 = generatePackagePolicy(
+ policyFactory(undefined, undefined, undefined, undefined, undefined, true)
+ );
+ const packagePolicy2 = generatePackagePolicy(
+ policyFactory(undefined, undefined, undefined, undefined, undefined, true)
+ );
+ packagePolicyServiceMock.list.mockResolvedValue({
+ items: [packagePolicy1, packagePolicy2],
+ page: 1,
+ perPage: SO_SEARCH_LIMIT,
+ total: 2,
+ });
+ packagePolicyServiceMock.bulkCreate.mockImplementation();
+
+ await setEndpointPackagePolicyServerlessFlag(
+ soClientMock,
+ esClientMock,
+ packagePolicyServiceMock
+ );
+
+ expect(packagePolicyServiceMock.list).toBeCalledWith(soClientMock, {
+ page: 1,
+ perPage: SO_SEARCH_LIMIT,
+ kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${FLEET_ENDPOINT_PACKAGE}`,
+ });
+ expect(packagePolicyServiceMock.bulkUpdate).not.toBeCalled();
+ });
+
it('batches properly when over perPage', async () => {
packagePolicyServiceMock.list
.mockResolvedValueOnce({
diff --git a/x-pack/plugins/security_solution_serverless/server/endpoint/services/set_package_policy_flag.ts b/x-pack/plugins/security_solution_serverless/server/endpoint/services/set_package_policy_flag.ts
index 0c6191e8df706d..8e41dba3502d1a 100644
--- a/x-pack/plugins/security_solution_serverless/server/endpoint/services/set_package_policy_flag.ts
+++ b/x-pack/plugins/security_solution_serverless/server/endpoint/services/set_package_policy_flag.ts
@@ -60,30 +60,44 @@ async function processBatch(
return;
}
- const updatedEndpointPackages = endpointPackagesResult.items.map((endpointPackage) => ({
- ...endpointPackage,
- inputs: endpointPackage.inputs.map((input) => {
- const config = input?.config || {};
- const policy = config.policy || {};
- const policyValue = policy?.value || {};
- const meta = policyValue?.meta || {};
- return {
- ...input,
- config: {
- ...config,
- policy: {
- ...policy,
- value: {
- ...policyValue,
- meta: {
- ...meta,
- serverless: true,
+ const updatedEndpointPackages = endpointPackagesResult.items
+ .filter(
+ (endpointPackage) =>
+ !(
+ endpointPackage?.inputs.every(
+ (input) => input.config?.policy?.value?.meta?.serverless ?? false
+ ) ?? false
+ )
+ )
+ .map((endpointPackage) => ({
+ ...endpointPackage,
+ inputs: endpointPackage.inputs.map((input) => {
+ const config = input?.config || {};
+ const policy = config.policy || {};
+ const policyValue = policy?.value || {};
+ const meta = policyValue?.meta || {};
+ return {
+ ...input,
+ config: {
+ ...config,
+ policy: {
+ ...policy,
+ value: {
+ ...policyValue,
+ meta: {
+ ...meta,
+ serverless: true,
+ },
},
},
},
- },
- };
- }),
- }));
+ };
+ }),
+ }));
+
+ if (updatedEndpointPackages.length === 0) {
+ return;
+ }
+
await packagePolicyService.bulkUpdate(soClient, esClient, updatedEndpointPackages);
}
diff --git a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.ts b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.ts
index 561a0e5a90e4c4..da0434201eaa24 100644
--- a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.ts
+++ b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.ts
@@ -14,7 +14,7 @@ import {
TaskManagerSetupContract,
TaskManagerStartContract,
} from '@kbn/task-manager-plugin/server';
-import { Subject } from 'rxjs';
+import { concatMap, Subject } from 'rxjs';
import { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server';
import pMap from 'p-map';
import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common';
@@ -377,45 +377,49 @@ export class SyntheticsService {
let output: ServiceData['output'] | null = null;
- subject.subscribe(async (monitors) => {
- try {
- if (monitors.length === 0 || !this.config.manifestUrl) {
- return;
- }
+ subject
+ .pipe(
+ concatMap(async (monitors) => {
+ try {
+ if (monitors.length === 0 || !this.config.manifestUrl) {
+ return;
+ }
- if (!output) {
- output = await this.getOutput();
+ if (!output) {
+ output = await this.getOutput();
+
+ if (!output) {
+ sendErrorTelemetryEvents(service.logger, service.server.telemetry, {
+ reason: 'API key is not valid.',
+ message: 'Failed to push configs. API key is not valid.',
+ type: 'invalidApiKey',
+ stackVersion: service.server.stackVersion,
+ });
+ return;
+ }
+ }
- if (!output) {
+ this.logger.debug(`${monitors.length} monitors will be pushed to synthetics service.`);
+
+ service.syncErrors = await this.apiClient.syncMonitors({
+ monitors,
+ output,
+ license,
+ });
+ } catch (e) {
sendErrorTelemetryEvents(service.logger, service.server.telemetry, {
- reason: 'API key is not valid.',
- message: 'Failed to push configs. API key is not valid.',
- type: 'invalidApiKey',
+ reason: 'Failed to push configs to service',
+ message: e?.message,
+ type: 'pushConfigsError',
+ code: e?.code,
+ status: e.status,
stackVersion: service.server.stackVersion,
});
- return;
+ this.logger.error(e);
}
- }
-
- this.logger.debug(`${monitors.length} monitors will be pushed to synthetics service.`);
-
- service.syncErrors = await this.apiClient.syncMonitors({
- monitors,
- output,
- license,
- });
- } catch (e) {
- sendErrorTelemetryEvents(service.logger, service.server.telemetry, {
- reason: 'Failed to push configs to service',
- message: e?.message,
- type: 'pushConfigsError',
- code: e?.code,
- status: e.status,
- stackVersion: service.server.stackVersion,
- });
- this.logger.error(e);
- }
- });
+ })
+ )
+ .subscribe();
await this.getMonitorConfigs(subject);
}
@@ -479,25 +483,29 @@ export class SyntheticsService {
const license = await this.getLicense();
const subject = new Subject();
- subject.subscribe(async (monitors) => {
- const hasPublicLocations = monitors.some((config) =>
- config.locations.some(({ isServiceManaged }) => isServiceManaged)
- );
-
- if (hasPublicLocations) {
- const output = await this.getOutput();
- if (!output) {
- return;
- }
-
- const data = {
- output,
- monitors,
- license,
- };
- return await this.apiClient.delete(data);
- }
- });
+ subject
+ .pipe(
+ concatMap(async (monitors) => {
+ const hasPublicLocations = monitors.some((config) =>
+ config.locations.some(({ isServiceManaged }) => isServiceManaged)
+ );
+
+ if (hasPublicLocations) {
+ const output = await this.getOutput();
+ if (!output) {
+ return;
+ }
+
+ const data = {
+ output,
+ monitors,
+ license,
+ };
+ return await this.apiClient.delete(data);
+ }
+ })
+ )
+ .subscribe();
await this.getMonitorConfigs(subject);
}
diff --git a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.ts b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.ts
index 16d8cd796809d5..e5ca18f39c0b40 100644
--- a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.ts
+++ b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.ts
@@ -85,7 +85,7 @@ export class EphemeralTaskLifecycle {
);
})
)
- .subscribe(async (e) => {
+ .subscribe((e) => {
let overallCapacity = this.getCapacity();
const capacityByType = new Map();
const tasksWithinCapacity = [...this.ephemeralTaskQueue]
diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json
index f3514ba7fbb65d..c1f3af869cb78e 100644
--- a/x-pack/plugins/translations/translations/fr-FR.json
+++ b/x-pack/plugins/translations/translations/fr-FR.json
@@ -229,6 +229,8 @@
"coloring.dynamicColoring.rangeType.label": "Type de valeur",
"coloring.dynamicColoring.rangeType.number": "Numéro",
"coloring.dynamicColoring.rangeType.percent": "Pourcent",
+ "coloring.colorMapping.terms.otherBucketLabel": "Autre",
+ "coloring.colorMapping.terms.emptyLabel": "(vide)",
"console.helpPage.learnAboutConsoleAndQueryDslText": "En savoir plus sur {console} et {queryDsl}",
"console.historyPage.itemOfRequestListAriaLabel": "Requête : {historyItem}",
"console.settingsPage.refreshInterval.everyNMinutesTimeInterval": "Toutes les {value} {value, plural, one {minute} many {minutes} other {minutes}}",
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index d078f535b6c998..18492c43f42164 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -229,6 +229,8 @@
"coloring.dynamicColoring.rangeType.label": "値型",
"coloring.dynamicColoring.rangeType.number": "数字",
"coloring.dynamicColoring.rangeType.percent": "割合(%)",
+ "coloring.colorMapping.terms.otherBucketLabel":"その他",
+ "coloring.colorMapping.terms.emptyLabel": "(空)",
"console.helpPage.learnAboutConsoleAndQueryDslText": "{console}と{queryDsl}についてさらに詳しく",
"console.historyPage.itemOfRequestListAriaLabel": "リクエスト:{historyItem}",
"console.settingsPage.refreshInterval.everyNMinutesTimeInterval": "{value}{value, plural, other {分}}毎",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 3b8006085b1daa..296b107b99e5c8 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -229,6 +229,8 @@
"coloring.dynamicColoring.rangeType.label": "值类型",
"coloring.dynamicColoring.rangeType.number": "数字",
"coloring.dynamicColoring.rangeType.percent": "百分比",
+ "coloring.colorMapping.terms.otherBucketLabel":"其他",
+ "coloring.colorMapping.terms.emptyLabel": "(空)",
"console.helpPage.learnAboutConsoleAndQueryDslText": "了解 {console} 和 {queryDsl}",
"console.historyPage.itemOfRequestListAriaLabel": "请求:{historyItem}",
"console.settingsPage.refreshInterval.everyNMinutesTimeInterval": "每 {value} {value, plural, other {分钟}}",
diff --git a/x-pack/test/api_integration/apis/ml/modules/get_module.ts b/x-pack/test/api_integration/apis/ml/modules/get_module.ts
index 488714f13c399e..7c72f18f918e39 100644
--- a/x-pack/test/api_integration/apis/ml/modules/get_module.ts
+++ b/x-pack/test/api_integration/apis/ml/modules/get_module.ts
@@ -49,8 +49,7 @@ export default ({ getService }: FtrProviderContext) => {
return body;
}
- // FLAKY: https://github.com/elastic/kibana/issues/164420
- describe.skip('get_module', function () {
+ describe('get_module', function () {
before(async () => {
await ml.testResources.setKibanaTimeZoneToUTC();
});
diff --git a/x-pack/test/api_integration/apis/ml/modules/index.ts b/x-pack/test/api_integration/apis/ml/modules/index.ts
index d28263b4877011..68db43fd0ea3c4 100644
--- a/x-pack/test/api_integration/apis/ml/modules/index.ts
+++ b/x-pack/test/api_integration/apis/ml/modules/index.ts
@@ -26,6 +26,10 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
const version = await ml.testResources.installFleetPackage(fleetPackage);
installedPackages.push({ pkgName: fleetPackage, version });
}
+
+ // ensure fleet installed packages are ready
+ await ml.testResources.assertModuleExists('apache_data_stream');
+ await ml.testResources.assertModuleExists('nginx_data_stream');
});
after(async () => {
diff --git a/x-pack/test/api_integration/apis/synthetics/services/private_location_test_service.ts b/x-pack/test/api_integration/apis/synthetics/services/private_location_test_service.ts
index 60ee844e11ab8b..046d5098e6a5bd 100644
--- a/x-pack/test/api_integration/apis/synthetics/services/private_location_test_service.ts
+++ b/x-pack/test/api_integration/apis/synthetics/services/private_location_test_service.ts
@@ -10,7 +10,7 @@ import { privateLocationsSavedObjectId } from '@kbn/synthetics-plugin/server/sav
import { FtrProviderContext } from '../../../ftr_provider_context';
import { KibanaSupertestProvider } from '../../../../../../test/api_integration/services/supertest';
-export const INSTALLED_VERSION = '1.0.7';
+export const INSTALLED_VERSION = '1.1.0';
export class PrivateLocationTestService {
private supertest: ReturnType;
diff --git a/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts
index e3b0d06fbc5a19..e9519a0122203d 100644
--- a/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts
+++ b/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts
@@ -32,6 +32,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const registry = getService('registry');
const supertest = getService('supertest');
const es = getService('es');
+ const logger = getService('log');
const apmApiClient = getService('apmApiClient');
const synthtraceEsClient = getService('synthtraceEsClient');
@@ -133,9 +134,13 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
after(async () => {
- await deleteActionConnector({ supertest, es, actionId });
- await deleteRuleById({ supertest, ruleId });
- await deleteAlertsByRuleId({ es, ruleId });
+ try {
+ await deleteActionConnector({ supertest, es, actionId });
+ await deleteRuleById({ supertest, ruleId });
+ await deleteAlertsByRuleId({ es, ruleId });
+ } catch (e) {
+ logger.info('Could not delete rule or action connector', e);
+ }
});
it('checks if rule is active', async () => {
diff --git a/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts
index 3dbfa93a69d468..cf4b713aa9586a 100644
--- a/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts
+++ b/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts
@@ -32,6 +32,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const registry = getService('registry');
const supertest = getService('supertest');
const es = getService('es');
+ const logger = getService('log');
const apmApiClient = getService('apmApiClient');
const synthtraceEsClient = getService('synthtraceEsClient');
@@ -106,9 +107,13 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
after(async () => {
- await deleteActionConnector({ supertest, es, actionId });
- await deleteAlertsByRuleId({ es, ruleId });
- await deleteRuleById({ supertest, ruleId });
+ try {
+ await deleteActionConnector({ supertest, es, actionId });
+ await deleteRuleById({ supertest, ruleId });
+ await deleteAlertsByRuleId({ es, ruleId });
+ } catch (e) {
+ logger.info('Could not delete rule or action connector', e);
+ }
});
it('checks if rule is active', async () => {
diff --git a/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts
index e51372f3d8b014..394db386c80e7d 100644
--- a/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts
+++ b/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts
@@ -32,6 +32,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const registry = getService('registry');
const supertest = getService('supertest');
const es = getService('es');
+ const logger = getService('log');
const apmApiClient = getService('apmApiClient');
const synthtraceEsClient = getService('synthtraceEsClient');
@@ -116,9 +117,13 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
after(async () => {
- await deleteActionConnector({ supertest, es, actionId });
- await deleteRuleById({ supertest, ruleId });
- await deleteAlertsByRuleId({ es, ruleId });
+ try {
+ await deleteActionConnector({ supertest, es, actionId });
+ await deleteRuleById({ supertest, ruleId });
+ await deleteAlertsByRuleId({ es, ruleId });
+ } catch (e) {
+ logger.info('Could not delete rule or action connector', e);
+ }
});
it('checks if rule is active', async () => {
diff --git a/x-pack/test/apm_api_integration/tests/index.ts b/x-pack/test/apm_api_integration/tests/index.ts
index 8f4be6ad1652cc..7b4ab43aaaeaea 100644
--- a/x-pack/test/apm_api_integration/tests/index.ts
+++ b/x-pack/test/apm_api_integration/tests/index.ts
@@ -26,10 +26,7 @@ function getGlobPattern() {
export default function apmApiIntegrationTests({ getService, loadTestFile }: FtrProviderContext) {
const registry = getService('registry');
- // Failing: See https://github.com/elastic/kibana/issues/167973
- // FLAKY: https://github.com/elastic/kibana/issues/167974
- // Failing: See https://github.com/elastic/kibana/issues/167975
- describe.skip('APM API tests', function () {
+ describe('APM API tests', function () {
const filePattern = getGlobPattern();
const tests = globby.sync(filePattern, { cwd });
diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/esql.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/esql.ts
index 055d2304ce367d..daac0f6c17dddb 100644
--- a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/esql.ts
+++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/esql.ts
@@ -116,9 +116,6 @@ export default ({ getService }: FtrProviderContext) => {
'kibana.space_ids': ['default'],
'kibana.alert.rule.tags': [],
'agent.name': 'test-1',
- 'agent.type': null,
- 'agent.version': null,
- 'host.name': null,
id,
'event.kind': 'signal',
'kibana.alert.original_time': expect.any(String),
@@ -155,8 +152,6 @@ export default ({ getService }: FtrProviderContext) => {
'kibana.alert.workflow_tags': [],
'kibana.alert.rule.risk_score': 55,
'kibana.alert.rule.severity': 'high',
- 'kibana.alert.original_event.created': null,
- 'kibana.alert.original_event.ingested': null,
});
});
@@ -829,5 +824,163 @@ export default ({ getService }: FtrProviderContext) => {
expect(previewAlerts[0]._source).toHaveProperty('host.risk.calculated_score_norm', 1);
});
});
+
+ describe('ECS fields validation', () => {
+ it('creates alert if ECS field has multifields', async () => {
+ const id = uuidv4();
+ const interval: [string, string] = ['2020-10-28T06:00:00.000Z', '2020-10-28T06:10:00.000Z'];
+ const doc1 = { agent: { name: 'test-1' }, 'observer.os.full': 'full test os' };
+ const doc2 = { agent: { name: 'test-2' } };
+
+ const rule: EsqlRuleCreateProps = {
+ ...getCreateEsqlRulesSchemaMock('rule-1', true),
+ query: `from ecs_compliant [metadata _id] ${internalIdPipe(
+ id
+ )} | where agent.name=="test-1"`,
+ from: 'now-1h',
+ interval: '1h',
+ };
+
+ await indexEnhancedDocuments({
+ documents: [doc1, doc2],
+ interval,
+ id,
+ });
+
+ const { previewId } = await previewRule({
+ supertest,
+ rule,
+ timeframeEnd: new Date('2020-10-28T06:30:00.000Z'),
+ });
+
+ const previewAlerts = await getPreviewAlerts({
+ es,
+ previewId,
+ size: 10,
+ });
+
+ expect(previewAlerts.length).toBe(1);
+ expect(previewAlerts[0]._source).toHaveProperty(['observer.os.full'], 'full test os');
+ // *.text is multifield define in mappings for observer.os.full
+ expect(previewAlerts[0]._source).not.toHaveProperty(['observer.os.full.text']);
+ });
+ // https://github.com/elastic/security-team/issues/7741
+ it('creates alert if ECS field has multifields and is not in ruleRegistry ECS map', async () => {
+ const id = uuidv4();
+ const interval: [string, string] = ['2020-10-28T06:00:00.000Z', '2020-10-28T06:10:00.000Z'];
+ const doc1 = {
+ agent: { name: 'test-1' },
+ // this field is mapped in alerts index, but not in ruleRegistry ECS map
+ 'process.entry_leader.name': 'test_process_name',
+ };
+ const doc2 = { agent: { name: 'test-2' } };
+
+ const rule: EsqlRuleCreateProps = {
+ ...getCreateEsqlRulesSchemaMock('rule-1', true),
+ query: `from ecs_compliant [metadata _id] ${internalIdPipe(
+ id
+ )} | where agent.name=="test-1"`,
+ from: 'now-1h',
+ interval: '1h',
+ };
+
+ await indexEnhancedDocuments({
+ documents: [doc1, doc2],
+ interval,
+ id,
+ });
+
+ const { previewId } = await previewRule({
+ supertest,
+ rule,
+ timeframeEnd: new Date('2020-10-28T06:30:00.000Z'),
+ });
+
+ const previewAlerts = await getPreviewAlerts({
+ es,
+ previewId,
+ size: 10,
+ });
+
+ expect(previewAlerts.length).toBe(1);
+ expect(previewAlerts[0]._source).toHaveProperty(
+ ['process.entry_leader.name'],
+ 'test_process_name'
+ );
+ expect(previewAlerts[0]._source).not.toHaveProperty(['process.entry_leader.name.text']);
+ expect(previewAlerts[0]._source).not.toHaveProperty(['process.entry_leader.name.caseless']);
+ });
+
+ describe('non-ecs', () => {
+ before(async () => {
+ await esArchiver.load(
+ 'x-pack/test/functional/es_archives/security_solution/ecs_non_compliant'
+ );
+ });
+
+ after(async () => {
+ await esArchiver.unload(
+ 'x-pack/test/functional/es_archives/security_solution/ecs_non_compliant'
+ );
+ });
+
+ const { indexEnhancedDocuments: indexEnhancedDocumentsToNonEcs } = dataGeneratorFactory({
+ es,
+ index: 'ecs_non_compliant',
+ log,
+ });
+
+ it('creates alert if non ECS field has multifields', async () => {
+ const id = uuidv4();
+ const interval: [string, string] = [
+ '2020-10-28T06:00:00.000Z',
+ '2020-10-28T06:10:00.000Z',
+ ];
+ const doc1 = {
+ 'random.entry_leader.name': 'random non-ecs field',
+ };
+
+ const rule: EsqlRuleCreateProps = {
+ ...getCreateEsqlRulesSchemaMock('rule-1', true),
+ query: `from ecs_non_compliant [metadata _id] ${internalIdPipe(id)}`,
+ from: 'now-1h',
+ interval: '1h',
+ };
+
+ await indexEnhancedDocumentsToNonEcs({
+ documents: [doc1],
+ interval,
+ id,
+ });
+
+ const { previewId } = await previewRule({
+ supertest,
+ rule,
+ timeframeEnd: new Date('2020-10-28T06:30:00.000Z'),
+ });
+
+ const previewAlerts = await getPreviewAlerts({
+ es,
+ previewId,
+ size: 10,
+ });
+
+ expect(previewAlerts.length).toBe(1);
+ // all multifields have been indexed, which is expected, seen we don't know original mappings
+ expect(previewAlerts[0]._source).toHaveProperty(
+ ['random.entry_leader.name'],
+ 'random non-ecs field'
+ );
+ expect(previewAlerts[0]._source).toHaveProperty(
+ ['random.entry_leader.name.text'],
+ 'random non-ecs field'
+ );
+ expect(previewAlerts[0]._source).toHaveProperty(
+ ['random.entry_leader.name.caseless'],
+ 'random non-ecs field'
+ );
+ });
+ });
+ });
});
};
diff --git a/x-pack/test/functional/es_archives/security_solution/ecs_compliant/mappings.json b/x-pack/test/functional/es_archives/security_solution/ecs_compliant/mappings.json
index c1d61b21fd295a..897ac651fcb432 100644
--- a/x-pack/test/functional/es_archives/security_solution/ecs_compliant/mappings.json
+++ b/x-pack/test/functional/es_archives/security_solution/ecs_compliant/mappings.json
@@ -23,6 +23,45 @@
}
}
},
+ "observer": {
+ "properties": {
+ "os": {
+ "properties": {
+ "full": {
+ "type": "keyword",
+ "ignore_above": 1024,
+ "fields": {
+ "text": {
+ "type": "match_only_text"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "process": {
+ "properties": {
+ "entry_leader": {
+ "properties": {
+ "name": {
+ "type": "keyword",
+ "ignore_above": 1024,
+ "fields": {
+ "text": {
+ "type": "match_only_text"
+ },
+ "caseless": {
+ "type": "keyword",
+ "ignore_above": 1024,
+ "normalizer": "lowercase"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"host": {
"properties": {
"name": {
diff --git a/x-pack/test/functional/es_archives/security_solution/ecs_non_compliant/mappings.json b/x-pack/test/functional/es_archives/security_solution/ecs_non_compliant/mappings.json
index 40408d65b6d89c..ea4f271af0ffcd 100644
--- a/x-pack/test/functional/es_archives/security_solution/ecs_non_compliant/mappings.json
+++ b/x-pack/test/functional/es_archives/security_solution/ecs_non_compliant/mappings.json
@@ -73,6 +73,28 @@
"type": "text"
}
}
+ },
+ "random": {
+ "properties": {
+ "entry_leader": {
+ "properties": {
+ "name": {
+ "type": "keyword",
+ "ignore_above": 1024,
+ "fields": {
+ "text": {
+ "type": "match_only_text"
+ },
+ "caseless": {
+ "type": "keyword",
+ "ignore_above": 1024,
+ "normalizer": "lowercase"
+ }
+ }
+ }
+ }
+ }
+ }
}
}
},
diff --git a/x-pack/test/functional/services/ml/api.ts b/x-pack/test/functional/services/ml/api.ts
index cf71764ee5397d..b514d18d552ad3 100644
--- a/x-pack/test/functional/services/ml/api.ts
+++ b/x-pack/test/functional/services/ml/api.ts
@@ -1552,5 +1552,14 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) {
log.debug('Module set up');
return module;
},
+
+ async getModule(moduleId: string) {
+ log.debug(`Get module with ID: "${moduleId}"`);
+ const { body: module, status } = await kbnSupertest
+ .get(`/internal/ml/modules/get_module/${moduleId}`)
+ .set(getCommonRequestHeader('1'));
+ this.assertResponseStatusCode(200, status, module);
+ return module;
+ },
};
}
diff --git a/x-pack/test/functional/services/ml/test_resources.ts b/x-pack/test/functional/services/ml/test_resources.ts
index 8771db60eabb68..22f43a08f1e784 100644
--- a/x-pack/test/functional/services/ml/test_resources.ts
+++ b/x-pack/test/functional/services/ml/test_resources.ts
@@ -641,5 +641,11 @@ export function MachineLearningTestResourcesProvider(
async clearAdvancedSettingProperty(propertyName: string) {
await kibanaServer.uiSettings.unset(propertyName);
},
+
+ async assertModuleExists(moduleId: string) {
+ await retry.tryForTime(30 * 1000, async () => {
+ await mlApi.getModule(moduleId);
+ });
+ },
};
}
diff --git a/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts b/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts
index d4aef2d44856f3..f75e51851528ca 100644
--- a/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts
+++ b/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts
@@ -27,7 +27,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
);
});
- describe('bulk delete', () => {
+ // FLAKY: https://github.com/elastic/kibana/issues/163817
+ describe.skip('bulk delete', () => {
it('deletes multiple tags', async () => {
const initialDisplayedTags = await tagManagementPage.getDisplayedTagNames();
await tagManagementPage.selectTagByName('tag-1');
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_state.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_state.cy.ts
index 7d438791c3f1b4..06c934083cd882 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_state.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_state.cy.ts
@@ -33,7 +33,8 @@ const INITIAL_END_DATE = 'Jan 19, 2024 @ 20:33:29.186';
const DEFAULT_ESQL_QUERY =
'from .alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-* | limit 10';
-describe(
+// TODO: reuse or remove this tests when ESQL tab will be added
+describe.skip(
'Discover State',
{
env: { ftrConfig: { enableExperimental: ['discoverInTimeline'] } },
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_timeline_state_integration.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_timeline_state_integration.cy.ts
index 6e1289f9f8450e..71b268e090b56e 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_timeline_state_integration.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_timeline_state_integration.cy.ts
@@ -60,7 +60,8 @@ const TIMELINE_RESPONSE_SAVED_OBJECT_ID_PATH =
'response.body.data.persistTimeline.timeline.savedObjectId';
const esqlQuery = 'from auditbeat-* | where ecs.version == "8.0.0"';
-describe(
+// TODO: reuse or remove this tests when ESQL tab will be added
+describe.skip(
'Discover Timeline State Integration',
{
env: { ftrConfig: { enableExperimental: ['discoverInTimeline'] } },
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/search_filter.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/search_filter.cy.ts
index 42375aeee5e2ea..cfe3ca421de36e 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/search_filter.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/search_filter.cy.ts
@@ -34,7 +34,8 @@ const NEW_START_DATE = 'Jan 18, 2023 @ 20:33:29.186';
const esqlQuery = 'from auditbeat-* | where ecs.version == "8.0.0"';
// Failing: See https://github.com/elastic/kibana/issues/167186
-describe(
+// TODO: reuse or remove this tests when ESQL tab will be added
+describe.skip(
'Basic discover search and filter operations',
{
env: { ftrConfig: { enableExperimental: ['discoverInTimeline'] } },
diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts
index cfb133c17caf4d..441dc06cab8297 100644
--- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts
+++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts
@@ -35,8 +35,7 @@ export default function ({ getService }: FtrProviderContext) {
const esClient = getService('es');
const esDeleteAllIndices = getService('esDeleteAllIndices');
- // Failing: See https://github.com/elastic/kibana/issues/167665
- describe.skip('Alerting rules', () => {
+ describe('Alerting rules', () => {
const RULE_TYPE_ID = '.es-query';
const ALERT_ACTION_INDEX = 'alert-action-es-query';
let actionId: string;
@@ -53,6 +52,7 @@ export default function ({ getService }: FtrProviderContext) {
.set('x-elastic-internal-origin', 'foo');
await esClient.deleteByQuery({
index: '.kibana-event-log-*',
+ conflicts: 'proceed',
query: { term: { 'kibana.alert.rule.consumer': 'alerts' } },
});
await esDeleteAllIndices([ALERT_ACTION_INDEX]);
diff --git a/x-pack/test_serverless/functional/services/index.ts b/x-pack/test_serverless/functional/services/index.ts
index da2688b22b645a..125c93de2fcff5 100644
--- a/x-pack/test_serverless/functional/services/index.ts
+++ b/x-pack/test_serverless/functional/services/index.ts
@@ -13,6 +13,7 @@ import { SvlObltNavigationServiceProvider } from './svl_oblt_navigation';
import { SvlSearchNavigationServiceProvider } from './svl_search_navigation';
import { SvlSecNavigationServiceProvider } from './svl_sec_navigation';
import { SvlCommonScreenshotsProvider } from './svl_common_screenshots';
+import { SvlCasesServiceProvider } from '../../api_integration/services/svl_cases';
import { MachineLearningProvider } from './ml';
export const services = {
@@ -26,5 +27,6 @@ export const services = {
svlSearchNavigation: SvlSearchNavigationServiceProvider,
svlSecNavigation: SvlSecNavigationServiceProvider,
svlCommonScreenshots: SvlCommonScreenshotsProvider,
+ svlCases: SvlCasesServiceProvider,
svlMl: MachineLearningProvider,
};
diff --git a/x-pack/test_serverless/functional/test_suites/common/management/index_management/create_enrich_policy.ts b/x-pack/test_serverless/functional/test_suites/common/management/index_management/create_enrich_policy.ts
index f5b51c815e0fef..b3e248ea82de4a 100644
--- a/x-pack/test_serverless/functional/test_suites/common/management/index_management/create_enrich_policy.ts
+++ b/x-pack/test_serverless/functional/test_suites/common/management/index_management/create_enrich_policy.ts
@@ -20,6 +20,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
const POLICY_NAME = `policy-${Math.random()}`;
describe('Create enrich policy', function () {
+ // TimeoutError: Waiting for element to be located By(css selector, [data-test-subj="enrichPoliciesEmptyPromptCreateButton"])
+ this.tags(['failsOnMKI']);
before(async () => {
log.debug('Creating test index');
try {
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/attachment_framework.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/attachment_framework.ts
index bfd2b8700d7852..cafe2d867241da 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/attachment_framework.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/attachment_framework.ts
@@ -20,7 +20,9 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const cases = getService('cases');
const find = getService('find');
- describe('Cases persistable attachments', () => {
+ describe('Cases persistable attachments', function () {
+ // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
+ this.tags(['failsOnMKI']);
describe('lens visualization', () => {
before(async () => {
await svlCommonPage.login();
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/create_case_form.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/create_case_form.ts
index e89e2b799dc30b..e0166ac9de2dfa 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/create_case_form.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/create_case_form.ts
@@ -16,6 +16,8 @@ const owner = OBSERVABILITY_OWNER;
export default ({ getService, getPageObject }: FtrProviderContext) => {
describe('Create Case', function () {
+ // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
+ this.tags(['failsOnMKI']);
const find = getService('find');
const cases = getService('cases');
const testSubjects = getService('testSubjects');
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/list_view.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/list_view.ts
index 61cbd55f04b664..30fd021e5c6f50 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/list_view.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/list_view.ts
@@ -18,7 +18,9 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const svlCommonPage = getPageObject('svlCommonPage');
const svlObltNavigation = getService('svlObltNavigation');
- describe('Cases list', () => {
+ describe('Cases list', function () {
+ // multiple errors in after hook due to delete permission
+ this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
await svlObltNavigation.navigateToLandingPage();
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/view_case.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/view_case.ts
index 24234d4a40e528..ac0fb6fec9d5f5 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/view_case.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/view_case.ts
@@ -29,7 +29,9 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const svlCommonNavigation = getPageObject('svlCommonNavigation');
const svlCommonPage = getPageObject('svlCommonPage');
- describe('Case View', () => {
+ describe('Case View', function () {
+ // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
+ this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
});
diff --git a/x-pack/test_serverless/functional/test_suites/observability/config.screenshots.ts b/x-pack/test_serverless/functional/test_suites/observability/config.screenshots.ts
new file mode 100644
index 00000000000000..f9a06826cb7559
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/config.screenshots.ts
@@ -0,0 +1,21 @@
+/*
+ * 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 { createTestConfig } from '../../config.base';
+
+const enabledActionTypes = ['.index', '.server-log'];
+
+export default createTestConfig({
+ serverlessProject: 'oblt',
+ testFiles: [require.resolve('./screenshot_creation')],
+ kbnServerArgs: [`--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`],
+ junit: {
+ reportName: 'Serverless Observability Screenshot Creation',
+ },
+
+ esServerArgs: ['xpack.ml.ad.enabled=false', 'xpack.ml.dfa.enabled=false'],
+});
diff --git a/x-pack/test_serverless/functional/test_suites/observability/infra/index.ts b/x-pack/test_serverless/functional/test_suites/observability/infra/index.ts
index c7096467f71bcc..80eb2f7a32a53f 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/infra/index.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/infra/index.ts
@@ -9,6 +9,8 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('Observability Infra', function () {
+ // TimeoutError: Waiting for element to be located By(css selector, [data-test-subj="infrastructure-alerts-and-rules"])
+ this.tags(['failsOnMKI']);
loadTestFile(require.resolve('./header_menu'));
});
}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/ml/anomaly_detection_jobs_list.ts b/x-pack/test_serverless/functional/test_suites/observability/ml/anomaly_detection_jobs_list.ts
index 10f203889e1eaa..05382e03f78d06 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/ml/anomaly_detection_jobs_list.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/ml/anomaly_detection_jobs_list.ts
@@ -13,7 +13,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['svlCommonPage']);
const adJobId = 'fq_single_permission';
- describe('Anomaly detection jobs list', () => {
+ describe('Anomaly detection jobs list', function () {
+ // Error: Failed to delete all indices with pattern [.ml-*]
+ this.tags(['failsOnMKI']);
before(async () => {
await PageObjects.svlCommonPage.login();
diff --git a/x-pack/test_serverless/functional/test_suites/observability/ml/index.ts b/x-pack/test_serverless/functional/test_suites/observability/ml/index.ts
index e50d10b5ce6fdc..ba88fce593abf4 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/ml/index.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/ml/index.ts
@@ -9,6 +9,9 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('Observability ML', function () {
+ // Error: Failed to delete all indices with pattern [.ml-*]
+ // Error: First result should be Machine Learning (got matching items 'undefined')
+ this.tags(['failsOnMKI']);
loadTestFile(require.resolve('./anomaly_detection_jobs_list'));
loadTestFile(require.resolve('./search_bar_features'));
});
diff --git a/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/index.ts b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/index.ts
new file mode 100644
index 00000000000000..8710906ab6408f
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/index.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { FtrProviderContext } from '../../../ftr_provider_context';
+
+export default function ({ loadTestFile }: FtrProviderContext) {
+ describe('Screenshots - serverless observability UI', function () {
+ loadTestFile(require.resolve('./response_ops_docs'));
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/cases/index.ts b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/cases/index.ts
new file mode 100644
index 00000000000000..171215dfbf4d36
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/cases/index.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { FtrProviderContext } from '../../../../../ftr_provider_context';
+
+export default function ({ loadTestFile, getService }: FtrProviderContext) {
+ const browser = getService('browser');
+
+ describe('observability cases', function () {
+ before(async () => {
+ await browser.setWindowSize(1920, 1080);
+ });
+
+ loadTestFile(require.resolve('./list_view'));
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/cases/list_view.ts b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/cases/list_view.ts
new file mode 100644
index 00000000000000..cc1d8b5f789952
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/cases/list_view.ts
@@ -0,0 +1,63 @@
+/*
+ * 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 { OBSERVABILITY_OWNER } from '@kbn/cases-plugin/common';
+import { FtrProviderContext } from '../../../../../ftr_provider_context';
+import { navigateToCasesApp } from '../../../../../../shared/lib/cases';
+
+export default function ({ getPageObject, getPageObjects, getService }: FtrProviderContext) {
+ const pageObjects = getPageObjects(['common', 'header', 'svlCommonPage']);
+ const svlCases = getService('svlCases');
+ const svlCommonScreenshots = getService('svlCommonScreenshots');
+ const screenshotDirectories = ['response_ops_docs', 'observability_cases'];
+ const owner = OBSERVABILITY_OWNER;
+
+ describe('list view', function () {
+ before(async () => {
+ await svlCases.api.createCase(
+ svlCases.api.getPostCaseRequest(owner, {
+ title: 'Metrics inventory',
+ tags: ['IBM resilient'],
+ description: 'Test.',
+ owner,
+ })
+ );
+
+ await svlCases.api.createCase(
+ svlCases.api.getPostCaseRequest(owner, {
+ title: 'Logs threshold',
+ tags: ['jira'],
+ description: 'Test.',
+ owner,
+ })
+ );
+
+ await svlCases.api.createCase(
+ svlCases.api.getPostCaseRequest(owner, {
+ title: 'Monitor uptime',
+ tags: ['swimlane'],
+ description: 'Test.',
+ owner,
+ })
+ );
+ });
+
+ after(async () => {
+ await svlCases.api.deleteAllCaseItems();
+ await pageObjects.svlCommonPage.forceLogout();
+ });
+
+ beforeEach(async () => {
+ await pageObjects.svlCommonPage.login();
+ });
+
+ it('cases list screenshot', async () => {
+ await navigateToCasesApp(getPageObject, getService, owner);
+ await svlCommonScreenshots.takeScreenshot('cases', screenshotDirectories, 1700, 1024);
+ });
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/connectors/index.ts b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/connectors/index.ts
new file mode 100644
index 00000000000000..4e2f00e35e94d9
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/connectors/index.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { FtrProviderContext } from '../../../../../ftr_provider_context';
+
+export default function ({ loadTestFile, getService }: FtrProviderContext) {
+ const browser = getService('browser');
+
+ describe('observability connectors', function () {
+ before(async () => {
+ await browser.setWindowSize(1920, 1080);
+ });
+
+ loadTestFile(require.resolve('./server_log_connector'));
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/connectors/server_log_connector.ts b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/connectors/server_log_connector.ts
new file mode 100644
index 00000000000000..7eed7d5cbbd267
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/connectors/server_log_connector.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { FtrProviderContext } from '../../../../../ftr_provider_context';
+
+export default function ({ getService, getPageObjects }: FtrProviderContext) {
+ const svlCommonScreenshots = getService('svlCommonScreenshots');
+ const screenshotDirectories = ['response_ops_docs', 'observability_connectors'];
+ const pageObjects = getPageObjects(['common', 'header', 'svlCommonPage']);
+ const testSubjects = getService('testSubjects');
+
+ describe('server log connector', function () {
+ beforeEach(async () => {
+ await pageObjects.svlCommonPage.login();
+ });
+
+ after(async () => {
+ await pageObjects.svlCommonPage.forceLogout();
+ });
+
+ it('server log connector screenshots', async () => {
+ await pageObjects.common.navigateToApp('connectors');
+ await pageObjects.header.waitUntilLoadingHasFinished();
+ await testSubjects.click('createFirstActionButton');
+ await testSubjects.click(`.server-log-card`);
+ await testSubjects.setValue('nameInput', 'Server log test connector');
+ await svlCommonScreenshots.takeScreenshot('serverlog-connector', screenshotDirectories);
+ const saveTestButton = await testSubjects.find('create-connector-flyout-save-test-btn');
+ await saveTestButton.click();
+ await svlCommonScreenshots.takeScreenshot('serverlog-params-test', screenshotDirectories);
+ const flyOutCancelButton = await testSubjects.find('euiFlyoutCloseButton');
+ await flyOutCancelButton.click();
+ });
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/index.ts b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/index.ts
new file mode 100644
index 00000000000000..10b33b5fc944d1
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/index.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { FtrProviderContext } from '../../../../ftr_provider_context';
+
+export default function ({ getService, loadTestFile }: FtrProviderContext) {
+ const browser = getService('browser');
+ const ml = getService('ml');
+
+ describe('response ops docs', function () {
+ this.tags(['responseOps']);
+
+ before(async () => {
+ await ml.testResources.setKibanaTimeZoneToUTC();
+ await ml.testResources.disableKibanaAnnouncements();
+ await browser.setWindowSize(1920, 1080);
+ });
+
+ after(async () => {
+ await ml.testResources.resetKibanaTimeZone();
+ await ml.testResources.resetKibanaAnnouncements();
+ });
+
+ loadTestFile(require.resolve('./cases'));
+ loadTestFile(require.resolve('./connectors'));
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/search/config.screenshots.ts b/x-pack/test_serverless/functional/test_suites/search/config.screenshots.ts
index b0c951ef3295c8..3a8966904b5c9a 100644
--- a/x-pack/test_serverless/functional/test_suites/search/config.screenshots.ts
+++ b/x-pack/test_serverless/functional/test_suites/search/config.screenshots.ts
@@ -7,7 +7,7 @@
import { createTestConfig } from '../../config.base';
-const enabledActionTypes = ['.index', '.server-log'];
+const enabledActionTypes = ['.index'];
export default createTestConfig({
serverlessProject: 'es',
diff --git a/x-pack/test_serverless/functional/test_suites/search/empty_page.ts b/x-pack/test_serverless/functional/test_suites/search/empty_page.ts
index e3e82c0b0d97ba..0f3be7d382f905 100644
--- a/x-pack/test_serverless/functional/test_suites/search/empty_page.ts
+++ b/x-pack/test_serverless/functional/test_suites/search/empty_page.ts
@@ -14,6 +14,8 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
const svlCommonPage = getPageObject('svlCommonPage');
describe('empty pages', function () {
+ // Error: expected testSubject(kbnOverviewElasticsearchGettingStarted) to exist
+ this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
await svlSearchNavigation.navigateToLandingPage();
diff --git a/x-pack/test_serverless/functional/test_suites/search/screenshot_creation/response_ops_docs/stack_connectors/index.ts b/x-pack/test_serverless/functional/test_suites/search/screenshot_creation/response_ops_docs/stack_connectors/index.ts
index f5f06e36d3ca70..e5d251e6911833 100644
--- a/x-pack/test_serverless/functional/test_suites/search/screenshot_creation/response_ops_docs/stack_connectors/index.ts
+++ b/x-pack/test_serverless/functional/test_suites/search/screenshot_creation/response_ops_docs/stack_connectors/index.ts
@@ -18,14 +18,6 @@ export default function ({ loadTestFile, getService }: FtrProviderContext) {
describe('stack connectors', function () {
before(async () => {
await browser.setWindowSize(1920, 1080);
- await actions.api.createConnector({
- name: 'server-log-connector',
- config: {},
- secrets: {},
- connectorTypeId: '.server-log',
- additionalRequestHeaders: svlCommonApi.getInternalRequestHeader(),
- });
-
await es.indices.create({
index: testIndex,
body: {
@@ -58,6 +50,5 @@ export default function ({ loadTestFile, getService }: FtrProviderContext) {
});
loadTestFile(require.resolve('./connectors'));
- // loadTestFile(require.resolve('./connector_types'));
});
}
diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts
index ca9e9ab6219e70..6eb306d43ad004 100644
--- a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts
+++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts
@@ -18,6 +18,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const retry = getService('retry');
describe('Configure Case', function () {
+ // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
+ this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
@@ -34,6 +36,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
describe('Closure options', function () {
+ // Error: Expected the radio group value to equal "close-by-pushing" (got "close-by-user")
+ this.tags(['failsOnMKI']);
it('defaults the closure option correctly', async () => {
await cases.common.assertRadioGroupValue('closure-options-radio-group', 'close-by-user');
});
diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/list_view.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/list_view.ts
index e3d1d8408beffe..045d56b9c5dd8a 100644
--- a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/list_view.ts
+++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/list_view.ts
@@ -17,7 +17,9 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const svlSecNavigation = getService('svlSecNavigation');
const svlCommonPage = getPageObject('svlCommonPage');
- describe('Cases List', () => {
+ describe('Cases List', function () {
+ // multiple errors in after hook due to delete permission
+ this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
@@ -47,6 +49,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
describe('bulk actions', () => {
+ // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
+ // action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
describe('delete', () => {
createNCasesBeforeDeleteAllAfter(8, getPageObject, getService);
@@ -148,6 +152,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
describe('severity filtering', () => {
+ // Error: retry.tryForTime timeout: Error: expected 10 to equal 5
before(async () => {
await testSubjects.click('solutionSideNavItemLink-cases');
@@ -196,6 +201,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
describe('pagination', () => {
+ // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
createNCasesBeforeDeleteAllAfter(12, getPageObject, getService);
it('paginates cases correctly', async () => {
diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/view_case.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/view_case.ts
index 4429c74040eafe..5ad615a4f97a9f 100644
--- a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/view_case.ts
+++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/view_case.ts
@@ -29,8 +29,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const svlCommonNavigation = getPageObject('svlCommonNavigation');
const svlCommonPage = getPageObject('svlCommonPage');
- // FLAKY: https://github.com/elastic/kibana/issues/166447
- describe.skip('Case View', () => {
+ describe('Case View', () => {
before(async () => {
await svlCommonPage.login();
});