Upgrade assistant telemetry (#28878)

* feat(NA): very first version with telemetry working for the upgrade assistant.

* refact(NA): import all types from a file and rewrite first version code.

* chore(NA): missing types for register and makeCollector functions.

* test(NA): tests for upgrade assistant telemetry routes.

* refact(NA): extra spaces on imports and imports order.

* test(NA): for ui_open saved objects on upgrade assistant telemetry.

* test(NA): for usage_collector functions.

* chore(NA): change the return object into the upgrade assistant telemetry.

* chore(NA): fix type imports.

* fix(NA): importing saved objects client types.

* refact(NA): simplify saved objects schema for upgrade assistant telemetry.

* feat(NA): only send upgrade assistant metrics after we have data. Never send telemetry info about a clicked tab if it is the current selected one.

* fix(NA): typo on word

* feat(NA): add telemetry schema support for for ui_reindex.

* test(NA): update integration test fixture to include new telemetry stats.

* chore(NA): fix mappings for ui_reindex.

* refact(NA): invest telemetry local expect data order.

* chore(NA): includeDefaults for callCluster.

* chore(NA): remove ignore 404 from callCluster.

* refact(NA): wrap get logging status from cluster settings into a function.

* feat(NA): add reindex ui telemetry.

* fix(NA): typo on mappings. fix(NA): non wait for send telemetry to complete will cause es doc errors when increment counters

* test(NA): wait for telemetry requests to end before esArchiver.unload on functional tests.

* refact(NA): remove bad added async to tabs onclick function.
This commit is contained in:
Tiago Costa 2019-02-04 21:24:31 +00:00 committed by GitHub
parent 4dab26ab58
commit 64334ca26e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 908 additions and 17 deletions

View file

@ -17,6 +17,8 @@
* under the License.
*/
import { BaseOptions, SavedObject } from '../saved_objects_client';
export interface SavedObjectsRepositoryOptions {
index: string | string[];
mappings: unknown;
@ -30,5 +32,13 @@ export interface SavedObjectsRepositoryOptions {
export declare class SavedObjectsRepository {
// ATTENTION: this interface is incomplete
public get: (type: string, id: string, options?: BaseOptions) => Promise<SavedObject>;
public incrementCounter: (
type: string,
id: string,
counterFieldName: string,
options?: BaseOptions
) => Promise<SavedObject>;
constructor(options: SavedObjectsRepositoryOptions);
}

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Legacy } from 'kibana';
import {
SavedObject,
SavedObjectAttributes,
@ -57,3 +58,68 @@ export enum IndexGroup {
ml = '___ML_REINDEX_LOCK___',
watcher = '___WATCHER_REINDEX_LOCK___',
}
// Telemetry types
export const UPGRADE_ASSISTANT_TYPE = 'upgrade-assistant-telemetry';
export const UPGRADE_ASSISTANT_DOC_ID = 'upgrade-assistant-telemetry';
export type UIOpenOption = 'overview' | 'cluster' | 'indices';
export type UIReindexOption = 'close' | 'open' | 'start' | 'stop';
export interface UIOpen {
overview: boolean;
cluster: boolean;
indices: boolean;
}
export interface UIReindex {
close: boolean;
open: boolean;
start: boolean;
stop: boolean;
}
export interface UpgradeAssistantTelemetryServer extends Legacy.Server {
usage: {
collectorSet: {
makeUsageCollector: any;
register: any;
};
};
}
export interface UpgradeAssistantTelemetrySavedObject {
ui_open: {
overview: number;
cluster: number;
indices: number;
};
ui_reindex: {
close: number;
open: number;
start: number;
stop: number;
};
}
export interface UpgradeAssistantTelemetry {
ui_open: {
overview: number;
cluster: number;
indices: number;
};
ui_reindex: {
close: number;
open: number;
start: number;
stop: number;
};
features: {
deprecation_logging: {
enabled: boolean;
};
};
}
export interface UpgradeAssistantTelemetrySavedObjectAttributes {
[key: string]: any;
}

View file

@ -6,6 +6,7 @@
import { Server } from 'hapi';
import Joi from 'joi';
import { resolve } from 'path';
import mappings from './mappings.json';
import { initServer } from './server';
export function upgradeAssistant(kibana: any) {
@ -14,13 +15,16 @@ export function upgradeAssistant(kibana: any) {
require: ['elasticsearch'],
uiExports: {
managementSections: ['plugins/upgrade_assistant'],
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
mappings: require('./mappings.json'),
savedObjectSchemas: {
'upgrade-assistant-reindex-operation': {
isNamespaceAgnostic: true,
},
'upgrade-assistant-telemetry': {
isNamespaceAgnostic: true,
},
},
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
mappings,
},
publicDir: resolve(__dirname, 'public'),

View file

@ -9,5 +9,57 @@
"type": "integer"
}
}
},
"upgrade-assistant-telemetry": {
"properties": {
"ui_open": {
"properties": {
"overview": {
"type": "long",
"null_value": 0
},
"cluster": {
"type": "long",
"null_value": 0
},
"indices": {
"type": "long",
"null_value": 0
}
}
},
"ui_reindex": {
"properties": {
"close": {
"type": "long",
"null_value": 0
},
"open": {
"type": "long",
"null_value": 0
},
"start": {
"type": "long",
"null_value": 0
},
"stop": {
"type": "long",
"null_value": 0
}
}
},
"features": {
"properties": {
"deprecation_logging": {
"properties": {
"enabled": {
"type": "boolean",
"null_value": true
}
}
}
}
}
}
}
}

View file

@ -5,25 +5,27 @@
*/
import axios from 'axios';
import { findIndex } from 'lodash';
import { findIndex, set } from 'lodash';
import React from 'react';
import { EuiTabbedContent, EuiTabbedContentTab } from '@elastic/eui';
import { injectI18n } from '@kbn/i18n/react';
import chrome from 'ui/chrome';
import { kfetch } from 'ui/kfetch';
import { UpgradeAssistantStatus } from '../../server/lib/es_migration_apis';
import { LatestMinorBanner } from './latest_minor_banner';
import { CheckupTab } from './tabs/checkup';
import { OverviewTab } from './tabs/overview';
import { LoadingState, UpgradeAssistantTabProps } from './types';
import { LoadingState, TelemetryState, UpgradeAssistantTabProps } from './types';
interface TabsState {
loadingState: LoadingState;
loadingError?: Error;
checkupData?: UpgradeAssistantStatus;
selectedTabIndex: number;
telemetryState: TelemetryState;
}
export class UpgradeAssistantTabsUI extends React.Component<
@ -36,19 +38,26 @@ export class UpgradeAssistantTabsUI extends React.Component<
this.state = {
loadingState: LoadingState.Loading,
selectedTabIndex: 0,
telemetryState: TelemetryState.Complete,
};
}
public componentDidMount() {
this.loadData();
public async componentDidMount() {
await this.loadData();
// Send telemetry info about the default selected tab
this.sendTelemetryInfo(this.tabs[this.state.selectedTabIndex].id);
}
public render() {
const { selectedTabIndex } = this.state;
const { selectedTabIndex, telemetryState } = this.state;
const tabs = this.tabs;
return (
<EuiTabbedContent
data-test-subj={
telemetryState === TelemetryState.Running ? 'upgradeAssistantTelemetryRunning' : undefined
}
tabs={tabs}
onTabClick={this.onTabClick}
selectedTab={tabs[selectedTabIndex]}
@ -62,6 +71,13 @@ export class UpgradeAssistantTabsUI extends React.Component<
throw new Error(`Clicked tab did not exist in tabs array`);
}
// Send telemetry info about the current selected tab
// only in case the clicked tab id it's different from the
// current selected tab id
if (this.tabs[this.state.selectedTabIndex].id !== selectedTab.id) {
this.sendTelemetryInfo(selectedTab.id);
}
this.setSelectedTabIndex(selectedTabIndex);
};
@ -142,6 +158,24 @@ export class UpgradeAssistantTabsUI extends React.Component<
},
];
}
private async sendTelemetryInfo(tabName: string) {
// In case we don't have any data yet, we wanna to ignore the
// telemetry info update
if (this.state.loadingState !== LoadingState.Success) {
return;
}
this.setState({ telemetryState: TelemetryState.Running });
await kfetch({
pathname: '/api/upgrade_assistant/telemetry/ui_open',
method: 'PUT',
body: JSON.stringify(set({}, tabName, true)),
});
this.setState({ telemetryState: TelemetryState.Complete });
}
}
export const UpgradeAssistantTabs = injectI18n(UpgradeAssistantTabsUI);

View file

@ -4,11 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { set } from 'lodash';
import React, { Fragment, ReactNode } from 'react';
import { Subscription } from 'rxjs';
import { EuiButton, EuiLoadingSpinner } from '@elastic/eui';
import { ReindexStatus } from '../../../../../../common/types';
import { kfetch } from 'ui/kfetch';
import { ReindexStatus, UIReindexOption } from '../../../../../../common/types';
import { LoadingState } from '../../../../types';
import { ReindexFlyout } from './flyout';
import { ReindexPollingService, ReindexState } from './polling_service';
@ -104,7 +106,7 @@ export class ReindexButton extends React.Component<ReindexButtonProps, ReindexBu
indexName={indexName}
closeFlyout={this.closeFlyout}
reindexState={reindexState}
startReindex={this.service.startReindex}
startReindex={this.startReindex}
/>
)}
</Fragment>
@ -133,11 +135,30 @@ export class ReindexButton extends React.Component<ReindexButtonProps, ReindexBu
}
}
private startReindex = async () => {
if (!this.state.reindexState.status) {
// if status didn't exist we are starting a reindex action
this.sendUIReindexTelemetryInfo('start');
}
await this.service.startReindex();
};
private showFlyout = () => {
this.sendUIReindexTelemetryInfo('open');
this.setState({ flyoutVisible: true });
};
private closeFlyout = () => {
this.sendUIReindexTelemetryInfo('close');
this.setState({ flyoutVisible: false });
};
private async sendUIReindexTelemetryInfo(uiReindexAction: UIReindexOption) {
await kfetch({
pathname: '/api/upgrade_assistant/telemetry/ui_reindex',
method: 'PUT',
body: JSON.stringify(set({}, uiReindexAction, true)),
});
}
}

