mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[Security Solution] [Endpoint] Show es_connection error in fleet agent details (#140167)
* Shows es_connection error in fleet agent details. Include new extension point for generic package errors list * Use filter instead of for loop * Updates wrong description comment * Rename variable and use a new one to improve readability * Updates data-test-subj names * Update x-pack/plugins/security_solution/public/management/components/package_action_item/package_action_formatter.ts Co-authored-by: Joe Peeples <joe.peeples@elastic.co> * Remove useMemo and fix typo in test-subj name * Remove temporary url in order to remove UI link until we get the final troubleshooting url * Update x-pack/plugins/security_solution/public/management/components/package_action_item/package_action_formatter.ts Co-authored-by: Joe Peeples <joe.peeples@elastic.co> * Uses filter instead of for loop to get the agent components Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Joe Peeples <joe.peeples@elastic.co>
This commit is contained in:
parent
e490ca9e61
commit
f821e6e2de
12 changed files with 301 additions and 3 deletions
|
@ -360,6 +360,10 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => {
|
|||
macos_system_ext: `${SECURITY_SOLUTION_DOCS}deploy-elastic-endpoint.html#system-extension-endpoint`,
|
||||
linux_deadlock: `${SECURITY_SOLUTION_DOCS}ts-management.html#linux-deadlock`,
|
||||
},
|
||||
packageActionTroubleshooting: {
|
||||
// TODO: Pending to be updated when docs are ready
|
||||
es_connection: '',
|
||||
},
|
||||
responseActions: `${SECURITY_SOLUTION_DOCS}response-actions.html`,
|
||||
},
|
||||
query: {
|
||||
|
|
|
@ -264,6 +264,9 @@ export interface DocLinks {
|
|||
macos_system_ext: string;
|
||||
linux_deadlock: string;
|
||||
};
|
||||
readonly packageActionTroubleshooting: {
|
||||
es_connection: string;
|
||||
};
|
||||
readonly threatIntelInt: string;
|
||||
readonly responseActions: string;
|
||||
};
|
||||
|
|
|
@ -81,6 +81,7 @@ interface AgentBase {
|
|||
user_provided_metadata: AgentMetadata;
|
||||
local_metadata: AgentMetadata;
|
||||
tags?: string[];
|
||||
components?: FleetServerAgentComponent[];
|
||||
}
|
||||
|
||||
export interface Agent extends AgentBase {
|
||||
|
@ -121,7 +122,7 @@ export interface ActionStatus {
|
|||
}
|
||||
|
||||
// Generated from FleetServer schema.json
|
||||
interface FleetServerAgentComponentUnit {
|
||||
export interface FleetServerAgentComponentUnit {
|
||||
id: string;
|
||||
type: 'input' | 'output';
|
||||
status: FleetServerAgentComponentStatus;
|
||||
|
|
|
@ -44,6 +44,12 @@ export class Plugin {
|
|||
view: 'package-policy-response',
|
||||
Component: getLazyEndpointPolicyResponseExtension(core, plugins),
|
||||
});
|
||||
|
||||
registerExtension({
|
||||
package: 'endpoint',
|
||||
view: 'package-generic-errors-list',
|
||||
Component: getLazyEndpointGenericErrorsListExtension(core, plugins),
|
||||
});
|
||||
}
|
||||
//...
|
||||
}
|
||||
|
|
|
@ -20,11 +20,13 @@ import {
|
|||
EuiBadge,
|
||||
useEuiTheme,
|
||||
} from '@elastic/eui';
|
||||
import { filter } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import type { Agent, AgentPolicy, PackagePolicy } from '../../../../../types';
|
||||
import type { FleetServerAgentComponentUnit } from '../../../../../../../../common/types/models/agent';
|
||||
import { useLink, useUIExtension } from '../../../../../hooks';
|
||||
import { ExtensionWrapper, PackageIcon } from '../../../../../components';
|
||||
|
||||
|
@ -94,11 +96,16 @@ export const AgentDetailsIntegration: React.FunctionComponent<{
|
|||
const { getHref } = useLink();
|
||||
const theme = useEuiTheme();
|
||||
|
||||
const [showNeedsAttentionBadge, setShowNeedsAttentionBadge] = useState(false);
|
||||
const [isAttentionBadgeNeededForPolicyResponse, setIsAttentionBadgeNeededForPolicyResponse] =
|
||||
useState(false);
|
||||
const extensionView = useUIExtension(
|
||||
packagePolicy.package?.name ?? '',
|
||||
'package-policy-response'
|
||||
);
|
||||
const genericErrorsListExtensionView = useUIExtension(
|
||||
packagePolicy.package?.name ?? '',
|
||||
'package-generic-errors-list'
|
||||
);
|
||||
|
||||
const policyResponseExtensionView = useMemo(() => {
|
||||
return (
|
||||
|
@ -106,13 +113,41 @@ export const AgentDetailsIntegration: React.FunctionComponent<{
|
|||
<ExtensionWrapper>
|
||||
<extensionView.Component
|
||||
agent={agent}
|
||||
onShowNeedsAttentionBadge={setShowNeedsAttentionBadge}
|
||||
onShowNeedsAttentionBadge={setIsAttentionBadgeNeededForPolicyResponse}
|
||||
/>
|
||||
</ExtensionWrapper>
|
||||
)
|
||||
);
|
||||
}, [agent, extensionView]);
|
||||
|
||||
const packageErrors = useMemo(() => {
|
||||
const packageErrorUnits: FleetServerAgentComponentUnit[] = [];
|
||||
if (!agent.components) {
|
||||
return packageErrorUnits;
|
||||
}
|
||||
|
||||
const filteredPackageComponents = filter(agent.components, {
|
||||
type: packagePolicy.package?.name,
|
||||
});
|
||||
|
||||
filteredPackageComponents.forEach((component) => {
|
||||
packageErrorUnits.push(...filter(component.units, { status: 'failed' }));
|
||||
});
|
||||
return packageErrorUnits;
|
||||
}, [agent.components, packagePolicy]);
|
||||
|
||||
const showNeedsAttentionBadge = isAttentionBadgeNeededForPolicyResponse || packageErrors.length;
|
||||
|
||||
const genericErrorsListExtensionViewWrapper = useMemo(() => {
|
||||
return (
|
||||
genericErrorsListExtensionView && (
|
||||
<ExtensionWrapper>
|
||||
<genericErrorsListExtensionView.Component packageErrors={packageErrors} />
|
||||
</ExtensionWrapper>
|
||||
)
|
||||
);
|
||||
}, [packageErrors, genericErrorsListExtensionView]);
|
||||
|
||||
const inputItems = [
|
||||
{
|
||||
label: (
|
||||
|
@ -217,6 +252,7 @@ export const AgentDetailsIntegration: React.FunctionComponent<{
|
|||
aria-labelledby="inputsTreeView"
|
||||
/>
|
||||
{policyResponseExtensionView}
|
||||
{genericErrorsListExtensionViewWrapper}
|
||||
<EuiSpacer />
|
||||
</CollapsablePanel>
|
||||
);
|
||||
|
|
|
@ -42,6 +42,8 @@ export type {
|
|||
PackagePolicyResponseExtension,
|
||||
PackagePolicyResponseExtensionComponent,
|
||||
PackagePolicyResponseExtensionComponentProps,
|
||||
PackageGenericErrorsListProps,
|
||||
PackageGenericErrorsListComponent,
|
||||
UIExtensionPoint,
|
||||
UIExtensionRegistrationCallback,
|
||||
UIExtensionsStorage,
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
import type { EuiStepProps } from '@elastic/eui';
|
||||
import type { ComponentType, LazyExoticComponent } from 'react';
|
||||
|
||||
import type { FleetServerAgentComponentUnit } from '../../common/types/models/agent';
|
||||
|
||||
import type { Agent, NewPackagePolicy, PackageInfo, PackagePolicy } from '.';
|
||||
|
||||
/** Register a Fleet UI extension */
|
||||
|
@ -60,6 +62,17 @@ export interface PackagePolicyResponseExtensionComponentProps {
|
|||
onShowNeedsAttentionBadge?: (val: boolean) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* UI Component Extension is used on the pages displaying the ability to see
|
||||
* a generic endpoint errors list
|
||||
*/
|
||||
export type PackageGenericErrorsListComponent = ComponentType<PackageGenericErrorsListProps>;
|
||||
|
||||
export interface PackageGenericErrorsListProps {
|
||||
/** A list of errors from a package */
|
||||
packageErrors: FleetServerAgentComponentUnit[];
|
||||
}
|
||||
|
||||
/** Extension point registration contract for Integration Policy Edit views */
|
||||
export interface PackagePolicyEditExtension {
|
||||
package: string;
|
||||
|
@ -74,6 +87,12 @@ export interface PackagePolicyResponseExtension {
|
|||
Component: LazyExoticComponent<PackagePolicyResponseExtensionComponent>;
|
||||
}
|
||||
|
||||
export interface PackageGenericErrorsListExtension {
|
||||
package: string;
|
||||
view: 'package-generic-errors-list';
|
||||
Component: LazyExoticComponent<PackageGenericErrorsListComponent>;
|
||||
}
|
||||
|
||||
/** Extension point registration contract for Integration Policy Edit tabs views */
|
||||
export interface PackagePolicyEditTabsExtension {
|
||||
package: string;
|
||||
|
@ -158,4 +177,5 @@ export type UIExtensionPoint =
|
|||
| PackageCustomExtension
|
||||
| PackagePolicyCreateExtension
|
||||
| PackageAssetsExtension
|
||||
| PackageGenericErrorsListExtension
|
||||
| AgentEnrollmentFlyoutFinalStepExtension;
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { DocLinks } from '@kbn/doc-links';
|
||||
|
||||
type PackageActions = 'es_connection';
|
||||
|
||||
export const titles = Object.freeze(
|
||||
new Map<PackageActions, string>([
|
||||
[
|
||||
'es_connection',
|
||||
i18n.translate('xpack.securitySolution.endpoint.details.packageActions.es_connection.title', {
|
||||
defaultMessage: 'Elasticsearch connection failure',
|
||||
}),
|
||||
],
|
||||
])
|
||||
);
|
||||
|
||||
export const descriptions = Object.freeze(
|
||||
new Map<Partial<PackageActions> | string, string>([
|
||||
[
|
||||
'es_connection',
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.endpoint.details.packageActions.es_connection.description',
|
||||
{
|
||||
defaultMessage:
|
||||
"The endpoint's connection to Elasticsearch is either down or misconfigured. Make sure it is configured correctly.",
|
||||
}
|
||||
),
|
||||
],
|
||||
])
|
||||
);
|
||||
|
||||
const linkTexts = Object.freeze(
|
||||
new Map<Partial<PackageActions> | string, string>([
|
||||
[
|
||||
'es_connection',
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.endpoint.details.packageActions.link.text.es_connection',
|
||||
{
|
||||
defaultMessage: ' Read more.',
|
||||
}
|
||||
),
|
||||
],
|
||||
])
|
||||
);
|
||||
|
||||
export class PackageActionFormatter {
|
||||
public key: PackageActions;
|
||||
public title: string;
|
||||
public description: string;
|
||||
public linkText?: string;
|
||||
|
||||
constructor(
|
||||
code: number,
|
||||
message: string,
|
||||
private docLinks: DocLinks['securitySolution']['packageActionTroubleshooting']
|
||||
) {
|
||||
this.key = this.getKeyFromErrorCode(code);
|
||||
this.title = titles.get(this.key) ?? this.key;
|
||||
this.description = descriptions.get(this.key) || message;
|
||||
this.linkText = linkTexts.get(this.key);
|
||||
}
|
||||
|
||||
public get linkUrl(): string {
|
||||
return this.docLinks[this.key];
|
||||
}
|
||||
|
||||
private getKeyFromErrorCode(code: number): PackageActions {
|
||||
if (code === 123) {
|
||||
return 'es_connection';
|
||||
} else {
|
||||
throw new Error(`Invalid error code ${code}`);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { EuiLink, EuiCallOut, EuiText } from '@elastic/eui';
|
||||
import type { PackageActionFormatter } from './package_action_formatter';
|
||||
|
||||
const StyledEuiCallout = styled(EuiCallOut)`
|
||||
padding: ${({ theme }) => theme.eui.euiSizeS};
|
||||
`;
|
||||
|
||||
const StyledEuiText = styled(EuiText)`
|
||||
white-space: break-spaces;
|
||||
text-align: left;
|
||||
line-height: inherit;
|
||||
`;
|
||||
|
||||
interface PackageActionItemErrorProps {
|
||||
actionFormatter: PackageActionFormatter;
|
||||
}
|
||||
/**
|
||||
* A package action item error
|
||||
*/
|
||||
export const PackageActionItemError = memo(({ actionFormatter }: PackageActionItemErrorProps) => {
|
||||
return (
|
||||
<StyledEuiCallout
|
||||
title={actionFormatter.title}
|
||||
color="danger"
|
||||
iconType="alert"
|
||||
data-test-subj="packageItemErrorCallOut"
|
||||
>
|
||||
<StyledEuiText size="s" data-test-subj="packageItemErrorCallOutMessage">
|
||||
{actionFormatter.description}
|
||||
{actionFormatter.linkText && actionFormatter.linkUrl && (
|
||||
<EuiLink
|
||||
target="_blank"
|
||||
href={actionFormatter.linkUrl}
|
||||
data-test-subj="packageItemErrorCallOutLink"
|
||||
>
|
||||
{actionFormatter.linkText}
|
||||
</EuiLink>
|
||||
)}
|
||||
</StyledEuiText>
|
||||
</StyledEuiCallout>
|
||||
);
|
||||
});
|
||||
|
||||
PackageActionItemError.displayName = 'PackageActionItemError';
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import type { PackageGenericErrorsListProps } from '@kbn/fleet-plugin/public';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
|
||||
import { useKibana } from '../../../../../common/lib/kibana';
|
||||
import { PackageActionFormatter } from '../../../../components/package_action_item/package_action_formatter';
|
||||
import { PackageActionItemError } from '../../../../components/package_action_item/package_action_item_error';
|
||||
|
||||
/**
|
||||
* Exports Endpoint-generic errors list
|
||||
*/
|
||||
export const EndpointGenericErrorsList = memo<PackageGenericErrorsListProps>(
|
||||
({ packageErrors }) => {
|
||||
const { docLinks } = useKibana().services;
|
||||
|
||||
const globalEndpointErrors = useMemo(() => {
|
||||
const errors: PackageActionFormatter[] = [];
|
||||
packageErrors.forEach((unit) => {
|
||||
if (unit.payload && unit.payload.error) {
|
||||
errors.push(
|
||||
new PackageActionFormatter(
|
||||
unit.payload.error.code,
|
||||
unit.payload.error.message,
|
||||
docLinks.links.securitySolution.packageActionTroubleshooting
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return errors;
|
||||
}, [packageErrors, docLinks.links.securitySolution.packageActionTroubleshooting]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{globalEndpointErrors.map((error) => (
|
||||
<React.Fragment key={error.key}>
|
||||
<PackageActionItemError actionFormatter={error} />
|
||||
<EuiSpacer size="m" />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
EndpointGenericErrorsList.displayName = 'EndpointGenericErrorsList';
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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 { lazy } from 'react';
|
||||
import type { CoreStart } from '@kbn/core/public';
|
||||
import type {
|
||||
PackageGenericErrorsListComponent,
|
||||
PackageGenericErrorsListProps,
|
||||
} from '@kbn/fleet-plugin/public';
|
||||
import type { StartPlugins } from '../../../../../types';
|
||||
|
||||
export const getLazyEndpointGenericErrorsListExtension = (
|
||||
coreStart: CoreStart,
|
||||
depsStart: Pick<StartPlugins, 'data' | 'fleet'>
|
||||
) => {
|
||||
return lazy<PackageGenericErrorsListComponent>(async () => {
|
||||
const [{ withSecurityContext }, { EndpointGenericErrorsList }] = await Promise.all([
|
||||
import('./with_security_context/with_security_context'),
|
||||
import('./endpoint_generic_errors_list'),
|
||||
]);
|
||||
|
||||
return {
|
||||
default: withSecurityContext<PackageGenericErrorsListProps>({
|
||||
coreStart,
|
||||
depsStart,
|
||||
WrappedComponent: EndpointGenericErrorsList,
|
||||
}),
|
||||
};
|
||||
});
|
||||
};
|
|
@ -62,6 +62,7 @@ import { getLazyEndpointPolicyEditExtension } from './management/pages/policy/vi
|
|||
import { LazyEndpointPolicyCreateExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_extension';
|
||||
import { getLazyEndpointPackageCustomExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_package_custom_extension';
|
||||
import { getLazyEndpointPolicyResponseExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_response_extension';
|
||||
import { getLazyEndpointGenericErrorsListExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_generic_errors_list';
|
||||
import type { ExperimentalFeatures } from '../common/experimental_features';
|
||||
import { parseExperimentalConfigValue } from '../common/experimental_features';
|
||||
import { LazyEndpointCustomAssetsExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_custom_assets_extension';
|
||||
|
@ -232,6 +233,11 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
view: 'package-policy-response',
|
||||
Component: getLazyEndpointPolicyResponseExtension(core, plugins),
|
||||
});
|
||||
registerExtension({
|
||||
package: 'endpoint',
|
||||
view: 'package-generic-errors-list',
|
||||
Component: getLazyEndpointGenericErrorsListExtension(core, plugins),
|
||||
});
|
||||
}
|
||||
|
||||
registerExtension({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue