-
Notifications
You must be signed in to change notification settings - Fork 8.6k
[kbn-scout] Add Synthtrace as a fixture #210505
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 16 commits
8cf3b53
240a855
853b80e
6b5c7fd
84191a7
307095b
9ea808d
88a527a
b0a2cdb
9742d27
b69a36d
ced8cf9
ea99d35
2f448e4
8ed9a19
3cd18cd
8da80e3
e15551e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| /* | ||
| * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
| * Public License v 1"; you may not use this file except in compliance with, at | ||
| * your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
| * License v3.0 only", or the "Server Side Public License, v 1". | ||
| */ | ||
|
|
||
| import { | ||
| ApmSynthtraceEsClient, | ||
| ApmSynthtraceKibanaClient, | ||
| InfraSynthtraceEsClient, | ||
| InfraSynthtraceKibanaClient, | ||
| LogLevel, | ||
| OtelSynthtraceEsClient, | ||
| createLogger, | ||
| } from '@kbn/apm-synthtrace'; | ||
| import { ScoutLogger } from './logger'; | ||
| import { EsClient } from '../../types'; | ||
|
|
||
| let apmSynthtraceEsClientInstance: ApmSynthtraceEsClient | undefined; | ||
| let infraSynthtraceEsClientInstance: InfraSynthtraceEsClient | undefined; | ||
| let otelSynthtraceEsClientInstance: OtelSynthtraceEsClient | undefined; | ||
| const logger = createLogger(LogLevel.info); | ||
|
|
||
| export async function getApmSynthtraceEsClient( | ||
| esClient: EsClient, | ||
| target: string, | ||
| log: ScoutLogger | ||
| ) { | ||
| if (!apmSynthtraceEsClientInstance) { | ||
| const apmSynthtraceKibanaClient = new ApmSynthtraceKibanaClient({ | ||
| logger, | ||
| target, | ||
| }); | ||
|
|
||
| const version = await apmSynthtraceKibanaClient.fetchLatestApmPackageVersion(); | ||
| await apmSynthtraceKibanaClient.installApmPackage(version); | ||
| apmSynthtraceEsClientInstance = new ApmSynthtraceEsClient({ | ||
| client: esClient, | ||
| logger, | ||
| refreshAfterIndex: true, | ||
| version, | ||
| }); | ||
|
|
||
| apmSynthtraceEsClientInstance.pipeline( | ||
| apmSynthtraceEsClientInstance.getDefaultPipeline({ includeSerialization: false }) | ||
| ); | ||
|
|
||
| log.serviceLoaded('apmSynthtraceClient'); | ||
| } | ||
|
|
||
| return apmSynthtraceEsClientInstance; | ||
| } | ||
|
|
||
| export async function getInfraSynthtraceEsClient( | ||
| esClient: EsClient, | ||
| kbnUrl: string, | ||
| auth: { username: string; password: string }, | ||
| log: ScoutLogger | ||
| ) { | ||
| if (!infraSynthtraceEsClientInstance) { | ||
| const infraSynthtraceKibanaClient = new InfraSynthtraceKibanaClient({ | ||
| logger, | ||
| target: kbnUrl, | ||
| username: auth.username, | ||
| password: auth.password, | ||
| }); | ||
|
|
||
| const version = await infraSynthtraceKibanaClient.fetchLatestSystemPackageVersion(); | ||
| await infraSynthtraceKibanaClient.installSystemPackage(version); | ||
| infraSynthtraceEsClientInstance = new InfraSynthtraceEsClient({ | ||
| client: esClient, | ||
| logger, | ||
| refreshAfterIndex: true, | ||
| }); | ||
|
|
||
| infraSynthtraceEsClientInstance.pipeline( | ||
| infraSynthtraceEsClientInstance.getDefaultPipeline({ includeSerialization: false }) | ||
| ); | ||
|
|
||
| log.serviceLoaded('infraSynthtraceClient'); | ||
| } | ||
|
|
||
| return infraSynthtraceEsClientInstance; | ||
| } | ||
|
|
||
| export function getOtelSynthtraceEsClient(esClient: EsClient, log: ScoutLogger) { | ||
| if (!otelSynthtraceEsClientInstance) { | ||
| otelSynthtraceEsClientInstance = new OtelSynthtraceEsClient({ | ||
| client: esClient, | ||
| logger, | ||
| refreshAfterIndex: true, | ||
| }); | ||
|
|
||
| otelSynthtraceEsClientInstance.pipeline( | ||
| otelSynthtraceEsClientInstance.getDefaultPipeline({ includeSerialization: false }) | ||
| ); | ||
|
|
||
| log.serviceLoaded('otelSynthtraceClient'); | ||
| } | ||
|
|
||
| return otelSynthtraceEsClientInstance; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
| * Public License v 1"; you may not use this file except in compliance with, at | ||
| * your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
| * License v3.0 only", or the "Server Side Public License, v 1". | ||
| */ | ||
|
|
||
| import { Readable } from 'stream'; | ||
| import type { ApmFields, Fields, InfraDocument, OtelDocument } from '@kbn/apm-synthtrace-client'; | ||
| import Url from 'url'; | ||
| import type { SynthtraceEsClient } from '@kbn/apm-synthtrace/src/lib/shared/base_client'; | ||
| import { | ||
| getApmSynthtraceEsClient, | ||
| getInfraSynthtraceEsClient, | ||
| getOtelSynthtraceEsClient, | ||
| } from '../../../common/services/synthtrace'; | ||
| import { coreWorkerFixtures } from './core_fixtures'; | ||
| import type { SynthtraceEvents } from '../../global_hooks/synthtrace_ingestion'; | ||
|
|
||
| interface SynthtraceFixtureEsClient<TFields extends Fields> { | ||
| index: (events: SynthtraceEvents<TFields>) => Promise<void>; | ||
| clean: SynthtraceEsClient<TFields>['clean']; | ||
| } | ||
|
|
||
| export interface SynthtraceFixture { | ||
| apmSynthtraceEsClient: SynthtraceFixtureEsClient<ApmFields>; | ||
| infraSynthtraceEsClient: SynthtraceFixtureEsClient<InfraDocument>; | ||
| otelSynthtraceEsClient: SynthtraceFixtureEsClient<OtelDocument>; | ||
| } | ||
|
|
||
| const useSynthtraceClient = async <TFields extends Fields>( | ||
| client: SynthtraceEsClient<TFields>, | ||
| use: (client: SynthtraceFixtureEsClient<TFields>) => Promise<void> | ||
| ) => { | ||
| const index = async (events: SynthtraceEvents<TFields>) => | ||
| await client.index(Readable.from(Array.from(events).flatMap((event) => event.serialize()))); | ||
|
|
||
| const clean = async () => await client.clean(); | ||
|
|
||
| await use({ index, clean }); | ||
|
|
||
| // cleanup function after all tests have ran | ||
| await client.clean(); | ||
| }; | ||
|
|
||
| export const synthtraceFixture = coreWorkerFixtures.extend<{}, SynthtraceFixture>({ | ||
| apmSynthtraceEsClient: [ | ||
| async ({ esClient, config, kbnUrl, log }, use) => { | ||
| const { username, password } = config.auth; | ||
| const kibanaUrl = new URL(kbnUrl.get()); | ||
| const kibanaUrlWithAuth = Url.format({ | ||
| protocol: kibanaUrl.protocol, | ||
| hostname: kibanaUrl.hostname, | ||
| port: kibanaUrl.port, | ||
| auth: `${username}:${password}`, | ||
| }); | ||
|
|
||
| const apmSynthtraceEsClient = await getApmSynthtraceEsClient( | ||
| esClient, | ||
| kibanaUrlWithAuth, | ||
| log | ||
| ); | ||
|
|
||
| await useSynthtraceClient<ApmFields>(apmSynthtraceEsClient, use); | ||
| }, | ||
| { scope: 'worker' }, | ||
| ], | ||
| infraSynthtraceEsClient: [ | ||
| async ({ esClient, config, kbnUrl, log }, use) => { | ||
| const infraSynthtraceEsClient = await getInfraSynthtraceEsClient( | ||
| esClient, | ||
| kbnUrl.get(), | ||
| config.auth, | ||
| log | ||
| ); | ||
|
|
||
| await useSynthtraceClient<InfraDocument>(infraSynthtraceEsClient, use); | ||
| }, | ||
| { scope: 'worker' }, | ||
| ], | ||
| otelSynthtraceEsClient: [ | ||
| async ({ esClient, log }, use) => { | ||
| const otelSynthtraceEsClient = await getOtelSynthtraceEsClient(esClient, log); | ||
|
|
||
| await useSynthtraceClient<OtelDocument>(otelSynthtraceEsClient, use); | ||
| }, | ||
| { scope: 'worker' }, | ||
| ], | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| /* | ||
| * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
| * Public License v 1"; you may not use this file except in compliance with, at | ||
| * your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
| * License v3.0 only", or the "Server Side Public License, v 1". | ||
| */ | ||
|
|
||
| import { FullConfig } from 'playwright/test'; | ||
| import Url from 'url'; | ||
| import { Readable } from 'node:stream'; | ||
| import type { | ||
| ApmFields, | ||
| Fields, | ||
| InfraDocument, | ||
| OtelDocument, | ||
| Serializable, | ||
| SynthtraceGenerator, | ||
| } from '@kbn/apm-synthtrace-client'; | ||
| import { | ||
| getLogger, | ||
| createScoutConfig, | ||
| measurePerformanceAsync, | ||
| getEsClient, | ||
| ScoutLogger, | ||
| EsClient, | ||
| } from '../../common'; | ||
| import { ScoutTestOptions } from '../types'; | ||
| import { | ||
| getApmSynthtraceEsClient, | ||
| getInfraSynthtraceEsClient, | ||
| getOtelSynthtraceEsClient, | ||
| } from '../../common/services/synthtrace'; | ||
|
|
||
| export type SynthtraceEvents<T extends Fields> = SynthtraceGenerator<T> | Array<Serializable<T>>; | ||
|
|
||
| interface SynthtraceIngestionData { | ||
| apm: Array<SynthtraceEvents<ApmFields>>; | ||
| infra: Array<SynthtraceEvents<InfraDocument>>; | ||
| otel: Array<SynthtraceEvents<OtelDocument>>; | ||
| } | ||
|
|
||
| const getClient = ( | ||
| key: keyof SynthtraceIngestionData, | ||
| esClient: EsClient, | ||
| kbnUrl: string, | ||
| auth: { username: string; password: string }, | ||
| log: ScoutLogger | ||
| ) => { | ||
| switch (key) { | ||
| case 'apm': | ||
| const kibanaUrl = new URL(kbnUrl); | ||
| const kibanaUrlWithAuth = Url.format({ | ||
| protocol: kibanaUrl.protocol, | ||
| hostname: kibanaUrl.hostname, | ||
| port: kibanaUrl.port, | ||
| auth: `${auth.username}:${auth.password}`, | ||
| }); | ||
| return getApmSynthtraceEsClient(esClient, kibanaUrlWithAuth, log); | ||
| case 'infra': | ||
| return getInfraSynthtraceEsClient(esClient, kbnUrl, auth, log); | ||
| case 'otel': | ||
| return getOtelSynthtraceEsClient(esClient, log); | ||
| } | ||
| }; | ||
|
|
||
| export async function ingestSynthtraceDataHook(config: FullConfig, data: SynthtraceIngestionData) { | ||
| const log = getLogger(); | ||
|
|
||
| const { apm, infra, otel } = data; | ||
| const hasApmData = apm.length > 0; | ||
| const hasInfraData = infra.length > 0; | ||
| const hasOtelData = otel.length > 0; | ||
| const hasAnyData = hasApmData || hasInfraData || hasOtelData; | ||
|
|
||
| if (!hasAnyData) { | ||
| log.debug('[setup] no synthtrace data to ingest'); | ||
| return; | ||
| } | ||
|
|
||
| return measurePerformanceAsync(log, '[setup]: ingestSynthtraceDataHook', async () => { | ||
| // TODO: This should be configurable local vs cloud | ||
|
rmyz marked this conversation as resolved.
|
||
|
|
||
| const configName = 'local'; | ||
| const projectUse = config.projects[0].use as ScoutTestOptions; | ||
| const serversConfigDir = projectUse.serversConfigDir; | ||
| const scoutConfig = createScoutConfig(serversConfigDir, configName, log); | ||
| const esClient = getEsClient(scoutConfig, log); | ||
| const kbnUrl = scoutConfig.hosts.kibana; | ||
|
|
||
| for (const key of Object.keys(data)) { | ||
| if (Object.hasOwn(data, key)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to check if
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed, not needed |
||
| const typedKey = key as keyof SynthtraceIngestionData; | ||
| if (data[typedKey].length > 0) { | ||
| const client = await getClient(typedKey, esClient, kbnUrl, scoutConfig.auth, log); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. changed |
||
|
|
||
| log.debug(`[setup] ingesting ${typedKey} synthtrace data`); | ||
|
|
||
| try { | ||
| await Promise.all( | ||
| data[typedKey].map((event) => { | ||
| return client.index(Readable.from(Array.from(event).flatMap((e) => e.serialize()))); | ||
| }) | ||
| ); | ||
| } catch (e) { | ||
| log.debug(`[setup] error ingesting ${typedKey} synthtrace data`, e); | ||
| } | ||
|
|
||
| log.debug(`[setup] ${typedKey} synthtrace data ingested successfully`); | ||
| } else { | ||
| log.debug(`[setup] no synthtrace data to ingest for ${typedKey}`); | ||
| } | ||
| } | ||
| } | ||
| }); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.