mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Serverless] Use latest published version for initial agent download (#166150)
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
ee92d7dd4e
commit
32d743a9f0
15 changed files with 255 additions and 146 deletions
|
@ -7,10 +7,10 @@
|
|||
|
||||
import React, { useState } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiText, EuiLink, EuiSteps, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiText, EuiLink, EuiSteps, EuiSpacer, EuiLoadingSpinner } from '@elastic/eui';
|
||||
|
||||
import { Error } from '../../../../../../../components';
|
||||
import { useKibanaVersion, useStartServices } from '../../../../../../../../../hooks';
|
||||
import { useStartServices, useAgentVersion } from '../../../../../../../../../hooks';
|
||||
|
||||
import { CreatePackagePolicyBottomBar, NotObscuredByBottomBar } from '../..';
|
||||
import {
|
||||
|
@ -40,7 +40,7 @@ export const InstallElasticAgentManagedPageStep: React.FC<InstallAgentPageProps>
|
|||
const { docLinks } = core;
|
||||
const link = docLinks.links.fleet.troubleshooting;
|
||||
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
const agentVersion = useAgentVersion();
|
||||
|
||||
const [commandCopied, setCommandCopied] = useState(false);
|
||||
const [applyCommandCopied, setApplyCommandCopied] = useState(false);
|
||||
|
@ -66,7 +66,7 @@ export const InstallElasticAgentManagedPageStep: React.FC<InstallAgentPageProps>
|
|||
apiKey: enrollmentAPIKey.api_key,
|
||||
fleetProxy,
|
||||
fleetServerHosts,
|
||||
kibanaVersion,
|
||||
agentVersion: agentVersion || '',
|
||||
});
|
||||
|
||||
const steps = [
|
||||
|
@ -103,6 +103,10 @@ export const InstallElasticAgentManagedPageStep: React.FC<InstallAgentPageProps>
|
|||
})
|
||||
);
|
||||
|
||||
if (!agentVersion) {
|
||||
return <EuiLoadingSpinner />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiText>
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiCallOut,
|
||||
EuiLoadingSpinner,
|
||||
EuiModal,
|
||||
EuiModalBody,
|
||||
EuiModalFooter,
|
||||
|
@ -25,7 +26,7 @@ import {
|
|||
sendGetEnrollmentAPIKeys,
|
||||
useCreateCloudShellUrl,
|
||||
useFleetServerHostsForPolicy,
|
||||
useKibanaVersion,
|
||||
useAgentVersion,
|
||||
} from '../../../../../hooks';
|
||||
import { GoogleCloudShellGuide } from '../../../../../components';
|
||||
import { ManualInstructions } from '../../../../../../../components/enrollment_instructions';
|
||||
|
@ -44,18 +45,22 @@ export const PostInstallGoogleCloudShellModal: React.FunctionComponent<{
|
|||
})
|
||||
);
|
||||
const { fleetServerHosts, fleetProxy } = useFleetServerHostsForPolicy(agentPolicy);
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
const agentVersion = useAgentVersion();
|
||||
|
||||
const { cloudShellUrl, error, isError, isLoading } = useCreateCloudShellUrl({
|
||||
enrollmentAPIKey: apyKeysData?.data?.items[0]?.api_key,
|
||||
packagePolicy,
|
||||
});
|
||||
|
||||
if (!agentVersion) {
|
||||
return <EuiLoadingSpinner />;
|
||||
}
|
||||
|
||||
const installManagedCommands = ManualInstructions({
|
||||
apiKey: apyKeysData?.data?.items[0]?.api_key || 'no_key',
|
||||
fleetServerHosts,
|
||||
fleetProxy,
|
||||
kibanaVersion,
|
||||
});
|
||||
|
||||
const { cloudShellUrl, error, isError, isLoading } = useCreateCloudShellUrl({
|
||||
enrollmentAPIKey: apyKeysData?.data?.items[0]?.api_key,
|
||||
packagePolicy,
|
||||
agentVersion,
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
|
@ -15,6 +15,7 @@ jest.mock('../../hooks', () => {
|
|||
...jest.requireActual('../../hooks'),
|
||||
useFleetServerStandalone: jest.fn(),
|
||||
useAgentEnrollmentFlyoutData: jest.fn(),
|
||||
useAgentVersion: jest.fn().mockReturnValue('8.1.0'),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import React, { useState, useMemo, useEffect } from 'react';
|
||||
|
||||
import { EuiSteps } from '@elastic/eui';
|
||||
import { EuiSteps, EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { safeDump } from 'js-yaml';
|
||||
|
||||
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
|
||||
|
@ -21,8 +21,8 @@ import { StandaloneInstructions, ManualInstructions } from '../../enrollment_ins
|
|||
import {
|
||||
useGetOneEnrollmentAPIKey,
|
||||
useStartServices,
|
||||
useKibanaVersion,
|
||||
sendGetOneAgentPolicyFull,
|
||||
useAgentVersion,
|
||||
} from '../../../hooks';
|
||||
|
||||
import type { InstructionProps } from '../types';
|
||||
|
@ -59,7 +59,6 @@ export const StandaloneSteps: React.FunctionComponent<InstructionProps> = ({
|
|||
const { notifications } = core;
|
||||
const [fullAgentPolicy, setFullAgentPolicy] = useState<FullAgentPolicy | undefined>();
|
||||
const [yaml, setYaml] = useState<any | undefined>('');
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
|
||||
let downloadLink = '';
|
||||
|
||||
|
@ -123,8 +122,10 @@ export const StandaloneSteps: React.FunctionComponent<InstructionProps> = ({
|
|||
}
|
||||
}, [fullAgentPolicy, isK8s]);
|
||||
|
||||
const agentVersion = useAgentVersion();
|
||||
|
||||
const instructionsSteps = useMemo(() => {
|
||||
const standaloneInstallCommands = StandaloneInstructions(kibanaVersion);
|
||||
const standaloneInstallCommands = StandaloneInstructions(agentVersion || '');
|
||||
|
||||
const steps: EuiContainedStepProps[] = !agentPolicy
|
||||
? [
|
||||
|
@ -164,7 +165,7 @@ export const StandaloneSteps: React.FunctionComponent<InstructionProps> = ({
|
|||
|
||||
return steps;
|
||||
}, [
|
||||
kibanaVersion,
|
||||
agentVersion,
|
||||
isK8s,
|
||||
cloudSecurityIntegration,
|
||||
agentPolicy,
|
||||
|
@ -181,6 +182,10 @@ export const StandaloneSteps: React.FunctionComponent<InstructionProps> = ({
|
|||
setMode,
|
||||
]);
|
||||
|
||||
if (!agentVersion) {
|
||||
return <EuiLoadingSpinner />;
|
||||
}
|
||||
|
||||
return <EuiSteps steps={instructionsSteps} />;
|
||||
};
|
||||
|
||||
|
@ -202,7 +207,6 @@ export const ManagedSteps: React.FunctionComponent<InstructionProps> = ({
|
|||
cloudSecurityIntegration,
|
||||
installedPackagePolicy,
|
||||
}) => {
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
const core = useStartServices();
|
||||
const { docLinks } = core;
|
||||
const link = docLinks.links.fleet.troubleshooting;
|
||||
|
@ -214,11 +218,13 @@ export const ManagedSteps: React.FunctionComponent<InstructionProps> = ({
|
|||
|
||||
const enrolledAgentIds = usePollingAgentCount(selectedPolicy?.id || '');
|
||||
|
||||
const agentVersion = useAgentVersion();
|
||||
|
||||
const installManagedCommands = ManualInstructions({
|
||||
apiKey: enrollToken,
|
||||
fleetServerHosts,
|
||||
fleetProxy,
|
||||
kibanaVersion,
|
||||
agentVersion: agentVersion || '',
|
||||
});
|
||||
|
||||
const instructionsSteps = useMemo(() => {
|
||||
|
@ -326,5 +332,9 @@ export const ManagedSteps: React.FunctionComponent<InstructionProps> = ({
|
|||
installedPackagePolicy,
|
||||
]);
|
||||
|
||||
if (!agentVersion) {
|
||||
return <EuiLoadingSpinner />;
|
||||
}
|
||||
|
||||
return <EuiSteps steps={instructionsSteps} />;
|
||||
};
|
||||
|
|
|
@ -27,12 +27,12 @@ export const ManualInstructions = ({
|
|||
apiKey,
|
||||
fleetServerHosts,
|
||||
fleetProxy,
|
||||
kibanaVersion,
|
||||
agentVersion: agentVersion,
|
||||
}: {
|
||||
apiKey: string;
|
||||
fleetServerHosts: string[];
|
||||
fleetProxy?: FleetProxy;
|
||||
kibanaVersion: string;
|
||||
agentVersion: string;
|
||||
}) => {
|
||||
const enrollArgs = getfleetServerHostsEnrollArgs(apiKey, fleetServerHosts, fleetProxy);
|
||||
const fleetServerUrl = enrollArgs?.split('--url=')?.pop()?.split('--enrollment')[0];
|
||||
|
@ -40,31 +40,31 @@ export const ManualInstructions = ({
|
|||
|
||||
const k8sCommand = 'kubectl apply -f elastic-agent-managed-kubernetes.yml';
|
||||
|
||||
const linuxCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-linux-x86_64.tar.gz
|
||||
tar xzvf elastic-agent-${kibanaVersion}-linux-x86_64.tar.gz
|
||||
cd elastic-agent-${kibanaVersion}-linux-x86_64
|
||||
const linuxCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${agentVersion}-linux-x86_64.tar.gz
|
||||
tar xzvf elastic-agent-${agentVersion}-linux-x86_64.tar.gz
|
||||
cd elastic-agent-${agentVersion}-linux-x86_64
|
||||
sudo ./elastic-agent install ${enrollArgs}`;
|
||||
|
||||
const macCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz
|
||||
tar xzvf elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz
|
||||
cd elastic-agent-${kibanaVersion}-darwin-x86_64
|
||||
const macCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${agentVersion}-darwin-x86_64.tar.gz
|
||||
tar xzvf elastic-agent-${agentVersion}-darwin-x86_64.tar.gz
|
||||
cd elastic-agent-${agentVersion}-darwin-x86_64
|
||||
sudo ./elastic-agent install ${enrollArgs}`;
|
||||
|
||||
const windowsCommand = `$ProgressPreference = 'SilentlyContinue'
|
||||
Invoke-WebRequest -Uri https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-windows-x86_64.zip -OutFile elastic-agent-${kibanaVersion}-windows-x86_64.zip
|
||||
Expand-Archive .\\elastic-agent-${kibanaVersion}-windows-x86_64.zip -DestinationPath .
|
||||
cd elastic-agent-${kibanaVersion}-windows-x86_64
|
||||
Invoke-WebRequest -Uri https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${agentVersion}-windows-x86_64.zip -OutFile elastic-agent-${agentVersion}-windows-x86_64.zip
|
||||
Expand-Archive .\\elastic-agent-${agentVersion}-windows-x86_64.zip -DestinationPath .
|
||||
cd elastic-agent-${agentVersion}-windows-x86_64
|
||||
.\\elastic-agent.exe install ${enrollArgs}`;
|
||||
|
||||
const linuxDebCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-amd64.deb
|
||||
sudo dpkg -i elastic-agent-${kibanaVersion}-amd64.deb
|
||||
const linuxDebCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${agentVersion}-amd64.deb
|
||||
sudo dpkg -i elastic-agent-${agentVersion}-amd64.deb
|
||||
sudo elastic-agent enroll ${enrollArgs} \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`;
|
||||
|
||||
const linuxRpmCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-x86_64.rpm
|
||||
sudo rpm -vi elastic-agent-${kibanaVersion}-x86_64.rpm
|
||||
const linuxRpmCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${agentVersion}-x86_64.rpm
|
||||
sudo rpm -vi elastic-agent-${agentVersion}-x86_64.rpm
|
||||
sudo elastic-agent enroll ${enrollArgs} \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`;
|
||||
|
||||
const googleCloudShellCommand = `gcloud config set project <PROJECT_ID> && \nFLEET_URL=${fleetServerUrl} ENROLLMENT_TOKEN=${enrollmentToken} STACK_VERSION=${kibanaVersion} ./deploy.sh`;
|
||||
const googleCloudShellCommand = `gcloud config set project <PROJECT_ID> && \nFLEET_URL=${fleetServerUrl} ENROLLMENT_TOKEN=${enrollmentToken} STACK_VERSION=${agentVersion} ./deploy.sh`;
|
||||
|
||||
return {
|
||||
linux: linuxCommand,
|
||||
|
|
|
@ -6,27 +6,27 @@
|
|||
*/
|
||||
import type { CommandsByPlatform } from '../../../applications/fleet/components/fleet_server_instructions/utils/install_command_utils';
|
||||
|
||||
export const StandaloneInstructions = (kibanaVersion: string): CommandsByPlatform => {
|
||||
const linuxDebCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-amd64.deb
|
||||
sudo dpkg -i elastic-agent-${kibanaVersion}-amd64.deb \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`;
|
||||
export const StandaloneInstructions = (agentVersion: string): CommandsByPlatform => {
|
||||
const linuxDebCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${agentVersion}-amd64.deb
|
||||
sudo dpkg -i elastic-agent-${agentVersion}-amd64.deb \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`;
|
||||
|
||||
const linuxRpmCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-x86_64.rpm
|
||||
sudo rpm -vi elastic-agent-${kibanaVersion}-x86_64.rpm \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`;
|
||||
const linuxRpmCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${agentVersion}-x86_64.rpm
|
||||
sudo rpm -vi elastic-agent-${agentVersion}-x86_64.rpm \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`;
|
||||
|
||||
const linuxCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-linux-x86_64.tar.gz
|
||||
tar xzvf elastic-agent-${kibanaVersion}-linux-x86_64.tar.gz
|
||||
cd elastic-agent-${kibanaVersion}-linux-x86_64
|
||||
const linuxCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${agentVersion}-linux-x86_64.tar.gz
|
||||
tar xzvf elastic-agent-${agentVersion}-linux-x86_64.tar.gz
|
||||
cd elastic-agent-${agentVersion}-linux-x86_64
|
||||
sudo ./elastic-agent install`;
|
||||
|
||||
const macCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz
|
||||
tar xzvf elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz
|
||||
cd elastic-agent-${kibanaVersion}-darwin-x86_64
|
||||
const macCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${agentVersion}-darwin-x86_64.tar.gz
|
||||
tar xzvf elastic-agent-${agentVersion}-darwin-x86_64.tar.gz
|
||||
cd elastic-agent-${agentVersion}-darwin-x86_64
|
||||
sudo ./elastic-agent install`;
|
||||
|
||||
const windowsCommand = `$ProgressPreference = 'SilentlyContinue'
|
||||
Invoke-WebRequest -Uri https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-windows-x86_64.zip -OutFile elastic-agent-${kibanaVersion}-windows-x86_64.zip
|
||||
Expand-Archive .\elastic-agent-${kibanaVersion}-windows-x86_64.zip -DestinationPath .
|
||||
cd elastic-agent-${kibanaVersion}-windows-x86_64
|
||||
Invoke-WebRequest -Uri https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${agentVersion}-windows-x86_64.zip -OutFile elastic-agent-${agentVersion}-windows-x86_64.zip
|
||||
Expand-Archive .\elastic-agent-${agentVersion}-windows-x86_64.zip -DestinationPath .
|
||||
cd elastic-agent-${agentVersion}-windows-x86_64
|
||||
.\\elastic-agent.exe install`;
|
||||
|
||||
const k8sCommand = 'kubectl apply -f elastic-agent-standalone-kubernetes.yml';
|
||||
|
|
|
@ -34,3 +34,4 @@ export * from './use_fleet_server_standalone';
|
|||
export * from './use_locator';
|
||||
export * from './use_create_cloud_formation_url';
|
||||
export * from './use_create_cloud_shell_url';
|
||||
export * from './use_agent_version';
|
||||
|
|
37
x-pack/plugins/fleet/public/hooks/use_agent_version.ts
Normal file
37
x-pack/plugins/fleet/public/hooks/use_agent_version.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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 { useEffect, useState } from 'react';
|
||||
|
||||
import { useKibanaVersion } from './use_kibana_version';
|
||||
import { sendGetAgentsAvailableVersions } from './use_request';
|
||||
|
||||
/**
|
||||
* @returns The most recent agent version available to install or upgrade to.
|
||||
*/
|
||||
export const useAgentVersion = (): string | undefined => {
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
const [agentVersion, setAgentVersion] = useState<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
const getVersions = async () => {
|
||||
try {
|
||||
const res = await sendGetAgentsAvailableVersions();
|
||||
// if the endpoint returns an error, use the fallback versions
|
||||
const versionsList = res?.data?.items ? res.data.items : [kibanaVersion];
|
||||
|
||||
setAgentVersion(versionsList[0]);
|
||||
} catch (err) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
getVersions();
|
||||
}, [kibanaVersion]);
|
||||
|
||||
return agentVersion;
|
||||
};
|
|
@ -5,72 +5,30 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { readFile } from 'fs/promises';
|
||||
|
||||
import { coreMock, httpServerMock } from '@kbn/core/server/mocks';
|
||||
|
||||
import { getAvailableVersionsHandler } from './handlers';
|
||||
|
||||
let mockKibanaVersion = '300.0.0';
|
||||
let mockConfig = {};
|
||||
jest.mock('../../services/agents/versions', () => {
|
||||
return {
|
||||
getAvailableVersions: jest.fn().mockReturnValue(['8.1.0', '8.0.0', '7.17.0']),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../services/app_context', () => {
|
||||
const { loggerMock } = jest.requireActual('@kbn/logging-mocks');
|
||||
return {
|
||||
appContextService: {
|
||||
getLogger: () => loggerMock.create(),
|
||||
getKibanaVersion: () => mockKibanaVersion,
|
||||
getConfig: () => mockConfig,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('fs/promises');
|
||||
|
||||
const mockedReadFile = readFile as jest.MockedFunction<typeof readFile>;
|
||||
|
||||
describe('getAvailableVersionsHandler', () => {
|
||||
it('should return available version and filter version < 7.17', async () => {
|
||||
mockKibanaVersion = '300.0.0';
|
||||
it('should return the value from getAvailableVersions', async () => {
|
||||
const ctx = coreMock.createCustomRequestHandlerContext(coreMock.createRequestHandlerContext());
|
||||
const response = httpServerMock.createResponseFactory();
|
||||
|
||||
mockedReadFile.mockResolvedValue(`["8.1.0", "8.0.0", "7.17.0", "7.16.0"]`);
|
||||
|
||||
await getAvailableVersionsHandler(ctx, httpServerMock.createKibanaRequest(), response);
|
||||
|
||||
expect(response.ok).toBeCalled();
|
||||
expect(response.ok.mock.calls[0][0]?.body).toEqual({
|
||||
items: ['300.0.0', '8.1.0', '8.0.0', '7.17.0'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should not strip -SNAPSHOT from kibana version', async () => {
|
||||
mockKibanaVersion = '300.0.0-SNAPSHOT';
|
||||
const ctx = coreMock.createCustomRequestHandlerContext(coreMock.createRequestHandlerContext());
|
||||
const response = httpServerMock.createResponseFactory();
|
||||
|
||||
mockedReadFile.mockResolvedValue(`["8.1.0", "8.0.0", "7.17.0", "7.16.0"]`);
|
||||
|
||||
await getAvailableVersionsHandler(ctx, httpServerMock.createKibanaRequest(), response);
|
||||
|
||||
expect(response.ok).toBeCalled();
|
||||
expect(response.ok.mock.calls[0][0]?.body).toEqual({
|
||||
items: ['300.0.0-SNAPSHOT', '8.1.0', '8.0.0', '7.17.0'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should not include the current version if onlyAllowAgentUpgradeToKnownVersions = true', async () => {
|
||||
mockKibanaVersion = '300.0.0-SNAPSHOT';
|
||||
mockConfig = {
|
||||
internal: {
|
||||
onlyAllowAgentUpgradeToKnownVersions: true,
|
||||
},
|
||||
};
|
||||
const ctx = coreMock.createCustomRequestHandlerContext(coreMock.createRequestHandlerContext());
|
||||
const response = httpServerMock.createResponseFactory();
|
||||
|
||||
mockedReadFile.mockResolvedValue(`["8.1.0", "8.0.0", "7.17.0", "7.16.0"]`);
|
||||
|
||||
await getAvailableVersionsHandler(ctx, httpServerMock.createKibanaRequest(), response);
|
||||
|
||||
expect(response.ok).toBeCalled();
|
||||
|
|
|
@ -5,21 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { readFile } from 'fs/promises';
|
||||
import Path from 'path';
|
||||
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
import { uniq } from 'lodash';
|
||||
import semverGte from 'semver/functions/gte';
|
||||
import semverGt from 'semver/functions/gt';
|
||||
import semverCoerce from 'semver/functions/coerce';
|
||||
import { type RequestHandler, SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
import type { TypeOf } from '@kbn/config-schema';
|
||||
|
||||
import { appContextService } from '../../services';
|
||||
|
||||
const MINIMUM_SUPPORTED_VERSION = '7.17.0';
|
||||
|
||||
import type {
|
||||
GetAgentsResponse,
|
||||
GetOneAgentResponse,
|
||||
|
@ -363,36 +352,10 @@ function isStringArray(arr: unknown | string[]): arr is string[] {
|
|||
return Array.isArray(arr) && arr.every((p) => typeof p === 'string');
|
||||
}
|
||||
|
||||
// Read a static file generated at build time
|
||||
export const getAvailableVersionsHandler: RequestHandler = async (context, request, response) => {
|
||||
const AGENT_VERSION_BUILD_FILE = 'x-pack/plugins/fleet/target/agent_versions_list.json';
|
||||
const config = await appContextService.getConfig();
|
||||
let versionsToDisplay: string[] = [];
|
||||
|
||||
const kibanaVersion = appContextService.getKibanaVersion();
|
||||
|
||||
try {
|
||||
const file = await readFile(Path.join(REPO_ROOT, AGENT_VERSION_BUILD_FILE), 'utf-8');
|
||||
|
||||
// Exclude versions older than MINIMUM_SUPPORTED_VERSION and pre-release versions (SNAPSHOT, rc..)
|
||||
// De-dup and sort in descending order
|
||||
const data: string[] = JSON.parse(file);
|
||||
|
||||
const versions = data
|
||||
.map((item: any) => semverCoerce(item)?.version || '')
|
||||
.filter((v: any) => semverGte(v, MINIMUM_SUPPORTED_VERSION))
|
||||
.sort((a: any, b: any) => (semverGt(a, b) ? -1 : 1));
|
||||
versionsToDisplay = uniq(versions) as string[];
|
||||
|
||||
if (!config?.internal?.onlyAllowAgentUpgradeToKnownVersions) {
|
||||
// Add current version if not already present
|
||||
const hasCurrentVersion = versionsToDisplay.some((v) => v === kibanaVersion);
|
||||
|
||||
versionsToDisplay = !hasCurrentVersion
|
||||
? [kibanaVersion].concat(versionsToDisplay)
|
||||
: versionsToDisplay;
|
||||
}
|
||||
const body: GetAvailableVersionsResponse = { items: versionsToDisplay };
|
||||
const availableVersions = await AgentService.getAvailableVersions();
|
||||
const body: GetAvailableVersionsResponse = { items: availableVersions };
|
||||
return response.ok({ body });
|
||||
} catch (error) {
|
||||
return defaultFleetErrorHandler({ error, response });
|
||||
|
|
|
@ -83,7 +83,7 @@ import {
|
|||
} from './elastic_agent_manifest';
|
||||
|
||||
import { bulkInstallPackages } from './epm/packages';
|
||||
import { getAgentsByKuery } from './agents';
|
||||
import { getAgentsByKuery, getLatestAvailableVersion } from './agents';
|
||||
import { packagePolicyService } from './package_policy';
|
||||
import { incrementPackagePolicyCopyName } from './package_policies';
|
||||
import { outputService } from './output';
|
||||
|
@ -1048,11 +1048,9 @@ class AgentPolicyService {
|
|||
},
|
||||
};
|
||||
|
||||
const agentVersion = await getLatestAvailableVersion();
|
||||
const configMapYaml = fullAgentConfigMapToYaml(fullAgentConfigMap, safeDump);
|
||||
const updateManifestVersion = elasticAgentStandaloneManifest.replace(
|
||||
'VERSION',
|
||||
appContextService.getKibanaVersion()
|
||||
);
|
||||
const updateManifestVersion = elasticAgentStandaloneManifest.replace('VERSION', agentVersion);
|
||||
const fixedAgentYML = configMapYaml.replace('agent.yml:', 'agent.yml: |-');
|
||||
return [fixedAgentYML, updateManifestVersion].join('\n');
|
||||
} else {
|
||||
|
@ -1064,10 +1062,8 @@ class AgentPolicyService {
|
|||
fleetServer: string,
|
||||
enrolToken: string
|
||||
): Promise<string | null> {
|
||||
const updateManifestVersion = elasticAgentManagedManifest.replace(
|
||||
'VERSION',
|
||||
appContextService.getKibanaVersion()
|
||||
);
|
||||
const agentVersion = await getLatestAvailableVersion();
|
||||
const updateManifestVersion = elasticAgentManagedManifest.replace('VERSION', agentVersion);
|
||||
let updateManifest = updateManifestVersion;
|
||||
if (fleetServer !== '') {
|
||||
updateManifest = updateManifest.replace('https://fleet-server:8220', fleetServer);
|
||||
|
|
|
@ -20,3 +20,4 @@ export { getAgentUploads, getAgentUploadFile } from './uploads';
|
|||
export { AgentServiceImpl } from './agent_service';
|
||||
export type { AgentClient, AgentService } from './agent_service';
|
||||
export { BulkActionsResolver } from './bulk_actions_resolver';
|
||||
export { getAvailableVersions, getLatestAvailableVersion } from './versions';
|
||||
|
|
59
x-pack/plugins/fleet/server/services/agents/versions.test.ts
Normal file
59
x-pack/plugins/fleet/server/services/agents/versions.test.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 { readFile } from 'fs/promises';
|
||||
|
||||
let mockKibanaVersion = '300.0.0';
|
||||
let mockConfig = {};
|
||||
jest.mock('../app_context', () => {
|
||||
const { loggerMock } = jest.requireActual('@kbn/logging-mocks');
|
||||
return {
|
||||
appContextService: {
|
||||
getLogger: () => loggerMock.create(),
|
||||
getKibanaVersion: () => mockKibanaVersion,
|
||||
getConfig: () => mockConfig,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('fs/promises');
|
||||
|
||||
const mockedReadFile = readFile as jest.MockedFunction<typeof readFile>;
|
||||
import { getAvailableVersions } from './versions';
|
||||
|
||||
describe('getAvailableVersions', () => {
|
||||
it('should return available version and filter version < 7.17', async () => {
|
||||
mockKibanaVersion = '300.0.0';
|
||||
mockedReadFile.mockResolvedValue(`["8.1.0", "8.0.0", "7.17.0", "7.16.0"]`);
|
||||
|
||||
const res = await getAvailableVersions(false);
|
||||
|
||||
expect(res).toEqual(['300.0.0', '8.1.0', '8.0.0', '7.17.0']);
|
||||
});
|
||||
|
||||
it('should not strip -SNAPSHOT from kibana version', async () => {
|
||||
mockKibanaVersion = '300.0.0-SNAPSHOT';
|
||||
mockedReadFile.mockResolvedValue(`["8.1.0", "8.0.0", "7.17.0", "7.16.0"]`);
|
||||
|
||||
const res = await getAvailableVersions(false);
|
||||
expect(res).toEqual(['300.0.0-SNAPSHOT', '8.1.0', '8.0.0', '7.17.0']);
|
||||
});
|
||||
|
||||
it('should not include the current version if onlyAllowAgentUpgradeToKnownVersions = true', async () => {
|
||||
mockKibanaVersion = '300.0.0-SNAPSHOT';
|
||||
mockConfig = {
|
||||
internal: {
|
||||
onlyAllowAgentUpgradeToKnownVersions: true,
|
||||
},
|
||||
};
|
||||
mockedReadFile.mockResolvedValue(`["8.1.0", "8.0.0", "7.17.0", "7.16.0"]`);
|
||||
|
||||
const res = await getAvailableVersions(false);
|
||||
|
||||
expect(res).toEqual(['8.1.0', '8.0.0', '7.17.0']);
|
||||
});
|
||||
});
|
73
x-pack/plugins/fleet/server/services/agents/versions.ts
Normal file
73
x-pack/plugins/fleet/server/services/agents/versions.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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 { readFile } from 'fs/promises';
|
||||
import Path from 'path';
|
||||
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
import { uniq } from 'lodash';
|
||||
import semverGte from 'semver/functions/gte';
|
||||
import semverGt from 'semver/functions/gt';
|
||||
import semverCoerce from 'semver/functions/coerce';
|
||||
|
||||
import { appContextService } from '..';
|
||||
|
||||
const MINIMUM_SUPPORTED_VERSION = '7.17.0';
|
||||
const AGENT_VERSION_BUILD_FILE = 'x-pack/plugins/fleet/target/agent_versions_list.json';
|
||||
|
||||
let availableVersions: string[] | undefined;
|
||||
|
||||
export const getLatestAvailableVersion = async (): Promise<string> => {
|
||||
const versions = await getAvailableVersions();
|
||||
return versions[0];
|
||||
};
|
||||
|
||||
export const getAvailableVersions = async (cached = true): Promise<string[]> => {
|
||||
// Use cached value to avoid reading from disk each time
|
||||
if (cached && availableVersions) {
|
||||
return availableVersions;
|
||||
}
|
||||
|
||||
// Read a static file generated at build time
|
||||
const config = appContextService.getConfig();
|
||||
let versionsToDisplay: string[] = [];
|
||||
|
||||
const kibanaVersion = appContextService.getKibanaVersion();
|
||||
|
||||
try {
|
||||
const file = await readFile(Path.join(REPO_ROOT, AGENT_VERSION_BUILD_FILE), 'utf-8');
|
||||
|
||||
// Exclude versions older than MINIMUM_SUPPORTED_VERSION and pre-release versions (SNAPSHOT, rc..)
|
||||
// De-dup and sort in descending order
|
||||
const data: string[] = JSON.parse(file);
|
||||
|
||||
const versions = data
|
||||
.map((item: any) => semverCoerce(item)?.version || '')
|
||||
.filter((v: any) => semverGte(v, MINIMUM_SUPPORTED_VERSION))
|
||||
.sort((a: any, b: any) => (semverGt(a, b) ? -1 : 1));
|
||||
versionsToDisplay = uniq(versions) as string[];
|
||||
|
||||
if (!config?.internal?.onlyAllowAgentUpgradeToKnownVersions) {
|
||||
// Add current version if not already present
|
||||
const hasCurrentVersion = versionsToDisplay.some((v) => v === kibanaVersion);
|
||||
|
||||
versionsToDisplay = !hasCurrentVersion
|
||||
? [kibanaVersion].concat(versionsToDisplay)
|
||||
: versionsToDisplay;
|
||||
}
|
||||
|
||||
availableVersions = versionsToDisplay;
|
||||
|
||||
return availableVersions;
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT' && !config?.internal?.onlyAllowAgentUpgradeToKnownVersions) {
|
||||
// If the file does not exist, return the current version
|
||||
return [kibanaVersion];
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
};
|
|
@ -38,6 +38,7 @@ export interface AgentPolicyServiceInterface {
|
|||
// Agent services
|
||||
export { AgentServiceImpl } from './agents';
|
||||
export type { AgentClient, AgentService } from './agents';
|
||||
export { getAvailableVersions, getLatestAvailableVersion } from './agents';
|
||||
|
||||
// Saved object services
|
||||
export { agentPolicyService } from './agent_policy';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue