mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Fleet] Change agent policies in edit package policy page (#186084)
## Summary Closes https://github.com/elastic/kibana/issues/184394 Added agent policy selection to Edit integration policy page. There is a lot of duplication between Create and Edit integration policy pages, I'll see if I can refactor to extract the common logic: steps components and managing its state. I extracted the steps to a hook, it would be a bigger refactor to use this in Create package policy page, so I might create a follow up issue for that. ## To verify - enable the `enableReusableIntegrationPolicies` experimental feature in `kibana.dev.yml` - Create a few agent policies - Add an integration - Go to Edit integration, and modify the linked agent policies - Verify that the existing agent policies are populated correctly in the Existing hosts combo box - Verify that the modified agent policy list is reflected in the `Preview API Request`, `policy_ids` list. - Verify that when submitting the form, the package policy linkages are updated to the selected ones (add/remove agent policies) - The agent count should update below the combo / in the submit modal window - It's not allowed to submit the form after removing all agent policies - If a new agent policy is selected, it will be created first and then assigned to the integration policy <img width="995" alt="image" src="0a7163c6
-154e-49b1-b73c-19ed024f6dc3"> <img width="993" alt="image" src="ad470a27
-90fa-40f5-b394-a93a08c95e06"> <img width="535" alt="image" src="3b0ddc29
-abf8-4e0d-8beb-300634c245b3"> <img width="1758" alt="image" src="e8b976fe
-3e53-439c-9b23-803deaf3e0aa"> ### Create agent policy <img width="1737" alt="image" src="6f2a7f65
-981a-487d-87c4-2dbb7ecd1835"> Preview API request contains the POST agent policy request <img width="896" alt="image" src="109140ab
-13f2-42c9-9bbc-fb64859c4f62"> After submit, the updated integration policy is assigned to the new agent policy too <img width="2552" alt="image" src="4027b47b
-8d20-4153-b7ec-ed3500f08c9a"> ## Open questions - Currently the namespace placeholder of the package policy is set to show the namespace of the first selected agent policy (if not set by the package policy). I have to check what happens on the backend, if the inherited namespace is changed if the agent policies change. The behaviour should be consistent in the backend and UI. Currently on the Agent policy details UI, the same integration policy might show different inherited namespace if its shared by multiple agent policies with different namespace. <img width="1498" alt="image" src="567800a8
-2dcb-4b18-af89-f6e902889092"> <img width="1326" alt="image" src="b59d131e
-314c-4d5a-81e3-ab8fe0fa6e1f"> <img width="1318" alt="image" src="69b54a63
-f7c1-4f0f-8041-74b1774f1e9e"> - When the Edit integration was started from the Agent policy details UI, the navigation goes back to the same agent policy after submitting the page. Is this okay? Might be somewhat unexpected if creating a new agent policy, though it is getting complex to decide where to navigate in case of multiple agent policies. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: criamico <mariacristina.amico@elastic.co>
This commit is contained in:
parent
592aafcaba
commit
ffa943c8be
22 changed files with 813 additions and 195 deletions
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 type { AgentPolicy } from '../types';
|
||||
|
||||
import { getInheritedNamespace } from './agent_policies_helpers';
|
||||
|
||||
describe('getInheritedNamespace', () => {
|
||||
const agentPolicy: AgentPolicy[] = [
|
||||
{
|
||||
id: 'agent-policy-1',
|
||||
namespace: 'testnamespace',
|
||||
name: 'Agent policy 1',
|
||||
is_managed: false,
|
||||
status: 'active',
|
||||
updated_at: '',
|
||||
updated_by: '',
|
||||
revision: 1,
|
||||
package_policies: [],
|
||||
is_protected: false,
|
||||
},
|
||||
];
|
||||
const agentPolicies: AgentPolicy[] = [
|
||||
{
|
||||
id: 'agent-policy-1',
|
||||
namespace: 'testnamespace',
|
||||
name: 'Agent policy 1',
|
||||
is_managed: false,
|
||||
status: 'active',
|
||||
updated_at: '',
|
||||
updated_by: '',
|
||||
revision: 1,
|
||||
package_policies: [],
|
||||
is_protected: false,
|
||||
},
|
||||
{
|
||||
id: 'agent-policy-2',
|
||||
namespace: 'default',
|
||||
name: 'Agent policy 2',
|
||||
is_managed: false,
|
||||
status: 'active',
|
||||
updated_at: '',
|
||||
updated_by: '',
|
||||
revision: 1,
|
||||
package_policies: [],
|
||||
is_protected: false,
|
||||
},
|
||||
];
|
||||
it('should return the policy namespace when there is only one agent policy', () => {
|
||||
expect(getInheritedNamespace(agentPolicy)).toEqual('testnamespace');
|
||||
});
|
||||
|
||||
it('should return default namespace when there is are multiple agent policies', () => {
|
||||
expect(getInheritedNamespace(agentPolicies)).toEqual('default');
|
||||
});
|
||||
|
||||
it('should return default namespace when there are no agent policies', () => {
|
||||
expect(getInheritedNamespace([])).toEqual('default');
|
||||
});
|
||||
});
|
|
@ -44,3 +44,10 @@ function policyHasIntegration(agentPolicy: AgentPolicy, packageName: string) {
|
|||
|
||||
return agentPolicy.package_policies?.some((p) => p.package?.name === packageName);
|
||||
}
|
||||
|
||||
export function getInheritedNamespace(agentPolicies: AgentPolicy[]): string {
|
||||
if (agentPolicies.length === 1) {
|
||||
return agentPolicies[0].namespace;
|
||||
}
|
||||
return 'default';
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ export {
|
|||
policyHasAPMIntegration,
|
||||
policyHasEndpointSecurity,
|
||||
policyHasSyntheticsIntegration,
|
||||
getInheritedNamespace,
|
||||
} from './agent_policies_helpers';
|
||||
|
||||
export {
|
||||
|
|
|
@ -102,6 +102,7 @@ describe('Add Integration - Mock API', () => {
|
|||
],
|
||||
};
|
||||
cy.intercept('/api/fleet/agent_policies?*', { items: [agentPolicy] });
|
||||
cy.intercept('/api/fleet/agent_policies/_bulk_get', { items: [agentPolicy] });
|
||||
cy.intercept('/api/fleet/agent_policies/policy-1', {
|
||||
item: agentPolicy,
|
||||
});
|
||||
|
|
|
@ -57,17 +57,19 @@ describe('Edit package policy', () => {
|
|||
hasErrors: false,
|
||||
},
|
||||
]);
|
||||
const agentPolicy = {
|
||||
id: 'fleet-server-policy',
|
||||
name: 'Fleet server policy 1',
|
||||
description: '',
|
||||
namespace: 'default',
|
||||
monitoring_enabled: ['logs', 'metrics'],
|
||||
status: 'active',
|
||||
package_policies: [{ id: 'policy-1', name: 'fleet_server-1' }],
|
||||
};
|
||||
cy.intercept('/api/fleet/agent_policies/fleet-server-policy', {
|
||||
item: {
|
||||
id: 'fleet-server-policy',
|
||||
name: 'Fleet server policy 1',
|
||||
description: '',
|
||||
namespace: 'default',
|
||||
monitoring_enabled: ['logs', 'metrics'],
|
||||
status: 'active',
|
||||
package_policies: [{ id: 'policy-1', name: 'fleet_server-1' }],
|
||||
},
|
||||
item: agentPolicy,
|
||||
});
|
||||
cy.intercept('/api/fleet/agent_policies/_bulk_get', { items: [agentPolicy] });
|
||||
cy.intercept('/api/fleet/epm/packages/fleet_server*', {
|
||||
item: {
|
||||
name: 'fleet_server',
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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 type { TestRenderer } from '../../../../../mock';
|
||||
import { createFleetTestRendererMock } from '../../../../../mock';
|
||||
|
||||
import { ConfirmDeployAgentPolicyModal } from './confirm_deploy_modal';
|
||||
|
||||
describe('ConfirmDeployAgentPolicyModal', () => {
|
||||
let testRenderer: TestRenderer;
|
||||
let renderResult: ReturnType<typeof testRenderer.render>;
|
||||
const render = (props: any) =>
|
||||
(renderResult = testRenderer.render(
|
||||
<ConfirmDeployAgentPolicyModal
|
||||
onConfirm={jest.fn()}
|
||||
onCancel={jest.fn()}
|
||||
agentCount={0}
|
||||
agentPolicies={[{ name: 'Agent policy 1' }, { name: 'Agent policy 2' }]}
|
||||
agentPoliciesToAdd={[]}
|
||||
agentPoliciesToRemove={[]}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
it('should render agent count with agent policies', () => {
|
||||
testRenderer = createFleetTestRendererMock();
|
||||
render({
|
||||
agentCount: 1,
|
||||
});
|
||||
expect(renderResult.getByText('This action will update 1 agent')).toBeInTheDocument();
|
||||
expect(renderResult.getByText('Agent policy 1, Agent policy 2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render agent policies to add and remove if no agent count', () => {
|
||||
testRenderer = createFleetTestRendererMock();
|
||||
render({
|
||||
agentCount: 0,
|
||||
agentPoliciesToAdd: ['Agent policy 1', 'Agent policy 2'],
|
||||
agentPoliciesToRemove: ['Agent policy 3'],
|
||||
});
|
||||
expect(
|
||||
renderResult.getByText('This action will update the selected agent policies')
|
||||
).toBeInTheDocument();
|
||||
const calloutText = renderResult.getByTestId('confirmAddRemovePoliciesCallout').textContent;
|
||||
expect(calloutText).toContain(
|
||||
'Agent policies that will be updated to use this integration policy:Agent policy 1Agent policy 2'
|
||||
);
|
||||
expect(calloutText).toContain(
|
||||
'Agent policies that will be updated to remove this integration policy:Agent policy 3'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -17,17 +17,15 @@ export const ConfirmDeployAgentPolicyModal: React.FunctionComponent<{
|
|||
onCancel: () => void;
|
||||
agentCount: number;
|
||||
agentPolicies: AgentPolicy[];
|
||||
showUnprivilegedAgentsCallout?: boolean;
|
||||
unprivilegedAgentsCount?: number;
|
||||
dataStreams?: Array<{ name: string; title: string }>;
|
||||
agentPoliciesToAdd?: string[];
|
||||
agentPoliciesToRemove?: string[];
|
||||
}> = ({
|
||||
onConfirm,
|
||||
onCancel,
|
||||
agentCount,
|
||||
agentPolicies,
|
||||
showUnprivilegedAgentsCallout = false,
|
||||
unprivilegedAgentsCount = 0,
|
||||
dataStreams,
|
||||
agentPoliciesToAdd = [],
|
||||
agentPoliciesToRemove = [],
|
||||
}) => {
|
||||
return (
|
||||
<EuiConfirmModal
|
||||
|
@ -53,28 +51,68 @@ export const ConfirmDeployAgentPolicyModal: React.FunctionComponent<{
|
|||
}
|
||||
buttonColor="primary"
|
||||
>
|
||||
<EuiCallOut
|
||||
iconType="iInCircle"
|
||||
title={i18n.translate('xpack.fleet.agentPolicy.confirmModalCalloutTitle', {
|
||||
defaultMessage:
|
||||
'This action will update {agentCount, plural, one {# agent} other {# agents}}',
|
||||
values: {
|
||||
agentCount,
|
||||
},
|
||||
})}
|
||||
>
|
||||
<div className="eui-textBreakWord">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentPolicy.confirmModalCalloutDescription"
|
||||
defaultMessage="Fleet has detected that the selected agent policies, {policyNames}, are already in use by
|
||||
{agentCount > 0 ? (
|
||||
<EuiCallOut
|
||||
iconType="iInCircle"
|
||||
title={i18n.translate('xpack.fleet.agentPolicy.confirmModalCalloutTitle', {
|
||||
defaultMessage:
|
||||
'This action will update {agentCount, plural, one {# agent} other {# agents}}',
|
||||
values: {
|
||||
agentCount,
|
||||
},
|
||||
})}
|
||||
>
|
||||
<div className="eui-textBreakWord">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentPolicy.confirmModalCalloutDescription"
|
||||
defaultMessage="Fleet has detected that the selected agent policies, {policyNames}, are already in use by
|
||||
some of your agents. As a result of this action, Fleet will deploy updates to all agents
|
||||
that use these policies."
|
||||
values={{
|
||||
policyNames: <b>{agentPolicies.map((policy) => policy.name).join(', ')}</b>,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</EuiCallOut>
|
||||
values={{
|
||||
policyNames: <b>{agentPolicies.map((policy) => policy.name).join(', ')}</b>,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</EuiCallOut>
|
||||
) : (
|
||||
<EuiCallOut
|
||||
data-test-subj="confirmAddRemovePoliciesCallout"
|
||||
iconType="iInCircle"
|
||||
title={i18n.translate('xpack.fleet.agentPolicy.confirmModalPoliciesCalloutTitle', {
|
||||
defaultMessage: 'This action will update the selected agent policies',
|
||||
values: {
|
||||
agentCount,
|
||||
},
|
||||
})}
|
||||
>
|
||||
{agentPoliciesToAdd.length > 0 && (
|
||||
<div className="eui-textBreakWord">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentPolicy.confirmModalPoliciesAddCalloutDescription"
|
||||
defaultMessage="Agent policies that will be updated to use this integration policy:"
|
||||
/>
|
||||
<ul>
|
||||
{agentPoliciesToAdd.map((policy) => (
|
||||
<li key={policy}>{policy}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
{agentPoliciesToRemove.length > 0 && (
|
||||
<div className="eui-textBreakWord">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentPolicy.confirmModalPoliciesRemoveCalloutDescription"
|
||||
defaultMessage="Agent policies that will be updated to remove this integration policy:"
|
||||
/>
|
||||
<ul>
|
||||
{agentPoliciesToRemove.map((policy) => (
|
||||
<li key={policy}>{policy}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</EuiCallOut>
|
||||
)}
|
||||
<EuiSpacer size="l" />
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentPolicy.confirmModalDescription"
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
import React from 'react';
|
||||
import { act, fireEvent, waitFor } from '@testing-library/react';
|
||||
|
||||
import { getInheritedNamespace } from '../../../../../../../../common/services';
|
||||
|
||||
import type { TestRenderer } from '../../../../../../../mock';
|
||||
import { createFleetTestRendererMock } from '../../../../../../../mock';
|
||||
import type { AgentPolicy, NewPackagePolicy, PackageInfo } from '../../../../../types';
|
||||
|
@ -92,7 +94,7 @@ describe('StepDefinePackagePolicy', () => {
|
|||
const render = () =>
|
||||
(renderResult = testRenderer.render(
|
||||
<StepDefinePackagePolicy
|
||||
agentPolicies={agentPolicies}
|
||||
namespacePlaceholder={getInheritedNamespace(agentPolicies)}
|
||||
packageInfo={packageInfo}
|
||||
packagePolicy={packagePolicy}
|
||||
updatePackagePolicy={mockUpdatePackagePolicy}
|
||||
|
|
|
@ -24,12 +24,7 @@ import {
|
|||
|
||||
import styled from 'styled-components';
|
||||
|
||||
import type {
|
||||
AgentPolicy,
|
||||
PackageInfo,
|
||||
NewPackagePolicy,
|
||||
RegistryVarsEntry,
|
||||
} from '../../../../../types';
|
||||
import type { PackageInfo, NewPackagePolicy, RegistryVarsEntry } from '../../../../../types';
|
||||
import { Loading } from '../../../../../components';
|
||||
import { useStartServices } from '../../../../../hooks';
|
||||
|
||||
|
@ -48,7 +43,7 @@ const FormGroupResponsiveFields = styled(EuiDescribedFormGroup)`
|
|||
`;
|
||||
|
||||
export const StepDefinePackagePolicy: React.FunctionComponent<{
|
||||
agentPolicies?: AgentPolicy[];
|
||||
namespacePlaceholder?: string;
|
||||
packageInfo: PackageInfo;
|
||||
packagePolicy: NewPackagePolicy;
|
||||
updatePackagePolicy: (fields: Partial<NewPackagePolicy>) => void;
|
||||
|
@ -58,7 +53,7 @@ export const StepDefinePackagePolicy: React.FunctionComponent<{
|
|||
noAdvancedToggle?: boolean;
|
||||
}> = memo(
|
||||
({
|
||||
agentPolicies,
|
||||
namespacePlaceholder,
|
||||
packageInfo,
|
||||
packagePolicy,
|
||||
updatePackagePolicy,
|
||||
|
@ -290,7 +285,7 @@ export const StepDefinePackagePolicy: React.FunctionComponent<{
|
|||
<EuiComboBox
|
||||
data-test-subj="packagePolicyNamespaceInput"
|
||||
noSuggestions
|
||||
placeholder={agentPolicies?.[0]?.namespace}
|
||||
placeholder={namespacePlaceholder}
|
||||
isDisabled={isEditPage && packageInfo.type === 'input'}
|
||||
singleSelection={true}
|
||||
selectedOptions={
|
||||
|
|
|
@ -67,14 +67,14 @@ describe('step select agent policy', () => {
|
|||
let renderResult: ReturnType<typeof testRenderer.render>;
|
||||
const mockSetHasAgentPolicyError = jest.fn();
|
||||
const updateAgentPoliciesMock = jest.fn();
|
||||
const render = (packageInfo?: PackageInfo, selectedAgentPolicyId?: string) =>
|
||||
const render = (packageInfo?: PackageInfo, selectedAgentPolicyIds: string[] = []) =>
|
||||
(renderResult = testRenderer.render(
|
||||
<StepSelectAgentPolicy
|
||||
packageInfo={packageInfo || ({ name: 'apache' } as any)}
|
||||
agentPolicies={[]}
|
||||
updateAgentPolicies={updateAgentPoliciesMock}
|
||||
setHasAgentPolicyError={mockSetHasAgentPolicyError}
|
||||
selectedAgentPolicyId={selectedAgentPolicyId}
|
||||
selectedAgentPolicyIds={selectedAgentPolicyIds}
|
||||
/>
|
||||
));
|
||||
|
||||
|
@ -182,7 +182,7 @@ describe('step select agent policy', () => {
|
|||
});
|
||||
|
||||
test('should select agent policy if pre selected', async () => {
|
||||
render(undefined, 'policy-1');
|
||||
render(undefined, ['policy-1']);
|
||||
await act(async () => {}); // Needed as updateAgentPolicies is called after multiple useEffect
|
||||
await act(async () => {
|
||||
expect(updateAgentPoliciesMock).toBeCalledWith([{ id: 'policy-1', package_policies: [] }]);
|
||||
|
|
|
@ -217,13 +217,13 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
|
|||
agentPolicies: AgentPolicy[];
|
||||
updateAgentPolicies: (agentPolicies: AgentPolicy[]) => void;
|
||||
setHasAgentPolicyError: (hasError: boolean) => void;
|
||||
selectedAgentPolicyId?: string;
|
||||
selectedAgentPolicyIds: string[];
|
||||
}> = ({
|
||||
packageInfo,
|
||||
agentPolicies,
|
||||
updateAgentPolicies: updateSelectedAgentPolicies,
|
||||
setHasAgentPolicyError,
|
||||
selectedAgentPolicyId,
|
||||
selectedAgentPolicyIds,
|
||||
}) => {
|
||||
const { isReady: isFleetReady } = useFleetStatus();
|
||||
|
||||
|
@ -240,8 +240,10 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
|
|||
} = useAgentPoliciesOptions(packageInfo);
|
||||
|
||||
const [selectedPolicyIds, setSelectedPolicyIds] = useState<string[]>([]);
|
||||
const [isFirstLoad, setIsFirstLoad] = useState<boolean>(true);
|
||||
|
||||
const [isLoadingSelectedAgentPolicy, setIsLoadingSelectedAgentPolicy] = useState<boolean>(false);
|
||||
const [isLoadingSelectedAgentPolicies, setIsLoadingSelectedAgentPolicies] =
|
||||
useState<boolean>(false);
|
||||
const [selectedAgentPolicies, setSelectedAgentPolicies] = useState<AgentPolicy[]>(agentPolicies);
|
||||
|
||||
const updateAgentPolicies = useCallback(
|
||||
|
@ -255,7 +257,7 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
|
|||
useEffect(() => {
|
||||
const fetchAgentPolicyInfo = async () => {
|
||||
if (selectedPolicyIds.length > 0) {
|
||||
setIsLoadingSelectedAgentPolicy(true);
|
||||
setIsLoadingSelectedAgentPolicies(true);
|
||||
const { data, error } = await sendBulkGetAgentPolicies(selectedPolicyIds, { full: true });
|
||||
if (error) {
|
||||
setSelectedAgentPolicyError(error);
|
||||
|
@ -264,7 +266,7 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
|
|||
setSelectedAgentPolicyError(undefined);
|
||||
updateAgentPolicies(data.items);
|
||||
}
|
||||
setIsLoadingSelectedAgentPolicy(false);
|
||||
setIsLoadingSelectedAgentPolicies(false);
|
||||
} else {
|
||||
setSelectedAgentPolicyError(undefined);
|
||||
updateAgentPolicies([]);
|
||||
|
@ -284,25 +286,27 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
|
|||
// Try to select default agent policy
|
||||
useEffect(() => {
|
||||
if (
|
||||
isFirstLoad &&
|
||||
selectedPolicyIds.length === 0 &&
|
||||
existingAgentPolicies.length &&
|
||||
(enableReusableIntegrationPolicies
|
||||
? agentPolicyMultiOptions.length
|
||||
: agentPolicyOptions.length)
|
||||
) {
|
||||
setIsFirstLoad(false);
|
||||
if (enableReusableIntegrationPolicies) {
|
||||
const enabledOptions = agentPolicyMultiOptions.filter((option) => !option.disabled);
|
||||
if (enabledOptions.length === 1) {
|
||||
setSelectedPolicyIds([enabledOptions[0].key!]);
|
||||
} else if (selectedAgentPolicyId) {
|
||||
setSelectedPolicyIds([selectedAgentPolicyId]);
|
||||
} else if (selectedAgentPolicyIds.length > 0) {
|
||||
setSelectedPolicyIds(selectedAgentPolicyIds);
|
||||
}
|
||||
} else {
|
||||
const enabledOptions = agentPolicyOptions.filter((option) => !option.disabled);
|
||||
if (enabledOptions.length === 1) {
|
||||
setSelectedPolicyIds([enabledOptions[0].value]);
|
||||
} else if (selectedAgentPolicyId) {
|
||||
setSelectedPolicyIds([selectedAgentPolicyId]);
|
||||
} else if (selectedAgentPolicyIds.length > 0) {
|
||||
setSelectedPolicyIds(selectedAgentPolicyIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -310,9 +314,10 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
|
|||
agentPolicyOptions,
|
||||
agentPolicyMultiOptions,
|
||||
enableReusableIntegrationPolicies,
|
||||
selectedAgentPolicyId,
|
||||
selectedAgentPolicyIds,
|
||||
selectedPolicyIds,
|
||||
existingAgentPolicies,
|
||||
isFirstLoad,
|
||||
]);
|
||||
|
||||
// Bubble up any issues with agent policy selection
|
||||
|
@ -342,6 +347,14 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
|
|||
);
|
||||
}
|
||||
|
||||
const someNewAgentPoliciesHaveLimitedPackage =
|
||||
!packageInfo ||
|
||||
selectedAgentPolicies
|
||||
.filter((policy) => !selectedAgentPolicyIds.find((id) => policy.id === id))
|
||||
.some((selectedAgentPolicy) =>
|
||||
doesAgentPolicyHaveLimitedPackage(selectedAgentPolicy, packageInfo)
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlexGroup direction="column" gutterSize="m">
|
||||
|
@ -381,7 +394,7 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
|
|||
</EuiFlexGroup>
|
||||
}
|
||||
helpText={
|
||||
isFleetReady && selectedPolicyIds.length > 0 && !isLoadingSelectedAgentPolicy ? (
|
||||
isFleetReady && selectedPolicyIds.length > 0 && !isLoadingSelectedAgentPolicies ? (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyAgentsDescriptionText"
|
||||
defaultMessage="{count, plural, one {# agent is} other {# agents are}} enrolled with the selected agent policies."
|
||||
|
@ -395,11 +408,7 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
|
|||
) : null
|
||||
}
|
||||
isInvalid={Boolean(
|
||||
selectedPolicyIds.length === 0 ||
|
||||
!packageInfo ||
|
||||
selectedAgentPolicies.every((selectedAgentPolicy) =>
|
||||
doesAgentPolicyHaveLimitedPackage(selectedAgentPolicy, packageInfo)
|
||||
)
|
||||
selectedPolicyIds.length === 0 || someNewAgentPoliciesHaveLimitedPackage
|
||||
)}
|
||||
error={
|
||||
selectedPolicyIds.length === 0 ? (
|
||||
|
@ -407,17 +416,17 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
|
|||
id="xpack.fleet.createPackagePolicy.StepSelectPolicy.noPolicySelectedError"
|
||||
defaultMessage="An agent policy is required."
|
||||
/>
|
||||
) : (
|
||||
) : someNewAgentPoliciesHaveLimitedPackage ? (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.createPackagePolicy.StepSelectPolicy.cannotAddLimitedIntegrationError"
|
||||
defaultMessage="This integration can only be added once per agent policy."
|
||||
/>
|
||||
)
|
||||
) : null
|
||||
}
|
||||
>
|
||||
{enableReusableIntegrationPolicies ? (
|
||||
<AgentPolicyMultiSelect
|
||||
isLoading={isLoading || !packageInfo || isLoadingSelectedAgentPolicy}
|
||||
isLoading={isLoading || !packageInfo || isLoadingSelectedAgentPolicies}
|
||||
selectedPolicyIds={selectedPolicyIds}
|
||||
setSelectedPolicyIds={setSelectedPolicyIds}
|
||||
agentPolicyMultiOptions={agentPolicyMultiOptions}
|
||||
|
@ -431,7 +440,7 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
|
|||
}
|
||||
)}
|
||||
fullWidth
|
||||
isLoading={isLoading || !packageInfo || isLoadingSelectedAgentPolicy}
|
||||
isLoading={isLoading || !packageInfo || isLoadingSelectedAgentPolicies}
|
||||
options={agentPolicyOptions}
|
||||
valueOfSelected={selectedPolicyIds[0]}
|
||||
onChange={onChange}
|
||||
|
|
|
@ -93,7 +93,7 @@ describe('StepSelectHosts', () => {
|
|||
packageInfo={packageInfo}
|
||||
setHasAgentPolicyError={jest.fn()}
|
||||
updateSelectedTab={jest.fn()}
|
||||
selectedAgentPolicyId={undefined}
|
||||
selectedAgentPolicyIds={[]}
|
||||
/>
|
||||
));
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -42,7 +42,8 @@ interface Props {
|
|||
packageInfo?: PackageInfo;
|
||||
setHasAgentPolicyError: (hasError: boolean) => void;
|
||||
updateSelectedTab: (tab: SelectedPolicyTab) => void;
|
||||
selectedAgentPolicyId?: string;
|
||||
selectedAgentPolicyIds: string[];
|
||||
initialSelectedTabIndex?: number;
|
||||
}
|
||||
|
||||
export const StepSelectHosts: React.FunctionComponent<Props> = ({
|
||||
|
@ -56,7 +57,8 @@ export const StepSelectHosts: React.FunctionComponent<Props> = ({
|
|||
packageInfo,
|
||||
setHasAgentPolicyError,
|
||||
updateSelectedTab,
|
||||
selectedAgentPolicyId,
|
||||
selectedAgentPolicyIds,
|
||||
initialSelectedTabIndex,
|
||||
}) => {
|
||||
let existingAgentPolicies: AgentPolicy[] = [];
|
||||
const { data: agentPoliciesData, error: err } = useGetAgentPolicies({
|
||||
|
@ -110,7 +112,7 @@ export const StepSelectHosts: React.FunctionComponent<Props> = ({
|
|||
agentPolicies={agentPolicies}
|
||||
updateAgentPolicies={updateAgentPolicies}
|
||||
setHasAgentPolicyError={setHasAgentPolicyError}
|
||||
selectedAgentPolicyId={selectedAgentPolicyId}
|
||||
selectedAgentPolicyIds={selectedAgentPolicyIds}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
@ -121,7 +123,13 @@ export const StepSelectHosts: React.FunctionComponent<Props> = ({
|
|||
|
||||
return existingAgentPolicies.length > 0 ? (
|
||||
<StyledEuiTabbedContent
|
||||
initialSelectedTab={selectedAgentPolicyId ? tabs[1] : tabs[0]}
|
||||
initialSelectedTab={
|
||||
initialSelectedTabIndex
|
||||
? tabs[initialSelectedTabIndex]
|
||||
: selectedAgentPolicyIds.length > 0
|
||||
? tabs[1]
|
||||
: tabs[0]
|
||||
}
|
||||
tabs={tabs}
|
||||
onTabClick={handleOnTabClick}
|
||||
/>
|
||||
|
|
|
@ -8,10 +8,14 @@
|
|||
import { useMemo } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { omit } from 'lodash';
|
||||
import { set } from '@kbn/safer-lodash-set';
|
||||
|
||||
import { ExperimentalFeaturesService } from '../../../../../services';
|
||||
import {
|
||||
generateCreatePackagePolicyDevToolsRequest,
|
||||
generateCreateAgentPolicyDevToolsRequest,
|
||||
generateUpdatePackagePolicyDevToolsRequest,
|
||||
} from '../../../services';
|
||||
import {
|
||||
FLEET_SYSTEM_PACKAGE,
|
||||
|
@ -26,12 +30,14 @@ export function useDevToolsRequest({
|
|||
packageInfo,
|
||||
selectedPolicyTab,
|
||||
withSysMonitoring,
|
||||
packagePolicyId,
|
||||
}: {
|
||||
withSysMonitoring: boolean;
|
||||
selectedPolicyTab: SelectedPolicyTab;
|
||||
newAgentPolicy: NewAgentPolicy;
|
||||
packagePolicy: NewPackagePolicy;
|
||||
packageInfo?: PackageInfo;
|
||||
packagePolicyId?: string;
|
||||
}) {
|
||||
const { showDevtoolsRequest: isShowDevtoolRequestExperimentEnabled } =
|
||||
ExperimentalFeaturesService.get();
|
||||
|
@ -47,28 +53,55 @@ export function useDevToolsRequest({
|
|||
`${generateCreateAgentPolicyDevToolsRequest(
|
||||
newAgentPolicy,
|
||||
withSysMonitoring && !packagePolicyIsSystem
|
||||
)}\n\n${generateCreatePackagePolicyDevToolsRequest({
|
||||
...{ ...packagePolicy, policy_id: '' },
|
||||
})}`,
|
||||
i18n.translate(
|
||||
'xpack.fleet.createPackagePolicy.devtoolsRequestWithAgentPolicyDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'These Kibana requests create a new agent policy and a new package policy.',
|
||||
}
|
||||
),
|
||||
)}\n\n${
|
||||
packagePolicyId
|
||||
? generateUpdatePackagePolicyDevToolsRequest(
|
||||
packagePolicyId,
|
||||
set(omit(packagePolicy, 'elasticsearch', 'policy_id'), 'policy_ids', [
|
||||
...packagePolicy.policy_ids,
|
||||
'',
|
||||
])
|
||||
)
|
||||
: generateCreatePackagePolicyDevToolsRequest({
|
||||
...{ ...packagePolicy, policy_ids: [''] },
|
||||
})
|
||||
}`,
|
||||
packagePolicyId
|
||||
? i18n.translate(
|
||||
'xpack.fleet.editPackagePolicy.devtoolsRequestWithAgentPolicyDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'These Kibana requests create a new agent policy and update a package policy.',
|
||||
}
|
||||
)
|
||||
: i18n.translate(
|
||||
'xpack.fleet.createPackagePolicy.devtoolsRequestWithAgentPolicyDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'These Kibana requests create a new agent policy and a new package policy.',
|
||||
}
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
generateCreatePackagePolicyDevToolsRequest({
|
||||
...packagePolicy,
|
||||
}),
|
||||
i18n.translate('xpack.fleet.createPackagePolicy.devtoolsRequestDescription', {
|
||||
defaultMessage: 'This Kibana request creates a new package policy.',
|
||||
}),
|
||||
packagePolicyId
|
||||
? generateUpdatePackagePolicyDevToolsRequest(
|
||||
packagePolicyId,
|
||||
omit(packagePolicy, 'elasticsearch', 'policy_id')
|
||||
)
|
||||
: generateCreatePackagePolicyDevToolsRequest({
|
||||
...packagePolicy,
|
||||
}),
|
||||
packagePolicyId
|
||||
? i18n.translate('xpack.fleet.editPackagePolicy.devtoolsRequestDescription', {
|
||||
defaultMessage: 'This Kibana request updates package policy.',
|
||||
})
|
||||
: i18n.translate('xpack.fleet.createPackagePolicy.devtoolsRequestDescription', {
|
||||
defaultMessage: 'This Kibana request creates a new package policy.',
|
||||
}),
|
||||
];
|
||||
}, [packagePolicy, newAgentPolicy, withSysMonitoring, selectedPolicyTab]);
|
||||
}, [packagePolicy, newAgentPolicy, withSysMonitoring, selectedPolicyTab, packagePolicyId]);
|
||||
|
||||
return { showDevtoolsRequest, devtoolRequest, devtoolRequestDescription };
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ import {
|
|||
|
||||
import { useAgentless } from './setup_technology';
|
||||
|
||||
async function createAgentPolicy({
|
||||
export async function createAgentPolicy({
|
||||
packagePolicy,
|
||||
newAgentPolicy,
|
||||
withSysMonitoring,
|
||||
|
@ -72,6 +72,45 @@ async function createAgentPolicy({
|
|||
return resp.data.item;
|
||||
}
|
||||
|
||||
export const createAgentPolicyIfNeeded = async ({
|
||||
selectedPolicyTab,
|
||||
withSysMonitoring,
|
||||
newAgentPolicy,
|
||||
packagePolicy,
|
||||
packageInfo,
|
||||
}: {
|
||||
selectedPolicyTab: SelectedPolicyTab;
|
||||
withSysMonitoring: boolean;
|
||||
newAgentPolicy: NewAgentPolicy;
|
||||
packagePolicy: NewPackagePolicy;
|
||||
packageInfo?: PackageInfo;
|
||||
}): Promise<AgentPolicy | undefined> => {
|
||||
if (selectedPolicyTab === SelectedPolicyTab.NEW) {
|
||||
if ((withSysMonitoring || newAgentPolicy.monitoring_enabled?.length) ?? 0 > 0) {
|
||||
const packagesToPreinstall: Array<string | { name: string; version: string }> = [];
|
||||
// skip preinstall of input package, to be able to rollback when package policy creation fails
|
||||
if (packageInfo && packageInfo.type !== 'input') {
|
||||
packagesToPreinstall.push({ name: packageInfo.name, version: packageInfo.version });
|
||||
}
|
||||
if (withSysMonitoring) {
|
||||
packagesToPreinstall.push(FLEET_SYSTEM_PACKAGE);
|
||||
}
|
||||
if (newAgentPolicy.monitoring_enabled?.length ?? 0 > 0) {
|
||||
packagesToPreinstall.push(FLEET_ELASTIC_AGENT_PACKAGE);
|
||||
}
|
||||
|
||||
if (packagesToPreinstall.length > 0) {
|
||||
await sendBulkInstallPackages([...new Set(packagesToPreinstall)]);
|
||||
}
|
||||
}
|
||||
return await createAgentPolicy({
|
||||
newAgentPolicy,
|
||||
packagePolicy,
|
||||
withSysMonitoring,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
async function savePackagePolicy(pkgPolicy: CreatePackagePolicyRequest['body']) {
|
||||
const { policy, forceCreateNeeded } = await prepareInputPackagePolicyDataset(pkgPolicy);
|
||||
const result = await sendCreatePackagePolicy({
|
||||
|
@ -281,33 +320,21 @@ export function useOnSubmit({
|
|||
return;
|
||||
}
|
||||
let createdPolicy = overrideCreatedAgentPolicy;
|
||||
if (selectedPolicyTab === SelectedPolicyTab.NEW && !overrideCreatedAgentPolicy) {
|
||||
if (!overrideCreatedAgentPolicy) {
|
||||
try {
|
||||
setFormState('LOADING');
|
||||
if ((withSysMonitoring || newAgentPolicy.monitoring_enabled?.length) ?? 0 > 0) {
|
||||
const packagesToPreinstall: Array<string | { name: string; version: string }> = [];
|
||||
// skip preinstall of input package, to be able to rollback when package policy creation fails
|
||||
if (packageInfo && packageInfo.type !== 'input') {
|
||||
packagesToPreinstall.push({ name: packageInfo.name, version: packageInfo.version });
|
||||
}
|
||||
if (withSysMonitoring) {
|
||||
packagesToPreinstall.push(FLEET_SYSTEM_PACKAGE);
|
||||
}
|
||||
if (newAgentPolicy.monitoring_enabled?.length ?? 0 > 0) {
|
||||
packagesToPreinstall.push(FLEET_ELASTIC_AGENT_PACKAGE);
|
||||
}
|
||||
|
||||
if (packagesToPreinstall.length > 0) {
|
||||
await sendBulkInstallPackages([...new Set(packagesToPreinstall)]);
|
||||
}
|
||||
}
|
||||
createdPolicy = await createAgentPolicy({
|
||||
const newPolicy = await createAgentPolicyIfNeeded({
|
||||
newAgentPolicy,
|
||||
packagePolicy,
|
||||
withSysMonitoring,
|
||||
packageInfo,
|
||||
selectedPolicyTab,
|
||||
});
|
||||
setAgentPolicies([createdPolicy]);
|
||||
updatePackagePolicy({ policy_ids: [createdPolicy.id] });
|
||||
if (newPolicy) {
|
||||
createdPolicy = newPolicy;
|
||||
setAgentPolicies([createdPolicy]);
|
||||
updatePackagePolicy({ policy_ids: [createdPolicy.id] });
|
||||
}
|
||||
} catch (e) {
|
||||
setFormState('VALID');
|
||||
notifications.toasts.addError(e, {
|
||||
|
|
|
@ -35,6 +35,7 @@ import {
|
|||
import { useCancelAddPackagePolicy } from '../hooks';
|
||||
|
||||
import {
|
||||
getInheritedNamespace,
|
||||
getRootPrivilegedDataStreams,
|
||||
isRootPrivilegesRequired,
|
||||
splitPkgKey,
|
||||
|
@ -81,7 +82,7 @@ import { PostInstallGoogleCloudShellModal } from './components/cloud_security_po
|
|||
import { PostInstallAzureArmTemplateModal } from './components/cloud_security_posture/post_install_azure_arm_template_modal';
|
||||
import { RootPrivilegesCallout } from './root_callout';
|
||||
|
||||
const StepsWithLessPadding = styled(EuiSteps)`
|
||||
export const StepsWithLessPadding = styled(EuiSteps)`
|
||||
.euiStep__content {
|
||||
padding-bottom: ${(props) => props.theme.eui.euiSizeM};
|
||||
}
|
||||
|
@ -293,7 +294,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
packageInfo={packageInfo}
|
||||
setHasAgentPolicyError={setHasAgentPolicyError}
|
||||
updateSelectedTab={updateSelectedPolicyTab}
|
||||
selectedAgentPolicyId={queryParamsPolicyId}
|
||||
selectedAgentPolicyIds={queryParamsPolicyId ? [queryParamsPolicyId] : []}
|
||||
/>
|
||||
),
|
||||
[
|
||||
|
@ -377,7 +378,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
) : packageInfo ? (
|
||||
<>
|
||||
<StepDefinePackagePolicy
|
||||
agentPolicies={agentPolicies}
|
||||
namespacePlaceholder={getInheritedNamespace(agentPolicies)}
|
||||
packageInfo={packageInfo}
|
||||
packagePolicy={packagePolicy}
|
||||
updatePackagePolicy={updatePackagePolicy}
|
||||
|
@ -462,10 +463,6 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
}
|
||||
|
||||
const rootPrivilegedDataStreams = packageInfo ? getRootPrivilegedDataStreams(packageInfo) : [];
|
||||
const unprivilegedAgentsCount = agentPolicies.reduce(
|
||||
(acc, curr) => acc + (curr.unprivileged_agents ?? 0),
|
||||
0
|
||||
);
|
||||
|
||||
return (
|
||||
<CreatePackagePolicySinglePageLayout {...layoutProps} data-test-subj="createPackagePolicy">
|
||||
|
@ -478,13 +475,6 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
agentPolicies={agentPolicies}
|
||||
onConfirm={onSubmit}
|
||||
onCancel={() => setFormState('VALID')}
|
||||
showUnprivilegedAgentsCallout={Boolean(
|
||||
packageInfo &&
|
||||
isRootPrivilegesRequired(packageInfo) &&
|
||||
unprivilegedAgentsCount > 0
|
||||
)}
|
||||
unprivilegedAgentsCount={unprivilegedAgentsCount}
|
||||
dataStreams={rootPrivilegedDataStreams}
|
||||
/>
|
||||
)}
|
||||
{formState === 'SUBMITTED_NO_AGENTS' &&
|
||||
|
|
|
@ -7,3 +7,4 @@
|
|||
|
||||
export { useHistoryBlock } from './use_history_block';
|
||||
export { usePackagePolicyWithRelatedData } from './use_package_policy';
|
||||
export { usePackagePolicySteps } from './use_package_policy_steps';
|
||||
|
|
|
@ -15,7 +15,7 @@ import type {
|
|||
UpgradePackagePolicyDryRunResponse,
|
||||
} from '../../../../../../../common/types/rest_spec';
|
||||
import {
|
||||
sendGetOneAgentPolicy,
|
||||
sendBulkGetAgentPolicies,
|
||||
sendGetOnePackagePolicy,
|
||||
sendGetPackageInfoByKey,
|
||||
sendGetSettings,
|
||||
|
@ -94,11 +94,14 @@ export function usePackagePolicyWithRelatedData(
|
|||
const [validationResults, setValidationResults] = useState<PackagePolicyValidationResults>();
|
||||
const hasErrors = validationResults ? validationHasErrors(validationResults) : false;
|
||||
|
||||
const savePackagePolicy = async () => {
|
||||
const savePackagePolicy = async (packagePolicyOverride?: Partial<PackagePolicy>) => {
|
||||
setFormState('LOADING');
|
||||
const {
|
||||
policy: { elasticsearch, ...restPackagePolicy },
|
||||
} = await prepareInputPackagePolicyDataset(packagePolicy);
|
||||
} = await prepareInputPackagePolicyDataset({
|
||||
...packagePolicy,
|
||||
...(packagePolicyOverride ?? {}),
|
||||
});
|
||||
const result = await sendUpdatePackagePolicy(packagePolicyId, restPackagePolicy);
|
||||
|
||||
setFormState('SUBMITTED');
|
||||
|
@ -171,21 +174,15 @@ export function usePackagePolicyWithRelatedData(
|
|||
throw packagePolicyError;
|
||||
}
|
||||
|
||||
const newAgentPolicies = [];
|
||||
for (const policyId of packagePolicyData!.item.policy_ids) {
|
||||
const { data: agentPolicyData, error: agentPolicyError } = await sendGetOneAgentPolicy(
|
||||
policyId
|
||||
);
|
||||
const { data, error: agentPolicyError } = await sendBulkGetAgentPolicies(
|
||||
packagePolicyData!.item.policy_ids
|
||||
);
|
||||
|
||||
if (agentPolicyError) {
|
||||
throw agentPolicyError;
|
||||
}
|
||||
|
||||
if (agentPolicyData?.item) {
|
||||
newAgentPolicies.push(agentPolicyData.item);
|
||||
}
|
||||
if (agentPolicyError) {
|
||||
throw agentPolicyError;
|
||||
}
|
||||
setAgentPolicies(newAgentPolicies);
|
||||
|
||||
setAgentPolicies(data?.items ?? []);
|
||||
|
||||
const { data: upgradePackagePolicyDryRunData, error: upgradePackagePolicyDryRunError } =
|
||||
await sendUpgradePackagePolicyDryRun([packagePolicyId]);
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* 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, { useCallback, useMemo, useState, useEffect } from 'react';
|
||||
import { isEqual } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { EuiStepProps } from '@elastic/eui';
|
||||
|
||||
import type { AgentPolicy, NewAgentPolicy, NewPackagePolicy } from '../../../../../../../common';
|
||||
import { generateNewAgentPolicyWithDefaults } from '../../../../../../../common/services';
|
||||
import { SelectedPolicyTab, StepSelectHosts } from '../../create_package_policy_page/components';
|
||||
import type { PackageInfo } from '../../../../types';
|
||||
import { SetupTechnology } from '../../../../types';
|
||||
import {
|
||||
useDevToolsRequest,
|
||||
useSetupTechnology,
|
||||
} from '../../create_package_policy_page/single_page_layout/hooks';
|
||||
import { agentPolicyFormValidation } from '../../components';
|
||||
import { createAgentPolicyIfNeeded } from '../../create_package_policy_page/single_page_layout/hooks/form';
|
||||
|
||||
interface Params {
|
||||
configureStep: React.ReactNode;
|
||||
packageInfo?: PackageInfo;
|
||||
existingAgentPolicies: AgentPolicy[];
|
||||
setHasAgentPolicyError: (hasError: boolean) => void;
|
||||
updatePackagePolicy: (data: { policy_ids: string[] }) => void;
|
||||
agentPolicies: AgentPolicy[];
|
||||
setAgentPolicies: (agentPolicies: AgentPolicy[]) => void;
|
||||
isLoadingData: boolean;
|
||||
packagePolicy: NewPackagePolicy;
|
||||
packagePolicyId: string;
|
||||
setNewAgentPolicyName: (newAgentPolicyName: string | undefined) => void;
|
||||
}
|
||||
|
||||
export function usePackagePolicySteps({
|
||||
configureStep,
|
||||
packageInfo,
|
||||
existingAgentPolicies,
|
||||
setHasAgentPolicyError,
|
||||
updatePackagePolicy,
|
||||
agentPolicies,
|
||||
setAgentPolicies,
|
||||
isLoadingData,
|
||||
packagePolicy,
|
||||
packagePolicyId,
|
||||
setNewAgentPolicyName,
|
||||
}: Params) {
|
||||
const [newAgentPolicy, setNewAgentPolicy] = useState<NewAgentPolicy>(
|
||||
generateNewAgentPolicyWithDefaults({ name: 'Agent policy 1' })
|
||||
);
|
||||
const [withSysMonitoring, setWithSysMonitoring] = useState<boolean>(true);
|
||||
|
||||
const [selectedPolicyTab, setSelectedPolicyTab] = useState<SelectedPolicyTab>(
|
||||
SelectedPolicyTab.EXISTING
|
||||
);
|
||||
const validation = agentPolicyFormValidation(newAgentPolicy);
|
||||
|
||||
const setPolicyValidation = useCallback(
|
||||
(currentTab: SelectedPolicyTab, updatedAgentPolicy: NewAgentPolicy) => {
|
||||
if (currentTab === SelectedPolicyTab.NEW) {
|
||||
if (
|
||||
!updatedAgentPolicy.name ||
|
||||
updatedAgentPolicy.name.trim() === '' ||
|
||||
!updatedAgentPolicy.namespace ||
|
||||
updatedAgentPolicy.namespace.trim() === ''
|
||||
) {
|
||||
setHasAgentPolicyError(true);
|
||||
} else {
|
||||
setHasAgentPolicyError(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
[setHasAgentPolicyError]
|
||||
);
|
||||
|
||||
const updateSelectedPolicyTab = useCallback(
|
||||
(currentTab) => {
|
||||
setSelectedPolicyTab(currentTab);
|
||||
setPolicyValidation(currentTab, newAgentPolicy);
|
||||
},
|
||||
[setSelectedPolicyTab, setPolicyValidation, newAgentPolicy]
|
||||
);
|
||||
|
||||
// Update agent policy method
|
||||
const updateAgentPolicies = useCallback(
|
||||
(updatedAgentPolicies: AgentPolicy[]) => {
|
||||
if (!isLoadingData && isEqual(updatedAgentPolicies, agentPolicies)) {
|
||||
return;
|
||||
}
|
||||
if (updatedAgentPolicies.length > 0) {
|
||||
setAgentPolicies(updatedAgentPolicies);
|
||||
updatePackagePolicy({
|
||||
policy_ids: updatedAgentPolicies.map((policy) => policy.id),
|
||||
});
|
||||
if (packageInfo) {
|
||||
setHasAgentPolicyError(false);
|
||||
}
|
||||
} else {
|
||||
setHasAgentPolicyError(true);
|
||||
setAgentPolicies([]);
|
||||
updatePackagePolicy({
|
||||
policy_ids: [],
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug('Agent policy updated', updatedAgentPolicies);
|
||||
},
|
||||
[
|
||||
packageInfo,
|
||||
agentPolicies,
|
||||
isLoadingData,
|
||||
updatePackagePolicy,
|
||||
setHasAgentPolicyError,
|
||||
setAgentPolicies,
|
||||
]
|
||||
);
|
||||
|
||||
const updateNewAgentPolicy = useCallback(
|
||||
(updatedFields: Partial<NewAgentPolicy>) => {
|
||||
const updatedAgentPolicy = {
|
||||
...newAgentPolicy,
|
||||
...updatedFields,
|
||||
};
|
||||
setNewAgentPolicy(updatedAgentPolicy);
|
||||
setPolicyValidation(selectedPolicyTab, updatedAgentPolicy);
|
||||
},
|
||||
[setNewAgentPolicy, setPolicyValidation, newAgentPolicy, selectedPolicyTab]
|
||||
);
|
||||
|
||||
const { selectedSetupTechnology } = useSetupTechnology({
|
||||
newAgentPolicy,
|
||||
updateNewAgentPolicy,
|
||||
updateAgentPolicies,
|
||||
setSelectedPolicyTab,
|
||||
packageInfo,
|
||||
});
|
||||
|
||||
const stepSelectAgentPolicy = useMemo(
|
||||
() => (
|
||||
<StepSelectHosts
|
||||
agentPolicies={agentPolicies}
|
||||
updateAgentPolicies={updateAgentPolicies}
|
||||
newAgentPolicy={newAgentPolicy}
|
||||
updateNewAgentPolicy={updateNewAgentPolicy}
|
||||
withSysMonitoring={withSysMonitoring}
|
||||
updateSysMonitoring={(newValue) => setWithSysMonitoring(newValue)}
|
||||
validation={validation}
|
||||
packageInfo={packageInfo}
|
||||
setHasAgentPolicyError={setHasAgentPolicyError}
|
||||
updateSelectedTab={updateSelectedPolicyTab}
|
||||
selectedAgentPolicyIds={existingAgentPolicies.map((policy) => policy.id)}
|
||||
initialSelectedTabIndex={1}
|
||||
/>
|
||||
),
|
||||
[
|
||||
packageInfo,
|
||||
agentPolicies,
|
||||
updateAgentPolicies,
|
||||
newAgentPolicy,
|
||||
updateNewAgentPolicy,
|
||||
validation,
|
||||
withSysMonitoring,
|
||||
updateSelectedPolicyTab,
|
||||
setHasAgentPolicyError,
|
||||
existingAgentPolicies,
|
||||
setWithSysMonitoring,
|
||||
]
|
||||
);
|
||||
|
||||
const steps: EuiStepProps[] = [
|
||||
{
|
||||
title: i18n.translate('xpack.fleet.createPackagePolicy.stepConfigurePackagePolicyTitle', {
|
||||
defaultMessage: 'Configure integration',
|
||||
}),
|
||||
'data-test-subj': 'dataCollectionSetupStep',
|
||||
children: configureStep,
|
||||
headingElement: 'h2',
|
||||
},
|
||||
];
|
||||
|
||||
if (selectedSetupTechnology !== SetupTechnology.AGENTLESS) {
|
||||
steps.push({
|
||||
title: i18n.translate('xpack.fleet.createPackagePolicy.stepSelectAgentPolicyTitle', {
|
||||
defaultMessage: 'Where to add this integration?',
|
||||
}),
|
||||
children: stepSelectAgentPolicy,
|
||||
headingElement: 'h2',
|
||||
});
|
||||
}
|
||||
|
||||
const devToolsProps = useDevToolsRequest({
|
||||
newAgentPolicy,
|
||||
packagePolicy,
|
||||
selectedPolicyTab,
|
||||
withSysMonitoring,
|
||||
packageInfo,
|
||||
packagePolicyId,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setNewAgentPolicyName(
|
||||
selectedPolicyTab === SelectedPolicyTab.NEW ? newAgentPolicy.name : undefined
|
||||
);
|
||||
}, [newAgentPolicy, selectedPolicyTab, setNewAgentPolicyName]);
|
||||
|
||||
return {
|
||||
steps,
|
||||
devToolsProps,
|
||||
createAgentPolicyIfNeeded: async () => {
|
||||
const createdAgentPolicy = await createAgentPolicyIfNeeded({
|
||||
newAgentPolicy,
|
||||
packagePolicy,
|
||||
withSysMonitoring,
|
||||
packageInfo,
|
||||
selectedPolicyTab,
|
||||
});
|
||||
return createdAgentPolicy?.id;
|
||||
},
|
||||
};
|
||||
}
|
|
@ -8,6 +8,8 @@
|
|||
import React from 'react';
|
||||
import { fireEvent, act, waitFor } from '@testing-library/react';
|
||||
|
||||
import { ExperimentalFeaturesService } from '../../../../../services';
|
||||
|
||||
import type { TestRenderer } from '../../../../../mock';
|
||||
import { createFleetTestRendererMock } from '../../../../../mock';
|
||||
|
||||
|
@ -19,6 +21,9 @@ import {
|
|||
sendUpgradePackagePolicyDryRun,
|
||||
sendUpdatePackagePolicy,
|
||||
useStartServices,
|
||||
sendCreateAgentPolicy,
|
||||
sendBulkGetAgentPolicies,
|
||||
useGetAgentPolicies,
|
||||
} from '../../../hooks';
|
||||
import { useGetOnePackagePolicy } from '../../../../integrations/hooks';
|
||||
|
||||
|
@ -29,7 +34,7 @@ type MockFn = jest.MockedFunction<any>;
|
|||
jest.mock('../../../hooks', () => {
|
||||
return {
|
||||
...jest.requireActual('../../../hooks'),
|
||||
sendGetAgentStatus: jest.fn().mockResolvedValue({ data: { results: { total: 0 } } }),
|
||||
sendGetAgentStatus: jest.fn(),
|
||||
sendUpdatePackagePolicy: jest.fn(),
|
||||
sendGetOnePackagePolicy: jest.fn(),
|
||||
sendGetOneAgentPolicy: jest.fn(),
|
||||
|
@ -125,6 +130,10 @@ jest.mock('../../../hooks', () => {
|
|||
},
|
||||
}),
|
||||
useLink: jest.fn().mockReturnValue({ getHref: jest.fn().mockReturnValue('/navigate/path') }),
|
||||
useGetAgentPolicies: jest.fn(),
|
||||
sendCreateAgentPolicy: jest.fn(),
|
||||
sendBulkGetAgentPolicies: jest.fn(),
|
||||
sendBulkInstallPackages: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -141,6 +150,7 @@ jest.mock('react-router-dom', () => ({
|
|||
useRouteMatch: jest.fn().mockReturnValue({
|
||||
params: {
|
||||
packagePolicyId: 'nginx-1',
|
||||
policyId: 'agent-policy-1',
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
@ -222,6 +232,24 @@ describe('edit package policy page', () => {
|
|||
(sendUpdatePackagePolicy as MockFn).mockResolvedValue({});
|
||||
(useStartServices().application.navigateToUrl as MockFn).mockReset();
|
||||
(useStartServices().notifications.toasts.addError as MockFn).mockReset();
|
||||
(sendGetAgentStatus as MockFn).mockResolvedValue({ data: { results: { total: 0 } } });
|
||||
(sendBulkGetAgentPolicies as MockFn).mockResolvedValue({
|
||||
data: { items: [{ id: 'agent-policy-1', name: 'Agent policy 1' }] },
|
||||
});
|
||||
(sendCreateAgentPolicy as MockFn).mockResolvedValue({
|
||||
data: { item: { id: 'agent-policy-2' } },
|
||||
});
|
||||
(useGetAgentPolicies as MockFn).mockReturnValue({
|
||||
data: {
|
||||
items: [
|
||||
{ id: 'agent-policy-1', name: 'Agent policy 1' },
|
||||
{ id: 'fleet-server-policy', name: 'Fleet Server Policy' },
|
||||
],
|
||||
},
|
||||
error: undefined,
|
||||
isLoading: false,
|
||||
resendRequest: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
it('should disable submit button on invalid form with empty package var', async () => {
|
||||
|
@ -455,4 +483,57 @@ describe('edit package policy page', () => {
|
|||
|
||||
expect(sendUpdatePackagePolicy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('modify agent policies', () => {
|
||||
beforeEach(() => {
|
||||
jest
|
||||
.spyOn(ExperimentalFeaturesService, 'get')
|
||||
.mockReturnValue({ enableReusableIntegrationPolicies: true });
|
||||
(sendGetAgentStatus as jest.MockedFunction<any>).mockResolvedValue({
|
||||
data: { results: { total: 0 } },
|
||||
});
|
||||
});
|
||||
|
||||
it('should create agent policy with sys monitoring when new hosts is selected', async () => {
|
||||
await act(async () => {
|
||||
render();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(renderResult.getByTestId('agentPolicyMultiItem')).toHaveAttribute(
|
||||
'title',
|
||||
'Agent policy 1'
|
||||
);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(renderResult.getByTestId('newHostsTab'));
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(renderResult.getByText(/Save integration/).closest('button')!);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(renderResult.getAllByText(/Save and deploy changes/)[1].closest('button')!);
|
||||
});
|
||||
expect(sendCreateAgentPolicy as jest.MockedFunction<any>).toHaveBeenCalledWith(
|
||||
{
|
||||
description: '',
|
||||
monitoring_enabled: ['logs', 'metrics'],
|
||||
name: 'Agent policy 2',
|
||||
namespace: 'default',
|
||||
inactivity_timeout: 1209600,
|
||||
is_protected: false,
|
||||
},
|
||||
{ withSysMonitoring: true }
|
||||
);
|
||||
expect(sendUpdatePackagePolicy).toHaveBeenCalledWith(
|
||||
'nginx-1',
|
||||
expect.objectContaining({
|
||||
policy_ids: ['agent-policy-1', 'agent-policy-2'],
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import React, { useState, useEffect, useCallback, useMemo, memo } from 'react';
|
||||
import { isEmpty, omit } from 'lodash';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useRouteMatch } from 'react-router-dom';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
@ -48,28 +48,28 @@ import {
|
|||
StepDefinePackagePolicy,
|
||||
} from '../create_package_policy_page/components';
|
||||
|
||||
import {
|
||||
AGENTLESS_POLICY_ID,
|
||||
HIDDEN_API_REFERENCE_PACKAGES,
|
||||
} from '../../../../../../common/constants';
|
||||
import type { PackagePolicyEditExtensionComponentProps } from '../../../types';
|
||||
import { AGENTLESS_POLICY_ID } from '../../../../../../common/constants';
|
||||
import type { AgentPolicy, PackagePolicyEditExtensionComponentProps } from '../../../types';
|
||||
import { ExperimentalFeaturesService, pkgKeyFromPackageInfo } from '../../../services';
|
||||
import { generateUpdatePackagePolicyDevToolsRequest } from '../services';
|
||||
|
||||
import {
|
||||
getInheritedNamespace,
|
||||
getRootPrivilegedDataStreams,
|
||||
isRootPrivilegesRequired,
|
||||
} from '../../../../../../common/services';
|
||||
|
||||
import { RootPrivilegesCallout } from '../create_package_policy_page/single_page_layout/root_callout';
|
||||
|
||||
import { StepsWithLessPadding } from '../create_package_policy_page/single_page_layout';
|
||||
|
||||
import { UpgradeStatusCallout } from './components';
|
||||
import { usePackagePolicyWithRelatedData, useHistoryBlock } from './hooks';
|
||||
import { getNewSecrets } from './utils';
|
||||
import { usePackagePolicySteps } from './hooks';
|
||||
|
||||
export const EditPackagePolicyPage = memo(() => {
|
||||
const {
|
||||
params: { packagePolicyId },
|
||||
params: { packagePolicyId, policyId },
|
||||
} = useRouteMatch<{ policyId: string; packagePolicyId: string }>();
|
||||
|
||||
const packagePolicy = useGetOnePackagePolicy(packagePolicyId);
|
||||
|
@ -82,6 +82,7 @@ export const EditPackagePolicyPage = memo(() => {
|
|||
return (
|
||||
<EditPackagePolicyForm
|
||||
packagePolicyId={packagePolicyId}
|
||||
policyId={policyId}
|
||||
// If an extension opts in to this `useLatestPackageVersion` flag, we want to display
|
||||
// the edit form in an "upgrade" state regardless of whether the user intended to
|
||||
// "edit" their policy or "upgrade" it. This ensures the new policy generated will be
|
||||
|
@ -95,16 +96,18 @@ export const EditPackagePolicyForm = memo<{
|
|||
packagePolicyId: string;
|
||||
forceUpgrade?: boolean;
|
||||
from?: EditPackagePolicyFrom;
|
||||
}>(({ packagePolicyId, forceUpgrade = false, from = 'edit' }) => {
|
||||
policyId?: string;
|
||||
}>(({ packagePolicyId, policyId, forceUpgrade = false, from = 'edit' }) => {
|
||||
const { application, notifications } = useStartServices();
|
||||
const {
|
||||
agents: { enabled: isFleetEnabled },
|
||||
} = useConfig();
|
||||
const { getHref } = useLink();
|
||||
const { enableReusableIntegrationPolicies } = ExperimentalFeaturesService.get();
|
||||
|
||||
const {
|
||||
// data
|
||||
agentPolicies,
|
||||
agentPolicies: existingAgentPolicies,
|
||||
isLoadingData,
|
||||
loadingError,
|
||||
packagePolicy,
|
||||
|
@ -135,15 +138,19 @@ export const EditPackagePolicyForm = memo<{
|
|||
return getNewSecrets({ packageInfo, packagePolicy });
|
||||
}, [packageInfo, packagePolicy]);
|
||||
|
||||
const policyIds = agentPolicies.map((policy) => policy.id);
|
||||
const [agentPolicies, setAgentPolicies] = useState<AgentPolicy[]>([]);
|
||||
const [isFirstLoad, setIsFirstLoad] = useState<boolean>(true);
|
||||
const [newAgentPolicyName, setNewAgentPolicyName] = useState<string | undefined>();
|
||||
|
||||
const [hasAgentPolicyError, setHasAgentPolicyError] = useState<boolean>(false);
|
||||
|
||||
// Retrieve agent count
|
||||
const [agentCount, setAgentCount] = useState<number>(0);
|
||||
useEffect(() => {
|
||||
const getAgentCount = async () => {
|
||||
let count = 0;
|
||||
for (const policyId of policyIds) {
|
||||
const { data } = await sendGetAgentStatus({ policyId });
|
||||
for (const id of packagePolicy.policy_ids) {
|
||||
const { data } = await sendGetAgentStatus({ policyId: id });
|
||||
if (data?.results.total) {
|
||||
count += data.results.total;
|
||||
}
|
||||
|
@ -151,10 +158,10 @@ export const EditPackagePolicyForm = memo<{
|
|||
setAgentCount(count);
|
||||
};
|
||||
|
||||
if (isFleetEnabled && policyIds.length > 0) {
|
||||
if (isFleetEnabled && packagePolicy.policy_ids.length > 0) {
|
||||
getAgentCount();
|
||||
}
|
||||
}, [policyIds, isFleetEnabled]);
|
||||
}, [packagePolicy.policy_ids, isFleetEnabled]);
|
||||
|
||||
const handleExtensionViewOnChange = useCallback<
|
||||
PackagePolicyEditExtensionComponentProps['onChange']
|
||||
|
@ -175,39 +182,90 @@ export const EditPackagePolicyForm = memo<{
|
|||
// if `from === 'edit'` then it links back to Policy Details
|
||||
// if `from === 'package-edit'`, or `upgrade-from-integrations-policy-list` then it links back to the Integration Policy List
|
||||
const cancelUrl = useMemo((): string => {
|
||||
if (packageInfo && policyIds.length > 0) {
|
||||
if (packageInfo && policyId) {
|
||||
return from === 'package-edit'
|
||||
? getHref('integration_details_policies', {
|
||||
pkgkey: pkgKeyFromPackageInfo(packageInfo!),
|
||||
})
|
||||
: getHref('policy_details', { policyId: policyIds[0] });
|
||||
: getHref('policy_details', { policyId });
|
||||
}
|
||||
return '/';
|
||||
}, [from, getHref, packageInfo, policyIds]);
|
||||
}, [from, getHref, packageInfo, policyId]);
|
||||
const successRedirectPath = useMemo(() => {
|
||||
if (packageInfo && policyIds.length > 0) {
|
||||
if (packageInfo && policyId) {
|
||||
return from === 'package-edit' || from === 'upgrade-from-integrations-policy-list'
|
||||
? getHref('integration_details_policies', {
|
||||
pkgkey: pkgKeyFromPackageInfo(packageInfo!),
|
||||
})
|
||||
: getHref('policy_details', { policyId: policyIds[0] });
|
||||
: getHref('policy_details', { policyId });
|
||||
}
|
||||
return '/';
|
||||
}, [from, getHref, packageInfo, policyIds]);
|
||||
}, [from, getHref, packageInfo, policyId]);
|
||||
|
||||
useHistoryBlock(isEdited);
|
||||
|
||||
useEffect(() => {
|
||||
if (existingAgentPolicies.length > 0 && isFirstLoad) {
|
||||
setIsFirstLoad(false);
|
||||
setAgentPolicies(existingAgentPolicies);
|
||||
}
|
||||
}, [existingAgentPolicies, isFirstLoad]);
|
||||
|
||||
const agentPoliciesToAdd = useMemo(
|
||||
() => [
|
||||
...agentPolicies
|
||||
.filter(
|
||||
(policy) =>
|
||||
!existingAgentPolicies.find((existingPolicy) => existingPolicy.id === policy.id)
|
||||
)
|
||||
.map((policy) => policy.name),
|
||||
...(newAgentPolicyName ? [newAgentPolicyName] : []),
|
||||
],
|
||||
[agentPolicies, existingAgentPolicies, newAgentPolicyName]
|
||||
);
|
||||
const agentPoliciesToRemove = useMemo(
|
||||
() =>
|
||||
existingAgentPolicies
|
||||
.filter(
|
||||
(existingPolicy) => !agentPolicies.find((policy) => policy.id === existingPolicy.id)
|
||||
)
|
||||
.map((policy) => policy.name),
|
||||
[agentPolicies, existingAgentPolicies]
|
||||
);
|
||||
|
||||
const onSubmit = async () => {
|
||||
if (formState === 'VALID' && hasErrors) {
|
||||
setFormState('INVALID');
|
||||
return;
|
||||
}
|
||||
if (agentCount !== 0 && !policyIds.includes(AGENTLESS_POLICY_ID) && formState !== 'CONFIRM') {
|
||||
if (
|
||||
(agentCount !== 0 || agentPoliciesToAdd.length > 0 || agentPoliciesToRemove.length > 0) &&
|
||||
!packagePolicy.policy_ids.includes(AGENTLESS_POLICY_ID) &&
|
||||
formState !== 'CONFIRM'
|
||||
) {
|
||||
setFormState('CONFIRM');
|
||||
return;
|
||||
}
|
||||
|
||||
const { error } = await savePackagePolicy();
|
||||
let newPolicyId;
|
||||
try {
|
||||
setFormState('LOADING');
|
||||
newPolicyId = await createAgentPolicyIfNeeded();
|
||||
} catch (e) {
|
||||
setFormState('VALID');
|
||||
notifications.toasts.addError(e, {
|
||||
title: i18n.translate('xpack.fleet.createAgentPolicy.errorNotificationTitle', {
|
||||
defaultMessage: 'Unable to create agent policy',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const { error } = await savePackagePolicy({
|
||||
policy_ids: newPolicyId
|
||||
? [...packagePolicy.policy_ids, newPolicyId]
|
||||
: packagePolicy.policy_ids,
|
||||
});
|
||||
if (!error) {
|
||||
setIsEdited(false);
|
||||
application.navigateToUrl(successRedirectPath);
|
||||
|
@ -311,7 +369,7 @@ export const EditPackagePolicyForm = memo<{
|
|||
<>
|
||||
{selectedTab === 0 && (
|
||||
<StepDefinePackagePolicy
|
||||
agentPolicies={agentPolicies}
|
||||
namespacePlaceholder={getInheritedNamespace(agentPolicies)}
|
||||
packageInfo={packageInfo}
|
||||
packagePolicy={packagePolicy}
|
||||
updatePackagePolicy={updatePackagePolicy}
|
||||
|
@ -384,29 +442,38 @@ export const EditPackagePolicyForm = memo<{
|
|||
</ExtensionWrapper>
|
||||
);
|
||||
|
||||
const { showDevtoolsRequest: isShowDevtoolRequestExperimentEnabled } =
|
||||
ExperimentalFeaturesService.get();
|
||||
|
||||
const showDevtoolsRequest =
|
||||
!HIDDEN_API_REFERENCE_PACKAGES.includes(packageInfo?.name ?? '') &&
|
||||
isShowDevtoolRequestExperimentEnabled;
|
||||
|
||||
const devtoolRequest = useMemo(
|
||||
() =>
|
||||
generateUpdatePackagePolicyDevToolsRequest(
|
||||
packagePolicyId,
|
||||
omit(packagePolicy, 'elasticsearch')
|
||||
),
|
||||
[packagePolicyId, packagePolicy]
|
||||
);
|
||||
const rootPrivilegedDataStreams = packageInfo ? getRootPrivilegedDataStreams(packageInfo) : [];
|
||||
|
||||
const agentPolicyBreadcrumb = useMemo(() => {
|
||||
return existingAgentPolicies.length > 0
|
||||
? existingAgentPolicies.find((policy) => policy.id === policyId) ?? existingAgentPolicies[0]
|
||||
: { name: '', id: '' };
|
||||
}, [existingAgentPolicies, policyId]);
|
||||
|
||||
const {
|
||||
steps,
|
||||
devToolsProps: { devtoolRequest, devtoolRequestDescription, showDevtoolsRequest },
|
||||
createAgentPolicyIfNeeded,
|
||||
} = usePackagePolicySteps({
|
||||
configureStep: replaceConfigurePackage || configurePackage,
|
||||
packageInfo,
|
||||
existingAgentPolicies,
|
||||
setHasAgentPolicyError,
|
||||
updatePackagePolicy,
|
||||
agentPolicies,
|
||||
setAgentPolicies,
|
||||
isLoadingData,
|
||||
packagePolicy,
|
||||
packagePolicyId,
|
||||
setNewAgentPolicyName,
|
||||
});
|
||||
|
||||
return (
|
||||
<CreatePackagePolicySinglePageLayout {...layoutProps} data-test-subj="editPackagePolicy">
|
||||
<EuiErrorBoundary>
|
||||
{isLoadingData ? (
|
||||
<Loading />
|
||||
) : loadingError || isEmpty(agentPolicies) || !packageInfo ? (
|
||||
) : loadingError || isEmpty(existingAgentPolicies) || !packageInfo ? (
|
||||
<ErrorComponent
|
||||
title={
|
||||
<FormattedMessage
|
||||
|
@ -424,12 +491,12 @@ export const EditPackagePolicyForm = memo<{
|
|||
) : (
|
||||
<>
|
||||
<Breadcrumb
|
||||
agentPolicyName={agentPolicies[0].name}
|
||||
agentPolicyName={agentPolicyBreadcrumb.name}
|
||||
from={from}
|
||||
packagePolicyName={packagePolicy.name}
|
||||
pkgkey={pkgKeyFromPackageInfo(packageInfo)}
|
||||
pkgTitle={packageInfo.title}
|
||||
policyId={policyIds[0]}
|
||||
policyId={agentPolicyBreadcrumb.id}
|
||||
/>
|
||||
{formState === 'CONFIRM' && (
|
||||
<ConfirmDeployAgentPolicyModal
|
||||
|
@ -437,6 +504,8 @@ export const EditPackagePolicyForm = memo<{
|
|||
agentPolicies={agentPolicies}
|
||||
onConfirm={onSubmit}
|
||||
onCancel={() => setFormState('VALID')}
|
||||
agentPoliciesToAdd={agentPoliciesToAdd}
|
||||
agentPoliciesToRemove={agentPoliciesToRemove}
|
||||
/>
|
||||
)}
|
||||
{packageInfo && isRootPrivilegesRequired(packageInfo) ? (
|
||||
|
@ -451,14 +520,20 @@ export const EditPackagePolicyForm = memo<{
|
|||
<EuiSpacer size="xxl" />
|
||||
</>
|
||||
)}
|
||||
{replaceConfigurePackage || configurePackage}
|
||||
{enableReusableIntegrationPolicies ? (
|
||||
<StepsWithLessPadding steps={steps} />
|
||||
) : (
|
||||
replaceConfigurePackage || configurePackage
|
||||
)}
|
||||
{/* Extra space to accomodate the EuiBottomBar height */}
|
||||
<EuiSpacer size="xxl" />
|
||||
<EuiSpacer size="xxl" />
|
||||
<EuiBottomBar>
|
||||
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
{agentPolicies && packageInfo && formState === 'INVALID' ? (
|
||||
{agentPolicies &&
|
||||
packageInfo &&
|
||||
(formState === 'INVALID' || hasAgentPolicyError) ? (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.createPackagePolicy.errorOnSaveText"
|
||||
defaultMessage="Your integration policy has errors. Please fix them before saving."
|
||||
|
@ -482,13 +557,8 @@ export const EditPackagePolicyForm = memo<{
|
|||
btnProps={{
|
||||
color: 'text',
|
||||
}}
|
||||
description={i18n.translate(
|
||||
'xpack.fleet.editPackagePolicy.devtoolsRequestDescription',
|
||||
{
|
||||
defaultMessage: 'This Kibana request updates a package policy.',
|
||||
}
|
||||
)}
|
||||
request={devtoolRequest}
|
||||
description={devtoolRequestDescription}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
|
@ -500,6 +570,8 @@ export const EditPackagePolicyForm = memo<{
|
|||
isDisabled={
|
||||
!canWriteIntegrationPolicies ||
|
||||
formState !== 'VALID' ||
|
||||
hasAgentPolicyError ||
|
||||
!validationResults ||
|
||||
(!isEdited && !isUpgrade)
|
||||
}
|
||||
tooltip={
|
||||
|
|
|
@ -14,7 +14,7 @@ import { EditPackagePolicyForm } from '../edit_package_policy_page';
|
|||
|
||||
export const UpgradePackagePolicyPage = memo(() => {
|
||||
const {
|
||||
params: { packagePolicyId },
|
||||
params: { packagePolicyId, policyId },
|
||||
} = useRouteMatch<{ policyId: string; packagePolicyId: string }>();
|
||||
const { search } = useLocation();
|
||||
|
||||
|
@ -30,5 +30,12 @@ export const UpgradePackagePolicyPage = memo(() => {
|
|||
from = 'upgrade-from-integrations-policy-list';
|
||||
}
|
||||
|
||||
return <EditPackagePolicyForm packagePolicyId={packagePolicyId} from={from} forceUpgrade />;
|
||||
return (
|
||||
<EditPackagePolicyForm
|
||||
packagePolicyId={packagePolicyId}
|
||||
policyId={policyId}
|
||||
from={from}
|
||||
forceUpgrade
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue