Move Kibana Usage collectors outside the telemetry plugin (#65663)

This commit is contained in:
Alejandro Fernández Haro 2020-05-13 09:22:38 +01:00 committed by GitHub
parent a149915c52
commit 577bf0928b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 389 additions and 58 deletions

1
.github/CODEOWNERS vendored
View file

@ -162,6 +162,7 @@
# Pulse
/packages/kbn-analytics/ @elastic/pulse
/src/legacy/core_plugins/ui_metric/ @elastic/pulse
/src/plugins/kibana_usage_collection/ @elastic/pulse
/src/plugins/telemetry/ @elastic/pulse
/src/plugins/telemetry_collection_manager/ @elastic/pulse
/src/plugins/telemetry_management_section/ @elastic/pulse

View file

@ -0,0 +1,9 @@
# Kibana Usage Collection
This plugin registers the basic usage collectors from Kibana:
- Application Usage
- UI Metrics
- Ops stats
- Number of Saved Objects per type
- Non-default UI Settings

View file

@ -0,0 +1,47 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export const PLUGIN_ID = 'kibanaUsageCollection';
export const PLUGIN_NAME = 'kibana_usage_collection';
/**
* UI metric usage type
*/
export const UI_METRIC_USAGE_TYPE = 'ui_metric';
/**
* Application Usage type
*/
export const APPLICATION_USAGE_TYPE = 'application_usage';
/**
* The type name used within the Monitoring index to publish management stats.
*/
export const KIBANA_STACK_MANAGEMENT_STATS_TYPE = 'stack_management';
/**
* The type name used to publish Kibana usage stats.
* NOTE: this string shows as-is in the stats API as a field name for the kibana usage stats
*/
export const KIBANA_USAGE_TYPE = 'kibana';
/**
* The type name used to publish Kibana usage stats in the formatted as bulk.
*/
export const KIBANA_STATS_TYPE = 'kibana_stats';

View file

@ -0,0 +1,10 @@
{
"id": "kibanaUsageCollection",
"version": "kibana",
"server": true,
"ui": false,
"requiredPlugins": [
"usageCollection"
],
"optionalPlugins": []
}

View file

@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`kibana_usage_collection Runs the setup method without issues 1`] = `true`;
exports[`kibana_usage_collection Runs the setup method without issues 2`] = `true`;
exports[`kibana_usage_collection Runs the setup method without issues 3`] = `false`;
exports[`kibana_usage_collection Runs the setup method without issues 4`] = `false`;
exports[`kibana_usage_collection Runs the setup method without issues 5`] = `false`;

View file

@ -0,0 +1,24 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export { registerUiMetricUsageCollector } from './ui_metric';
export { registerManagementUsageCollector } from './management';
export { registerApplicationUsageCollector } from './application_usage';
export { registerKibanaUsageCollector } from './kibana';
export { registerOpsStatsCollector } from './ops_stats';

View file

@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`telemetry_application_usage_collector fetch() 1`] = `
Object {
"my-key": "my-value",
}
`;

View file

@ -0,0 +1,67 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { uiSettingsServiceMock } from '../../../../../core/server/mocks';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { CollectorOptions } from '../../../../../plugins/usage_collection/server/collector/collector';
import { registerManagementUsageCollector } from './';
describe('telemetry_application_usage_collector', () => {
let collector: CollectorOptions;
const usageCollectionMock: jest.Mocked<UsageCollectionSetup> = {
makeUsageCollector: jest.fn().mockImplementation(config => (collector = config)),
registerCollector: jest.fn(),
} as any;
const uiSettingsClient = uiSettingsServiceMock.createClient();
const getUiSettingsClient = jest.fn(() => uiSettingsClient);
const callCluster = jest.fn();
beforeAll(() => {
registerManagementUsageCollector(usageCollectionMock, getUiSettingsClient);
});
test('registered collector is set', () => {
expect(collector).not.toBeUndefined();
});
test('isReady() => false if no client', () => {
getUiSettingsClient.mockImplementationOnce(() => undefined as any);
expect(collector.isReady()).toBe(false);
});
test('isReady() => true', () => {
expect(collector.isReady()).toBe(true);
});
test('fetch()', async () => {
uiSettingsClient.getUserProvided.mockImplementationOnce(async () => ({
'my-key': { userValue: 'my-value' },
}));
await expect(collector.fetch(callCluster)).resolves.toMatchSnapshot();
});
test('fetch() should not fail if invoked when not ready', async () => {
getUiSettingsClient.mockImplementationOnce(() => undefined as any);
await expect(collector.fetch(callCluster)).resolves.toBe(undefined);
});
});

View file

