Skip to content

Commit

Permalink
Merge branch '8.12' into backport/8.12/pr-169712
Browse files Browse the repository at this point in the history
  • Loading branch information
lcawl authored Feb 20, 2024
2 parents 2bb3087 + 6536da7 commit d3b5f39
Show file tree
Hide file tree
Showing 52 changed files with 2,052 additions and 620 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
EuiText,
EuiTourStep,
useEuiTheme,
EuiLink,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
Expand Down Expand Up @@ -64,17 +65,11 @@ const LeftpaddedNotificationBadge = styled(EuiNotificationBadge)`
margin-left: 10px;
`;

const TourStepNoHeaderFooter = styled(EuiTourStep)`
.euiTourFooter {
display: none;
}
.euiTourHeader {
display: none;
}
`;

const InactiveAgentsTourStep: React.FC<{ isOpen: boolean }> = ({ children, isOpen }) => (
<TourStepNoHeaderFooter
const InactiveAgentsTourStep: React.FC<{
isOpen: boolean;
setInactiveAgentsCalloutHasBeenDismissed: (val: boolean) => void;
}> = ({ children, isOpen, setInactiveAgentsCalloutHasBeenDismissed }) => (
<EuiTourStep
content={
<EuiText size="s">
<FormattedMessage
Expand All @@ -91,9 +86,21 @@ const InactiveAgentsTourStep: React.FC<{ isOpen: boolean }> = ({ children, isOpe
onFinish={() => {}}
anchorPosition="upCenter"
maxWidth={280}
footerAction={
<EuiLink
onClick={() => {
setInactiveAgentsCalloutHasBeenDismissed(true);
}}
>
<FormattedMessage
id="xpack.fleet.addAgentHelpPopover.footActionButton"
defaultMessage="Got it"
/>
</EuiLink>
}
>
{children as React.ReactElement}
</TourStepNoHeaderFooter>
</EuiTourStep>
);

export const AgentStatusFilter: React.FC<{
Expand Down Expand Up @@ -160,6 +167,7 @@ export const AgentStatusFilter: React.FC<{
return (
<InactiveAgentsTourStep
isOpen={newlyInactiveAgentsCount > 0 && !inactiveAgentsCalloutHasBeenDismissed}
setInactiveAgentsCalloutHasBeenDismissed={setInactiveAgentsCalloutHasBeenDismissed}
>
<EuiPopover
ownFocus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { Fragment, useEffect, useState } from 'react';
import React, { Fragment, useEffect, useState, useCallback } from 'react';
import { Redirect } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiTitle, EuiCallOut } from '@elastic/eui';
Expand Down Expand Up @@ -38,10 +38,12 @@ import { AssetsAccordion } from './assets_accordion';

interface AssetsPanelProps {
packageInfo: PackageInfo;
refetchPackageInfo: () => void;
}

export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => {
export const AssetsPage = ({ packageInfo, refetchPackageInfo }: AssetsPanelProps) => {
const { name, version } = packageInfo;

const pkgkey = `${name}-${version}`;
const { spaces, docLinks } = useStartServices();
const customAssetsExtension = useUIExtension(packageInfo.name, 'package-detail-assets');
Expand All @@ -60,6 +62,12 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => {
const [fetchError, setFetchError] = useState<undefined | Error>();
const [isLoading, setIsLoading] = useState<boolean>(true);

const forceRefreshAssets = useCallback(() => {
if (refetchPackageInfo) {
refetchPackageInfo();
}
}, [refetchPackageInfo]);

useEffect(() => {
const fetchAssetSavedObjects = async () => {
if ('installationInfo' in packageInfo) {
Expand Down Expand Up @@ -245,6 +253,7 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => {
<DeferredAssetsSection
deferredInstallations={deferredInstallations}
packageInfo={packageInfo}
forceRefreshAssets={forceRefreshAssets}
/>
<EuiSpacer size="m" />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ import { DeferredTransformAccordion } from './deferred_transforms_accordion';
interface Props {
packageInfo: PackageInfo;
deferredInstallations: EsAssetReference[];
forceRefreshAssets?: () => void;
}

export const DeferredAssetsSection: FunctionComponent<Props> = ({
deferredInstallations,
packageInfo,
forceRefreshAssets,
}) => {
const authz = useAuthz();

Expand Down Expand Up @@ -60,6 +62,7 @@ export const DeferredAssetsSection: FunctionComponent<Props> = ({
packageInfo={packageInfo}
type={ElasticsearchAssetType.transform}
deferredInstallations={deferredTransforms}
forceRefreshAssets={forceRefreshAssets}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { Fragment, useCallback, useMemo, useState } from 'react';
import React, { Fragment, useCallback, useState, useMemo } from 'react';
import type { FunctionComponent, MouseEvent } from 'react';

import {
Expand Down Expand Up @@ -42,6 +42,7 @@ interface Props {
packageInfo: PackageInfo;
type: ElasticsearchAssetType.transform;
deferredInstallations: EsAssetReference[];
forceRefreshAssets?: () => void;
}

export const getDeferredAssetDescription = (
Expand Down Expand Up @@ -83,6 +84,7 @@ export const DeferredTransformAccordion: FunctionComponent<Props> = ({
packageInfo,
type,
deferredInstallations,
forceRefreshAssets,
}) => {
const { notifications } = useStartServices();
const [isLoading, setIsLoading] = useState(false);
Expand Down Expand Up @@ -159,6 +161,9 @@ export const DeferredTransformAccordion: FunctionComponent<Props> = ({
),
{ toastLifeTimeMs: 1000 }
);
if (forceRefreshAssets) {
forceRefreshAssets();
}
}
}
} catch (e) {
Expand All @@ -171,11 +176,14 @@ export const DeferredTransformAccordion: FunctionComponent<Props> = ({
}
),
});
if (forceRefreshAssets) {
forceRefreshAssets();
}
}
}
setIsLoading(false);
},
[notifications.toasts, packageInfo.name, packageInfo.version]
[notifications.toasts, packageInfo.name, packageInfo.version, forceRefreshAssets]
);
if (deferredTransforms.length === 0) return null;
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ export function Detail() {
<SettingsPage packageInfo={packageInfo} theme$={services.theme.theme$} />
</Route>
<Route path={INTEGRATIONS_ROUTING_PATHS.integration_details_assets}>
<AssetsPage packageInfo={packageInfo} />
<AssetsPage packageInfo={packageInfo} refetchPackageInfo={refetchPackageInfo} />
</Route>
<Route path={INTEGRATIONS_ROUTING_PATHS.integration_details_configs}>
<Configs packageInfo={packageInfo} />
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/fleet/server/routes/epm/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,8 @@ export const bulkInstallPackagesFromRegistryHandler: FleetRequestHandler<
const savedObjectsClient = fleetContext.internalSoClient;
const esClient = coreContext.elasticsearch.client.asInternalUser;
const spaceId = fleetContext.spaceId;
const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined;
const authorizationHeader = HTTPAuthorizationHeader.parseFromRequest(request, user?.username);

const bulkInstalledResponses = await bulkInstallPackages({
savedObjectsClient,
Expand All @@ -420,6 +422,7 @@ export const bulkInstallPackagesFromRegistryHandler: FleetRequestHandler<
spaceId,
prerelease: request.query.prerelease,
force: request.body.force,
authorizationHeader,
});
const payload = bulkInstalledResponses.map(bulkInstallServiceResponseToHttpEntry);
const body: BulkInstallPackagesResponse = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,6 @@ const installTransformsAssets = async (
})
: // No need to generate api key/secondary auth if all transforms are run as kibana_system user
undefined;

// delete all previous transform
await Promise.all([
deleteTransforms(
Expand Down Expand Up @@ -761,7 +760,9 @@ async function handleTransformInstall({
throw err;
}
}
} else {
}

if (startTransform === false || transform?.content?.settings?.unattended === true) {
// if transform was not set to start automatically in yml config,
// we need to check using _stats if the transform had insufficient permissions
try {
Expand All @@ -773,7 +774,11 @@ async function handleTransformInstall({
),
{ logger, additionalResponseStatuses: [400] }
);
if (Array.isArray(transformStats.transforms) && transformStats.transforms.length === 1) {
if (
transformStats &&
Array.isArray(transformStats.transforms) &&
transformStats.transforms.length === 1
) {
const transformHealth = transformStats.transforms[0].health;
if (
transformHealth &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import type { Logger } from '@kbn/logging';
import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';

import { sortBy, uniqBy } from 'lodash';
import { isPopulatedObject } from '@kbn/ml-is-populated-object';
import type { ErrorResponseBase } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';

import type { SecondaryAuthorizationHeader } from '../../../../../common/types/models/transform_api_key';
import { updateEsAssetReferences } from '../../packages/install';
Expand All @@ -30,6 +32,9 @@ interface FleetTransformMetadata {
transformId: string;
}

const isErrorResponse = (arg: unknown): arg is ErrorResponseBase =>
isPopulatedObject(arg, ['error']);

async function reauthorizeAndStartTransform({
esClient,
logger,
Expand Down Expand Up @@ -68,6 +73,19 @@ async function reauthorizeAndStartTransform({
() => esClient.transform.startTransform({ transform_id: transformId }, { ignore: [409] }),
{ logger, additionalResponseStatuses: [400] }
);

// Transform can already be started even without sufficient permission if 'unattended: true'
// So we are just catching that special case to showcase in the UI
// If unattended, calling _start will return a successful response, but with the error message in the body
if (
isErrorResponse(startedTransform) &&
startedTransform.status === 409 &&
Array.isArray(startedTransform.error?.root_cause) &&
startedTransform.error.root_cause[0]?.reason?.includes('already started')
) {
return { transformId, success: true, error: null };
}

logger.debug(`Started transform: ${transformId}`);
return { transformId, success: startedTransform.acknowledged, error: null };
} catch (err) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export async function getBulkAssets(
type: obj.type as unknown as ElasticsearchAssetType | KibanaSavedObjectType,
updatedAt: obj.updated_at,
attributes: {
title: obj.attributes.title,
description: obj.attributes.description,
title: obj.attributes?.title,
description: obj.attributes?.description,
},
};
});
Expand Down
54 changes: 35 additions & 19 deletions x-pack/plugins/fleet/server/services/epm/packages/remove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,32 +190,48 @@ async function deleteAssets(
// must delete index templates first, or component templates which reference them cannot be deleted
// must delete ingestPipelines first, or ml models referenced in them cannot be deleted.
// separate the assets into Index Templates and other assets.
type Tuple = [EsAssetReference[], EsAssetReference[], EsAssetReference[]];
const [indexTemplatesAndPipelines, indexAssets, otherAssets] = installedEs.reduce<Tuple>(
([indexTemplateAndPipelineTypes, indexAssetTypes, otherAssetTypes], asset) => {
if (
asset.type === ElasticsearchAssetType.indexTemplate ||
asset.type === ElasticsearchAssetType.ingestPipeline
) {
indexTemplateAndPipelineTypes.push(asset);
} else if (asset.type === ElasticsearchAssetType.index) {
indexAssetTypes.push(asset);
} else {
otherAssetTypes.push(asset);
}

return [indexTemplateAndPipelineTypes, indexAssetTypes, otherAssetTypes];
},
[[], [], []]
);
type Tuple = [EsAssetReference[], EsAssetReference[], EsAssetReference[], EsAssetReference[]];
const [indexTemplatesAndPipelines, indexAssets, transformAssets, otherAssets] =
installedEs.reduce<Tuple>(
(
[indexTemplateAndPipelineTypes, indexAssetTypes, transformAssetTypes, otherAssetTypes],
asset
) => {
if (
asset.type === ElasticsearchAssetType.indexTemplate ||
asset.type === ElasticsearchAssetType.ingestPipeline
) {
indexTemplateAndPipelineTypes.push(asset);
} else if (asset.type === ElasticsearchAssetType.index) {
indexAssetTypes.push(asset);
} else if (asset.type === ElasticsearchAssetType.transform) {
transformAssetTypes.push(asset);
} else {
otherAssetTypes.push(asset);
}

return [
indexTemplateAndPipelineTypes,
indexAssetTypes,
transformAssetTypes,
otherAssetTypes,
];
},
[[], [], [], []]
);

try {
// must first unset any default pipeline associated with any existing indices
// by setting empty string
await Promise.all(
indexAssets.map((asset) => updateIndexSettings(esClient, asset.id, { default_pipeline: '' }))
);
// must delete index templates and pipelines first

// in case transform's destination index contains any pipline,
// we should delete the transforms first
await Promise.all(deleteESAssets(transformAssets, esClient));

// then delete index templates and pipelines
await Promise.all(deleteESAssets(indexTemplatesAndPipelines, esClient));
// then the other asset types
await Promise.all([
Expand Down
Loading

0 comments on commit d3b5f39

Please sign in to comment.