mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Fleet] Add support for "Edit Package Policy" extensions using latest version of a package (#114914)
* Add support for extensions using latest version of a package and forcing upgrade state for edit policy view * Fix isUpgrade flag on integrations UI version of edit page * Treat non-validation errors as general failures in server and UI * Fix tests + don't call upgrade API when saving * fix i18n * Fix default name always appearing when editing package policies via extension UI * Opt security solution plugin out of new extension option Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
1da11dfdc0
commit
6d4cfc5e39
14 changed files with 194 additions and 68 deletions
|
@ -367,6 +367,7 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
|
|||
fleet.registerExtension({
|
||||
package: 'apm',
|
||||
view: 'package-policy-edit',
|
||||
useLatestPackageVersion: true,
|
||||
Component: getLazyAPMPolicyEditExtension(),
|
||||
});
|
||||
|
||||
|
|
|
@ -61,7 +61,11 @@ export const CreatePackagePolicyPageLayout: React.FunctionComponent<{
|
|||
const isEdit = useMemo(() => ['edit', 'package-edit'].includes(from), [from]);
|
||||
const isUpgrade = useMemo(
|
||||
() =>
|
||||
['upgrade-from-fleet-policy-list', 'upgrade-from-integrations-policy-list'].includes(from),
|
||||
[
|
||||
'upgrade-from-fleet-policy-list',
|
||||
'upgrade-from-integrations-policy-list',
|
||||
'upgrade-from-extension',
|
||||
].includes(from),
|
||||
[from]
|
||||
);
|
||||
|
||||
|
|
|
@ -105,11 +105,12 @@ export const StepDefinePackagePolicy: React.FunctionComponent<{
|
|||
agentPolicy?.id || '',
|
||||
packagePolicy.output_id,
|
||||
packagePolicy.namespace,
|
||||
`${packageInfo.name}-${
|
||||
pkgPoliciesWithMatchingNames.length
|
||||
? pkgPoliciesWithMatchingNames[pkgPoliciesWithMatchingNames.length - 1] + 1
|
||||
: 1
|
||||
}`,
|
||||
packagePolicy.name ||
|
||||
`${packageInfo.name}-${
|
||||
pkgPoliciesWithMatchingNames.length
|
||||
? pkgPoliciesWithMatchingNames[pkgPoliciesWithMatchingNames.length - 1] + 1
|
||||
: 1
|
||||
}`,
|
||||
packagePolicy.description,
|
||||
integrationToEnable
|
||||
)
|
||||
|
|
|
@ -11,7 +11,9 @@ export type EditPackagePolicyFrom =
|
|||
| 'policy'
|
||||
| 'edit'
|
||||
| 'upgrade-from-fleet-policy-list'
|
||||
| 'upgrade-from-integrations-policy-list';
|
||||
| 'upgrade-from-integrations-policy-list'
|
||||
| 'upgrade-from-extension';
|
||||
|
||||
export type PackagePolicyFormState =
|
||||
| 'VALID'
|
||||
| 'INVALID'
|
||||
|
|
|
@ -41,10 +41,12 @@ import {
|
|||
sendGetOneAgentPolicy,
|
||||
sendGetOnePackagePolicy,
|
||||
sendGetPackageInfoByKey,
|
||||
sendUpgradePackagePolicy,
|
||||
sendUpgradePackagePolicyDryRun,
|
||||
} from '../../../hooks';
|
||||
import { useBreadcrumbs as useIntegrationsBreadcrumbs } from '../../../../integrations/hooks';
|
||||
import {
|
||||
useBreadcrumbs as useIntegrationsBreadcrumbs,
|
||||
useGetOnePackagePolicy,
|
||||
} from '../../../../integrations/hooks';
|
||||
import { Loading, Error, ExtensionWrapper } from '../../../components';
|
||||
import { ConfirmDeployAgentPolicyModal } from '../components';
|
||||
import { CreatePackagePolicyPageLayout } from '../create_package_policy_page/components';
|
||||
|
@ -68,7 +70,23 @@ export const EditPackagePolicyPage = memo(() => {
|
|||
params: { packagePolicyId },
|
||||
} = useRouteMatch<{ policyId: string; packagePolicyId: string }>();
|
||||
|
||||
return <EditPackagePolicyForm packagePolicyId={packagePolicyId} />;
|
||||
const packagePolicy = useGetOnePackagePolicy(packagePolicyId);
|
||||
|
||||
const extensionView = useUIExtension(
|
||||
packagePolicy.data?.item?.package?.name ?? '',
|
||||
'package-policy-edit'
|
||||
);
|
||||
|
||||
return (
|
||||
<EditPackagePolicyForm
|
||||
packagePolicyId={packagePolicyId}
|
||||
// If an extension opts in to this `useLatestPackageVersion` flag, we want to display
|
||||
// the edit form in an "upgrade" state regardless of whether the user intended to
|
||||
// "edit" their policy or "upgrade" it. This ensures the new policy generated will be
|
||||
// set to use the latest version of the package, not its current version.
|
||||
isUpgrade={extensionView?.useLatestPackageVersion}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const EditPackagePolicyForm = memo<{
|
||||
|
@ -345,29 +363,6 @@ export const EditPackagePolicyForm = memo<{
|
|||
|
||||
const { error } = await savePackagePolicy();
|
||||
if (!error) {
|
||||
if (isUpgrade) {
|
||||
const { error: upgradeError } = await sendUpgradePackagePolicy([packagePolicyId]);
|
||||
|
||||
if (upgradeError) {
|
||||
notifications.toasts.addError(upgradeError, {
|
||||
title: i18n.translate('xpack.fleet.upgradePackagePolicy.failedNotificationTitle', {
|
||||
defaultMessage: 'Error upgrading {packagePolicyName}',
|
||||
values: {
|
||||
packagePolicyName: packagePolicy.name,
|
||||
},
|
||||
}),
|
||||
toastMessage: i18n.translate(
|
||||
'xpack.fleet.editPackagePolicy.failedConflictNotificationMessage',
|
||||
{
|
||||
defaultMessage: `Data is out of date. Refresh the page to get the latest policy.`,
|
||||
}
|
||||
),
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
application.navigateToUrl(successRedirectPath);
|
||||
notifications.toasts.addSuccess({
|
||||
title: i18n.translate('xpack.fleet.editPackagePolicy.updatedNotificationTitle', {
|
||||
|
@ -426,7 +421,7 @@ export const EditPackagePolicyForm = memo<{
|
|||
const [selectedTab, setSelectedTab] = useState(0);
|
||||
|
||||
const layoutProps = {
|
||||
from,
|
||||
from: extensionView?.useLatestPackageVersion ? 'upgrade-from-extension' : from,
|
||||
cancelUrl,
|
||||
agentPolicy,
|
||||
packageInfo,
|
||||
|
|
|
@ -10,11 +10,25 @@ import { useRouteMatch } from 'react-router-dom';
|
|||
|
||||
// TODO: Needs to be moved
|
||||
import { EditPackagePolicyForm } from '../../../../../fleet/sections/agent_policy/edit_package_policy_page';
|
||||
import { useGetOnePackagePolicy, useUIExtension } from '../../../../hooks';
|
||||
|
||||
export const Policy = memo(() => {
|
||||
const {
|
||||
params: { packagePolicyId },
|
||||
} = useRouteMatch<{ packagePolicyId: string }>();
|
||||
|
||||
return <EditPackagePolicyForm packagePolicyId={packagePolicyId} from="package-edit" />;
|
||||
const packagePolicy = useGetOnePackagePolicy(packagePolicyId);
|
||||
|
||||
const extensionView = useUIExtension(
|
||||
packagePolicy.data?.item?.package?.name ?? '',
|
||||
'package-policy-edit'
|
||||
);
|
||||
|
||||
return (
|
||||
<EditPackagePolicyForm
|
||||
packagePolicyId={packagePolicyId}
|
||||
from="package-edit"
|
||||
isUpgrade={extensionView?.useLatestPackageVersion}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -59,6 +59,13 @@ export function useGetPackagePolicies(query: GetPackagePoliciesRequest['query'])
|
|||
});
|
||||
}
|
||||
|
||||
export const useGetOnePackagePolicy = (packagePolicyId: string) => {
|
||||
return useRequest<GetOnePackagePolicyResponse>({
|
||||
path: packagePolicyRouteService.getInfoPath(packagePolicyId),
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
export const sendGetOnePackagePolicy = (packagePolicyId: string) => {
|
||||
return sendRequest<GetOnePackagePolicyResponse>({
|
||||
path: packagePolicyRouteService.getInfoPath(packagePolicyId),
|
||||
|
|
|
@ -50,6 +50,7 @@ export interface PackagePolicyEditExtensionComponentProps {
|
|||
export interface PackagePolicyEditExtension {
|
||||
package: string;
|
||||
view: 'package-policy-edit';
|
||||
useLatestPackageVersion?: boolean;
|
||||
Component: LazyExoticComponent<PackagePolicyEditExtensionComponent>;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@ export class PackageCacheError extends IngestManagerError {}
|
|||
export class PackageOperationNotSupportedError extends IngestManagerError {}
|
||||
export class ConcurrentInstallOperationError extends IngestManagerError {}
|
||||
export class AgentReassignmentError extends IngestManagerError {}
|
||||
export class PackagePolicyIneligibleForUpgradeError extends IngestManagerError {}
|
||||
export class PackagePolicyValidationError extends IngestManagerError {}
|
||||
export class HostedAgentPolicyRestrictionRelatedError extends IngestManagerError {
|
||||
constructor(message = 'Cannot perform that action') {
|
||||
super(
|
||||
|
|
|
@ -45,6 +45,8 @@ import {
|
|||
HostedAgentPolicyRestrictionRelatedError,
|
||||
IngestManagerError,
|
||||
ingestErrorToResponseOptions,
|
||||
PackagePolicyIneligibleForUpgradeError,
|
||||
PackagePolicyValidationError,
|
||||
} from '../errors';
|
||||
import { NewPackagePolicySchema, UpdatePackagePolicySchema } from '../types';
|
||||
import type {
|
||||
|
@ -528,25 +530,25 @@ class PackagePolicyService {
|
|||
pkgName: packagePolicy.package.name,
|
||||
pkgVersion: installedPackage?.version ?? '',
|
||||
});
|
||||
}
|
||||
|
||||
const isInstalledVersionLessThanOrEqualToPolicyVersion = semverLte(
|
||||
installedPackage?.version ?? '',
|
||||
packagePolicy.package.version
|
||||
const isInstalledVersionLessThanOrEqualToPolicyVersion = semverLte(
|
||||
packageInfo?.version ?? '',
|
||||
packagePolicy.package.version
|
||||
);
|
||||
|
||||
if (isInstalledVersionLessThanOrEqualToPolicyVersion) {
|
||||
throw new PackagePolicyIneligibleForUpgradeError(
|
||||
i18n.translate('xpack.fleet.packagePolicy.ineligibleForUpgradeError', {
|
||||
defaultMessage:
|
||||
"Package policy {id}'s package version {version} of package {name} is up to date with the installed package. Please install the latest version of {name}.",
|
||||
values: {
|
||||
id: packagePolicy.id,
|
||||
name: packagePolicy.package.name,
|
||||
version: packagePolicy.package.version,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
if (isInstalledVersionLessThanOrEqualToPolicyVersion) {
|
||||
throw new IngestManagerError(
|
||||
i18n.translate('xpack.fleet.packagePolicy.ineligibleForUpgradeError', {
|
||||
defaultMessage:
|
||||
"Package policy {id}'s package version {version} of package {name} is up to date with the installed package. Please install the latest version of {name}.",
|
||||
values: {
|
||||
id: packagePolicy.id,
|
||||
name: packagePolicy.package.name,
|
||||
version: packagePolicy.package.version,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -600,6 +602,13 @@ class PackagePolicyService {
|
|||
currentVersion: packageInfo.version,
|
||||
});
|
||||
} catch (error) {
|
||||
// We only want to specifically handle validation errors for the new package policy. If a more severe or
|
||||
// general error is thrown elsewhere during the upgrade process, we want to surface that directly in
|
||||
// order to preserve any status code mappings, etc that might be included w/ the particular error type
|
||||
if (!(error instanceof PackagePolicyValidationError)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
result.push({
|
||||
id,
|
||||
success: false,
|
||||
|
@ -653,6 +662,10 @@ class PackagePolicyService {
|
|||
hasErrors,
|
||||
};
|
||||
} catch (error) {
|
||||
if (!(error instanceof PackagePolicyValidationError)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return {
|
||||
hasErrors: true,
|
||||
...ingestErrorToResponseOptions(error),
|
||||
|
@ -1089,7 +1102,7 @@ export function overridePackageInputs(
|
|||
return { ...resultingPackagePolicy, errors: responseFormattedValidationErrors };
|
||||
}
|
||||
|
||||
throw new IngestManagerError(
|
||||
throw new PackagePolicyValidationError(
|
||||
i18n.translate('xpack.fleet.packagePolicyInvalidError', {
|
||||
defaultMessage: 'Package policy is invalid: {errors}',
|
||||
values: {
|
||||
|
|
|
@ -11255,7 +11255,6 @@
|
|||
"xpack.fleet.upgradeAgents.upgradeMultipleDescription": "このアクションにより、複数のエージェントがバージョン{version}にアップグレードされます。このアクションは元に戻せません。続行していいですか?",
|
||||
"xpack.fleet.upgradeAgents.upgradeSingleDescription": "このアクションにより、「{hostName}」で実行中のエージェントがバージョン{version}にアップグレードされます。このアクションは元に戻せません。続行していいですか?",
|
||||
"xpack.fleet.upgradeAgents.upgradeSingleTitle": "エージェントを最新バージョンにアップグレード",
|
||||
"xpack.fleet.upgradePackagePolicy.failedNotificationTitle": "{packagePolicyName}のアップグレードエラー",
|
||||
"xpack.fleet.upgradePackagePolicy.pageDescriptionFromUpgrade": "この統合をアップグレードし、選択したエージェントポリシーに変更をデプロイします",
|
||||
"xpack.fleet.upgradePackagePolicy.previousVersionFlyout.title": "'{name}'パッケージポリシー",
|
||||
"xpack.fleet.upgradePackagePolicy.statusCallout.errorContent": "この統合には、バージョン{currentVersion}から{upgradeVersion}で競合するフィールドがあります。構成を確認して保存し、アップグレードを実行してください。{previousConfigurationLink}を参照して比較できます。",
|
||||
|
|
|
@ -11378,7 +11378,6 @@
|
|||
"xpack.fleet.upgradeAgents.upgradeMultipleTitle": "将{count, plural, one {代理} other { {count} 个代理} =true {所有选定代理}}升级到最新版本",
|
||||
"xpack.fleet.upgradeAgents.upgradeSingleDescription": "此操作会将“{hostName}”上运行的代理升级到版本 {version}。此操作无法撤消。是否确定要继续?",
|
||||
"xpack.fleet.upgradeAgents.upgradeSingleTitle": "将代理升级到最新版本",
|
||||
"xpack.fleet.upgradePackagePolicy.failedNotificationTitle": "升级 {packagePolicyName} 时出错",
|
||||
"xpack.fleet.upgradePackagePolicy.pageDescriptionFromUpgrade": "升级此集成并将更改部署到选定代理策略",
|
||||
"xpack.fleet.upgradePackagePolicy.previousVersionFlyout.title": "“{name}”软件包策略",
|
||||
"xpack.fleet.upgradePackagePolicy.statusCallout.errorContent": "此集成在版本 {currentVersion} 和 {upgradeVersion} 之间有冲突字段。请复查配置并保存,以执行升级。您可以参考您的 {previousConfigurationLink}以进行比较。",
|
||||
|
|
|
@ -221,6 +221,7 @@ export class UptimePlugin
|
|||
registerExtension({
|
||||
package: 'synthetics',
|
||||
view: 'package-policy-edit',
|
||||
useLatestPackageVersion: true,
|
||||
Component: LazySyntheticsPolicyEditExtension,
|
||||
});
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ export default function (providerContext: FtrProviderContext) {
|
|||
|
||||
describe('upgrade', function () {
|
||||
it('should respond with an error when "dryRun: false" is provided', async function () {
|
||||
const { body }: { body: UpgradePackagePolicyResponse } = await supertest
|
||||
await supertest
|
||||
.post(`/api/fleet/package_policies/upgrade`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
|
@ -154,10 +154,7 @@ export default function (providerContext: FtrProviderContext) {
|
|||
dryRun: false,
|
||||
packageVersion: '0.2.0-add-non-required-test-var',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body.length).to.be(1);
|
||||
expect(body[0].success).to.be(false);
|
||||
.expect(400);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1041,31 +1038,121 @@ export default function (providerContext: FtrProviderContext) {
|
|||
|
||||
describe('when package policy is not found', function () {
|
||||
it('should return an 200 with errors when "dryRun:true" is provided', async function () {
|
||||
const { body }: { body: UpgradePackagePolicyDryRunResponse } = await supertest
|
||||
await supertest
|
||||
.post(`/api/fleet/package_policies/upgrade`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
packagePolicyIds: ['xxxx', 'yyyy'],
|
||||
dryRun: true,
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body[0].hasErrors).to.be(true);
|
||||
expect(body[1].hasErrors).to.be(true);
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
it('should return a 200 with errors and "success:false" when "dryRun:false" is provided', async function () {
|
||||
const { body }: { body: UpgradePackagePolicyResponse } = await supertest
|
||||
await supertest
|
||||
.post(`/api/fleet/package_policies/upgrade`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
packagePolicyIds: ['xxxx', 'yyyy'],
|
||||
dryRun: false,
|
||||
})
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when policy's package version is up to date", function () {
|
||||
withTestPackageVersion('0.1.0');
|
||||
|
||||
beforeEach(async function () {
|
||||
const { body: agentPolicyResponse } = await supertest
|
||||
.post(`/api/fleet/agent_policies`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: 'Test policy',
|
||||
namespace: 'default',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body[0].success).to.be(false);
|
||||
expect(body[1].success).to.be(false);
|
||||
agentPolicyId = agentPolicyResponse.item.id;
|
||||
|
||||
const { body: packagePolicyResponse } = await supertest
|
||||
.post(`/api/fleet/package_policies`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: 'package_policy_upgrade_1',
|
||||
description: '',
|
||||
namespace: 'default',
|
||||
policy_id: agentPolicyId,
|
||||
enabled: true,
|
||||
output_id: '',
|
||||
inputs: [
|
||||
{
|
||||
policy_template: 'package_policy_upgrade',
|
||||
type: 'test_input',
|
||||
enabled: true,
|
||||
streams: [
|
||||
{
|
||||
id: 'test-package_policy_upgrade-xxxx',
|
||||
enabled: true,
|
||||
data_stream: {
|
||||
type: 'test_stream',
|
||||
dataset: 'package_policy_upgrade.test_stream',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
package: {
|
||||
name: 'package_policy_upgrade',
|
||||
title: 'This is a test package for upgrading package policies',
|
||||
version: '0.1.0',
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
packagePolicyId = packagePolicyResponse.item.id;
|
||||
});
|
||||
|
||||
afterEach(async function () {
|
||||
await supertest
|
||||
.post(`/api/fleet/package_policies/delete`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({ packagePolicyIds: [packagePolicyId] })
|
||||
.expect(200);
|
||||
|
||||
await supertest
|
||||
.post('/api/fleet/agent_policies/delete')
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({ agentPolicyId })
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
describe('dry run', function () {
|
||||
it('should respond with a bad request', async function () {
|
||||
await supertest
|
||||
.post(`/api/fleet/package_policies/upgrade`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
packagePolicyIds: [packagePolicyId],
|
||||
dryRun: true,
|
||||
packageVersion: '0.1.0',
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('upgrade', function () {
|
||||
it('should respond with a bad request', async function () {
|
||||
await supertest
|
||||
.post(`/api/fleet/package_policies/upgrade`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
packagePolicyIds: [packagePolicyId],
|
||||
dryRun: false,
|
||||
packageVersion: '0.1.0',
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue