[Fleet] Do not break UI and API on orphaned package policies (#137107)

This commit is contained in:
Nicolas Chaulet 2022-07-25 16:21:23 -04:00 committed by GitHub
parent becaec81e1
commit 616f7be237
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 65 additions and 24 deletions

View file

@ -15,6 +15,7 @@ import {
EuiFlexItem,
EuiText,
EuiButton,
EuiIcon,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedRelative, FormattedMessage } from '@kbn/i18n-react';
@ -47,7 +48,7 @@ interface PackagePoliciesPanelProps {
interface InMemoryPackagePolicyAndAgentPolicy {
packagePolicy: InMemoryPackagePolicy;
agentPolicy: GetAgentPoliciesResponseItem;
agentPolicy?: GetAgentPoliciesResponseItem;
}
const IntegrationDetailsLink = memo<{
@ -68,6 +69,17 @@ const IntegrationDetailsLink = memo<{
);
});
const AgentPolicyNotFound = () => (
<EuiText color="subdued" size="xs" className="eui-textNoWrap">
<EuiIcon size="m" type="alert" color="warning" />
&nbsp;
<FormattedMessage
id="xpack.fleet.epm.packageDetails.integrationList.agentPolicyDeletedWarning"
defaultMessage="Policy not found"
/>
</EuiText>
);
export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps) => {
const { search } = useLocation();
const history = useHistory();
@ -101,7 +113,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
const canWriteIntegrationPolicies = useAuthz().integrations.writeIntegrationPolicies;
const packageAndAgentPolicies = useMemo((): Array<{
agentPolicy: GetAgentPoliciesResponseItem;
agentPolicy?: GetAgentPoliciesResponseItem;
packagePolicy: InMemoryPackagePolicy;
}> => {
if (!data?.items) {
@ -131,7 +143,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
}, [data?.items, updatableIntegrations]);
const showAddAgentHelpForPackagePolicyId = packageAndAgentPolicies.find(
({ agentPolicy }) => agentPolicy.id === showAddAgentHelpForPolicyId
({ agentPolicy }) => agentPolicy?.id === showAddAgentHelpForPolicyId
)?.packagePolicy?.id;
// Handle the "add agent" link displayed in post-installation toast notifications in the case
// where a user is clicking the link while on the package policies listing page
@ -187,7 +199,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
</EuiText>
</EuiFlexItem>
{packagePolicy.hasUpgrade && (
{agentPolicy && packagePolicy.hasUpgrade && (
<EuiFlexItem grow={false}>
<EuiButton
size="s"
@ -217,7 +229,11 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
}),
truncateText: true,
render(id, { agentPolicy }) {
return <AgentPolicySummaryLine policy={agentPolicy} />;
return agentPolicy ? (
<AgentPolicySummaryLine policy={agentPolicy} />
) : (
<AgentPolicyNotFound />
);
},
},
{
@ -250,6 +266,9 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
defaultMessage: 'Agents',
}),
render({ agentPolicy, packagePolicy }: InMemoryPackagePolicyAndAgentPolicy) {
if (!agentPolicy) {
return null;
}
return (
<PackagePolicyAgentsCell
agentPolicy={agentPolicy}
@ -273,10 +292,14 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
agentPolicy={agentPolicy}
packagePolicy={packagePolicy}
showAddAgent={true}
upgradePackagePolicyHref={`${getHref('upgrade_package_policy', {
policyId: agentPolicy.id,
packagePolicyId: packagePolicy.id,
})}?from=integrations-policy-list`}
upgradePackagePolicyHref={
agentPolicy
? `${getHref('upgrade_package_policy', {
policyId: agentPolicy.id,
packagePolicyId: packagePolicy.id,
})}?from=integrations-policy-list`
: undefined
}
/>
);
},
@ -311,7 +334,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
);
}
const selectedPolicies = packageAndAgentPolicies.find(
({ agentPolicy: policy }) => policy.id === flyoutOpenForPolicyId
({ agentPolicy: policy }) => policy?.id === flyoutOpenForPolicyId
);
const agentPolicy = selectedPolicies?.agentPolicy;
const packagePolicy = selectedPolicies?.packagePolicy;

View file