View file

@ -42,3 +42,8 @@ export enum GroupByOption {
index = 'index',
node = 'node',
}
export enum TelemetryState {
Running,
Complete,
}

View file

@ -5,10 +5,13 @@
*/
import { Legacy } from 'kibana';
import { UpgradeAssistantTelemetryServer } from '../common/types';
import { credentialStoreFactory } from './lib/reindexing/credential_store';
import { makeUpgradeAssistantUsageCollector } from './lib/telemetry';
import { registerClusterCheckupRoutes } from './routes/cluster_checkup';
import { registerDeprecationLoggingRoutes } from './routes/deprecation_logging';
import { registerReindexIndicesRoutes, registerReindexWorker } from './routes/reindex_indices';
import { registerTelemetryRoutes } from './routes/telemetry';
export function initServer(server: Legacy.Server) {
registerClusterCheckupRoutes(server);
@ -24,4 +27,8 @@ export function initServer(server: Legacy.Server) {
const worker = registerReindexWorker(server, credentialStore);
registerReindexIndicesRoutes(server, worker, credentialStore);
// Bootstrap the needed routes and the collector for the telemetry
registerTelemetryRoutes(server as UpgradeAssistantTelemetryServer);
makeUpgradeAssistantUsageCollector(server as UpgradeAssistantTelemetryServer);
}

View file

@ -0,0 +1,76 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { UPGRADE_ASSISTANT_DOC_ID, UPGRADE_ASSISTANT_TYPE } from '../../../common/types';
import { upsertUIOpenOption } from './es_ui_open_apis';
/**
* Since these route callbacks are so thin, these serve simply as integration tests
* to ensure they're wired up to the lib functions correctly. Business logic is tested
* more thoroughly in the lib/telemetry tests.
*/
describe('Upgrade Assistant Telemetry SavedObject UIOpen', () => {
const mockIncrementCounter = jest.fn();
const server = jest.fn().mockReturnValue({
usage: {
collectorSet: {
makeUsageCollector: {},
register: {},
},
},
savedObjects: {
getSavedObjectsRepository: jest.fn().mockImplementation(() => {
return {
incrementCounter: mockIncrementCounter,
};
}),
},
plugins: {
elasticsearch: {
getCluster: () => {
return {
callWithInternalUser: {},
};
},
},
},
});
const request = jest.fn().mockReturnValue({
payload: {
overview: true,
cluster: true,
indices: true,
},
});
describe('Upsert UIOpen Option', () => {
it('call saved objects internal repository with the correct info', async () => {
const serverMock = server();
const incCounterSORepoFunc = serverMock.savedObjects.getSavedObjectsRepository()
.incrementCounter;
await upsertUIOpenOption(serverMock, request());
expect(incCounterSORepoFunc).toHaveBeenCalledTimes(3);
expect(incCounterSORepoFunc).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
`ui_open.overview`
);
expect(incCounterSORepoFunc).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
`ui_open.cluster`
);
expect(incCounterSORepoFunc).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
`ui_open.indices`
);
});
});
});

