diff --git a/x-pack/plugins/fleet/server/services/managed_package_policies.test.ts b/x-pack/plugins/fleet/server/services/managed_package_policies.test.ts index af3b3ef4dfccd..402b65334121a 100644 --- a/x-pack/plugins/fleet/server/services/managed_package_policies.test.ts +++ b/x-pack/plugins/fleet/server/services/managed_package_policies.test.ts @@ -7,11 +7,9 @@ import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; -import type { Installation } from '../../common'; - -import { shouldUpgradePolicies, upgradeManagedPackagePolicies } from './managed_package_policies'; +import { upgradeManagedPackagePolicies } from './managed_package_policies'; import { packagePolicyService } from './package_policy'; -import { getInstallation } from './epm/packages'; +import { getInstallations } from './epm/packages'; jest.mock('./package_policy'); jest.mock('./epm/packages'); @@ -20,7 +18,7 @@ jest.mock('./app_context', () => { ...jest.requireActual('./app_context'), appContextService: { getLogger: jest.fn(() => { - return { error: jest.fn() }; + return { error: jest.fn(), debug: jest.fn() }; }), }, }; @@ -28,20 +26,30 @@ jest.mock('./app_context', () => { describe('upgradeManagedPackagePolicies', () => { afterEach(() => { - (packagePolicyService.get as jest.Mock).mockReset(); - (packagePolicyService.getUpgradeDryRunDiff as jest.Mock).mockReset(); - (getInstallation as jest.Mock).mockReset(); - (packagePolicyService.upgrade as jest.Mock).mockReset(); + jest.clearAllMocks(); }); it('should not upgrade policies for non-managed package', async () => { const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; const soClient = savedObjectsClientMock.create(); - (packagePolicyService.get as jest.Mock).mockImplementationOnce( - (savedObjectsClient: any, id: string) => { - return { - id, + (getInstallations as jest.Mock).mockResolvedValueOnce({ + saved_objects: [], + }); + + await upgradeManagedPackagePolicies(soClient, esClient); + + expect(packagePolicyService.upgrade).not.toBeCalled(); + }); + + it('should upgrade policies for managed package', async () => { + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + const soClient = savedObjectsClientMock.create(); + + (packagePolicyService.list as jest.Mock).mockResolvedValueOnce({ + items: [ + { + id: 'managed-package-id', inputs: {}, version: '', revision: 1, @@ -50,43 +58,48 @@ describe('upgradeManagedPackagePolicies', () => { created_at: '', created_by: '', package: { - name: 'non-managed-package', - title: 'Non-Managed Package', - version: '1.0.0', + name: 'managed-package', + title: 'Managed Package', + version: '0.0.1', }, - }; - } - ); + }, + ], + }); - (packagePolicyService.getUpgradeDryRunDiff as jest.Mock).mockImplementationOnce( - (savedObjectsClient: any, id: string) => { - return { - name: 'non-managed-package-policy', - diff: [{ id: 'foo' }, { id: 'bar' }], - hasErrors: false, - }; - } - ); + (packagePolicyService.getUpgradeDryRunDiff as jest.Mock).mockResolvedValueOnce({ + name: 'non-managed-package-policy', + diff: [{ id: 'foo' }, { id: 'bar' }], + hasErrors: false, + }); - (getInstallation as jest.Mock).mockResolvedValueOnce({ - id: 'test-installation', - version: '0.0.1', - keep_policies_up_to_date: false, + (getInstallations as jest.Mock).mockResolvedValueOnce({ + saved_objects: [ + { + attributes: { + id: 'test-installation', + version: '1.0.0', + keep_policies_up_to_date: true, + }, + }, + ], }); - await upgradeManagedPackagePolicies(soClient, esClient, ['non-managed-package-id']); + const results = await upgradeManagedPackagePolicies(soClient, esClient); + expect(results).toEqual([ + { packagePolicyId: 'managed-package-id', diff: [{ id: 'foo' }, { id: 'bar' }], errors: [] }, + ]); - expect(packagePolicyService.upgrade).not.toBeCalled(); + expect(packagePolicyService.upgrade).toBeCalledWith(soClient, esClient, ['managed-package-id']); }); - it('should upgrade policies for managed package', async () => { + it('should not upgrade policy if newer than installed package version', async () => { const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; const soClient = savedObjectsClientMock.create(); - (packagePolicyService.get as jest.Mock).mockImplementationOnce( - (savedObjectsClient: any, id: string) => { - return { - id, + (packagePolicyService.list as jest.Mock).mockResolvedValueOnce({ + items: [ + { + id: 'managed-package-id', inputs: {}, version: '', revision: 1, @@ -97,31 +110,28 @@ describe('upgradeManagedPackagePolicies', () => { package: { name: 'managed-package', title: 'Managed Package', - version: '0.0.1', + version: '1.0.1', }, - }; - } - ); - - (packagePolicyService.getUpgradeDryRunDiff as jest.Mock).mockImplementationOnce( - (savedObjectsClient: any, id: string) => { - return { - name: 'non-managed-package-policy', - diff: [{ id: 'foo' }, { id: 'bar' }], - hasErrors: false, - }; - } - ); + }, + ], + }); - (getInstallation as jest.Mock).mockResolvedValueOnce({ - id: 'test-installation', - version: '1.0.0', - keep_policies_up_to_date: true, + (getInstallations as jest.Mock).mockResolvedValueOnce({ + saved_objects: [ + { + attributes: { + id: 'test-installation', + version: '1.0.0', + keep_policies_up_to_date: true, + }, + }, + ], }); - await upgradeManagedPackagePolicies(soClient, esClient, ['managed-package-id']); + await upgradeManagedPackagePolicies(soClient, esClient); - expect(packagePolicyService.upgrade).toBeCalledWith(soClient, esClient, ['managed-package-id']); + expect(packagePolicyService.getUpgradeDryRunDiff).not.toHaveBeenCalled(); + expect(packagePolicyService.upgrade).not.toHaveBeenCalled(); }); describe('when dry run reports conflicts', () => { @@ -129,10 +139,10 @@ describe('upgradeManagedPackagePolicies', () => { const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; const soClient = savedObjectsClientMock.create(); - (packagePolicyService.get as jest.Mock).mockImplementationOnce( - (savedObjectsClient: any, id: string) => { - return { - id, + (packagePolicyService.list as jest.Mock).mockResolvedValueOnce({ + items: [ + { + id: 'conflicting-package-policy', inputs: {}, version: '', revision: 1, @@ -145,32 +155,32 @@ describe('upgradeManagedPackagePolicies', () => { title: 'Conflicting Package', version: '0.0.1', }, - }; - } - ); + }, + ], + }); - (packagePolicyService.getUpgradeDryRunDiff as jest.Mock).mockImplementationOnce( - (savedObjectsClient: any, id: string) => { - return { - name: 'conflicting-package-policy', - diff: [ - { id: 'foo' }, - { id: 'bar', errors: [{ key: 'some.test.value', message: 'Conflict detected' }] }, - ], - hasErrors: true, - }; - } - ); + (packagePolicyService.getUpgradeDryRunDiff as jest.Mock).mockResolvedValueOnce({ + name: 'conflicting-package-policy', + diff: [ + { id: 'foo' }, + { id: 'bar', errors: [{ key: 'some.test.value', message: 'Conflict detected' }] }, + ], + hasErrors: true, + }); - (getInstallation as jest.Mock).mockResolvedValueOnce({ - id: 'test-installation', - version: '1.0.0', - keep_policies_up_to_date: true, + (getInstallations as jest.Mock).mockResolvedValueOnce({ + saved_objects: [ + { + attributes: { + id: 'test-installation', + version: '1.0.0', + keep_policies_up_to_date: true, + }, + }, + ], }); - const result = await upgradeManagedPackagePolicies(soClient, esClient, [ - 'conflicting-package-policy', - ]); + const result = await upgradeManagedPackagePolicies(soClient, esClient); expect(result).toEqual([ { @@ -202,61 +212,3 @@ describe('upgradeManagedPackagePolicies', () => { }); }); }); - -describe('shouldUpgradePolicies', () => { - describe('package policy is up-to-date', () => { - describe('keep_policies_up_to_date is true', () => { - it('returns false', () => { - const installedPackage = { - version: '1.0.0', - keep_policies_up_to_date: true, - }; - - const result = shouldUpgradePolicies('1.0.0', installedPackage as Installation); - - expect(result).toBe(false); - }); - }); - - describe('keep_policies_up_to_date is false', () => { - it('returns false', () => { - const installedPackage = { - version: '1.0.0', - keep_policies_up_to_date: false, - }; - - const result = shouldUpgradePolicies('1.0.0', installedPackage as Installation); - - expect(result).toBe(false); - }); - }); - }); - - describe('package policy is out-of-date', () => { - describe('keep_policies_up_to_date is true', () => { - it('returns true', () => { - const installedPackage = { - version: '1.1.0', - keep_policies_up_to_date: true, - }; - - const result = shouldUpgradePolicies('1.0.0', installedPackage as Installation); - - expect(result).toBe(true); - }); - }); - - describe('keep_policies_up_to_date is false', () => { - it('returns false', () => { - const installedPackage = { - version: '1.1.0', - keep_policies_up_to_date: false, - }; - - const result = shouldUpgradePolicies('1.0.0', installedPackage as Installation); - - expect(result).toBe(false); - }); - }); - }); -}); diff --git a/x-pack/plugins/fleet/server/services/managed_package_policies.ts b/x-pack/plugins/fleet/server/services/managed_package_policies.ts index 77715ad488feb..2c4b326d56532 100644 --- a/x-pack/plugins/fleet/server/services/managed_package_policies.ts +++ b/x-pack/plugins/fleet/server/services/managed_package_policies.ts @@ -6,12 +6,16 @@ */ import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; -import semverGte from 'semver/functions/gte'; +import semverLt from 'semver/functions/lt'; -import type { Installation, UpgradePackagePolicyDryRunResponseItem } from '../../common'; +import type { UpgradePackagePolicyDryRunResponseItem } from '../../common'; + +import { PACKAGES_SAVED_OBJECT_TYPE, PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../constants'; + +import type { Installation, PackagePolicy } from '../types'; import { appContextService } from './app_context'; -import { getInstallation } from './epm/packages'; +import { getInstallations } from './epm/packages'; import { packagePolicyService } from './package_policy'; export interface UpgradeManagedPackagePoliciesResult { @@ -26,78 +30,87 @@ export interface UpgradeManagedPackagePoliciesResult { */ export const upgradeManagedPackagePolicies = async ( soClient: SavedObjectsClientContract, - esClient: ElasticsearchClient, - packagePolicyIds: string[] + esClient: ElasticsearchClient ): Promise => { + appContextService + .getLogger() + .debug('Running required package policies upgrades for managed policies'); const results: UpgradeManagedPackagePoliciesResult[] = []; - for (const packagePolicyId of packagePolicyIds) { - const packagePolicy = await packagePolicyService.get(soClient, packagePolicyId); - - if (!packagePolicy || !packagePolicy.package) { - continue; - } - - const installedPackage = await getInstallation({ - savedObjectsClient: soClient, - pkgName: packagePolicy.package.name, - }); + const installedPackages = await getInstallations(soClient, { + filter: `${PACKAGES_SAVED_OBJECT_TYPE}.attributes.install_status:installed AND ${PACKAGES_SAVED_OBJECT_TYPE}.attributes.keep_policies_up_to_date:true`, + }); - if (!installedPackage) { - results.push({ - packagePolicyId, - errors: [`${packagePolicy.package.name} is not installed`], - }); - - continue; - } - - if (shouldUpgradePolicies(packagePolicy.package.version, installedPackage)) { - // Since upgrades don't report diffs/errors, we need to perform a dry run first in order - // to notify the user of any granular policy upgrade errors that occur during Fleet's - // preconfiguration check - const dryRunResults = await packagePolicyService.getUpgradeDryRunDiff( - soClient, - packagePolicyId - ); - - if (dryRunResults.hasErrors) { - const errors = dryRunResults.diff - ? dryRunResults.diff?.[1].errors - : [dryRunResults.body?.message]; - - appContextService - .getLogger() - .error( - new Error( - `Error upgrading package policy ${packagePolicyId}: ${JSON.stringify(errors)}` - ) - ); - - results.push({ packagePolicyId, diff: dryRunResults.diff, errors }); - continue; - } + for (const { attributes: installedPackage } of installedPackages.saved_objects) { + const packagePolicies = await getPackagePoliciesNotMatchingVersion( + soClient, + installedPackage.name, + installedPackage.version + ); - try { - await packagePolicyService.upgrade(soClient, esClient, [packagePolicyId]); - results.push({ packagePolicyId, diff: dryRunResults.diff, errors: [] }); - } catch (error) { - results.push({ packagePolicyId, diff: dryRunResults.diff, errors: [error] }); + for (const packagePolicy of packagePolicies) { + if (isPolicyVersionLtInstalledVersion(packagePolicy, installedPackage)) { + await upgradePackagePolicy(soClient, esClient, packagePolicy.id, results); } } } - return results; }; -export function shouldUpgradePolicies( - packagePolicyPackageVersion: string, +async function getPackagePoliciesNotMatchingVersion( + soClient: SavedObjectsClientContract, + pkgName: string, + pkgVersion: string +): Promise { + return ( + await packagePolicyService.list(soClient, { + page: 1, + perPage: 1000, + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${pkgName} AND NOT ${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.version:${pkgVersion}`, + }) + ).items; +} + +function isPolicyVersionLtInstalledVersion( + packagePolicy: PackagePolicy, installedPackage: Installation ): boolean { - const isPolicyVersionGteInstalledVersion = semverGte( - packagePolicyPackageVersion, - installedPackage.version + return ( + packagePolicy.package !== undefined && + semverLt(packagePolicy.package.version, installedPackage.version) ); +} + +async function upgradePackagePolicy( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + packagePolicyId: string, + results: UpgradeManagedPackagePoliciesResult[] +) { + // Since upgrades don't report diffs/errors, we need to perform a dry run first in order + // to notify the user of any granular policy upgrade errors that occur during Fleet's + // preconfiguration check + const dryRunResults = await packagePolicyService.getUpgradeDryRunDiff(soClient, packagePolicyId); + + if (dryRunResults.hasErrors) { + const errors = dryRunResults.diff + ? dryRunResults.diff?.[1].errors + : [dryRunResults.body?.message]; + + appContextService + .getLogger() + .error( + new Error(`Error upgrading package policy ${packagePolicyId}: ${JSON.stringify(errors)}`) + ); - return !isPolicyVersionGteInstalledVersion && !!installedPackage.keep_policies_up_to_date; + results.push({ packagePolicyId, diff: dryRunResults.diff, errors }); + return; + } + + try { + await packagePolicyService.upgrade(soClient, esClient, [packagePolicyId]); + results.push({ packagePolicyId, diff: dryRunResults.diff, errors: [] }); + } catch (error) { + results.push({ packagePolicyId, diff: dryRunResults.diff, errors: [error] }); + } } diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.ts b/x-pack/plugins/fleet/server/services/preconfiguration.ts index e9c079d435e7e..2e0c3c7722b13 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.ts @@ -22,7 +22,7 @@ import type { PackagePolicy, } from '../../common'; import { PRECONFIGURATION_LATEST_KEYWORD } from '../../common'; -import { SO_SEARCH_LIMIT, normalizeHostsForAgents } from '../../common'; +import { normalizeHostsForAgents } from '../../common'; import { PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE } from '../constants'; import { escapeSearchQueryPhrase } from './saved_object'; @@ -32,10 +32,9 @@ import { ensurePackagesCompletedInstall } from './epm/packages/install'; import { bulkInstallPackages } from './epm/packages/bulk_install_packages'; import { agentPolicyService, addPackageToAgentPolicy } from './agent_policy'; import type { InputsOverride } from './package_policy'; -import { preconfigurePackageInputs, packagePolicyService } from './package_policy'; +import { preconfigurePackageInputs } from './package_policy'; import { appContextService } from './app_context'; import type { UpgradeManagedPackagePoliciesResult } from './managed_package_policies'; -import { upgradeManagedPackagePolicies } from './managed_package_policies'; import { outputService } from './output'; interface PreconfigurationResult { @@ -357,18 +356,6 @@ export async function ensurePreconfiguredPackagesAndPolicies( } } - // Handle automatic package policy upgrades for managed packages and package with - // the `keep_policies_up_to_date` setting enabled - const allPackagePolicyIds = await packagePolicyService.listIds(soClient, { - page: 1, - perPage: SO_SEARCH_LIMIT, - }); - const packagePolicyUpgradeResults = await upgradeManagedPackagePolicies( - soClient, - esClient, - allPackagePolicyIds.items - ); - return { policies: fulfilledPolicies.map((p) => p.policy @@ -385,7 +372,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( } ), packages: fulfilledPackages.map((pkg) => ('version' in pkg ? pkgToPkgKey(pkg) : pkg.name)), - nonFatalErrors: [...rejectedPackages, ...rejectedPolicies, ...packagePolicyUpgradeResults], + nonFatalErrors: [...rejectedPackages, ...rejectedPolicies], }; } diff --git a/x-pack/plugins/fleet/server/services/setup.test.ts b/x-pack/plugins/fleet/server/services/setup.test.ts index caa2e1b3ffcee..02972648e80d2 100644 --- a/x-pack/plugins/fleet/server/services/setup.test.ts +++ b/x-pack/plugins/fleet/server/services/setup.test.ts @@ -5,29 +5,56 @@ * 2.0. */ +import type { SavedObjectsClientContract } from 'kibana/server'; +import type { ElasticsearchClientMock } from 'src/core/server/mocks'; + import { createAppContextStartContractMock, xpackMocks } from '../mocks'; +import { ensurePreconfiguredPackagesAndPolicies } from '.'; + import { appContextService } from './app_context'; +import { getInstallations } from './epm/packages'; +import { upgradeManagedPackagePolicies } from './managed_package_policies'; import { setupFleet } from './setup'; -const mockedMethodThrowsError = () => - jest.fn().mockImplementation(() => { +jest.mock('./preconfiguration'); +jest.mock('./settings'); +jest.mock('./output'); +jest.mock('./epm/packages'); +jest.mock('./managed_package_policies'); + +const mockedMethodThrowsError = (mockFn: jest.Mock) => + mockFn.mockImplementation(() => { throw new Error('SO method mocked to throw'); }); class CustomTestError extends Error {} -const mockedMethodThrowsCustom = () => - jest.fn().mockImplementation(() => { +const mockedMethodThrowsCustom = (mockFn: jest.Mock) => + mockFn.mockImplementation(() => { throw new CustomTestError('method mocked to throw'); }); describe('setupFleet', () => { let context: ReturnType; + let soClient: jest.Mocked; + let esClient: ElasticsearchClientMock; beforeEach(async () => { context = xpackMocks.createRequestHandlerContext(); // prevents `Logger not set.` and other appContext errors appContextService.start(createAppContextStartContractMock()); + soClient = context.core.savedObjects.client; + esClient = context.core.elasticsearch.client.asInternalUser; + + (getInstallations as jest.Mock).mockResolvedValueOnce({ + saved_objects: [], + }); + + (ensurePreconfiguredPackagesAndPolicies as jest.Mock).mockResolvedValue({ + nonFatalErrors: [], + }); + + (upgradeManagedPackagePolicies as jest.Mock).mockResolvedValue([]); }); afterEach(async () => { @@ -37,12 +64,7 @@ describe('setupFleet', () => { describe('should reject with any error thrown underneath', () => { it('SO client throws plain Error', async () => { - const soClient = context.core.savedObjects.client; - soClient.create = mockedMethodThrowsError(); - soClient.find = mockedMethodThrowsError(); - soClient.get = mockedMethodThrowsError(); - soClient.update = mockedMethodThrowsError(); - const esClient = context.core.elasticsearch.client.asInternalUser; + mockedMethodThrowsError(upgradeManagedPackagePolicies as jest.Mock); const setupPromise = setupFleet(soClient, esClient); await expect(setupPromise).rejects.toThrow('SO method mocked to throw'); @@ -50,16 +72,53 @@ describe('setupFleet', () => { }); it('SO client throws other error', async () => { - const soClient = context.core.savedObjects.client; - soClient.create = mockedMethodThrowsCustom(); - soClient.find = mockedMethodThrowsCustom(); - soClient.get = mockedMethodThrowsCustom(); - soClient.update = mockedMethodThrowsCustom(); - const esClient = context.core.elasticsearch.client.asInternalUser; + mockedMethodThrowsCustom(upgradeManagedPackagePolicies as jest.Mock); const setupPromise = setupFleet(soClient, esClient); await expect(setupPromise).rejects.toThrow('method mocked to throw'); await expect(setupPromise).rejects.toThrow(CustomTestError); }); }); + + it('should not return non fatal errors when upgrade result has no errors', async () => { + (upgradeManagedPackagePolicies as jest.Mock).mockResolvedValue([ + { + errors: [], + packagePolicyId: '1', + }, + ]); + + const result = await setupFleet(soClient, esClient); + + expect(result).toEqual({ + isInitialized: true, + nonFatalErrors: [], + }); + }); + + it('should return non fatal errors when upgrade result has errors', async () => { + (upgradeManagedPackagePolicies as jest.Mock).mockResolvedValue([ + { + errors: [{ key: 'key', message: 'message' }], + packagePolicyId: '1', + }, + ]); + + const result = await setupFleet(soClient, esClient); + + expect(result).toEqual({ + isInitialized: true, + nonFatalErrors: [ + { + errors: [ + { + key: 'key', + message: 'message', + }, + ], + packagePolicyId: '1', + }, + ], + }); + }); }); diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index b99b73be8588c..e7ba627f5cbdf 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -33,6 +33,7 @@ import { getInstallations, installPackage } from './epm/packages'; import { isPackageInstalled } from './epm/packages/install'; import { pkgToPkgKey } from './epm/registry'; import type { UpgradeManagedPackagePoliciesResult } from './managed_package_policies'; +import { upgradeManagedPackagePolicies } from './managed_package_policies'; export interface SetupStatus { isInitialized: boolean; @@ -98,14 +99,21 @@ async function createSetupSideEffects( logger.debug('Setting up initial Fleet packages'); - const { nonFatalErrors } = await ensurePreconfiguredPackagesAndPolicies( - soClient, - esClient, - policies, - packages, - defaultOutput, - DEFAULT_SPACE_ID - ); + const { nonFatalErrors: preconfiguredPackagesNonFatalErrors } = + await ensurePreconfiguredPackagesAndPolicies( + soClient, + esClient, + policies, + packages, + defaultOutput, + DEFAULT_SPACE_ID + ); + + const packagePolicyUpgradeErrors = ( + await upgradeManagedPackagePolicies(soClient, esClient) + ).filter((result) => (result.errors ?? []).length > 0); + + const nonFatalErrors = [...preconfiguredPackagesNonFatalErrors, ...packagePolicyUpgradeErrors]; logger.debug('Cleaning up Fleet outputs'); await cleanPreconfiguredOutputs(soClient, outputsOrUndefined ?? []); diff --git a/x-pack/test/fleet_api_integration/apis/epm/setup.ts b/x-pack/test/fleet_api_integration/apis/epm/setup.ts index eb29920b83036..253e19c2db1b1 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/setup.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/setup.ts @@ -27,7 +27,7 @@ export default function (providerContext: FtrProviderContext) { const url = '/api/fleet/epm/packages/endpoint'; await supertest.delete(url).set('kbn-xsrf', 'xxxx').send({ force: true }).expect(200); await supertest - .post(`${url}-${oldEndpointVersion}`) + .post(`${url}/${oldEndpointVersion}`) .set('kbn-xsrf', 'xxxx') .send({ force: true }) .expect(200); @@ -52,6 +52,75 @@ export default function (providerContext: FtrProviderContext) { }); }); + describe('package policy upgrade on setup', () => { + let agentPolicyId: string; + before(async function () { + const { body: agentPolicyResponse } = await supertest + .post(`/api/fleet/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'Test policy', + namespace: 'default', + }); + agentPolicyId = agentPolicyResponse.item.id; + }); + + after(async function () { + await supertest + .post(`/api/fleet/agent_policies/delete`) + .set('kbn-xsrf', 'xxxx') + .send({ agentPolicyId }); + }); + + it('should upgrade package policy on setup if keep policies up to date set to true', async () => { + const oldVersion = '1.9.0'; + await supertest + .post(`/api/fleet/epm/packages/system/${oldVersion}`) + .set('kbn-xsrf', 'xxxx') + .send({ force: true }) + .expect(200); + await supertest + .put(`/api/fleet/epm/packages/system/${oldVersion}`) + .set('kbn-xsrf', 'xxxx') + .send({ keepPoliciesUpToDate: true }) + .expect(200); + await supertest + .post('/api/fleet/package_policies') + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'system-1', + namespace: 'default', + policy_id: agentPolicyId, + package: { name: 'system', version: oldVersion }, + inputs: [], + }) + .expect(200); + + let { body } = await supertest + .get(`/api/fleet/epm/packages/system/${oldVersion}`) + .expect(200); + const latestVersion = body.item.latestVersion; + log.info(`System package latest version: ${latestVersion}`); + // make sure we're actually doing an upgrade + expect(latestVersion).not.eql(oldVersion); + + ({ body } = await supertest + .post(`/api/fleet/epm/packages/system/${latestVersion}`) + .set('kbn-xsrf', 'xxxx') + .expect(200)); + + await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxxx').expect(200); + + ({ body } = await supertest + .get('/api/fleet/package_policies') + .set('kbn-xsrf', 'xxxx') + .expect(200)); + expect(body.items.find((pkg: any) => pkg.name === 'system-1').package.version).to.equal( + latestVersion + ); + }); + }); + it('does not fail when package is no longer compatible in registry', async () => { await supertest .post(`/api/fleet/epm/packages/deprecated/0.1.0`)