[UII] Allow to create integration policy with no agent policies (#201051)

## Summary

Resolves #198165. This PR fixes an issue where `policy_ids` array was
not able to be cleared behind the scenes to create an orphaned
integration policy, even when cluster is able to use the multiple agent
policies feature.
This commit is contained in:
Jen Huang 2024-11-21 16:57:06 -08:00 committed by GitHub
parent 0b92c1d072
commit 3952dea396
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 80 additions and 56 deletions

View file

@ -4,36 +4,46 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { EXISTING_HOSTS_TAB } from '../screens/fleet';
import {
ADD_INTEGRATION_POLICY_BTN,
CREATE_PACKAGE_POLICY_SAVE_BTN,
POLICY_EDITOR,
} from '../screens/integrations';
import { CONFIRM_MODAL } from '../screens/navigation';
import { login } from '../tasks/login';
describe('Edit package policy', () => {
const policyConfig = {
id: 'policy-1',
name: 'fleet_server-1',
namespace: 'default',
package: { name: 'fleet_server', title: 'Fleet Server', version: '1.1.0' },
enabled: true,
policy_id: 'fleet-server-policy',
policy_ids: ['fleet-server-policy'],
output_id: 'fleet-default-output',
inputs: [
{
type: 'fleet-server',
policy_template: 'fleet_server',
enabled: true,
streams: [],
vars: {
host: { value: ['0.0.0.0'], type: 'text' },
port: { value: [8220], type: 'integer' },
max_connections: { type: 'integer' },
custom: { value: '', type: 'yaml' },
},
compiled_input: { server: { port: 8220, host: '0.0.0.0' } },
},
],
};
describe('Package policy', () => {
beforeEach(() => {
login();
});
it('should edit package policy', () => {
const policyConfig = {
id: 'policy-1',
name: 'fleet_server-1',
namespace: 'default',
package: { name: 'fleet_server', title: 'Fleet Server', version: '1.1.0' },
enabled: true,
policy_id: 'fleet-server-policy',
policy_ids: ['fleet-server-policy'],
output_id: 'fleet-default-output',
inputs: [
{
type: 'fleet-server',
policy_template: 'fleet_server',
enabled: true,
streams: [],
vars: {
host: { value: ['0.0.0.0'], type: 'text' },
port: { value: [8220], type: 'integer' },
max_connections: { type: 'integer' },
custom: { value: '', type: 'yaml' },
},
compiled_input: { server: { port: 8220, host: '0.0.0.0' } },
},
],
};
cy.intercept('/api/fleet/package_policies/policy-1', {
item: policyConfig,
@ -111,9 +121,7 @@ describe('Edit package policy', () => {
status: 'not_installed',
},
});
});
it('should edit package policy', () => {
cy.visit('/app/fleet/policies/fleet-server-policy/edit-integration/policy-1');
cy.getBySel('packagePolicyDescriptionInput').clear().type('desc');
@ -128,4 +136,27 @@ describe('Edit package policy', () => {
expect(interception.request.body.description).to.equal('desc');
});
});
it('should create a new orphaned package policy', () => {
cy.visit('/app/integrations/detail/system');
cy.getBySel(ADD_INTEGRATION_POLICY_BTN).click();
cy.getBySel(EXISTING_HOSTS_TAB).click();
cy.getBySel(POLICY_EDITOR.AGENT_POLICY_SELECT).should('exist');
cy.getBySel(POLICY_EDITOR.AGENT_POLICY_CLEAR).should('not.exist');
cy.getBySel(POLICY_EDITOR.POLICY_NAME_INPUT).clear().type('system-orphaned-test');
cy.intercept({
method: 'POST',
url: '/api/fleet/package_policies',
}).as('createPackagePolicy');
cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN).should('be.enabled').click();
cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON).click();
cy.wait('@createPackagePolicy').then((interception) => {
expect(interception.request.body.name).to.equal('system-orphaned-test');
expect(interception.request.body.policy_id).to.equal(undefined);
expect(interception.request.body.policy_ids).to.deep.equal([]);
});
});
});

View file

@ -39,6 +39,7 @@ export const POLICY_EDITOR = {
POLICY_NAME_INPUT: 'packagePolicyNameInput',
DATASET_SELECT: 'datasetComboBox',
AGENT_POLICY_SELECT: 'agentPolicyMultiSelect',
AGENT_POLICY_CLEAR: 'comboBoxClearButton',
INSPECT_PIPELINES_BTN: 'datastreamInspectPipelineBtn',
EDIT_MAPPINGS_BTN: 'datastreamEditMappingsBtn',
CREATE_MAPPINGS_BTN: 'datastreamAddCustomComponentTemplateBtn',

View file

@ -123,7 +123,8 @@ describe('StepSelectHosts', () => {
await waitFor(() => {
expect(renderResult.getByText('New agent policy name')).toBeInTheDocument();
});
expect(renderResult.queryByRole('tablist')).not.toBeInTheDocument();
expect(renderResult.queryByRole('tablist')).toBeInTheDocument();
expect(renderResult.getByText('Create agent policy')).toBeInTheDocument();
});
it('should display tabs with New hosts selected when agent policies exist', async () => {

View file

@ -105,7 +105,7 @@ export const StepSelectHosts: React.FunctionComponent<Props> = ({
const handleOnTabClick = (tab: EuiTabbedContentTab) =>
updateSelectedTab(tab.id as SelectedPolicyTab);
return existingAgentPolicies.length > 0 ? (
return (
<StyledEuiTabbedContent
initialSelectedTab={
initialSelectedTabIndex
@ -117,13 +117,5 @@ export const StepSelectHosts: React.FunctionComponent<Props> = ({
tabs={tabs}
onTabClick={handleOnTabClick}
/>
) : (
<AgentPolicyIntegrationForm
agentPolicy={newAgentPolicy}
updateAgentPolicy={updateNewAgentPolicy}
withSysMonitoring={withSysMonitoring}
updateSysMonitoring={updateSysMonitoring}
validation={validation}
/>
);
};

View file

@ -280,7 +280,7 @@ export function useOnSubmit({
useEffect(() => {
if (
agentPolicies.length > 0 &&
(canUseMultipleAgentPolicies || agentPolicies.length > 0) &&
!isEqual(
agentPolicies.map((policy) => policy.id),
packagePolicy.policy_ids
@ -290,7 +290,7 @@ export function useOnSubmit({
policy_ids: agentPolicies.map((policy) => policy.id),
});
}
}, [packagePolicy, agentPolicies, updatePackagePolicy]);
}, [packagePolicy, agentPolicies, updatePackagePolicy, canUseMultipleAgentPolicies]);
const onSaveNavigate = useOnSaveNavigate({
packagePolicy,

View file

@ -124,8 +124,9 @@ describe('StepEditHosts', () => {
render();
expect(renderResult.getByText('New agent policy name')).toBeInTheDocument();
expect(renderResult.queryByRole('tablist')).not.toBeInTheDocument();
expect(renderResult.getByText('For existing hosts:')).toBeInTheDocument();
expect(renderResult.getByText('For a new host:')).toBeInTheDocument();
});
it('should display new policy button and existing policies when agent policies exist', () => {

View file

@ -60,7 +60,7 @@ export const StepEditHosts: React.FunctionComponent<Props> = ({
}
}, [existingAgentPolicies.length]); // eslint-disable-line react-hooks/exhaustive-deps
return existingAgentPolicies.length > 0 ? (
return (
<EuiFlexGroup direction="column" alignItems="flexStart">
<EuiFlexItem>
<EuiTitle size="xs">
@ -141,13 +141,5 @@ export const StepEditHosts: React.FunctionComponent<Props> = ({
</>
)}
</EuiFlexGroup>
) : (
<AgentPolicyIntegrationForm
agentPolicy={newAgentPolicy}
updateAgentPolicy={updateNewAgentPolicy}
withSysMonitoring={withSysMonitoring}
updateSysMonitoring={updateSysMonitoring}
validation={validation}
/>
);
};

View file

@ -18,6 +18,7 @@ import type {
UpdatePackagePolicy,
UpdateAgentPolicyRequest,
} from '../../../types';
import { canUseMultipleAgentPolicies } from '../../../hooks';
function generateKibanaDevToolsRequest(method: string, path: string, body: any) {
return `${method} kbn:${path}\n${JSON.stringify(body, null, 2)}\n`;
@ -49,9 +50,13 @@ export function generateCreateAgentPolicyDevToolsRequest(
export function generateCreatePackagePolicyDevToolsRequest(
packagePolicy: NewPackagePolicy & { force?: boolean }
) {
const canHaveNoAgentPolicies = canUseMultipleAgentPolicies();
return generateKibanaDevToolsRequest('POST', packagePolicyRouteService.getCreatePath(), {
policy_ids:
packagePolicy.policy_ids.length > 0 ? packagePolicy.policy_ids : ['<agent_policy_id>'],
packagePolicy.policy_ids.length > 0 || canHaveNoAgentPolicies
? packagePolicy.policy_ids
: ['<agent_policy_id>'],
package: formatPackage(packagePolicy.package),
...omit(packagePolicy, 'policy_ids', 'package', 'enabled'),
inputs: formatInputs(packagePolicy.inputs),

View file

@ -8,15 +8,16 @@
import { LICENCE_FOR_MULTIPLE_AGENT_POLICIES } from '../../common/constants';
import { ExperimentalFeaturesService } from '../services';
import { useLicense } from './use_license';
import { licenseService } from './use_license';
export function useMultipleAgentPolicies() {
const licenseService = useLicense();
return { canUseMultipleAgentPolicies: canUseMultipleAgentPolicies() };
}
export function canUseMultipleAgentPolicies() {
const { enableReusableIntegrationPolicies } = ExperimentalFeaturesService.get();
const hasEnterpriseLicence = licenseService.hasAtLeast(LICENCE_FOR_MULTIPLE_AGENT_POLICIES);
const canUseMultipleAgentPolicies = enableReusableIntegrationPolicies && hasEnterpriseLicence;
return { canUseMultipleAgentPolicies };
return Boolean(enableReusableIntegrationPolicies && hasEnterpriseLicence);
}