From d9aca86bbcda0a5b7c6e701f0a32f7e919c61658 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Tue, 21 May 2024 09:09:44 -0400 Subject: [PATCH] [Fleet] Support kibana_namespace in preconfiguration --- .../common/types/models/preconfiguration.ts | 1 + .../fleet/server/saved_objects/index.ts | 4 +- .../fleet/server/services/agent_policy.ts | 6 +- .../server/services/preconfiguration.test.ts | 64 +++++++++++++++++++ .../fleet/server/services/preconfiguration.ts | 39 +++++++---- .../server/types/models/preconfiguration.ts | 1 + 6 files changed, 101 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/fleet/common/types/models/preconfiguration.ts b/x-pack/plugins/fleet/common/types/models/preconfiguration.ts index 8363116bf3bb1..4a0823aa82a93 100644 --- a/x-pack/plugins/fleet/common/types/models/preconfiguration.ts +++ b/x-pack/plugins/fleet/common/types/models/preconfiguration.ts @@ -24,6 +24,7 @@ export type InputsOverride = Partial & { export interface PreconfiguredAgentPolicy extends Omit { id: string | number; + kibana_namespace?: string; namespace?: string; package_policies: Array< | (Partial> & { diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 457711b2646e7..8e058a3a3ff8f 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -587,7 +587,7 @@ export const getSavedObjectTypes = ( name: PACKAGES_SAVED_OBJECT_TYPE, indexPattern: INGEST_SAVED_OBJECT_INDEX, hidden: false, - namespaceType: useSpaceAwareness ? 'single' : 'agnostic', + namespaceType: 'agnostic', management: { importableAndExportable: false, }, @@ -680,7 +680,7 @@ export const getSavedObjectTypes = ( name: ASSETS_SAVED_OBJECT_TYPE, indexPattern: INGEST_SAVED_OBJECT_INDEX, hidden: false, - namespaceType: useSpaceAwareness ? 'single' : 'agnostic', + namespaceType: 'agnostic', management: { importableAndExportable: false, }, diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index afa7b7c5d92de..4eb5f6e348842 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -195,7 +195,11 @@ class AgentPolicyService { created: boolean; policy?: AgentPolicy; }> { - const { id, ...preconfiguredAgentPolicy } = omit(config, 'package_policies'); + const { + id, + kibana_namespace: kibanaNamespace, + ...preconfiguredAgentPolicy + } = omit(config, 'package_policies'); const newAgentPolicyDefaults: Pick = { namespace: 'default', monitoring_enabled: ['logs', 'metrics'], diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.test.ts b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts index 004d4b83a460a..2c7d1452914fe 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.test.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts @@ -306,6 +306,7 @@ jest.mock('./app_context', () => ({ getExperimentalFeatures: jest.fn().mockReturnValue({ agentless: false, }), + getInternalUserSOClientForSpaceId: jest.fn(), }, })); @@ -1124,6 +1125,69 @@ describe('policy preconfiguration', () => { expect(policies[0].id).toBe('test-id'); expect(nonFatalErrorsB.length).toBe(0); }); + + it('should used a namespaced saved objet client if the agent policy kibana_namespace is set', async () => { + const TEST_NAMESPACE = 'test'; + const namespacedSOClient = getPutPreconfiguredPackagesMock(); + const soClient = getPutPreconfiguredPackagesMock(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + + jest + .mocked(appContextService) + .getInternalUserSOClientForSpaceId.mockReturnValue(namespacedSOClient); + + await ensurePreconfiguredPackagesAndPolicies( + soClient, + esClient, + [ + { + name: 'Test policy', + namespace: 'default', + id: 'test-id', + kibana_namespace: TEST_NAMESPACE, + is_managed: true, + package_policies: [ + { + package: { name: 'test_package' }, + name: 'test_package1', + }, + ], + }, + ] as PreconfiguredAgentPolicy[], + [{ name: 'test_package', version: '3.0.0' }], + mockDefaultOutput, + mockDefaultDownloadService, + DEFAULT_SPACE_ID + ); + + jest.mocked(appContextService.getExperimentalFeatures).mockReturnValue({ + agentless: true, + } as any); + + expect(appContextService.getInternalUserSOClientForSpaceId).toBeCalledTimes(1); + expect(appContextService.getInternalUserSOClientForSpaceId).toBeCalledWith(TEST_NAMESPACE); + + expect(mockedPackagePolicyService.create).toBeCalledTimes(1); + expect(mockedPackagePolicyService.create).toBeCalledWith( + namespacedSOClient, // namespaced so client + expect.anything(), // es client + expect.objectContaining({ + name: 'test_package1', + }), + expect.anything() // options + ); + + expect(spyAgentPolicyServiceUpdate).toBeCalledTimes(1); + expect(spyAgentPolicyServiceUpdate).toBeCalledWith( + namespacedSOClient, // namespaced so client + expect.anything(), // es client + expect.anything(), // id + expect.objectContaining({ + is_managed: true, + }), + expect.anything() // options + ); + }); }); describe('with bundled packages', () => { diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.ts b/x-pack/plugins/fleet/server/services/preconfiguration.ts index 26f633cc8f021..3e61e0ba65459 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.ts @@ -174,14 +174,20 @@ export async function ensurePreconfiguredPackagesAndPolicies( ); } + const namespacedSoClient = preconfiguredAgentPolicy.kibana_namespace + ? appContextService.getInternalUserSOClientForSpaceId( + preconfiguredAgentPolicy.kibana_namespace + ) + : soClient; + const { created, policy } = await agentPolicyService.ensurePreconfiguredAgentPolicy( - soClient, + namespacedSoClient, esClient, - omit(preconfiguredAgentPolicy, 'is_managed') // Don't add `is_managed` until the policy has been fully configured + omit(preconfiguredAgentPolicy, 'is_managed', 'kibana_namespace') // Don't add `is_managed` until the policy has been fully configured ); if (!created) { - if (!policy) return { created, policy }; + if (!policy) return { created, policy, namespacedSoClient }; if (!policy.is_managed && !preconfiguredAgentPolicy.is_managed) return { created, policy }; const { hasChanged, fields } = comparePreconfiguredPolicyToCurrent( preconfiguredAgentPolicy, @@ -194,7 +200,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( }; if (hasChanged) { const updatedPolicy = await agentPolicyService.update( - soClient, + namespacedSoClient, esClient, String(preconfiguredAgentPolicy.id), newFields, @@ -202,14 +208,15 @@ export async function ensurePreconfiguredPackagesAndPolicies( force: true, } ); - return { created, policy: updatedPolicy }; + return { created, policy: updatedPolicy, namespacedSoClient }; } - return { created, policy }; + return { created, policy, namespacedSoClient }; } return { created, policy, + namespacedSoClient, shouldAddIsManagedFlag: preconfiguredAgentPolicy.is_managed, }; }) @@ -227,13 +234,17 @@ export async function ensurePreconfiguredPackagesAndPolicies( continue; } fulfilledPolicies.push(policyResult.value); - const { created, policy, shouldAddIsManagedFlag } = policyResult.value; + const { created, policy, shouldAddIsManagedFlag, namespacedSoClient } = policyResult.value; + if (created || policies[i].is_managed) { + if (!namespacedSoClient) { + throw new Error('No soClient created for that policy'); + } const preconfiguredAgentPolicy = policies[i]; const { package_policies: packagePolicies } = preconfiguredAgentPolicy; const agentPolicyWithPackagePolicies = await agentPolicyService.get( - soClient, + namespacedSoClient, policy!.id, true ); @@ -287,7 +298,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( logger.debug(`Adding preconfigured package policies ${packagePoliciesToAdd}`); const s = apm.startSpan('Add preconfigured package policies', 'preconfiguration'); await addPreconfiguredPolicyPackages( - soClient, + namespacedSoClient, esClient, policy!, packagePoliciesToAdd!, @@ -299,7 +310,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( // Add the is_managed flag after configuring package policies to avoid errors if (shouldAddIsManagedFlag) { await agentPolicyService.update( - soClient, + namespacedSoClient, esClient, policy!.id, { is_managed: true }, @@ -338,7 +349,13 @@ export function comparePreconfiguredPolicyToCurrent( ) { // Namespace is omitted from being compared because even for managed policies, we still // want users to be able to pick their own namespace: https://github.com/elastic/kibana/issues/110533 - const configTopLevelFields = omit(policyFromConfig, 'package_policies', 'id', 'namespace'); + const configTopLevelFields = omit( + policyFromConfig, + 'package_policies', + 'id', + 'namespace', + 'kibana_namespace' + ); const currentTopLevelFields = pick(currentPolicy, ...Object.keys(configTopLevelFields)); return { diff --git a/x-pack/plugins/fleet/server/types/models/preconfiguration.ts b/x-pack/plugins/fleet/server/types/models/preconfiguration.ts index ce8fb7df3937d..297702e7c8b15 100644 --- a/x-pack/plugins/fleet/server/types/models/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/types/models/preconfiguration.ts @@ -134,6 +134,7 @@ export const PreconfiguredFleetProxiesSchema = schema.arrayOf( export const PreconfiguredAgentPoliciesSchema = schema.arrayOf( schema.object({ ...AgentPolicyBaseSchema, + kibana_namespace: schema.maybe(schema.string()), namespace: schema.maybe(AgentPolicyNamespaceSchema), id: schema.maybe(schema.oneOf([schema.string(), schema.number()])), is_default: schema.maybe(schema.boolean()),