diff --git a/oas_docs/bundle.json b/oas_docs/bundle.json index 9b446eacf7bb9..1d03129d262c2 100644 --- a/oas_docs/bundle.json +++ b/oas_docs/bundle.json @@ -36623,6 +36623,7 @@ "type": "boolean" }, "use_space_awareness_migration_started_at": { + "nullable": true, "type": "string" }, "use_space_awareness_migration_status": { @@ -36824,6 +36825,7 @@ "type": "boolean" }, "use_space_awareness_migration_started_at": { + "nullable": true, "type": "string" }, "use_space_awareness_migration_status": { diff --git a/oas_docs/bundle.serverless.json b/oas_docs/bundle.serverless.json index 7adb2e8140491..5a1e2b69438e1 100644 --- a/oas_docs/bundle.serverless.json +++ b/oas_docs/bundle.serverless.json @@ -36623,6 +36623,7 @@ "type": "boolean" }, "use_space_awareness_migration_started_at": { + "nullable": true, "type": "string" }, "use_space_awareness_migration_status": { @@ -36824,6 +36825,7 @@ "type": "boolean" }, "use_space_awareness_migration_started_at": { + "nullable": true, "type": "string" }, "use_space_awareness_migration_status": { diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index c858823e2ae0a..33378690b6500 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -29839,6 +29839,7 @@ paths: secret_storage_requirements_met: type: boolean use_space_awareness_migration_started_at: + nullable: true type: string use_space_awareness_migration_status: enum: @@ -29972,6 +29973,7 @@ paths: secret_storage_requirements_met: type: boolean use_space_awareness_migration_started_at: + nullable: true type: string use_space_awareness_migration_status: enum: diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index 6780ef4926c73..6eb1e4c23f065 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -32607,6 +32607,7 @@ paths: secret_storage_requirements_met: type: boolean use_space_awareness_migration_started_at: + nullable: true type: string use_space_awareness_migration_status: enum: @@ -32739,6 +32740,7 @@ paths: secret_storage_requirements_met: type: boolean use_space_awareness_migration_started_at: + nullable: true type: string use_space_awareness_migration_status: enum: diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index 619bdd6c29321..1b1b847bb2747 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -511,6 +511,7 @@ ], "fleet-message-signing-keys": [], "fleet-package-policies": [ + "bump_agent_policy_revision", "created_at", "created_by", "description", @@ -692,6 +693,7 @@ "version" ], "ingest-package-policies": [ + "bump_agent_policy_revision", "created_at", "created_by", "description", diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index d6ec30393e099..80739b3412e9b 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -1715,6 +1715,9 @@ }, "fleet-package-policies": { "properties": { + "bump_agent_policy_revision": { + "type": "boolean" + }, "created_at": { "type": "date" }, @@ -2300,6 +2303,9 @@ }, "ingest-package-policies": { "properties": { + "bump_agent_policy_revision": { + "type": "boolean" + }, "created_at": { "type": "date" }, diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index f16c956107c7b..61b92f4ccda64 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -108,7 +108,7 @@ describe('checking migration metadata changes on all registered SO types', () => "fleet-agent-policies": "f57d3b70e4175a19a18f18ee72a379ceec82e1fc", "fleet-fleet-server-host": "69be15f6b6f2a2875ad3c7050ddea7a87f505417", "fleet-message-signing-keys": "93421f43fed2526b59092a4e3c65d64bc2266c0f", - "fleet-package-policies": "2f4d524adb49a5281d3af0b66bb3003ba0ff2e44", + "fleet-package-policies": "8be2cabfed89e103e0d413f2900e9cf6cd31bc68", "fleet-preconfiguration-deletion-record": "c52ea1e13c919afe8a5e8e3adbb7080980ecc08e", "fleet-proxy": "6cb688f0d2dd856400c1dbc998b28704ff70363d", "fleet-setup-lock": "0dc784792c79b5af5a6e6b5dcac06b0dbaa90bde", @@ -124,7 +124,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", diff --git a/x-pack/plugins/fleet/dev_docs/space_awareness.md b/x-pack/plugins/fleet/dev_docs/space_awareness.md index fc4ce3a0acd06..90371d1f8bb08 100644 --- a/x-pack/plugins/fleet/dev_docs/space_awareness.md +++ b/x-pack/plugins/fleet/dev_docs/space_awareness.md @@ -15,7 +15,7 @@ xpack.fleet.enableExperimental: ['useSpaceAwareness', 'subfeaturePrivileges'] After the feature flag is enabled you will have to do another step to opt-in for the feature, that call will migrate the current space agnostic saved objects to new space aware saved objects. ```shell -curl -u elastic:changeme -XPOST "http://localhost:5601/internal/fleet/enable_space_awareness" -H "kbn-xsrf: reporting" -H 'elastic-api-version: 1' +curl -u elastic:changeme -XPOST "http://localhost:5601/internal/fleet/enable_space_awareness" -H "kbn-xsrf: reporting" -H 'elastic-api-version: 1' -H 'x-elastic-internal-origin: 1' ``` ## Space aware entities in Fleet diff --git a/x-pack/plugins/fleet/scripts/create_agent_policies/create_agent_policies.ts b/x-pack/plugins/fleet/scripts/create_agent_policies/create_agent_policies.ts index db47c056ce12d..e000640a477e3 100644 --- a/x-pack/plugins/fleet/scripts/create_agent_policies/create_agent_policies.ts +++ b/x-pack/plugins/fleet/scripts/create_agent_policies/create_agent_policies.ts @@ -9,8 +9,7 @@ import { ToolingLog } from '@kbn/tooling-log'; import yargs from 'yargs'; import { chunk } from 'lodash'; -import { LEGACY_PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../common/constants'; -import { LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE } from '../../common'; +import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '../../common/constants'; import { packagePolicyFixture } from './fixtures'; @@ -30,20 +29,18 @@ const printUsage = () => const INDEX_BULK_OP = '{ "index":{ "_id": "{{id}}" } }\n'; +const space = 'default'; function getPolicyId(idx: number | string) { - return `test-policy-${idx}`; + return `test-policy-${space}-${idx}`; } async function createAgentPoliciesDocsBulk(range: number[]) { const auth = 'Basic ' + Buffer.from(ES_SUPERUSER + ':' + ES_PASSWORD).toString('base64'); const body = range .flatMap((idx) => [ - INDEX_BULK_OP.replace( - /{{id}}/, - `${LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE}:${getPolicyId(idx)}` - ), + INDEX_BULK_OP.replace(/{{id}}/, `${AGENT_POLICY_SAVED_OBJECT_TYPE}:${getPolicyId(idx)}`), JSON.stringify({ - [LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE]: { + [AGENT_POLICY_SAVED_OBJECT_TYPE]: { namespace: 'default', monitoring_enabled: ['logs', 'metrics', 'traces'], name: `Test Policy ${idx}`, @@ -60,11 +57,11 @@ async function createAgentPoliciesDocsBulk(range: number[]) { schema_version: '1.1.1', is_protected: false, }, - type: LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, + namespaces: [space], + type: AGENT_POLICY_SAVED_OBJECT_TYPE, references: [], managed: false, coreMigrationVersion: '8.8.0', - typeMigrationVersion: '10.3.0', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }) + '\n', @@ -81,7 +78,7 @@ async function createAgentPoliciesDocsBulk(range: number[]) { const data = await res.json(); if (!data.items) { - logger.error('Error creating agent policies docs: ' + JSON.stringify(data)); + logger.error('Error creating agent policy docs: ' + JSON.stringify(data)); process.exit(1); } return data; @@ -91,14 +88,14 @@ async function createEnrollmentToken(range: number[]) { const auth = 'Basic ' + Buffer.from(ES_SUPERUSER + ':' + ES_PASSWORD).toString('base64'); const body = range .flatMap((idx) => [ - INDEX_BULK_OP.replace(/{{id}}/, `test-enrollment-token-${idx}`), + INDEX_BULK_OP.replace(/{{id}}/, `test-enrollment-token-${space}-${idx}`), JSON.stringify({ active: true, api_key_id: 'faketest123', api_key: 'test==', name: `Test Policy ${idx}`, policy_id: `${getPolicyId(idx)}`, - namespaces: [], + namespaces: [space], created_at: new Date().toISOString(), }) + '\n', ]) @@ -115,7 +112,7 @@ async function createEnrollmentToken(range: number[]) { const data = await res.json(); if (!data.items) { - logger.error('Error creating agent policies docs: ' + JSON.stringify(data)); + logger.error('Error creating enrollment key docs: ' + JSON.stringify(data)); process.exit(1); } return data; @@ -125,14 +122,12 @@ async function createPackagePolicies(range: number[]) { const auth = 'Basic ' + Buffer.from(ES_SUPERUSER + ':' + ES_PASSWORD).toString('base64'); const body = range .flatMap((idx) => [ - INDEX_BULK_OP.replace( - /{{id}}/, - `${LEGACY_PACKAGE_POLICY_SAVED_OBJECT_TYPE}:test-policy-${idx}` - ), + INDEX_BULK_OP.replace(/{{id}}/, `fleet-package-policies:test-policy-${space}-${idx}`), JSON.stringify( packagePolicyFixture({ idx, agentPolicyId: getPolicyId(idx), + space, }) ) + '\n', ]) @@ -150,7 +145,7 @@ async function createPackagePolicies(range: number[]) { const data = await res.json(); if (!data.items) { - logger.error('Error creating agent policies docs: ' + JSON.stringify(data)); + logger.error('Error creating package policy docs: ' + JSON.stringify(data)); process.exit(1); } return data; diff --git a/x-pack/plugins/fleet/scripts/create_agent_policies/fixtures.ts b/x-pack/plugins/fleet/scripts/create_agent_policies/fixtures.ts index b10f412ac43fe..ddaa226c0729e 100644 --- a/x-pack/plugins/fleet/scripts/create_agent_policies/fixtures.ts +++ b/x-pack/plugins/fleet/scripts/create_agent_policies/fixtures.ts @@ -8,11 +8,13 @@ export const packagePolicyFixture = ({ agentPolicyId, idx, + space, }: { idx: number; agentPolicyId: string; + space: string; }) => ({ - 'ingest-package-policies': { + 'fleet-package-policies': { name: `system-test-${idx}`, namespace: '', description: '', @@ -790,11 +792,12 @@ export const packagePolicyFixture = ({ updated_at: '2024-08-30T13:45:51.197Z', updated_by: 'system', }, - type: 'ingest-package-policies', + namespaces: [space], + type: 'fleet-package-policies', references: [], managed: false, coreMigrationVersion: '8.8.0', - typeMigrationVersion: '10.14.0', + typeMigrationVersion: '10.1.0', updated_at: '2024-08-30T13:45:51.197Z', created_at: '2024-08-30T13:45:51.197Z', }); diff --git a/x-pack/plugins/fleet/scripts/create_agent_policies/index.js b/x-pack/plugins/fleet/scripts/create_agent_policies/index.js index a51859ee684c6..a61ed0e7e54d4 100644 --- a/x-pack/plugins/fleet/scripts/create_agent_policies/index.js +++ b/x-pack/plugins/fleet/scripts/create_agent_policies/index.js @@ -12,6 +12,6 @@ require('./create_agent_policies').run(); Usage: cd x-pack/plugins/fleet -node scripts/create_agents/index.js +node scripts/create_agent_policies/index.js */ diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index 4c5cdf7070530..7716d6b21d9e3 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -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; @@ -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({ diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 706d0686b9845..5173ada3f5418 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -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: { @@ -763,6 +764,16 @@ export const getSavedObjectTypes = ( }, ], }, + '15': { + changes: [ + { + type: 'mappings_addition', + addedMappings: { + bump_agent_policy_revision: { type: 'boolean' }, + }, + }, + ], + }, }, migrations: { '7.10.0': migratePackagePolicyToV7100, @@ -823,6 +834,19 @@ export const getSavedObjectTypes = ( updated_by: { type: 'keyword' }, created_at: { type: 'date' }, created_by: { type: 'keyword' }, + bump_agent_policy_revision: { type: 'boolean' }, + }, + }, + modelVersions: { + '1': { + changes: [ + { + type: 'mappings_addition', + addedMappings: { + bump_agent_policy_revision: { type: 'boolean' }, + }, + }, + ], }, }, }, diff --git a/x-pack/plugins/fleet/server/services/agent_policies/bump_agent_policies_task.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/bump_agent_policies_task.test.ts new file mode 100644 index 0000000000000..2e7305c5c5717 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agent_policies/bump_agent_policies_task.test.ts @@ -0,0 +1,115 @@ +/* + * 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 { appContextService } from '..'; +import { getPackagePolicySavedObjectType } from '../package_policy'; + +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 mockedAppContextService = jest.mocked(appContextService); +const mockSoClient = { + find: jest.fn(), + bulkUpdate: jest.fn(), +} as any; +const mockGetPackagePolicySavedObjectType = jest.mocked(getPackagePolicySavedObjectType); + +describe('_updatePackagePoliciesThatNeedBump', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockSoClient.find.mockResolvedValue({ + total: 3, + saved_objects: [ + { + id: 'packagePolicy1', + namespaces: ['default'], + attributes: { + policy_ids: ['policy1'], + }, + }, + { + id: 'packagePolicy12', + namespaces: ['default'], + attributes: { + policy_ids: ['policy1'], + }, + }, + { + id: 'packagePolicy2', + namespaces: ['space'], + attributes: { + policy_ids: ['policy2'], + }, + }, + { + id: 'packagePolicy3', + namespaces: ['space'], + attributes: { + policy_ids: ['policy3'], + }, + }, + ], + page: 1, + perPage: 100, + }); + mockedAppContextService.getInternalUserSOClientWithoutSpaceExtension.mockReturnValue( + mockSoClient + ); + mockedAppContextService.getInternalUserSOClientForSpaceId.mockReturnValue(mockSoClient); + mockGetPackagePolicySavedObjectType.mockResolvedValue('fleet-package-policies'); + }); + + it('should update package policy if bump agent policy revision needed', async () => { + const logger = loggingSystemMock.createLogger(); + + await _updatePackagePoliciesThatNeedBump(logger, () => false); + + expect(mockSoClient.bulkUpdate).toHaveBeenCalledWith([ + { + attributes: { bump_agent_policy_revision: false }, + id: 'packagePolicy1', + type: 'fleet-package-policies', + }, + { + attributes: { bump_agent_policy_revision: false }, + id: 'packagePolicy12', + type: 'fleet-package-policies', + }, + ]); + expect(mockSoClient.bulkUpdate).toHaveBeenCalledWith([ + { + attributes: { bump_agent_policy_revision: false }, + id: 'packagePolicy2', + type: 'fleet-package-policies', + }, + { + attributes: { bump_agent_policy_revision: false }, + id: 'packagePolicy3', + type: 'fleet-package-policies', + }, + ]); + + expect(mockedAgentPolicyService.bumpAgentPoliciesByIds).toHaveBeenCalledWith( + expect.anything(), + undefined, + ['policy1'] + ); + expect(mockedAgentPolicyService.bumpAgentPoliciesByIds).toHaveBeenCalledWith( + expect.anything(), + undefined, + ['policy2', 'policy3'] + ); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/agent_policies/bump_agent_policies_task.ts b/x-pack/plugins/fleet/server/services/agent_policies/bump_agent_policies_task.ts new file mode 100644 index 0000000000000..d134fdbfae04f --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agent_policies/bump_agent_policies_task.ts @@ -0,0 +1,139 @@ +/* + * 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 { uniq } from 'lodash'; + +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; + +import { agentPolicyService, appContextService } from '..'; +import { runWithCache } from '../epm/packages/cache'; +import { getPackagePolicySavedObjectType } from '../package_policy'; +import { mapPackagePolicySavedObjectToPackagePolicy } from '../package_policies'; +import type { PackagePolicy, PackagePolicySOAttributes } from '../../types'; +import { normalizeKuery } from '../saved_object'; +import { SO_SEARCH_LIMIT } from '../../constants'; + +const TASK_TYPE = 'fleet:bump_agent_policies'; + +export function registerBumpAgentPoliciesTask(taskManagerSetup: TaskManagerSetupContract) { + taskManagerSetup.registerTaskDefinitions({ + [TASK_TYPE]: { + title: 'Fleet Bump policies', + timeout: '5m', + maxAttempts: 3, + createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => { + let cancelled = false; + const isCancelled = () => cancelled; + return { + async run() { + if (isCancelled()) { + throw new Error('Task has been cancelled'); + } + + await runWithCache(async () => { + await _updatePackagePoliciesThatNeedBump(appContextService.getLogger(), isCancelled); + }); + }, + async cancel() { + cancelled = true; + }, + }; + }, + }, + }); +} + +async function getPackagePoliciesToBump(savedObjectType: string) { + const result = await appContextService + .getInternalUserSOClientWithoutSpaceExtension() + .find({ + type: savedObjectType, + filter: normalizeKuery(savedObjectType, `${savedObjectType}.bump_agent_policy_revision:true`), + perPage: SO_SEARCH_LIMIT, + namespaces: ['*'], + fields: ['id', 'namespaces', 'policy_ids'], + }); + return { + total: result.total, + items: result.saved_objects.map((so) => + mapPackagePolicySavedObjectToPackagePolicy(so, so.namespaces) + ), + }; +} + +export async function _updatePackagePoliciesThatNeedBump( + logger: Logger, + isCancelled: () => boolean +) { + const savedObjectType = await getPackagePolicySavedObjectType(); + const packagePoliciesToBump = await getPackagePoliciesToBump(savedObjectType); + + logger.info( + `Found ${packagePoliciesToBump.total} package policies that need agent policy revision bump` + ); + + const packagePoliciesIndexedBySpace = packagePoliciesToBump.items.reduce((acc, policy) => { + const spaceId = policy.spaceIds?.[0] ?? DEFAULT_SPACE_ID; + if (!acc[spaceId]) { + acc[spaceId] = []; + } + + acc[spaceId].push(policy); + + return acc; + }, {} as { [k: string]: PackagePolicy[] }); + + const start = Date.now(); + + for (const [spaceId, packagePolicies] of Object.entries(packagePoliciesIndexedBySpace)) { + if (isCancelled()) { + throw new Error('Task has been cancelled'); + } + + const soClient = appContextService.getInternalUserSOClientForSpaceId(spaceId); + const esClient = appContextService.getInternalUserESClient(); + + await soClient.bulkUpdate( + packagePolicies.map((item) => ({ + type: savedObjectType, + id: item.id, + attributes: { + bump_agent_policy_revision: false, + }, + })) + ); + + const updatedCount = packagePolicies.length; + + const agentPoliciesToBump = uniq(packagePolicies.map((item) => item.policy_ids).flat()); + + await agentPolicyService.bumpAgentPoliciesByIds(soClient, esClient, agentPoliciesToBump); + + logger.debug( + `Updated ${updatedCount} package policies in space ${spaceId} in ${ + Date.now() - start + }ms, bump ${agentPoliciesToBump.length} agent policies` + ); + } +} + +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: {}, + }); +} diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index cada1c8e64452..21614d2a97481 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -1646,6 +1646,27 @@ class AgentPolicyService { ); } + public async bumpAgentPoliciesByIds( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + agentPolicyIds: string[], + options?: { user?: AuthenticatedUser } + ): Promise> { + const internalSoClientWithoutSpaceExtension = + appContextService.getInternalUserSOClientWithoutSpaceExtension(); + const savedObjectType = await getAgentPolicySavedObjectType(); + + const objects = agentPolicyIds.map((id: string) => ({ id, type: savedObjectType })); + const bulkGetResponse = await soClient.bulkGet(objects); + + return this._bumpPolicies( + internalSoClientWithoutSpaceExtension, + esClient, + bulkGetResponse.saved_objects, + options + ); + } + public async getInactivityTimeouts(): Promise< Array<{ policyIds: string[]; inactivityTimeout: number }> > { diff --git a/x-pack/plugins/fleet/server/services/package_policies/utils.ts b/x-pack/plugins/fleet/server/services/package_policies/utils.ts index ef59c643a8b35..a27ac5943f80f 100644 --- a/x-pack/plugins/fleet/server/services/package_policies/utils.ts +++ b/x-pack/plugins/fleet/server/services/package_policies/utils.ts @@ -28,17 +28,16 @@ import { licenseService } from '../license'; import { outputService } from '../output'; import { appContextService } from '../app_context'; -export const mapPackagePolicySavedObjectToPackagePolicy = ({ - id, - version, - attributes, - namespaces, -}: SavedObject): PackagePolicy => { +export const mapPackagePolicySavedObjectToPackagePolicy = ( + { id, version, attributes }: SavedObject, + namespaces?: string[] +): PackagePolicy => { + const { bump_agent_policy_revision: bumpAgentPolicyRevision, ...restAttributes } = attributes; return { id, version, - spaceIds: namespaces, - ...attributes, + ...(namespaces ? { spaceIds: namespaces } : {}), + ...restAttributes, }; }; diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 48dc3956d6984..31747952173ce 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -20,6 +20,7 @@ import type { RequestHandlerContext, SavedObjectsBulkCreateObject, SavedObjectsBulkUpdateObject, + SavedObject, } from '@kbn/core/server'; import { SavedObjectsUtils } from '@kbn/core/server'; import { v4 as uuidv4 } from 'uuid'; @@ -446,7 +447,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } } - const createdPackagePolicy = { id: newSo.id, version: newSo.version, ...newSo.attributes }; + const createdPackagePolicy = mapPackagePolicySavedObjectToPackagePolicy(newSo); logger.debug(`Created new package policy with id ${newSo.id} and version ${newSo.version}`); return packagePolicyService.runExternalCallbacks( @@ -668,11 +669,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } logger.debug(`Created new package policies`); return { - created: newSos.map((newSo) => ({ - id: newSo.id, - version: newSo.version, - ...newSo.attributes, - })), + created: newSos.map((newSo) => mapPackagePolicySavedObjectToPackagePolicy(newSo)), failed: failedPolicies, }; } @@ -754,11 +751,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } } - const response = { - id: packagePolicySO.id, - version: packagePolicySO.version, - ...packagePolicySO.attributes, - }; + const response = mapPackagePolicySavedObjectToPackagePolicy(packagePolicySO); // If possible, return the experimental features map for the package policy's `package` field if (experimentalFeatures && response.package) { @@ -788,11 +781,9 @@ class PackagePolicyClientImpl implements PackagePolicyClient { return []; } - const packagePolicies = packagePolicySO.saved_objects.map((so) => ({ - id: so.id, - version: so.version, - ...so.attributes, - })); + const packagePolicies = packagePolicySO.saved_objects.map((so) => + mapPackagePolicySavedObjectToPackagePolicy(so) + ); for (const packagePolicy of packagePolicies) { auditLoggingService.writeCustomSoAuditLog({ @@ -835,11 +826,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } } - return { - id: so.id, - version: so.version, - ...so.attributes, - }; + return mapPackagePolicySavedObjectToPackagePolicy(so); }) .filter((packagePolicy): packagePolicy is PackagePolicy => packagePolicy !== null); @@ -889,12 +876,9 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } return { - items: packagePolicies?.saved_objects.map((packagePolicySO) => ({ - id: packagePolicySO.id, - version: packagePolicySO.version, - ...packagePolicySO.attributes, - spaceIds: packagePolicySO.namespaces, - })), + items: packagePolicies?.saved_objects.map((so) => + mapPackagePolicySavedObjectToPackagePolicy(so, so.namespaces) + ), total: packagePolicies?.total, page, perPage, @@ -1392,13 +1376,10 @@ class PackagePolicyClientImpl implements PackagePolicyClient { const updatedPoliciesSuccess = updatedPolicies .filter((policy) => !policy.error && policy.attributes) - .map( - (soPolicy) => - ({ - id: soPolicy.id, - version: soPolicy.version, - ...soPolicy.attributes, - } as PackagePolicy) + .map((soPolicy) => + mapPackagePolicySavedObjectToPackagePolicy( + soPolicy as SavedObject + ) ); return { updatedPolicies: updatedPoliciesSuccess, failedPolicies }; @@ -2189,7 +2170,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { perPage: SO_SEARCH_LIMIT, namespaces: ['*'], }) - ).saved_objects.map(mapPackagePolicySavedObjectToPackagePolicy); + ).saved_objects.map((so) => mapPackagePolicySavedObjectToPackagePolicy(so, so.namespaces)); if (packagePolicies.length > 0) { const getPackagePolicyUpdate = (packagePolicy: PackagePolicy) => ({ @@ -2306,7 +2287,10 @@ class PackagePolicyClientImpl implements PackagePolicyClient { savedObjectType, }); - return mapPackagePolicySavedObjectToPackagePolicy(packagePolicySO); + return mapPackagePolicySavedObjectToPackagePolicy( + packagePolicySO, + packagePolicySO.namespaces + ); }); }, }); diff --git a/x-pack/plugins/fleet/server/services/setup/fleet_server_policies_enrollment_keys.test.ts b/x-pack/plugins/fleet/server/services/setup/fleet_server_policies_enrollment_keys.test.ts index 07ec6593ec9e5..9fb30ad75ab7e 100644 --- a/x-pack/plugins/fleet/server/services/setup/fleet_server_policies_enrollment_keys.test.ts +++ b/x-pack/plugins/fleet/server/services/setup/fleet_server_policies_enrollment_keys.test.ts @@ -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 diff --git a/x-pack/plugins/fleet/server/services/setup/fleet_server_policies_enrollment_keys.ts b/x-pack/plugins/fleet/server/services/setup/fleet_server_policies_enrollment_keys.ts index f5ed816d96e61..cd7e91fb81ac4 100644 --- a/x-pack/plugins/fleet/server/services/setup/fleet_server_policies_enrollment_keys.ts +++ b/x-pack/plugins/fleet/server/services/setup/fleet_server_policies_enrollment_keys.ts @@ -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, @@ -37,7 +38,6 @@ export async function ensureAgentPoliciesFleetServerKeysAndPolicies({ }); const outdatedAgentPolicyIds: Array<{ id: string; spaceId?: string }> = []; - await pMap( agentPolicies, async (agentPolicy) => { @@ -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 }); + }); + } } } diff --git a/x-pack/plugins/fleet/server/types/rest_spec/settings.ts b/x-pack/plugins/fleet/server/types/rest_spec/settings.ts index c40dcc0b63596..64684e51350d4 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/settings.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/settings.ts @@ -61,7 +61,9 @@ export const SettingsResponseSchema = schema.object({ use_space_awareness_migration_status: schema.maybe( schema.oneOf([schema.literal('pending'), schema.literal('success'), schema.literal('error')]) ), - use_space_awareness_migration_started_at: schema.maybe(schema.string()), + use_space_awareness_migration_started_at: schema.maybe( + schema.oneOf([schema.literal(null), schema.string()]) + ), delete_unenrolled_agents: schema.maybe( schema.object({ enabled: schema.boolean(), diff --git a/x-pack/plugins/fleet/server/types/so_attributes.ts b/x-pack/plugins/fleet/server/types/so_attributes.ts index 31207dda64bc8..6f415eea9eb61 100644 --- a/x-pack/plugins/fleet/server/types/so_attributes.ts +++ b/x-pack/plugins/fleet/server/types/so_attributes.ts @@ -137,6 +137,7 @@ export interface PackagePolicySOAttributes { }; agents?: number; overrides?: any | null; + bump_agent_policy_revision?: boolean; } interface OutputSoBaseAttributes { diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts index a6bf7e7e9d5f2..e8e24f53551e8 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts @@ -143,6 +143,7 @@ export default function ({ getService }: FtrProviderContext) { 'endpoint:metadata-check-transforms-task', 'endpoint:user-artifact-packager', 'entity_store:field_retention:enrichment', + 'fleet:bump_agent_policies', 'fleet:check-deleted-files-task', 'fleet:delete-unenrolled-agents-task', 'fleet:deploy_agent_policies',