diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index 1e1d9ce5687d1..3a439df245609 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -367,6 +367,7 @@ export class ApmPlugin implements Plugin { fleet.registerExtension({ package: 'apm', view: 'package-policy-edit', + useLatestPackageVersion: true, Component: getLazyAPMPolicyEditExtension(), }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx index 1de37912b05c9..3daf7fa545f24 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx @@ -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] ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_define_package_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_define_package_policy.tsx index 7e4896837013c..29c226ca64f57 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_define_package_policy.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_define_package_policy.tsx @@ -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 ) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/types.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/types.ts index d3b1fb1290596..82c18c34977df 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/types.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/types.ts @@ -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' diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index 840a5a71a63c7..71dfb610a9151 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -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 ; + const packagePolicy = useGetOnePackagePolicy(packagePolicyId); + + const extensionView = useUIExtension( + packagePolicy.data?.item?.package?.name ?? '', + 'package-policy-edit' + ); + + return ( + + ); }); 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, diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/policy/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/policy/index.tsx index cb95634de4c07..ed8ad166cde9b 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/policy/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/policy/index.tsx @@ -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 ; + const packagePolicy = useGetOnePackagePolicy(packagePolicyId); + + const extensionView = useUIExtension( + packagePolicy.data?.item?.package?.name ?? '', + 'package-policy-edit' + ); + + return ( + + ); }); diff --git a/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts b/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts index f8d14647439b2..5aa36ba2e8268 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts @@ -59,6 +59,13 @@ export function useGetPackagePolicies(query: GetPackagePoliciesRequest['query']) }); } +export const useGetOnePackagePolicy = (packagePolicyId: string) => { + return useRequest({ + path: packagePolicyRouteService.getInfoPath(packagePolicyId), + method: 'get', + }); +}; + export const sendGetOnePackagePolicy = (packagePolicyId: string) => { return sendRequest({ path: packagePolicyRouteService.getInfoPath(packagePolicyId), diff --git a/x-pack/plugins/fleet/public/types/ui_extensions.ts b/x-pack/plugins/fleet/public/types/ui_extensions.ts index d365b798fe83e..6c959bec1d785 100644 --- a/x-pack/plugins/fleet/public/types/ui_extensions.ts +++ b/x-pack/plugins/fleet/public/types/ui_extensions.ts @@ -50,6 +50,7 @@ export interface PackagePolicyEditExtensionComponentProps { export interface PackagePolicyEditExtension { package: string; view: 'package-policy-edit'; + useLatestPackageVersion?: boolean; Component: LazyExoticComponent; } diff --git a/x-pack/plugins/fleet/server/errors/index.ts b/x-pack/plugins/fleet/server/errors/index.ts index f31719d6c4364..22f4b8cd6daab 100644 --- a/x-pack/plugins/fleet/server/errors/index.ts +++ b/x-pack/plugins/fleet/server/errors/index.ts @@ -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( diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index b0c0b9499c68e..9928ce3063159 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -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 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, - }, - }) - ); - } + 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, + }, + }) + ); } 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: { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index c54512203677d..90862e4da92d5 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -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}を参照して比較できます。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index fbc65161a47f8..b9451bee2f616 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -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}以进行比较。", diff --git a/x-pack/plugins/uptime/public/apps/plugin.ts b/x-pack/plugins/uptime/public/apps/plugin.ts index 124de9a60110c..0cd0af6231c9c 100644 --- a/x-pack/plugins/uptime/public/apps/plugin.ts +++ b/x-pack/plugins/uptime/public/apps/plugin.ts @@ -221,6 +221,7 @@ export class UptimePlugin registerExtension({ package: 'synthetics', view: 'package-policy-edit', + useLatestPackageVersion: true, Component: LazySyntheticsPolicyEditExtension, }); diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/upgrade.ts b/x-pack/test/fleet_api_integration/apis/package_policy/upgrade.ts index 0be2d7d0a7468..a61a77fd37f6b 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/upgrade.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/upgrade.ts @@ -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); + }); }); }); });