View file

@ -0,0 +1,54 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { Legacy } from 'kibana';
import {
UIOpen,
UIOpenOption,
UPGRADE_ASSISTANT_DOC_ID,
UPGRADE_ASSISTANT_TYPE,
UpgradeAssistantTelemetryServer,
} from '../../../common/types';
async function incrementUIOpenOptionCounter(
server: UpgradeAssistantTelemetryServer,
uiOpenOptionCounter: UIOpenOption
) {
const { getSavedObjectsRepository } = server.savedObjects;
const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin');
const internalRepository = getSavedObjectsRepository(callWithInternalUser);
await internalRepository.incrementCounter(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
`ui_open.${uiOpenOptionCounter}`
);
}
export async function upsertUIOpenOption(
server: UpgradeAssistantTelemetryServer,
req: Legacy.Request
): Promise<UIOpen> {
const { overview, cluster, indices } = req.payload as UIOpen;
if (overview) {
await incrementUIOpenOptionCounter(server, 'overview');
}
if (cluster) {
await incrementUIOpenOptionCounter(server, 'cluster');
}
if (indices) {
await incrementUIOpenOptionCounter(server, 'indices');
}
return {
overview,
cluster,
indices,
};
}

View file

@ -0,0 +1,82 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { UPGRADE_ASSISTANT_DOC_ID, UPGRADE_ASSISTANT_TYPE } from '../../../common/types';
import { upsertUIReindexOption } from './es_ui_reindex_apis';
/**
* Since these route callbacks are so thin, these serve simply as integration tests
* to ensure they're wired up to the lib functions correctly. Business logic is tested
* more thoroughly in the lib/telemetry tests.
*/
describe('Upgrade Assistant Telemetry SavedObject UIReindex', () => {
const mockIncrementCounter = jest.fn();
const server = jest.fn().mockReturnValue({
usage: {
collectorSet: {
makeUsageCollector: {},
register: {},
},
},
savedObjects: {
getSavedObjectsRepository: jest.fn().mockImplementation(() => {
return {
incrementCounter: mockIncrementCounter,
};
}),
},
plugins: {
elasticsearch: {
getCluster: () => {
return {
callWithInternalUser: {},
};
},
},
},
});
const request = jest.fn().mockReturnValue({
payload: {
close: true,
open: true,
start: true,
stop: true,
},
});
describe('Upsert UIReindex Option', () => {
it('call saved objects internal repository with the correct info', async () => {
const serverMock = server();
const incCounterSORepoFunc = serverMock.savedObjects.getSavedObjectsRepository()
.incrementCounter;
await upsertUIReindexOption(serverMock, request());
expect(incCounterSORepoFunc).toHaveBeenCalledTimes(4);
expect(incCounterSORepoFunc).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
`ui_reindex.close`
);
expect(incCounterSORepoFunc).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
`ui_reindex.open`
);
expect(incCounterSORepoFunc).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
`ui_reindex.start`
);
expect(incCounterSORepoFunc).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
`ui_reindex.stop`
);
});
});
});

View file

