Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fleet] Allow some agent actions for managed policies #155923

Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* 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.
*/

import React from 'react';
import { fireEvent } from '@testing-library/dom';

import { createFleetTestRendererMock } from '../../../../../../mock';
import type { Agent, AgentPolicy } from '../../../../types';
import { ExperimentalFeaturesService } from '../../../../services';
import { useAuthz } from '../../../../../../hooks/use_authz';

import { AgentDetailsActionMenu } from './actions_menu';

jest.mock('../../../../../../hooks/use_fleet_status', () => ({
FleetStatusProvider: (props: any) => {
return props.children;
},
}));

jest.mock('../../../../../../services/experimental_features');
jest.mock('../../../../../../hooks/use_authz');

const mockedExperimentalFeaturesService = jest.mocked(ExperimentalFeaturesService);
const mockedUseAuthz = jest.mocked(useAuthz);

function renderActions({ agent, agentPolicy }: { agent: Agent; agentPolicy?: AgentPolicy }) {
const renderer = createFleetTestRendererMock();

const utils = renderer.render(
<AgentDetailsActionMenu
agent={agent}
agentPolicy={agentPolicy}
assignFlyoutOpenByDefault={false}
onCancelReassign={jest.fn()}
/>
);

fireEvent.click(utils.getByRole('button'));

return { utils };
}

