[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:
Cristina Amico 2023-10-10 15:33:33 +02:00 committed by GitHub
parent 348563b52f
commit 229b883e50
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 803 additions and 4 deletions

View file

@ -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,

View file

@ -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",

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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) {

View file

@ -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) {

View file

@ -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,

View file

@ -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: [] };
}

View file

@ -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' },
],
},
]);
});
});

View file

@ -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';

View file

@ -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}`,
})),
};
});

View file

@ -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',
}),
}),
};

View file

@ -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);
});
});
}

View file

@ -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'));
});
}