Skip to content

Commit df83efc

Browse files
[8.x] [Fleet] Reuse shared integration policies when duplicating agent policies (elastic#217872) (elastic#218002)
# Backport This will backport the following commits from `main` to `8.x`: - [[Fleet] Reuse shared integration policies when duplicating agent policies (elastic#217872)](elastic#217872) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Jill Guyonnet","email":"[email protected]"},"sourceCommit":{"committedDate":"2025-04-11T15:16:17Z","message":"[Fleet] Reuse shared integration policies when duplicating agent policies (elastic#217872)\n\n## Summary\n\nCloses https://github.com/elastic/kibana/issues/215335\n\nCurrently, when an agent policy is duplicated, shared integration\npolicies are also duplicated. This PR adds logic where the duplicated\nagent policy also shares these integration policies.\n\n### Testing\n\n* Run ES with an [Entreprise\nlicense](https://www.elastic.co/subscriptions) to avail of reusable\nintegration policies.\n* Create an agent policy with a shared integration policy and a\nnon-shared integration policy.\n* Duplicate the agent policy: the duplicated policy should only\nduplicate the non-shared integration policy and the shared integration\npolicy should be reused.\n\n### Checklist\n\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [ ] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n\n### Identify risks\n\nIncorrect package policies in duplicated agent policies.","sha":"5c78ff18484e77b5ec5a4ba2ab341ed65db4f21c","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:enhancement","Team:Fleet","v9.0.0","backport:version","v9.1.0","v8.19.0"],"title":"[Fleet] Reuse shared integration policies when duplicating agent policies","number":217872,"url":"https://github.com/elastic/kibana/pull/217872","mergeCommit":{"message":"[Fleet] Reuse shared integration policies when duplicating agent policies (elastic#217872)\n\n## Summary\n\nCloses https://github.com/elastic/kibana/issues/215335\n\nCurrently, when an agent policy is duplicated, shared integration\npolicies are also duplicated. This PR adds logic where the duplicated\nagent policy also shares these integration policies.\n\n### Testing\n\n* Run ES with an [Entreprise\nlicense](https://www.elastic.co/subscriptions) to avail of reusable\nintegration policies.\n* Create an agent policy with a shared integration policy and a\nnon-shared integration policy.\n* Duplicate the agent policy: the duplicated policy should only\nduplicate the non-shared integration policy and the shared integration\npolicy should be reused.\n\n### Checklist\n\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [ ] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n\n### Identify risks\n\nIncorrect package policies in duplicated agent policies.","sha":"5c78ff18484e77b5ec5a4ba2ab341ed65db4f21c"}},"sourceBranch":"main","suggestedTargetBranches":["9.0","8.x"],"targetPullRequestStates":[{"branch":"9.0","label":"v9.0.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/217872","number":217872,"mergeCommit":{"message":"[Fleet] Reuse shared integration policies when duplicating agent policies (elastic#217872)\n\n## Summary\n\nCloses https://github.com/elastic/kibana/issues/215335\n\nCurrently, when an agent policy is duplicated, shared integration\npolicies are also duplicated. This PR adds logic where the duplicated\nagent policy also shares these integration policies.\n\n### Testing\n\n* Run ES with an [Entreprise\nlicense](https://www.elastic.co/subscriptions) to avail of reusable\nintegration policies.\n* Create an agent policy with a shared integration policy and a\nnon-shared integration policy.\n* Duplicate the agent policy: the duplicated policy should only\nduplicate the non-shared integration policy and the shared integration\npolicy should be reused.\n\n### Checklist\n\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [ ] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n\n### Identify risks\n\nIncorrect package policies in duplicated agent policies.","sha":"5c78ff18484e77b5ec5a4ba2ab341ed65db4f21c"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Jill Guyonnet <[email protected]>
1 parent 92623b4 commit df83efc

File tree

2 files changed

+131
-23
lines changed

2 files changed

+131
-23
lines changed

x-pack/platform/plugins/shared/fleet/server/services/agent_policy.test.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,98 @@ describe('Agent policy', () => {
13371337
);
13381338
}
13391339
});
1340+
1341+
it('should link shared package policies', async () => {
1342+
agentPolicyService.requireUniqueName = async () => {};
1343+
soClient = savedObjectsClientMock.create();
1344+
const mockPolicy = {
1345+
type: LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE,
1346+
references: [],
1347+
attributes: { revision: 1, package_policies: ['package-1'] } as any,
1348+
};
1349+
soClient.get.mockImplementation(async (type: string, id: string) => {
1350+
return {
1351+
id,
1352+
...mockPolicy,
1353+
};
1354+
});
1355+
soClient.find
1356+
.mockImplementationOnce(async () => ({
1357+
saved_objects: [
1358+
{
1359+
id: 'agent-policy-id',
1360+
score: 1,
1361+
...{ ...mockPolicy, name: 'mocked-policy' },
1362+
},
1363+
],
1364+
total: 1,
1365+
page: 1,
1366+
per_page: 1,
1367+
}))
1368+
.mockImplementationOnce(async () => ({
1369+
saved_objects: [
1370+
{
1371+
id: 'agent-policy-id-copy',
1372+
score: 1,
1373+
...{ ...mockPolicy, name: 'mocked-policy' },
1374+
},
1375+
],
1376+
total: 1,
1377+
page: 1,
1378+
per_page: 1,
1379+
}));
1380+
soClient.create.mockImplementation(async (type, attributes) => {
1381+
return {
1382+
attributes: attributes as unknown as NewAgentPolicy,
1383+
id: 'mocked',
1384+
type: 'mocked',
1385+
references: [],
1386+
};
1387+
});
1388+
const packagePolicies = [
1389+
{
1390+
id: 'package-1',
1391+
name: 'package-1',
1392+
policy_id: 'policy_1',
1393+
policy_ids: ['policy_1', 'policy_2'],
1394+
},
1395+
{
1396+
id: 'package-2',
1397+
name: 'package-2',
1398+
policy_id: 'policy_1',
1399+
policy_ids: ['policy_1'],
1400+
},
1401+
] as any;
1402+
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue(packagePolicies);
1403+
mockedPackagePolicyService.list.mockResolvedValue({ items: packagePolicies } as any);
1404+
await agentPolicyService.copy(soClient, esClient, 'mocked', {
1405+
name: 'copy mocked',
1406+
});
1407+
expect(mockedPackagePolicyService.bulkCreate).toBeCalledWith(
1408+
expect.anything(),
1409+
expect.anything(),
1410+
[
1411+
{
1412+
name: 'package-2 (copy)',
1413+
policy_id: 'policy_1',
1414+
policy_ids: ['mocked'],
1415+
},
1416+
],
1417+
expect.anything()
1418+
);
1419+
expect(mockedPackagePolicyService.bulkUpdate).toBeCalledWith(
1420+
expect.anything(),
1421+
expect.anything(),
1422+
[
1423+
{
1424+
id: 'package-1',
1425+
name: 'package-1',
1426+
policy_id: 'policy_1',
1427+
policy_ids: ['policy_1', 'policy_2', 'mocked'],
1428+
},
1429+
]
1430+
);
1431+
});
13401432
});
13411433

