Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Alejandro Fernández Haro <alejandro.haro@elastic.co>
This commit is contained in:
parent
3ac7fb9abf
commit
ed9c83ca07
8 changed files with 174 additions and 81 deletions
|
@ -22,6 +22,11 @@ describe('getKID', () => {
|
|||
const kid = getKID(useProdKey);
|
||||
expect(kid).toBe('kibana1');
|
||||
});
|
||||
|
||||
it(`should fallback to development`, async () => {
|
||||
const kid = getKID();
|
||||
expect(kid).toBe('kibana_dev1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('encryptTelemetry', () => {
|
||||
|
@ -46,4 +51,10 @@ describe('encryptTelemetry', () => {
|
|||
await encryptTelemetry(payload, { useProdKey: false });
|
||||
expect(mockEncrypt).toBeCalledWith('kibana_dev1', payload);
|
||||
});
|
||||
|
||||
it('should fallback to { useProdKey: false }', async () => {
|
||||
const payload = { some: 'value' };
|
||||
await encryptTelemetry(payload);
|
||||
expect(mockEncrypt).toBeCalledWith('kibana_dev1', payload);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import { coreMock, httpServerMock } from '../../../core/server/mocks';
|
||||
import { usageCollectionPluginMock } from '../../usage_collection/server/mocks';
|
||||
import { TelemetryCollectionManagerPlugin } from './plugin';
|
||||
import { CollectionStrategyConfig, StatsGetterConfig } from './types';
|
||||
import type { BasicStatsPayload, CollectionStrategyConfig, StatsGetterConfig } from './types';
|
||||
import { TelemetrySavedObjectsClient } from './telemetry_saved_objects_client';
|
||||
|
||||
function createCollectionStrategy(priority: number): jest.Mocked<CollectionStrategyConfig> {
|
||||
|
@ -87,6 +87,15 @@ describe('Telemetry Collection Manager', () => {
|
|||
});
|
||||
|
||||
describe(`after start`, () => {
|
||||
const basicStats: BasicStatsPayload = {
|
||||
cluster_uuid: 'clusterUuid',
|
||||
cluster_name: 'clusterName',
|
||||
timestamp: new Date().toISOString(),
|
||||
cluster_stats: {},
|
||||
stack_stats: {},
|
||||
version: 'version',
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
telemetryCollectionManager.start(coreMock.createStart());
|
||||
});
|
||||
|
@ -97,19 +106,59 @@ describe('Telemetry Collection Manager', () => {
|
|||
describe('unencrypted: false', () => {
|
||||
const config: StatsGetterConfig = { unencrypted: false };
|
||||
|
||||
test('getStats returns empty because clusterDetails returns empty, and the soClient is an instance of the TelemetrySavedObjectsClient', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([]);
|
||||
await expect(setupApi.getStats(config)).resolves.toStrictEqual([]);
|
||||
expect(collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient).toBeInstanceOf(
|
||||
TelemetrySavedObjectsClient
|
||||
);
|
||||
describe('getStats', () => {
|
||||
test('returns empty because clusterDetails returns empty, and the soClient is an instance of the TelemetrySavedObjectsClient', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([]);
|
||||
await expect(setupApi.getStats(config)).resolves.toStrictEqual([]);
|
||||
expect(
|
||||
collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient
|
||||
).toBeInstanceOf(TelemetrySavedObjectsClient);
|
||||
});
|
||||
|
||||
test('returns encrypted payload', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([
|
||||
{ clusterUuid: 'clusterUuid' },
|
||||
]);
|
||||
collectionStrategy.statsGetter.mockResolvedValue([basicStats]);
|
||||
await expect(setupApi.getStats(config)).resolves.toStrictEqual([expect.any(String)]);
|
||||
expect(
|
||||
collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient
|
||||
).toBeInstanceOf(TelemetrySavedObjectsClient);
|
||||
});
|
||||
});
|
||||
test('getOptInStats returns empty', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([]);
|
||||
await expect(setupApi.getOptInStats(true, config)).resolves.toStrictEqual([]);
|
||||
expect(collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient).toBeInstanceOf(
|
||||
TelemetrySavedObjectsClient
|
||||
);
|
||||
|
||||
describe('getOptInStats', () => {
|
||||
test('returns empty', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([]);
|
||||
await expect(setupApi.getOptInStats(true, config)).resolves.toStrictEqual([]);
|
||||
expect(
|
||||
collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient
|
||||
).toBeInstanceOf(TelemetrySavedObjectsClient);
|
||||
});
|
||||
|
||||
test('returns encrypted results for opt-in true', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([
|
||||
{ clusterUuid: 'clusterUuid' },
|
||||
]);
|
||||
await expect(setupApi.getOptInStats(true, config)).resolves.toStrictEqual([
|
||||
expect.any(String),
|
||||
]);
|
||||
expect(
|
||||
collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient
|
||||
).toBeInstanceOf(TelemetrySavedObjectsClient);
|
||||
});
|
||||
|
||||
test('returns encrypted results for opt-in false', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([
|
||||
{ clusterUuid: 'clusterUuid' },
|
||||
]);
|
||||
await expect(setupApi.getOptInStats(false, config)).resolves.toStrictEqual([
|
||||
expect.any(String),
|
||||
]);
|
||||
expect(
|
||||
collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient
|
||||
).toBeInstanceOf(TelemetrySavedObjectsClient);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('unencrypted: true', () => {
|
||||
|
@ -118,19 +167,60 @@ describe('Telemetry Collection Manager', () => {
|
|||
request: httpServerMock.createKibanaRequest(),
|
||||
};
|
||||
|
||||
test('getStats returns empty because clusterDetails returns empty, and the soClient is not an instance of the TelemetrySavedObjectsClient', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([]);
|
||||
await expect(setupApi.getStats(config)).resolves.toStrictEqual([]);
|
||||
expect(
|
||||
collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient
|
||||
).not.toBeInstanceOf(TelemetrySavedObjectsClient);
|
||||
describe('getStats', () => {
|
||||
test('getStats returns empty because clusterDetails returns empty, and the soClient is not an instance of the TelemetrySavedObjectsClient', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([]);
|
||||
await expect(setupApi.getStats(config)).resolves.toStrictEqual([]);
|
||||
expect(
|
||||
collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient
|
||||
).not.toBeInstanceOf(TelemetrySavedObjectsClient);
|
||||
});
|
||||
test('returns encrypted payload (assumes opted-in when no explicitly opted-out)', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([
|
||||
{ clusterUuid: 'clusterUuid' },
|
||||
]);
|
||||
collectionStrategy.statsGetter.mockResolvedValue([basicStats]);
|
||||
await expect(setupApi.getStats(config)).resolves.toStrictEqual([
|
||||
{ ...basicStats, collectionSource: 'test_collection' },
|
||||
]);
|
||||
expect(
|
||||
collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient
|
||||
).not.toBeInstanceOf(TelemetrySavedObjectsClient);
|
||||
});
|
||||
});
|
||||
test('getOptInStats returns empty', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([]);
|
||||
await expect(setupApi.getOptInStats(true, config)).resolves.toStrictEqual([]);
|
||||
expect(
|
||||
collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient
|
||||
).not.toBeInstanceOf(TelemetrySavedObjectsClient);
|
||||
|
||||
describe('getOptInStats', () => {
|
||||
test('returns empty', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([]);
|
||||
await expect(setupApi.getOptInStats(true, config)).resolves.toStrictEqual([]);
|
||||
expect(
|
||||
collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient
|
||||
).not.toBeInstanceOf(TelemetrySavedObjectsClient);
|
||||
});
|
||||
|
||||
test('returns results for opt-in true', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([
|
||||
{ clusterUuid: 'clusterUuid' },
|
||||
]);
|
||||
await expect(setupApi.getOptInStats(true, config)).resolves.toStrictEqual([
|
||||
{ cluster_uuid: 'clusterUuid', opt_in_status: true },
|
||||
]);
|
||||
expect(
|
||||
collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient
|
||||
).not.toBeInstanceOf(TelemetrySavedObjectsClient);
|
||||
});
|
||||
|
||||
test('returns results for opt-in false', async () => {
|
||||
collectionStrategy.clusterDetailsGetter.mockResolvedValue([
|
||||
{ clusterUuid: 'clusterUuid' },
|
||||
]);
|
||||
await expect(setupApi.getOptInStats(false, config)).resolves.toStrictEqual([
|
||||
{ cluster_uuid: 'clusterUuid', opt_in_status: false },
|
||||
]);
|
||||
expect(
|
||||
collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient
|
||||
).not.toBeInstanceOf(TelemetrySavedObjectsClient);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -30,7 +30,6 @@ import {
|
|||
UsageStatsPayload,
|
||||
StatsCollectionContext,
|
||||
} from './types';
|
||||
import { isClusterOptedIn } from './util';
|
||||
import { encryptTelemetry } from './encryption';
|
||||
import { TelemetrySavedObjectsClient } from './telemetry_saved_objects_client';
|
||||
|
||||
|
@ -233,7 +232,7 @@ export class TelemetryCollectionManagerPlugin
|
|||
return usageData;
|
||||
}
|
||||
|
||||
return encryptTelemetry(usageData.filter(isClusterOptedIn), {
|
||||
return await encryptTelemetry(usageData, {
|
||||
useProdKey: this.isDistributable,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
* 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 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 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const isClusterOptedIn = (clusterUsage: any): boolean => {
|
||||
return clusterUsage?.stack_stats?.kibana?.plugins?.telemetry?.opt_in_status === true;
|
||||
};
|
|
@ -7,13 +7,27 @@
|
|||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import supertestAsPromised from 'supertest-as-promised';
|
||||
import { basicUiCounters } from './__fixtures__/ui_counters';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
import { SavedObject } from '../../../../src/core/server';
|
||||
import type { FtrProviderContext } from '../../ftr_provider_context';
|
||||
import type { SavedObject } from '../../../../src/core/server';
|
||||
import ossRootTelemetrySchema from '../../../../src/plugins/telemetry/schema/oss_root.json';
|
||||
import ossPluginsTelemetrySchema from '../../../../src/plugins/telemetry/schema/oss_plugins.json';
|
||||
import { assertTelemetryPayload, flatKeys } from './utils';
|
||||
|
||||
async function retrieveTelemetry(
|
||||
supertest: supertestAsPromised.SuperTest<supertestAsPromised.Test>
|
||||
) {
|
||||
const { body } = await supertest
|
||||
.post('/api/telemetry/v2/clusters/_stats')
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({ unencrypted: true })
|
||||
.expect(200);
|
||||
|
||||
expect(body.length).to.be(1);
|
||||
return body[0];
|
||||
}
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
const es = getService('es');
|
||||
|
@ -35,14 +49,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
let stats: Record<string, any>;
|
||||
|
||||
before('pull local stats', async () => {
|
||||
const { body } = await supertest
|
||||
.post('/api/telemetry/v2/clusters/_stats')
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({ unencrypted: true })
|
||||
.expect(200);
|
||||
|
||||
expect(body.length).to.be(1);
|
||||
stats = body[0];
|
||||
stats = await retrieveTelemetry(supertest);
|
||||
});
|
||||
|
||||
it('should pass the schema validation', () => {
|
||||
|
@ -141,14 +148,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
before('Add UI Counters saved objects', () => esArchiver.load('saved_objects/ui_counters'));
|
||||
after('cleanup saved objects changes', () => esArchiver.unload('saved_objects/ui_counters'));
|
||||
it('returns ui counters aggregated by day', async () => {
|
||||
const { body } = await supertest
|
||||
.post('/api/telemetry/v2/clusters/_stats')
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({ unencrypted: true })
|
||||
.expect(200);
|
||||
|
||||
expect(body.length).to.be(1);
|
||||
const stats = body[0];
|
||||
const stats = await retrieveTelemetry(supertest);
|
||||
expect(stats.stack_stats.kibana.plugins.ui_counters).to.eql(basicUiCounters);
|
||||
});
|
||||
});
|
||||
|
@ -191,14 +191,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('should return application_usage data', async () => {
|
||||
const { body } = await supertest
|
||||
.post('/api/telemetry/v2/clusters/_stats')
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({ unencrypted: true })
|
||||
.expect(200);
|
||||
|
||||
expect(body.length).to.be(1);
|
||||
const stats = body[0];
|
||||
const stats = await retrieveTelemetry(supertest);
|
||||
expect(stats.stack_stats.kibana.plugins.application_usage).to.eql({
|
||||
'test-app': {
|
||||
appId: 'test-app',
|
||||
|
@ -262,14 +255,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
// flaky https://github.com/elastic/kibana/issues/94513
|
||||
it.skip("should only use the first 10k docs for the application_usage data (they'll be rolled up in a later process)", async () => {
|
||||
const { body } = await supertest
|
||||
.post('/api/telemetry/v2/clusters/_stats')
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({ unencrypted: true })
|
||||
.expect(200);
|
||||
|
||||
expect(body.length).to.be(1);
|
||||
const stats = body[0];
|
||||
const stats = await retrieveTelemetry(supertest);
|
||||
expect(stats.stack_stats.kibana.plugins.application_usage).to.eql({
|
||||
'test-app': {
|
||||
appId: 'test-app',
|
||||
|
|
|
@ -9,6 +9,7 @@ import { StatsGetter } from 'src/plugins/telemetry_collection_manager/server';
|
|||
import { TelemetryLocalStats, getLocalStats } from '../../../../../src/plugins/telemetry/server';
|
||||
import { getXPackUsage } from './get_xpack';
|
||||
import { ESLicense, getLicenseFromLocalOrMaster } from './get_license';
|
||||
import { isClusterOptedIn } from './is_cluster_opted_in';
|
||||
|
||||
export type TelemetryAggregatedStats = TelemetryLocalStats & {
|
||||
stack_stats: { xpack?: object };
|
||||
|
@ -48,6 +49,10 @@ export const getStatsWithXpack: StatsGetter<TelemetryAggregatedStats> = async fu
|
|||
if (monitoringTelemetry) {
|
||||
delete stats.stack_stats.kibana!.plugins.monitoringTelemetry;
|
||||
}
|
||||
return [...acc, stats, ...(monitoringTelemetry || [])];
|
||||
|
||||
// From the monitoring-sourced telemetry, we need to filter out the clusters that are opted-out.
|
||||
const onlyOptedInMonitoringClusters = (monitoringTelemetry || []).filter(isClusterOptedIn);
|
||||
|
||||
return [...acc, stats, ...onlyOptedInMonitoringClusters];
|
||||
}, [] as TelemetryAggregatedStats[]);
|
||||
};
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
/*
|
||||
* 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 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 or the Server
|
||||
* Side Public License, v 1.
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { isClusterOptedIn } from './util';
|
||||
import { isClusterOptedIn } from './is_cluster_opted_in';
|
||||
|
||||
const createMockClusterUsage = (plugins: any) => {
|
||||
return {
|
||||
|
@ -32,9 +31,9 @@ describe('isClusterOptedIn', () => {
|
|||
const result = isClusterOptedIn(mockClusterUsage);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
it('returns false if cluster stats is malformed', () => {
|
||||
expect(isClusterOptedIn(createMockClusterUsage({}))).toBe(false);
|
||||
expect(isClusterOptedIn({})).toBe(false);
|
||||
expect(isClusterOptedIn(undefined)).toBe(false);
|
||||
it('returns true if kibana.plugins.telemetry does not exist', () => {
|
||||
expect(isClusterOptedIn(createMockClusterUsage({}))).toBe(true);
|
||||
expect(isClusterOptedIn({})).toBe(true);
|
||||
expect(isClusterOptedIn(undefined)).toBe(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export const isClusterOptedIn = (clusterUsage: any): boolean => {
|
||||
return (
|
||||
clusterUsage?.stack_stats?.kibana?.plugins?.telemetry?.opt_in_status === true ||
|
||||
// If stack_stats.kibana.plugins.telemetry does not exist, assume opted-in for BWC
|
||||
!clusterUsage?.stack_stats?.kibana?.plugins?.telemetry
|
||||
);
|
||||
};
|
Loading…
Reference in a new issue