Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fleet] flag package policy SO to trigger agent policy bump #200536

Merged
merged 20 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c46cff2
bump policy if SO different from full policy
juliaElastic Nov 18, 2024
0985863
move logic to async task
juliaElastic Nov 18, 2024
7cd7cf5
use new field bump_agent_policy_revision to trigger revision bump
juliaElastic Nov 18, 2024
33e2a37
[CI] Auto-commit changed files from 'node scripts/check_mappings_upda…
kibanamachine Nov 18, 2024
65eee0f
[CI] Auto-commit changed files from 'node scripts/jest_integration -u…
kibanamachine Nov 19, 2024
600590a
refactor
juliaElastic Nov 20, 2024
58c89a9
[CI] Auto-commit changed files from 'node scripts/capture_oas_snapsho…
kibanamachine Nov 20, 2024
409bbf7
[CI] Auto-commit changed files from 'node scripts/check_mappings_upda…
kibanamachine Nov 20, 2024
32dc285
fix test
juliaElastic Nov 20, 2024
92a2a62
add missing header to /enable_space_awareness doc
juliaElastic Nov 20, 2024
6704eb9
[CI] Auto-commit changed files from 'make api-docs'
kibanamachine Nov 20, 2024
3409529
[CI] Auto-commit changed files from 'node scripts/jest_integration -u…
kibanamachine Nov 20, 2024
2878cbd
bump agent policies in bulk
juliaElastic Nov 20, 2024
eeb2c54
fix test, updated create_agent_policies script to use spaces
juliaElastic Nov 20, 2024
9daf046
Merge branch 'main' into so-migration-deploy-policies
juliaElastic Nov 20, 2024
2681c7c
Merge branch 'main' into so-migration-deploy-policies
juliaElastic Nov 21, 2024
26e46d4
use isCancelled instead of cancelled
juliaElastic Nov 22, 2024
4ce6b39
Merge branch 'main' into so-migration-deploy-policies
juliaElastic Nov 22, 2024
d8c9711
fix test
juliaElastic Nov 22, 2024
e031878
Merge branch 'main' into so-migration-deploy-policies
juliaElastic Nov 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,7 @@
"version"
],
"ingest-package-policies": [
"bump_agent_policy_revision",
"created_at",
"created_by",
"description",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2292,6 +2292,9 @@
},
"ingest-package-policies": {
"properties": {
"bump_agent_policy_revision": {
"type": "boolean"
},
"created_at": {
"type": "date"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"ingest-agent-policies": "5e95e539826a40ad08fd0c1d161da0a4d86ffc6d",
"ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d",
"ingest-outputs": "55988d5f778bbe0e76caa7e6468707a0a056bdd8",
"ingest-package-policies": "53a94064674835fdb35e5186233bcd7052eabd22",
"ingest-package-policies": "dfa7b1045a2667a822181f40f012786724492439",
"ingest_manager_settings": "111a616eb72627c002029c19feb9e6c439a10505",
"inventory-view": "b8683c8e352a286b4aca1ab21003115a4800af83",
"kql-telemetry": "93c1d16c1a0dfca9c8842062cf5ef8f62ae401ad",
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/common/types/models/package_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export interface PackagePolicy extends Omit<NewPackagePolicy, 'inputs'> {
updated_by: string;
created_at: string;
created_by: string;
bump_agent_policy_revision?: boolean;
}

export type DryRunPackagePolicy = NewPackagePolicy & {
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/fleet/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ import { registerFieldsMetadataExtractors } from './services/register_fields_met
import { registerUpgradeManagedPackagePoliciesTask } from './services/setup/managed_package_policies';
import { registerDeployAgentPoliciesTask } from './services/agent_policies/deploy_agent_policies_task';
import { DeleteUnenrolledAgentsTask } from './tasks/delete_unenrolled_agents_task';
import { registerBumpAgentPoliciesTask } from './services/agent_policies/bump_agent_policies_task';

export interface FleetSetupDeps {
security: SecurityPluginSetup;
Expand Down Expand Up @@ -619,6 +620,7 @@ export class FleetPlugin
// Register task
registerUpgradeManagedPackagePoliciesTask(deps.taskManager);
registerDeployAgentPoliciesTask(deps.taskManager);
registerBumpAgentPoliciesTask(deps.taskManager);

this.bulkActionsResolver = new BulkActionsResolver(deps.taskManager, core);
this.checkDeletedFilesTask = new CheckDeletedFilesTask({
Expand Down
21 changes: 21 additions & 0 deletions x-pack/plugins/fleet/server/saved_objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,7 @@ export const getSavedObjectTypes = (
updated_by: { type: 'keyword' },
created_at: { type: 'date' },
created_by: { type: 'keyword' },
bump_agent_policy_revision: { type: 'boolean' },
},
},
modelVersions: {
Expand Down Expand Up @@ -763,6 +764,26 @@ export const getSavedObjectTypes = (
},
],
},
'15': {
changes: [
{
type: 'mappings_addition',
addedMappings: {
bump_agent_policy_revision: { type: 'boolean' },
},
},
],
},
// '16': {
// changes: [
// {
// type: 'data_backfill',
// backfillFn: (doc) => {
// return { attributes: { ...doc.attributes, bump_agent_policy_revision: true } };
// },
// },
// ],
// },
},
migrations: {
'7.10.0': migratePackagePolicyToV7100,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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 { loggingSystemMock } from '@kbn/core/server/mocks';

import { agentPolicyService } from '../agent_policy';

import { packagePolicyService } from '../package_policy';
import type { PackagePolicy } from '../../types';

import { _updatePackagePoliciesThatNeedBump } from './bump_agent_policies_task';

jest.mock('../app_context');
jest.mock('../agent_policy');
jest.mock('../package_policy');

const mockedAgentPolicyService = jest.mocked(agentPolicyService);
const mockedPackagePolicyService = jest.mocked(packagePolicyService);

describe('_updatePackagePoliciesThatNeedBump', () => {
beforeEach(() => {
jest.clearAllMocks();
mockedPackagePolicyService.list.mockResolvedValueOnce({
total: 1,
items: [
{
id: 'packagePolicy1',
bump_agent_policy_revision: true,
} as PackagePolicy,
],
page: 1,
perPage: 100,
});
mockedPackagePolicyService.list.mockResolvedValueOnce({
total: 0,
items: [],
page: 1,
perPage: 100,
});
});

it('should update package policy if bump agent policy revision needed', async () => {
const logger = loggingSystemMock.createLogger();

await _updatePackagePoliciesThatNeedBump(logger);

expect(mockedPackagePolicyService.bulkUpdate).toHaveBeenCalledWith(undefined, undefined, [
{ bump_agent_policy_revision: false, id: 'packagePolicy1' },
]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* 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 type { Logger } from '@kbn/core/server';
import type {
ConcreteTaskInstance,
TaskManagerSetupContract,
TaskManagerStartContract,
} from '@kbn/task-manager-plugin/server';
import { v4 as uuidv4 } from 'uuid';

import { appContextService, packagePolicyService } from '..';
import { runWithCache } from '../epm/packages/cache';

const TASK_TYPE = 'fleet:bump_agent_policies';
const BATCH_SIZE = 100;
export function registerBumpAgentPoliciesTask(taskManagerSetup: TaskManagerSetupContract) {
taskManagerSetup.registerTaskDefinitions({
[TASK_TYPE]: {
title: 'Fleet Bump policies',
timeout: '5m',
maxAttempts: 3,
createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => {
let cancelled = false;
return {
async run() {
if (cancelled) {
throw new Error('Task has been cancelled');
}

await runWithCache(async () => {
await _updatePackagePoliciesThatNeedBump(appContextService.getLogger());

// TODO agent policies
});
},
async cancel() {
cancelled = true;
},
};
},
},
});
}

async function getPackagePoliciesToBump() {
return await packagePolicyService.list(appContextService.getInternalUserSOClient(), {
Copy link
Contributor Author

@juliaElastic juliaElastic Nov 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to query package policies from all spaces, we need to query with soClient for each space. For this, we need to query all spaces first.

I think similarly the deploy policies task doesn't work correctly, because the logic only queries agent policies from the default space: https://github.com/elastic/kibana/blob/main/x-pack/plugins/fleet/server/services/setup/fleet_server_policies_enrollment_keys.ts#L35

I'll query from all spaces like this:

.getInternalUserSOClientWithoutSpaceExtension()
.find<AgentPolicySOAttributes>({
type: savedObjectType,
fields: ['revision', 'data_output_id', 'monitoring_output_id'],
searchFields: ['data_output_id', 'monitoring_output_id'],
search: escapeSearchQueryPhrase(outputId),
perPage: SO_SEARCH_LIMIT,
namespaces: ['*'],

kuery: 'ingest-package-policies.bump_agent_policy_revision:true',
perPage: BATCH_SIZE,
});
}

export async function _updatePackagePoliciesThatNeedBump(logger: Logger) {
// TODO spaces?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we probably need to use the SO for the correct space here

let packagePoliciesToBump = await getPackagePoliciesToBump();

logger.info(
`Found ${packagePoliciesToBump.total} package policies that need agent policy revision bump`
);

while (packagePoliciesToBump.total > 0) {
const start = Date.now();
// resetting the flag will trigger a revision bump
await packagePolicyService.bulkUpdate(
appContextService.getInternalUserSOClient(),
appContextService.getInternalUserESClient(),
packagePoliciesToBump.items.map((item) => ({
...item,
bump_agent_policy_revision: false,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flag has to be set to false, otherwise an update will happen on every Fleet setup.
This update triggers the agent policy bump anyway, so no need to bump separately.

}))
);
const updatedCount = packagePoliciesToBump.items.length;

packagePoliciesToBump = await getPackagePoliciesToBump();
logger.debug(
`Updated ${updatedCount} package policies in ${Date.now() - start}ms, ${
packagePoliciesToBump.total
} remaining`
);
}
}

export async function scheduleBumpAgentPoliciesTask(taskManagerStart: TaskManagerStartContract) {
await taskManagerStart.ensureScheduled({
id: `${TASK_TYPE}:${uuidv4()}`,
scope: ['fleet'],
params: {},
taskType: TASK_TYPE,
runAt: new Date(Date.now() + 3 * 1000),
state: {},
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ensureAgentPoliciesFleetServerKeysAndPolicies } from './fleet_server_po
jest.mock('../app_context');
jest.mock('../agent_policy');
jest.mock('../api_keys');
jest.mock('../agent_policies/bump_agent_policies_task');

const mockedEnsureDefaultEnrollmentAPIKeyForAgentPolicy = jest.mocked(
ensureDefaultEnrollmentAPIKeyForAgentPolicy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ensureDefaultEnrollmentAPIKeyForAgentPolicy } from '../api_keys';
import { SO_SEARCH_LIMIT } from '../../constants';
import { appContextService } from '../app_context';
import { scheduleDeployAgentPoliciesTask } from '../agent_policies/deploy_agent_policies_task';
import { scheduleBumpAgentPoliciesTask } from '../agent_policies/bump_agent_policies_task';

export async function ensureAgentPoliciesFleetServerKeysAndPolicies({
logger,
Expand All @@ -37,7 +38,6 @@ export async function ensureAgentPoliciesFleetServerKeysAndPolicies({
});

const outdatedAgentPolicyIds: Array<{ id: string; spaceId?: string }> = [];

await pMap(
agentPolicies,
async (agentPolicy) => {
Expand All @@ -55,23 +55,23 @@ export async function ensureAgentPoliciesFleetServerKeysAndPolicies({
}
);

if (!outdatedAgentPolicyIds.length) {
return;
}
await scheduleBumpAgentPoliciesTask(appContextService.getTaskManagerStart()!);

if (appContextService.getExperimentalFeatures().asyncDeployPolicies) {
return scheduleDeployAgentPoliciesTask(
appContextService.getTaskManagerStart()!,
outdatedAgentPolicyIds
);
} else {
return agentPolicyService
.deployPolicies(
soClient,
outdatedAgentPolicyIds.map(({ id }) => id)
)
.catch((error) => {
logger.warn(`Error deploying policies: ${error.message}`, { error });
});
if (outdatedAgentPolicyIds.length) {
if (appContextService.getExperimentalFeatures().asyncDeployPolicies) {
return scheduleDeployAgentPoliciesTask(
appContextService.getTaskManagerStart()!,
outdatedAgentPolicyIds
);
} else {
return agentPolicyService
.deployPolicies(
soClient,
outdatedAgentPolicyIds.map(({ id }) => id)
)
.catch((error) => {
logger.warn(`Error deploying policies: ${error.message}`, { error });
});
}
}
}