@ -0,0 +1,59 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { Legacy } from 'kibana';
import {
UIReindex,
UIReindexOption,
UPGRADE_ASSISTANT_DOC_ID,
UPGRADE_ASSISTANT_TYPE,
UpgradeAssistantTelemetryServer,
} from '../../../common/types';
async function incrementUIReindexOptionCounter(
server: UpgradeAssistantTelemetryServer,
uiOpenOptionCounter: UIReindexOption
) {
const { getSavedObjectsRepository } = server.savedObjects;
const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin');
const internalRepository = getSavedObjectsRepository(callWithInternalUser);
await internalRepository.incrementCounter(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
`ui_reindex.${uiOpenOptionCounter}`
);
}
export async function upsertUIReindexOption(
server: UpgradeAssistantTelemetryServer,
req: Legacy.Request
): Promise<UIReindex> {
const { close, open, start, stop } = req.payload as UIReindex;
if (close) {
await incrementUIReindexOptionCounter(server, 'close');
}
if (open) {
await incrementUIReindexOptionCounter(server, 'open');
}
if (start) {
await incrementUIReindexOptionCounter(server, 'start');
}
if (stop) {
await incrementUIReindexOptionCounter(server, 'stop');
}
return {
close,
open,
start,
stop,
};
}

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { makeUpgradeAssistantUsageCollector } from './usage_collector';

View file

@ -0,0 +1,95 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import * as usageCollector from './usage_collector';
/**
* Since these route callbacks are so thin, these serve simply as integration tests
* to ensure they're wired up to the lib functions correctly. Business logic is tested
* more thoroughly in the lib/telemetry tests.
*/
describe('Upgrade Assistant Usage Collector', () => {
let makeUsageCollectorStub: any;
let registerStub: any;
let server: any;
let callClusterStub: any;
beforeEach(() => {
makeUsageCollectorStub = jest.fn();
registerStub = jest.fn();
server = jest.fn().mockReturnValue({
usage: {
collectorSet: { makeUsageCollector: makeUsageCollectorStub, register: registerStub },
register: {},
},
savedObjects: {
getSavedObjectsRepository: jest.fn().mockImplementation(() => {
return {
get: () => {
return {
attributes: {
'ui_open.overview': 10,
'ui_open.cluster': 20,
'ui_open.indices': 30,
'ui_reindex.close': 1,
'ui_reindex.open': 4,
'ui_reindex.start': 2,
'ui_reindex.stop': 1,
},
};
},
};
}),
},
});
callClusterStub = jest.fn().mockResolvedValue({
persistent: {},
transient: {
logger: {
deprecation: 'WARN',
},
},
});
});
describe('makeUpgradeAssistantUsageCollector', () => {
it('should call collectorSet.register', () => {
usageCollector.makeUpgradeAssistantUsageCollector(server());
expect(registerStub).toHaveBeenCalledTimes(1);
});
it('should call makeUsageCollector with type = upgrade-assistant', () => {
usageCollector.makeUpgradeAssistantUsageCollector(server());
expect(makeUsageCollectorStub).toHaveBeenCalledTimes(1);
expect(makeUsageCollectorStub.mock.calls[0][0].type).toBe('upgrade-assistant-telemetry');
});
it('fetchUpgradeAssistantMetrics should return correct info', async () => {
usageCollector.makeUpgradeAssistantUsageCollector(server());
const upgradeAssistantStats = await makeUsageCollectorStub.mock.calls[0][0].fetch(
callClusterStub
);
expect(upgradeAssistantStats).toEqual({
ui_open: {
overview: 10,
cluster: 20,
indices: 30,
},
ui_reindex: {
close: 1,
open: 4,
start: 2,
stop: 1,
},
features: {
deprecation_logging: {
enabled: true,
},
},
});
});
});
});

View file

