Skip to content

Commit

Permalink
[7.x] Move Kibana Usage collectors outside the telemetry plugin (#65663
Browse files Browse the repository at this point in the history
…) (#66359)
  • Loading branch information
afharo authored May 13, 2020
1 parent 761b438 commit e2cffce
Show file tree
Hide file tree
Showing 33 changed files with 388 additions and 58 deletions.
9 changes: 9 additions & 0 deletions src/plugins/kibana_usage_collection/README.md
Original file line number Diff line number Diff line change
@@ -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
47 changes: 47 additions & 0 deletions src/plugins/kibana_usage_collection/common/constants.ts
Original file line number Diff line number Diff line change
@@ -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';
10 changes: 10 additions & 0 deletions src/plugins/kibana_usage_collection/kibana.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"id": "kibanaUsageCollection",
"version": "kibana",
"server": true,
"ui": false,
"requiredPlugins": [
"usageCollection"
],
"optionalPlugins": []
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions src/plugins/kibana_usage_collection/server/collectors/index.ts
Original file line number Diff line number Diff line change
@@ -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';

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -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);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
96 changes: 96 additions & 0 deletions src/plugins/kibana_usage_collection/server/index.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
28 changes: 28 additions & 0 deletions src/plugins/kibana_usage_collection/server/index.ts
Original file line number Diff line number Diff line change
@@ -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);
}
Loading

0 comments on commit e2cffce

Please sign in to comment.