Skip to content

Commit

Permalink
[Telemetry] Report Kibana distro in local collectors + Usage Collecto…
Browse files Browse the repository at this point in the history
…rs in TS (#55859) (#56849)

* [Telemetry] Report Kibana distro in local collectors + Usage Collectors in TS

* Ensure isReady is a function

* Move CollectorSet tests to TS + Jest

* Fix test

Co-authored-by: Elastic Machine <[email protected]>

Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
afharo and elasticmachine authored Feb 5, 2020
1 parent 85037bd commit 9b045e7
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ describe('telemetry_usage_collector', () => {
const collectorOptions = createTelemetryUsageCollector(usageCollector, server);

expect(collectorOptions.type).toBe('static_telemetry');
expect(await collectorOptions.fetch()).toEqual(expectedObject);
expect(await collectorOptions.fetch({} as any)).toEqual(expectedObject); // Sending any as the callCluster client because it's not needed in this collector but TS requires it when calling it.
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,27 @@
* under the License.
*/

import { get, omit } from 'lodash';
import { omit } from 'lodash';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { CallCluster } from 'src/legacy/core_plugins/elasticsearch';

export function handleKibanaStats(server, response) {
export interface KibanaUsageStats {
kibana: {
index: string;
};
kibana_stats: {
os: {
platform: string;
platformRelease: string;
distro?: string;
distroRelease?: string;
};
};

[plugin: string]: any;
}

export function handleKibanaStats(server: any, response?: KibanaUsageStats) {
if (!response) {
server.log(
['warning', 'telemetry', 'local-stats'],
Expand All @@ -30,8 +48,17 @@ export function handleKibanaStats(server, response) {

const { kibana, kibana_stats: kibanaStats, ...plugins } = response;

const platform = get(kibanaStats, 'os.platform', 'unknown');
const platformRelease = get(kibanaStats, 'os.platformRelease', 'unknown');
const os = {
platform: 'unknown',
platformRelease: 'unknown',
...kibanaStats.os,
};
const formattedOsStats = Object.entries(os).reduce((acc, [key, value]) => {
return {
...acc,
[`${key}s`]: [{ [key]: value, count: 1 }],
};
}, {});

const version = server
.config()
Expand All @@ -44,16 +71,16 @@ export function handleKibanaStats(server, response) {
...omit(kibana, 'index'), // discard index
count: 1,
indices: 1,
os: {
platforms: [{ platform, count: 1 }],
platformReleases: [{ platformRelease, count: 1 }],
},
os: formattedOsStats,
versions: [{ version, count: 1 }],
plugins,
};
}

export async function getKibana(usageCollection, callWithInternalUser) {
export async function getKibana(
usageCollection: UsageCollectionSetup,
callWithInternalUser: CallCluster
): Promise<KibanaUsageStats> {
const usage = await usageCollection.bulkFetch(callWithInternalUser);
return usageCollection.toObject(usage);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,25 @@ import { get, omit } from 'lodash';
import { getClusterInfo } from './get_cluster_info';
import { getClusterStats } from './get_cluster_stats';
// @ts-ignore
import { getKibana, handleKibanaStats } from './get_kibana';
import { getKibana, handleKibanaStats, KibanaUsageStats } from './get_kibana';
import { StatsGetter } from '../collection_manager';

/**
* Handle the separate local calls by combining them into a single object response that looks like the
* "cluster_stats" document from X-Pack monitoring.
*
* @param {Object} server ??
* @param {Object} clusterInfo Cluster info (GET /)
* @param {Object} clusterStats Cluster stats (GET /_cluster/stats)
* @param {Object} kibana The Kibana Usage stats
* @return {Object} A combined object containing the different responses.
*/
export function handleLocalStats(server: any, clusterInfo: any, clusterStats: any, kibana: any) {
export function handleLocalStats(
server: any,
clusterInfo: any,
clusterStats: any,
kibana: KibanaUsageStats
) {
return {
timestamp: new Date().toISOString(),
cluster_uuid: get(clusterInfo, 'cluster_uuid'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,30 @@
* under the License.
*/

export class Collector {
import { Logger } from 'kibana/server';
import { CallCluster } from 'src/legacy/core_plugins/elasticsearch';

export type CollectorFormatForBulkUpload<T, U> = (result: T) => { type: string; payload: U };

export interface CollectorOptions<T = unknown, U = T> {
type: string;
init?: Function;
fetch: (callCluster: CallCluster) => Promise<T> | T;
/*
* A hook for allowing the fetched data payload to be organized into a typed
* data model for internal bulk upload. See defaultFormatterForBulkUpload for
* a generic example.
*/
formatForBulkUpload?: CollectorFormatForBulkUpload<T, U>;
isReady: () => Promise<boolean> | boolean;
}

export class Collector<T = unknown, U = T> {
public readonly type: CollectorOptions<T, U>['type'];
public readonly init?: CollectorOptions<T, U>['init'];
public readonly fetch: CollectorOptions<T, U>['fetch'];
private readonly _formatForBulkUpload?: CollectorFormatForBulkUpload<T, U>;
public readonly isReady: CollectorOptions<T, U>['isReady'];
/*
* @param {Object} logger - logger object
* @param {String} options.type - property name as the key for the data
Expand All @@ -27,8 +50,8 @@ export class Collector {
* @param {Function} options.rest - optional other properties
*/
constructor(
logger,
{ type, init, fetch, formatForBulkUpload = null, isReady = null, ...options } = {}
protected readonly log: Logger,
{ type, init, fetch, formatForBulkUpload, isReady, ...options }: CollectorOptions<T, U>
) {
if (type === undefined) {
throw new Error('Collector must be instantiated with a options.type string property');
Expand All @@ -42,41 +65,27 @@ export class Collector {
throw new Error('Collector must be instantiated with a options.fetch function property');
}

this.log = logger;

Object.assign(this, options); // spread in other properties and mutate "this"

this.type = type;
this.init = init;
this.fetch = fetch;

const defaultFormatterForBulkUpload = result => ({ type, payload: result });
this._formatForBulkUpload = formatForBulkUpload || defaultFormatterForBulkUpload;
if (typeof isReady === 'function') {
this.isReady = isReady;
}
this.isReady = typeof isReady === 'function' ? isReady : () => true;
this._formatForBulkUpload = formatForBulkUpload;
}

/*
* @param {Function} callCluster - callCluster function
*/
fetchInternal(callCluster) {
if (typeof callCluster !== 'function') {
throw new Error('A `callCluster` function must be passed to the fetch methods of collectors');
public formatForBulkUpload(result: T) {
if (this._formatForBulkUpload) {
return this._formatForBulkUpload(result);
} else {
return this.defaultFormatterForBulkUpload(result);
}
return this.fetch(callCluster);
}

/*
* A hook for allowing the fetched data payload to be organized into a typed
* data model for internal bulk upload. See defaultFormatterForBulkUpload for
* a generic example.
*/
formatForBulkUpload(result) {
return this._formatForBulkUpload(result);
}

isReady() {
throw `isReady() must be implemented in ${this.type} collector`;
protected defaultFormatterForBulkUpload(result: T) {
return {
type: this.type,
payload: result,
};
}
}
Loading

0 comments on commit 9b045e7

Please sign in to comment.