@ -0,0 +1,107 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { set } from 'lodash';
import { SavedObjectsRepository } from 'src/server/saved_objects/service/lib/repository';
import {
UPGRADE_ASSISTANT_DOC_ID,
UPGRADE_ASSISTANT_TYPE,
UpgradeAssistantTelemetry,
UpgradeAssistantTelemetrySavedObject,
UpgradeAssistantTelemetrySavedObjectAttributes,
UpgradeAssistantTelemetryServer,
} from '../../../common/types';
import { isDeprecationLoggingEnabled } from '../es_deprecation_logging_apis';
async function getSavedObjectAttributesFromRepo(
savedObjectsRepository: SavedObjectsRepository,
docType: string,
docID: string
) {
try {
return (await savedObjectsRepository.get(docType, docID)).attributes;
} catch (e) {
return null;
}
}
async function getDeprecationLoggingStatusValue(callCluster: any): Promise<boolean> {
try {
const loggerDeprecationCallResult = await callCluster('cluster.getSettings', {
includeDefaults: true,
});
return isDeprecationLoggingEnabled(loggerDeprecationCallResult);
} catch (e) {
return false;
}
}
export async function fetchUpgradeAssistantMetrics(
callCluster: any,
server: UpgradeAssistantTelemetryServer
): Promise<UpgradeAssistantTelemetry> {
const { getSavedObjectsRepository } = server.savedObjects;
const savedObjectsRepository = getSavedObjectsRepository(callCluster);
const upgradeAssistantSOAttributes = await getSavedObjectAttributesFromRepo(
savedObjectsRepository,
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID
);
const deprecationLoggingStatusValue = await getDeprecationLoggingStatusValue(callCluster);
const getTelemetrySavedObject = (
upgradeAssistantTelemetrySavedObjectAttrs: UpgradeAssistantTelemetrySavedObjectAttributes | null
): UpgradeAssistantTelemetrySavedObject => {
const defaultTelemetrySavedObject = {
ui_open: {
overview: 0,
cluster: 0,
indices: 0,
},
ui_reindex: {
close: 0,
open: 0,
start: 0,
stop: 0,
},
};
if (!upgradeAssistantTelemetrySavedObjectAttrs) {
return defaultTelemetrySavedObject;
}
const upgradeAssistantTelemetrySOAttrsKeys = Object.keys(
upgradeAssistantTelemetrySavedObjectAttrs
);
const telemetryObj = defaultTelemetrySavedObject;
upgradeAssistantTelemetrySOAttrsKeys.forEach((key: string) => {
set(telemetryObj, key, upgradeAssistantTelemetrySavedObjectAttrs[key]);
});
return telemetryObj as UpgradeAssistantTelemetrySavedObject;
};
return {
...getTelemetrySavedObject(upgradeAssistantSOAttributes),
features: {
deprecation_logging: {
enabled: deprecationLoggingStatusValue,
},
},
};
}
export function makeUpgradeAssistantUsageCollector(server: UpgradeAssistantTelemetryServer) {
const kbnServer = server as UpgradeAssistantTelemetryServer;
const upgradeAssistantUsageCollector = kbnServer.usage.collectorSet.makeUsageCollector({
type: UPGRADE_ASSISTANT_TYPE,
fetch: async (callCluster: any) => fetchUpgradeAssistantMetrics(callCluster, server),
});
kbnServer.usage.collectorSet.register(upgradeAssistantUsageCollector);
}

View file

@ -0,0 +1,146 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
jest.mock('../lib/telemetry/es_ui_open_apis', () => ({
upsertUIOpenOption: jest.fn(),
}));
jest.mock('../lib/telemetry/es_ui_reindex_apis', () => ({
upsertUIReindexOption: jest.fn(),
}));
import { Server } from 'hapi';
import { upsertUIOpenOption } from '../lib/telemetry/es_ui_open_apis';
import { upsertUIReindexOption } from '../lib/telemetry/es_ui_reindex_apis';
import { registerTelemetryRoutes } from './telemetry';
/**
* Since these route callbacks are so thin, these serve simply as integration tests
* to ensure they're wired up to the lib functions correctly. Business logic is tested
* more thoroughly in the lib/telemetry tests.
*/
describe('Upgrade Assistant Telemetry API', () => {
const server = new Server();
registerTelemetryRoutes(server);
describe('PUT /api/upgrade_assistant/telemetry/ui_open', () => {
it('returns correct payload with single option', async () => {
const returnPayload = {
overview: true,
cluster: false,
indices: false,
};
upsertUIOpenOption.mockResolvedValue(returnPayload);
const resp = await server.inject({
method: 'PUT',
url: '/api/upgrade_assistant/telemetry/ui_open',
payload: {
overview: true,
},
});
expect(JSON.parse(resp.payload)).toEqual(returnPayload);
});
it('returns correct payload with multiple option', async () => {
const returnPayload = {
overview: true,
cluster: true,
indices: true,
};
upsertUIOpenOption.mockResolvedValue(returnPayload);
const resp = await server.inject({
method: 'PUT',
url: '/api/upgrade_assistant/telemetry/ui_open',
payload: {
overview: true,
cluster: true,
indices: true,
},
});
expect(JSON.parse(resp.payload)).toEqual(returnPayload);
});
it('returns an error if it throws', async () => {
upsertUIOpenOption.mockRejectedValue(new Error(`scary error!`));
const resp = await server.inject({
method: 'PUT',
url: '/api/upgrade_assistant/telemetry/ui_open',
payload: {
overview: false,
},
});
expect(resp.statusCode).toEqual(500);
});
});
describe('PUT /api/upgrade_assistant/telemetry/ui_reindex', () => {
it('returns correct payload with single option', async () => {
const returnPayload = {
close: false,
open: false,
start: true,
stop: false,
};
upsertUIReindexOption.mockResolvedValue(returnPayload);
const resp = await server.inject({
method: 'PUT',
url: '/api/upgrade_assistant/telemetry/ui_reindex',
payload: {
start: true,
},
});
expect(JSON.parse(resp.payload)).toEqual(returnPayload);
});
it('returns correct payload with multiple option', async () => {
const returnPayload = {
close: true,
open: true,
start: true,
stop: true,
};
upsertUIReindexOption.mockResolvedValue(returnPayload);
const resp = await server.inject({
method: 'PUT',
url: '/api/upgrade_assistant/telemetry/ui_reindex',
payload: {
close: true,
open: true,
start: true,
stop: true,
},
});
expect(JSON.parse(resp.payload)).toEqual(returnPayload);
});
it('returns an error if it throws', async () => {
upsertUIReindexOption.mockRejectedValue(new Error(`scary error!`));
const resp = await server.inject({
method: 'PUT',
url: '/api/upgrade_assistant/telemetry/ui_reindex',
payload: {
start: false,
},
});
expect(resp.statusCode).toEqual(500);
});
});
});

