Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import { useLink, useConfig, useAuthz, useStartServices } from '../../hooks';
import { WithHeaderLayout } from '../../../../layouts';

import { AutoUpgradeAgentsTour } from '../../sections/agent_policy/components/auto_upgrade_agents_tour';

import { ExperimentalFeaturesService } from '../../services';
import { useCanEnableAutomaticAgentUpgrades } from '../../../../hooks/use_can_enable_auto_upgrades';

import { DefaultPageTitle } from './default_page_title';

Expand All @@ -36,7 +35,7 @@ export const DefaultLayout: React.FunctionComponent<Props> = ({
const authz = useAuthz();
const { docLinks } = useStartServices();
const granularPrivilegesCallout = useDismissableTour('GRANULAR_PRIVILEGES');
const { enableAutomaticAgentUpgrades } = ExperimentalFeaturesService.get();
const canEnableAutomaticAgentUpgrades = useCanEnableAutomaticAgentUpgrades();

const tabs = [
{
Expand Down Expand Up @@ -147,7 +146,7 @@ export const DefaultLayout: React.FunctionComponent<Props> = ({
<WithHeaderLayout leftColumn={<DefaultPageTitle />} rightColumn={rightColumn} tabs={tabs}>
{children}
</WithHeaderLayout>
{enableAutomaticAgentUpgrades ? (
{canEnableAutomaticAgentUpgrades ? (
<AutoUpgradeAgentsTour anchor="#fleet-agent-policies-tab" />
) : null}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ import {
} from '../../../components';
import { FLEET_SERVER_PACKAGE } from '../../../constants';

import { ExperimentalFeaturesService, policyHasFleetServer } from '../../../services';
import { policyHasFleetServer } from '../../../services';

import { AgentUpgradeAgentModal } from '../../agents/components';

import { ManageAutoUpgradeAgentsModal } from '../../agents/components/manage_auto_upgrade_agents_modal';

import { useCanEnableAutomaticAgentUpgrades } from '../../../../../hooks/use_can_enable_auto_upgrades';

import { AgentPolicyYamlFlyout } from './agent_policy_yaml_flyout';
import { AgentPolicyCopyProvider } from './agent_policy_copy_provider';
import { AgentPolicyDeleteProvider } from './agent_policy_delete_provider';
Expand Down Expand Up @@ -54,7 +56,7 @@ export const AgentPolicyActionMenu = memo<{
const [isManageAutoUpgradeAgentsModalOpen, setIsManageAutoUpgradeAgentsModalOpen] =
useState<boolean>(false);
const refreshAgentPolicy = useAgentPolicyRefresh();
const { enableAutomaticAgentUpgrades } = ExperimentalFeaturesService.get();
const canEnableAutomaticAgentUpgrades = useCanEnableAutomaticAgentUpgrades();

const isFleetServerPolicy = useMemo(
() =>
Expand Down Expand Up @@ -210,7 +212,7 @@ export const AgentPolicyActionMenu = memo<{
)}
</EuiContextMenuItem>,
viewPolicyItem,
...(enableAutomaticAgentUpgrades ? [manageAutoUpgradeAgentsItem] : []),
...(canEnableAutomaticAgentUpgrades ? [manageAutoUpgradeAgentsItem] : []),
copyPolicyItem,
deletePolicyItem,
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ import { FLEET_SERVER_PACKAGE } from '../../../../../../../../common/constants';
import { getRootIntegrations } from '../../../../../../../../common/services';
import { ManageAutoUpgradeAgentsModal } from '../../../../agents/components/manage_auto_upgrade_agents_modal';
import { AutoUpgradeAgentsTour } from '../../../components/auto_upgrade_agents_tour';
import { ExperimentalFeaturesService } from '../../../../../services';

import { useCanEnableAutomaticAgentUpgrades } from '../../../../../../../hooks/use_can_enable_auto_upgrades';

import { ManageAutoUpgradeAgentsBadge } from './manage_auto_upgrade_agents';

Expand Down Expand Up @@ -64,7 +65,7 @@ export const HeaderRightContent: React.FunctionComponent<HeaderRightContentProps
const [isManageAutoUpgradeAgentsModalOpen, setIsManageAutoUpgradeAgentsModalOpen] =
useState<boolean>(false);
const refreshAgentPolicy = useAgentPolicyRefresh();
const { enableAutomaticAgentUpgrades } = ExperimentalFeaturesService.get();
const canEnableAutomaticAgentUpgrades = useCanEnableAutomaticAgentUpgrades();

const isFleetServerPolicy = useMemo(
() =>
Expand Down Expand Up @@ -217,7 +218,7 @@ export const HeaderRightContent: React.FunctionComponent<HeaderRightContentProps
'',
},
{ isDivider: true },
...(enableAutomaticAgentUpgrades && authz.fleet.allAgentPolicies
...(canEnableAutomaticAgentUpgrades && authz.fleet.allAgentPolicies
? [
{
label: i18n.translate('xpack.fleet.policyDetails.summary.autoUpgrade', {
Expand Down Expand Up @@ -253,7 +254,7 @@ export const HeaderRightContent: React.FunctionComponent<HeaderRightContentProps
{item.isDivider ?? false ? (
<Divider />
) : item.label ? (
<EuiDescriptionList compressed textStyle="reverse" style={{ textAlign: 'right' }}>
<EuiDescriptionList compressed textStyle="reverse" css={{ textAlign: 'right' }}>
<EuiDescriptionListTitle className="eui-textNoWrap">
{item.label}
</EuiDescriptionListTitle>
Expand Down Expand Up @@ -281,7 +282,7 @@ export const HeaderRightContent: React.FunctionComponent<HeaderRightContentProps
/>
</EuiPortal>
)}
{enableAutomaticAgentUpgrades ? (
{canEnableAutomaticAgentUpgrades ? (
<AutoUpgradeAgentsTour anchor="#auto-upgrade-manage-button" />
) : null}
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { ExperimentalFeaturesService } from '../services';

import { licenseService } from './use_license';

export function useCanEnableAutomaticAgentUpgrades() {
const { enableAutomaticAgentUpgrades } = ExperimentalFeaturesService.get();
return enableAutomaticAgentUpgrades && licenseService.isEnterprise();
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ import { inputsFormat } from '../../../common/constants';
import { HTTPAuthorizationHeader } from '../../../common/http_authorization_header';

import { fullAgentPolicyToYaml } from '../../../common/services';
import { appContextService, agentPolicyService, packagePolicyService } from '../../services';
import {
appContextService,
agentPolicyService,
packagePolicyService,
licenseService,
} from '../../services';
import { type AgentClient, getLatestAvailableAgentVersion } from '../../services/agents';
import {
AGENTS_PREFIX,
Expand Down Expand Up @@ -310,6 +315,12 @@ export const getAutoUpgradeAgentsStatusHandler: FleetRequestHandler<

const agentClient = fleetContext.agentClient.asCurrentUser;

if (!licenseService.isEnterprise()) {
throw new FleetUnauthorizedError(
'Auto-upgrade agents feature requires at least Enterprise license'
);
}

const body = await getAutoUpgradeAgentsStatus(agentClient, request.params.agentPolicyId);
return response.ok({
body,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
*/

import { appContextService } from '..';
import { AgentPolicyInvalidError } from '../../errors';
import { AgentPolicyInvalidError, FleetUnauthorizedError } from '../../errors';

import { licenseService } from '..';

import { validateRequiredVersions } from './required_versions';

Expand All @@ -30,6 +32,10 @@ describe('validateRequiredVersions', () => {
jest
.spyOn(appContextService, 'getExperimentalFeatures')
.mockReturnValue({ enableAutomaticAgentUpgrades: true } as any);
jest.spyOn(licenseService, 'isEnterprise').mockReturnValue(true);
});
afterEach(() => {
jest.spyOn(licenseService, 'isEnterprise').mockClear();
});

it('should throw error if duplicate versions', () => {
Expand All @@ -45,6 +51,20 @@ describe('validateRequiredVersions', () => {
);
});

it('should throw error if license is not at least Enterprise', () => {
jest.spyOn(licenseService, 'isEnterprise').mockReturnValue(false);
expect(() => {
validateRequiredVersions('test policy', [
{ version: '9.0.0', percentage: 10 },
{ version: '9.0.0', percentage: 10 },
]);
}).toThrow(
new FleetUnauthorizedError(
`Agents auto upgrades feature requires at least Enterprise license`
)
);
});

it('should throw error if has invalid semver version', () => {
expect(() => {
validateRequiredVersions('test policy', [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import type { AgentTargetVersion } from '../../../common/types';

import { AgentPolicyInvalidError } from '../../errors';
import { appContextService } from '..';
import { AgentPolicyInvalidError, FleetUnauthorizedError } from '../../errors';
import { appContextService, licenseService } from '..';
import { checkTargetVersionsValidity } from '../../../common/services/agent_utils';

export function validateRequiredVersions(
Expand All @@ -23,6 +23,11 @@ export function validateRequiredVersions(
`Policy "${name}" failed validation: required_versions are not allowed when automatic upgrades feature is disabled`
);
}
if (requiredVersions && !licenseService.isEnterprise()) {
throw new FleetUnauthorizedError(
'Agents auto upgrades feature requires at least Enterprise license'
);
}
const error = checkTargetVersionsValidity(requiredVersions);
if (error) {
throw new AgentPolicyInvalidError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { TaskStatus } from '@kbn/task-manager-plugin/server';
import { getDeleteTaskRunResult } from '@kbn/task-manager-plugin/server/task';

import { createAppContextStartContractMock } from '../mocks';
import { agentPolicyService, appContextService } from '../services';
import { agentPolicyService, appContextService, licenseService } from '../services';
import {
fetchAllAgentsByKuery,
getAgentsByKuery,
Expand Down Expand Up @@ -106,6 +106,8 @@ describe('AutomaticAgentUpgradeTask', () => {
let mockTaskManagerSetup: jest.Mocked<TaskManagerSetupContract>;

beforeEach(() => {
jest.spyOn(licenseService, 'isEnterprise').mockReturnValue(true);

mockContract = createAppContextStartContractMock();
appContextService.start(mockContract);
mockCore = coreSetupMock();
Expand All @@ -123,6 +125,7 @@ describe('AutomaticAgentUpgradeTask', () => {

afterEach(() => {
jest.clearAllMocks();
jest.spyOn(licenseService, 'isEnterprise').mockClear();
});

describe('Task lifecycle', () => {
Expand Down Expand Up @@ -181,6 +184,14 @@ describe('AutomaticAgentUpgradeTask', () => {
expect(mockAgentPolicyService.fetchAllAgentPolicies).not.toHaveBeenCalled();
});

it('Should exit if the license is not at least Enterprise', async () => {
jest.spyOn(licenseService, 'isEnterprise').mockReturnValue(false);

await runTask();

expect(mockAgentPolicyService.fetchAllAgentPolicies).not.toHaveBeenCalled();
});

it('Should upgrade eligible agents', async () => {
const agents = generateAgents(10);
mockedGetAgentsByKuery
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import type {
FleetServerAgentMetadata,
} from '../../common/types';

import { agentPolicyService, appContextService } from '../services';
import { agentPolicyService, appContextService, licenseService } from '../services';
import {
fetchAllAgentsByKuery,
getAgentsByKuery,
Expand Down Expand Up @@ -141,6 +141,12 @@ export class AutomaticAgentUpgradeTask {
);
return;
}
if (!licenseService.isEnterprise()) {
this.logger.debug(
'[AutomaticAgentUpgradeTask] Aborting runTask: automatic upgrades feature requires at least Enterprise license'
);
return;
}

if (!this.wasStarted) {
this.logger.debug('[AutomaticAgentUpgradeTask] Aborting runTask(): task not started yet');
Expand Down Expand Up @@ -376,6 +382,9 @@ export class AutomaticAgentUpgradeTask {

numberOfAgentsForUpgrade -= numberOfRetriedAgents;
if (numberOfAgentsForUpgrade <= 0) {
this.logger.debug(
`[AutomaticAgentUpgradeTask] Number of agents ${numberOfAgentsForUpgrade}: no candidate agents found for upgrade (target version: ${requiredVersion.version}, percentage: ${requiredVersion.percentage})`
);
return;
}

Expand Down
Loading