Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RAC] Get o11y alerts in alerts table #109346

Merged
merged 9 commits into from
Aug 20, 2021
5 changes: 1 addition & 4 deletions x-pack/plugins/cases/public/components/case_view/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ export const CaseComponent = React.memo<CaseComponentProps>(
const [initLoadingData, setInitLoadingData] = useState(true);
const init = useRef(true);
const timelineUi = useTimelineContext()?.ui;
const alertConsumers = useTimelineContext()?.alertConsumers;

const {
caseUserActions,
Expand Down Expand Up @@ -487,9 +486,7 @@ export const CaseComponent = React.memo<CaseComponentProps>(
</EuiFlexGroup>
</ContentWrapper>
</WhitePageWrapper>
{timelineUi?.renderTimelineDetailsPanel
? timelineUi.renderTimelineDetailsPanel({ alertConsumers })
: null}
{timelineUi?.renderTimelineDetailsPanel ? timelineUi.renderTimelineDetailsPanel() : null}
</>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import React, { useState } from 'react';
import { EuiMarkdownEditorUiPlugin, EuiMarkdownAstNodePosition } from '@elastic/eui';
import { AlertConsumers } from '@kbn/rule-data-utils';
import { Plugin } from 'unified';
/**
* @description - manage the plugins, hooks, and ui components needed to enable timeline functionality within the cases plugin
Expand All @@ -29,7 +28,6 @@ interface TimelineProcessingPluginRendererProps {
}

export interface CasesTimelineIntegration {
alertConsumers?: AlertConsumers[];
editor_plugins: {
parsingPlugin: Plugin;
processingPluginRenderer: React.FC<
Expand All @@ -45,11 +43,7 @@ export interface CasesTimelineIntegration {
};
ui?: {
renderInvestigateInTimelineActionComponent?: (alertIds: string[]) => JSX.Element;
renderTimelineDetailsPanel?: ({
alertConsumers,
}: {
alertConsumers?: AlertConsumers[];
}) => JSX.Element;
renderTimelineDetailsPanel?: () => JSX.Element;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@
* 2.0.
*/

import { IndexPatternBase } from '@kbn/es-query';
import { i18n } from '@kbn/i18n';
import React, { useMemo, useState } from 'react';
import { IIndexPattern, SearchBar, TimeHistory } from '../../../../../../src/plugins/data/public';
import { SearchBar, TimeHistory } from '../../../../../../src/plugins/data/public';
import { Storage } from '../../../../../../src/plugins/kibana_utils/public';

export function AlertsSearchBar({
dynamicIndexPattern,
dynamicIndexPatterns,
rangeFrom,
rangeTo,
onQueryChange,
query,
}: {
dynamicIndexPattern: IIndexPattern[];
dynamicIndexPatterns: IndexPatternBase[];
rangeFrom?: string;
rangeTo?: string;
query?: string;
Expand All @@ -31,9 +32,19 @@ export function AlertsSearchBar({
}, []);
const [queryLanguage, setQueryLanguage] = useState<'lucene' | 'kuery'>('kuery');

const compatibleIndexPatterns = useMemo(
() =>
dynamicIndexPatterns.map((dynamicIndexPattern) => ({
title: dynamicIndexPattern.title ?? '',
id: dynamicIndexPattern.id ?? '',
fields: dynamicIndexPattern.fields,
})),
[dynamicIndexPatterns]
);

return (
<SearchBar
indexPatterns={dynamicIndexPattern}
indexPatterns={compatibleIndexPatterns}
placeholder={i18n.translate('xpack.observability.alerts.searchBarPlaceholder', {
defaultMessage: 'kibana.alert.evaluation.threshold > 75',
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
* This way plugins can do targeted imports to reduce the final code bundle
*/
import {
AlertConsumers as AlertConsumersTyped,
ALERT_DURATION as ALERT_DURATION_TYPED,
ALERT_REASON as ALERT_REASON_TYPED,
ALERT_RULE_CONSUMER,
Expand Down Expand Up @@ -62,14 +61,13 @@ import { LazyAlertsFlyout } from '../..';
import { parseAlert } from './parse_alert';
import { CoreStart } from '../../../../../../src/core/public';

const AlertConsumers: typeof AlertConsumersTyped = AlertConsumersNonTyped;
const ALERT_DURATION: typeof ALERT_DURATION_TYPED = ALERT_DURATION_NON_TYPED;
const ALERT_REASON: typeof ALERT_REASON_TYPED = ALERT_REASON_NON_TYPED;
const ALERT_STATUS: typeof ALERT_STATUS_TYPED = ALERT_STATUS_NON_TYPED;
const ALERT_WORKFLOW_STATUS: typeof ALERT_WORKFLOW_STATUS_TYPED = ALERT_WORKFLOW_STATUS_NON_TYPED;

interface AlertsTableTGridProps {
indexName: string;
indexNames: string[];
rangeFrom: string;
rangeTo: string;
kuery: string;
Expand Down Expand Up @@ -147,13 +145,6 @@ const NO_ROW_RENDER: RowRenderer[] = [];

const trailingControlColumns: never[] = [];

const OBSERVABILITY_ALERT_CONSUMERS = [
AlertConsumers.APM,
AlertConsumers.LOGS,
AlertConsumers.INFRASTRUCTURE,
AlertConsumers.UPTIME,
];

function ObservabilityActions({
data,
eventId,
Expand Down Expand Up @@ -290,7 +281,7 @@ function ObservabilityActions({
}

export function AlertsTableTGrid(props: AlertsTableTGridProps) {
const { indexName, rangeFrom, rangeTo, kuery, workflowStatus, setRefetch, addToQuery } = props;
const { indexNames, rangeFrom, rangeTo, kuery, workflowStatus, setRefetch, addToQuery } = props;
const { timelines } = useKibana<{ timelines: TimelinesUIStart }>().services;

const [flyoutAlert, setFlyoutAlert] = useState<TopAlert | undefined>(undefined);
Expand Down Expand Up @@ -328,7 +319,6 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
const type: TGridType = 'standalone';
const sortDirection: SortDirection = 'desc';
return {
alertConsumers: OBSERVABILITY_ALERT_CONSUMERS,
appId: observabilityFeatureId,
casePermissions,
type,
Expand All @@ -337,7 +327,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
defaultCellActions: getDefaultCellActions({ addToQuery }),
end: rangeTo,
filters: [],
indexNames: [indexName],
indexNames,
itemsPerPage: 10,
itemsPerPageOptions: [10, 25, 50],
loadingText: i18n.translate('xpack.observability.alertsTable.loadingTextLabel', {
Expand Down Expand Up @@ -372,7 +362,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
};
}, [
casePermissions,
indexName,
indexNames,
kuery,
leadingControlColumns,
rangeFrom,
Expand Down
47 changes: 38 additions & 9 deletions x-pack/plugins/observability/public/pages/alerts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

import { EuiButtonEmpty, EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useCallback, useMemo, useRef } from 'react';
import React, { useCallback, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import useAsync from 'react-use/lib/useAsync';
import { IndexPatternBase } from '@kbn/es-query';
import { ParsedTechnicalFields } from '../../../../rule_registry/common/parse_technical_fields';
import type { AlertWorkflowStatus } from '../../../common/typings';
import { ExperimentalBadge } from '../../components/shared/experimental_badge';
Expand All @@ -35,7 +37,7 @@ interface AlertsPageProps {
}

export function AlertsPage({ routeParams }: AlertsPageProps) {
const { core, ObservabilityPageTemplate } = usePluginContext();
const { core, plugins, ObservabilityPageTemplate } = usePluginContext();
const { prepend } = core.http.basePath;
const history = useHistory();
const refetch = useRef<() => void>();
Expand All @@ -60,17 +62,41 @@ export function AlertsPage({ routeParams }: AlertsPageProps) {
// observability. For now link to the settings page.
const manageRulesHref = prepend('/app/management/insightsAndAlerting/triggersActions/alerts');

const { data: dynamicIndexPatternResp } = useFetcher(({ signal }) => {
const { data: indexNames = NO_INDEX_NAMES } = useFetcher(({ signal }) => {
return callObservabilityApi({
signal,
endpoint: 'GET /api/observability/rules/alerts/dynamic_index_pattern',
params: {
query: {
namespace: 'default',
Copy link
Contributor

@dhurley14 dhurley14 Aug 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weltenwort Is there a way to get the space id from the UI? This may not always be default, no?

We were pulling it off of the alerting framework rulesClient in the rules/alerts/dynamic_index_pattern route so we could ensure we acquired the correct space id on the request.. If observability doesn't plan on segmenting by space id I could understand why this would be hard coded to default then. Just looking for clarification here..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentionally fixed as default, because in observability we're not using the space id in the namespace. The filtering by space happens as part of the RBAC filter if I saw that correctly.

registrationContexts: [
'observability.apm',
'observability.logs',
'observability.infrastructure',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

observability.infrastructure registration context doesn't exist in the code. What plugin is supposed to be defining it?

Copy link
Member

@weltenwort weltenwort Aug 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be observability.metrics. The app is called infrastructure for historical reasons, but not the registration context.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO in the follow-up PR: remove observability.infrastructure and make sure observability.metrics is there.

'observability.metrics',
'observability.uptime',
],
},
},
});
}, []);

const dynamicIndexPattern = useMemo(
() => (dynamicIndexPatternResp ? [dynamicIndexPatternResp] : []),
[dynamicIndexPatternResp]
);
const dynamicIndexPatternsAsyncState = useAsync(async (): Promise<IndexPatternBase[]> => {
if (indexNames.length === 0) {
return [];
}

return [
{
id: 'dynamic-observability-alerts-table-index-pattern',
title: indexNames.join(','),
fields: await plugins.data.indexPatterns.getFieldsForWildcard({
pattern: indexNames.join(','),
allowNoIndex: true,
}),
},
];
}, [indexNames]);

const setWorkflowStatusFilter = useCallback(
(value: AlertWorkflowStatus) => {
Expand Down Expand Up @@ -165,7 +191,7 @@ export function AlertsPage({ routeParams }: AlertsPageProps) {
</EuiFlexItem>
<EuiFlexItem>
<AlertsSearchBar
dynamicIndexPattern={dynamicIndexPattern}
dynamicIndexPatterns={dynamicIndexPatternsAsyncState.value ?? NO_INDEX_PATTERNS}
rangeFrom={rangeFrom}
rangeTo={rangeTo}
query={kuery}
Expand All @@ -183,7 +209,7 @@ export function AlertsPage({ routeParams }: AlertsPageProps) {

<EuiFlexItem>
<AlertsTableTGrid
indexName={dynamicIndexPattern.length > 0 ? dynamicIndexPattern[0].title : ''}
indexNames={indexNames}
rangeFrom={rangeFrom}
rangeTo={rangeTo}
kuery={kuery}
Expand All @@ -196,3 +222,6 @@ export function AlertsPage({ routeParams }: AlertsPageProps) {
</ObservabilityPageTemplate>
);
}

const NO_INDEX_NAMES: string[] = [];
const NO_INDEX_PATTERNS: IndexPatternBase[] = [];
14 changes: 2 additions & 12 deletions x-pack/plugins/observability/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
ScopedAnnotationsClientFactory,
AnnotationsAPI,
} from './lib/annotations/bootstrap_annotations';
import { Dataset, RuleRegistryPluginSetupContract } from '../../rule_registry/server';
import { RuleRegistryPluginSetupContract } from '../../rule_registry/server';
import { PluginSetupContract as FeaturesSetup } from '../../features/server';
import { uiSettings } from './ui_settings';
import { registerRoutes } from './routes/register_routes';
Expand Down Expand Up @@ -101,16 +101,6 @@ export class ObservabilityPlugin implements Plugin<ObservabilityPluginSetup> {
const start = () => core.getStartServices().then(([coreStart]) => coreStart);

const { ruleDataService } = plugins.ruleRegistry;
const ruleDataClient = ruleDataService.initializeIndex({
feature: 'observability',
registrationContext: 'observability',
dataset: Dataset.alerts,
componentTemplateRefs: [],
componentTemplates: [],
indexTemplate: {
version: 0,
},
});
banderror marked this conversation as resolved.
Show resolved Hide resolved

registerRoutes({
core: {
Expand All @@ -119,7 +109,7 @@ export class ObservabilityPlugin implements Plugin<ObservabilityPluginSetup> {
},
logger: this.initContext.logger.get(),
repository: getGlobalObservabilityServerRouteRepository(),
ruleDataClient,
ruleDataService,
});

return {
Expand Down
8 changes: 4 additions & 4 deletions x-pack/plugins/observability/server/routes/register_routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,23 @@ import {
import { CoreSetup, CoreStart, Logger, RouteRegistrar } from 'kibana/server';
import Boom from '@hapi/boom';
import { RequestAbortedError } from '@elastic/elasticsearch/lib/errors';
import { IRuleDataClient } from '../../../rule_registry/server';
import { RuleDataPluginService } from '../../../rule_registry/server';
import { ObservabilityRequestHandlerContext } from '../types';
import { AbstractObservabilityServerRouteRepository } from './types';

export function registerRoutes({
repository,
core,
logger,
ruleDataClient,
ruleDataService,
}: {
core: {
setup: CoreSetup;
start: () => Promise<CoreStart>;
};
repository: AbstractObservabilityServerRouteRepository;
logger: Logger;
ruleDataClient: IRuleDataClient;
ruleDataService: RuleDataPluginService;
}) {
const routes = repository.getRoutes();

Expand Down Expand Up @@ -62,7 +62,7 @@ export function registerRoutes({
core,
logger,
params: decodedParams,
ruleDataClient,
ruleDataService,
})) as any;

return response.ok({ body: data });
Expand Down
25 changes: 21 additions & 4 deletions x-pack/plugins/observability/server/routes/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { observabilityFeatureId } from '../../common';
import * as t from 'io-ts';
import { createObservabilityServerRoute } from './create_observability_server_route';
import { createObservabilityServerRouteRepository } from './create_observability_server_route_repository';

Expand All @@ -14,10 +14,27 @@ const alertsDynamicIndexPatternRoute = createObservabilityServerRoute({
options: {
tags: [],
},
handler: async ({ ruleDataClient }) => {
const reader = ruleDataClient.getReader({ namespace: observabilityFeatureId });
params: t.type({
query: t.type({
registrationContexts: t.array(t.string),
namespace: t.string,
banderror marked this conversation as resolved.
Show resolved Hide resolved
}),
weltenwort marked this conversation as resolved.
Show resolved Hide resolved
}),
handler: async ({ ruleDataService, params }) => {
const { namespace, registrationContexts } = params.query;
const indexNames = registrationContexts.flatMap((registrationContext) => {
const indexName = ruleDataService
.getRegisteredIndexInfo(registrationContext)
?.getPrimaryAlias(namespace);

return reader.getDynamicIndexPattern();
if (indexName != null) {
return [indexName];
} else {
return [];
}
});

return indexNames;
},
});

Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/observability/server/routes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {
ServerRouteRepository,
} from '@kbn/server-route-repository';
import { CoreSetup, CoreStart, KibanaRequest, Logger } from 'kibana/server';
import { IRuleDataClient } from '../../../rule_registry/server';
import { RuleDataPluginService } from '../../../rule_registry/server';

import { ObservabilityServerRouteRepository } from './get_global_observability_server_route_repository';
import { ObservabilityRequestHandlerContext } from '../types';
Expand All @@ -24,7 +24,7 @@ export interface ObservabilityRouteHandlerResources {
start: () => Promise<CoreStart>;
setup: CoreSetup;
};
ruleDataClient: IRuleDataClient;
ruleDataService: RuleDataPluginService;
request: KibanaRequest;
context: ObservabilityRequestHandlerContext;
logger: Logger;
Expand Down
Loading