@ -17,18 +17,12 @@
* under the License.
*/
import { size } from 'lodash';
import { IUiSettingsClient } from 'kibana/server';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { KIBANA_STACK_MANAGEMENT_STATS_TYPE } from '../../../common/constants';
export type UsageStats = Record<string, any>;
export async function getTranslationCount(loader: any, locale: string): Promise<number> {
const translations = await loader.getTranslationsByLocale(locale);
return size(translations.messages);
}
export function createCollectorFetch(getUiSettingsClient: () => IUiSettingsClient | undefined) {
return async function fetchUsageStats(): Promise<UsageStats | undefined> {
const uiSettingsClient = getUiSettingsClient();

View file

@ -0,0 +1,96 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { BehaviorSubject } from 'rxjs';
import {
coreMock,
savedObjectsRepositoryMock,
uiSettingsServiceMock,
} from '../../../core/server/mocks';
import { UsageCollectionSetup } from '../../usage_collection/server';
import { plugin } from './';
describe('kibana_usage_collection', () => {
const pluginInstance = plugin(coreMock.createPluginInitializerContext({}));
const usageCollectors: Array<{ isReady: () => boolean }> = [];
const usageCollection: jest.Mocked<UsageCollectionSetup> = {
makeStatsCollector: jest.fn().mockImplementation(opts => {
usageCollectors.push(opts);
return opts;
}),
makeUsageCollector: jest.fn().mockImplementation(opts => {
usageCollectors.push(opts);
return opts;
}),
registerCollector: jest.fn(),
} as any;
test('Runs the setup method without issues', () => {
const coreSetup = coreMock.createSetup();
coreSetup.metrics.getOpsMetrics$.mockImplementation(
() =>
new BehaviorSubject({
process: {
memory: {
heap: { total_in_bytes: 1, used_in_bytes: 1, size_limit: 1 },
resident_set_size_in_bytes: 1,
},
event_loop_delay: 1,
pid: 1,
uptime_in_millis: 1,
},
os: {
platform: 'darwin' as const,
platformRelease: 'test',
load: { '1m': 1, '5m': 1, '15m': 1 },
memory: { total_in_bytes: 1, free_in_bytes: 1, used_in_bytes: 1 },
uptime_in_millis: 1,
},
response_times: { avg_in_millis: 1, max_in_millis: 1 },
requests: { disconnects: 1, total: 1, statusCodes: { '200': 1 } },
concurrent_connections: 1,
})
);
expect(pluginInstance.setup(coreSetup, { usageCollection })).toBe(undefined);
usageCollectors.forEach(({ isReady }) => {
expect(isReady()).toMatchSnapshot(); // Some should return false at this stage
});
});
test('Runs the start method without issues', () => {
const coreStart = coreMock.createStart();
coreStart.savedObjects.createInternalRepository.mockImplementation(() =>
savedObjectsRepositoryMock.create()
);
coreStart.uiSettings.asScopedToClient.mockImplementation(() =>
uiSettingsServiceMock.createClient()
);
expect(pluginInstance.start(coreStart)).toBe(undefined);
usageCollectors.forEach(({ isReady }) => {
expect(isReady()).toBe(true); // All should return true at this point
});
});
test('Runs the stop method without issues', () => {
expect(pluginInstance.stop()).toBe(undefined);
});
});

View file

@ -0,0 +1,28 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { PluginInitializerContext } from '../../../core/server';
import { KibanaUsageCollectionPlugin } from './plugin';
// This exports static code and TypeScript types,
// as well as, Kibana Platform `plugin()` initializer.
export function plugin(initializerContext: PluginInitializerContext) {
return new KibanaUsageCollectionPlugin(initializerContext);
}

View file

@ -0,0 +1,87 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { Observable } from 'rxjs';
import {
PluginInitializerContext,
CoreSetup,
Plugin,
MetricsServiceSetup,
ISavedObjectsRepository,
IUiSettingsClient,
SharedGlobalConfig,
SavedObjectsClient,
CoreStart,
SavedObjectsServiceSetup,
} from '../../../core/server';
import {
registerApplicationUsageCollector,
registerKibanaUsageCollector,
registerManagementUsageCollector,
registerOpsStatsCollector,
registerUiMetricUsageCollector,
} from './collectors';
interface KibanaUsageCollectionPluginsDepsSetup {
usageCollection: UsageCollectionSetup;
}
type SavedObjectsRegisterType = SavedObjectsServiceSetup['registerType'];
export class KibanaUsageCollectionPlugin implements Plugin {
private readonly legacyConfig$: Observable<SharedGlobalConfig>;
private savedObjectsClient?: ISavedObjectsRepository;
private uiSettingsClient?: IUiSettingsClient;
constructor(initializerContext: PluginInitializerContext) {
this.legacyConfig$ = initializerContext.config.legacy.globalConfig$;
}
public setup(
{ savedObjects, metrics, getStartServices }: CoreSetup,
{ usageCollection }: KibanaUsageCollectionPluginsDepsSetup
) {
this.registerUsageCollectors(usageCollection, metrics, opts => savedObjects.registerType(opts));
}
public start(core: CoreStart) {
const { savedObjects, uiSettings } = core;
this.savedObjectsClient = savedObjects.createInternalRepository();
const savedObjectsClient = new SavedObjectsClient(this.savedObjectsClient);
this.uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient);
}
public stop() {}
private registerUsageCollectors(
usageCollection: UsageCollectionSetup,
metrics: MetricsServiceSetup,
registerType: SavedObjectsRegisterType
) {
const getSavedObjectsClient = () => this.savedObjectsClient;
const getUiSettingsClient = () => this.uiSettingsClient;
registerOpsStatsCollector(usageCollection, metrics.getOpsMetrics$());
registerKibanaUsageCollector(usageCollection, this.legacyConfig$);
registerManagementUsageCollector(usageCollection, getUiSettingsClient);
registerUiMetricUsageCollector(usageCollection, registerType, getSavedObjectsClient);
registerApplicationUsageCollector(usageCollection, registerType, getSavedObjectsClient);
}
}

View file

@ -65,29 +65,3 @@ export const TELEMETRY_STATS_TYPE = 'telemetry';
* The endpoint version when hitting the remote telemetry service
*/
export const ENDPOINT_VERSION = 'v2';
/**
* UI metric usage type
*/
export const UI_METRIC_USAGE_TYPE = 'ui_metric';
/**
* Application Usage type
*/
export const APPLICATION_USAGE_TYPE = 'application_usage';
/**
* The type name used within the Monitoring index to publish management stats.
*/
export const KIBANA_STACK_MANAGEMENT_STATS_TYPE = 'stack_management';
/**
* The type name used to publish Kibana usage stats.
* NOTE: this string shows as-is in the stats API as a field name for the kibana usage stats
*/
export const KIBANA_USAGE_TYPE = 'kibana';
/**
* The type name used to publish Kibana usage stats in the formatted as bulk.
*/
export const KIBANA_STATS_TYPE = 'kibana_stats';

View file

@ -18,9 +18,4 @@
*/
export { registerTelemetryUsageCollector } from './usage';
export { registerUiMetricUsageCollector } from './ui_metric';
export { registerTelemetryPluginUsageCollector } from './telemetry_plugin';
export { registerManagementUsageCollector } from './management';
export { registerApplicationUsageCollector } from './application_usage';
export { registerKibanaUsageCollector } from './kibana';
export { registerOpsStatsCollector } from './ops_stats';

View file

@ -32,19 +32,12 @@ import {
SavedObjectsClient,
Plugin,
Logger,
SharedGlobalConfig,
MetricsServiceSetup,
} from '../../../core/server';
import { registerRoutes } from './routes';
import { registerCollection } from './telemetry_collection';
import {
registerUiMetricUsageCollector,
registerTelemetryUsageCollector,
registerTelemetryPluginUsageCollector,
registerManagementUsageCollector,
registerApplicationUsageCollector,
registerKibanaUsageCollector,
registerOpsStatsCollector,
} from './collectors';
import { TelemetryConfigType } from './config';
import { FetcherTask } from './fetcher';
@ -65,7 +58,6 @@ export class TelemetryPlugin implements Plugin {
private readonly logger: Logger;
private readonly currentKibanaVersion: string;
private readonly config$: Observable<TelemetryConfigType>;
private readonly legacyConfig$: Observable<SharedGlobalConfig>;
private readonly isDev: boolean;
private readonly fetcherTask: FetcherTask;
private savedObjectsClient?: ISavedObjectsRepository;
@ -76,7 +68,6 @@ export class TelemetryPlugin implements Plugin {
this.isDev = initializerContext.env.mode.dev;
this.currentKibanaVersion = initializerContext.env.packageInfo.version;
this.config$ = initializerContext.config.create();
this.legacyConfig$ = initializerContext.config.legacy.globalConfig$;
this.fetcherTask = new FetcherTask({
...initializerContext,
logger: this.logger,
@ -103,7 +94,7 @@ export class TelemetryPlugin implements Plugin {
});
this.registerMappings(opts => savedObjects.registerType(opts));
this.registerUsageCollectors(usageCollection, metrics, opts => savedObjects.registerType(opts));
this.registerUsageCollectors(usageCollection);
}
public async start(core: CoreStart, { telemetryCollectionManager }: TelemetryPluginsStart) {
@ -157,24 +148,14 @@ export class TelemetryPlugin implements Plugin {
});
}
private registerUsageCollectors(
usageCollection: UsageCollectionSetup,
metrics: MetricsServiceSetup,
registerType: SavedObjectsRegisterType
) {
private registerUsageCollectors(usageCollection: UsageCollectionSetup) {
const getSavedObjectsClient = () => this.savedObjectsClient;
const getUiSettingsClient = () => this.uiSettingsClient;
registerOpsStatsCollector(usageCollection, metrics.getOpsMetrics$());
registerKibanaUsageCollector(usageCollection, this.legacyConfig$);
registerTelemetryPluginUsageCollector(usageCollection, {
currentKibanaVersion: this.currentKibanaVersion,
config$: this.config$,
getSavedObjectsClient,
});
registerTelemetryUsageCollector(usageCollection, this.config$);
registerManagementUsageCollector(usageCollection, getUiSettingsClient);
registerUiMetricUsageCollector(usageCollection, registerType, getSavedObjectsClient);
registerApplicationUsageCollector(usageCollection, registerType, getSavedObjectsClient);
}
}