describe('AgentDetailsActionMenu', () => {
beforeEach(() => {
mockedExperimentalFeaturesService.get.mockReturnValue({
diagnosticFileUploadEnabled: true,
} as any);
mockedUseAuthz.mockReturnValue({
fleet: {
all: true,
},
} as any);
});

describe('Request Diagnotics action', () => {
function renderAndGetDiagnosticsButton({
agent,
agentPolicy,
}: {
agent: Agent;
agentPolicy?: AgentPolicy;
}) {
const { utils } = renderActions({
agent,
agentPolicy,
});

return utils.queryByTestId('requestAgentDiagnosticsBtn');
}

it('should not render action if feature is disabled', async () => {
mockedExperimentalFeaturesService.get.mockReturnValue({
diagnosticFileUploadEnabled: false,
} as any);
const res = renderAndGetDiagnosticsButton({
agent: {} as Agent,
agentPolicy: {} as AgentPolicy,
});
expect(res).toBe(null);
});

it('should render an active action button if agent version >= 8.7', async () => {
const res = renderAndGetDiagnosticsButton({
agent: {
status: 'online',
local_metadata: { elastic: { agent: { version: '8.8.0' } } },
} as any,
agentPolicy: {} as AgentPolicy,
});

expect(res).not.toBe(null);
expect(res).toBeEnabled();
});

it('should render an active action button if agent version >= 8.7 and policy is_managed', async () => {
const res = renderAndGetDiagnosticsButton({
agent: {
status: 'online',
local_metadata: { elastic: { agent: { version: '8.8.0' } } },
} as any,
agentPolicy: {
is_managed: true,
} as AgentPolicy,
});

expect(res).not.toBe(null);
expect(res).toBeEnabled();
});

it('should render a disabled action button if agent version < 8.7', async () => {
const res = renderAndGetDiagnosticsButton({
agent: {
status: 'online',
local_metadata: { elastic: { agent: { version: '8.6.0' } } },
} as any,
agentPolicy: {
is_managed: true,
} as AgentPolicy,
});

expect(res).not.toBe(null);
expect(res).not.toBeEnabled();
});
});

describe('View agent JSON action', () => {
function renderAndGetViewJSONButton({
agent,
agentPolicy,
}: {
agent: Agent;
agentPolicy?: AgentPolicy;
}) {
const { utils } = renderActions({
agent,
agentPolicy,
});

return utils.queryByTestId('viewAgentDetailsJsonBtn');
}

it('should render an active button', async () => {
const res = renderAndGetViewJSONButton({
agent: {} as any,
agentPolicy: {} as AgentPolicy,
});

expect(res).not.toBe(null);
expect(res).toBeEnabled();
});

it('should render an active button for managed agent policy', async () => {
const res = renderAndGetViewJSONButton({
agent: {} as any,
agentPolicy: {
is_managed: true,
} as AgentPolicy,
});

expect(res).not.toBe(null);
expect(res).toBeEnabled();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
onClick={() => {
setIsReassignFlyoutOpen(true);
}}
disabled={!agent.active}
disabled={!agent.active || agentPolicy?.is_managed}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the UX would be less confusing if we hid these options instead of disabling them

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I will make the change

key="reassignPolicy"
>
<FormattedMessage
Expand All @@ -77,7 +77,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
</EuiContextMenuItem>,
<EuiContextMenuItem
icon="trash"
disabled={!hasFleetAllPrivileges || !agent.active}
disabled={!hasFleetAllPrivileges || !agent.active || agentPolicy?.is_managed}
onClick={() => {
setIsUnenrollModalOpen(true);
}}
Expand All @@ -97,7 +97,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
</EuiContextMenuItem>,
<EuiContextMenuItem
icon="refresh"
disabled={!isAgentUpgradeable(agent, kibanaVersion)}
disabled={!isAgentUpgradeable(agent, kibanaVersion) || agentPolicy?.is_managed}
onClick={() => {
setIsUpgradeModalOpen(true);
}}
Expand All @@ -108,20 +108,24 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
defaultMessage="Upgrade agent"
/>
</EuiContextMenuItem>,
];

menuItems.push(
<EuiContextMenuItem
icon="inspect"
onClick={() => {
setIsContextMenuOpen(false);
setIsAgentDetailsJsonFlyoutOpen(!isAgentDetailsJsonFlyoutOpen);
}}
key="agentDetailsJson"
data-test-subj="viewAgentDetailsJsonBtn"
>
<FormattedMessage
id="xpack.fleet.agentList.viewAgentDetailsJsonText"
defaultMessage="View agent JSON"
/>
</EuiContextMenuItem>,
];
</EuiContextMenuItem>
);

if (diagnosticFileUploadEnabled) {
menuItems.push(
Expand All @@ -131,6 +135,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
onClick={() => {
setIsRequestDiagnosticsModalOpen(true);
}}
data-test-subj="requestAgentDiagnosticsBtn"
key="requestDiagnostics"
>
<FormattedMessage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export const AgentDetailsPage: React.FunctionComponent = () => {
<>
<EuiSpacer size="m" />
<EuiFlexGroup justifyContent="flexEnd" alignItems="center" gutterSize="s" direction="row">
{!isAgentPolicyLoading && !agentPolicyData?.item?.is_managed && (
{!isAgentPolicyLoading && (
<EuiFlexItem grow={false}>
<AgentDetailsActionMenu
agent={agentData.item}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* 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.
*/

import React from 'react';
import { fireEvent } from '@testing-library/dom';

import { createFleetTestRendererMock } from '../../../../../../mock';
import type { Agent, AgentPolicy } from '../../../../types';
import { ExperimentalFeaturesService } from '../../../../services';
import { useAuthz } from '../../../../../../hooks/use_authz';

import { TableRowActions } from './table_row_actions';

jest.mock('../../../../../../hooks/use_fleet_status', () => ({
FleetStatusProvider: (props: any) => {
return props.children;
},
}));

jest.mock('../../../../../../services/experimental_features');
jest.mock('../../../../../../hooks/use_authz');

const mockedExperimentalFeaturesService = jest.mocked(ExperimentalFeaturesService);
const mockedUseAuthz = jest.mocked(useAuthz);

function renderTableRowActions({
agent,
agentPolicy,
}: {
agent: Agent;
agentPolicy?: AgentPolicy;
}) {
const renderer = createFleetTestRendererMock();

const utils = renderer.render(
<TableRowActions
agent={agent}
agentPolicy={agentPolicy}
onAddRemoveTagsClick={jest.fn()}
onReassignClick={jest.fn()}
onRequestDiagnosticsClick={jest.fn()}
onUnenrollClick={jest.fn()}
onUpgradeClick={jest.fn}
/>
);

fireEvent.click(utils.getByTestId('agentActionsBtn'));

return { utils };
}
describe('TableRowActions', () => {
beforeEach(() => {
mockedExperimentalFeaturesService.get.mockReturnValue({
diagnosticFileUploadEnabled: true,
} as any);
mockedUseAuthz.mockReturnValue({
fleet: {
all: true,
},
} as any);
});

describe('Request Diagnotics action', () => {
function renderAndGetDiagnosticsButton({
agent,
agentPolicy,
}: {
agent: Agent;
agentPolicy?: AgentPolicy;
}) {
const { utils } = renderTableRowActions({
agent,
agentPolicy,
});

return utils.queryByTestId('requestAgentDiagnosticsBtn');
}

it('should not render action if feature is disabled', async () => {
mockedExperimentalFeaturesService.get.mockReturnValue({
diagnosticFileUploadEnabled: false,
} as any);
const res = renderAndGetDiagnosticsButton({
agent: {} as Agent,
agentPolicy: {} as AgentPolicy,
});
expect(res).toBe(null);
});

it('should render an active action button if agent version >= 8.7', async () => {
const res = renderAndGetDiagnosticsButton({
agent: {
status: 'online',
local_metadata: { elastic: { agent: { version: '8.8.0' } } },
} as any,
agentPolicy: {} as AgentPolicy,
});

expect(res).not.toBe(null);
expect(res).toBeEnabled();
});

it('should render an active action button if agent version >= 8.7 and policy is_managed', async () => {
const res = renderAndGetDiagnosticsButton({
agent: {
status: 'online',
local_metadata: { elastic: { agent: { version: '8.8.0' } } },
} as any,
agentPolicy: {
is_managed: true,
} as AgentPolicy,
});

expect(res).not.toBe(null);
expect(res).toBeEnabled();
});

it('should render a disabled action button if agent version < 8.7', async () => {
const res = renderAndGetDiagnosticsButton({
agent: {
status: 'online',
local_metadata: { elastic: { agent: { version: '8.6.0' } } },
} as any,
agentPolicy: {
is_managed: true,
} as AgentPolicy,
});

expect(res).not.toBe(null);
expect(res).not.toBeEnabled();
});
});
});
Loading