Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion x-pack/plugins/asset_manager/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"optionalPlugins": [
],
"requiredPlugins": [
"apmDataAccess"
"apmDataAccess",
"metricsDataAccess"
],
"browser": false,
"server": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,18 @@ import { collectHosts } from '../../collectors/hosts';
export async function getHostsBySignals(
options: GetHostsOptionsInjected
): Promise<{ hosts: Asset[] }> {
const metricsIndices = await options.metricsClient.getMetricIndices({
savedObjectsClient: options.soClient,
});

const { assets } = await collectHosts({
client: options.esClient,
from: options.from,
to: options.to,
sourceIndices: options.sourceIndices,
sourceIndices: {
metrics: metricsIndices,
logs: options.sourceIndices.logs,
},
});
return {
hosts: assets,
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/asset_manager/server/lib/accessors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@

import { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import { APMDataAccessConfig } from '@kbn/apm-data-access-plugin/server';
import { MetricsDataClient } from '@kbn/metrics-data-access-plugin/server';
import { SavedObjectsClientContract } from '@kbn/core/server';
import { AssetManagerConfig } from '../../types';

export interface InjectedValues {
sourceIndices: AssetManagerConfig['sourceIndices'];
getApmIndices: (soClient: SavedObjectsClientContract) => Promise<APMDataAccessConfig['indices']>;
metricsClient: MetricsDataClient;
}

export type OptionsWithInjectedValues<T extends object> = T & InjectedValues;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ export async function getServicesBySignals(
client: options.esClient,
from: options.from,
to: options.to,
apmIndices,
sourceIndices: {
apm: apmIndices,
},
filters,
});

Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/asset_manager/server/lib/asset_accessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { APMDataAccessConfig } from '@kbn/apm-data-access-plugin/server';
import { MetricsDataClient } from '@kbn/metrics-data-access-plugin/server';
import { SavedObjectsClientContract } from '@kbn/core/server';
import { Asset } from '../../common/types_api';
import { AssetManagerConfig } from '../types';
Expand All @@ -21,6 +22,7 @@ interface AssetAccessorClassOptions {
sourceIndices: AssetManagerConfig['sourceIndices'];
source: AssetManagerConfig['lockedSource'];
getApmIndices: (soClient: SavedObjectsClientContract) => Promise<APMDataAccessConfig['indices']>;
metricsClient: MetricsDataClient;
}

export class AssetAccessor {
Expand All @@ -31,6 +33,7 @@ export class AssetAccessor {
...options,
sourceIndices: this.options.sourceIndices,
getApmIndices: this.options.getApmIndices,
metricsClient: this.options.metricsClient,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ export async function collectContainers({
sourceIndices,
afterKey,
}: CollectorOptions) {
if (!sourceIndices?.metrics || !sourceIndices?.logs) {
throw new Error('missing required metrics/logs indices');
}

const { metrics, logs } = sourceIndices;
const dsl: estypes.SearchRequest = {
index: [logs, metrics],
index: [metrics, logs],
size: QUERY_MAX_SIZE,
collapse: {
field: 'container.id',
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/asset_manager/server/lib/collectors/hosts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export async function collectHosts({
sourceIndices,
afterKey,
}: CollectorOptions) {
if (!sourceIndices?.metrics || !sourceIndices?.logs) {
throw new Error('missing required metrics/logs indices');
}

const { metrics, logs } = sourceIndices;
const dsl: estypes.SearchRequest = {
index: [metrics, logs],
Expand Down
13 changes: 5 additions & 8 deletions x-pack/plugins/asset_manager/server/lib/collectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import { estypes } from '@elastic/elasticsearch';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import { ElasticsearchClient } from '@kbn/core/server';
import { AssetManagerConfig } from '../../types';
import { Asset } from '../../../common/types_api';

export const QUERY_MAX_SIZE = 10000;
Expand All @@ -19,17 +18,15 @@ export interface CollectorOptions {
client: ElasticsearchClient;
from: string;
to: string;
sourceIndices: AssetManagerConfig['sourceIndices'];
sourceIndices?: {
apm?: APMIndices;
metrics?: string;
logs?: string;
};
afterKey?: estypes.SortResults;
filters?: estypes.QueryDslQueryContainer[];
}

type OmitSourceIndices<T> = Omit<T, 'sourceIndices'>;

export type ServicesCollectorOptions = OmitSourceIndices<CollectorOptions> & {
apmIndices: APMIndices;
};

export interface CollectorResult {
assets: Asset[];
afterKey?: estypes.SortResults;
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/asset_manager/server/lib/collectors/pods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import { Asset } from '../../../common/types_api';
import { CollectorOptions, QUERY_MAX_SIZE } from '.';

export async function collectPods({ client, from, to, sourceIndices, afterKey }: CollectorOptions) {
if (!sourceIndices?.metrics || !sourceIndices?.logs) {
throw new Error('missing required metrics/logs indices');
}

const { metrics, logs } = sourceIndices;
const dsl: estypes.SearchRequest = {
index: [metrics, logs],
Expand Down
12 changes: 8 additions & 4 deletions x-pack/plugins/asset_manager/server/lib/collectors/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@

import { estypes } from '@elastic/elasticsearch';
import { Asset } from '../../../common/types_api';
import { ServicesCollectorOptions, QUERY_MAX_SIZE } from '.';
import { CollectorOptions, QUERY_MAX_SIZE } from '.';

export async function collectServices({
client,
from,
to,
apmIndices,
sourceIndices,
afterKey,
filters = [],
}: ServicesCollectorOptions) {
const { transaction, error, metric } = apmIndices;
}: CollectorOptions) {
if (!sourceIndices?.apm) {
throw new Error('missing required apm indices');
}

const { transaction, error, metric } = sourceIndices.apm;
const musts: estypes.QueryDslQueryContainer[] = [
...filters,
{
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/asset_manager/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export class AssetManagerServerPlugin
source: this.config.lockedSource,
sourceIndices: this.config.sourceIndices,
getApmIndices: plugins.apmDataAccess.getApmIndices,
metricsClient: plugins.metricsDataAccess.client,
});

const router = core.http.createRouter();
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/asset_manager/server/routes/assets/hosts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ export function hostsRoutes<T extends RequestHandlerContext>({
router,
assetAccessor,
}: SetupRouteOptions<T>) {
// GET /assets/hosts
router.get<unknown, GetHostAssetsQueryOptions, unknown>(
{
path: `${ASSET_MANAGER_API_BASE}/assets/hosts`,
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/asset_manager/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import {
ApmDataAccessPluginSetup,
ApmDataAccessPluginStart,
} from '@kbn/apm-data-access-plugin/server';
import { MetricsDataPluginSetup } from '@kbn/metrics-data-access-plugin/server';

export interface ElasticsearchAccessorOptions {
esClient: ElasticsearchClient;
}

export const INDEX_DEFAULTS = {
metrics: 'metricbeat-*,metrics-*',
logs: 'filebeat-*,logs-*',
};

Expand All @@ -29,7 +29,6 @@ export const configSchema = schema.object({
// that value is propagated everywhere. For now, we duplicate the value here.
sourceIndices: schema.object(
{
metrics: schema.string({ defaultValue: INDEX_DEFAULTS.metrics }),
logs: schema.string({ defaultValue: INDEX_DEFAULTS.logs }),
},
{ defaultValue: INDEX_DEFAULTS }
Expand All @@ -48,6 +47,7 @@ export type AssetManagerConfig = TypeOf<typeof configSchema>;

export interface AssetManagerPluginSetupDependencies {
apmDataAccess: ApmDataAccessPluginSetup;
metricsDataAccess: MetricsDataPluginSetup;
}
export interface AssetManagerPluginStartDependencies {
apmDataAccess: ApmDataAccessPluginStart;
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/asset_manager/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@kbn/core-elasticsearch-server",
"@kbn/core-http-request-handler-context-server",
"@kbn/datemath",
"@kbn/apm-data-access-plugin"
"@kbn/apm-data-access-plugin",
"@kbn/metrics-data-access-plugin"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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 { timerange, infra } from '@kbn/apm-synthtrace-client';
import expect from '@kbn/expect';
import { ASSETS_ENDPOINT } from '../constants';
import { FtrProviderContext } from '../../types';

const HOSTS_ASSETS_ENDPOINT = `${ASSETS_ENDPOINT}/hosts`;

export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const synthtrace = getService('infraSynthtraceEsClient');

describe('GET /assets/hosts', () => {
beforeEach(async () => {
await synthtrace.clean();
});

it('should return hosts', async () => {
const from = new Date(Date.now() - 1000 * 60 * 2).toISOString();
const to = new Date().toISOString();
await synthtrace.index(generateHostsData({ from, to, count: 5 }));

const response = await supertest
.get(HOSTS_ASSETS_ENDPOINT)
.query({
from,
to,
})
.expect(200);

expect(response.body).to.have.property('hosts');
expect(response.body.hosts.length).to.equal(5);
});
});
}

function generateHostsData({ from, to, count = 1 }: { from: string; to: string; count: number }) {
const range = timerange(from, to);

const hosts = Array(count)
.fill(0)
.map((_, idx) => infra.host(`my-host-${idx}`));

return range
.interval('1m')
.rate(1)
.generator((timestamp, index) => hosts.map((host) => host.metrics().timestamp(timestamp)));
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('Asset Manager API Endpoints - with signals source', () => {
loadTestFile(require.resolve('./basics'));
loadTestFile(require.resolve('./hosts'));
loadTestFile(require.resolve('./services'));
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,53 +17,51 @@ export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const synthtrace = getService('apmSynthtraceEsClient');

describe('asset management', () => {
describe('GET /assets/services', () => {
beforeEach(async () => {
await synthtrace.clean();
});

describe('GET /assets/services', () => {
it('should return services', async () => {
const from = new Date(Date.now() - 1000 * 60 * 2).toISOString();
const to = new Date().toISOString();
await synthtrace.index(generateServicesData({ from, to, count: 2 }));
it('should return services', async () => {
const from = new Date(Date.now() - 1000 * 60 * 2).toISOString();
const to = new Date().toISOString();
await synthtrace.index(generateServicesData({ from, to, count: 2 }));

const response = await supertest
.get(SERVICES_ASSETS_ENDPOINT)
.query({
from,
to,
})
.expect(200);
const response = await supertest
.get(SERVICES_ASSETS_ENDPOINT)
.query({
from,
to,
})
.expect(200);

expect(response.body).to.have.property('services');
expect(response.body.services.length).to.equal(2);
});
expect(response.body).to.have.property('services');
expect(response.body.services.length).to.equal(2);
});

it('should return services running on specified host', async () => {
const from = new Date(Date.now() - 1000 * 60 * 2).toISOString();
const to = new Date().toISOString();
await synthtrace.index(generateServicesData({ from, to, count: 5 }));
it('should return services running on specified host', async () => {
const from = new Date(Date.now() - 1000 * 60 * 2).toISOString();
const to = new Date().toISOString();
await synthtrace.index(generateServicesData({ from, to, count: 5 }));

const response = await supertest
.get(SERVICES_ASSETS_ENDPOINT)
.query({
from,
to,
parent: 'my-host-1',
})
.expect(200);
const response = await supertest
.get(SERVICES_ASSETS_ENDPOINT)
.query({
from,
to,
parent: 'my-host-1',
})
.expect(200);

expect(response.body).to.have.property('services');
expect(response.body.services.length).to.equal(1);
expect(omit(response.body.services[0], ['@timestamp'])).to.eql({
'asset.kind': 'service',
'asset.id': 'service-1',
'asset.ean': 'service:service-1',
'asset.references': [],
'asset.parents': [],
'service.environment': 'production',
});
expect(response.body).to.have.property('services');
expect(response.body.services.length).to.equal(1);
expect(omit(response.body.services[0], ['@timestamp'])).to.eql({
'asset.kind': 'service',
'asset.id': 'service-1',
'asset.ean': 'service:service-1',
'asset.references': [],
'asset.parents': [],
'service.environment': 'production',
});
});
});
Expand Down