Skip to content

Commit 316baef

Browse files
matejkubinecidoqo
andauthored
PMM-14348 Add toggle for PMM Server QAN (#849)
* PMM-14348 Add toggle for PMM Server QAN * PMM-14348 Add unit tests * PMM-14348 Run lint * PMM-14348 Fix unit tests * PMM-14348 Remove unused import * PMM-14348 Refactor UpdateAgentBody * PMM-14248 Update type for agent item in update * PMM-14248 Update wording for QAN toggle * PMM-14348 Refactor to use settings property * PMM-14348 Revert unnecessary changes * PMM-14348 Remove fetching of services * PMM-14348 Add in missing settings stub --------- Co-authored-by: Michael Okoko <[email protected]>
1 parent 98d5fd4 commit 316baef

File tree

11 files changed

+225
-57
lines changed

11 files changed

+225
-57
lines changed

public/app/percona/inventory/Inventory.service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
NodeListDBPayload,
1212
RemoveNodeBody,
1313
ServiceAgentListPayload,
14+
UpdateAgentBody,
1415
} from './Inventory.types';
1516

1617
const BASE_URL = `/v1/inventory`;
@@ -25,6 +26,9 @@ export const InventoryService = {
2526
},
2627
});
2728
},
29+
updateAgent(agentId: string, payload: UpdateAgentBody, token?: CancelToken) {
30+
return api.put(`${BASE_URL}/agents/${agentId}`, payload, false, token);
31+
},
2832
removeAgent(agentId: string, forceMode = false, token?: CancelToken) {
2933
// todo: address forceMode
3034
return api.delete<void>(`${BASE_URL}/agents/${agentId}`, false, token, { force: forceMode });

public/app/percona/inventory/Inventory.types.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { MetricsResolutions } from '../settings/Settings.types';
12
import { Databases } from '../shared/core';
23
import { DbNode, NodeType } from '../shared/services/nodes/Nodes.types';
34
import {
@@ -188,3 +189,30 @@ export interface AgentsOption {
188189
value: string;
189190
label: string;
190191
}
192+
193+
export interface UpdateAgentItem {
194+
enable?: boolean;
195+
custom_labels?: Record<string, string>;
196+
enable_push_metrics?: boolean;
197+
metrics_resolutions?: MetricsResolutions;
198+
}
199+
200+
export interface UpdateAgentBody {
201+
node_exporter?: UpdateAgentItem;
202+
mysqld_exporter?: UpdateAgentItem;
203+
mongodb_exporter?: UpdateAgentItem;
204+
postgres_exporter?: UpdateAgentItem;
205+
proxysql_exporter?: UpdateAgentItem;
206+
external_exporter?: UpdateAgentItem;
207+
rds_exporter?: UpdateAgentItem;
208+
azure_database_exporter?: UpdateAgentItem;
209+
qan_mysql_perfschema_agent?: UpdateAgentItem;
210+
qan_mysql_slowlog_agent?: UpdateAgentItem;
211+
qan_mongodb_profiler_agent?: UpdateAgentItem;
212+
qan_mongodb_mongolog_agent?: UpdateAgentItem;
213+
qan_postgresql_pgstatements_agent?: UpdateAgentItem;
214+
qan_postgresql_pgstatmonitor_agent?: UpdateAgentItem;
215+
nomad_agent?: {
216+
enable?: boolean;
217+
};
218+
}

public/app/percona/settings/Settings.messages.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ export const Messages = {
3838
backupLabel: 'Backup Management',
3939
backupTooltip: 'Option to enable/disable Backup Management features.',
4040
backupLink: `https://per.co.na/backup_management`,
41+
enableInternalPgQanLabel: 'QAN for PMM Server',
42+
enableInternalPgQanTooltip:
43+
"Displays queries from PMM Server's internal PostgreSQL database in Query Analytics (QAN). Enable to troubleshoot PMM Server's database performance alongside your monitored instances.",
44+
enableInternalPgQanLink: 'https://per.co.na/qan-pmm-server',
4145
technicalPreviewLegend: 'Technical preview features',
4246
technicalPreviewDescription:
4347
'These are technical preview features, not recommended to be used in production environments. Read more\n' +

public/app/percona/settings/Settings.service.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ const toModel = (response: SettingsPayload): Settings => ({
7878
isConnectedToPortal: response.connected_to_platform,
7979
defaultRoleId: response.default_role_id,
8080
enableAccessControl: response.enable_access_control,
81+
enableInternalPgQan: response.enable_internal_pg_qan,
8182
});
8283

8384
const toReadonlyModel = (response: ReadonlySettingsPayload): Settings => ({
@@ -116,4 +117,5 @@ const toReadonlyModel = (response: ReadonlySettingsPayload): Settings => ({
116117
isConnectedToPortal: false,
117118
defaultRoleId: -1,
118119
enableAccessControl: response.enable_access_control,
120+
enableInternalPgQan: false,
119121
});

public/app/percona/settings/Settings.types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export interface AdvancedChangePayload extends AdvancedPayload {
6363
enable_azurediscover?: boolean;
6464
enable_updates?: boolean;
6565
enable_access_control?: boolean;
66+
enable_internal_pg_qan?: boolean;
6667
}
6768

6869
export interface MetricsResolutionsPayload {
@@ -122,6 +123,7 @@ export interface SettingsPayload
122123
telemetry_summaries: string[];
123124
default_role_id: number;
124125
enable_access_control: boolean;
126+
enable_internal_pg_qan: boolean;
125127
}
126128

127129
export interface SettingsPayload
@@ -180,6 +182,7 @@ export interface Settings extends ReadonlySettings {
180182
isConnectedToPortal?: boolean;
181183
telemetrySummaries: string[];
182184
defaultRoleId: number;
185+
enableInternalPgQan: boolean;
183186
}
184187

185188
export interface MetricsResolutions {

public/app/percona/settings/__mocks__/Settings.service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export const stub: Settings = {
3737
frequentInterval: '10s',
3838
},
3939
defaultRoleId: 1,
40+
enableInternalPgQan: false,
4041
};
4142

4243
SettingsService.getSettings = () => Promise.resolve(stub);

public/app/percona/settings/components/Advanced/Advanced.test.tsx

Lines changed: 120 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { render, screen, fireEvent, waitForElementToBeRemoved } from '@testing-library/react';
1+
import { render, screen, fireEvent, waitForElementToBeRemoved, waitFor } from '@testing-library/react';
22
import { Provider } from 'react-redux';
33

44
import * as reducers from 'app/percona/shared/core/reducers';
@@ -10,8 +10,48 @@ import { Advanced } from './Advanced';
1010

1111
jest.mock('app/percona/settings/Settings.service');
1212

13+
const updateSettingsSpy = jest.spyOn(reducers, 'updateSettingsAction');
14+
15+
const setup = (pmmMonitoringEnabled = true) =>
16+
render(
17+
<Provider
18+
store={configureStore({
19+
percona: {
20+
user: { isAuthorized: true },
21+
settings: {
22+
loading: false,
23+
result: {
24+
advisorRunIntervals: {
25+
rareInterval: '280800s',
26+
standardInterval: '86400s',
27+
frequentInterval: '14400s',
28+
},
29+
dataRetention: '2592000s',
30+
telemetryEnabled: true,
31+
telemetrySummaries: ['summary1', 'summary2'],
32+
updatesEnabled: false,
33+
backupEnabled: false,
34+
advisorEnabled: true,
35+
azureDiscoverEnabled: true,
36+
publicAddress: 'localhost',
37+
alertingEnabled: true,
38+
enableInternalPgQan: pmmMonitoringEnabled,
39+
},
40+
},
41+
},
42+
navIndex: {},
43+
} as StoreState)}
44+
>
45+
{wrapWithGrafanaContextMock(<Advanced />)}
46+
</Provider>
47+
);
48+
1349
describe('Advanced::', () => {
14-
it('Renders correctly with props', () => {
50+
beforeEach(() => {
51+
updateSettingsSpy.mockClear();
52+
});
53+
54+
it('renders correctly with props', async () => {
1555
render(
1656
<Provider
1757
store={configureStore({
@@ -37,18 +77,17 @@ describe('Advanced::', () => {
3777
},
3878
},
3979
},
40-
} as StoreState)}
80+
} as unknown as StoreState)}
4181
>
4282
{wrapWithGrafanaContextMock(<Advanced />)}
4383
</Provider>
4484
);
4585

46-
expect(screen.getByTestId('retention-number-input')).toHaveValue(30);
47-
expect(screen.getByTestId('publicAddress-text-input')).toHaveValue('localhost');
86+
await waitFor(() => expect(screen.getByTestId('retention-number-input')).toHaveValue(30));
87+
await waitFor(() => expect(screen.getByTestId('publicAddress-text-input')).toHaveValue('localhost'));
4888
});
4989

50-
it('Calls apply changes', async () => {
51-
const spy = jest.spyOn(reducers, 'updateSettingsAction');
90+
it('calls apply changes', async () => {
5291
render(
5392
<Provider
5493
store={configureStore({
@@ -79,14 +118,16 @@ describe('Advanced::', () => {
79118
{wrapWithGrafanaContextMock(<Advanced />)}
80119
</Provider>
81120
);
121+
82122
fireEvent.change(screen.getByTestId('retention-number-input'), { target: { value: 70 } });
83123
fireEvent.submit(screen.getByTestId('advanced-button'));
124+
84125
await waitForElementToBeRemoved(() => screen.getByTestId('Spinner'));
85126

86-
expect(spy).toHaveBeenCalled();
127+
expect(updateSettingsSpy).toHaveBeenCalled();
87128
});
88129

89-
it('Sets correct URL from browser', async () => {
130+
it('sets correct URL from browser', async () => {
90131
const location = {
91132
...window.location,
92133
host: 'pmmtest.percona.com',
@@ -129,12 +170,11 @@ describe('Advanced::', () => {
129170
);
130171

131172
fireEvent.click(screen.getByTestId('public-address-button'));
132-
expect(screen.getByTestId('publicAddress-text-input')).toHaveValue('pmmtest.percona.com');
133-
});
134173

135-
it('Does not include STT check intervals in the change request if STT checks are disabled', async () => {
136-
const spy = jest.spyOn(reducers, 'updateSettingsAction');
174+
await waitFor(() => expect(screen.getByTestId('publicAddress-text-input')).toHaveValue('pmmtest.percona.com'));
175+
});
137176

177+
it('does not include STT check intervals in the change request if STT checks are disabled', async () => {
138178
render(
139179
<Provider
140180
store={configureStore({
@@ -168,10 +208,10 @@ describe('Advanced::', () => {
168208

169209
fireEvent.change(screen.getByTestId('retention-number-input'), { target: { value: 70 } });
170210
fireEvent.submit(screen.getByTestId('advanced-button'));
211+
171212
await waitForElementToBeRemoved(() => screen.getByTestId('Spinner'));
172213

173-
// expect(spy.calls.mostRecent().args[0].body.stt_check_intervals).toBeUndefined();
174-
expect(spy).toHaveBeenLastCalledWith(
214+
expect(updateSettingsSpy).toHaveBeenLastCalledWith(
175215
expect.objectContaining({
176216
body: expect.objectContaining({
177217
advisor_run_intervals: undefined,
@@ -181,8 +221,6 @@ describe('Advanced::', () => {
181221
});
182222

183223
it('Includes STT check intervals in the change request if STT checks are enabled', async () => {
184-
const spy = jest.spyOn(reducers, 'updateSettingsAction');
185-
186224
render(
187225
<Provider
188226
store={configureStore({
@@ -220,7 +258,7 @@ describe('Advanced::', () => {
220258
await waitForElementToBeRemoved(() => screen.getByTestId('Spinner'));
221259

222260
// expect(spy.calls.mostRecent().args[0].body.stt_check_intervals).toBeDefined();
223-
expect(spy).toHaveBeenLastCalledWith(
261+
expect(updateSettingsSpy).toHaveBeenLastCalledWith(
224262
expect.objectContaining({
225263
body: expect.objectContaining({
226264
advisor_run_intervals: {
@@ -232,4 +270,68 @@ describe('Advanced::', () => {
232270
})
233271
);
234272
});
273+
274+
it('updates internal monitoring when pmm server monitoring is turned on', async () => {
275+
const { container } = setup();
276+
277+
const monitoringSwitch = container.querySelector(
278+
'[data-testid="enable-internal-pg-qan"] [name="enableInternalPgQan"]'
279+
);
280+
281+
expect(monitoringSwitch).toBeInTheDocument();
282+
283+
fireEvent.click(monitoringSwitch!);
284+
285+
fireEvent.submit(screen.getByTestId('advanced-button'));
286+
287+
await waitForElementToBeRemoved(() => screen.getByTestId('Spinner'));
288+
289+
expect(updateSettingsSpy).toHaveBeenCalledWith(
290+
expect.objectContaining({
291+
body: expect.objectContaining({
292+
enable_internal_pg_qan: false,
293+
}),
294+
})
295+
);
296+
});
297+
298+
it('updates internal monitoring when pmm server monitoring is turned off', async () => {
299+
const { container } = setup(false);
300+
301+
const monitoringSwitch = container.querySelector(
302+
'[data-testid="enable-internal-pg-qan"] [name="enableInternalPgQan"]'
303+
);
304+
305+
expect(monitoringSwitch).toBeInTheDocument();
306+
307+
fireEvent.click(monitoringSwitch!);
308+
309+
fireEvent.submit(screen.getByTestId('advanced-button'));
310+
311+
await waitForElementToBeRemoved(() => screen.getByTestId('Spinner'));
312+
313+
expect(updateSettingsSpy).toHaveBeenCalledWith(
314+
expect.objectContaining({
315+
body: expect.objectContaining({
316+
enable_internal_pg_qan: true,
317+
}),
318+
})
319+
);
320+
});
321+
322+
it("doesn't update internal monitoring when pmm server monitoring doesn't change", async () => {
323+
setup();
324+
325+
fireEvent.submit(screen.getByTestId('advanced-button'));
326+
327+
await waitForElementToBeRemoved(() => screen.getByTestId('Spinner'));
328+
329+
expect(updateSettingsSpy).toHaveBeenCalledWith(
330+
expect.objectContaining({
331+
body: expect.objectContaining({
332+
enable_internal_pg_qan: true,
333+
}),
334+
})
335+
);
336+
});
235337
});

0 commit comments

Comments
 (0)