diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index 703730a262281..e743f325ff441 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -500,6 +500,7 @@ "description", "download_source_id", "fleet_server_host_id", + "global_data_tags", "inactivity_timeout", "is_default", "is_default_fleet_server", diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 94f2148280f08..3e38e78694dad 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -1678,6 +1678,10 @@ "fleet_server_host_id": { "type": "keyword" }, + "global_data_tags": { + "index": false, + "type": "flattened" + }, "inactivity_timeout": { "type": "integer" }, 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 1de50f0547b35..be06461d0015d 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 @@ -109,7 +109,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infra-custom-dashboards": "1a5994f2e05bb8a1609825ddbf5012f77c5c67f3", "infrastructure-monitoring-log-view": "5f86709d3c27aed7a8379153b08ee5d3d90d77f5", "infrastructure-ui-source": "113182d6895764378dfe7fa9fa027244f3a457c4", - "ingest-agent-policies": "803dc27e106440c41e8f3c3d8ee8bbb0821bcde2", + "ingest-agent-policies": "90625b4a5ded9d4867358fcccc14a57c0454fcee", "ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d", "ingest-outputs": "daafff49255ab700e07491376fe89f04fc998b91", "ingest-package-policies": "44c682a6bf23993c665f0a60a427f3c120a0a10d", diff --git a/x-pack/plugins/fleet/common/constants/epm.ts b/x-pack/plugins/fleet/common/constants/epm.ts index 562f099fdd3ca..e10485affa87f 100644 --- a/x-pack/plugins/fleet/common/constants/epm.ts +++ b/x-pack/plugins/fleet/common/constants/epm.ts @@ -25,6 +25,38 @@ export const FLEET_CLOUD_SECURITY_POSTURE_KSPM_POLICY_TEMPLATE = 'kspm'; export const FLEET_CLOUD_SECURITY_POSTURE_CSPM_POLICY_TEMPLATE = 'cspm'; export const FLEET_CLOUD_SECURITY_POSTURE_CNVM_POLICY_TEMPLATE = 'vuln_mgmt'; export const FLEET_CLOUD_DEFEND_PACKAGE = 'cloud_defend'; +export const FLEET_PF_HOST_AGENT_PACKAGE = 'pf-host-agent'; +export const FLEET_PF_ELASTIC_SYMBOLIZER_PACKAGE = 'pf-elastic-symbolizer'; +export const FLEET_PF_ELASTIC_COLLECTOR_PACKAGE = 'pf-elastic-collector'; +export const FLEET_CLOUD_BEAT_PACKAGE = 'cloudbeat'; +export const FLEET_CLOUD_BEAT_CIS_K8S_PACKAGE = `${FLEET_CLOUD_BEAT_PACKAGE}/cis_k8s`; +export const FLEET_CLOUD_BEAT_CIS_EKS_PACKAGE = `${FLEET_CLOUD_BEAT_PACKAGE}/cis_eks`; +export const FLEET_CLOUD_BEAT_CIS_AWS_PACKAGE = `${FLEET_CLOUD_BEAT_PACKAGE}/cis_aws`; +export const FLEET_CLOUD_BEAT_CIS_GCP_PACKAGE = `${FLEET_CLOUD_BEAT_PACKAGE}/cis_gcp`; +export const FLEET_CLOUD_BEAT_CIS_AZURE_PACKAGE = `${FLEET_CLOUD_BEAT_PACKAGE}/cis_azure`; +export const FLEET_CLOUD_BEAT_VULN_MGMT_AWS_PACKAGE = `${FLEET_CLOUD_BEAT_PACKAGE}/vuln_mgmt_aws`; + +export const GLOBAL_DATA_TAG_EXCLUDED_INPUTS = new Set([ + FLEET_APM_PACKAGE, + FLEET_PF_HOST_AGENT_PACKAGE, + FLEET_PF_ELASTIC_SYMBOLIZER_PACKAGE, + FLEET_PF_ELASTIC_COLLECTOR_PACKAGE, + /* The package names and input types are not the same. For example package + * name for fleet server is "fleet_server" whereas the input type is "fleet-server". + * This is the same case for cloud defend. That's why we are replacing the + * underscores with dashes for the two of them. Global data tag functionality + * relies on input types. + */ + FLEET_SERVER_PACKAGE.replace(/_/g, '-'), + FLEET_CLOUD_DEFEND_PACKAGE.replace(/_/g, '-'), + FLEET_CLOUD_BEAT_PACKAGE, + FLEET_CLOUD_BEAT_CIS_K8S_PACKAGE, + FLEET_CLOUD_BEAT_CIS_EKS_PACKAGE, + FLEET_CLOUD_BEAT_CIS_AWS_PACKAGE, + FLEET_CLOUD_BEAT_CIS_GCP_PACKAGE, + FLEET_CLOUD_BEAT_CIS_AZURE_PACKAGE, + FLEET_CLOUD_BEAT_VULN_MGMT_AWS_PACKAGE, +]); export const PACKAGE_TEMPLATE_SUFFIX = '@package'; export const USER_SETTINGS_TEMPLATE_SUFFIX = '@custom'; diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index 5cbc85dd12ce1..709692b82c6ac 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -7660,6 +7660,23 @@ "supports_agentless": { "type": "boolean", "description": "Indicates whether the agent policy supports agentless integrations. Only allowed in a serverless environment." + }, + "global_data_tags": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + }, + "description": "User defined data tags that are added to all of the inputs. The values can be strings or numbers." + } } }, "required": [ @@ -7741,6 +7758,23 @@ "force": { "type": "boolean", "description": "Force agent policy creation even if packages are not verified." + }, + "global_data_tags": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + }, + "description": "User defined data tags that are added to all of the inputs. The values can be strings or numbers." + } } }, "required": [ diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index f0f5bf761ada8..93ed244d05805 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -4933,6 +4933,17 @@ components: description: >- Indicates whether the agent policy supports agentless integrations. Only allowed in a serverless environment. + global_data_tags: + type: array + items: + type: object + additionalProperties: + oneOf: + - type: string + - type: number + description: >- + User defined data tags that are added to all of the inputs. The + values can be strings or numbers. required: - id - status @@ -4990,6 +5001,17 @@ components: force: type: boolean description: Force agent policy creation even if packages are not verified. + global_data_tags: + type: array + items: + type: object + additionalProperties: + oneOf: + - type: string + - type: number + description: >- + User defined data tags that are added to all of the inputs. The + values can be strings or numbers. required: - name - namespace diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml index 5f8a2f578132e..02fd5c2800a48 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml @@ -78,6 +78,15 @@ properties: supports_agentless: type: boolean description: Indicates whether the agent policy supports agentless integrations. Only allowed in a serverless environment. + global_data_tags: + type: array + items: + type: object + additionalProperties: + oneOf: + - type: string + - type: number + description: User defined data tags that are added to all of the inputs. The values can be strings or numbers. required: - id - status diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy_create_request.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy_create_request.yaml index d2b69e37672e8..1d39b911b3007 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy_create_request.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy_create_request.yaml @@ -49,6 +49,15 @@ properties: force: type: boolean description: Force agent policy creation even if packages are not verified. + global_data_tags: + type: array + items: + type: object + additionalProperties: + oneOf: + - type: string + - type: number + description: User defined data tags that are added to all of the inputs. The values can be strings or numbers. required: - name - namespace diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index 9bae6421050de..7243c54007fd9 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -42,6 +42,12 @@ export interface NewAgentPolicy { advanced_settings?: { [key: string]: any } | null; keep_monitoring_alive?: boolean | null; supports_agentless?: boolean | null; + global_data_tags?: GlobalDataTag[]; +} + +export interface GlobalDataTag { + name: string; + value: string | number; } // SO definition for this type is declared in server/types/interfaces @@ -80,9 +86,19 @@ export interface FullAgentPolicyInput { [key: string]: unknown; }; streams?: FullAgentPolicyInputStream[]; + processors?: FullAgentPolicyAddFields[]; [key: string]: any; } +export interface FullAgentPolicyAddFields { + add_fields: { + target: string; + fields: { + [key: string]: string | number; + }; + }; +} + export type FullAgentPolicyOutputPermissions = Record; export type FullAgentPolicyOutput = Pick & { diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 8e058a3a3ff8f..bb8eaba04a6c7 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -167,6 +167,7 @@ export const getSavedObjectTypes = ( keep_monitoring_alive: { type: 'boolean' }, advanced_settings: { type: 'flattened', index: false }, supports_agentless: { type: 'boolean' }, + global_data_tags: { type: 'flattened', index: false }, }, }, migrations: { @@ -197,6 +198,16 @@ export const getSavedObjectTypes = ( }, ], }, + '3': { + changes: [ + { + type: 'mappings_addition', + addedMappings: { + global_data_tags: { type: 'flattened', index: false }, + }, + }, + ], + }, }, }, [OUTPUT_SAVED_OBJECT_TYPE]: { diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index bec47eea524c4..bcc6956711b64 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -118,7 +118,8 @@ export async function getFullAgentPolicy( agentPolicy.package_policies as PackagePolicy[], packageInfoCache, getOutputIdForAgentPolicy(dataOutput), - agentPolicy.namespace + agentPolicy.namespace, + agentPolicy.global_data_tags ); const features = (agentPolicy.agent_features || []).reduce((acc, { name, ...featureConfig }) => { acc[name] = featureConfig; diff --git a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.test.ts index 23998b7f7cdd1..eb94fc7a1c67b 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.test.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { GLOBAL_DATA_TAG_EXCLUDED_INPUTS } from '../../../common/constants/epm'; + import type { PackagePolicy, PackagePolicyInput } from '../../types'; import { storedPackagePoliciesToAgentInputs } from './package_policies_to_agent_inputs'; @@ -737,4 +739,141 @@ describe('Fleet - storedPackagePoliciesToAgentInputs', () => { }, ]); }); + + it('returns agent inputs with add fields process if global data tags are defined', async () => { + const excludedInputs: PackagePolicyInput[] = []; + const expectedExcluded = []; + + for (const input of GLOBAL_DATA_TAG_EXCLUDED_INPUTS) { + excludedInputs.push({ + type: input, + enabled: true, + vars: { + inputVar: { value: 'input-value' }, + inputVar2: { value: undefined }, + inputVar3: { + type: 'yaml', + value: 'testField: test', + }, + inputVar4: { value: '' }, + }, + streams: [], + }); + + expectedExcluded.push({ + data_stream: { + namespace: 'default', + }, + id: `${input}-some-uuid`, + meta: { + package: { + name: 'mock_package', + version: '0.0.0', + }, + }, + name: 'mock_package-policy', + package_policy_id: 'some-uuid', + revision: 1, + type: input, + use_output: 'default', + }); + } + + expect( + await storedPackagePoliciesToAgentInputs( + [ + { + ...mockPackagePolicy, + package: { + name: 'mock_package', + title: 'Mock package', + version: '0.0.0', + }, + inputs: [ + ...excludedInputs, + { + ...mockInput, + compiled_input: { + inputVar: 'input-value', + }, + streams: [], + }, + { + ...mockInput2, + compiled_input: { + inputVar: 'input-value', + }, + streams: [], + }, + ], + }, + ], + packageInfoCache, + undefined, + undefined, + [ + { name: 'testName', value: 'testValue' }, + { name: 'testName2', value: 'testValue2' }, + ] + ) + ).toEqual([ + ...expectedExcluded, + { + id: 'test-logs-some-uuid', + name: 'mock_package-policy', + package_policy_id: 'some-uuid', + processors: [ + { + add_fields: { + fields: { + testName: 'testValue', + testName2: 'testValue2', + }, + target: '', + }, + }, + ], + revision: 1, + type: 'test-logs', + data_stream: { namespace: 'default' }, + use_output: 'default', + meta: { + package: { + name: 'mock_package', + version: '0.0.0', + }, + }, + inputVar: 'input-value', + }, + { + id: 'test-metrics-some-template-some-uuid', + data_stream: { + namespace: 'default', + }, + inputVar: 'input-value', + meta: { + package: { + name: 'mock_package', + version: '0.0.0', + }, + }, + name: 'mock_package-policy', + package_policy_id: 'some-uuid', + processors: [ + { + add_fields: { + target: '', + fields: { + testName: 'testValue', + testName2: 'testValue2', + }, + }, + }, + ], + revision: 1, + type: 'test-metrics', + use_output: 'default', + }, + ]); + }); }); diff --git a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.ts b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.ts index 3783f0035bf80..0a1643f293e58 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.ts @@ -7,6 +7,7 @@ import { merge } from 'lodash'; import deepMerge from 'deepmerge'; +import type { FullAgentPolicyAddFields, GlobalDataTag } from '../../../common/types'; import { isPackageLimited } from '../../../common/services'; import type { PackagePolicy, @@ -17,6 +18,7 @@ import type { } from '../../types'; import { DEFAULT_OUTPUT } from '../../constants'; import { pkgToPkgKey } from '../epm/registry'; +import { GLOBAL_DATA_TAG_EXCLUDED_INPUTS } from '../../../common/constants/epm'; const isPolicyEnabled = (packagePolicy: PackagePolicy) => { return packagePolicy.enabled && packagePolicy.inputs && packagePolicy.inputs.length; @@ -26,7 +28,8 @@ export const storedPackagePolicyToAgentInputs = ( packagePolicy: PackagePolicy, packageInfo?: PackageInfo, outputId: string = DEFAULT_OUTPUT.name, - agentPolicyNamespace?: string + agentPolicyNamespace?: string, + addFields?: FullAgentPolicyAddFields ): FullAgentPolicyInput[] => { const fullInputs: FullAgentPolicyInput[] = []; @@ -64,6 +67,10 @@ export const storedPackagePolicyToAgentInputs = ( ...getFullInputStreams(input), }; + if (addFields && !GLOBAL_DATA_TAG_EXCLUDED_INPUTS.has(fullInput.type)) { + fullInput.processors = [addFields]; + } + // deeply merge the input.config values with the full policy input merge( fullInput, @@ -134,10 +141,13 @@ export const storedPackagePoliciesToAgentInputs = async ( packagePolicies: PackagePolicy[], packageInfoCache: Map, outputId: string = DEFAULT_OUTPUT.name, - agentPolicyNamespace?: string + agentPolicyNamespace?: string, + globalDataTags?: GlobalDataTag[] ): Promise => { const fullInputs: FullAgentPolicyInput[] = []; + const addFields = globalDataTags ? globalDataTagsToAddFields(globalDataTags) : undefined; + for (const packagePolicy of packagePolicies) { if (!isPolicyEnabled(packagePolicy)) { continue; @@ -152,10 +162,26 @@ export const storedPackagePoliciesToAgentInputs = async ( packagePolicy, packageInfo, outputId, - agentPolicyNamespace + agentPolicyNamespace, + addFields ) ); } return fullInputs; }; + +const globalDataTagsToAddFields = (tags: GlobalDataTag[]): FullAgentPolicyAddFields => { + const fields: { [key: string]: string | number } = {}; + + tags.forEach((tag) => { + fields[tag.name] = tag.value; + }); + + return { + add_fields: { + target: '', + fields, + }, + }; +}; diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index 635948a5ca4e3..3e564d0fe8536 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -664,6 +664,7 @@ class AgentPolicyService { 'download_source_id', 'fleet_server_host_id', 'supports_agentless', + 'global_data_tags', ]), ...newAgentPolicyProps, }, diff --git a/x-pack/plugins/fleet/server/types/models/agent_policy.test.ts b/x-pack/plugins/fleet/server/types/models/agent_policy.test.ts new file mode 100644 index 0000000000000..2dea7144df4f5 --- /dev/null +++ b/x-pack/plugins/fleet/server/types/models/agent_policy.test.ts @@ -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 { GlobalDataTag } from '../../../common/types'; + +import { AgentPolicyBaseSchema } from './agent_policy'; + +describe('AgentPolicyBaseSchema', () => { + describe('global_data_tags validations', () => { + it('should throw an error if provided with duplicate tag names', () => { + const tags: GlobalDataTag[] = [ + { + name: 'testName', + value: 'testValue', + }, + { + name: 'testName', + value: 'testValue2', + }, + { + name: 'anotherName', + value: 'value', + }, + { + name: 'anotherName', + value: 'value2', + }, + ]; + + expect(() => { + AgentPolicyBaseSchema.global_data_tags.validate(tags); + }).toThrow( + `Found duplicate tag names: ['testName', 'anotherName'], duplicate tag names are not allowed.` + ); + }); + + it('should throw an error if provided with tag names with spaces in it', () => { + const tags: GlobalDataTag[] = [ + { + name: ' testName', + value: 'testValue', + }, + { + name: 'test Name', + value: 'testValue2', + }, + ]; + + expect(() => { + AgentPolicyBaseSchema.global_data_tags.validate(tags); + }).toThrow( + `Found tag names with spaces: [' testName', 'test Name'], tag names with spaces are not allowed.` + ); + }); + + it('should throw an error showing all validation errors', () => { + const tags: GlobalDataTag[] = [ + { + name: ' testName', + value: 'testValue', + }, + { + name: 'testName ', + value: 'testValue2', + }, + ]; + + expect(() => { + AgentPolicyBaseSchema.global_data_tags.validate(tags); + }).toThrow( + `Found duplicate tag names: ['testName'], duplicate tag names are not allowed. Found tag names with spaces: [' testName', 'testName '], tag names with spaces are not allowed.` + ); + }); + + it('should not throw an error if provided with valid global data tags', () => { + const tags: GlobalDataTag[] = [ + { + name: 'testName', + value: 'testValue', + }, + { + name: 'anotherName', + value: 'anotherValue', + }, + ]; + + expect(() => { + AgentPolicyBaseSchema.global_data_tags.validate(tags); + }).not.toThrow(); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/types/models/agent_policy.ts b/x-pack/plugins/fleet/server/types/models/agent_policy.ts index ed2a50854c58b..43928b3737e21 100644 --- a/x-pack/plugins/fleet/server/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/agent_policy.ts @@ -7,6 +7,8 @@ import { schema } from '@kbn/config-schema'; +import type { GlobalDataTag } from '../../../common/types'; + import { agentPolicyStatuses, dataTypes } from '../../../common/constants'; import { isValidNamespace } from '../../../common/services'; import { getSettingsAPISchema } from '../../services/form_settings'; @@ -84,8 +86,55 @@ export const AgentPolicyBaseSchema = { ), ...getSettingsAPISchema('AGENT_POLICY_ADVANCED_SETTINGS'), supports_agentless: schema.maybe(schema.boolean({ defaultValue: false })), + global_data_tags: schema.maybe( + schema.arrayOf( + schema.object({ + name: schema.string(), + value: schema.oneOf([schema.string(), schema.number()]), + }), + { + validate: validateGlobalDataTagInput, + } + ) + ), }; +function validateGlobalDataTagInput(tags: GlobalDataTag[]): string | undefined { + const seen = new Set([]); + const duplicates: string[] = []; + const namesWithSpaces: string[] = []; + const errors: string[] = []; + + for (const tag of tags) { + if (/\s/.test(tag.name)) { + namesWithSpaces.push(`'${tag.name}'`); + } + + if (!seen.has(tag.name.trim())) { + seen.add(tag.name.trim()); + } else { + duplicates.push(`'${tag.name.trim()}'`); + } + } + + if (duplicates.length !== 0) { + errors.push( + `Found duplicate tag names: [${duplicates.join(', ')}], duplicate tag names are not allowed.` + ); + } + if (namesWithSpaces.length !== 0) { + errors.push( + `Found tag names with spaces: [${namesWithSpaces.join( + ', ' + )}], tag names with spaces are not allowed.` + ); + } + + if (errors.length !== 0) { + return errors.join(' '); + } +} + export const NewAgentPolicySchema = schema.object({ ...AgentPolicyBaseSchema, force: schema.maybe(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 8db683ba136de..ac084733b81d7 100644 --- a/x-pack/plugins/fleet/server/types/so_attributes.ts +++ b/x-pack/plugins/fleet/server/types/so_attributes.ts @@ -64,6 +64,7 @@ export interface AgentPolicySOAttributes { package_policies?: PackagePolicy[]; agents?: number; overrides?: any | null; + global_data_tags?: Array<{ name: string; value: string | number }>; } export interface AgentSOAttributes { diff --git a/x-pack/test/fleet_api_integration/apis/agent_policy/agent_policy.ts b/x-pack/test/fleet_api_integration/apis/agent_policy/agent_policy.ts index 3d13f43531f3f..625f8a444f608 100644 --- a/x-pack/test/fleet_api_integration/apis/agent_policy/agent_policy.ts +++ b/x-pack/test/fleet_api_integration/apis/agent_policy/agent_policy.ts @@ -437,6 +437,40 @@ export default function (providerContext: FtrProviderContext) { expect(policy.package_policies[0].name).be('system-457'); }); + + it('should create policy with global data tags given valid tags', async () => { + const { + body: { item: createdPolicy }, + } = await supertest + .post(`/api/fleet/agent_policies?sys_monitoring=true`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'global data tag test', + namespace: 'default', + global_data_tags: [ + { name: 'testName', value: 'testValue' }, + { name: 'testName2', value: 123 }, + ], + }) + .expect(200); + + let res = await supertest.get(`/api/fleet/agent_policies/${createdPolicy.id}`).expect(200); + expect(res.body.item.global_data_tags).to.eql([ + { name: 'testName', value: 'testValue' }, + { name: 'testName2', value: 123 }, + ]); + + res = await supertest.get(`/api/fleet/agent_policies/${createdPolicy.id}/full`).expect(200); + for (const input of res.body.item.inputs) { + expect(input.processors).not.to.equal(undefined); + expect(input.processors.length).to.equal(1); + const addFields = input.processors[0].add_fields; + expect(addFields).to.eql({ + fields: { testName: 'testValue', testName2: 123 }, + target: '', + }); + } + }); }); describe('POST /api/fleet/agent_policies/{agentPolicyId}/copy', () => { @@ -838,6 +872,33 @@ export default function (providerContext: FtrProviderContext) { }) .expect(409); }); + + it('should copy global data tags', async () => { + const { + body: { item: policyWithGlobalDataTags }, + } = await supertest + .post(`/api/fleet/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'Global Data Tag Test', + namespace: 'default', + global_data_tags: [{ name: 'testName', value: 'testValue' }], + }) + .expect(200); + + const { + body: { item: newPolicy }, + } = await supertest + .post(`/api/fleet/agent_policies/${policyWithGlobalDataTags.id}/copy`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'Global Data Tag Test Copy', + description: 'Test', + }) + .expect(200); + + expect(newPolicy.global_data_tags).to.eql([{ name: 'testName', value: 'testValue' }]); + }); }); describe('PUT /api/fleet/agent_policies/{agentPolicyId}', () => { @@ -1032,7 +1093,6 @@ export default function (providerContext: FtrProviderContext) { ); expect(installedPackages.length).to.be(0); - agentPolicyId = originalPolicy.id; const { body: { item: updatedPolicy }, @@ -1159,6 +1219,38 @@ export default function (providerContext: FtrProviderContext) { }) .expect(400); }); + + it('should overwrite global data tags if provided with valid input', async () => { + const { + body: { item: originalPolicy }, + } = await supertest + .post(`/api/fleet/agent_policies?sys_monitoring=true`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'TEST', + namespace: 'default', + global_data_tags: [ + { name: 'testName', value: 'testValue' }, + { name: 'testName2', value: 123 }, + ], + }) + .expect(200); + createdPolicyIds.push(originalPolicy.id as string); + + const { + body: { item: updatedPolicy }, + } = await supertest + .put(`/api/fleet/agent_policies/${originalPolicy.id}`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: originalPolicy.name, + namespace: 'default', + global_data_tags: [{ name: 'newTag', value: 'newValue' }], + }) + .expect(200); + + expect(updatedPolicy.global_data_tags).to.eql([{ name: 'newTag', value: 'newValue' }]); + }); }); describe('POST /api/fleet/agent_policies/delete', () => {