Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions public/app/percona/inventory/Inventory.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
NodeListDBPayload,
RemoveNodeBody,
ServiceAgentListPayload,
UpdateAgentBody,
} from './Inventory.types';

const BASE_URL = `/v1/inventory`;
Expand All @@ -25,6 +26,9 @@ export const InventoryService = {
},
});
},
updateAgent(agentId: string, payload: UpdateAgentBody, token?: CancelToken) {
return api.put(`${BASE_URL}/agents/${agentId}`, payload, false, token);
},
removeAgent(agentId: string, forceMode = false, token?: CancelToken) {
// todo: address forceMode
return api.delete<void>(`${BASE_URL}/agents/${agentId}`, false, token, { force: forceMode });
Expand Down
28 changes: 28 additions & 0 deletions public/app/percona/inventory/Inventory.types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { MetricsResolutions } from '../settings/Settings.types';
import { Databases } from '../shared/core';
import { DbNode, NodeType } from '../shared/services/nodes/Nodes.types';
import {
Expand Down Expand Up @@ -188,3 +189,30 @@ export interface AgentsOption {
value: string;
label: string;
}

export interface UpdateAgentItem {
enable?: boolean;
custom_labels?: Record<string, string>;
enable_push_metrics?: boolean;
metrics_resolutions?: MetricsResolutions;
}

export interface UpdateAgentBody {
node_exporter?: UpdateAgentItem;
mysqld_exporter?: UpdateAgentItem;
mongodb_exporter?: UpdateAgentItem;
postgres_exporter?: UpdateAgentItem;
proxysql_exporter?: UpdateAgentItem;
external_exporter?: UpdateAgentItem;
rds_exporter?: UpdateAgentItem;
azure_database_exporter?: UpdateAgentItem;
qan_mysql_perfschema_agent?: UpdateAgentItem;
qan_mysql_slowlog_agent?: UpdateAgentItem;
qan_mongodb_profiler_agent?: UpdateAgentItem;
qan_mongodb_mongolog_agent?: UpdateAgentItem;
qan_postgresql_pgstatements_agent?: UpdateAgentItem;
qan_postgresql_pgstatmonitor_agent?: UpdateAgentItem;
nomad_agent?: {
enable?: boolean;
};
}
4 changes: 4 additions & 0 deletions public/app/percona/settings/Settings.messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export const Messages = {
backupLabel: 'Backup Management',
backupTooltip: 'Option to enable/disable Backup Management features.',
backupLink: `https://per.co.na/backup_management`,
enableInternalPgQanLabel: 'QAN for PMM Server',
enableInternalPgQanTooltip:
"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.",
enableInternalPgQanLink: 'https://per.co.na/qan-pmm-server',
technicalPreviewLegend: 'Technical preview features',
technicalPreviewDescription:
'These are technical preview features, not recommended to be used in production environments. Read more\n' +
Expand Down
2 changes: 2 additions & 0 deletions public/app/percona/settings/Settings.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const toModel = (response: SettingsPayload): Settings => ({
isConnectedToPortal: response.connected_to_platform,
defaultRoleId: response.default_role_id,
enableAccessControl: response.enable_access_control,
enableInternalPgQan: response.enable_internal_pg_qan,
});

const toReadonlyModel = (response: ReadonlySettingsPayload): Settings => ({
Expand Down Expand Up @@ -116,4 +117,5 @@ const toReadonlyModel = (response: ReadonlySettingsPayload): Settings => ({
isConnectedToPortal: false,
defaultRoleId: -1,
enableAccessControl: response.enable_access_control,
enableInternalPgQan: false,
});
3 changes: 3 additions & 0 deletions public/app/percona/settings/Settings.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export interface AdvancedChangePayload extends AdvancedPayload {
enable_azurediscover?: boolean;
enable_updates?: boolean;
enable_access_control?: boolean;
enable_internal_pg_qan?: boolean;
}

export interface MetricsResolutionsPayload {
Expand Down Expand Up @@ -122,6 +123,7 @@ export interface SettingsPayload
telemetry_summaries: string[];
default_role_id: number;
enable_access_control: boolean;
enable_internal_pg_qan: boolean;
}

export interface SettingsPayload
Expand Down Expand Up @@ -180,6 +182,7 @@ export interface Settings extends ReadonlySettings {
isConnectedToPortal?: boolean;
telemetrySummaries: string[];
defaultRoleId: number;
enableInternalPgQan: boolean;
}

export interface MetricsResolutions {
Expand Down
1 change: 1 addition & 0 deletions public/app/percona/settings/__mocks__/Settings.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const stub: Settings = {
frequentInterval: '10s',
},
defaultRoleId: 1,
enableInternalPgQan: false,
};

SettingsService.getSettings = () => Promise.resolve(stub);
Expand Down
138 changes: 120 additions & 18 deletions public/app/percona/settings/components/Advanced/Advanced.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render, screen, fireEvent, waitForElementToBeRemoved } from '@testing-library/react';
import { render, screen, fireEvent, waitForElementToBeRemoved, waitFor } from '@testing-library/react';
import { Provider } from 'react-redux';

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

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

const updateSettingsSpy = jest.spyOn(reducers, 'updateSettingsAction');

const setup = (pmmMonitoringEnabled = true) =>
render(
<Provider
store={configureStore({
percona: {
user: { isAuthorized: true },
settings: {
loading: false,
result: {
advisorRunIntervals: {
rareInterval: '280800s',
standardInterval: '86400s',
frequentInterval: '14400s',
},
dataRetention: '2592000s',
telemetryEnabled: true,
telemetrySummaries: ['summary1', 'summary2'],
updatesEnabled: false,
backupEnabled: false,
advisorEnabled: true,
azureDiscoverEnabled: true,
publicAddress: 'localhost',
alertingEnabled: true,
enableInternalPgQan: pmmMonitoringEnabled,
},
},
},
navIndex: {},
} as StoreState)}
>
{wrapWithGrafanaContextMock(<Advanced />)}
</Provider>
);

describe('Advanced::', () => {
it('Renders correctly with props', () => {
beforeEach(() => {
updateSettingsSpy.mockClear();
});

it('renders correctly with props', async () => {
render(
<Provider
store={configureStore({
Expand All @@ -37,18 +77,17 @@ describe('Advanced::', () => {
},
},
},
} as StoreState)}
} as unknown as StoreState)}
>
{wrapWithGrafanaContextMock(<Advanced />)}
</Provider>
);

expect(screen.getByTestId('retention-number-input')).toHaveValue(30);
expect(screen.getByTestId('publicAddress-text-input')).toHaveValue('localhost');
await waitFor(() => expect(screen.getByTestId('retention-number-input')).toHaveValue(30));
await waitFor(() => expect(screen.getByTestId('publicAddress-text-input')).toHaveValue('localhost'));
});

it('Calls apply changes', async () => {
const spy = jest.spyOn(reducers, 'updateSettingsAction');
it('calls apply changes', async () => {
render(
<Provider
store={configureStore({
Expand Down Expand Up @@ -79,14 +118,16 @@ describe('Advanced::', () => {
{wrapWithGrafanaContextMock(<Advanced />)}
</Provider>
);

fireEvent.change(screen.getByTestId('retention-number-input'), { target: { value: 70 } });
fireEvent.submit(screen.getByTestId('advanced-button'));

await waitForElementToBeRemoved(() => screen.getByTestId('Spinner'));

expect(spy).toHaveBeenCalled();
expect(updateSettingsSpy).toHaveBeenCalled();
});

it('Sets correct URL from browser', async () => {
it('sets correct URL from browser', async () => {
const location = {
...window.location,
host: 'pmmtest.percona.com',
Expand Down Expand Up @@ -129,12 +170,11 @@ describe('Advanced::', () => {
);

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

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

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

fireEvent.change(screen.getByTestId('retention-number-input'), { target: { value: 70 } });
fireEvent.submit(screen.getByTestId('advanced-button'));

await waitForElementToBeRemoved(() => screen.getByTestId('Spinner'));

// expect(spy.calls.mostRecent().args[0].body.stt_check_intervals).toBeUndefined();
expect(spy).toHaveBeenLastCalledWith(
expect(updateSettingsSpy).toHaveBeenLastCalledWith(
expect.objectContaining({
body: expect.objectContaining({
advisor_run_intervals: undefined,
Expand All @@ -181,8 +221,6 @@ describe('Advanced::', () => {
});

it('Includes STT check intervals in the change request if STT checks are enabled', async () => {
const spy = jest.spyOn(reducers, 'updateSettingsAction');

render(
<Provider
store={configureStore({
Expand Down Expand Up @@ -220,7 +258,7 @@ describe('Advanced::', () => {
await waitForElementToBeRemoved(() => screen.getByTestId('Spinner'));

// expect(spy.calls.mostRecent().args[0].body.stt_check_intervals).toBeDefined();
expect(spy).toHaveBeenLastCalledWith(
expect(updateSettingsSpy).toHaveBeenLastCalledWith(
expect.objectContaining({
body: expect.objectContaining({
advisor_run_intervals: {
Expand All @@ -232,4 +270,68 @@ describe('Advanced::', () => {
})
);
});

it('updates internal monitoring when pmm server monitoring is turned on', async () => {
const { container } = setup();

const monitoringSwitch = container.querySelector(
'[data-testid="enable-internal-pg-qan"] [name="enableInternalPgQan"]'
);

expect(monitoringSwitch).toBeInTheDocument();

fireEvent.click(monitoringSwitch!);

fireEvent.submit(screen.getByTestId('advanced-button'));

await waitForElementToBeRemoved(() => screen.getByTestId('Spinner'));

expect(updateSettingsSpy).toHaveBeenCalledWith(
expect.objectContaining({
body: expect.objectContaining({
enable_internal_pg_qan: false,
}),
})
);
});

it('updates internal monitoring when pmm server monitoring is turned off', async () => {
const { container } = setup(false);

const monitoringSwitch = container.querySelector(
'[data-testid="enable-internal-pg-qan"] [name="enableInternalPgQan"]'
);

expect(monitoringSwitch).toBeInTheDocument();

fireEvent.click(monitoringSwitch!);

fireEvent.submit(screen.getByTestId('advanced-button'));

await waitForElementToBeRemoved(() => screen.getByTestId('Spinner'));

expect(updateSettingsSpy).toHaveBeenCalledWith(
expect.objectContaining({
body: expect.objectContaining({
enable_internal_pg_qan: true,
}),
})
);
});

it("doesn't update internal monitoring when pmm server monitoring doesn't change", async () => {
setup();

fireEvent.submit(screen.getByTestId('advanced-button'));

await waitForElementToBeRemoved(() => screen.getByTestId('Spinner'));

expect(updateSettingsSpy).toHaveBeenCalledWith(
expect.objectContaining({
body: expect.objectContaining({
enable_internal_pg_qan: true,
}),
})
);
});
});
Loading
Loading