13421434
describe('deployPolicy', () => {

x-pack/platform/plugins/shared/fleet/server/services/agent_policy.ts

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -831,32 +831,48 @@ class AgentPolicyService {
831831
options
832832
);
833833

834-
// Copy all package policies and append (copy n) to their names
835834
if (baseAgentPolicy.package_policies) {
836-
const newPackagePolicies = await pMap(
837-
baseAgentPolicy.package_policies as PackagePolicy[],
838-
async (packagePolicy: PackagePolicy) => {
839-
const { id: packagePolicyId, version, ...newPackagePolicy } = packagePolicy;
840-
841-
const updatedPackagePolicy = {
842-
...newPackagePolicy,
843-
name: await incrementPackagePolicyCopyName(soClient, packagePolicy.name),
844-
};
845-
return updatedPackagePolicy;
846-
}
835+
// Copy non-shared package policies and append (copy n) to their names.
836+
const basePackagePolicies = baseAgentPolicy.package_policies.filter(
837+
(packagePolicy) => packagePolicy.policy_ids.length < 2
847838
);
848-
await packagePolicyService.bulkCreate(
849-
soClient,
850-
esClient,
851-
newPackagePolicies.map((newPackagePolicy) => ({
852-
...newPackagePolicy,
853-
policy_ids: [newAgentPolicy.id],
854-
})),
855-
{
856-
...options,
857-
bumpRevision: false,
858-
}
839+
if (basePackagePolicies.length > 0) {
840+
const newPackagePolicies = await pMap(
841+
basePackagePolicies,
842+
async (packagePolicy: PackagePolicy) => {
843+
const { id: packagePolicyId, version, ...newPackagePolicy } = packagePolicy;
844+
845+
const updatedPackagePolicy = {
846+
...newPackagePolicy,
847+
name: await incrementPackagePolicyCopyName(soClient, packagePolicy.name),
848+
};
849+
return updatedPackagePolicy;
850+
}
851+
);
852+
await packagePolicyService.bulkCreate(
853+
soClient,
854+
esClient,
855+
newPackagePolicies.map((newPackagePolicy) => ({
856+
...newPackagePolicy,
857+
policy_ids: [newAgentPolicy.id],
858+
})),
859+
{
860+
...options,
861+
bumpRevision: false,
862+
}
863+
);
864+
}
865+
// Link shared package policies to new agent policy.
866+
const sharedBasePackagePolicies = baseAgentPolicy.package_policies.filter(
867+
(packagePolicy) => packagePolicy.policy_ids.length > 1
859868
);
869+
if (sharedBasePackagePolicies.length > 0) {
870+
const updatedSharedPackagePolicies = sharedBasePackagePolicies.map((packagePolicy) => ({
871+
...packagePolicy,
872+
policy_ids: [...packagePolicy.policy_ids, newAgentPolicy.id],
873+
}));
874+
await packagePolicyService.bulkUpdate(soClient, esClient, updatedSharedPackagePolicies);
875+
}
860876
}
861877

862878
// Tamper protection is dependent on endpoint package policy

0 commit comments

Comments
 (0)