From f96af1660ca41ec69135ca4db25f8048cbbee298 Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Wed, 23 Feb 2022 16:50:44 +0100 Subject: [PATCH] [Fleet] optimizing package policy upgrade and dry run (#126088) * optimizing package policy upgrade and dry run * optimizing package policy upgrade and dry run * optimizing package policy upgrade and dry run * missing params * fixed tests * removed unused import * fixed tests * fixed checks * reduced duplication, separated validate logic * reduced duplication, separated validate logic Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/fleet/server/mocks/index.ts | 2 - .../routes/package_policy/handlers.test.ts | 9 +- .../services/managed_package_policies.test.ts | 42 +- .../services/managed_package_policies.ts | 29 +- .../server/services/package_policy.test.ts | 56 +- .../fleet/server/services/package_policy.ts | 483 +++++++++++------- 6 files changed, 371 insertions(+), 250 deletions(-) diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 7c4c822bd550a..8e6155e6bf7c2 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -99,9 +99,7 @@ export const xpackMocks = { export const createPackagePolicyServiceMock = (): jest.Mocked => { return { - _compilePackagePolicyInputs: jest.fn(), buildPackagePolicyFromPackage: jest.fn(), - buildPackagePolicyFromPackageWithVersion: jest.fn(), bulkCreate: jest.fn(), create: jest.fn(), delete: jest.fn(), diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts index a178422e20ee5..25d0f5c577667 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts @@ -28,21 +28,16 @@ import type { PackagePolicy } from '../../types'; import { registerRoutes } from './index'; -type PackagePolicyServicePublicInterface = Omit< - PackagePolicyServiceInterface, - 'getUpgradePackagePolicyInfo' ->; - const packagePolicyServiceMock = packagePolicyService as jest.Mocked; jest.mock( '../../services/package_policy', (): { - packagePolicyService: jest.Mocked; + packagePolicyService: jest.Mocked; } => { return { packagePolicyService: { - _compilePackagePolicyInputs: jest.fn((registryPkgInfo, packageInfo, vars, dataInputs) => + _compilePackagePolicyInputs: jest.fn((packageInfo, vars, dataInputs) => Promise.resolve(dataInputs) ), buildPackagePolicyFromPackage: jest.fn(), 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 402b65334121a..6cbb21a37a196 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 @@ -45,25 +45,24 @@ describe('upgradeManagedPackagePolicies', () => { it('should upgrade policies for managed package', async () => { const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; const soClient = savedObjectsClientMock.create(); + const packagePolicy = { + id: 'managed-package-id', + inputs: {}, + version: '', + revision: 1, + updated_at: '', + updated_by: '', + created_at: '', + created_by: '', + package: { + name: 'managed-package', + title: 'Managed Package', + version: '0.0.1', + }, + }; (packagePolicyService.list as jest.Mock).mockResolvedValueOnce({ - items: [ - { - id: 'managed-package-id', - inputs: {}, - version: '', - revision: 1, - updated_at: '', - updated_by: '', - created_at: '', - created_by: '', - package: { - name: 'managed-package', - title: 'Managed Package', - version: '0.0.1', - }, - }, - ], + items: [packagePolicy], }); (packagePolicyService.getUpgradeDryRunDiff as jest.Mock).mockResolvedValueOnce({ @@ -89,7 +88,14 @@ describe('upgradeManagedPackagePolicies', () => { { packagePolicyId: 'managed-package-id', diff: [{ id: 'foo' }, { id: 'bar' }], errors: [] }, ]); - expect(packagePolicyService.upgrade).toBeCalledWith(soClient, esClient, ['managed-package-id']); + expect(packagePolicyService.upgrade).toBeCalledWith( + soClient, + esClient, + ['managed-package-id'], + undefined, + packagePolicy, + '1.0.0' + ); }); it('should not upgrade policy if newer than installed package version', async () => { 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 2c4b326d56532..e17ca5ebcd1de 100644 --- a/x-pack/plugins/fleet/server/services/managed_package_policies.ts +++ b/x-pack/plugins/fleet/server/services/managed_package_policies.ts @@ -50,7 +50,7 @@ export const upgradeManagedPackagePolicies = async ( for (const packagePolicy of packagePolicies) { if (isPolicyVersionLtInstalledVersion(packagePolicy, installedPackage)) { - await upgradePackagePolicy(soClient, esClient, packagePolicy.id, results); + await upgradePackagePolicy(soClient, esClient, packagePolicy, installedPackage, results); } } } @@ -84,13 +84,19 @@ function isPolicyVersionLtInstalledVersion( async function upgradePackagePolicy( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, - packagePolicyId: string, + packagePolicy: PackagePolicy, + installedPackage: Installation, 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); + const dryRunResults = await packagePolicyService.getUpgradeDryRunDiff( + soClient, + packagePolicy.id, + packagePolicy, + installedPackage.version + ); if (dryRunResults.hasErrors) { const errors = dryRunResults.diff @@ -100,17 +106,24 @@ async function upgradePackagePolicy( appContextService .getLogger() .error( - new Error(`Error upgrading package policy ${packagePolicyId}: ${JSON.stringify(errors)}`) + new Error(`Error upgrading package policy ${packagePolicy.id}: ${JSON.stringify(errors)}`) ); - results.push({ packagePolicyId, diff: dryRunResults.diff, errors }); + results.push({ packagePolicyId: packagePolicy.id, diff: dryRunResults.diff, errors }); return; } try { - await packagePolicyService.upgrade(soClient, esClient, [packagePolicyId]); - results.push({ packagePolicyId, diff: dryRunResults.diff, errors: [] }); + await packagePolicyService.upgrade( + soClient, + esClient, + [packagePolicy.id], + undefined, + packagePolicy, + installedPackage.version + ); + results.push({ packagePolicyId: packagePolicy.id, diff: dryRunResults.diff, errors: [] }); } catch (error) { - results.push({ packagePolicyId, diff: dryRunResults.diff, errors: [error] }); + results.push({ packagePolicyId: packagePolicy.id, diff: dryRunResults.diff, errors: [error] }); } } diff --git a/x-pack/plugins/fleet/server/services/package_policy.test.ts b/x-pack/plugins/fleet/server/services/package_policy.test.ts index 40a60e2b525cc..704071154ac94 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.test.ts @@ -39,7 +39,6 @@ import type { NewPackagePolicy, NewPackagePolicyInput, PackagePolicyPackage, - RegistryPackage, } from '../../common'; import { packageToPackagePolicy } from '../../common'; @@ -50,9 +49,11 @@ import { updatePackageInputs, packagePolicyService, _applyIndexPrivileges, + _compilePackagePolicyInputs, } from './package_policy'; import { appContextService } from './app_context'; -import { fetchInfo } from './epm/registry'; + +import { getPackageInfo } from './epm/packages'; async function mockedGetAssetsData(_a: any, _b: any, dataset: string) { if (dataset === 'dataset1') { @@ -113,10 +114,6 @@ async function mockedGetPackageInfo(params: any) { return Promise.resolve(pkg); } -function mockedRegistryInfo(): RegistryPackage { - return {} as RegistryPackage; -} - jest.mock('./epm/packages/assets', () => { return { getAssetsData: mockedGetAssetsData, @@ -125,7 +122,7 @@ jest.mock('./epm/packages/assets', () => { jest.mock('./epm/packages', () => { return { - getPackageInfo: mockedGetPackageInfo, + getPackageInfo: jest.fn().mockImplementation(mockedGetPackageInfo), getInstallation: mockedGetInstallation, }; }); @@ -170,18 +167,12 @@ jest.mock('./upgrade_sender', () => { }; }); -const mockedFetchInfo = fetchInfo as jest.Mock>; - type CombinedExternalCallback = PutPackagePolicyUpdateCallback | PostPackagePolicyCreateCallback; describe('Package policy service', () => { - beforeEach(() => { - mockedFetchInfo.mockResolvedValue({} as RegistryPackage); - }); describe('_compilePackagePolicyInputs', () => { it('should work with config variables from the stream', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { data_streams: [ { @@ -244,8 +235,7 @@ describe('Package policy service', () => { }); it('should work with a two level dataset name', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { data_streams: [ { @@ -297,8 +287,7 @@ describe('Package policy service', () => { }); it('should work with config variables at the input level', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { data_streams: [ { @@ -361,8 +350,7 @@ describe('Package policy service', () => { }); it('should work with config variables at the package level', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { data_streams: [ { @@ -430,8 +418,7 @@ describe('Package policy service', () => { }); it('should work with an input with a template and no streams', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { data_streams: [], policy_templates: [ @@ -473,8 +460,7 @@ describe('Package policy service', () => { }); it('should work with an input with a template and streams', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { data_streams: [ { @@ -579,8 +565,7 @@ describe('Package policy service', () => { }); it('should work with a package without input', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { policy_templates: [ { @@ -596,8 +581,7 @@ describe('Package policy service', () => { }); it('should work with a package with a empty inputs array', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { policy_templates: [ { @@ -906,14 +890,16 @@ describe('Package policy service', () => { ...mockPackagePolicy, inputs: [], }; - - mockedFetchInfo.mockResolvedValue({ - elasticsearch: { - privileges: { - cluster: ['monitor'], + (getPackageInfo as jest.Mock).mockImplementation(async (params) => { + return Promise.resolve({ + ...(await mockedGetPackageInfo(params)), + elasticsearch: { + privileges: { + cluster: ['monitor'], + }, }, - }, - } as RegistryPackage); + } as PackageInfo); + }); savedObjectsClient.get.mockResolvedValue({ id: 'test', diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 0a4ab8e2f26d5..1acbce4f22cd1 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -41,7 +41,7 @@ import type { ListResult, UpgradePackagePolicyDryRunResponseItem, RegistryDataStream, - InstallablePackage, + PackagePolicyPackage, } from '../../common'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../constants'; import { @@ -62,7 +62,6 @@ import type { ExternalCallback } from '..'; import { agentPolicyService } from './agent_policy'; import { outputService } from './output'; -import * as Registry from './epm/registry'; import { getPackageInfo, getInstallation, ensureInstalledPackage } from './epm/packages'; import { getAssetsData } from './epm/packages/assets'; import { compileTemplate } from './epm/agent/agent'; @@ -71,7 +70,6 @@ import { appContextService } from '.'; import { removeOldAssets } from './epm/packages/cleanup'; import type { PackageUpdateEvent, UpdateEventType } from './upgrade_sender'; import { sendTelemetryEvents } from './upgrade_sender'; -import { getArchivePackage } from './epm/archive'; export type InputsOverride = Partial & { vars?: Array; @@ -88,7 +86,7 @@ export const DATA_STREAM_ALLOWED_INDEX_PRIVILEGES = new Set([ 'read_cross_cluster', ]); -class PackagePolicyService { +class PackagePolicyService implements PackagePolicyServiceInterface { public async create( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, @@ -164,21 +162,9 @@ class PackagePolicyService { } validatePackagePolicyOrThrow(packagePolicy, pkgInfo); - let installablePackage: InstallablePackage | undefined = - getArchivePackage(pkgInfo)?.packageInfo; + inputs = await _compilePackagePolicyInputs(pkgInfo, packagePolicy.vars || {}, inputs); - if (!installablePackage) { - installablePackage = await Registry.fetchInfo(pkgInfo.name, pkgInfo.version); - } - - inputs = await this._compilePackagePolicyInputs( - installablePackage, - pkgInfo, - packagePolicy.vars || {}, - inputs - ); - - elasticsearch = installablePackage.elasticsearch; + elasticsearch = pkgInfo.elasticsearch; } const isoDate = new Date().toISOString(); @@ -409,20 +395,8 @@ class PackagePolicyService { validatePackagePolicyOrThrow(packagePolicy, pkgInfo); - let installablePackage: InstallablePackage | undefined = - getArchivePackage(pkgInfo)?.packageInfo; - - if (!installablePackage) { - installablePackage = await Registry.fetchInfo(pkgInfo.name, pkgInfo.version); - } - - inputs = await this._compilePackagePolicyInputs( - installablePackage, - pkgInfo, - packagePolicy.vars || {}, - inputs - ); - elasticsearch = installablePackage.elasticsearch; + inputs = await _compilePackagePolicyInputs(pkgInfo, packagePolicy.vars || {}, inputs); + elasticsearch = pkgInfo.elasticsearch; } await soClient.update( @@ -526,8 +500,52 @@ class PackagePolicyService { return result; } - public async getUpgradePackagePolicyInfo(soClient: SavedObjectsClientContract, id: string) { - const packagePolicy = await this.get(soClient, id); + // TODO should move out, public only for unit tests + public async getUpgradePackagePolicyInfo( + soClient: SavedObjectsClientContract, + id: string, + packagePolicy?: PackagePolicy, + pkgVersion?: string + ): Promise<{ packagePolicy: PackagePolicy; packageInfo: PackageInfo }> { + if (!packagePolicy) { + packagePolicy = (await this.get(soClient, id)) ?? undefined; + } + if (!pkgVersion && packagePolicy) { + const installedPackage = await getInstallation({ + savedObjectsClient: soClient, + pkgName: packagePolicy.package!.name, + }); + if (!installedPackage) { + throw new IngestManagerError( + i18n.translate('xpack.fleet.packagePolicy.packageNotInstalledError', { + defaultMessage: 'Package {name} is not installed', + values: { + name: packagePolicy.package!.name, + }, + }) + ); + } + pkgVersion = installedPackage.version; + } + let packageInfo: PackageInfo | undefined; + if (packagePolicy) { + packageInfo = await getPackageInfo({ + savedObjectsClient: soClient, + pkgName: packagePolicy!.package!.name, + pkgVersion: pkgVersion ?? '', + }); + } + + this.validateUpgradePackagePolicy(id, packageInfo, packagePolicy); + + return { packagePolicy: packagePolicy!, packageInfo: packageInfo! }; + } + + private validateUpgradePackagePolicy( + id: string, + packageInfo?: PackageInfo, + packagePolicy?: PackagePolicy + ) { if (!packagePolicy) { throw new IngestManagerError( i18n.translate('xpack.fleet.packagePolicy.policyNotFoundError', { @@ -546,28 +564,6 @@ class PackagePolicyService { ); } - const installedPackage = await getInstallation({ - savedObjectsClient: soClient, - pkgName: packagePolicy.package.name, - }); - - if (!installedPackage) { - throw new IngestManagerError( - i18n.translate('xpack.fleet.packagePolicy.packageNotInstalledError', { - defaultMessage: 'Package {name} is not installed', - values: { - name: packagePolicy.package.name, - }, - }) - ); - } - - const packageInfo = await getPackageInfo({ - savedObjectsClient: soClient, - pkgName: packagePolicy.package.name, - pkgVersion: installedPackage?.version ?? '', - }); - const isInstalledVersionLessThanPolicyVersion = semverLt( packageInfo?.version ?? '', packagePolicy.package.version @@ -586,59 +582,29 @@ class PackagePolicyService { }) ); } - - return { - packagePolicy: packagePolicy as Required, - packageInfo, - }; } public async upgrade( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, ids: string[], - options?: { user?: AuthenticatedUser } + options?: { user?: AuthenticatedUser }, + packagePolicy?: PackagePolicy, + pkgVersion?: string ): Promise { const result: UpgradePackagePolicyResponse = []; for (const id of ids) { try { - const { packagePolicy, packageInfo } = await this.getUpgradePackagePolicyInfo(soClient, id); - - const updatePackagePolicy = updatePackageInputs( - { - ...omit(packagePolicy, 'id'), - inputs: packagePolicy.inputs, - package: { - ...packagePolicy.package, - version: packageInfo.version, - }, - }, - packageInfo, - packageToPackagePolicyInputs(packageInfo) as InputsOverride[] - ); - const registryPkgInfo = await Registry.fetchInfo(packageInfo.name, packageInfo.version); - updatePackagePolicy.inputs = await this._compilePackagePolicyInputs( - registryPkgInfo, - packageInfo, - updatePackagePolicy.vars || {}, - updatePackagePolicy.inputs as PackagePolicyInput[] - ); - updatePackagePolicy.elasticsearch = registryPkgInfo.elasticsearch; - - await this.update( + let packageInfo: PackageInfo; + ({ packagePolicy, packageInfo } = await this.getUpgradePackagePolicyInfo( soClient, - esClient, - id, - updatePackagePolicy, - options, - packagePolicy.package.version - ); - result.push({ id, - name: packagePolicy.name, - success: true, - }); + packagePolicy, + pkgVersion + )); + + await this.doUpgrade(soClient, esClient, id, packagePolicy!, result, packageInfo, options); } catch (error) { result.push({ id, @@ -651,67 +617,65 @@ class PackagePolicyService { return result; } - public async getUpgradeDryRunDiff( + private async doUpgrade( soClient: SavedObjectsClientContract, - id: string - ): Promise { - try { - const { packagePolicy, packageInfo } = await this.getUpgradePackagePolicyInfo(soClient, id); - - const updatedPackagePolicy = updatePackageInputs( - { - ...omit(packagePolicy, 'id'), - inputs: packagePolicy.inputs, - package: { - ...packagePolicy.package, - version: packageInfo.version, - }, + esClient: ElasticsearchClient, + id: string, + packagePolicy: PackagePolicy, + result: UpgradePackagePolicyResponse, + packageInfo: PackageInfo, + options?: { user?: AuthenticatedUser } + ) { + const updatePackagePolicy = updatePackageInputs( + { + ...omit(packagePolicy, 'id'), + inputs: packagePolicy.inputs, + package: { + ...packagePolicy.package!, + version: packageInfo.version, }, - packageInfo, - packageToPackagePolicyInputs(packageInfo) as InputsOverride[], - true - ); - const registryPkgInfo = await Registry.fetchInfo(packageInfo.name, packageInfo.version); - updatedPackagePolicy.inputs = await this._compilePackagePolicyInputs( - registryPkgInfo, - packageInfo, - updatedPackagePolicy.vars || {}, - updatedPackagePolicy.inputs as PackagePolicyInput[] - ); - updatedPackagePolicy.elasticsearch = registryPkgInfo.elasticsearch; + }, + packageInfo, + packageToPackagePolicyInputs(packageInfo) as InputsOverride[] + ); + updatePackagePolicy.inputs = await _compilePackagePolicyInputs( + packageInfo, + updatePackagePolicy.vars || {}, + updatePackagePolicy.inputs as PackagePolicyInput[] + ); + updatePackagePolicy.elasticsearch = packageInfo.elasticsearch; - const hasErrors = 'errors' in updatedPackagePolicy; + await this.update( + soClient, + esClient, + id, + updatePackagePolicy, + options, + packagePolicy.package!.version + ); + result.push({ + id, + name: packagePolicy.name, + success: true, + }); + } - if (packagePolicy.package.version !== packageInfo.version) { - const upgradeTelemetry: PackageUpdateEvent = { - packageName: packageInfo.name, - currentVersion: packagePolicy.package.version, - newVersion: packageInfo.version, - status: hasErrors ? 'failure' : 'success', - error: hasErrors ? updatedPackagePolicy.errors : undefined, - dryRun: true, - eventType: 'package-policy-upgrade' as UpdateEventType, - }; - sendTelemetryEvents( - appContextService.getLogger(), - appContextService.getTelemetryEventsSender(), - upgradeTelemetry - ); - appContextService - .getLogger() - .info( - `Package policy upgrade dry run ${ - hasErrors ? 'resulted in errors' : 'ran successfully' - }` - ); - appContextService.getLogger().debug(JSON.stringify(upgradeTelemetry)); - } + public async getUpgradeDryRunDiff( + soClient: SavedObjectsClientContract, + id: string, + packagePolicy?: PackagePolicy, + pkgVersion?: string + ): Promise { + try { + let packageInfo: PackageInfo; + ({ packagePolicy, packageInfo } = await this.getUpgradePackagePolicyInfo( + soClient, + id, + packagePolicy, + pkgVersion + )); - return { - name: updatedPackagePolicy.name, - diff: [packagePolicy, updatedPackagePolicy], - hasErrors, - }; + return this.calculateDiff(packagePolicy, packageInfo); } catch (error) { return { hasErrors: true, @@ -720,6 +684,76 @@ class PackagePolicyService { } } + private async calculateDiff( + packagePolicy: PackagePolicy, + packageInfo: PackageInfo + ): Promise { + const updatedPackagePolicy = updatePackageInputs( + { + ...omit(packagePolicy, 'id'), + inputs: packagePolicy.inputs, + package: { + ...packagePolicy.package!, + version: packageInfo.version, + }, + }, + packageInfo, + packageToPackagePolicyInputs(packageInfo) as InputsOverride[], + true + ); + updatedPackagePolicy.inputs = await _compilePackagePolicyInputs( + packageInfo, + updatedPackagePolicy.vars || {}, + updatedPackagePolicy.inputs as PackagePolicyInput[] + ); + updatedPackagePolicy.elasticsearch = packageInfo.elasticsearch; + + const hasErrors = 'errors' in updatedPackagePolicy; + + this.sendUpgradeTelemetry( + packagePolicy.package!, + packageInfo.version, + hasErrors, + updatedPackagePolicy.errors + ); + + return { + name: updatedPackagePolicy.name, + diff: [packagePolicy, updatedPackagePolicy], + hasErrors, + }; + } + + private sendUpgradeTelemetry( + packagePolicyPackage: PackagePolicyPackage, + latestVersion: string, + hasErrors: boolean, + errors?: Array<{ key: string | undefined; message: string }> + ) { + if (packagePolicyPackage.version !== latestVersion) { + const upgradeTelemetry: PackageUpdateEvent = { + packageName: packagePolicyPackage.name, + currentVersion: packagePolicyPackage.version, + newVersion: latestVersion, + status: hasErrors ? 'failure' : 'success', + error: hasErrors ? errors : undefined, + dryRun: true, + eventType: 'package-policy-upgrade' as UpdateEventType, + }; + sendTelemetryEvents( + appContextService.getLogger(), + appContextService.getTelemetryEventsSender(), + upgradeTelemetry + ); + appContextService + .getLogger() + .info( + `Package policy upgrade dry run ${hasErrors ? 'resulted in errors' : 'ran successfully'}` + ); + appContextService.getLogger().debug(JSON.stringify(upgradeTelemetry)); + } + } + public async enrichPolicyWithDefaultsFromPackage( soClient: SavedObjectsClientContract, newPolicy: NewPackagePolicy @@ -775,7 +809,7 @@ class PackagePolicyService { return newPackagePolicy; } - public async buildPackagePolicyFromPackageWithVersion( + private async buildPackagePolicyFromPackageWithVersion( soClient: SavedObjectsClientContract, pkgName: string, pkgVersion: string @@ -813,30 +847,6 @@ class PackagePolicyService { } } - public async _compilePackagePolicyInputs( - installablePackage: InstallablePackage, - pkgInfo: PackageInfo, - vars: PackagePolicy['vars'], - inputs: PackagePolicyInput[] - ): Promise { - const inputsPromises = inputs.map(async (input) => { - const compiledInput = await _compilePackagePolicyInput(pkgInfo, vars, input); - const compiledStreams = await _compilePackageStreams( - installablePackage, - pkgInfo, - vars, - input - ); - return { - ...input, - compiled_input: compiledInput, - streams: compiledStreams, - }; - }); - - return Promise.all(inputsPromises); - } - public async runExternalCallbacks( externalCallbackType: A, packagePolicy: A extends 'postPackagePolicyDelete' @@ -936,6 +946,24 @@ function assignStreamIdToInput(packagePolicyId: string, input: NewPackagePolicyI }; } +export async function _compilePackagePolicyInputs( + pkgInfo: PackageInfo, + vars: PackagePolicy['vars'], + inputs: PackagePolicyInput[] +): Promise { + const inputsPromises = inputs.map(async (input) => { + const compiledInput = await _compilePackagePolicyInput(pkgInfo, vars, input); + const compiledStreams = await _compilePackageStreams(pkgInfo, vars, input); + return { + ...input, + compiled_input: compiledInput, + streams: compiledStreams, + }; + }); + + return Promise.all(inputsPromises); +} + async function _compilePackagePolicyInput( pkgInfo: PackageInfo, vars: PackagePolicy['vars'], @@ -977,7 +1005,6 @@ async function _compilePackagePolicyInput( } async function _compilePackageStreams( - installablePackage: InstallablePackage, pkgInfo: PackageInfo, vars: PackagePolicy['vars'], input: PackagePolicyInput @@ -1133,8 +1160,104 @@ function _enforceFrozenVars( return resultVars; } -export type PackagePolicyServiceInterface = PackagePolicyService; -export const packagePolicyService = new PackagePolicyService(); +export interface PackagePolicyServiceInterface { + create( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + packagePolicy: NewPackagePolicy, + options?: { + spaceId?: string; + id?: string; + user?: AuthenticatedUser; + bumpRevision?: boolean; + force?: boolean; + skipEnsureInstalled?: boolean; + skipUniqueNameVerification?: boolean; + overwrite?: boolean; + } + ): Promise; + + bulkCreate( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + packagePolicies: NewPackagePolicy[], + agentPolicyId: string, + options?: { user?: AuthenticatedUser; bumpRevision?: boolean } + ): Promise; + + get(soClient: SavedObjectsClientContract, id: string): Promise; + + getByIDs(soClient: SavedObjectsClientContract, ids: string[]): Promise; + + list( + soClient: SavedObjectsClientContract, + options: ListWithKuery + ): Promise>; + + listIds( + soClient: SavedObjectsClientContract, + options: ListWithKuery + ): Promise>; + + update( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + id: string, + packagePolicyUpdate: UpdatePackagePolicy, + options?: { user?: AuthenticatedUser }, + currentVersion?: string + ): Promise; + + delete( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + ids: string[], + options?: { user?: AuthenticatedUser; skipUnassignFromAgentPolicies?: boolean; force?: boolean } + ): Promise; + + upgrade( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + ids: string[], + options?: { user?: AuthenticatedUser }, + packagePolicy?: PackagePolicy, + pkgVersion?: string + ): Promise; + + getUpgradeDryRunDiff( + soClient: SavedObjectsClientContract, + id: string, + packagePolicy?: PackagePolicy, + pkgVersion?: string + ): Promise; + + enrichPolicyWithDefaultsFromPackage( + soClient: SavedObjectsClientContract, + newPolicy: NewPackagePolicy + ): Promise; + + buildPackagePolicyFromPackage( + soClient: SavedObjectsClientContract, + pkgName: string + ): Promise; + + runExternalCallbacks( + externalCallbackType: A, + packagePolicy: A extends 'postPackagePolicyDelete' + ? DeletePackagePoliciesResponse + : NewPackagePolicy, + context: RequestHandlerContext, + request: KibanaRequest + ): Promise; + + runDeleteExternalCallbacks(deletedPackagePolicies: DeletePackagePoliciesResponse): Promise; + + getUpgradePackagePolicyInfo( + soClient: SavedObjectsClientContract, + id: string + ): Promise<{ packagePolicy: PackagePolicy; packageInfo: PackageInfo }>; +} +export const packagePolicyService: PackagePolicyServiceInterface = new PackagePolicyService(); export type { PackagePolicyService };