View file

@ -0,0 +1,56 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import Boom from 'boom';
import Joi from 'joi';
import { UpgradeAssistantTelemetryServer } from '../../common/types';
import { upsertUIOpenOption } from '../lib/telemetry/es_ui_open_apis';
import { upsertUIReindexOption } from '../lib/telemetry/es_ui_reindex_apis';
export function registerTelemetryRoutes(server: UpgradeAssistantTelemetryServer) {
server.route({
path: '/api/upgrade_assistant/telemetry/ui_open',
method: 'PUT',
options: {
validate: {
payload: Joi.object({
overview: Joi.boolean().default(false),
cluster: Joi.boolean().default(false),
indices: Joi.boolean().default(false),
}),
},
},
async handler(request) {
try {
return await upsertUIOpenOption(server, request);
} catch (e) {
return Boom.boomify(e, { statusCode: 500 });
}
},
});
server.route({
path: '/api/upgrade_assistant/telemetry/ui_reindex',
method: 'PUT',
options: {
validate: {
payload: Joi.object({
close: Joi.boolean().default(false),
open: Joi.boolean().default(false),
start: Joi.boolean().default(false),
stop: Joi.boolean().default(false),
}),
},
},
async handler(request) {
try {
return await upsertUIReindexOption(server, request);
} catch (e) {
return Boom.boomify(e, { statusCode: 500 });
}
},
});
}

View file

@ -24,15 +24,15 @@ function flatKeys(source) {
const disableCollection = {
'persistent':
{
xpack: {
monitoring: {
collection: {
enabled: false
{
xpack: {
monitoring: {
collection: {
enabled: false
}
}
}
}
}
};
export default function ({ getService }) {
@ -174,4 +174,3 @@ export default function ({ getService }) {
});
}

View file

@ -16,7 +16,10 @@ export default function upgradeAssistantFunctionalTests({
describe('Upgrade Checkup', () => {
before(async () => await esArchiver.load('empty_kibana'));
after(async () => await esArchiver.unload('empty_kibana'));
after(async () => {
await PageObjects.upgradeAssistant.expectTelemetryHasFinish();
await esArchiver.unload('empty_kibana');
});
it('allows user to navigate to upgrade checkup', async () => {
await PageObjects.upgradeAssistant.navigateToPage();

View file

@ -67,6 +67,14 @@ export function UpgradeAssistantProvider({ getService, getPageObjects }) {
expect(summaryElText).to.eql(summary);
});
}
async expectTelemetryHasFinish() {
return await retry.try(async () => {
log.debug('expectTelemetryHasFinish');
const isTelemetryFinished = !(await testSubjects.exists('upgradeAssistantTelemetryRunning'));
expect(isTelemetryFinished).to.equal(true);
});
}
}
return new UpgradeAssistant();