Skip to content

Commit

Permalink
[Fleet] Add package policy upgrade API (elastic#103017)
Browse files Browse the repository at this point in the history
* Add Integrations page callout for package upgades

* Fix props

* Add missing file

* Add integrations upgrade callout message

* Add link to updates available tab

* Fix merge

* Upgrade ppolicies UI WIP

* Initial upgrade dry run API

* Add upgrade method

* Move overridePackageInputs and use for upgrade method

* Add new variables to dry run diff

* Revert UI changes to uto upgrade wizard

* Add vars and streams to error keys

* Type fix

* Fix jest

* Fix types

* Fix typecheck

* Fix types

* Add integration test for dry run API

* Flesh out test cases

* Clean up error responses for dry runs

* Fix failing tests

* WIP: Add (failing for now) test case for package upgrade w/ error

* Add compiled_stream to test API payload

* Fix failing test case for automatic upgrade

* Fix compiled stream in package policy upgrade

* Remove fleet and agent setup from integration test

* Unload esarchiver fixtures in api integration test

Co-authored-by: Kyle Pollich <[email protected]>
Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
3 people committed Jul 27, 2021
1 parent 1bc4d31 commit b065196
Show file tree
Hide file tree
Showing 30 changed files with 932 additions and 135 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/common/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const PACKAGE_POLICY_API_ROUTES = {
CREATE_PATTERN: `${PACKAGE_POLICY_API_ROOT}`,
UPDATE_PATTERN: `${PACKAGE_POLICY_API_ROOT}/{packagePolicyId}`,
DELETE_PATTERN: `${PACKAGE_POLICY_API_ROOT}/delete`,
UPGRADE_PATTERN: `${PACKAGE_POLICY_API_ROOT}/upgrade`,
};

// Agent policy API routes
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/fleet/common/types/models/package_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,7 @@ export interface PackagePolicy extends Omit<NewPackagePolicy, 'inputs'> {
}

export type PackagePolicySOAttributes = Omit<PackagePolicy, 'id' | 'version'>;

export type DryRunPackagePolicy = NewPackagePolicy & {
errors?: Array<{ key: string | undefined; message: string }>;
};
25 changes: 24 additions & 1 deletion x-pack/plugins/fleet/common/types/rest_spec/package_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
* 2.0.
*/

import type { PackagePolicy, NewPackagePolicy, UpdatePackagePolicy } from '../models';
import type {
PackagePolicy,
NewPackagePolicy,
UpdatePackagePolicy,
DryRunPackagePolicy,
} from '../models';

export interface GetPackagePoliciesRequest {
query: {
Expand Down Expand Up @@ -57,3 +62,21 @@ export type DeletePackagePoliciesResponse = Array<{
name?: string;
success: boolean;
}>;

export interface UpgradePackagePolicyBaseResponse {
name?: string;
}

export interface UpgradePackagePolicyDryRunResponseItem extends UpgradePackagePolicyBaseResponse {
hasErrors: boolean;
diff?: [PackagePolicy, DryRunPackagePolicy];
}

export type UpgradePackagePolicyDryRunResponse = UpgradePackagePolicyDryRunResponseItem[];

export interface UpgradePackagePolicyResponseItem extends UpgradePackagePolicyBaseResponse {
id: string;
success: boolean;
}

export type UpgradePackagePolicyResponse = UpgradePackagePolicyResponseItem[];
84 changes: 84 additions & 0 deletions x-pack/plugins/fleet/public/hooks/use_package_installations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useMemo } from 'react';
import semverLt from 'semver/functions/lt';

import { installationStatuses } from '../../common/constants';
import type { PackagePolicy } from '../types';

import { useGetPackages } from './use_request/epm';
import { useGetAgentPolicies } from './use_request/agent_policy';

export const usePackageInstallations = () => {
const { data: allPackages, isLoading: isLoadingPackages } = useGetPackages({
experimental: true,
});

const { data: agentPolicyData, isLoading: isLoadingPolicies } = useGetAgentPolicies({
full: true,
});

const allInstalledPackages = useMemo(
() =>
(allPackages?.response || []).filter((pkg) => pkg.status === installationStatuses.Installed),
[allPackages?.response]
);

const updatablePackages = useMemo(
() =>
allInstalledPackages.filter(
(item) =>
'savedObject' in item && semverLt(item.savedObject.attributes.version, item.version)
),
[allInstalledPackages]
);

const updatableIntegrations = useMemo(
() =>
(agentPolicyData?.items || []).reduce((result, policy) => {
policy.package_policies.forEach((pkgPolicy: PackagePolicy | string) => {
if (typeof pkgPolicy === 'string' || !pkgPolicy.package) return false;
const { name, version } = pkgPolicy.package;
const installedPackage = allInstalledPackages.find(
(installedPkg) =>
'savedObject' in installedPkg && installedPkg.savedObject.attributes.name === name
);
if (
installedPackage &&
'savedObject' in installedPackage &&
semverLt(version, installedPackage.savedObject.attributes.version)
) {
const packageData = result.get(name) ?? {
currentVersion: installedPackage.savedObject.attributes.version,
policiesToUpgrade: [],
};
packageData.policiesToUpgrade.push({
id: policy.id,
name: policy.name,
agentsCount: policy.agents,
pkgPolicyId: pkgPolicy.id,
pkgPolicyName: pkgPolicy.name,
pkgPolicyIntegrationVersion: version,
});
result.set(name, packageData);
}
});
return result;
}, new Map()),
[allInstalledPackages, agentPolicyData]
);

return {
allPackages,
allInstalledPackages,
updatablePackages,
updatableIntegrations,
isLoadingPackages,
isLoadingPolicies,
};
};
3 changes: 3 additions & 0 deletions x-pack/plugins/fleet/server/mocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ export const createPackagePolicyServiceMock = () => {
listIds: jest.fn(),
update: jest.fn(),
runExternalCallbacks: jest.fn(),
upgrade: jest.fn(),
getUpgradeDryRunDiff: jest.fn(),
getUpgradePackagePolicyInfo: jest.fn(),
} as jest.Mocked<PackagePolicyServiceInterface>;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@ import type { CreatePackagePolicyRequestSchema } from '../../types/rest_spec';

import { registerRoutes } from './index';

type PackagePolicyServicePublicInterface = Omit<
PackagePolicyServiceInterface,
'getUpgradePackagePolicyInfo'
>;

const packagePolicyServiceMock = packagePolicyService as jest.Mocked<PackagePolicyServiceInterface>;

jest.mock('../../services/package_policy', (): {
packagePolicyService: jest.Mocked<PackagePolicyServiceInterface>;
packagePolicyService: jest.Mocked<PackagePolicyServicePublicInterface>;
} => {
return {
packagePolicyService: {
Expand Down Expand Up @@ -56,6 +61,8 @@ jest.mock('../../services/package_policy', (): {
runExternalCallbacks: jest.fn((callbackType, newPackagePolicy, context, request) =>
Promise.resolve(newPackagePolicy)
),
upgrade: jest.fn(),
getUpgradeDryRunDiff: jest.fn(),
},
};
});
Expand Down
43 changes: 42 additions & 1 deletion x-pack/plugins/fleet/server/routes/package_policy/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ import type {
CreatePackagePolicyRequestSchema,
UpdatePackagePolicyRequestSchema,
DeletePackagePoliciesRequestSchema,
UpgradePackagePoliciesRequestSchema,
} from '../../types';
import type { CreatePackagePolicyResponse, DeletePackagePoliciesResponse } from '../../../common';
import type {
CreatePackagePolicyResponse,
DeletePackagePoliciesResponse,
UpgradePackagePolicyDryRunResponse,
UpgradePackagePolicyResponse,
} from '../../../common';
import { defaultIngestErrorHandler } from '../../errors';

export const getPackagePoliciesHandler: RequestHandler<
Expand Down Expand Up @@ -172,3 +178,38 @@ export const deletePackagePolicyHandler: RequestHandler<
return defaultIngestErrorHandler({ error, response });
}
};

export const upgradePackagePolicyHandler: RequestHandler<
unknown,
unknown,
TypeOf<typeof UpgradePackagePoliciesRequestSchema.body>
> = async (context, request, response) => {
const soClient = context.core.savedObjects.client;
const esClient = context.core.elasticsearch.client.asCurrentUser;
const user = appContextService.getSecurity()?.authc.getCurrentUser(request) || undefined;
try {
if (request.body.dryRun) {
const body: UpgradePackagePolicyDryRunResponse = [];

for (const id of request.body.packagePolicyIds) {
const result = await packagePolicyService.getUpgradeDryRunDiff(soClient, id);
body.push(result);
}
return response.ok({
body,
});
} else {
const body: UpgradePackagePolicyResponse = await packagePolicyService.upgrade(
soClient,
esClient,
request.body.packagePolicyIds,
{ user }
);
return response.ok({
body,
});
}
} catch (error) {
return defaultIngestErrorHandler({ error, response });
}
};
12 changes: 12 additions & 0 deletions x-pack/plugins/fleet/server/routes/package_policy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
CreatePackagePolicyRequestSchema,
UpdatePackagePolicyRequestSchema,
DeletePackagePoliciesRequestSchema,
UpgradePackagePoliciesRequestSchema,
} from '../../types';

import {
Expand All @@ -22,6 +23,7 @@ import {
createPackagePolicyHandler,
updatePackagePolicyHandler,
deletePackagePolicyHandler,
upgradePackagePolicyHandler,
} from './handlers';

export const registerRoutes = (router: IRouter) => {
Expand Down Expand Up @@ -74,4 +76,14 @@ export const registerRoutes = (router: IRouter) => {
},
deletePackagePolicyHandler
);

// Upgrade
router.post(
{
path: PACKAGE_POLICY_API_ROUTES.UPGRADE_PATTERN,
validate: UpgradePackagePoliciesRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
upgradePackagePolicyHandler
);
};
Loading

0 comments on commit b065196

Please sign in to comment.