mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Fleet] remove enabled feature flags (#206448)
## Summary Closes https://github.com/elastic/kibana/issues/190844 Removed feature flags enabled by default, deleted conditionals where not needed.
This commit is contained in:
parent
62308641bf
commit
21493f7990
59 changed files with 686 additions and 1292 deletions
|
@ -8,25 +8,8 @@
|
|||
export type ExperimentalFeatures = typeof allowedExperimentalValues;
|
||||
|
||||
const _allowedExperimentalValues = {
|
||||
createPackagePolicyMultiPageLayout: true,
|
||||
packageVerification: true,
|
||||
diagnosticFileUploadEnabled: true,
|
||||
displayAgentMetrics: true,
|
||||
showIntegrationsSubcategories: true,
|
||||
agentFqdnMode: true,
|
||||
showExperimentalShipperOptions: false,
|
||||
agentTamperProtectionEnabled: true,
|
||||
secretsStorage: true,
|
||||
kafkaOutput: true,
|
||||
outputSecretsStorage: true,
|
||||
remoteESOutput: true,
|
||||
enableStrictKQLValidation: true,
|
||||
subfeaturePrivileges: true,
|
||||
advancedPolicySettings: true,
|
||||
useSpaceAwareness: false,
|
||||
enableReusableIntegrationPolicies: true,
|
||||
asyncDeployPolicies: true,
|
||||
enableExportCSV: true,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,12 +4,12 @@ Fleet is in the process to become space aware. This means that different Fleet o
|
|||
|
||||
## How to enable the feature
|
||||
|
||||
The feature is behind a feature flag `useSpaceAwareness`. If you are testing space awareness you could also be interested in testing `subfeaturePrivileges` at the same time which enables granular privileges to different parts of Fleet.
|
||||
The feature is behind a feature flag `useSpaceAwareness`.
|
||||
|
||||
In your `kibana.yml` config
|
||||
|
||||
```yaml
|
||||
xpack.fleet.enableExperimental: ['useSpaceAwareness', 'subfeaturePrivileges']
|
||||
xpack.fleet.enableExperimental: ['useSpaceAwareness']
|
||||
```
|
||||
|
||||
After the feature flag is enabled you will have to do another step to opt-in for the feature, that call will migrate the current space agnostic saved objects to new space aware saved objects.
|
||||
|
|
|
@ -60,7 +60,6 @@ import { EnrollmentTokenListPage } from './sections/agents/enrollment_token_list
|
|||
import { UninstallTokenListPage } from './sections/agents/uninstall_token_list_page';
|
||||
import { SettingsApp } from './sections/settings';
|
||||
import { DebugPage } from './sections/debug';
|
||||
import { ExperimentalFeaturesService } from './services';
|
||||
import { ErrorLayout, PermissionsError } from './layouts';
|
||||
|
||||
const FEEDBACK_URL = 'https://ela.st/fleet-feedback';
|
||||
|
@ -307,8 +306,6 @@ export const AppRoutes = memo(
|
|||
const flyoutContext = useFlyoutContext();
|
||||
const fleetStatus = useFleetStatus();
|
||||
|
||||
const { agentTamperProtectionEnabled } = ExperimentalFeaturesService.get();
|
||||
|
||||
const authz = useAuthz();
|
||||
|
||||
return (
|
||||
|
@ -364,21 +361,19 @@ export const AppRoutes = memo(
|
|||
</AppLayout>
|
||||
)}
|
||||
</Route>
|
||||
{agentTamperProtectionEnabled && (
|
||||
<Route path={FLEET_ROUTING_PATHS.uninstall_tokens}>
|
||||
{authz.fleet.allAgents ? (
|
||||
<AppLayout setHeaderActionMenu={setHeaderActionMenu}>
|
||||
<UninstallTokenListPage />
|
||||
</AppLayout>
|
||||
) : (
|
||||
<AppLayout setHeaderActionMenu={setHeaderActionMenu}>
|
||||
<ErrorLayout isAddIntegrationsPath={false}>
|
||||
<PermissionsError error="MISSING_PRIVILEGES" requiredFleetRole="Agents All" />
|
||||
</ErrorLayout>
|
||||
</AppLayout>
|
||||
)}
|
||||
</Route>
|
||||
)}
|
||||
<Route path={FLEET_ROUTING_PATHS.uninstall_tokens}>
|
||||
{authz.fleet.allAgents ? (
|
||||
<AppLayout setHeaderActionMenu={setHeaderActionMenu}>
|
||||
<UninstallTokenListPage />
|
||||
</AppLayout>
|
||||
) : (
|
||||
<AppLayout setHeaderActionMenu={setHeaderActionMenu}>
|
||||
<ErrorLayout isAddIntegrationsPath={false}>
|
||||
<PermissionsError error="MISSING_PRIVILEGES" requiredFleetRole="Agents All" />
|
||||
</ErrorLayout>
|
||||
</AppLayout>
|
||||
)}
|
||||
</Route>
|
||||
<Route path={FLEET_ROUTING_PATHS.data_streams}>
|
||||
<AppLayout setHeaderActionMenu={setHeaderActionMenu}>
|
||||
<DataStreamApp />
|
||||
|
|
|
@ -14,8 +14,6 @@ import type { Section } from '../../sections';
|
|||
import { useLink, useConfig, useAuthz, useStartServices } from '../../hooks';
|
||||
import { WithHeaderLayout } from '../../../../layouts';
|
||||
|
||||
import { ExperimentalFeaturesService } from '../../services';
|
||||
|
||||
import { DefaultPageTitle } from './default_page_title';
|
||||
|
||||
interface Props {
|
||||
|
@ -32,8 +30,6 @@ export const DefaultLayout: React.FunctionComponent<Props> = ({
|
|||
const { getHref } = useLink();
|
||||
const { agents } = useConfig();
|
||||
const authz = useAuthz();
|
||||
const { agentTamperProtectionEnabled, subfeaturePrivileges } = ExperimentalFeaturesService.get();
|
||||
|
||||
const { docLinks } = useStartServices();
|
||||
const granularPrivilegesCallout = useDismissableTour('GRANULAR_PRIVILEGES');
|
||||
|
||||
|
@ -83,7 +79,7 @@ export const DefaultLayout: React.FunctionComponent<Props> = ({
|
|||
isSelected: section === 'uninstall_tokens',
|
||||
href: getHref('uninstall_tokens'),
|
||||
'data-test-subj': 'fleet-uninstall-tokens-tab',
|
||||
isHidden: !authz.fleet.allAgents || !agentTamperProtectionEnabled, // needed only for agentTamperProtectionEnabled feature flag
|
||||
isHidden: !authz.fleet.allAgents,
|
||||
},
|
||||
{
|
||||
name: (
|
||||
|
@ -115,7 +111,7 @@ export const DefaultLayout: React.FunctionComponent<Props> = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
{!subfeaturePrivileges || !authz.fleet.all || granularPrivilegesCallout.isHidden ? null : (
|
||||
{!authz.fleet.all || granularPrivilegesCallout.isHidden ? null : (
|
||||
<EuiCallOut
|
||||
size="s"
|
||||
iconType="cheer"
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
} from '../../../components';
|
||||
import { FLEET_SERVER_PACKAGE } from '../../../constants';
|
||||
|
||||
import { policyHasFleetServer, ExperimentalFeaturesService } from '../../../services';
|
||||
import { policyHasFleetServer } from '../../../services';
|
||||
|
||||
import { AgentUpgradeAgentModal } from '../../agents/components';
|
||||
|
||||
|
@ -50,8 +50,6 @@ export const AgentPolicyActionMenu = memo<{
|
|||
useState<boolean>(false);
|
||||
const [isUpgradeAgentsModalOpen, setIsUpgradeAgentsModalOpen] = useState<boolean>(false);
|
||||
|
||||
const { agentTamperProtectionEnabled } = ExperimentalFeaturesService.get();
|
||||
|
||||
const isFleetServerPolicy = useMemo(
|
||||
() =>
|
||||
agentPolicy.package_policies?.some(
|
||||
|
@ -228,7 +226,6 @@ export const AgentPolicyActionMenu = memo<{
|
|||
|
||||
if (
|
||||
authz.fleet.allAgents &&
|
||||
agentTamperProtectionEnabled &&
|
||||
!agentPolicy?.is_managed &&
|
||||
!agentPolicy?.supports_agentless
|
||||
) {
|
||||
|
|
|
@ -53,7 +53,6 @@ import { UninstallCommandFlyout } from '../../../../../../components';
|
|||
|
||||
import type { ValidationResults } from '../agent_policy_validation';
|
||||
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
import { useAgentPolicyFormContext } from '../agent_policy_form';
|
||||
import { policyHasEndpointSecurity as hasElasticDefend } from '../../../../../../../common/services';
|
||||
|
||||
|
@ -121,7 +120,6 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =
|
|||
// agent monitoring checkbox group can appear multiple times in the DOM, ids have to be unique to work correctly
|
||||
const monitoringCheckboxIdSuffix = Date.now();
|
||||
|
||||
const { agentTamperProtectionEnabled } = ExperimentalFeaturesService.get();
|
||||
const licenseService = useLicense();
|
||||
const [isUninstallCommandFlyoutOpen, setIsUninstallCommandFlyoutOpen] = useState(false);
|
||||
const policyHasElasticDefend = useMemo(() => hasElasticDefend(agentPolicy), [agentPolicy]);
|
||||
|
@ -227,12 +225,7 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =
|
|||
);
|
||||
|
||||
const AgentTamperProtectionSection = useMemo(() => {
|
||||
if (
|
||||
agentTamperProtectionEnabled &&
|
||||
licenseService.isPlatinum() &&
|
||||
!agentPolicy.is_managed &&
|
||||
!agentPolicy.supports_agentless
|
||||
) {
|
||||
if (licenseService.isPlatinum() && !agentPolicy.is_managed && !agentPolicy.supports_agentless) {
|
||||
if (AgentTamperProtectionWrapper) {
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
|
@ -245,7 +238,6 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =
|
|||
return AgentTamperProtectionSectionContent;
|
||||
}
|
||||
}, [
|
||||
agentTamperProtectionEnabled,
|
||||
licenseService,
|
||||
agentPolicy.is_managed,
|
||||
AgentTamperProtectionWrapper,
|
||||
|
|
|
@ -23,8 +23,6 @@ import { useAuthz } from '../../../../../hooks';
|
|||
|
||||
import { ConfiguredSettings } from '../../../components/form_settings';
|
||||
|
||||
import { ExperimentalFeaturesService } from '../../../../../services';
|
||||
|
||||
import { AgentPolicyAdvancedOptionsContent } from './agent_policy_advanced_fields';
|
||||
import { AgentPolicyGeneralFields } from './agent_policy_general_fields';
|
||||
import { AgentPolicyFormSystemMonitoringCheckbox } from './agent_policy_system_monitoring_field';
|
||||
|
@ -74,8 +72,6 @@ export const AgentPolicyForm: React.FunctionComponent<Props> = ({
|
|||
const authz = useAuthz();
|
||||
const isDisabled = !authz.fleet.allAgentPolicies;
|
||||
|
||||
const { advancedPolicySettings } = ExperimentalFeaturesService.get();
|
||||
|
||||
const generalSettingsWrapper = (children: JSX.Element[]) => (
|
||||
<EuiDescribedFormGroup
|
||||
fullWidth
|
||||
|
@ -152,37 +148,6 @@ export const AgentPolicyForm: React.FunctionComponent<Props> = ({
|
|||
updateAgentPolicy={updateAgentPolicy}
|
||||
validation={validation}
|
||||
/>
|
||||
|
||||
{advancedPolicySettings ? (
|
||||
<>
|
||||
<EuiSpacer size="xl" />
|
||||
|
||||
<EuiTitle>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentPolicyForm.advancedSettingsTitle"
|
||||
defaultMessage="Advanced settings"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
<ConfiguredSettings
|
||||
configuredSettings={AGENT_POLICY_ADVANCED_SETTINGS}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</StyledEuiAccordion>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<AgentPolicyAdvancedOptionsContent
|
||||
agentPolicy={agentPolicy}
|
||||
updateAgentPolicy={updateAgentPolicy}
|
||||
validation={validation}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
{advancedPolicySettings ? (
|
||||
<>
|
||||
<EuiSpacer size="xl" />
|
||||
|
||||
|
@ -197,10 +162,36 @@ export const AgentPolicyForm: React.FunctionComponent<Props> = ({
|
|||
<EuiSpacer size="m" />
|
||||
<ConfiguredSettings
|
||||
configuredSettings={AGENT_POLICY_ADVANCED_SETTINGS}
|
||||
disabled={isDisabled || !!agentPolicy?.supports_agentless}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</StyledEuiAccordion>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<AgentPolicyAdvancedOptionsContent
|
||||
agentPolicy={agentPolicy}
|
||||
updateAgentPolicy={updateAgentPolicy}
|
||||
validation={validation}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
<>
|
||||
<EuiSpacer size="xl" />
|
||||
|
||||
<EuiTitle>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentPolicyForm.advancedSettingsTitle"
|
||||
defaultMessage="Advanced settings"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
<ConfiguredSettings
|
||||
configuredSettings={AGENT_POLICY_ADVANCED_SETTINGS}
|
||||
disabled={isDisabled || !!agentPolicy?.supports_agentless}
|
||||
/>
|
||||
</>
|
||||
<EuiSpacer size="xl" />
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -43,9 +43,7 @@ function renderActions({ agent, agentPolicy }: { agent: Agent; agentPolicy?: Age
|
|||
|
||||
describe('AgentDetailsActionMenu', () => {
|
||||
beforeEach(() => {
|
||||
mockedExperimentalFeaturesService.get.mockReturnValue({
|
||||
diagnosticFileUploadEnabled: true,
|
||||
} as any);
|
||||
mockedExperimentalFeaturesService.get.mockReturnValue({} as any);
|
||||
mockedUseAuthz.mockReturnValue({
|
||||
fleet: {
|
||||
readAgents: true,
|
||||
|
@ -72,17 +70,6 @@ describe('AgentDetailsActionMenu', () => {
|
|||
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: {
|
||||
|
|
|
@ -23,7 +23,6 @@ import {
|
|||
import { useAgentRefresh } from '../hooks';
|
||||
import { isAgentUpgradeable, policyHasFleetServer } from '../../../../services';
|
||||
import { AgentRequestDiagnosticsModal } from '../../components/agent_request_diagnostics_modal';
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
|
||||
import { AgentDetailsJsonFlyout } from './agent_details_json_flyout';
|
||||
|
||||
|
@ -53,7 +52,6 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
|
|||
);
|
||||
|
||||
const hasFleetServer = agentPolicy && policyHasFleetServer(agentPolicy);
|
||||
const { diagnosticFileUploadEnabled } = ExperimentalFeaturesService.get();
|
||||
|
||||
const onClose = useMemo(() => {
|
||||
if (onCancelReassign) {
|
||||
|
@ -126,7 +124,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
|
|||
defaultMessage="View agent JSON"
|
||||
/>
|
||||
</EuiContextMenuItem>,
|
||||
...(authz.fleet.readAgents && diagnosticFileUploadEnabled
|
||||
...(authz.fleet.readAgents
|
||||
? [
|
||||
<EuiContextMenuItem
|
||||
icon="download"
|
||||
|
|
|
@ -23,7 +23,7 @@ import { FormattedMessage, FormattedRelative } from '@kbn/i18n-react';
|
|||
|
||||
import type { Agent, AgentPolicy } from '../../../../../types';
|
||||
import { useAgentVersion, useGetInfoOutputsForPolicy } from '../../../../../hooks';
|
||||
import { ExperimentalFeaturesService, isAgentUpgradeable } from '../../../../../services';
|
||||
import { isAgentUpgradeable } from '../../../../../services';
|
||||
import { AgentPolicySummaryLine } from '../../../../../components';
|
||||
import { AgentHealth } from '../../../components';
|
||||
import { Tags } from '../../../components/tags';
|
||||
|
@ -42,7 +42,6 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{
|
|||
agentPolicy?: AgentPolicy;
|
||||
}> = memo(({ agent, agentPolicy }) => {
|
||||
const latestAgentVersion = useAgentVersion();
|
||||
const { displayAgentMetrics } = ExperimentalFeaturesService.get();
|
||||
|
||||
const outputRes = useGetInfoOutputsForPolicy(agentPolicy?.id);
|
||||
const outputs = outputRes?.data?.item;
|
||||
|
@ -55,84 +54,82 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{
|
|||
gutterSize="m"
|
||||
data-test-subj="agentDetailsOverviewSection"
|
||||
>
|
||||
{displayAgentMetrics && (
|
||||
<EuiFlexGroup>
|
||||
<FlexItemWithMinWidth grow={5}>
|
||||
<EuiFlexGroup direction="column" gutterSize="m">
|
||||
{[
|
||||
{
|
||||
title: (
|
||||
<EuiToolTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentDetails.cpuTooltip"
|
||||
defaultMessage="Average CPU usage in the last 5 minutes"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentDetails.cpuTitle"
|
||||
defaultMessage="CPU"
|
||||
/>
|
||||
|
||||
<EuiIcon type="iInCircle" />
|
||||
</span>
|
||||
<EuiFlexGroup>
|
||||
<FlexItemWithMinWidth grow={5}>
|
||||
<EuiFlexGroup direction="column" gutterSize="m">
|
||||
{[
|
||||
{
|
||||
title: (
|
||||
<EuiToolTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentDetails.cpuTooltip"
|
||||
defaultMessage="Average CPU usage in the last 5 minutes"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentDetails.cpuTitle"
|
||||
defaultMessage="CPU"
|
||||
/>
|
||||
|
||||
<EuiIcon type="iInCircle" />
|
||||
</span>
|
||||
</EuiToolTip>
|
||||
),
|
||||
description: formatAgentCPU(agent.metrics, agentPolicy),
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<EuiToolTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentDetails.memoryTooltip"
|
||||
defaultMessage="Average memory usage in the last 5 minutes"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentDetails.memoryTitle"
|
||||
defaultMessage="Memory"
|
||||
/>
|
||||
|
||||
<EuiIcon type="iInCircle" />
|
||||
</span>
|
||||
</EuiToolTip>
|
||||
),
|
||||
description: formatAgentMemory(agent.metrics, agentPolicy),
|
||||
},
|
||||
].map(({ title, description }) => {
|
||||
const tooltip =
|
||||
typeof description === 'string' && description.length > 20 ? description : '';
|
||||
return (
|
||||
<EuiFlexGroup>
|
||||
<FlexItemWithMinWidth grow={8}>
|
||||
<EuiDescriptionListTitle>{title}</EuiDescriptionListTitle>
|
||||
</FlexItemWithMinWidth>
|
||||
<FlexItemWithMinWidth grow={4}>
|
||||
<EuiToolTip position="top" content={tooltip}>
|
||||
<EuiDescriptionListDescription className="eui-textTruncate">
|
||||
{description}
|
||||
</EuiDescriptionListDescription>
|
||||
</EuiToolTip>
|
||||
),
|
||||
description: formatAgentCPU(agent.metrics, agentPolicy),
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<EuiToolTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentDetails.memoryTooltip"
|
||||
defaultMessage="Average memory usage in the last 5 minutes"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentDetails.memoryTitle"
|
||||
defaultMessage="Memory"
|
||||
/>
|
||||
|
||||
<EuiIcon type="iInCircle" />
|
||||
</span>
|
||||
</EuiToolTip>
|
||||
),
|
||||
description: formatAgentMemory(agent.metrics, agentPolicy),
|
||||
},
|
||||
].map(({ title, description }) => {
|
||||
const tooltip =
|
||||
typeof description === 'string' && description.length > 20 ? description : '';
|
||||
return (
|
||||
<EuiFlexGroup>
|
||||
<FlexItemWithMinWidth grow={8}>
|
||||
<EuiDescriptionListTitle>{title}</EuiDescriptionListTitle>
|
||||
</FlexItemWithMinWidth>
|
||||
<FlexItemWithMinWidth grow={4}>
|
||||
<EuiToolTip position="top" content={tooltip}>
|
||||
<EuiDescriptionListDescription className="eui-textTruncate">
|
||||
{description}
|
||||
</EuiDescriptionListDescription>
|
||||
</EuiToolTip>
|
||||
</FlexItemWithMinWidth>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
})}
|
||||
</EuiFlexGroup>
|
||||
</FlexItemWithMinWidth>
|
||||
<FlexItemWithMinWidth grow={5}>
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<AgentDashboardLink agent={agent} agentPolicy={agentPolicy} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</FlexItemWithMinWidth>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
</FlexItemWithMinWidth>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
})}
|
||||
</EuiFlexGroup>
|
||||
</FlexItemWithMinWidth>
|
||||
<FlexItemWithMinWidth grow={5}>
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<AgentDashboardLink agent={agent} agentPolicy={agentPolicy} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</FlexItemWithMinWidth>
|
||||
</EuiFlexGroup>
|
||||
{[
|
||||
{
|
||||
title: i18n.translate('xpack.fleet.agentDetails.statusLabel', {
|
||||
|
|
|
@ -26,8 +26,6 @@ import {
|
|||
} from '../../../hooks';
|
||||
import { WithHeaderLayout } from '../../../layouts';
|
||||
|
||||
import { ExperimentalFeaturesService } from '../../../services';
|
||||
|
||||
import { AgentRefreshContext } from './hooks';
|
||||
import {
|
||||
AgentLogs,
|
||||
|
@ -41,7 +39,6 @@ export const AgentDetailsPage: React.FunctionComponent = () => {
|
|||
params: { agentId, tabId = '' },
|
||||
} = useRouteMatch<{ agentId: string; tabId?: string }>();
|
||||
const { getHref } = useLink();
|
||||
const { displayAgentMetrics } = ExperimentalFeaturesService.get();
|
||||
const {
|
||||
isLoading,
|
||||
isInitialRequest,
|
||||
|
@ -51,7 +48,7 @@ export const AgentDetailsPage: React.FunctionComponent = () => {
|
|||
} = useGetOneAgent(agentId, {
|
||||
pollIntervalMs: 5000,
|
||||
query: {
|
||||
withMetrics: displayAgentMetrics,
|
||||
withMetrics: true,
|
||||
},
|
||||
});
|
||||
const {
|
||||
|
@ -72,7 +69,6 @@ export const AgentDetailsPage: React.FunctionComponent = () => {
|
|||
navigateToApp(routeState.onDoneNavigateTo[0], routeState.onDoneNavigateTo[1]);
|
||||
}
|
||||
}, [routeState, navigateToApp]);
|
||||
const { diagnosticFileUploadEnabled } = ExperimentalFeaturesService.get();
|
||||
|
||||
const host = agentData?.item?.local_metadata?.host;
|
||||
|
||||
|
@ -156,19 +152,17 @@ export const AgentDetailsPage: React.FunctionComponent = () => {
|
|||
href: getHref('agent_details_logs', { agentId, tabId: 'logs' }),
|
||||
isSelected: tabId === 'logs',
|
||||
},
|
||||
];
|
||||
if (diagnosticFileUploadEnabled) {
|
||||
tabs.push({
|
||||
{
|
||||
id: 'diagnostics',
|
||||
name: i18n.translate('xpack.fleet.agentDetails.subTabs.diagnosticsTab', {
|
||||
defaultMessage: 'Diagnostics',
|
||||
}),
|
||||
href: getHref('agent_details_diagnostics', { agentId, tabId: 'diagnostics' }),
|
||||
isSelected: tabId === 'diagnostics',
|
||||
});
|
||||
}
|
||||
},
|
||||
];
|
||||
return tabs;
|
||||
}, [getHref, agentId, tabId, diagnosticFileUploadEnabled]);
|
||||
}, [getHref, agentId, tabId]);
|
||||
|
||||
return (
|
||||
<AgentRefreshContext.Provider
|
||||
|
|
|
@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage, FormattedRelative } from '@kbn/i18n-react';
|
||||
|
||||
import type { Agent, AgentPolicy } from '../../../../types';
|
||||
import { isAgentUpgradeable, ExperimentalFeaturesService } from '../../../../services';
|
||||
import { isAgentUpgradeable } from '../../../../services';
|
||||
import { AgentHealth } from '../../components';
|
||||
|
||||
import type { Pagination } from '../../../../hooks';
|
||||
|
@ -99,7 +99,6 @@ export const AgentListTable: React.FC<Props> = (props: Props) => {
|
|||
} = props;
|
||||
|
||||
const authz = useAuthz();
|
||||
const { displayAgentMetrics } = ExperimentalFeaturesService.get();
|
||||
|
||||
const { getHref } = useLink();
|
||||
const latestAgentVersion = useAgentVersion();
|
||||
|
@ -224,65 +223,59 @@ export const AgentListTable: React.FC<Props> = (props: Props) => {
|
|||
);
|
||||
},
|
||||
},
|
||||
...(displayAgentMetrics
|
||||
? [
|
||||
{
|
||||
field: AGENTS_TABLE_FIELDS.METRICS,
|
||||
sortable: false,
|
||||
name: (
|
||||
<EuiToolTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentList.cpuTooltip"
|
||||
defaultMessage="Average CPU usage in the last 5 minutes. This includes usage from the Agent and the component it supervises. Possible value ranges from 0 to (number of available CPU cores * 100)"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<FormattedMessage id="xpack.fleet.agentList.cpuTitle" defaultMessage="CPU" />
|
||||
|
||||
<EuiIcon type="iInCircle" />
|
||||
</span>
|
||||
</EuiToolTip>
|
||||
),
|
||||
width: '75px',
|
||||
render: (metrics: AgentMetrics | undefined, agent: Agent) =>
|
||||
formatAgentCPU(
|
||||
agent.metrics,
|
||||
agent.policy_id ? agentPoliciesIndexedById[agent.policy_id] : undefined
|
||||
),
|
||||
},
|
||||
{
|
||||
field: AGENTS_TABLE_FIELDS.METRICS,
|
||||
sortable: false,
|
||||
name: (
|
||||
<EuiToolTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentList.memoryTooltip"
|
||||
defaultMessage="Average memory usage in the last 5 minutes"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentList.memoryTitle"
|
||||
defaultMessage="Memory"
|
||||
/>
|
||||
|
||||
<EuiIcon type="iInCircle" />
|
||||
</span>
|
||||
</EuiToolTip>
|
||||
),
|
||||
width: '90px',
|
||||
render: (metrics: AgentMetrics | undefined, agent: Agent) =>
|
||||
formatAgentMemory(
|
||||
agent.metrics,
|
||||
agent.policy_id ? agentPoliciesIndexedById[agent.policy_id] : undefined
|
||||
),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
{
|
||||
field: AGENTS_TABLE_FIELDS.METRICS,
|
||||
sortable: false,
|
||||
name: (
|
||||
<EuiToolTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentList.cpuTooltip"
|
||||
defaultMessage="Average CPU usage in the last 5 minutes. This includes usage from the Agent and the component it supervises. Possible value ranges from 0 to (number of available CPU cores * 100)"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<FormattedMessage id="xpack.fleet.agentList.cpuTitle" defaultMessage="CPU" />
|
||||
|
||||
<EuiIcon type="iInCircle" />
|
||||
</span>
|
||||
</EuiToolTip>
|
||||
),
|
||||
width: '75px',
|
||||
render: (metrics: AgentMetrics | undefined, agent: Agent) =>
|
||||
formatAgentCPU(
|
||||
agent.metrics,
|
||||
agent.policy_id ? agentPoliciesIndexedById[agent.policy_id] : undefined
|
||||
),
|
||||
},
|
||||
{
|
||||
field: AGENTS_TABLE_FIELDS.METRICS,
|
||||
sortable: false,
|
||||
name: (
|
||||
<EuiToolTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentList.memoryTooltip"
|
||||
defaultMessage="Average memory usage in the last 5 minutes"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<FormattedMessage id="xpack.fleet.agentList.memoryTitle" defaultMessage="Memory" />
|
||||
|
||||
<EuiIcon type="iInCircle" />
|
||||
</span>
|
||||
</EuiToolTip>
|
||||
),
|
||||
width: '90px',
|
||||
render: (metrics: AgentMetrics | undefined, agent: Agent) =>
|
||||
formatAgentMemory(
|
||||
agent.metrics,
|
||||
agent.policy_id ? agentPoliciesIndexedById[agent.policy_id] : undefined
|
||||
),
|
||||
},
|
||||
{
|
||||
field: AGENTS_TABLE_FIELDS.LAST_CHECKIN,
|
||||
sortable: true,
|
||||
|
|
|
@ -48,9 +48,7 @@ const defaultProps = {
|
|||
|
||||
describe('AgentBulkActions', () => {
|
||||
beforeAll(() => {
|
||||
mockedExperimentalFeaturesService.get.mockReturnValue({
|
||||
diagnosticFileUploadEnabled: true,
|
||||
} as any);
|
||||
mockedExperimentalFeaturesService.get.mockReturnValue({} as any);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -25,7 +25,6 @@ import {
|
|||
} from '../../components';
|
||||
import { useLicense } from '../../../../hooks';
|
||||
import { LICENSE_FOR_SCHEDULE_UPGRADE, AGENTS_PREFIX } from '../../../../../../../common/constants';
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
|
||||
import { getCommonTags } from '../utils';
|
||||
|
||||
|
@ -105,9 +104,8 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
|
|||
: nAgentsInTable - totalManagedAgentIds?.length;
|
||||
|
||||
const [tagsPopoverButton, setTagsPopoverButton] = useState<HTMLElement>();
|
||||
const { diagnosticFileUploadEnabled, enableExportCSV } = ExperimentalFeaturesService.get();
|
||||
|
||||
const { generateReportingJobCSV } = useExportCSV(enableExportCSV);
|
||||
const { generateReportingJobCSV } = useExportCSV();
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
|
@ -190,27 +188,23 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
|
|||
setUpgradeModalState({ isOpen: true, isScheduled: false, isUpdating: true });
|
||||
},
|
||||
},
|
||||
...(diagnosticFileUploadEnabled
|
||||
? [
|
||||
{
|
||||
name: (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentBulkActions.requestDiagnostics"
|
||||
data-test-subj="agentBulkActionsRequestDiagnostics"
|
||||
defaultMessage="Request diagnostics for {agentCount, plural, one {# agent} other {# agents}}"
|
||||
values={{
|
||||
agentCount,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
icon: <EuiIcon type="download" size="m" />,
|
||||
onClick: () => {
|
||||
closeMenu();
|
||||
setIsRequestDiagnosticsModalOpen(true);
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
name: (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentBulkActions.requestDiagnostics"
|
||||
data-test-subj="agentBulkActionsRequestDiagnostics"
|
||||
defaultMessage="Request diagnostics for {agentCount, plural, one {# agent} other {# agents}}"
|
||||
values={{
|
||||
agentCount,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
icon: <EuiIcon type="download" size="m" />,
|
||||
onClick: () => {
|
||||
closeMenu();
|
||||
setIsRequestDiagnosticsModalOpen(true);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: (
|
||||
<FormattedMessage
|
||||
|
@ -228,27 +222,23 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
|
|||
setIsUnenrollModalOpen(true);
|
||||
},
|
||||
},
|
||||
...(enableExportCSV
|
||||
? [
|
||||
{
|
||||
name: (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentBulkActions.exportAgents"
|
||||
data-test-subj="bulkAgentExportBtn"
|
||||
defaultMessage="Export {agentCount, plural, one {# agent} other {# agents}} as CSV"
|
||||
values={{
|
||||
agentCount,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
icon: <EuiIcon type="exportAction" size="m" />,
|
||||
onClick: () => {
|
||||
closeMenu();
|
||||
setIsExportCSVModalOpen(true);
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
name: (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentBulkActions.exportAgents"
|
||||
data-test-subj="bulkAgentExportBtn"
|
||||
defaultMessage="Export {agentCount, plural, one {# agent} other {# agents}} as CSV"
|
||||
values={{
|
||||
agentCount,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
icon: <EuiIcon type="exportAction" size="m" />,
|
||||
onClick: () => {
|
||||
closeMenu();
|
||||
setIsExportCSVModalOpen(true);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const panels = [
|
||||
|
|
|
@ -52,9 +52,7 @@ function renderTableRowActions({
|
|||
}
|
||||
describe('TableRowActions', () => {
|
||||
beforeEach(() => {
|
||||
mockedExperimentalFeaturesService.get.mockReturnValue({
|
||||
diagnosticFileUploadEnabled: true,
|
||||
} as any);
|
||||
mockedExperimentalFeaturesService.get.mockReturnValue({} as any);
|
||||
mockedUseAuthz.mockReturnValue({
|
||||
fleet: {
|
||||
all: true,
|
||||
|
@ -82,17 +80,6 @@ describe('TableRowActions', () => {
|
|||
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: { active: true } as Agent,
|
||||
agentPolicy: {} as AgentPolicy,
|
||||
});
|
||||
expect(res).toBe(null);
|
||||
});
|
||||
|
||||
it('should not render action if authz do not have Agents:All', async () => {
|
||||
mockedUseAuthz.mockReturnValue({
|
||||
fleet: {
|
||||
|
|
|
@ -18,7 +18,6 @@ import { useLink } from '../../../../hooks';
|
|||
import { useAuthz } from '../../../../../../hooks/use_authz';
|
||||
import { ContextMenuActions } from '../../../../components';
|
||||
import { isAgentUpgradeable } from '../../../../services';
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
|
||||
export const TableRowActions: React.FunctionComponent<{
|
||||
agent: Agent;
|
||||
|
@ -44,8 +43,6 @@ export const TableRowActions: React.FunctionComponent<{
|
|||
|
||||
const isUnenrolling = agent.status === 'unenrolling';
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const { diagnosticFileUploadEnabled, agentTamperProtectionEnabled } =
|
||||
ExperimentalFeaturesService.get();
|
||||
const menuItems = [
|
||||
<EuiContextMenuItem
|
||||
icon="inspect"
|
||||
|
@ -138,12 +135,7 @@ export const TableRowActions: React.FunctionComponent<{
|
|||
);
|
||||
}
|
||||
|
||||
if (
|
||||
authz.fleet.allAgents &&
|
||||
agentTamperProtectionEnabled &&
|
||||
agent.policy_id &&
|
||||
!agentPolicy?.supports_agentless
|
||||
) {
|
||||
if (authz.fleet.allAgents && agent.policy_id && !agentPolicy?.supports_agentless) {
|
||||
menuItems.push(
|
||||
<EuiContextMenuItem
|
||||
icon="minusInCircle"
|
||||
|
@ -164,7 +156,7 @@ export const TableRowActions: React.FunctionComponent<{
|
|||
}
|
||||
}
|
||||
|
||||
if (authz.fleet.readAgents && diagnosticFileUploadEnabled) {
|
||||
if (authz.fleet.readAgents) {
|
||||
menuItems.push(
|
||||
<EuiContextMenuItem
|
||||
key="requestAgentDiagnosticsBtn"
|
||||
|
|
|
@ -46,7 +46,7 @@ describe('export_csv', () => {
|
|||
|
||||
function render() {
|
||||
const renderer = createFleetTestRendererMock();
|
||||
return renderer.renderHook(() => useExportCSV(true));
|
||||
return renderer.renderHook(() => useExportCSV());
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -27,12 +27,12 @@ import type { ExportField } from '../../components/agent_export_csv_modal';
|
|||
|
||||
import { getSortFieldForAPI } from './use_fetch_agents_data';
|
||||
|
||||
export function useExportCSV(enableExportCSV?: boolean) {
|
||||
export function useExportCSV() {
|
||||
const startServices = useStartServices();
|
||||
const { notifications, http, uiSettings } = startServices;
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
const { data: runtimeFieldsResponse } = useGetAgentStatusRuntimeFieldQuery({
|
||||
enabled: enableExportCSV,
|
||||
enabled: true,
|
||||
});
|
||||
const runtimeFields = runtimeFieldsResponse ? runtimeFieldsResponse : 'emit("")';
|
||||
|
||||
|
|
|
@ -106,9 +106,7 @@ describe('useFetchAgentsData', () => {
|
|||
const mockErrorToast = startServices.notifications.toasts.addError as jest.Mock;
|
||||
|
||||
beforeAll(() => {
|
||||
mockedExperimentalFeaturesService.get.mockReturnValue({
|
||||
displayAgentMetrics: true,
|
||||
} as any);
|
||||
mockedExperimentalFeaturesService.get.mockReturnValue({} as any);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -25,7 +25,7 @@ import {
|
|||
sendGetActionStatus,
|
||||
sendBulkGetAgentPolicies,
|
||||
} from '../../../../hooks';
|
||||
import { AgentStatusKueryHelper, ExperimentalFeaturesService } from '../../../../services';
|
||||
import { AgentStatusKueryHelper } from '../../../../services';
|
||||
import { LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT } from '../../../../constants';
|
||||
|
||||
import { getKuery } from '../utils/get_kuery';
|
||||
|
@ -97,7 +97,6 @@ export const getSortFieldForAPI = (field: string): string => {
|
|||
|
||||
export function useFetchAgentsData() {
|
||||
const fullAgentPolicyFecher = useFullAgentPolicyFetcher();
|
||||
const { displayAgentMetrics } = ExperimentalFeaturesService.get();
|
||||
|
||||
const { notifications } = useStartServices();
|
||||
|
||||
|
@ -218,7 +217,7 @@ export function useFetchAgentsData() {
|
|||
showInactive,
|
||||
showUpgradeable,
|
||||
getStatusSummary: true,
|
||||
withMetrics: displayAgentMetrics,
|
||||
withMetrics: true,
|
||||
}),
|
||||
sendGetAgentStatus({
|
||||
kuery: AgentStatusKueryHelper.buildKueryForInactiveAgents(),
|
||||
|
@ -362,7 +361,6 @@ export function useFetchAgentsData() {
|
|||
sortOrder,
|
||||
showInactive,
|
||||
showUpgradeable,
|
||||
displayAgentMetrics,
|
||||
fullAgentPolicyFecher,
|
||||
allTags,
|
||||
latestAgentActionErrors,
|
||||
|
|
|
@ -190,9 +190,7 @@ describe('EditOutputFlyout', () => {
|
|||
});
|
||||
|
||||
it('should populate secret input with plain text value when editing kafka output', async () => {
|
||||
jest
|
||||
.spyOn(ExperimentalFeaturesService, 'get')
|
||||
.mockReturnValue({ outputSecretsStorage: true, kafkaOutput: true } as any);
|
||||
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any);
|
||||
|
||||
mockedUseFleetStatus.mockReturnValue({
|
||||
isLoading: false,
|
||||
|
@ -230,9 +228,7 @@ describe('EditOutputFlyout', () => {
|
|||
});
|
||||
|
||||
it('should populate secret password input with plain text value when editing kafka output', async () => {
|
||||
jest
|
||||
.spyOn(ExperimentalFeaturesService, 'get')
|
||||
.mockReturnValue({ outputSecretsStorage: true, kafkaOutput: true } as any);
|
||||
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any);
|
||||
|
||||
mockedUseFleetStatus.mockReturnValue({
|
||||
isLoading: false,
|
||||
|
@ -273,9 +269,7 @@ describe('EditOutputFlyout', () => {
|
|||
});
|
||||
|
||||
it('should populate secret input with plain text value when editing logstash output', async () => {
|
||||
jest
|
||||
.spyOn(ExperimentalFeaturesService, 'get')
|
||||
.mockReturnValue({ outputSecretsStorage: true } as any);
|
||||
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any);
|
||||
|
||||
mockedUseFleetStatus.mockReturnValue({
|
||||
isLoading: false,
|
||||
|
@ -325,9 +319,7 @@ describe('EditOutputFlyout', () => {
|
|||
});
|
||||
|
||||
it('should render the flyout if the output provided is a remote ES output', async () => {
|
||||
jest
|
||||
.spyOn(ExperimentalFeaturesService, 'get')
|
||||
.mockReturnValue({ remoteESOutput: true, outputSecretsStorage: true } as any);
|
||||
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any);
|
||||
|
||||
mockedUseFleetStatus.mockReturnValue({
|
||||
isLoading: false,
|
||||
|
@ -356,9 +348,7 @@ describe('EditOutputFlyout', () => {
|
|||
});
|
||||
|
||||
it('should populate secret service token input with plain text value when editing remote ES output', async () => {
|
||||
jest
|
||||
.spyOn(ExperimentalFeaturesService, 'get')
|
||||
.mockReturnValue({ remoteESOutput: true, outputSecretsStorage: true } as any);
|
||||
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any);
|
||||
|
||||
mockedUseFleetStatus.mockReturnValue({
|
||||
isLoading: false,
|
||||
|
@ -394,7 +384,7 @@ describe('EditOutputFlyout', () => {
|
|||
});
|
||||
|
||||
it('should not display remote ES output in type lists if serverless', async () => {
|
||||
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({ remoteESOutput: true } as any);
|
||||
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any);
|
||||
mockUseStartServices.mockReset();
|
||||
mockStartServices(true);
|
||||
const { utils } = renderFlyout({
|
||||
|
|
|
@ -41,8 +41,6 @@ import {
|
|||
outputYmlIncludesReservedPerformanceKey,
|
||||
} from '../../../../../../../common/services/output_helpers';
|
||||
|
||||
import { ExperimentalFeaturesService } from '../../../../../../services';
|
||||
|
||||
import { outputType, RESERVED_CONFIG_YML_KEYS } from '../../../../../../../common/constants';
|
||||
|
||||
import { MAX_FLYOUT_WIDTH } from '../../../../constants';
|
||||
|
@ -105,9 +103,6 @@ export const EditOutputFlyout: React.FunctionComponent<EditOutputFlyoutProps> =
|
|||
[proxies]
|
||||
);
|
||||
|
||||
const { kafkaOutput: isKafkaOutputEnabled, remoteESOutput: isRemoteESOutputEnabled } =
|
||||
ExperimentalFeaturesService.get();
|
||||
|
||||
const isRemoteESOutput = inputs.typeInput.value === outputType.RemoteElasticsearch;
|
||||
const isESOutput = inputs.typeInput.value === outputType.Elasticsearch;
|
||||
const supportsPresets = inputs.typeInput.value
|
||||
|
@ -119,11 +114,11 @@ export const EditOutputFlyout: React.FunctionComponent<EditOutputFlyoutProps> =
|
|||
|
||||
const OUTPUT_TYPE_OPTIONS = [
|
||||
{ value: outputType.Elasticsearch, text: 'Elasticsearch' },
|
||||
...(isRemoteESOutputEnabled && isStateful
|
||||
...(isStateful
|
||||
? [{ value: outputType.RemoteElasticsearch, text: 'Remote Elasticsearch' }]
|
||||
: []),
|
||||
{ value: outputType.Logstash, text: 'Logstash' },
|
||||
...(isKafkaOutputEnabled ? [{ value: outputType.Kafka, text: 'Kafka' }] : []),
|
||||
{ value: outputType.Kafka, text: 'Kafka' },
|
||||
];
|
||||
|
||||
const renderLogstashSection = () => {
|
||||
|
@ -142,29 +137,23 @@ export const EditOutputFlyout: React.FunctionComponent<EditOutputFlyoutProps> =
|
|||
};
|
||||
|
||||
const renderRemoteElasticsearchSection = () => {
|
||||
if (isRemoteESOutputEnabled) {
|
||||
return (
|
||||
<OutputFormRemoteEsSection
|
||||
inputs={inputs}
|
||||
useSecretsStorage={useSecretsStorage}
|
||||
onToggleSecretStorage={onToggleSecretStorage}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
return (
|
||||
<OutputFormRemoteEsSection
|
||||
inputs={inputs}
|
||||
useSecretsStorage={useSecretsStorage}
|
||||
onToggleSecretStorage={onToggleSecretStorage}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderKafkaSection = () => {
|
||||
if (isKafkaOutputEnabled) {
|
||||
return (
|
||||
<OutputFormKafkaSection
|
||||
inputs={inputs}
|
||||
useSecretsStorage={useSecretsStorage}
|
||||
onToggleSecretStorage={onToggleSecretStorage}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
return (
|
||||
<OutputFormKafkaSection
|
||||
inputs={inputs}
|
||||
useSecretsStorage={useSecretsStorage}
|
||||
onToggleSecretStorage={onToggleSecretStorage}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderOutputTypeSection = (type: string) => {
|
||||
|
|
|
@ -34,8 +34,6 @@ import type {
|
|||
|
||||
import type { IntegrationsURLParameters } from '../../screens/home/hooks/use_available_packages';
|
||||
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
|
||||
import { promoteFeaturedIntegrations } from '../utils';
|
||||
|
||||
import { ControlsColumn } from './controls';
|
||||
|
@ -111,8 +109,6 @@ export const PackageListGrid: FunctionComponent<PackageListGridProps> = ({
|
|||
|
||||
const MAX_SUBCATEGORIES_NUMBER = 6;
|
||||
|
||||
const { showIntegrationsSubcategories } = ExperimentalFeaturesService.get();
|
||||
|
||||
const onButtonClick = () => {
|
||||
setPopover(!isPopoverOpen);
|
||||
};
|
||||
|
@ -215,66 +211,65 @@ export const PackageListGrid: FunctionComponent<PackageListGridProps> = ({
|
|||
</EuiFlexItem>
|
||||
)}
|
||||
|
||||
{showIntegrationsSubcategories && availableSubCategories?.length ? <EuiSpacer /> : null}
|
||||
{showIntegrationsSubcategories ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup
|
||||
data-test-subj="epmList.subcategoriesRow"
|
||||
justifyContent="flexStart"
|
||||
direction="row"
|
||||
gutterSize="s"
|
||||
style={{
|
||||
maxWidth: 943,
|
||||
}}
|
||||
>
|
||||
{visibleSubCategories?.map((subCategory) => {
|
||||
const isSelected = subCategory.id === selectedSubCategory;
|
||||
return (
|
||||
<EuiFlexItem grow={false} key={subCategory.id}>
|
||||
<EuiButton
|
||||
css={isSelected ? 'color: white' : ''}
|
||||
color={isSelected ? 'accent' : 'text'}
|
||||
fill={isSelected}
|
||||
aria-label={subCategory?.title}
|
||||
onClick={() => onSubCategoryClick(subCategory.id)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.epmList.subcategoriesButton"
|
||||
defaultMessage="{subcategory}"
|
||||
values={{
|
||||
subcategory: subCategory.title,
|
||||
}}
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
})}
|
||||
{hiddenSubCategoriesItems?.length ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiPopover
|
||||
data-test-subj="epmList.showMoreSubCategoriesButton"
|
||||
id="moreSubCategories"
|
||||
button={
|
||||
<EuiButtonIcon
|
||||
display="base"
|
||||
onClick={onButtonClick}
|
||||
iconType="boxesHorizontal"
|
||||
aria-label="Show more subcategories"
|
||||
size="m"
|
||||
/>
|
||||
}
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={closePopover}
|
||||
panelPaddingSize="none"
|
||||
anchorPosition="downLeft"
|
||||
{availableSubCategories?.length ? <EuiSpacer /> : null}
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup
|
||||
data-test-subj="epmList.subcategoriesRow"
|
||||
justifyContent="flexStart"
|
||||
direction="row"
|
||||
gutterSize="s"
|
||||
style={{
|
||||
maxWidth: 943,
|
||||
}}
|
||||
>
|
||||
{visibleSubCategories?.map((subCategory) => {
|
||||
const isSelected = subCategory.id === selectedSubCategory;
|
||||
return (
|
||||
<EuiFlexItem grow={false} key={subCategory.id}>
|
||||
<EuiButton
|
||||
css={isSelected ? 'color: white' : ''}
|
||||
color={isSelected ? 'accent' : 'text'}
|
||||
fill={isSelected}
|
||||
aria-label={subCategory?.title}
|
||||
onClick={() => onSubCategoryClick(subCategory.id)}
|
||||
>
|
||||
<EuiContextMenuPanel size="s" items={hiddenSubCategoriesItems} />
|
||||
</EuiPopover>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.epmList.subcategoriesButton"
|
||||
defaultMessage="{subcategory}"
|
||||
values={{
|
||||
subcategory: subCategory.title,
|
||||
}}
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
);
|
||||
})}
|
||||
{hiddenSubCategoriesItems?.length ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiPopover
|
||||
data-test-subj="epmList.showMoreSubCategoriesButton"
|
||||
id="moreSubCategories"
|
||||
button={
|
||||
<EuiButtonIcon
|
||||
display="base"
|
||||
onClick={onButtonClick}
|
||||
iconType="boxesHorizontal"
|
||||
aria-label="Show more subcategories"
|
||||
size="m"
|
||||
/>
|
||||
}
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={closePopover}
|
||||
panelPaddingSize="none"
|
||||
anchorPosition="downLeft"
|
||||
>
|
||||
<EuiContextMenuPanel size="s" items={hiddenSubCategoriesItems} />
|
||||
</EuiPopover>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
{callout ? (
|
||||
<>
|
||||
<EuiSpacer size={calloutTopSpacerSize} />
|
||||
|
|
|
@ -48,7 +48,6 @@ import {
|
|||
useGetSettingsQuery,
|
||||
} from '../../../../hooks';
|
||||
import { INTEGRATIONS_ROUTING_PATHS } from '../../../../constants';
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
import {
|
||||
useGetPackageInfoByKeyQuery,
|
||||
useLink,
|
||||
|
@ -162,8 +161,6 @@ export function Detail() {
|
|||
|
||||
const services = useStartServices();
|
||||
const isCloud = !!services?.cloud?.cloudId;
|
||||
const { createPackagePolicyMultiPageLayout: isExperimentalAddIntegrationPageEnabled } =
|
||||
ExperimentalFeaturesService.get();
|
||||
const agentPolicyIdFromContext = getAgentPolicyId();
|
||||
const isOverviewPage = panel === 'overview';
|
||||
|
||||
|
@ -412,7 +409,6 @@ export function Detail() {
|
|||
currentPath,
|
||||
integration,
|
||||
isCloud,
|
||||
isExperimentalAddIntegrationPageEnabled,
|
||||
isFirstTimeAgentUser,
|
||||
isGuidedOnboardingActive,
|
||||
pkgkey,
|
||||
|
@ -445,7 +441,6 @@ export function Detail() {
|
|||
history,
|
||||
integration,
|
||||
isCloud,
|
||||
isExperimentalAddIntegrationPageEnabled,
|
||||
isFirstTimeAgentUser,
|
||||
isGuidedOnboardingActive,
|
||||
onboardingAppId,
|
||||
|
|
|
@ -27,7 +27,6 @@ interface GetInstallPkgRouteOptionsParams {
|
|||
agentPolicyId?: string;
|
||||
pkgkey: string;
|
||||
isCloud: boolean;
|
||||
isExperimentalAddIntegrationPageEnabled: boolean;
|
||||
isFirstTimeAgentUser: boolean;
|
||||
isGuidedOnboardingActive: boolean;
|
||||
}
|
||||
|
@ -50,16 +49,12 @@ export const getInstallPkgRouteOptions = ({
|
|||
pkgkey,
|
||||
isFirstTimeAgentUser,
|
||||
isCloud,
|
||||
isExperimentalAddIntegrationPageEnabled,
|
||||
isGuidedOnboardingActive,
|
||||
}: GetInstallPkgRouteOptionsParams): InstallPkgRouteOptions => {
|
||||
const integrationOpts: { integration?: string } = integration ? { integration } : {};
|
||||
const packageExemptFromStepsLayout = isPackageExemptFromStepsLayout(pkgkey);
|
||||
const useMultiPageLayout =
|
||||
isExperimentalAddIntegrationPageEnabled &&
|
||||
isCloud &&
|
||||
(isFirstTimeAgentUser || isGuidedOnboardingActive) &&
|
||||
!packageExemptFromStepsLayout;
|
||||
isCloud && (isFirstTimeAgentUser || isGuidedOnboardingActive) && !packageExemptFromStepsLayout;
|
||||
const path = pagePathGetters.add_integration_to_policy({
|
||||
pkgkey,
|
||||
useMultiPageLayout,
|
||||
|
|
|
@ -23,7 +23,7 @@ import { useMergeEprPackagesWithReplacements } from '../../../../../hooks/use_me
|
|||
import { mapToCard } from '..';
|
||||
import type { PackageList, PackageListItem } from '../../../../../types';
|
||||
|
||||
import { doesPackageHaveIntegrations, ExperimentalFeaturesService } from '../../../../../services';
|
||||
import { doesPackageHaveIntegrations } from '../../../../../services';
|
||||
|
||||
import {
|
||||
isInputOnlyPolicyTemplate,
|
||||
|
@ -140,7 +140,6 @@ export const useAvailablePackages = ({
|
|||
}) => {
|
||||
const [preference, setPreference] = useState<IntegrationPreferenceType>('recommended');
|
||||
|
||||
const { showIntegrationsSubcategories } = ExperimentalFeaturesService.get();
|
||||
const { isAgentlessEnabled } = useAgentless();
|
||||
|
||||
const {
|
||||
|
@ -244,16 +243,12 @@ export const useAvailablePackages = ({
|
|||
|
||||
// Filter out subcategories
|
||||
const mainCategories = useMemo(() => {
|
||||
return showIntegrationsSubcategories
|
||||
? allCategories.filter((category) => category.parent_id === undefined)
|
||||
: allCategories;
|
||||
}, [allCategories, showIntegrationsSubcategories]);
|
||||
return allCategories.filter((category) => category.parent_id === undefined);
|
||||
}, [allCategories]);
|
||||
|
||||
const availableSubCategories = useMemo(() => {
|
||||
return showIntegrationsSubcategories
|
||||
? allCategories?.filter((c) => c.parent_id === selectedCategory)
|
||||
: [];
|
||||
}, [allCategories, selectedCategory, showIntegrationsSubcategories]);
|
||||
return allCategories?.filter((c) => c.parent_id === selectedCategory);
|
||||
}, [allCategories, selectedCategory]);
|
||||
|
||||
return {
|
||||
initialSelectedCategory,
|
||||
|
|
|
@ -49,18 +49,14 @@ export const getFleetDeepLinks: (
|
|||
path: FLEET_ROUTING_PATHS.enrollment_tokens,
|
||||
visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch'],
|
||||
},
|
||||
...((experimentalFeatures.agentTamperProtectionEnabled
|
||||
? [
|
||||
{
|
||||
id: FleetDeepLinkId.uninstallTokens,
|
||||
title: i18n.translate('xpack.fleet.deepLinks.uninstallTokens.title', {
|
||||
defaultMessage: 'Uninstall tokens',
|
||||
}),
|
||||
path: FLEET_ROUTING_PATHS.uninstall_tokens,
|
||||
visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch'],
|
||||
},
|
||||
]
|
||||
: []) as AppDeepLink[]),
|
||||
{
|
||||
id: FleetDeepLinkId.uninstallTokens,
|
||||
title: i18n.translate('xpack.fleet.deepLinks.uninstallTokens.title', {
|
||||
defaultMessage: 'Uninstall tokens',
|
||||
}),
|
||||
path: FLEET_ROUTING_PATHS.uninstall_tokens,
|
||||
visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch'],
|
||||
},
|
||||
{
|
||||
id: FleetDeepLinkId.dataStreams,
|
||||
title: i18n.translate('xpack.fleet.deepLinks.dataStreams.title', {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { LICENCE_FOR_MULTIPLE_AGENT_POLICIES } from '../../common/constants';
|
||||
import { ExperimentalFeaturesService } from '../services';
|
||||
|
||||
import { licenseService } from './use_license';
|
||||
|
||||
|
@ -15,9 +14,7 @@ export function useMultipleAgentPolicies() {
|
|||
}
|
||||
|
||||
export function canUseMultipleAgentPolicies() {
|
||||
const { enableReusableIntegrationPolicies } = ExperimentalFeaturesService.get();
|
||||
|
||||
const hasEnterpriseLicence = licenseService.hasAtLeast(LICENCE_FOR_MULTIPLE_AGENT_POLICIES);
|
||||
|
||||
return Boolean(enableReusableIntegrationPolicies && hasEnterpriseLicence);
|
||||
return Boolean(hasEnterpriseLicence);
|
||||
}
|
||||
|
|
|
@ -333,7 +333,7 @@ export class FleetPlugin implements Plugin<FleetSetup, FleetStart, FleetSetupDep
|
|||
all: capabilities.fleet.all as boolean,
|
||||
read: capabilities.fleet.read as boolean,
|
||||
},
|
||||
subfeatureEnabled: this.experimentalFeatures.subfeaturePrivileges ?? false,
|
||||
subfeatureEnabled: true,
|
||||
}),
|
||||
packagePrivileges: calculatePackagePrivilegesFromCapabilities(capabilities),
|
||||
endpointExceptionsPrivileges:
|
||||
|
|
|
@ -12,10 +12,6 @@ import { ElasticsearchAssetType } from '../../common/types';
|
|||
|
||||
import { hasDeferredInstallations } from './has_deferred_installations';
|
||||
|
||||
import { ExperimentalFeaturesService } from '.';
|
||||
|
||||
const mockGet = jest.spyOn(ExperimentalFeaturesService, 'get');
|
||||
|
||||
const createPackage = ({
|
||||
installedEs = [],
|
||||
}: {
|
||||
|
@ -46,47 +42,38 @@ const createPackage = ({
|
|||
});
|
||||
|
||||
describe('isPackageUnverified', () => {
|
||||
describe('When experimental feature is disabled', () => {
|
||||
beforeEach(() => {
|
||||
// @ts-ignore don't want to define all experimental features here
|
||||
mockGet.mockReturnValue({
|
||||
packageVerification: false,
|
||||
} as ReturnType<(typeof ExperimentalFeaturesService)['get']>);
|
||||
it('Should return false for a package with no installationInfo', () => {
|
||||
const noSoPkg = createPackage();
|
||||
// @ts-ignore we know pkg has installationInfo but ts doesn't
|
||||
delete noSoPkg.installationInfo;
|
||||
expect(hasDeferredInstallations(noSoPkg)).toEqual(false);
|
||||
});
|
||||
|
||||
it('Should return true for a package with at least one asset deferred', () => {
|
||||
const pkgWithDeferredInstallations = createPackage({
|
||||
installedEs: [
|
||||
{ id: '', type: ElasticsearchAssetType.ingestPipeline },
|
||||
{ id: '', type: ElasticsearchAssetType.transform, deferred: true },
|
||||
],
|
||||
});
|
||||
|
||||
it('Should return false for a package with no installationInfo', () => {
|
||||
const noSoPkg = createPackage();
|
||||
// @ts-ignore we know pkg has installationInfo but ts doesn't
|
||||
delete noSoPkg.installationInfo;
|
||||
expect(hasDeferredInstallations(noSoPkg)).toEqual(false);
|
||||
});
|
||||
expect(hasDeferredInstallations(pkgWithDeferredInstallations)).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should return true for a package with at least one asset deferred', () => {
|
||||
const pkgWithDeferredInstallations = createPackage({
|
||||
installedEs: [
|
||||
{ id: '', type: ElasticsearchAssetType.ingestPipeline },
|
||||
{ id: '', type: ElasticsearchAssetType.transform, deferred: true },
|
||||
],
|
||||
});
|
||||
|
||||
expect(hasDeferredInstallations(pkgWithDeferredInstallations)).toEqual(true);
|
||||
it('Should return false for a package that has no asset deferred', () => {
|
||||
const pkgWithoutDeferredInstallations = createPackage({
|
||||
installedEs: [
|
||||
{ id: '', type: ElasticsearchAssetType.ingestPipeline },
|
||||
{ id: '', type: ElasticsearchAssetType.transform, deferred: false },
|
||||
],
|
||||
});
|
||||
expect(hasDeferredInstallations(pkgWithoutDeferredInstallations)).toEqual(false);
|
||||
});
|
||||
|
||||
it('Should return false for a package that has no asset deferred', () => {
|
||||
const pkgWithoutDeferredInstallations = createPackage({
|
||||
installedEs: [
|
||||
{ id: '', type: ElasticsearchAssetType.ingestPipeline },
|
||||
{ id: '', type: ElasticsearchAssetType.transform, deferred: false },
|
||||
],
|
||||
});
|
||||
expect(hasDeferredInstallations(pkgWithoutDeferredInstallations)).toEqual(false);
|
||||
});
|
||||
|
||||
it('Should return false for a package that has no asset', () => {
|
||||
const pkgWithoutDeferredInstallations = createPackage({
|
||||
installedEs: [],
|
||||
});
|
||||
expect(hasDeferredInstallations(pkgWithoutDeferredInstallations)).toEqual(false);
|
||||
it('Should return false for a package that has no asset', () => {
|
||||
const pkgWithoutDeferredInstallations = createPackage({
|
||||
installedEs: [],
|
||||
});
|
||||
expect(hasDeferredInstallations(pkgWithoutDeferredInstallations)).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -44,52 +44,9 @@ const createPackage = ({
|
|||
});
|
||||
|
||||
describe('isPackageUnverified', () => {
|
||||
describe('When experimental feature is disabled', () => {
|
||||
beforeEach(() => {
|
||||
// @ts-ignore don't want to define all experimental features here
|
||||
mockGet.mockReturnValue({
|
||||
packageVerification: false,
|
||||
} as ReturnType<(typeof ExperimentalFeaturesService)['get']>);
|
||||
});
|
||||
|
||||
it('Should return false for a package with no saved object', () => {
|
||||
const noSoPkg = createPackage();
|
||||
// @ts-ignore we know pkg has installationInfo but ts doesn't
|
||||
delete noSoPkg.installationInfo;
|
||||
expect(isPackageUnverified(noSoPkg)).toEqual(false);
|
||||
});
|
||||
it('Should return false for an unverified package', () => {
|
||||
const unverifiedPkg = createPackage({ verificationStatus: 'unverified' });
|
||||
expect(isPackageUnverified(unverifiedPkg)).toEqual(false);
|
||||
});
|
||||
it('Should return false for a verified package', () => {
|
||||
const verifiedPkg = createPackage({ verificationStatus: 'verified' });
|
||||
expect(isPackageUnverified(verifiedPkg)).toEqual(false);
|
||||
});
|
||||
it('Should return false for a verified package with correct key', () => {
|
||||
const unverifiedPkg = createPackage({
|
||||
verificationStatus: 'verified',
|
||||
verificationKeyId: '1234',
|
||||
});
|
||||
expect(isPackageUnverified(unverifiedPkg, '1234')).toEqual(false);
|
||||
});
|
||||
it('Should return false for a verified package with out of date key', () => {
|
||||
const unverifiedPkg = createPackage({
|
||||
verificationStatus: 'verified',
|
||||
verificationKeyId: '1234',
|
||||
});
|
||||
expect(isPackageUnverified(unverifiedPkg, 'not_1234')).toEqual(false);
|
||||
});
|
||||
it('Should return false for an unknown verification package', () => {
|
||||
const unknownPkg = createPackage({ verificationStatus: 'unknown' });
|
||||
expect(isPackageUnverified(unknownPkg)).toEqual(false);
|
||||
});
|
||||
});
|
||||
describe('When experimental feature is enabled', () => {
|
||||
beforeEach(() => {
|
||||
mockGet.mockReturnValue({
|
||||
packageVerification: true,
|
||||
} as ReturnType<(typeof ExperimentalFeaturesService)['get']>);
|
||||
mockGet.mockReturnValue({} as ReturnType<(typeof ExperimentalFeaturesService)['get']>);
|
||||
});
|
||||
it('Should return false for a package with no saved object', () => {
|
||||
const noSoPkg = createPackage();
|
||||
|
|
|
@ -11,8 +11,6 @@ import type { PackageInfo, PackageListItem } from '../types';
|
|||
|
||||
import type { RequestError } from '../hooks';
|
||||
|
||||
import { ExperimentalFeaturesService } from '.';
|
||||
|
||||
export function isPackageUnverified(
|
||||
pkg: PackageInfo | PackageListItem,
|
||||
packageVerificationKeyId?: string
|
||||
|
@ -22,11 +20,10 @@ export function isPackageUnverified(
|
|||
const { verification_status: verificationStatus, verification_key_id: verificationKeyId } =
|
||||
pkg?.installationInfo;
|
||||
|
||||
const { packageVerification: isPackageVerificationEnabled } = ExperimentalFeaturesService.get();
|
||||
const isKeyOutdated = !!verificationKeyId && verificationKeyId !== packageVerificationKeyId;
|
||||
const isUnverified =
|
||||
verificationStatus === 'unverified' || (verificationStatus === 'verified' && isKeyOutdated);
|
||||
return isPackageVerificationEnabled && isUnverified;
|
||||
return isUnverified;
|
||||
}
|
||||
|
||||
export const isVerificationError = (err?: FleetErrorResponse | RequestError) =>
|
||||
|
|
|
@ -150,7 +150,7 @@ describe('Config schema', () => {
|
|||
|
||||
it('should not add a depreciations when enabling an existing experimental feature', () => {
|
||||
const res = applyConfigDeprecations({
|
||||
enableExperimental: ['displayAgentMetrics'],
|
||||
enableExperimental: ['useSpaceAwareness'],
|
||||
});
|
||||
|
||||
expect(res.messages).toMatchInlineSnapshot(`Array []`);
|
||||
|
|
|
@ -112,11 +112,7 @@ export const createAppContextStartContractMock = (
|
|||
securitySetup: securityMock.createSetup(),
|
||||
securityStart: securityMock.createStart(),
|
||||
logger: loggingSystemMock.create().get(),
|
||||
experimentalFeatures: {
|
||||
agentTamperProtectionEnabled: true,
|
||||
diagnosticFileUploadEnabled: true,
|
||||
enableReusableIntegrationPolicies: true,
|
||||
} as ExperimentalFeatures,
|
||||
experimentalFeatures: {} as ExperimentalFeatures,
|
||||
isProductionMode: true,
|
||||
configInitialValue: {
|
||||
agents: { enabled: true, elasticsearch: {} },
|
||||
|
|
|
@ -368,115 +368,110 @@ export class FleetPlugin
|
|||
},
|
||||
],
|
||||
},
|
||||
subFeatures: experimentalFeatures.subfeaturePrivileges
|
||||
? [
|
||||
subFeatures: [
|
||||
{
|
||||
name: 'Agents',
|
||||
requireAllSpaces,
|
||||
privilegeGroups: [
|
||||
{
|
||||
name: 'Agents',
|
||||
requireAllSpaces,
|
||||
privilegeGroups: [
|
||||
groupType: 'mutually_exclusive',
|
||||
privileges: [
|
||||
{
|
||||
groupType: 'mutually_exclusive',
|
||||
privileges: [
|
||||
{
|
||||
id: `agents_all`,
|
||||
api: [`${PLUGIN_ID}-agents-read`, `${PLUGIN_ID}-agents-all`],
|
||||
name: 'All',
|
||||
ui: ['agents_read', 'agents_all'],
|
||||
savedObject: {
|
||||
all: allSavedObjectTypes,
|
||||
read: allSavedObjectTypes,
|
||||
},
|
||||
includeIn: 'all',
|
||||
},
|
||||
{
|
||||
id: `agents_read`,
|
||||
api: [`${PLUGIN_ID}-agents-read`],
|
||||
name: 'Read',
|
||||
ui: ['agents_read'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: allSavedObjectTypes,
|
||||
},
|
||||
includeIn: 'read',
|
||||
alerting: {},
|
||||
},
|
||||
],
|
||||
id: `agents_all`,
|
||||
api: [`${PLUGIN_ID}-agents-read`, `${PLUGIN_ID}-agents-all`],
|
||||
name: 'All',
|
||||
ui: ['agents_read', 'agents_all'],
|
||||
savedObject: {
|
||||
all: allSavedObjectTypes,
|
||||
read: allSavedObjectTypes,
|
||||
},
|
||||
includeIn: 'all',
|
||||
},
|
||||
{
|
||||
id: `agents_read`,
|
||||
api: [`${PLUGIN_ID}-agents-read`],
|
||||
name: 'Read',
|
||||
ui: ['agents_read'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: allSavedObjectTypes,
|
||||
},
|
||||
includeIn: 'read',
|
||||
alerting: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Agent policies',
|
||||
requireAllSpaces,
|
||||
privilegeGroups: [
|
||||
{
|
||||
name: 'Agent policies',
|
||||
requireAllSpaces,
|
||||
privilegeGroups: [
|
||||
groupType: 'mutually_exclusive',
|
||||
privileges: [
|
||||
{
|
||||
groupType: 'mutually_exclusive',
|
||||
privileges: [
|
||||
{
|
||||
id: `agent_policies_all`,
|
||||
api: [
|
||||
`${PLUGIN_ID}-agent-policies-read`,
|
||||
`${PLUGIN_ID}-agent-policies-all`,
|
||||
],
|
||||
name: 'All',
|
||||
ui: ['agent_policies_read', 'agent_policies_all'],
|
||||
savedObject: {
|
||||
all: allSavedObjectTypes,
|
||||
read: allSavedObjectTypes,
|
||||
},
|
||||
includeIn: 'all',
|
||||
},
|
||||
{
|
||||
id: `agent_policies_read`,
|
||||
api: [`${PLUGIN_ID}-agent-policies-read`],
|
||||
name: 'Read',
|
||||
ui: ['agent_policies_read'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: allSavedObjectTypes,
|
||||
},
|
||||
includeIn: 'read',
|
||||
alerting: {},
|
||||
},
|
||||
],
|
||||
id: `agent_policies_all`,
|
||||
api: [`${PLUGIN_ID}-agent-policies-read`, `${PLUGIN_ID}-agent-policies-all`],
|
||||
name: 'All',
|
||||
ui: ['agent_policies_read', 'agent_policies_all'],
|
||||
savedObject: {
|
||||
all: allSavedObjectTypes,
|
||||
read: allSavedObjectTypes,
|
||||
},
|
||||
includeIn: 'all',
|
||||
},
|
||||
{
|
||||
id: `agent_policies_read`,
|
||||
api: [`${PLUGIN_ID}-agent-policies-read`],
|
||||
name: 'Read',
|
||||
ui: ['agent_policies_read'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: allSavedObjectTypes,
|
||||
},
|
||||
includeIn: 'read',
|
||||
alerting: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Settings',
|
||||
requireAllSpaces,
|
||||
privilegeGroups: [
|
||||
{
|
||||
name: 'Settings',
|
||||
requireAllSpaces,
|
||||
privilegeGroups: [
|
||||
groupType: 'mutually_exclusive',
|
||||
privileges: [
|
||||
{
|
||||
groupType: 'mutually_exclusive',
|
||||
privileges: [
|
||||
{
|
||||
id: `settings_all`,
|
||||
api: [`${PLUGIN_ID}-settings-read`, `${PLUGIN_ID}-settings-all`],
|
||||
name: 'All',
|
||||
ui: ['settings_read', 'settings_all'],
|
||||
savedObject: {
|
||||
all: allSavedObjectTypes,
|
||||
read: allSavedObjectTypes,
|
||||
},
|
||||
includeIn: 'all',
|
||||
},
|
||||
{
|
||||
id: `settings_read`,
|
||||
api: [`${PLUGIN_ID}-settings-read`],
|
||||
name: 'Read',
|
||||
ui: ['settings_read'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: allSavedObjectTypes,
|
||||
},
|
||||
includeIn: 'read',
|
||||
alerting: {},
|
||||
},
|
||||
],
|
||||
id: `settings_all`,
|
||||
api: [`${PLUGIN_ID}-settings-read`, `${PLUGIN_ID}-settings-all`],
|
||||
name: 'All',
|
||||
ui: ['settings_read', 'settings_all'],
|
||||
savedObject: {
|
||||
all: allSavedObjectTypes,
|
||||
read: allSavedObjectTypes,
|
||||
},
|
||||
includeIn: 'all',
|
||||
},
|
||||
{
|
||||
id: `settings_read`,
|
||||
api: [`${PLUGIN_ID}-settings-read`],
|
||||
name: 'Read',
|
||||
ui: ['settings_read'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: allSavedObjectTypes,
|
||||
},
|
||||
includeIn: 'read',
|
||||
alerting: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [],
|
||||
],
|
||||
},
|
||||
],
|
||||
privileges: {
|
||||
all: {
|
||||
api: [`${PLUGIN_ID}-read`, `${PLUGIN_ID}-all`],
|
||||
|
|
|
@ -8,7 +8,6 @@ import { schema } from '@kbn/config-schema';
|
|||
|
||||
import type { FleetAuthz } from '../../../common';
|
||||
import { API_VERSIONS } from '../../../common/constants';
|
||||
import { parseExperimentalConfigValue } from '../../../common/experimental_features';
|
||||
import { getRouteRequiredAuthz, type FleetAuthzRouter } from '../../services/security';
|
||||
|
||||
import { AGENT_API_ROUTES } from '../../constants';
|
||||
|
@ -854,36 +853,32 @@ export const registerAPIRoutes = (router: FleetAuthzRouter, config: FleetConfigT
|
|||
getAvailableVersionsHandler
|
||||
);
|
||||
|
||||
const experimentalFeatures = parseExperimentalConfigValue(config.enableExperimental);
|
||||
|
||||
// route used by export CSV feature on the UI to generate report
|
||||
if (experimentalFeatures.enableExportCSV) {
|
||||
router.versioned
|
||||
.get({
|
||||
path: '/internal/fleet/agents/status_runtime_field',
|
||||
access: 'internal',
|
||||
security: {
|
||||
authz: {
|
||||
requiredPrivileges: [FLEET_API_PRIVILEGES.AGENTS.READ],
|
||||
},
|
||||
router.versioned
|
||||
.get({
|
||||
path: '/internal/fleet/agents/status_runtime_field',
|
||||
access: 'internal',
|
||||
security: {
|
||||
authz: {
|
||||
requiredPrivileges: [FLEET_API_PRIVILEGES.AGENTS.READ],
|
||||
},
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.internal.v1,
|
||||
validate: {
|
||||
request: {},
|
||||
response: {
|
||||
200: {
|
||||
body: () => schema.string(),
|
||||
},
|
||||
400: {
|
||||
body: genericErrorResponse,
|
||||
},
|
||||
},
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.internal.v1,
|
||||
validate: {
|
||||
request: {},
|
||||
response: {
|
||||
200: {
|
||||
body: () => schema.string(),
|
||||
},
|
||||
400: {
|
||||
body: genericErrorResponse,
|
||||
},
|
||||
},
|
||||
},
|
||||
getAgentStatusRuntimeFieldHandler
|
||||
);
|
||||
}
|
||||
},
|
||||
getAgentStatusRuntimeFieldHandler
|
||||
);
|
||||
};
|
||||
|
|
|
@ -22,7 +22,6 @@ jest.mock('../../services', () => ({
|
|||
appContextService: {
|
||||
getSecurityLicense: jest.fn().mockReturnValue({ isEnabled: jest.fn().mockReturnValue(false) }),
|
||||
getCloud: jest.fn().mockReturnValue({ isServerlessEnabled: false } as any),
|
||||
getExperimentalFeatures: jest.fn().mockReturnValue({ subfeaturePrivileges: false }),
|
||||
getLogger: jest.fn().mockReturnValue({ debug: jest.fn(), error: jest.fn() } as any),
|
||||
},
|
||||
}));
|
||||
|
|
|
@ -33,12 +33,10 @@ export const getCheckPermissionsHandler: FleetRequestHandler<
|
|||
};
|
||||
|
||||
const isServerless = appContextService.getCloud()?.isServerlessEnabled;
|
||||
const isSubfeaturePrivilegesEnabled =
|
||||
appContextService.getExperimentalFeatures().subfeaturePrivileges ?? false;
|
||||
|
||||
if (!appContextService.getSecurityLicense().isEnabled()) {
|
||||
return response.ok({ body: missingSecurityBody });
|
||||
} else if (isSubfeaturePrivilegesEnabled) {
|
||||
} else {
|
||||
const fleetContext = await context.fleet;
|
||||
if (
|
||||
!fleetContext.authz.fleet.all &&
|
||||
|
@ -70,34 +68,6 @@ export const getCheckPermissionsHandler: FleetRequestHandler<
|
|||
}
|
||||
}
|
||||
|
||||
return response.ok({ body: { success: true } as CheckPermissionsResponse });
|
||||
} else {
|
||||
const fleetContext = await context.fleet;
|
||||
if (!fleetContext.authz.fleet.all) {
|
||||
return response.ok({
|
||||
body: {
|
||||
success: false,
|
||||
error: 'MISSING_PRIVILEGES',
|
||||
} as CheckPermissionsResponse,
|
||||
});
|
||||
}
|
||||
// check the manage_service_account cluster privilege only on stateful
|
||||
else if (request.query.fleetServerSetup && !isServerless) {
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const { has_all_requested: hasAllPrivileges } = await esClient.security.hasPrivileges({
|
||||
body: { cluster: ['manage_service_account'] },
|
||||
});
|
||||
|
||||
if (!hasAllPrivileges) {
|
||||
return response.ok({
|
||||
body: {
|
||||
success: false,
|
||||
error: 'MISSING_FLEET_SERVER_SETUP_PRIVILEGES',
|
||||
} as CheckPermissionsResponse,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return response.ok({ body: { success: true } as CheckPermissionsResponse });
|
||||
}
|
||||
};
|
||||
|
|
|
@ -403,9 +403,6 @@ describe('When calling package policy', () => {
|
|||
inputs: [],
|
||||
});
|
||||
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true);
|
||||
jest
|
||||
.spyOn(appContextService, 'getExperimentalFeatures')
|
||||
.mockReturnValue({ enableReusableIntegrationPolicies: true } as any);
|
||||
const request = getUpdateKibanaRequest({ policy_ids: ['1', '2'] } as any);
|
||||
|
||||
await expect(() => routeHandler(context, request, response)).rejects.toThrow(
|
||||
|
|
|
@ -39,6 +39,9 @@ jest.mock('../../services/setup', () => {
|
|||
});
|
||||
|
||||
jest.mock('../../services/fleet_server');
|
||||
jest.mock('../../services/secrets', () => ({
|
||||
isSecretStorageEnabled: jest.fn().mockResolvedValue(true),
|
||||
}));
|
||||
|
||||
const mockSetupFleet = setupFleet as jest.MockedFunction<typeof setupFleet>;
|
||||
|
||||
|
@ -199,7 +202,7 @@ describe('FleetStatusHandler', () => {
|
|||
|
||||
const expectedBody = {
|
||||
isReady: true,
|
||||
is_secrets_storage_enabled: false,
|
||||
is_secrets_storage_enabled: true,
|
||||
is_space_awareness_enabled: false,
|
||||
missing_optional_features: [],
|
||||
missing_requirements: [],
|
||||
|
@ -221,7 +224,7 @@ describe('FleetStatusHandler', () => {
|
|||
|
||||
const expectedBody = {
|
||||
isReady: false,
|
||||
is_secrets_storage_enabled: false,
|
||||
is_secrets_storage_enabled: true,
|
||||
is_space_awareness_enabled: false,
|
||||
missing_optional_features: [],
|
||||
missing_requirements: ['api_keys', 'fleet_server'],
|
||||
|
@ -252,7 +255,7 @@ describe('FleetStatusHandler', () => {
|
|||
|
||||
const expectedBody = {
|
||||
isReady: true,
|
||||
is_secrets_storage_enabled: false,
|
||||
is_secrets_storage_enabled: true,
|
||||
is_space_awareness_enabled: false,
|
||||
missing_optional_features: [],
|
||||
missing_requirements: [],
|
||||
|
|
|
@ -6,15 +6,10 @@
|
|||
*/
|
||||
|
||||
import type { TypeOf } from '@kbn/config-schema';
|
||||
import type { KibanaRequest, VersionedRouter } from '@kbn/core-http-server';
|
||||
import { httpServerMock, coreMock, loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import type { RequestHandler } from '@kbn/core/server';
|
||||
import type { KibanaRequest } from '@kbn/core-http-server';
|
||||
import { httpServerMock, coreMock } from '@kbn/core/server/mocks';
|
||||
|
||||
import {
|
||||
makeRouterWithFleetAuthz,
|
||||
withDefaultErrorHandler,
|
||||
} from '../../services/security/fleet_router';
|
||||
import type { FleetAuthzRouter } from '../../services/security/types';
|
||||
import { withDefaultErrorHandler } from '../../services/security/fleet_router';
|
||||
|
||||
import type {
|
||||
UninstallToken,
|
||||
|
@ -40,8 +35,6 @@ import {
|
|||
|
||||
import { createAgentPolicyMock } from '../../../common/mocks';
|
||||
|
||||
import { registerRoutes } from '.';
|
||||
|
||||
import { getUninstallTokenHandler, getUninstallTokensMetadataHandler } from './handlers';
|
||||
|
||||
const getUninstallTokenHandlerWithErrorHandler = withDefaultErrorHandler(getUninstallTokenHandler);
|
||||
|
@ -197,48 +190,4 @@ describe('uninstall token handlers', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: remove it when agentTamperProtectionEnabled FF is removed
|
||||
describe.skip('Agent Tamper Protection feature flag', () => {
|
||||
let config: { enableExperimental: string[] };
|
||||
let fakeRouter: jest.Mocked<VersionedRouter<FleetRequestHandlerContext>>;
|
||||
let fleetAuthzRouter: FleetAuthzRouter;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeRouter = {
|
||||
versioned: {
|
||||
get: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
addVersion: jest
|
||||
.fn()
|
||||
.mockImplementation((options: any, handler: RequestHandler) => Promise.resolve()),
|
||||
};
|
||||
}),
|
||||
},
|
||||
} as unknown as jest.Mocked<VersionedRouter<FleetRequestHandlerContext>>;
|
||||
|
||||
const mockLogger = loggingSystemMock.createLogger();
|
||||
fleetAuthzRouter = makeRouterWithFleetAuthz(fakeRouter as any, mockLogger);
|
||||
});
|
||||
|
||||
it('should register handlers if feature flag is enabled', () => {
|
||||
config = { enableExperimental: ['agentTamperProtectionEnabled'] };
|
||||
|
||||
registerRoutes(fleetAuthzRouter, config);
|
||||
const wrappedHandler =
|
||||
// @ts-ignore
|
||||
fakeRouter.versioned.get.mock.results[0].value.addVersion;
|
||||
|
||||
expect(wrappedHandler).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should NOT register handlers if feature flag is disabled', async () => {
|
||||
config = { enableExperimental: [] };
|
||||
registerRoutes(fleetAuthzRouter, config);
|
||||
// @ts-ignore
|
||||
const mockGet = fakeRouter.versioned.get;
|
||||
|
||||
expect(mockGet).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,78 +15,73 @@ import {
|
|||
GetUninstallTokensMetadataRequestSchema,
|
||||
GetUninstallTokensMetadataResponseSchema,
|
||||
} from '../../types/rest_spec/uninstall_token';
|
||||
import { parseExperimentalConfigValue } from '../../../common/experimental_features';
|
||||
|
||||
import { genericErrorResponse } from '../schema/errors';
|
||||
|
||||
import { getUninstallTokenHandler, getUninstallTokensMetadataHandler } from './handlers';
|
||||
|
||||
export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType) => {
|
||||
const experimentalFeatures = parseExperimentalConfigValue(config.enableExperimental);
|
||||
|
||||
if (experimentalFeatures.agentTamperProtectionEnabled) {
|
||||
router.versioned
|
||||
.get({
|
||||
path: UNINSTALL_TOKEN_ROUTES.LIST_PATTERN,
|
||||
security: {
|
||||
authz: {
|
||||
requiredPrivileges: [FLEET_API_PRIVILEGES.AGENTS.ALL],
|
||||
},
|
||||
router.versioned
|
||||
.get({
|
||||
path: UNINSTALL_TOKEN_ROUTES.LIST_PATTERN,
|
||||
security: {
|
||||
authz: {
|
||||
requiredPrivileges: [FLEET_API_PRIVILEGES.AGENTS.ALL],
|
||||
},
|
||||
summary: 'Get metadata for latest uninstall tokens',
|
||||
description: 'List the metadata for the latest uninstall tokens per agent policy.',
|
||||
options: {
|
||||
tags: ['oas-tag:Fleet uninstall tokens'],
|
||||
},
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.public.v1,
|
||||
validate: {
|
||||
request: GetUninstallTokensMetadataRequestSchema,
|
||||
response: {
|
||||
200: {
|
||||
body: () => GetUninstallTokensMetadataResponseSchema,
|
||||
},
|
||||
400: {
|
||||
body: genericErrorResponse,
|
||||
},
|
||||
},
|
||||
summary: 'Get metadata for latest uninstall tokens',
|
||||
description: 'List the metadata for the latest uninstall tokens per agent policy.',
|
||||
options: {
|
||||
tags: ['oas-tag:Fleet uninstall tokens'],
|
||||
},
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.public.v1,
|
||||
validate: {
|
||||
request: GetUninstallTokensMetadataRequestSchema,
|
||||
response: {
|
||||
200: {
|
||||
body: () => GetUninstallTokensMetadataResponseSchema,
|
||||
},
|
||||
400: {
|
||||
body: genericErrorResponse,
|
||||
},
|
||||
},
|
||||
},
|
||||
getUninstallTokensMetadataHandler
|
||||
);
|
||||
},
|
||||
getUninstallTokensMetadataHandler
|
||||
);
|
||||
|
||||
router.versioned
|
||||
.get({
|
||||
path: UNINSTALL_TOKEN_ROUTES.INFO_PATTERN,
|
||||
security: {
|
||||
authz: {
|
||||
requiredPrivileges: [FLEET_API_PRIVILEGES.AGENTS.ALL],
|
||||
},
|
||||
router.versioned
|
||||
.get({
|
||||
path: UNINSTALL_TOKEN_ROUTES.INFO_PATTERN,
|
||||
security: {
|
||||
authz: {
|
||||
requiredPrivileges: [FLEET_API_PRIVILEGES.AGENTS.ALL],
|
||||
},
|
||||
summary: 'Get a decrypted uninstall token',
|
||||
description: 'Get one decrypted uninstall token by its ID.',
|
||||
options: {
|
||||
tags: ['oas-tag:Fleet uninstall tokens'],
|
||||
},
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.public.v1,
|
||||
validate: {
|
||||
request: GetUninstallTokenRequestSchema,
|
||||
response: {
|
||||
200: {
|
||||
body: () => GetUninstallTokenResponseSchema,
|
||||
},
|
||||
400: {
|
||||
body: genericErrorResponse,
|
||||
},
|
||||
},
|
||||
summary: 'Get a decrypted uninstall token',
|
||||
description: 'Get one decrypted uninstall token by its ID.',
|
||||
options: {
|
||||
tags: ['oas-tag:Fleet uninstall tokens'],
|
||||
},
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.public.v1,
|
||||
validate: {
|
||||
request: GetUninstallTokenRequestSchema,
|
||||
response: {
|
||||
200: {
|
||||
body: () => GetUninstallTokenResponseSchema,
|
||||
},
|
||||
400: {
|
||||
body: genericErrorResponse,
|
||||
},
|
||||
},
|
||||
},
|
||||
getUninstallTokenHandler
|
||||
);
|
||||
}
|
||||
},
|
||||
getUninstallTokenHandler
|
||||
);
|
||||
};
|
||||
|
|
|
@ -9,8 +9,6 @@ import { get } from 'lodash';
|
|||
import * as esKuery from '@kbn/es-query';
|
||||
import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal';
|
||||
|
||||
import { appContextService } from '../../services/app_context';
|
||||
|
||||
type KueryNode = any;
|
||||
|
||||
const astFunctionType = ['is', 'range', 'nested'];
|
||||
|
@ -228,12 +226,7 @@ export const validateKuery = (
|
|||
) => {
|
||||
let isValid = true;
|
||||
let error: string | undefined;
|
||||
const { enableStrictKQLValidation } = appContextService.getExperimentalFeatures();
|
||||
|
||||
// Skip validation when enableStrictKQLValidation is disabled
|
||||
if (!enableStrictKQLValidation) {
|
||||
return { isValid, error };
|
||||
}
|
||||
if (!kuery) {
|
||||
isValid = true;
|
||||
}
|
||||
|
|
|
@ -19,12 +19,9 @@ import {
|
|||
|
||||
import { FLEET_ENROLLMENT_API_PREFIX } from '../../../common/constants';
|
||||
|
||||
import { appContextService } from '../../services/app_context';
|
||||
|
||||
import { validateFilterKueryNode, validateKuery } from './filter_utils';
|
||||
|
||||
jest.mock('../../services/app_context');
|
||||
const mockedAppContextService = appContextService as jest.Mocked<typeof appContextService>;
|
||||
|
||||
describe('ValidateFilterKueryNode validates real kueries through KueryNode', () => {
|
||||
describe('Agent policies', () => {
|
||||
|
@ -512,14 +509,6 @@ describe('ValidateFilterKueryNode validates real kueries through KueryNode', ()
|
|||
});
|
||||
|
||||
describe('validateKuery validates real kueries', () => {
|
||||
beforeEach(() => {
|
||||
mockedAppContextService.getExperimentalFeatures.mockReturnValue({
|
||||
enableStrictKQLValidation: true,
|
||||
} as any);
|
||||
});
|
||||
afterEach(() => {
|
||||
mockedAppContextService.getExperimentalFeatures.mockReset();
|
||||
});
|
||||
describe('Agent policies', () => {
|
||||
it('Search by data_output_id', async () => {
|
||||
const validationObj = validateKuery(
|
||||
|
@ -834,34 +823,4 @@ describe('validateKuery validates real kueries', () => {
|
|||
expect(validationObj?.error).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
describe('Feature flag enableStrictKQLValidation', () => {
|
||||
beforeEach(() => {
|
||||
mockedAppContextService.getExperimentalFeatures.mockReturnValue({
|
||||
enableStrictKQLValidation: false,
|
||||
} as any);
|
||||
});
|
||||
|
||||
it('Allows to skip validation for a free text query', async () => {
|
||||
const validationObj = validateKuery(`test`, [AGENTS_PREFIX], AGENT_MAPPINGS, true);
|
||||
expect(validationObj?.isValid).toEqual(true);
|
||||
expect(validationObj?.error).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('Allows to skip validation for a catch all query', async () => {
|
||||
const validationObj = validateKuery(`*`, [AGENTS_PREFIX], AGENT_MAPPINGS, true);
|
||||
expect(validationObj?.isValid).toEqual(true);
|
||||
expect(validationObj?.error).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('Allows to skip validation for a disallowed query too', async () => {
|
||||
const validationObj = validateKuery(
|
||||
`non_existent_parameter: 'test_id'`,
|
||||
[AGENTS_PREFIX],
|
||||
AGENT_MAPPINGS,
|
||||
true
|
||||
);
|
||||
expect(validationObj?.isValid).toEqual(true);
|
||||
expect(validationObj?.error).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -46,6 +46,7 @@ import { auditLoggingService } from './audit_logging';
|
|||
import { licenseService } from './license';
|
||||
import type { UninstallTokenServiceInterface } from './security/uninstall_token_service';
|
||||
import { isSpaceAwarenessEnabled } from './spaces/helpers';
|
||||
import { scheduleDeployAgentPoliciesTask } from './agent_policies/deploy_agent_policies_task';
|
||||
|
||||
jest.mock('./spaces/helpers');
|
||||
|
||||
|
@ -121,6 +122,7 @@ jest.mock('./app_context');
|
|||
jest.mock('./audit_logging');
|
||||
jest.mock('./agent_policies/full_agent_policy');
|
||||
jest.mock('./agent_policies/outputs_helpers');
|
||||
jest.mock('./agent_policies/deploy_agent_policies_task');
|
||||
|
||||
const mockedAppContextService = appContextService as jest.Mocked<typeof appContextService>;
|
||||
mockedAppContextService.getSecuritySetup.mockImplementation(() => ({
|
||||
|
@ -591,241 +593,130 @@ describe('Agent policy', () => {
|
|||
let soClient: ReturnType<typeof savedObjectsClientMock.create>;
|
||||
let esClient: ReturnType<typeof elasticsearchServiceMock.createClusterClient>['asInternalUser'];
|
||||
|
||||
describe('with enableReusableIntegrationPolicies disabled', () => {
|
||||
beforeEach(() => {
|
||||
soClient = getSavedObjectMock({ revision: 1, package_policies: ['package-1'] });
|
||||
mockedPackagePolicyService.create.mockReset();
|
||||
esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
beforeEach(() => {
|
||||
soClient = getSavedObjectMock({ revision: 1, package_policies: ['package-1'] });
|
||||
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
|
||||
{
|
||||
id: 'package-1',
|
||||
},
|
||||
] as any);
|
||||
esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
|
||||
(getAgentsByKuery as jest.Mock).mockResolvedValue({
|
||||
agents: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
});
|
||||
|
||||
mockedPackagePolicyService.delete.mockResolvedValue([
|
||||
{
|
||||
id: 'package-1',
|
||||
} as any,
|
||||
]);
|
||||
jest
|
||||
.spyOn(appContextService, 'getExperimentalFeatures')
|
||||
.mockReturnValue({ enableReusableIntegrationPolicies: false } as any);
|
||||
(getAgentsByKuery as jest.Mock).mockResolvedValue({
|
||||
agents: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
});
|
||||
mockedPackagePolicyService.create.mockReset();
|
||||
});
|
||||
|
||||
it('should throw error for agent policy which has managed package policy', async () => {
|
||||
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
|
||||
{
|
||||
id: 'package-1',
|
||||
is_managed: true,
|
||||
},
|
||||
] as any);
|
||||
try {
|
||||
await agentPolicyService.delete(soClient, esClient, 'mocked');
|
||||
} catch (e) {
|
||||
expect(e.message).toEqual(
|
||||
new PackagePolicyRestrictionRelatedError(
|
||||
`Cannot delete agent policy mocked that contains managed package policies`
|
||||
).message
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow delete with force for agent policy which has managed package policy', async () => {
|
||||
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
|
||||
{
|
||||
id: 'package-1',
|
||||
is_managed: true,
|
||||
},
|
||||
] as any);
|
||||
const response = await agentPolicyService.delete(soClient, esClient, 'mocked', {
|
||||
force: true,
|
||||
});
|
||||
expect(response.id).toEqual('mocked');
|
||||
});
|
||||
|
||||
it('should call audit logger', async () => {
|
||||
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
|
||||
{
|
||||
id: 'package-1',
|
||||
},
|
||||
] as any);
|
||||
it('should throw error for agent policy which has managed package policy', async () => {
|
||||
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
|
||||
{
|
||||
id: 'package-1',
|
||||
is_managed: true,
|
||||
},
|
||||
] as any);
|
||||
try {
|
||||
await agentPolicyService.delete(soClient, esClient, 'mocked');
|
||||
|
||||
expect(mockedAuditLoggingService.writeCustomSoAuditLog).toHaveBeenCalledWith({
|
||||
action: 'delete',
|
||||
id: 'mocked',
|
||||
savedObjectType: LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE,
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw error if active agents are assigned to the policy', async () => {
|
||||
(getAgentsByKuery as jest.Mock).mockResolvedValue({
|
||||
agents: [],
|
||||
total: 2,
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
});
|
||||
await expect(agentPolicyService.delete(soClient, esClient, 'mocked')).rejects.toThrowError(
|
||||
'Cannot delete an agent policy that is assigned to any active or inactive agents'
|
||||
} catch (e) {
|
||||
expect(e.message).toEqual(
|
||||
new PackagePolicyRestrictionRelatedError(
|
||||
`Cannot delete agent policy mocked that contains managed package policies`
|
||||
).message
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow delete with force for agent policy which has managed package policy', async () => {
|
||||
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
|
||||
{
|
||||
id: 'package-1',
|
||||
is_managed: true,
|
||||
},
|
||||
] as any);
|
||||
const response = await agentPolicyService.delete(soClient, esClient, 'mocked', {
|
||||
force: true,
|
||||
});
|
||||
expect(response.id).toEqual('mocked');
|
||||
});
|
||||
|
||||
it('should delete .fleet-policies entries on agent policy delete', async () => {
|
||||
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
|
||||
{
|
||||
id: 'package-1',
|
||||
},
|
||||
] as any);
|
||||
esClient.deleteByQuery.mockResolvedValueOnce({
|
||||
deleted: 2,
|
||||
});
|
||||
it('should call audit logger', async () => {
|
||||
await agentPolicyService.delete(soClient, esClient, 'mocked');
|
||||
|
||||
await agentPolicyService.delete(soClient, esClient, 'mocked');
|
||||
|
||||
expect(esClient.deleteByQuery).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
index: AGENT_POLICY_INDEX,
|
||||
query: {
|
||||
term: {
|
||||
policy_id: 'mocked',
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(mockedAuditLoggingService.writeCustomSoAuditLog).toHaveBeenCalledWith({
|
||||
action: 'delete',
|
||||
id: 'mocked',
|
||||
savedObjectType: LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE,
|
||||
});
|
||||
});
|
||||
|
||||
describe('with enableReusableIntegrationPolicies enabled', () => {
|
||||
beforeEach(() => {
|
||||
soClient = getSavedObjectMock({ revision: 1, package_policies: ['package-1'] });
|
||||
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
|
||||
{
|
||||
id: 'package-1',
|
||||
},
|
||||
] as any);
|
||||
esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
it('should throw error if active agents are assigned to the policy', async () => {
|
||||
(getAgentsByKuery as jest.Mock).mockResolvedValue({
|
||||
agents: [],
|
||||
total: 2,
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
});
|
||||
await expect(agentPolicyService.delete(soClient, esClient, 'mocked')).rejects.toThrowError(
|
||||
'Cannot delete an agent policy that is assigned to any active or inactive agents'
|
||||
);
|
||||
});
|
||||
|
||||
(getAgentsByKuery as jest.Mock).mockResolvedValue({
|
||||
agents: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
});
|
||||
mockedPackagePolicyService.create.mockReset();
|
||||
jest
|
||||
.spyOn(appContextService, 'getExperimentalFeatures')
|
||||
.mockReturnValue({ enableReusableIntegrationPolicies: true } as any);
|
||||
it('should delete .fleet-policies entries on agent policy delete', async () => {
|
||||
esClient.deleteByQuery.mockResolvedValueOnce({
|
||||
deleted: 2,
|
||||
});
|
||||
|
||||
it('should throw error for agent policy which has managed package policy', async () => {
|
||||
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
|
||||
{
|
||||
id: 'package-1',
|
||||
is_managed: true,
|
||||
},
|
||||
] as any);
|
||||
try {
|
||||
await agentPolicyService.delete(soClient, esClient, 'mocked');
|
||||
} catch (e) {
|
||||
expect(e.message).toEqual(
|
||||
new PackagePolicyRestrictionRelatedError(
|
||||
`Cannot delete agent policy mocked that contains managed package policies`
|
||||
).message
|
||||
);
|
||||
}
|
||||
});
|
||||
await agentPolicyService.delete(soClient, esClient, 'mocked');
|
||||
|
||||
it('should allow delete with force for agent policy which has managed package policy', async () => {
|
||||
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
|
||||
{
|
||||
id: 'package-1',
|
||||
is_managed: true,
|
||||
},
|
||||
] as any);
|
||||
const response = await agentPolicyService.delete(soClient, esClient, 'mocked', {
|
||||
force: true,
|
||||
});
|
||||
expect(response.id).toEqual('mocked');
|
||||
});
|
||||
|
||||
it('should call audit logger', async () => {
|
||||
await agentPolicyService.delete(soClient, esClient, 'mocked');
|
||||
|
||||
expect(mockedAuditLoggingService.writeCustomSoAuditLog).toHaveBeenCalledWith({
|
||||
action: 'delete',
|
||||
id: 'mocked',
|
||||
savedObjectType: LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE,
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw error if active agents are assigned to the policy', async () => {
|
||||
(getAgentsByKuery as jest.Mock).mockResolvedValue({
|
||||
agents: [],
|
||||
total: 2,
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
});
|
||||
await expect(agentPolicyService.delete(soClient, esClient, 'mocked')).rejects.toThrowError(
|
||||
'Cannot delete an agent policy that is assigned to any active or inactive agents'
|
||||
);
|
||||
});
|
||||
|
||||
it('should delete .fleet-policies entries on agent policy delete', async () => {
|
||||
esClient.deleteByQuery.mockResolvedValueOnce({
|
||||
deleted: 2,
|
||||
});
|
||||
|
||||
await agentPolicyService.delete(soClient, esClient, 'mocked');
|
||||
|
||||
expect(esClient.deleteByQuery).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
index: AGENT_POLICY_INDEX,
|
||||
query: {
|
||||
term: {
|
||||
policy_id: 'mocked',
|
||||
},
|
||||
expect(esClient.deleteByQuery).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
index: AGENT_POLICY_INDEX,
|
||||
query: {
|
||||
term: {
|
||||
policy_id: 'mocked',
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should only delete package polices that are not shared with other agent policies', async () => {
|
||||
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
|
||||
it('should only delete package polices that are not shared with other agent policies', async () => {
|
||||
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
|
||||
{
|
||||
id: 'package-1',
|
||||
policy_id: ['policy_1'],
|
||||
policy_ids: ['policy_1', 'int_policy_2'],
|
||||
},
|
||||
{
|
||||
id: 'package-2',
|
||||
policy_id: ['policy_1'],
|
||||
policy_ids: ['policy_1'],
|
||||
},
|
||||
{
|
||||
id: 'package-3',
|
||||
},
|
||||
] as any);
|
||||
await agentPolicyService.delete(soClient, esClient, 'policy_1');
|
||||
expect(mockedPackagePolicyService.delete).toBeCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
['package-2', 'package-3'],
|
||||
expect.anything()
|
||||
);
|
||||
expect(mockedPackagePolicyService.bulkUpdate).toBeCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
[
|
||||
{
|
||||
id: 'package-1',
|
||||
policy_id: ['policy_1'],
|
||||
policy_ids: ['policy_1', 'int_policy_2'],
|
||||
policy_id: 'int_policy_2',
|
||||
policy_ids: ['int_policy_2'],
|
||||
},
|
||||
{
|
||||
id: 'package-2',
|
||||
policy_id: ['policy_1'],
|
||||
policy_ids: ['policy_1'],
|
||||
},
|
||||
{
|
||||
id: 'package-3',
|
||||
},
|
||||
] as any);
|
||||
await agentPolicyService.delete(soClient, esClient, 'policy_1');
|
||||
expect(mockedPackagePolicyService.delete).toBeCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
['package-2', 'package-3'],
|
||||
expect.anything()
|
||||
);
|
||||
expect(mockedPackagePolicyService.bulkUpdate).toBeCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
[
|
||||
{
|
||||
id: 'package-1',
|
||||
policy_id: 'int_policy_2',
|
||||
policy_ids: ['int_policy_2'],
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -844,7 +735,7 @@ describe('Agent policy', () => {
|
|||
});
|
||||
|
||||
describe('bumpAllAgentPolicies', () => {
|
||||
it('should call agentPolicyUpdateEventHandler with updated event once', async () => {
|
||||
it('should call scheduleDeployAgentPoliciesTask with updated event once', async () => {
|
||||
const soClient = getSavedObjectMock({
|
||||
revision: 1,
|
||||
monitoring_enabled: ['metrics'],
|
||||
|
@ -872,12 +763,12 @@ describe('Agent policy', () => {
|
|||
})
|
||||
);
|
||||
|
||||
expect(agentPolicyUpdateEventHandler).toHaveBeenCalledTimes(1);
|
||||
expect(scheduleDeployAgentPoliciesTask).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bumpAllAgentPoliciesForOutput', () => {
|
||||
it('should call agentPolicyUpdateEventHandler with updated event once', async () => {
|
||||
it('should call scheduleDeployAgentPoliciesTask with updated event once', async () => {
|
||||
const soClient = getSavedObjectMock({
|
||||
revision: 1,
|
||||
monitoring_enabled: ['metrics'],
|
||||
|
@ -888,7 +779,7 @@ describe('Agent policy', () => {
|
|||
);
|
||||
await agentPolicyService.bumpAllAgentPoliciesForOutput(esClient, 'output-id-123');
|
||||
|
||||
expect(agentPolicyUpdateEventHandler).toHaveBeenCalledTimes(1);
|
||||
expect(scheduleDeployAgentPoliciesTask).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1030,7 +921,7 @@ describe('Agent policy', () => {
|
|||
});
|
||||
|
||||
describe('bumpAllAgentPoliciesForDownloadSource', () => {
|
||||
it('should call agentPolicyUpdateEventHandler with updated event once', async () => {
|
||||
it('should call scheduleDeployAgentPoliciesTask with updated event once', async () => {
|
||||
const soClient = getSavedObjectMock({
|
||||
revision: 1,
|
||||
monitoring_enabled: ['metrics'],
|
||||
|
@ -1041,7 +932,7 @@ describe('Agent policy', () => {
|
|||
);
|
||||
await agentPolicyService.bumpAllAgentPoliciesForDownloadSource(esClient, 'test-id-1');
|
||||
|
||||
expect(agentPolicyUpdateEventHandler).toHaveBeenCalledTimes(1);
|
||||
expect(scheduleDeployAgentPoliciesTask).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { chunk, groupBy, isEqual, keyBy, omit, pick, uniq } from 'lodash';
|
||||
import { groupBy, isEqual, keyBy, omit, pick, uniq } from 'lodash';
|
||||
import { v5 as uuidv5 } from 'uuid';
|
||||
import { dump } from 'js-yaml';
|
||||
import pMap from 'p-map';
|
||||
|
@ -26,8 +26,6 @@ import type { BulkResponseItem } from '@elastic/elasticsearch/lib/api/types';
|
|||
|
||||
import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants';
|
||||
|
||||
import { asyncForEach } from '@kbn/std';
|
||||
|
||||
import type { SavedObjectError } from '@kbn/core-saved-objects-common';
|
||||
|
||||
import { withSpan } from '@kbn/apm-utils';
|
||||
|
@ -1073,24 +1071,13 @@ class AgentPolicyService {
|
|||
}
|
||||
);
|
||||
|
||||
if (appContextService.getExperimentalFeatures().asyncDeployPolicies) {
|
||||
await scheduleDeployAgentPoliciesTask(
|
||||
appContextService.getTaskManagerStart()!,
|
||||
savedObjectsResults.map((policy) => ({
|
||||
id: policy.id,
|
||||
spaceId: policy.namespaces?.[0],
|
||||
}))
|
||||
);
|
||||
} else {
|
||||
await pMap(
|
||||
savedObjectsResults,
|
||||
(policy) =>
|
||||
this.triggerAgentPolicyUpdatedEvent(esClient, 'updated', policy.id, {
|
||||
spaceId: policy.namespaces?.[0],
|
||||
}),
|
||||
{ concurrency: MAX_CONCURRENT_AGENT_POLICIES_OPERATIONS }
|
||||
);
|
||||
}
|
||||
await scheduleDeployAgentPoliciesTask(
|
||||
appContextService.getTaskManagerStart()!,
|
||||
savedObjectsResults.map((policy) => ({
|
||||
id: policy.id,
|
||||
spaceId: policy.namespaces?.[0],
|
||||
}))
|
||||
);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -1750,24 +1737,13 @@ class AgentPolicyService {
|
|||
|
||||
const updatedPoliciesSuccess = updatedAgentPolicies.filter((policy) => !policy.error);
|
||||
|
||||
const config = appContextService.getConfig();
|
||||
const batchSize = config?.setup?.agentPolicySchemaUpgradeBatchSize ?? 100;
|
||||
const policyIds = updatedPoliciesSuccess.map((policy) => policy.id);
|
||||
|
||||
if (appContextService.getExperimentalFeatures().asyncDeployPolicies) {
|
||||
await scheduleDeployAgentPoliciesTask(
|
||||
appContextService.getTaskManagerStart()!,
|
||||
updatedPoliciesSuccess.map((policy) => ({
|
||||
id: policy.id,
|
||||
spaceId: policy.namespaces?.[0],
|
||||
}))
|
||||
);
|
||||
} else {
|
||||
await asyncForEach(
|
||||
chunk(policyIds, batchSize),
|
||||
async (policyIdsBatch) => await this.deployPolicies(soClient, policyIdsBatch)
|
||||
);
|
||||
}
|
||||
await scheduleDeployAgentPoliciesTask(
|
||||
appContextService.getTaskManagerStart()!,
|
||||
updatedPoliciesSuccess.map((policy) => ({
|
||||
id: policy.id,
|
||||
spaceId: policy.namespaces?.[0],
|
||||
}))
|
||||
);
|
||||
|
||||
return { updatedPolicies: updatedPoliciesSuccess, failedPolicies };
|
||||
}
|
||||
|
|
|
@ -364,11 +364,11 @@ export async function getPackage(
|
|||
verificationResult?: PackageVerificationResult;
|
||||
}> {
|
||||
const logger = appContextService.getLogger();
|
||||
const verifyPackage = appContextService.getExperimentalFeatures().packageVerification;
|
||||
let packageInfo: ArchivePackage | undefined = getPackageInfo({ name, version });
|
||||
let verificationResult: PackageVerificationResult | undefined = verifyPackage
|
||||
? getVerificationResult({ name, version })
|
||||
: undefined;
|
||||
let verificationResult: PackageVerificationResult | undefined = getVerificationResult({
|
||||
name,
|
||||
version,
|
||||
});
|
||||
try {
|
||||
const {
|
||||
archiveBuffer,
|
||||
|
@ -378,7 +378,7 @@ export async function getPackage(
|
|||
fetchArchiveBuffer({
|
||||
pkgName: name,
|
||||
pkgVersion: version,
|
||||
shouldVerify: verifyPackage,
|
||||
shouldVerify: true,
|
||||
ignoreUnverified: options?.ignoreUnverified,
|
||||
})
|
||||
);
|
||||
|
|
|
@ -26,6 +26,7 @@ jest.mock('./app_context');
|
|||
jest.mock('./agent_policy');
|
||||
jest.mock('./package_policy');
|
||||
jest.mock('./audit_logging');
|
||||
jest.mock('./secrets');
|
||||
|
||||
const mockedAuditLoggingService = auditLoggingService as jest.Mocked<typeof auditLoggingService>;
|
||||
const mockedAppContextService = appContextService as jest.Mocked<typeof appContextService>;
|
||||
|
|
|
@ -75,9 +75,6 @@ describe('Package Policy Utils', () => {
|
|||
|
||||
it('should throw if no enterprise license and multiple policy_ids is provided', async () => {
|
||||
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(false);
|
||||
jest
|
||||
.spyOn(appContextService, 'getExperimentalFeatures')
|
||||
.mockReturnValue({ enableReusableIntegrationPolicies: true } as any);
|
||||
|
||||
await expect(
|
||||
preflightCheckPackagePolicy(soClient, { ...testPolicy, policy_ids: ['1', '2'] })
|
||||
|
@ -88,9 +85,6 @@ describe('Package Policy Utils', () => {
|
|||
|
||||
it('should throw if no enterprise license and no policy_ids is provided', async () => {
|
||||
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(false);
|
||||
jest
|
||||
.spyOn(appContextService, 'getExperimentalFeatures')
|
||||
.mockReturnValue({ enableReusableIntegrationPolicies: true } as any);
|
||||
|
||||
await expect(
|
||||
preflightCheckPackagePolicy(soClient, { ...testPolicy, policy_ids: [] })
|
||||
|
@ -99,22 +93,8 @@ describe('Package Policy Utils', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should throw if enterprise license and multiple policy_ids is provided but no feature flag', async () => {
|
||||
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true);
|
||||
jest
|
||||
.spyOn(appContextService, 'getExperimentalFeatures')
|
||||
.mockReturnValue({ enableReusableIntegrationPolicies: false } as any);
|
||||
|
||||
await expect(
|
||||
preflightCheckPackagePolicy(soClient, { ...testPolicy, policy_ids: ['1', '2'] })
|
||||
).rejects.toThrowError('Reusable integration policies are not supported');
|
||||
});
|
||||
|
||||
it('should not throw if enterprise license and multiple policy_ids is provided', async () => {
|
||||
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true);
|
||||
jest
|
||||
.spyOn(appContextService, 'getExperimentalFeatures')
|
||||
.mockReturnValue({ enableReusableIntegrationPolicies: true } as any);
|
||||
await expect(
|
||||
preflightCheckPackagePolicy(soClient, { ...testPolicy, policy_ids: ['1', '2'] })
|
||||
).resolves.not.toThrow();
|
||||
|
@ -122,9 +102,6 @@ describe('Package Policy Utils', () => {
|
|||
|
||||
it('should not throw if enterprise license and no policy_ids is provided', async () => {
|
||||
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true);
|
||||
jest
|
||||
.spyOn(appContextService, 'getExperimentalFeatures')
|
||||
.mockReturnValue({ enableReusableIntegrationPolicies: true } as any);
|
||||
await expect(
|
||||
preflightCheckPackagePolicy(soClient, { ...testPolicy, policy_ids: [] })
|
||||
).resolves.not.toThrow();
|
||||
|
|
|
@ -26,7 +26,6 @@ import {
|
|||
} from '../../errors';
|
||||
import { licenseService } from '../license';
|
||||
import { outputService } from '../output';
|
||||
import { appContextService } from '../app_context';
|
||||
|
||||
export const mapPackagePolicySavedObjectToPackagePolicy = (
|
||||
{ id, version, attributes }: SavedObject<PackagePolicySOAttributes>,
|
||||
|
@ -75,10 +74,9 @@ export async function preflightCheckPackagePolicy(
|
|||
|
||||
export function canUseMultipleAgentPolicies() {
|
||||
const hasEnterpriseLicence = licenseService.hasAtLeast(LICENCE_FOR_MULTIPLE_AGENT_POLICIES);
|
||||
const { enableReusableIntegrationPolicies } = appContextService.getExperimentalFeatures();
|
||||
|
||||
return {
|
||||
canUseReusablePolicies: hasEnterpriseLicence && enableReusableIntegrationPolicies,
|
||||
canUseReusablePolicies: hasEnterpriseLicence,
|
||||
errorMessage: !hasEnterpriseLicence
|
||||
? 'Reusable integration policies are only available with an Enterprise license'
|
||||
: 'Reusable integration policies are not supported',
|
||||
|
|
|
@ -208,6 +208,10 @@ jest.mock('./upgrade_sender', () => {
|
|||
jest.mock('./audit_logging');
|
||||
const mockedAuditLoggingService = auditLoggingService as jest.Mocked<typeof auditLoggingService>;
|
||||
|
||||
jest.mock('./secrets', () => ({
|
||||
isSecretStorageEnabled: jest.fn(),
|
||||
}));
|
||||
|
||||
type CombinedExternalCallback = PutPackagePolicyUpdateCallback | PostPackagePolicyCreateCallback;
|
||||
|
||||
const mockAgentPolicyGet = (spaceIds: string[] = ['default']) => {
|
||||
|
|
|
@ -44,6 +44,7 @@ jest.mock('../app_context', () => ({
|
|||
},
|
||||
}
|
||||
),
|
||||
getTaskManagerStart: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
|
@ -604,13 +604,6 @@ export async function isSecretStorageEnabled(
|
|||
): Promise<boolean> {
|
||||
const logger = appContextService.getLogger();
|
||||
|
||||
// first check if the feature flag is enabled, if not secrets are disabled
|
||||
const { secretsStorage: secretsStorageEnabled } = appContextService.getExperimentalFeatures();
|
||||
if (!secretsStorageEnabled) {
|
||||
logger.debug('Secrets storage is disabled by feature flag');
|
||||
return false;
|
||||
}
|
||||
|
||||
// if serverless then secrets will always be supported
|
||||
const isFleetServerStandalone =
|
||||
appContextService.getConfig()?.internal?.fleetServerStandalone ?? false;
|
||||
|
@ -660,14 +653,6 @@ export async function isOutputSecretStorageEnabled(
|
|||
): Promise<boolean> {
|
||||
const logger = appContextService.getLogger();
|
||||
|
||||
// first check if the feature flag is enabled, if not output secrets are disabled
|
||||
const { outputSecretsStorage: outputSecretsStorageEnabled } =
|
||||
appContextService.getExperimentalFeatures();
|
||||
if (!outputSecretsStorageEnabled) {
|
||||
logger.debug('Output secrets storage is disabled by feature flag');
|
||||
return false;
|
||||
}
|
||||
|
||||
// if serverless then output secrets will always be supported
|
||||
const isFleetServerStandalone =
|
||||
appContextService.getConfig()?.internal?.fleetServerStandalone ?? false;
|
||||
|
|
|
@ -584,9 +584,6 @@ describe('getAuthzFromRequest', () => {
|
|||
jest.mocked(appContextService.getSecurityLicense).mockReturnValue({
|
||||
isEnabled: () => true,
|
||||
} as any);
|
||||
jest.mocked(appContextService.getExperimentalFeatures).mockReturnValue({
|
||||
subfeaturePrivileges: true,
|
||||
} as any);
|
||||
});
|
||||
it('should not authorize access if RBAC is not enabled', async () => {
|
||||
mockSecurity.authz.mode.useRbacForRequest.mockReturnValue(false);
|
||||
|
|
|
@ -78,8 +78,6 @@ const FLEET_SUBFEATURES = ['agents', 'agent-policies', 'settings'];
|
|||
|
||||
export async function getAuthzFromRequest(req: KibanaRequest): Promise<FleetAuthz> {
|
||||
const security = appContextService.getSecurity();
|
||||
const isSubfeatureEnabled =
|
||||
appContextService.getExperimentalFeatures()?.subfeaturePrivileges ?? false;
|
||||
|
||||
if (security.authz.mode.useRbacForRequest(req)) {
|
||||
const checkPrivileges = security.authz.checkPrivilegesDynamicallyWithRequest(req);
|
||||
|
@ -180,7 +178,7 @@ export async function getAuthzFromRequest(req: KibanaRequest): Promise<FleetAuth
|
|||
all: intAllAuth,
|
||||
read: intReadAuth,
|
||||
},
|
||||
subfeatureEnabled: isSubfeatureEnabled,
|
||||
subfeatureEnabled: true,
|
||||
}),
|
||||
packagePrivileges: calculatePackagePrivilegesFromKibanaPrivileges(privileges.kibana),
|
||||
endpointExceptionsPrivileges: calculateEndpointExceptionsPrivilegesFromKibanaPrivileges(
|
||||
|
@ -195,7 +193,7 @@ export async function getAuthzFromRequest(req: KibanaRequest): Promise<FleetAuth
|
|||
all: false,
|
||||
read: false,
|
||||
},
|
||||
subfeatureEnabled: isSubfeatureEnabled,
|
||||
subfeatureEnabled: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -769,31 +769,6 @@ describe('UninstallTokenService', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('agentTamperProtectionEnabled false', () => {
|
||||
beforeAll(() => {
|
||||
// @ts-ignore
|
||||
mockContext.experimentalFeatures.agentTamperProtectionEnabled = false;
|
||||
});
|
||||
|
||||
it('generateTokensForPolicyIds should not generate token if agentTamperProtectionEnabled: false', async () => {
|
||||
const so = getDefaultSO();
|
||||
await uninstallTokenService.generateTokensForPolicyIds([so.attributes.policy_id]);
|
||||
expect(soClientMock.bulkCreate).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('generateTokensForAllPolicies should not generate token if agentTamperProtectionEnabled: false', async () => {
|
||||
mockAgentPolicyFetchAllAgentPolicyIds(canEncrypt);
|
||||
await uninstallTokenService.generateTokensForAllPolicies();
|
||||
expect(soClientMock.bulkCreate).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('generateTokenForPolicyId should not generate token if agentTamperProtectionEnabled: false', async () => {
|
||||
const so = getDefaultSO();
|
||||
await uninstallTokenService.generateTokenForPolicyId(so.attributes.policy_id);
|
||||
expect(soClientMock.bulkCreate).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('check validity of tokens', () => {
|
||||
|
|
|
@ -520,9 +520,7 @@ export class UninstallTokenService implements UninstallTokenServiceInterface {
|
|||
policyIds: string[],
|
||||
force: boolean = false
|
||||
): Promise<void> {
|
||||
const { agentTamperProtectionEnabled } = appContextService.getExperimentalFeatures();
|
||||
|
||||
if (!agentTamperProtectionEnabled || !policyIds.length) {
|
||||
if (!policyIds.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,12 +12,15 @@ import { appContextService } from '../app_context';
|
|||
import { agentPolicyService } from '../agent_policy';
|
||||
import { ensureDefaultEnrollmentAPIKeyForAgentPolicy } from '../api_keys';
|
||||
|
||||
import { scheduleDeployAgentPoliciesTask } from '../agent_policies/deploy_agent_policies_task';
|
||||
|
||||
import { ensureAgentPoliciesFleetServerKeysAndPolicies } from './fleet_server_policies_enrollment_keys';
|
||||
|
||||
jest.mock('../app_context');
|
||||
jest.mock('../agent_policy');
|
||||
jest.mock('../api_keys');
|
||||
jest.mock('../agent_policies/bump_agent_policies_task');
|
||||
jest.mock('../agent_policies/deploy_agent_policies_task');
|
||||
|
||||
const mockedEnsureDefaultEnrollmentAPIKeyForAgentPolicy = jest.mocked(
|
||||
ensureDefaultEnrollmentAPIKeyForAgentPolicy
|
||||
|
@ -30,9 +33,6 @@ describe('ensureAgentPoliciesFleetServerKeysAndPolicies', () => {
|
|||
jest.mocked(appContextService).getSecurity.mockReturnValue({
|
||||
authc: { apiKeys: { areAPIKeysEnabled: async () => true } },
|
||||
} as any);
|
||||
jest.mocked(appContextService).getExperimentalFeatures.mockReturnValue({
|
||||
asyncDeployPolicies: false,
|
||||
} as any);
|
||||
|
||||
mockedEnsureDefaultEnrollmentAPIKeyForAgentPolicy.mockReset();
|
||||
mockedAgentPolicyService.getLatestFleetPolicy.mockReset();
|
||||
|
@ -108,7 +108,9 @@ describe('ensureAgentPoliciesFleetServerKeysAndPolicies', () => {
|
|||
});
|
||||
|
||||
expect(mockedEnsureDefaultEnrollmentAPIKeyForAgentPolicy).toBeCalledTimes(2);
|
||||
expect(mockedAgentPolicyService.deployPolicies).toBeCalledWith(expect.anything(), ['policy2']);
|
||||
expect(scheduleDeployAgentPoliciesTask).toBeCalledWith(undefined, [
|
||||
{ id: 'policy2', spaceId: undefined },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should do deploy policies never deployed', async () => {
|
||||
|
@ -132,36 +134,8 @@ describe('ensureAgentPoliciesFleetServerKeysAndPolicies', () => {
|
|||
});
|
||||
|
||||
expect(mockedEnsureDefaultEnrollmentAPIKeyForAgentPolicy).toBeCalledTimes(2);
|
||||
expect(mockedAgentPolicyService.deployPolicies).toBeCalledWith(expect.anything(), ['policy2']);
|
||||
});
|
||||
|
||||
it('handle errors when deploying policies', async () => {
|
||||
const logger = loggingSystemMock.createLogger();
|
||||
const esClient = elasticsearchServiceMock.createInternalClient();
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
mockedAgentPolicyService.getLatestFleetPolicy.mockImplementation(async (_, agentPolicyId) => {
|
||||
if (agentPolicyId === 'policy1') {
|
||||
return {
|
||||
revision_idx: 1,
|
||||
} as any;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
mockedAgentPolicyService.deployPolicies.mockRejectedValue(new Error('test rejection'));
|
||||
|
||||
await ensureAgentPoliciesFleetServerKeysAndPolicies({
|
||||
logger,
|
||||
esClient,
|
||||
soClient,
|
||||
});
|
||||
|
||||
expect(mockedEnsureDefaultEnrollmentAPIKeyForAgentPolicy).toBeCalledTimes(2);
|
||||
expect(mockedAgentPolicyService.deployPolicies).toBeCalledWith(expect.anything(), ['policy2']);
|
||||
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
'Error deploying policies: test rejection',
|
||||
expect.anything()
|
||||
);
|
||||
expect(scheduleDeployAgentPoliciesTask).toBeCalledWith(undefined, [
|
||||
{ id: 'policy2', spaceId: undefined },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -58,20 +58,9 @@ export async function ensureAgentPoliciesFleetServerKeysAndPolicies({
|
|||
await scheduleBumpAgentPoliciesTask(appContextService.getTaskManagerStart()!);
|
||||
|
||||
if (outdatedAgentPolicyIds.length) {
|
||||
if (appContextService.getExperimentalFeatures().asyncDeployPolicies) {
|
||||
return scheduleDeployAgentPoliciesTask(
|
||||
appContextService.getTaskManagerStart()!,
|
||||
outdatedAgentPolicyIds
|
||||
);
|
||||
} else {
|
||||
return agentPolicyService
|
||||
.deployPolicies(
|
||||
soClient,
|
||||
outdatedAgentPolicyIds.map(({ id }) => id)
|
||||
)
|
||||
.catch((error) => {
|
||||
logger.warn(`Error deploying policies: ${error.message}`, { error });
|
||||
});
|
||||
}
|
||||
return scheduleDeployAgentPoliciesTask(
|
||||
appContextService.getTaskManagerStart()!,
|
||||
outdatedAgentPolicyIds
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue