Skip to content

Commit

Permalink
refactor upgrade package policies
Browse files Browse the repository at this point in the history
  • Loading branch information
juliaElastic committed Feb 17, 2022
1 parent 24ea50e commit cae61df
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 77 deletions.
131 changes: 73 additions & 58 deletions x-pack/plugins/fleet/server/services/managed_package_policies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@
import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server';
import semverGte from 'semver/functions/gte';

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 {
Expand All @@ -26,78 +30,89 @@ export interface UpgradeManagedPackagePoliciesResult {
*/
export const upgradeManagedPackagePolicies = async (
soClient: SavedObjectsClientContract,
esClient: ElasticsearchClient,
packagePolicyIds: string[]
esClient: ElasticsearchClient
): Promise<UpgradeManagedPackagePoliciesResult[]> => {
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`,
});

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(
for (const { attributes: installedPackage } of installedPackages.saved_objects) {
if (installedPackage.keep_policies_up_to_date) {
const packagePolicies = await getPackagePoliciesNotMatchingVersion(
soClient,
packagePolicyId
installedPackage.name,
installedPackage.version
);

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;
}

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 (isPolicyVersionGteInstalledVersion(packagePolicy, installedPackage)) {
continue;
}
upgradePackagePolicy(soClient, esClient, packagePolicy.id, results);
}
}
}

return results;
};

export function shouldUpgradePolicies(
packagePolicyPackageVersion: string,
async function getPackagePoliciesNotMatchingVersion(
soClient: SavedObjectsClientContract,
pkgName: string,
pkgVersion: string
): Promise<PackagePolicy[]> {
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 isPolicyVersionGteInstalledVersion(
packagePolicy: PackagePolicy,
installedPackage: Installation
): boolean {
const isPolicyVersionGteInstalledVersion = semverGte(
packagePolicyPackageVersion,
installedPackage.version
return (
!!packagePolicy.package &&
!!installedPackage.version &&
semverGte(packagePolicy.package.version, installedPackage.version)
);
}

return !isPolicyVersionGteInstalledVersion && !!installedPackage.keep_policies_up_to_date;
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)}`)
);

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] });
}
}
23 changes: 10 additions & 13 deletions x-pack/plugins/fleet/server/services/preconfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,18 +357,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
Expand All @@ -385,10 +373,19 @@ export async function ensurePreconfiguredPackagesAndPolicies(
}
),
packages: fulfilledPackages.map((pkg) => ('version' in pkg ? pkgToPkgKey(pkg) : pkg.name)),
nonFatalErrors: [...rejectedPackages, ...rejectedPolicies, ...packagePolicyUpgradeResults],
nonFatalErrors: [...rejectedPackages, ...rejectedPolicies],
};
}

export async function ensureManagedPackagePoliciesUpgraded(
soClient: SavedObjectsClientContract,
esClient: ElasticsearchClient
): Promise<UpgradeManagedPackagePoliciesResult[]> {
// Handle automatic package policy upgrades for managed packages and package with
// the `keep_policies_up_to_date` setting enabled
return upgradeManagedPackagePolicies(soClient, esClient);
}

export function comparePreconfiguredPolicyToCurrent(
policyFromConfig: PreconfiguredAgentPolicy,
currentPolicy: AgentPolicy
Expand Down
22 changes: 16 additions & 6 deletions x-pack/plugins/fleet/server/services/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { appContextService } from './app_context';
import { agentPolicyService } from './agent_policy';
import {
cleanPreconfiguredOutputs,
ensureManagedPackagePoliciesUpgraded,
ensurePreconfiguredOutputs,
ensurePreconfiguredPackagesAndPolicies,
} from './preconfiguration';
Expand Down Expand Up @@ -98,15 +99,24 @@ async function createSetupSideEffects(

logger.debug('Setting up initial Fleet packages');

const { nonFatalErrors } = await ensurePreconfiguredPackagesAndPolicies(
const { nonFatalErrors: preconfiguredPackagesNonFatalErrors } =
await ensurePreconfiguredPackagesAndPolicies(
soClient,
esClient,
policies,
packages,
defaultOutput,
DEFAULT_SPACE_ID
);

logger.debug('Ensure managed package policies are upgraded');
const packagePolicyUpgradeResults = await ensureManagedPackagePoliciesUpgraded(
soClient,
esClient,
policies,
packages,
defaultOutput,
DEFAULT_SPACE_ID
esClient
);

const nonFatalErrors = [...preconfiguredPackagesNonFatalErrors, ...packagePolicyUpgradeResults];

logger.debug('Cleaning up Fleet outputs');
await cleanPreconfiguredOutputs(soClient, outputsOrUndefined ?? []);

Expand Down

0 comments on commit cae61df

Please sign in to comment.