diff --git a/x-pack/plugins/beats/server/routes/api/index.js b/x-pack/plugins/beats/server/routes/api/index.js index 6ec0ad737352a..fde5a2e4c2646 100644 --- a/x-pack/plugins/beats/server/routes/api/index.js +++ b/x-pack/plugins/beats/server/routes/api/index.js @@ -12,6 +12,7 @@ import { registerUpdateBeatRoute } from './register_update_beat_route'; import { registerSetTagRoute } from './register_set_tag_route'; import { registerAssignTagsToBeatsRoute } from './register_assign_tags_to_beats_route'; import { registerRemoveTagsFromBeatsRoute } from './register_remove_tags_from_beats_route'; +import { registerGetBeatConfigurationRoute } from './register_get_beat_configuration_route'; export function registerApiRoutes(server) { registerCreateEnrollmentTokensRoute(server); @@ -22,4 +23,5 @@ export function registerApiRoutes(server) { registerSetTagRoute(server); registerAssignTagsToBeatsRoute(server); registerRemoveTagsFromBeatsRoute(server); + registerGetBeatConfigurationRoute(server); } diff --git a/x-pack/plugins/beats/server/routes/api/register_get_beat_configuration_route.js b/x-pack/plugins/beats/server/routes/api/register_get_beat_configuration_route.js new file mode 100644 index 0000000000000..9ac71cd8e5187 --- /dev/null +++ b/x-pack/plugins/beats/server/routes/api/register_get_beat_configuration_route.js @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Joi from 'joi'; +import { get } from "lodash"; +import { INDEX_NAMES } from "../../../common/constants"; +import { callWithInternalUserFactory } from '../../lib/client'; +import { wrapEsError } from "../../lib/error_wrappers"; + +async function getBeat(callWithInternalUser, beatId) { + const params = { + index: INDEX_NAMES.BEATS, + type: '_doc', + id: `beat:${beatId}`, + ignore: [ 404 ] + }; + + const response = await callWithInternalUser('get', params); + if (!response.found) { + return null; + } + + return get(response, '_source.beat'); +} + +// TODO: add license check pre-hook +export function registerGetBeatConfigurationRoute(server) { + server.route({ + method: 'GET', + path: '/api/beats/agent/{beatId}/configuration', + config: { + validate: { + headers: Joi.object({ + 'kbn-beats-access-token': Joi.string().required() + }).options({ allowUnknown: true }) + }, + auth: false + }, + handler: async (request, reply) => { + const callWithInternalUser = callWithInternalUserFactory(server); + const beatId = request.params.beatId; + const accessToken = request.headers['kbn-beats-access-token']; + + // TODO: remove conditional and hardcoding + if (beatId !== 'foo') { // foo is used by the API integration tests + return reply({ + configuration_blocks: [ + { + type: "output", + data: "elasticsearch:\n hosts: [\"localhost:9200\"]\n username: \"...\"" + }, + { + type: "metricbeat.modules", + data: "module: memcached\nhosts: [\"localhost:11211\"]", + }, + { + type: "metricbeat.modules", + data: "module: munin\nhosts: [\"localhost:4949\"]\nnode.namespace: node", + } + ] + }); + } + + let beat; + try { + beat = await getBeat(callWithInternalUser, beatId); + } catch (err) { + return reply(wrapEsError(err)); + } + + if (beat === null) { + return reply({ message: 'Beat not found' }).code(404); + } + + const isAccessTokenValid = beat.access_token === accessToken; + if (!isAccessTokenValid) { + return reply({ message: 'Invalid access token' }).code(401); + } + + const isBeatVerified = beat.hasOwnProperty('verified_on'); + if (!isBeatVerified) { + return reply({ message: 'Beat has not been verified' }).code(400); + } + + reply({ configuration_blocks: beat.central_configuration_blocks }); + } + }); +} diff --git a/x-pack/test/api_integration/apis/beats/get_beat_configuration.js b/x-pack/test/api_integration/apis/beats/get_beat_configuration.js new file mode 100644 index 0000000000000..d912ebab0209c --- /dev/null +++ b/x-pack/test/api_integration/apis/beats/get_beat_configuration.js @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from 'expect.js'; + +export default function ({ getService }) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + describe('get_beat_configuration', () => { + const archive = 'beats/list'; + + beforeEach('load beats archive', () => esArchiver.load(archive)); + afterEach('unload beats archive', () => esArchiver.unload(archive)); + + it('should return merged configuration for the beat', async () => { + const { body: apiResponse } = await supertest + .get( + '/api/beats/agent/foo/configuration' + ) + .set('kbn-beats-access-token', '93c4a4dd08564c189a7ec4e4f046b975') + .expect(200); + + const configurationBlocks = apiResponse.configuration_blocks; + + expect(configurationBlocks).to.be.an(Array); + expect(configurationBlocks.length).to.be(3); + + expect(configurationBlocks[0].type).to.be('output'); + expect(configurationBlocks[0].data).to.be('elasticsearch:\n hosts: ["localhost:9200"]\n username: ...'); + + expect(configurationBlocks[1].type).to.be('metricbeat.modules'); + expect(configurationBlocks[1].data).to.be('module: memcached\nhosts: ["localhost:11211"]'); + + expect(configurationBlocks[2].type).to.be('metricbeat.modules'); + expect(configurationBlocks[2].data).to.be('module: munin\nhosts: ["localhost:4949"]\nnode.namespace: node'); + }); + }); +} \ No newline at end of file diff --git a/x-pack/test/api_integration/apis/beats/index.js b/x-pack/test/api_integration/apis/beats/index.js index f8956d3e498ba..c2d719c6e7c60 100644 --- a/x-pack/test/api_integration/apis/beats/index.js +++ b/x-pack/test/api_integration/apis/beats/index.js @@ -25,5 +25,6 @@ export default function ({ getService, loadTestFile }) { loadTestFile(require.resolve('./set_tag')); loadTestFile(require.resolve('./assign_tags_to_beats')); loadTestFile(require.resolve('./remove_tags_from_beats')); + loadTestFile(require.resolve('./get_beat_configuration')); }); }