mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Fleet] Create template endpoint for integrations inputs (#168015)
Closes https://github.com/elastic/kibana/issues/167325 ## Summary Create a new endpoints that returns template integrations inputs with all possible options enabled. It's going to be used for enhancing the standalone flow. This is basically equivalent to the following flow: - Add an integration to a new agent policy - Enable all the options in the package policy - Go to the agent policy page and select action "view agent policy" - Copy only the `inputs` section of it. Actually the api returns the `streams` fields of the inputs and applies a `flatMap`, so the format is slightly different. Note that the api returns the template even when the integration is not installed, since the endpoint retrieves the packageInfo from cache or registry (depending on what it finds). I looked for a way to preserve comments in yaml but it seems that the [js-yaml](https://github.com/nodeca/js-yaml) library does not have this capability. ### Testing Integration that has `inputs.streams` (`yml` and `yaml` options are equivalent): ``` GET kbn:api/fleet/epm/templates/nginx/1.15.0/inputs?format=json GET kbn:api/fleet/epm/templates/nginx/1.15.0/inputs?format=yml GET kbn:api/fleet/epm/templates/nginx/1.15.0/inputs?format=yaml ``` Integration that has only `compiled_inputs`: ``` GET kbn:api/fleet/epm/templates/apm/8.4.2/inputs?format=json GET kbn:api/fleet/epm/templates/apm/8.4.2/inputs?format=yaml ``` ### Checklist - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
348563b52f
commit
229b883e50
15 changed files with 803 additions and 4 deletions
|
@ -41,6 +41,7 @@ export const EPM_API_ROUTES = {
|
|||
VERIFICATION_KEY_ID: `${EPM_API_ROOT}/verification_key_id`,
|
||||
STATS_PATTERN: `${EPM_PACKAGES_MANY}/{pkgName}/stats`,
|
||||
BULK_ASSETS_PATTERN: `${EPM_API_ROOT}/bulk_assets`,
|
||||
INPUTS_PATTERN: `${EPM_API_ROOT}/templates/{pkgName}/{pkgVersion}/inputs`,
|
||||
|
||||
INFO_PATTERN_DEPRECATED: EPM_PACKAGES_ONE_DEPRECATED,
|
||||
INSTALL_FROM_REGISTRY_PATTERN_DEPRECATED: EPM_PACKAGES_ONE_DEPRECATED,
|
||||
|
|
|
@ -1420,6 +1420,56 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"/epm/templates/{pkgName}/{pkgVersion}/inputs": {
|
||||
"get": {
|
||||
"summary": "Get inputs template",
|
||||
"tags": [
|
||||
"Elastic Package Manager (EPM)"
|
||||
],
|
||||
"responses": {
|
||||
"400": {
|
||||
"$ref": "#/components/responses/error"
|
||||
}
|
||||
},
|
||||
"operationId": "get-inputs-template",
|
||||
"security": [
|
||||
{
|
||||
"basicAuth": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": "pkgName",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": "pkgVersion",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"json",
|
||||
"yaml",
|
||||
"yml"
|
||||
]
|
||||
},
|
||||
"name": "format",
|
||||
"description": "Format of response - json or yaml",
|
||||
"in": "query"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/agents/setup": {
|
||||
"get": {
|
||||
"summary": "Get agent setup info",
|
||||
|
|
|
@ -894,6 +894,37 @@ paths:
|
|||
name: pkgName
|
||||
in: path
|
||||
required: true
|
||||
/epm/templates/{pkgName}/{pkgVersion}/inputs:
|
||||
get:
|
||||
summary: Get inputs template
|
||||
tags:
|
||||
- Elastic Package Manager (EPM)
|
||||
responses:
|
||||
'400':
|
||||
$ref: '#/components/responses/error'
|
||||
operationId: get-inputs-template
|
||||
security:
|
||||
- basicAuth: []
|
||||
parameters:
|
||||
- schema:
|
||||
type: string
|
||||
name: pkgName
|
||||
in: path
|
||||
required: true
|
||||
- schema:
|
||||
type: string
|
||||
name: pkgVersion
|
||||
in: path
|
||||
required: true
|
||||
- schema:
|
||||
type: string
|
||||
enum:
|
||||
- json
|
||||
- yaml
|
||||
- yml
|
||||
name: format
|
||||
description: Format of response - json or yaml
|
||||
in: query
|
||||
/agents/setup:
|
||||
get:
|
||||
summary: Get agent setup info
|
||||
|
|
|
@ -46,6 +46,8 @@ paths:
|
|||
$ref: paths/epm@get_file.yaml
|
||||
'/epm/packages/{pkgName}/stats':
|
||||
$ref: 'paths/epm@packages@{pkg_name}@stats.yaml'
|
||||
'/epm/templates/{pkgName}/{pkgVersion}/inputs':
|
||||
$ref: 'paths/epm@templates@{pkg_name}@{pkg_version}@inputs.yaml'
|
||||
|
||||
# Agent endpoints
|
||||
/agents/setup:
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
get:
|
||||
summary: Get inputs template
|
||||
tags:
|
||||
- Elastic Package Manager (EPM)
|
||||
responses:
|
||||
'400':
|
||||
$ref: ../components/responses/error.yaml
|
||||
operationId: get-inputs-template
|
||||
security:
|
||||
- basicAuth: []
|
||||
parameters:
|
||||
- schema:
|
||||
type: string
|
||||
name: pkgName
|
||||
in: path
|
||||
required: true
|
||||
- schema:
|
||||
type: string
|
||||
name: pkgVersion
|
||||
in: path
|
||||
required: true
|
||||
- schema:
|
||||
type: string
|
||||
enum:
|
||||
- json
|
||||
- yaml
|
||||
- yml
|
||||
name: format
|
||||
description: 'Format of response - json or yaml'
|
||||
in: query
|
|
@ -39,7 +39,7 @@ export const fullAgentPolicyToYaml = (policy: FullAgentPolicy, toYaml: typeof sa
|
|||
return _formatSecrets(policy.secret_references, yaml);
|
||||
};
|
||||
|
||||
function _sortYamlKeys(keyA: string, keyB: string) {
|
||||
export function _sortYamlKeys(keyA: string, keyB: string) {
|
||||
const indexA = POLICY_KEYS_ORDER.indexOf(keyA);
|
||||
const indexB = POLICY_KEYS_ORDER.indexOf(keyB);
|
||||
if (indexA >= 0 && indexB < 0) {
|
||||
|
|
|
@ -53,6 +53,7 @@ import type {
|
|||
GetLimitedPackagesRequestSchema,
|
||||
GetBulkAssetsRequestSchema,
|
||||
CreateCustomIntegrationRequestSchema,
|
||||
GetInputsRequestSchema,
|
||||
} from '../../types';
|
||||
import {
|
||||
bulkInstallPackages,
|
||||
|
@ -67,6 +68,7 @@ import {
|
|||
getLimitedPackages,
|
||||
getInstallation,
|
||||
getBulkAssets,
|
||||
getTemplateInputs,
|
||||
} from '../../services/epm/packages';
|
||||
import type { BulkInstallResponse } from '../../services/epm/packages';
|
||||
import { defaultFleetErrorHandler, fleetErrorToResponseOptions, FleetError } from '../../errors';
|
||||
|
@ -650,6 +652,28 @@ export const reauthorizeTransformsHandler: FleetRequestHandler<
|
|||
}
|
||||
};
|
||||
|
||||
export const getInputsHandler: FleetRequestHandler<
|
||||
TypeOf<typeof GetInputsRequestSchema.params>,
|
||||
TypeOf<typeof GetInputsRequestSchema.query>,
|
||||
undefined
|
||||
> = async (context, request, response) => {
|
||||
const soClient = (await context.fleet).internalSoClient;
|
||||
|
||||
try {
|
||||
const { pkgName, pkgVersion } = request.params;
|
||||
const { format } = request.query;
|
||||
let body;
|
||||
if (format === 'json') {
|
||||
body = await getTemplateInputs(soClient, pkgName, pkgVersion, 'json');
|
||||
} else if (format === 'yml' || format === 'yaml') {
|
||||
body = await getTemplateInputs(soClient, pkgName, pkgVersion, 'yml');
|
||||
}
|
||||
return response.ok({ body });
|
||||
} catch (error) {
|
||||
return defaultFleetErrorHandler({ error, response });
|
||||
}
|
||||
};
|
||||
|
||||
// Don't expose the whole SO in the API response, only selected fields
|
||||
const soToInstallationInfo = (pkg: PackageListItem | PackageInfo) => {
|
||||
if ('savedObject' in pkg && pkg.savedObject?.attributes) {
|
||||
|
|
|
@ -47,6 +47,7 @@ import {
|
|||
ReauthorizeTransformRequestSchema,
|
||||
GetDataStreamsRequestSchema,
|
||||
CreateCustomIntegrationRequestSchema,
|
||||
GetInputsRequestSchema,
|
||||
} from '../../types';
|
||||
|
||||
import {
|
||||
|
@ -67,6 +68,7 @@ import {
|
|||
reauthorizeTransformsHandler,
|
||||
getDataStreamsHandler,
|
||||
createCustomIntegrationHandler,
|
||||
getInputsHandler,
|
||||
} from './handlers';
|
||||
|
||||
const MAX_FILE_SIZE_BYTES = 104857600; // 100MB
|
||||
|
@ -145,6 +147,19 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
|
|||
getStatsHandler
|
||||
);
|
||||
|
||||
router.versioned
|
||||
.get({
|
||||
path: EPM_API_ROUTES.INPUTS_PATTERN,
|
||||
fleetAuthz: READ_PACKAGE_INFO_AUTHZ,
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.public.v1,
|
||||
validate: { request: GetInputsRequestSchema },
|
||||
},
|
||||
getInputsHandler
|
||||
);
|
||||
|
||||
router.versioned
|
||||
.get({
|
||||
path: EPM_API_ROUTES.FILEPATH_PATTERN,
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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 { SavedObjectsClientContract } from '@kbn/core/server';
|
||||
|
||||
import { merge } from 'lodash';
|
||||
import { safeDump } from 'js-yaml';
|
||||
|
||||
import { packageToPackagePolicy } from '../../../../common/services/package_to_package_policy';
|
||||
import { getInputsWithStreamIds, _compilePackagePolicyInputs } from '../../package_policy';
|
||||
|
||||
import type {
|
||||
PackageInfo,
|
||||
NewPackagePolicy,
|
||||
PackagePolicyInput,
|
||||
FullAgentPolicyInput,
|
||||
FullAgentPolicyInputStream,
|
||||
} from '../../../../common/types';
|
||||
import { _sortYamlKeys } from '../../../../common/services/full_agent_policy_to_yaml';
|
||||
|
||||
import { getPackageInfo } from '.';
|
||||
|
||||
type Format = 'yml' | 'json';
|
||||
|
||||
// Function based off storedPackagePolicyToAgentInputs, it only creates the `streams` section instead of the FullAgentPolicyInput
|
||||
export const templatePackagePolicyToFullInputs = (
|
||||
packagePolicyInputs: PackagePolicyInput[]
|
||||
): FullAgentPolicyInput[] => {
|
||||
const fullInputs: FullAgentPolicyInput[] = [];
|
||||
|
||||
if (!packagePolicyInputs || packagePolicyInputs.length === 0) return fullInputs;
|
||||
|
||||
packagePolicyInputs.forEach((input) => {
|
||||
const fullInput = {
|
||||
...(input.compiled_input || {}),
|
||||
...(input.streams.length
|
||||
? {
|
||||
streams: input.streams.map((stream) => {
|
||||
const fullStream: FullAgentPolicyInputStream = {
|
||||
id: stream.id,
|
||||
type: input.type,
|
||||
data_stream: stream.data_stream,
|
||||
...stream.compiled_stream,
|
||||
...Object.entries(stream.config || {}).reduce((acc, [key, { value }]) => {
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
}, {} as { [k: string]: any }),
|
||||
};
|
||||
return fullStream;
|
||||
}),
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
|
||||
// deeply merge the input.config values with the full policy input
|
||||
merge(
|
||||
fullInput,
|
||||
Object.entries(input.config || {}).reduce((acc, [key, { value }]) => {
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
}, {} as Record<string, unknown>)
|
||||
);
|
||||
fullInputs.push(fullInput);
|
||||
});
|
||||
|
||||
return fullInputs;
|
||||
};
|
||||
|
||||
export async function getTemplateInputs(
|
||||
soClient: SavedObjectsClientContract,
|
||||
pkgName: string,
|
||||
pkgVersion: string,
|
||||
format: Format
|
||||
) {
|
||||
const packageInfoMap = new Map<string, PackageInfo>();
|
||||
let packageInfo: PackageInfo;
|
||||
|
||||
if (packageInfoMap.has(pkgName)) {
|
||||
packageInfo = packageInfoMap.get(pkgName)!;
|
||||
} else {
|
||||
packageInfo = await getPackageInfo({
|
||||
savedObjectsClient: soClient,
|
||||
pkgName,
|
||||
pkgVersion,
|
||||
});
|
||||
}
|
||||
const emptyPackagePolicy = packageToPackagePolicy(packageInfo, '');
|
||||
const inputsWithStreamIds = getInputsWithStreamIds(emptyPackagePolicy, undefined, true);
|
||||
|
||||
const compiledInputs = await _compilePackagePolicyInputs(
|
||||
packageInfo,
|
||||
emptyPackagePolicy.vars || {},
|
||||
inputsWithStreamIds
|
||||
);
|
||||
const packagePolicyWithInputs: NewPackagePolicy = {
|
||||
...emptyPackagePolicy,
|
||||
inputs: compiledInputs,
|
||||
};
|
||||
const fullAgentPolicyInputs = templatePackagePolicyToFullInputs(
|
||||
packagePolicyWithInputs.inputs as PackagePolicyInput[]
|
||||
);
|
||||
// @ts-ignore-next-line The return type is any because in some case we can have compiled_input instead of input.streams
|
||||
// we don't know what it is. An example is integration APM
|
||||
const inputs: any = fullAgentPolicyInputs.flatMap((input) => input?.streams || input);
|
||||
|
||||
if (format === 'json') {
|
||||
return { inputs };
|
||||
} else if (format === 'yml') {
|
||||
const yaml = safeDump(
|
||||
{ inputs },
|
||||
{
|
||||
skipInvalid: true,
|
||||
sortKeys: _sortYamlKeys,
|
||||
}
|
||||
);
|
||||
return yaml;
|
||||
}
|
||||
return { inputs: [] };
|
||||
}
|
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* 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 { PackagePolicyInput } from '../../../../common/types';
|
||||
|
||||
import { templatePackagePolicyToFullInputs } from './get_template_inputs';
|
||||
|
||||
const packageInfoCache = new Map();
|
||||
packageInfoCache.set('mock_package-0.0.0', {
|
||||
name: 'mock_package',
|
||||
version: '0.0.0',
|
||||
policy_templates: [
|
||||
{
|
||||
multiple: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
packageInfoCache.set('limited_package-0.0.0', {
|
||||
name: 'limited_package',
|
||||
version: '0.0.0',
|
||||
policy_templates: [
|
||||
{
|
||||
multiple: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
describe('Fleet - templatePackagePolicyToFullInputs', () => {
|
||||
const mockInput: PackagePolicyInput = {
|
||||
type: 'test-logs',
|
||||
enabled: true,
|
||||
vars: {
|
||||
inputVar: { value: 'input-value' },
|
||||
inputVar2: { value: undefined },
|
||||
inputVar3: {
|
||||
type: 'yaml',
|
||||
value: 'testField: test',
|
||||
},
|
||||
inputVar4: { value: '' },
|
||||
},
|
||||
streams: [
|
||||
{
|
||||
id: 'test-logs-foo',
|
||||
enabled: true,
|
||||
data_stream: { dataset: 'foo', type: 'logs' },
|
||||
vars: {
|
||||
fooVar: { value: 'foo-value' },
|
||||
fooVar2: { value: [1, 2] },
|
||||
},
|
||||
compiled_stream: {
|
||||
fooKey: 'fooValue1',
|
||||
fooKey2: ['fooValue2'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'test-logs-bar',
|
||||
enabled: true,
|
||||
data_stream: { dataset: 'bar', type: 'logs' },
|
||||
vars: {
|
||||
barVar: { value: 'bar-value' },
|
||||
barVar2: { value: [1, 2] },
|
||||
barVar3: {
|
||||
type: 'yaml',
|
||||
value:
|
||||
'- namespace: mockNamespace\n #disabledProp: ["test"]\n anotherProp: test\n- namespace: mockNamespace2\n #disabledProp: ["test2"]\n anotherProp: test2',
|
||||
},
|
||||
barVar4: {
|
||||
type: 'yaml',
|
||||
value: '',
|
||||
},
|
||||
barVar5: {
|
||||
type: 'yaml',
|
||||
value: 'testField: test\n invalidSpacing: foo',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const mockInput2: PackagePolicyInput = {
|
||||
type: 'test-metrics',
|
||||
policy_template: 'some-template',
|
||||
enabled: true,
|
||||
vars: {
|
||||
inputVar: { value: 'input-value' },
|
||||
inputVar2: { value: undefined },
|
||||
inputVar3: {
|
||||
type: 'yaml',
|
||||
value: 'testField: test',
|
||||
},
|
||||
inputVar4: { value: '' },
|
||||
},
|
||||
streams: [
|
||||
{
|
||||
id: 'test-metrics-foo',
|
||||
enabled: true,
|
||||
data_stream: { dataset: 'foo', type: 'metrics' },
|
||||
vars: {
|
||||
fooVar: { value: 'foo-value' },
|
||||
fooVar2: { value: [1, 2] },
|
||||
},
|
||||
compiled_stream: {
|
||||
fooKey: 'fooValue1',
|
||||
fooKey2: ['fooValue2'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
it('returns no inputs for package policy with no inputs', async () => {
|
||||
expect(await templatePackagePolicyToFullInputs([])).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns inputs even when inputs where disabled', async () => {
|
||||
expect(await templatePackagePolicyToFullInputs([{ ...mockInput, enabled: false }])).toEqual([
|
||||
{
|
||||
streams: [
|
||||
{
|
||||
data_stream: {
|
||||
dataset: 'foo',
|
||||
type: 'logs',
|
||||
},
|
||||
fooKey: 'fooValue1',
|
||||
fooKey2: ['fooValue2'],
|
||||
id: 'test-logs-foo',
|
||||
type: 'test-logs',
|
||||
},
|
||||
{
|
||||
data_stream: {
|
||||
dataset: 'bar',
|
||||
type: 'logs',
|
||||
},
|
||||
id: 'test-logs-bar',
|
||||
type: 'test-logs',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns agent inputs with streams', async () => {
|
||||
expect(await templatePackagePolicyToFullInputs([mockInput])).toEqual([
|
||||
{
|
||||
streams: [
|
||||
{
|
||||
id: 'test-logs-foo',
|
||||
data_stream: { dataset: 'foo', type: 'logs' },
|
||||
fooKey: 'fooValue1',
|
||||
fooKey2: ['fooValue2'],
|
||||
type: 'test-logs',
|
||||
},
|
||||
{
|
||||
id: 'test-logs-bar',
|
||||
data_stream: { dataset: 'bar', type: 'logs' },
|
||||
type: 'test-logs',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns unique agent inputs IDs, with policy template name if one exists for non-limited packages', async () => {
|
||||
expect(await templatePackagePolicyToFullInputs([mockInput])).toEqual([
|
||||
{
|
||||
streams: [
|
||||
{
|
||||
id: 'test-logs-foo',
|
||||
type: 'test-logs',
|
||||
data_stream: { dataset: 'foo', type: 'logs' },
|
||||
fooKey: 'fooValue1',
|
||||
fooKey2: ['fooValue2'],
|
||||
},
|
||||
{
|
||||
id: 'test-logs-bar',
|
||||
data_stream: { dataset: 'bar', type: 'logs' },
|
||||
type: 'test-logs',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns agent inputs without streams', async () => {
|
||||
expect(await templatePackagePolicyToFullInputs([mockInput2])).toEqual([
|
||||
{
|
||||
streams: [
|
||||
{
|
||||
data_stream: {
|
||||
dataset: 'foo',
|
||||
type: 'metrics',
|
||||
},
|
||||
fooKey: 'fooValue1',
|
||||
fooKey2: ['fooValue2'],
|
||||
id: 'test-metrics-foo',
|
||||
type: 'test-metrics',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns agent inputs without disabled streams', async () => {
|
||||
expect(
|
||||
await templatePackagePolicyToFullInputs([
|
||||
{
|
||||
...mockInput,
|
||||
streams: [{ ...mockInput.streams[0] }, { ...mockInput.streams[1], enabled: false }],
|
||||
},
|
||||
])
|
||||
).toEqual([
|
||||
{
|
||||
streams: [
|
||||
{
|
||||
id: 'test-logs-foo',
|
||||
type: 'test-logs',
|
||||
data_stream: { dataset: 'foo', type: 'logs' },
|
||||
fooKey: 'fooValue1',
|
||||
fooKey2: ['fooValue2'],
|
||||
},
|
||||
{
|
||||
data_stream: {
|
||||
dataset: 'bar',
|
||||
type: 'logs',
|
||||
},
|
||||
id: 'test-logs-bar',
|
||||
type: 'test-logs',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns agent inputs with deeply merged config values', async () => {
|
||||
expect(
|
||||
await templatePackagePolicyToFullInputs([
|
||||
{
|
||||
...mockInput,
|
||||
compiled_input: {
|
||||
agent_input_template_group1_vars: {
|
||||
inputVar: 'input-value',
|
||||
},
|
||||
agent_input_template_group2_vars: {
|
||||
inputVar3: {
|
||||
testFieldGroup: {
|
||||
subField1: 'subfield1',
|
||||
},
|
||||
testField: 'test',
|
||||
},
|
||||
},
|
||||
},
|
||||
config: {
|
||||
agent_input_template_group1_vars: {
|
||||
value: {
|
||||
inputVar2: {},
|
||||
},
|
||||
},
|
||||
agent_input_template_group2_vars: {
|
||||
value: {
|
||||
inputVar3: {
|
||||
testFieldGroup: {
|
||||
subField2: 'subfield2',
|
||||
},
|
||||
},
|
||||
inputVar4: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
).toEqual([
|
||||
{
|
||||
agent_input_template_group1_vars: {
|
||||
inputVar: 'input-value',
|
||||
inputVar2: {},
|
||||
},
|
||||
agent_input_template_group2_vars: {
|
||||
inputVar3: {
|
||||
testField: 'test',
|
||||
testFieldGroup: {
|
||||
subField1: 'subfield1',
|
||||
subField2: 'subfield2',
|
||||
},
|
||||
},
|
||||
inputVar4: '',
|
||||
},
|
||||
streams: [
|
||||
{
|
||||
id: 'test-logs-foo',
|
||||
data_stream: { dataset: 'foo', type: 'logs' },
|
||||
fooKey: 'fooValue1',
|
||||
fooKey2: ['fooValue2'],
|
||||
type: 'test-logs',
|
||||
},
|
||||
{ id: 'test-logs-bar', data_stream: { dataset: 'bar', type: 'logs' }, type: 'test-logs' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -27,6 +27,7 @@ export {
|
|||
|
||||
export { getBundledPackages } from './bundled_packages';
|
||||
export { getBulkAssets } from './get_bulk_assets';
|
||||
export { getTemplateInputs } from './get_template_inputs';
|
||||
|
||||
export type { BulkInstallResponse, IBulkInstallPackageError } from './install';
|
||||
export { handleInstallPackageFailure, installPackage, ensureInstalledPackage } from './install';
|
||||
|
|
|
@ -1861,16 +1861,23 @@ function validatePackagePolicyOrThrow(packagePolicy: NewPackagePolicy, pkgInfo:
|
|||
}
|
||||
}
|
||||
|
||||
function getInputsWithStreamIds(
|
||||
// the option `allEnabled` is only used to generate inputs integration templates where everything is enabled by default
|
||||
// it shouldn't be used in the normal install flow
|
||||
export function getInputsWithStreamIds(
|
||||
packagePolicy: NewPackagePolicy,
|
||||
packagePolicyId: string
|
||||
packagePolicyId?: string,
|
||||
allEnabled?: boolean
|
||||
): PackagePolicy['inputs'] {
|
||||
return packagePolicy.inputs.map((input) => {
|
||||
return {
|
||||
...input,
|
||||
enabled: !!allEnabled ? true : input.enabled,
|
||||
streams: input.streams.map((stream) => ({
|
||||
...stream,
|
||||
id: `${input.type}-${stream.data_stream.dataset}-${packagePolicyId}`,
|
||||
enabled: !!allEnabled ? true : stream.enabled,
|
||||
id: packagePolicyId
|
||||
? `${input.type}-${stream.data_stream.dataset}-${packagePolicyId}`
|
||||
: `${input.type}-${stream.data_stream.dataset}`,
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
|
|
@ -247,3 +247,15 @@ export const DeletePackageRequestSchemaDeprecated = {
|
|||
})
|
||||
),
|
||||
};
|
||||
|
||||
export const GetInputsRequestSchema = {
|
||||
params: schema.object({
|
||||
pkgName: schema.string(),
|
||||
pkgVersion: schema.string(),
|
||||
}),
|
||||
query: schema.object({
|
||||
format: schema.oneOf([schema.literal('json'), schema.literal('yml'), schema.literal('yaml')], {
|
||||
defaultValue: 'json',
|
||||
}),
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
|
||||
import { skipIfNoDockerRegistry } from '../../helpers';
|
||||
import { setupFleetAndAgents } from '../agents/services';
|
||||
import { testUsers } from '../test_users';
|
||||
|
||||
export default function (providerContext: FtrProviderContext) {
|
||||
const { getService } = providerContext;
|
||||
|
||||
const supertest = getService('supertest');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
|
||||
const testPkgName = 'apache';
|
||||
const testPkgVersion = '0.1.4';
|
||||
|
||||
const uninstallPackage = async (name: string, version: string) => {
|
||||
await supertest.delete(`/api/fleet/epm/packages/${name}/${version}`).set('kbn-xsrf', 'xxxx');
|
||||
};
|
||||
|
||||
const testPkgArchiveZip = path.join(
|
||||
path.dirname(__filename),
|
||||
'../fixtures/direct_upload_packages/apache_0.1.4.zip'
|
||||
);
|
||||
|
||||
describe('EPM Templates - Get Inputs', () => {
|
||||
skipIfNoDockerRegistry(providerContext);
|
||||
setupFleetAndAgents(providerContext);
|
||||
before(async () => {
|
||||
const buf = fs.readFileSync(testPkgArchiveZip);
|
||||
await supertest
|
||||
.post(`/api/fleet/epm/packages`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.type('application/zip')
|
||||
.send(buf)
|
||||
.expect(200);
|
||||
});
|
||||
after(async () => {
|
||||
await uninstallPackage(testPkgName, testPkgVersion);
|
||||
});
|
||||
const expectedYml = `inputs:
|
||||
- id: logfile-apache.access
|
||||
type: logfile
|
||||
data_stream:
|
||||
dataset: apache.access
|
||||
type: logs
|
||||
paths:
|
||||
- /var/log/apache2/access.log*
|
||||
- /var/log/apache2/other_vhosts_access.log*
|
||||
- /var/log/httpd/access_log*
|
||||
exclude_files:
|
||||
- .gz$
|
||||
processors:
|
||||
- add_fields:
|
||||
target: ''
|
||||
fields:
|
||||
ecs.version: 1.5.0
|
||||
- id: logfile-apache.error
|
||||
type: logfile
|
||||
data_stream:
|
||||
dataset: apache.error
|
||||
type: logs
|
||||
paths:
|
||||
- /var/log/apache2/error.log*
|
||||
- /var/log/httpd/error_log*
|
||||
exclude_files:
|
||||
- .gz$
|
||||
processors:
|
||||
- add_locale: null
|
||||
- add_fields:
|
||||
target: ''
|
||||
fields:
|
||||
ecs.version: 1.5.0
|
||||
- id: apache/metrics-apache.status
|
||||
type: apache/metrics
|
||||
data_stream:
|
||||
dataset: apache.status
|
||||
type: metrics
|
||||
metricsets:
|
||||
- status
|
||||
hosts:
|
||||
- 'http://127.0.0.1'
|
||||
period: 10s
|
||||
server_status_path: /server-status
|
||||
`;
|
||||
const expectedJson = [
|
||||
{
|
||||
id: 'logfile-apache.access',
|
||||
type: 'logfile',
|
||||
data_stream: {
|
||||
type: 'logs',
|
||||
dataset: 'apache.access',
|
||||
},
|
||||
paths: [
|
||||
'/var/log/apache2/access.log*',
|
||||
'/var/log/apache2/other_vhosts_access.log*',
|
||||
'/var/log/httpd/access_log*',
|
||||
],
|
||||
exclude_files: ['.gz$'],
|
||||
processors: [
|
||||
{
|
||||
add_fields: {
|
||||
target: '',
|
||||
fields: {
|
||||
'ecs.version': '1.5.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'logfile-apache.error',
|
||||
type: 'logfile',
|
||||
data_stream: {
|
||||
type: 'logs',
|
||||
dataset: 'apache.error',
|
||||
},
|
||||
paths: ['/var/log/apache2/error.log*', '/var/log/httpd/error_log*'],
|
||||
exclude_files: ['.gz$'],
|
||||
processors: [
|
||||
{
|
||||
add_locale: null,
|
||||
},
|
||||
{
|
||||
add_fields: {
|
||||
target: '',
|
||||
fields: {
|
||||
'ecs.version': '1.5.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'apache/metrics-apache.status',
|
||||
type: 'apache/metrics',
|
||||
data_stream: {
|
||||
type: 'metrics',
|
||||
dataset: 'apache.status',
|
||||
},
|
||||
metricsets: ['status'],
|
||||
hosts: ['http://127.0.0.1'],
|
||||
period: '10s',
|
||||
server_status_path: '/server-status',
|
||||
},
|
||||
];
|
||||
|
||||
it('returns inputs template in json format', async function () {
|
||||
const res = await supertest
|
||||
.get(`/api/fleet/epm/templates/${testPkgName}/${testPkgVersion}/inputs?format=json`)
|
||||
.expect(200);
|
||||
const inputs = res.body.inputs;
|
||||
|
||||
expect(inputs).to.eql(expectedJson);
|
||||
});
|
||||
|
||||
it('returns inputs template in yaml format if format=yaml', async function () {
|
||||
const res = await supertest
|
||||
.get(`/api/fleet/epm/templates/${testPkgName}/${testPkgVersion}/inputs?format=yaml`)
|
||||
.expect(200);
|
||||
|
||||
expect(res.text).to.eql(expectedYml);
|
||||
});
|
||||
|
||||
it('returns inputs template in yaml format if format=yml', async function () {
|
||||
const res = await supertest
|
||||
.get(`/api/fleet/epm/templates/${testPkgName}/${testPkgVersion}/inputs?format=yml`)
|
||||
.expect(200);
|
||||
expect(res.text).to.eql(expectedYml);
|
||||
});
|
||||
|
||||
it('returns a 404 for a version that does not exists', async function () {
|
||||
await supertest
|
||||
.get(`/api/fleet/epm/templates/${testPkgName}/0.1.0/inputs?format=json`)
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
it('allows user with only fleet permission to access', async () => {
|
||||
await supertestWithoutAuth
|
||||
.get(`/api/fleet/epm/templates/${testPkgName}/${testPkgVersion}/inputs?format=json`)
|
||||
.auth(testUsers.fleet_all_only.username, testUsers.fleet_all_only.password)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('allows user with integrations read permission to access', async () => {
|
||||
await supertestWithoutAuth
|
||||
.get(`/api/fleet/epm/templates/${testPkgName}/${testPkgVersion}/inputs?format=json`)
|
||||
.auth(testUsers.fleet_all_int_read.username, testUsers.fleet_all_int_read.password)
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -46,5 +46,6 @@ export default function loadTests({ loadTestFile, getService }) {
|
|||
loadTestFile(require.resolve('./install_dynamic_template_metric'));
|
||||
loadTestFile(require.resolve('./routing_rules'));
|
||||
loadTestFile(require.resolve('./install_runtime_field'));
|
||||
loadTestFile(require.resolve('./get_templates_inputs'));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue