Skip to content

Commit ce6ff95

Browse files
authored
[Fleet] Add package meta variable to agent template (#241140)
1 parent a33688b commit ce6ff95

File tree

3 files changed

+121
-26
lines changed

3 files changed

+121
-26
lines changed

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

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,26 @@ mockedAppContextService.getSecuritySetup.mockImplementation(() => ({
2323

2424
let mockedLogger: jest.Mocked<Logger>;
2525

26+
function getMockedMetaVariable() {
27+
return {
28+
package: {
29+
name: 'test-package',
30+
title: 'Test Package',
31+
version: '1.0.0',
32+
},
33+
stream: {
34+
id: 'stream-id',
35+
data_stream: {
36+
dataset: 'dataset.name',
37+
type: 'logs',
38+
},
39+
},
40+
input: {
41+
id: 'input-id',
42+
},
43+
};
44+
}
45+
2646
describe('compileTemplate', () => {
2747
beforeEach(() => {
2848
mockedLogger = loggerMock.create();
@@ -62,7 +82,7 @@ multi_text_field:
6282
multi_text: { type: 'text', value: ['1234', 'foo', 'bar'] },
6383
};
6484

65-
const output = compileTemplate(vars, streamTemplate);
85+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
6686
expect(output).toEqual({
6787
input: 'log',
6888
paths: ['/usr/local/var/log/nginx/access.log'],
@@ -113,7 +133,7 @@ foo: bar
113133
nullfield: { type: 'yaml' },
114134
};
115135

116-
const output = compileTemplate(vars, streamTemplate);
136+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
117137
expect(output).toEqual({
118138
input: 'redis/metrics',
119139
metricsets: ['key'],
@@ -167,7 +187,7 @@ pcap: false
167187
tags: { value: ['foo', 'bar', 'forwarded'] },
168188
};
169189

170-
const output = compileTemplate(vars, streamTemplate);
190+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
171191
expect(output).toEqual({
172192
input: 'log',
173193
paths: ['/usr/local/var/log/nginx/access.log'],
@@ -186,7 +206,7 @@ pcap: false
186206
tags: { value: ['foo', 'bar'] },
187207
};
188208

189-
const output = compileTemplate(vars, streamTemplate);
209+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
190210
expect(output).toEqual({
191211
input: 'log',
192212
paths: ['/usr/local/var/log/nginx/access.log'],
@@ -202,7 +222,7 @@ pcap: false
202222
file: { value: 'foo.pcap' },
203223
};
204224

205-
const output = compileTemplate(vars, streamTemplateWithString);
225+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplateWithString);
206226
expect(output).toEqual({
207227
pcap: true,
208228
});
@@ -213,7 +233,7 @@ pcap: false
213233
file: { value: 'file' },
214234
};
215235

216-
const output = compileTemplate(vars, streamTemplateWithString);
236+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplateWithString);
217237
expect(output).toEqual({
218238
pcap: false,
219239
});
@@ -236,7 +256,7 @@ text_var: {{escape_string text_var}}
236256
password: { type: 'password', value: "ab'c'" },
237257
};
238258

239-
const output = compileTemplate(vars, streamTemplate);
259+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
240260
expect(output).toEqual({
241261
input: 'log',
242262
password: "ab'c'",
@@ -252,7 +272,11 @@ New lines and\\n escaped values.`,
252272
},
253273
};
254274

255-
const output = compileTemplate(vars, streamTemplateWithNewlinesAndEscapes);
275+
const output = compileTemplate(
276+
vars,
277+
getMockedMetaVariable(),
278+
streamTemplateWithNewlinesAndEscapes
279+
);
256280
expect(output).toEqual({
257281
input: 'log',
258282
text_var: `This is a text with
@@ -276,7 +300,7 @@ New lines and\n escaped values.`,
276300
},
277301
};
278302

279-
const output = compileTemplate(vars, streamTemplate);
303+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
280304
expect(output).toEqual({
281305
input: 'log',
282306
multiline_text: `This is a text with
@@ -299,7 +323,7 @@ escaped values.`,
299323
},
300324
};
301325

302-
const output = compileTemplate(vars, streamTemplate);
326+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
303327
expect(output).toEqual({
304328
input: 'log',
305329
multiline_text: `This is a multiline text with
@@ -323,7 +347,7 @@ New lines and\nescaped values.`,
323347
},
324348
};
325349

326-
const output = compileTemplate(vars, streamTemplate);
350+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
327351
expect(output).toEqual({
328352
input: 'log',
329353
multiline_text: `This is a text with
@@ -351,7 +375,7 @@ yaml_var: {{to_json yaml_var}}
351375
json_var: { type: 'text', value: `{"foo":["bar","bazz"]}` },
352376
};
353377

354-
const output = compileTemplate(vars, streamTemplate);
378+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
355379
expect(output).toEqual({
356380
input: 'log',
357381
json_var: {
@@ -371,7 +395,7 @@ yaml_var: {{to_json yaml_var}}
371395
},
372396
};
373397

374-
const output = compileTemplate(vars, streamTemplateWithNewYaml);
398+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplateWithNewYaml);
375399
expect(output).toEqual({
376400
input: 'log',
377401
yaml_var: {
@@ -395,7 +419,7 @@ input: logs
395419
},
396420
};
397421

398-
const output = compileTemplate(vars, streamTemplate);
422+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
399423
expect(output).toEqual({
400424
input: 'logs',
401425
});
@@ -413,7 +437,7 @@ input: logs
413437
},
414438
};
415439

416-
const output = compileTemplate(vars, streamTemplate);
440+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
417441
expect(output).toEqual({
418442
input: 'logs',
419443
test: '$$$$',
@@ -458,7 +482,7 @@ my-package:
458482
},
459483
};
460484

461-
const output = compileTemplate(vars, stringTemplate);
485+
const output = compileTemplate(vars, getMockedMetaVariable(), stringTemplate);
462486
expect(output).toEqual(targetOutput);
463487
});
464488

@@ -472,7 +496,7 @@ paths:
472496
`;
473497
const vars = {};
474498

475-
expect(() => compileTemplate(vars, streamTemplate)).toThrowError(
499+
expect(() => compileTemplate(vars, getMockedMetaVariable(), streamTemplate)).toThrowError(
476500
'Error while compiling agent template: options.inverse is not a function'
477501
);
478502
});
@@ -489,10 +513,35 @@ paths:
489513
},
490514
};
491515

492-
expect(() => compileTemplate(vars, template)).toThrowError(
516+
expect(() => compileTemplate(vars, getMockedMetaVariable(), template)).toThrowError(
493517
'YAMLException: Duplicated key "processors" found in agent policy yaml, please check your yaml variables.'
494518
);
495519
});
520+
521+
it('should inject package meta varaible', () => {
522+
const streamTemplate = `
523+
input: {{_meta.input.id}}
524+
package_name: {{_meta.package.name}}
525+
package_title: {{_meta.package.title}}
526+
package_version: {{_meta.package.version}}
527+
stream_id: {{_meta.stream.id}}
528+
dataset: {{_meta.stream.data_stream.dataset}}
529+
type: {{_meta.stream.data_stream.type}}
530+
`;
531+
532+
const vars = {};
533+
534+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
535+
expect(output).toEqual({
536+
input: 'input-id',
537+
package_name: 'test-package',
538+
package_title: 'Test Package',
539+
package_version: '1.0.0',
540+
stream_id: 'stream-id',
541+
dataset: 'dataset.name',
542+
type: 'logs',
543+
});
544+
});
496545
});
497546

498547
describe('encode', () => {
@@ -507,7 +556,7 @@ describe('encode', () => {
507556
hosts: { value: 'localhost', type: 'text' },
508557
};
509558

510-
const output = compileTemplate(vars, streamTemplate);
559+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
511560
expect(output).toEqual({
512561
hosts: ['sqlserver://db_elastic_agent%40%3F%23%3A:dbelasticagent%5B%21%23%402023@localhost'],
513562
});
@@ -524,7 +573,7 @@ describe('encode', () => {
524573
hosts: { value: 'localhost', type: 'text' },
525574
};
526575

527-
const output = compileTemplate(vars, streamTemplate);
576+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
528577
expect(output).toEqual({
529578
hosts: ['sqlserver://domain%5Cusername:dbelasticagent%5B%21%23%402023@localhost'],
530579
});
@@ -541,7 +590,7 @@ describe('encode', () => {
541590
hosts: { value: 'localhost', type: 'text' },
542591
};
543592

544-
const output = compileTemplate(vars, streamTemplate);
593+
const output = compileTemplate(vars, getMockedMetaVariable(), streamTemplate);
545594
expect(output).toEqual({
546595
hosts: [
547596
'sqlserver://db_elastic_agent:Special%20Characters%3A%20%21%20%2A%20%28%20%29%27@localhost',

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

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ import Handlebars from '@kbn/handlebars';
99
import { load, dump } from 'js-yaml';
1010
import type { Logger } from '@kbn/core/server';
1111

12-
import type { PackagePolicyConfigRecord } from '../../../../common/types';
12+
import type {
13+
PackageInfo,
14+
PackagePolicyConfigRecord,
15+
PackagePolicyInput,
16+
PackagePolicyInputStream,
17+
} from '../../../../common/types';
1318
import { PackagePolicyValidationError } from '../../../../common/errors';
1419
import { toCompiledSecretRef } from '../../secrets';
1520
import { PackageInvalidArchiveError } from '../../../errors';
@@ -22,9 +27,42 @@ import {
2227

2328
const handlebars = Handlebars.create();
2429

25-
export function compileTemplate(variables: PackagePolicyConfigRecord, templateStr: string) {
30+
export function getMetaVariables(
31+
pkg: Pick<PackageInfo, 'name' | 'title' | 'version'>,
32+
input: PackagePolicyInput,
33+
stream?: PackagePolicyInputStream
34+
) {
35+
return {
36+
// Package variables
37+
package: {
38+
name: pkg.name,
39+
title: pkg.title,
40+
version: pkg.version,
41+
},
42+
// Stream meta variables
43+
stream: {
44+
id: stream?.id || '',
45+
data_stream: {
46+
dataset: stream?.data_stream.dataset || '',
47+
type: stream?.data_stream.type || '',
48+
},
49+
},
50+
// Input meta variables
51+
input: {
52+
id: input?.id || '',
53+
},
54+
};
55+
}
56+
57+
export type MetaVariable = ReturnType<typeof getMetaVariables>;
58+
59+
export function compileTemplate(
60+
variables: PackagePolicyConfigRecord,
61+
metaVariable: MetaVariable,
62+
templateStr: string
63+
) {
2664
const logger = appContextService.getLogger();
27-
const { vars, yamlValues } = buildTemplateVariables(logger, variables);
65+
const { vars, yamlValues } = buildTemplateVariables(logger, variables, metaVariable);
2866
let compiledTemplate: string;
2967
try {
3068
let template = getHandlebarsCompiledTemplateCache(templateStr);
@@ -103,7 +141,11 @@ function replaceVariablesInYaml(yamlVariables: { [k: string]: any }, yaml: any)
103141
return yaml;
104142
}
105143

106-
function buildTemplateVariables(logger: Logger, variables: PackagePolicyConfigRecord) {
144+
function buildTemplateVariables(
145+
logger: Logger,
146+
variables: PackagePolicyConfigRecord,
147+
metaVariable: MetaVariable
148+
) {
107149
const yamlValues: { [k: string]: any } = {};
108150
const vars = Object.entries(variables).reduce((acc, [key, recordEntry]) => {
109151
// support variables with . like key.patterns
@@ -145,6 +187,8 @@ function buildTemplateVariables(logger: Logger, variables: PackagePolicyConfigRe
145187
return acc;
146188
}, {} as { [k: string]: any });
147189

190+
vars._meta = metaVariable;
191+
148192
return { vars, yamlValues };
149193
}
150194

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ import { getAuthzFromRequest, doesNotHaveRequiredFleetAuthz } from './security';
144144
import { agentPolicyService, getAgentPolicySavedObjectType } from './agent_policy';
145145
import { getPackageInfo, ensureInstalledPackage, getInstallationObject } from './epm/packages';
146146
import { getAssetsDataFromAssetsMap } from './epm/packages/assets';
147-
import { compileTemplate } from './epm/agent/agent';
147+
import { compileTemplate, getMetaVariables } from './epm/agent/agent';
148148
import { escapeSearchQueryPhrase, normalizeKuery } from './saved_object';
149149
import { appContextService, cloudConnectorService } from '.';
150150
import { removeOldAssets } from './epm/packages/cleanup';
@@ -3217,6 +3217,7 @@ function _compilePackagePolicyInput(
32173217
return compileTemplate(
32183218
// Populate template variables from package- and input-level vars
32193219
Object.assign({}, vars, input.vars),
3220+
getMetaVariables(pkgInfo, input),
32203221
pkgInputTemplate.buffer.toString()
32213222
);
32223223
}
@@ -3378,6 +3379,7 @@ function _compilePackageStream(
33783379
const yaml = compileTemplate(
33793380
// Populate template variables from package-, input-, and stream-level vars
33803381
Object.assign({}, vars, input.vars, stream.vars),
3382+
getMetaVariables(pkgInfo, input, streamIn),
33813383
pkgStreamTemplate.buffer.toString()
33823384
);
33833385

0 commit comments

Comments
 (0)