Skip to content

Commit

Permalink
PMM-12840 PT Summary fix and migration (#704)
Browse files Browse the repository at this point in the history
* PMM-12840 Move PT Summary to main repo

* PMM-12840 Fix code style issues
  • Loading branch information
matejkubinec authored Feb 20, 2024
1 parent de169a3 commit f4fa5e7
Show file tree
Hide file tree
Showing 22 changed files with 495 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@
"diff": "^5.1.0",
"emotion": "11.0.0",
"eventemitter3": "5.0.1",
"exponential-backoff": "^3.1.1",
"fast-deep-equal": "^3.1.3",
"fast-json-patch": "3.1.1",
"file-saver": "2.0.5",
Expand Down
2 changes: 2 additions & 0 deletions pkg/plugins/pfs/corelist/corelist_load_gen.go

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

9 changes: 9 additions & 0 deletions public/app/features/plugins/built_in_plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ const pyroscopePlugin = async () =>
await import(/* webpackChunkName: "pyroscopePlugin" */ 'app/plugins/datasource/grafana-pyroscope-datasource/module');
const parcaPlugin = async () =>
await import(/* webpackChunkName: "parcaPlugin" */ 'app/plugins/datasource/parca/module');
// @PERCONA
const pmmPTSummaryDatasource = async () =>
await import(
/* webpackChunkName: "pmmPTSummaryDatasource" */ 'app/plugins/datasource/pmm-pt-summary-datasource/module'
);

import * as alertGroupsPanel from 'app/plugins/panel/alertGroups/module';
import * as alertListPanel from 'app/plugins/panel/alertlist/module';
Expand All @@ -62,6 +67,7 @@ import * as nodeGraph from 'app/plugins/panel/nodeGraph/module';
import * as pieChartPanel from 'app/plugins/panel/piechart/module';
// @PERCONA
import * as pmmCheckPanel from 'app/plugins/panel/pmm-check/module';
import * as pmmPTSummaryPanel from 'app/plugins/panel/pmm-pt-summary-panel/module';
import * as pmmUpdatePanel from 'app/plugins/panel/pmm-update/module';
import * as statPanel from 'app/plugins/panel/stat/module';
import * as stateTimelinePanel from 'app/plugins/panel/state-timeline/module';
Expand Down Expand Up @@ -107,6 +113,8 @@ const builtInPlugins: Record<string, System.Module | (() => Promise<System.Modul
'core:plugin/alertmanager': alertmanagerPlugin,
'core:plugin/grafana-pyroscope-datasource': pyroscopePlugin,
'core:plugin/parca': parcaPlugin,
// @PERCONA
'core:plugin/pmm-pt-summary-datasource': pmmPTSummaryDatasource,
// panels
'core:plugin/text': textPanel,
'core:plugin/timeseries': timeseriesPanel,
Expand Down Expand Up @@ -144,6 +152,7 @@ const builtInPlugins: Record<string, System.Module | (() => Promise<System.Modul
// @PERCONA
'core:plugin/pmm-check': pmmCheckPanel,
'core:plugin/pmm-update': pmmUpdatePanel,
'core:plugin/pmm-pt-summary-panel': pmmPTSummaryPanel,
};

export default builtInPlugins;
9 changes: 9 additions & 0 deletions public/app/percona/shared/services/actions/Actions.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { apiManagement } from '../../helpers/api';

import { ActionRequest, ActionResponse } from './Actions.types';

export const ActionsService = {
getActionResult<T>(body: ActionRequest) {
return apiManagement.post<ActionResponse<T>, ActionRequest>('/Actions/Get', body);
},
};
15 changes: 15 additions & 0 deletions public/app/percona/shared/services/actions/Actions.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface ActionResult<T = object> {
error: string;
loading: boolean;
value: T | null;
}

export interface ActionRequest {
action_id: string;
}

export interface ActionResponse<T = object> {
done: boolean;
error: string;
output: T;
}
50 changes: 50 additions & 0 deletions public/app/percona/shared/services/actions/Actions.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { backOff } from 'exponential-backoff';

import { ActionsService } from './Actions.service';
import { ActionResult } from './Actions.types';

const INTERVAL = 500;

export const getActionResult = async <T>(actionId: string): Promise<ActionResult<T>> => {
const getData = async () => {
const result = await ActionsService.getActionResult<T>({
action_id: actionId,
});

if (!result.done && !result.error) {
throw Error('');
}

return result;
};

let requestResult;

try {
requestResult = await backOff(() => getData(), {
startingDelay: INTERVAL,
numOfAttempts: 10,
delayFirstAttempt: false,
});
} catch (e) {
return {
loading: false,
value: null,
error: 'Unknown error',
};
}

if (requestResult.error) {
return {
loading: false,
value: requestResult.output,
error: requestResult.error,
};
}

return {
loading: false,
value: requestResult.output,
error: '',
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { getTemplateSrv } from '@grafana/runtime';
import { apiManagement } from 'app/percona/shared/helpers/api';

import { PTSummaryRequest, PTSummaryResponse, DatabaseSummaryRequest } from './PTSummary.types';

export const PTSummaryService = {
async getPTSummary(variableName: string) {
const body: PTSummaryRequest = { node_id: getTemplateSrv().replace(`$${variableName || 'node_id'}`) };

return apiManagement.post<PTSummaryResponse, any>('/Actions/StartPTSummary', body, true);
},
async getMysqlPTSummary(variableName: string) {
const body: DatabaseSummaryRequest = {
service_id: getTemplateSrv().replace(`$${variableName || 'service_name'}`),
};

return apiManagement.post<PTSummaryResponse, any>('/Actions/StartPTMySQLSummary', body, true);
},
async getPostgresqlPTSummary(variableName: string) {
const body: DatabaseSummaryRequest = {
service_id: getTemplateSrv().replace(`$${variableName || 'service_name'}`),
};

return apiManagement.post<PTSummaryResponse, any>('/Actions/StartPTPgSummary', body, true);
},
async getMongodbPTSummary(variableName: string) {
const body: DatabaseSummaryRequest = {
service_id: getTemplateSrv().replace(`$${variableName || 'service-name'}`),
};

return apiManagement.post<PTSummaryResponse, any>('/Actions/StartPTMongoDBSummary', body, true);
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export interface PTSummaryRequest {
node_id: string;
}

export interface DatabaseSummaryRequest {
service_id: string;
}

export interface PTSummaryResponse {
action_id: string;
pmm_agent_id: string;
}

export enum DatasourceType {
postgresql = 'postgresql',
mongodb = 'mongodb',
mysql = 'mysql',
node = 'node',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { FieldType, toDataFrame } from '@grafana/data';
import { ActionResult } from 'app/percona/shared/services/actions/Actions.types';

import { PTSummaryService } from './PTSummary.service';
import { PTSummaryDataSource } from './PTSummaryDataSource';

jest.mock('@grafana/runtime', () => ({
getTemplateSrv: () => ({
replace: () => 'node',
}),
}));
jest.mock('app/percona/shared/services/actions/Actions.utils', () => ({
getActionResult: async (): Promise<ActionResult<string>> =>
new Promise((resolve) => {
resolve({
loading: false,
value: 'Test data',
error: '',
});
}),
}));

describe('PTSummaryDatasource::', () => {
it('Returns correct data for Node summary', async () => {
const expected = {
data: [
toDataFrame({
fields: [{ name: 'summary', values: ['Test data'], type: FieldType.string }],
}),
],
};
const instance = new PTSummaryDataSource({});

PTSummaryService.getPTSummary = jest.fn().mockResolvedValueOnce({ value: 'Test data' });

const result = await instance.query({
targets: [{ refId: 'A', queryType: { type: 'node', variableName: undefined } }],
} as any);

expect(result).toEqual(expected);
});

it('Returns correct data for MySQL summary', async () => {
const expected = {
data: [
toDataFrame({
fields: [{ name: 'summary', values: ['Test data'], type: FieldType.string }],
}),
],
};
const instance = new PTSummaryDataSource({});

PTSummaryService.getMysqlPTSummary = jest.fn().mockResolvedValueOnce({ value: 'Test data' });

const result = await instance.query({
targets: [{ refId: 'A', queryType: { type: 'mysql', variableName: undefined } }],
} as any);

expect(result).toEqual(expected);
});

it('Returns correct data for MongoDB summary', async () => {
const expected = {
data: [
toDataFrame({
fields: [{ name: 'summary', values: ['Test data'], type: FieldType.string }],
}),
],
};
const instance = new PTSummaryDataSource({});

PTSummaryService.getMongodbPTSummary = jest.fn().mockResolvedValueOnce({ value: 'Test data' });

const result = await instance.query({
targets: [{ refId: 'A', queryType: { type: 'mongodb', variableName: undefined } }],
} as any);

expect(result).toEqual(expected);
});

it('Returns correct data for PostgreSQL summary', async () => {
const expected = {
data: [
toDataFrame({
fields: [{ name: 'summary', values: ['Test data'], type: FieldType.string }],
}),
],
};
const instance = new PTSummaryDataSource({});

PTSummaryService.getPostgresqlPTSummary = jest.fn().mockResolvedValueOnce({ value: 'Test data' });

const result = await instance.query({
targets: [{ refId: 'A', queryType: { type: 'postgresql', variableName: undefined } }],
} as any);

expect(result).toEqual(expected);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { DataQueryResponse, DataSourceApi, toDataFrame, FieldType } from '@grafana/data';
import { getActionResult } from 'app/percona/shared/services/actions/Actions.utils';

import { PTSummaryService } from './PTSummary.service';
import { DatasourceType, PTSummaryResponse } from './PTSummary.types';

export class PTSummaryDataSource extends DataSourceApi {
constructor(instanceSettings: any) {
super(instanceSettings);
}

async query(options: any): Promise<DataQueryResponse> {
const { variableName, type } = options.targets[0]?.queryType || {};

const getRequest = (type: DatasourceType) => {
switch (type) {
case DatasourceType.node:
return PTSummaryService.getPTSummary(variableName);
case DatasourceType.mysql:
return PTSummaryService.getMysqlPTSummary(variableName);
case DatasourceType.mongodb:
return PTSummaryService.getMongodbPTSummary(variableName);
case DatasourceType.postgresql:
return PTSummaryService.getPostgresqlPTSummary(variableName);
default:
return PTSummaryService.getPTSummary(variableName);
}
};

return getRequest(type)
.then(async (response) => {
const result = await getActionResult<string>((response as PTSummaryResponse).action_id);

return this.newDataFrame(result.value ? result.value : result.error);
})
.catch((error) => this.newDataFrame(error.response.data.message));
}

async testDatasource() {
return {
status: 'success',
message: 'Success',
};
}

newDataFrame(value: string) {
return {
data: [
toDataFrame({
fields: [
{
name: 'summary',
values: [value],
type: FieldType.string,
},
],
}),
],
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { DatasourceType } from '../PTSummary.types';

import { Messages } from './QueryEditor.messages';

export const DATASOURCES = [
{
value: DatasourceType.node,
label: Messages.labels.nodePTSummary,
},
{
value: DatasourceType.mysql,
label: Messages.labels.mysqlPTSummary,
},
{
value: DatasourceType.postgresql,
label: Messages.labels.postgresqlPTSummary,
},
{
value: DatasourceType.mongodb,
label: Messages.labels.mongodbPTSummary,
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const Messages = {
labels: {
postgresqlPTSummary: 'PostgreSQL PT Summary',
mongodbPTSummary: 'MongoDB PT Summary',
mysqlPTSummary: 'MySQL PT Summary',
nodePTSummary: 'Node PT Summary',
field: {
datasourceType: 'Datasource type',
variableName: 'Grafana variable name',
},
},
placeholders: {
variableName: 'Variable name',
},
};
Loading

0 comments on commit f4fa5e7

Please sign in to comment.