@ -32,7 +32,7 @@ function renderMenu({
agentPolicy={agentPolicy}
packagePolicy={packagePolicy}
showAddAgent={showAddAgent}
upgradePackagePolicyHref=""
upgradePackagePolicyHref="/test/upgrade-link"
defaultIsOpen={defaultIsOpen}
key="test1"
/>
@ -95,8 +95,9 @@ test('Should enable upgrade button if package has upgrade', async () => {
const agentPolicy = createMockAgentPolicy();
const packagePolicy = createMockPackagePolicy({ hasUpgrade: true });
const { utils } = renderMenu({ agentPolicy, packagePolicy });
await act(async () => {
const upgradeButton = utils.getByText('Upgrade integration policy').closest('button');
const upgradeButton = utils.getByTestId('PackagePolicyActionsUpgradeItem');
expect(upgradeButton).not.toBeDisabled();
});
});

View file

@ -19,11 +19,11 @@ import { DangerEuiContextMenuItem } from './danger_eui_context_menu_item';
import { PackagePolicyDeleteProvider } from './package_policy_delete_provider';
export const PackagePolicyActionsMenu: React.FunctionComponent<{
agentPolicy: AgentPolicy;
agentPolicy?: AgentPolicy;
packagePolicy: InMemoryPackagePolicy;
showAddAgent?: boolean;
defaultIsOpen?: boolean;
upgradePackagePolicyHref: string;
upgradePackagePolicyHref?: string;
}> = ({
agentPolicy,
packagePolicy,
@ -38,6 +38,9 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{
const [isActionsMenuOpen, setIsActionsMenuOpen] = useState(defaultIsOpen);
const isManaged = Boolean(packagePolicy.is_managed);
const agentPolicyIsManaged = Boolean(agentPolicy?.is_managed);
const isAddAgentVisible = showAddAgent && agentPolicy && !agentPolicyIsManaged;
const onEnrollmentFlyoutClose = useMemo(() => {
return () => setIsEnrollmentFlyoutOpen(false);
@ -55,7 +58,7 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{
// defaultMessage="View integration"
// />
// </EuiContextMenuItem>,
...(showAddAgent && !agentPolicy.is_managed
...(isAddAgentVisible
? [
<EuiContextMenuItem
data-test-subj="PackagePolicyActionsAddAgentItem"
@ -75,7 +78,7 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{
: []),
<EuiContextMenuItem
data-test-subj="PackagePolicyActionsEditItem"
disabled={!canWriteIntegrationPolicies}
disabled={!canWriteIntegrationPolicies || !agentPolicy}
icon="pencil"
href={getHref('integration_policy_edit', {
packagePolicyId: packagePolicy.id,
@ -89,7 +92,9 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{
</EuiContextMenuItem>,
<EuiContextMenuItem
data-test-subj="PackagePolicyActionsUpgradeItem"
disabled={!packagePolicy.hasUpgrade || !canWriteIntegrationPolicies}
disabled={
!packagePolicy.hasUpgrade || !canWriteIntegrationPolicies || !upgradePackagePolicyHref
}
icon="refresh"
href={upgradePackagePolicyHref}
key="packagePolicyUpgrade"
@ -108,7 +113,7 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{
// </EuiContextMenuItem>,
];
if (!agentPolicy.is_managed) {
if (!agentPolicy || !agentPolicyIsManaged) {
menuItems.push(
<PackagePolicyDeleteProvider agentPolicy={agentPolicy} key="packagePolicyDelete">
{(deletePackagePoliciesPrompt) => {

View file

@ -15,7 +15,7 @@ import { AGENT_API_ROUTES, AGENTS_PREFIX } from '../../common/constants';
import type { AgentPolicy } from '../types';
interface Props {
agentPolicy: AgentPolicy;
agentPolicy?: AgentPolicy;
children: (deletePackagePoliciesPrompt: DeletePackagePoliciesPrompt) => React.ReactElement;
}
@ -43,7 +43,7 @@ export const PackagePolicyDeleteProvider: React.FunctionComponent<Props> = ({
const fetchAgentsCount = useMemo(
() => async () => {
if (isLoadingAgentsCount || !isFleetEnabled) {
if (isLoadingAgentsCount || !isFleetEnabled || !agentPolicy) {
return;
}
setIsLoadingAgentsCount(true);
@ -59,7 +59,7 @@ export const PackagePolicyDeleteProvider: React.FunctionComponent<Props> = ({
setAgentsCount(data?.total || 0);
setIsLoadingAgentsCount(false);
},
[agentPolicy.id, isFleetEnabled, isLoadingAgentsCount]
[agentPolicy, isFleetEnabled, isLoadingAgentsCount]
);
const deletePackagePoliciesPrompt = useMemo(
@ -200,7 +200,7 @@ export const PackagePolicyDeleteProvider: React.FunctionComponent<Props> = ({
id="xpack.fleet.deletePackagePolicy.confirmModal.affectedAgentsMessage"
defaultMessage="Fleet has detected that {agentPolicyName} is already in use by some of your agents."
values={{
agentPolicyName: <strong>{agentPolicy.name}</strong>,
agentPolicyName: <strong>{agentPolicy?.name}</strong>,
}}
/>
</EuiCallOut>

View file

@ -613,7 +613,7 @@ class AgentPolicyService {
id: string,
packagePolicyIds: string[],
options?: { user?: AuthenticatedUser; force?: boolean }
): Promise<AgentPolicy> {
) {
const oldAgentPolicy = await this.get(soClient, id, false);
if (!oldAgentPolicy) {

View file

@ -484,7 +484,19 @@ class PackagePolicyService implements PackagePolicyServiceInterface {
throw new PackagePolicyRestrictionRelatedError(`Cannot delete package policy ${id}`);
}
if (!options?.skipUnassignFromAgentPolicies) {
const agentPolicy = await agentPolicyService
.get(soClient, packagePolicy.policy_id)
.catch((err) => {
if (soClient.errors.isNotFoundError(err)) {
appContextService
.getLogger()
.warn(`Agent policy ${packagePolicy.policy_id} not found`);
return null;
}
throw err;
});
if (agentPolicy && !options?.skipUnassignFromAgentPolicies) {
await agentPolicyService.unassignPackagePolicies(
soClient,
esClient,