[Fleet] Better onboarding experience for Fleet Server on premise (#103550)
This commit is contained in:
parent
5d24b23182
commit
770aa79121
|
@ -17,15 +17,21 @@ import { DefaultPageTitle } from './default_page_title';
|
|||
interface Props {
|
||||
section?: Section;
|
||||
children?: React.ReactNode;
|
||||
rightColumn?: JSX.Element;
|
||||
}
|
||||
|
||||
export const DefaultLayout: React.FunctionComponent<Props> = ({ section, children }) => {
|
||||
export const DefaultLayout: React.FunctionComponent<Props> = ({
|
||||
section,
|
||||
children,
|
||||
rightColumn,
|
||||
}) => {
|
||||
const { getHref } = useLink();
|
||||
const { agents } = useConfig();
|
||||
|
||||
return (
|
||||
<WithHeaderLayout
|
||||
leftColumn={<DefaultPageTitle />}
|
||||
rightColumn={rightColumn}
|
||||
tabs={[
|
||||
{
|
||||
name: (
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiPanel,
|
||||
EuiLink,
|
||||
EuiEmptyPrompt,
|
||||
EuiFlexItem,
|
||||
EuiFlexGroup,
|
||||
EuiText,
|
||||
EuiLoadingSpinner,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { useFleetStatus, useStartServices } from '../../../../hooks';
|
||||
|
||||
const REFRESH_INTERVAL = 10000;
|
||||
|
||||
export const CloudInstructions: React.FC<{ deploymentUrl: string }> = ({ deploymentUrl }) => {
|
||||
const { docLinks } = useStartServices();
|
||||
|
||||
const { refresh } = useFleetStatus();
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
refresh();
|
||||
}, REFRESH_INTERVAL);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [refresh]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiPanel
|
||||
paddingSize="none"
|
||||
grow={false}
|
||||
hasShadow={false}
|
||||
hasBorder={true}
|
||||
className="eui-textCenter"
|
||||
>
|
||||
<EuiEmptyPrompt
|
||||
title={
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.cloudSetupTitle"
|
||||
defaultMessage="Enable APM & Fleet"
|
||||
/>
|
||||
</h2>
|
||||
}
|
||||
body={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.cloudSetupText"
|
||||
defaultMessage="A Fleet Server is required before you can enroll agents with Fleet. You can add one to your deployment by enabling APM & Fleet. For more information see the {link}"
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink
|
||||
href={docLinks.links.fleet.fleetServerAddFleetServer}
|
||||
target="_blank"
|
||||
external
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.userGuideLink"
|
||||
defaultMessage="Fleet User Guide"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
<EuiButton
|
||||
iconSide="right"
|
||||
iconType="popout"
|
||||
fill
|
||||
isLoading={false}
|
||||
type="submit"
|
||||
href={`${deploymentUrl}/edit`}
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.cloudDeploymentLink"
|
||||
defaultMessage="Edit deployment"
|
||||
/>
|
||||
</EuiButton>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</EuiPanel>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup justifyContent="center" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner size="m" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.waitingText"
|
||||
defaultMessage="Waiting for a Fleet Server to connect..."
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,789 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiLink,
|
||||
EuiSteps,
|
||||
EuiCode,
|
||||
EuiCodeBlock,
|
||||
EuiCallOut,
|
||||
EuiSelect,
|
||||
EuiRadioGroup,
|
||||
EuiFieldText,
|
||||
EuiForm,
|
||||
EuiFormErrorText,
|
||||
} from '@elastic/eui';
|
||||
import type { EuiStepProps } from '@elastic/eui/src/components/steps/step';
|
||||
import styled from 'styled-components';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { DownloadStep } from '../../../../components';
|
||||
import {
|
||||
useStartServices,
|
||||
useGetOutputs,
|
||||
sendGenerateServiceToken,
|
||||
usePlatform,
|
||||
PLATFORM_OPTIONS,
|
||||
useGetAgentPolicies,
|
||||
useGetSettings,
|
||||
sendPutSettings,
|
||||
sendGetFleetStatus,
|
||||
useFleetStatus,
|
||||
useUrlModal,
|
||||
} from '../../../../hooks';
|
||||
import type { PLATFORM_TYPE } from '../../../../hooks';
|
||||
import type { PackagePolicy } from '../../../../types';
|
||||
import { FLEET_SERVER_PACKAGE } from '../../../../constants';
|
||||
|
||||
import { getInstallCommandForPlatform } from './install_command_utils';
|
||||
|
||||
const URL_REGEX = /^(https?):\/\/[^\s$.?#].[^\s]*$/gm;
|
||||
const REFRESH_INTERVAL = 10000;
|
||||
|
||||
type DeploymentMode = 'production' | 'quickstart';
|
||||
|
||||
const FlexItemWithMinWidth = styled(EuiFlexItem)`
|
||||
min-width: 0px;
|
||||
max-width: 100%;
|
||||
`;
|
||||
|
||||
export const ContentWrapper = styled(EuiFlexGroup)`
|
||||
height: 100%;
|
||||
margin: 0 auto;
|
||||
max-width: 800px;
|
||||
`;
|
||||
|
||||
// Otherwise the copy button is over the text
|
||||
const CommandCode = styled.div.attrs(() => {
|
||||
return {
|
||||
className: 'eui-textBreakAll',
|
||||
};
|
||||
})`
|
||||
margin-right: ${(props) => props.theme.eui.paddingSizes.m};
|
||||
`;
|
||||
|
||||
export const ServiceTokenStep = ({
|
||||
disabled = false,
|
||||
serviceToken,
|
||||
getServiceToken,
|
||||
isLoadingServiceToken,
|
||||
}: {
|
||||
disabled?: boolean;
|
||||
serviceToken?: string;
|
||||
getServiceToken: () => void;
|
||||
isLoadingServiceToken: boolean;
|
||||
}): EuiStepProps => {
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.fleetServerSetup.stepGenerateServiceTokenTitle', {
|
||||
defaultMessage: 'Generate a service token',
|
||||
}),
|
||||
status: disabled ? 'disabled' : undefined,
|
||||
children: !disabled && (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.generateServiceTokenDescription"
|
||||
defaultMessage="A service token grants Fleet Server permissions to write to Elasticsearch."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
{!serviceToken ? (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
fill
|
||||
isLoading={isLoadingServiceToken}
|
||||
isDisabled={isLoadingServiceToken}
|
||||
onClick={() => {
|
||||
getServiceToken();
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.generateServiceTokenButton"
|
||||
defaultMessage="Generate service token"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<>
|
||||
<EuiCallOut
|
||||
iconType="check"
|
||||
size="s"
|
||||
color="success"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.saveServiceTokenDescription"
|
||||
defaultMessage="Save your service token information. This will be shown only once."
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup direction="column" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.serviceTokenLabel"
|
||||
defaultMessage="Service token"
|
||||
/>
|
||||
</strong>
|
||||
</EuiFlexItem>
|
||||
<FlexItemWithMinWidth>
|
||||
<EuiCodeBlock paddingSize="m" isCopyable>
|
||||
<CommandCode>{serviceToken}</CommandCode>
|
||||
</EuiCodeBlock>
|
||||
</FlexItemWithMinWidth>
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
export const FleetServerCommandStep = ({
|
||||
serviceToken,
|
||||
installCommand,
|
||||
platform,
|
||||
setPlatform,
|
||||
}: {
|
||||
serviceToken?: string;
|
||||
installCommand: string;
|
||||
platform: string;
|
||||
setPlatform: (platform: PLATFORM_TYPE) => void;
|
||||
}): EuiStepProps => {
|
||||
const { docLinks } = useStartServices();
|
||||
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.fleetServerSetup.stepInstallAgentTitle', {
|
||||
defaultMessage: 'Start Fleet Server',
|
||||
}),
|
||||
status: !serviceToken ? 'disabled' : undefined,
|
||||
children: serviceToken ? (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.installAgentDescription"
|
||||
defaultMessage="From the agent directory, copy and run the appropriate quick start command to start an Elastic Agent as a Fleet Server using the generated token and a self-signed certificate. See the {userGuideLink} for instructions on using your own certificates for production deployment. All commands require administrator privileges."
|
||||
values={{
|
||||
userGuideLink: (
|
||||
<EuiLink
|
||||
href={docLinks.links.fleet.fleetServerAddFleetServer}
|
||||
external
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.setupGuideLink"
|
||||
defaultMessage="Fleet User Guide"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiSelect
|
||||
prepend={
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.platformSelectLabel"
|
||||
defaultMessage="Platform"
|
||||
/>
|
||||
</EuiText>
|
||||
}
|
||||
options={PLATFORM_OPTIONS}
|
||||
value={platform}
|
||||
onChange={(e) => setPlatform(e.target.value as PLATFORM_TYPE)}
|
||||
aria-label={i18n.translate('xpack.fleet.fleetServerSetup.platformSelectAriaLabel', {
|
||||
defaultMessage: 'Platform',
|
||||
})}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiCodeBlock
|
||||
fontSize="m"
|
||||
isCopyable={true}
|
||||
paddingSize="m"
|
||||
language="console"
|
||||
whiteSpace="pre-wrap"
|
||||
>
|
||||
<CommandCode>{installCommand}</CommandCode>
|
||||
</EuiCodeBlock>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.enrollmentInstructions.troubleshootingText"
|
||||
defaultMessage="If you are having trouble connecting, see our {link}."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink target="_blank" external href={docLinks.links.fleet.troubleshooting}>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.enrollmentInstructions.troubleshootingLink"
|
||||
defaultMessage="troubleshooting guide"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
</>
|
||||
) : null,
|
||||
};
|
||||
};
|
||||
|
||||
export const useFleetServerInstructions = (policyId?: string) => {
|
||||
const outputsRequest = useGetOutputs();
|
||||
const { notifications } = useStartServices();
|
||||
const [serviceToken, setServiceToken] = useState<string>();
|
||||
const [isLoadingServiceToken, setIsLoadingServiceToken] = useState<boolean>(false);
|
||||
const { platform, setPlatform } = usePlatform();
|
||||
const [deploymentMode, setDeploymentMode] = useState<DeploymentMode>('production');
|
||||
const { data: settings, resendRequest: refreshSettings } = useGetSettings();
|
||||
const fleetServerHost = settings?.item.fleet_server_hosts?.[0];
|
||||
const output = outputsRequest.data?.items?.[0];
|
||||
const esHost = output?.hosts?.[0];
|
||||
|
||||
const installCommand = useMemo((): string => {
|
||||
if (!serviceToken || !esHost) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return getInstallCommandForPlatform(
|
||||
platform,
|
||||
esHost,
|
||||
serviceToken,
|
||||
policyId,
|
||||
fleetServerHost,
|
||||
deploymentMode === 'production'
|
||||
);
|
||||
}, [serviceToken, esHost, platform, policyId, fleetServerHost, deploymentMode]);
|
||||
|
||||
const getServiceToken = useCallback(async () => {
|
||||
setIsLoadingServiceToken(true);
|
||||
try {
|
||||
const { data } = await sendGenerateServiceToken();
|
||||
if (data?.value) {
|
||||
setServiceToken(data?.value);
|
||||
}
|
||||
} catch (err) {
|
||||
notifications.toasts.addError(err, {
|
||||
title: i18n.translate('xpack.fleet.fleetServerSetup.errorGeneratingTokenTitleText', {
|
||||
defaultMessage: 'Error generating token',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
setIsLoadingServiceToken(false);
|
||||
}, [notifications.toasts]);
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
return Promise.all([outputsRequest.resendRequest(), refreshSettings()]);
|
||||
}, [outputsRequest, refreshSettings]);
|
||||
|
||||
const addFleetServerHost = useCallback(
|
||||
async (host: string) => {
|
||||
try {
|
||||
await sendPutSettings({
|
||||
fleet_server_hosts: [host, ...(settings?.item.fleet_server_hosts || [])],
|
||||
});
|
||||
await refreshSettings();
|
||||
} catch (err) {
|
||||
notifications.toasts.addError(err, {
|
||||
title: i18n.translate('xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle', {
|
||||
defaultMessage: 'Error adding Fleet Server host',
|
||||
}),
|
||||
});
|
||||
}
|
||||
},
|
||||
[refreshSettings, notifications.toasts, settings?.item.fleet_server_hosts]
|
||||
);
|
||||
|
||||
return {
|
||||
addFleetServerHost,
|
||||
fleetServerHost,
|
||||
deploymentMode,
|
||||
setDeploymentMode,
|
||||
serviceToken,
|
||||
getServiceToken,
|
||||
isLoadingServiceToken,
|
||||
installCommand,
|
||||
platform,
|
||||
setPlatform,
|
||||
refresh,
|
||||
};
|
||||
};
|
||||
|
||||
const AgentPolicySelectionStep = ({
|
||||
policyId,
|
||||
setPolicyId,
|
||||
}: {
|
||||
policyId?: string;
|
||||
setPolicyId: (v: string) => void;
|
||||
}): EuiStepProps => {
|
||||
const { data } = useGetAgentPolicies({ full: true });
|
||||
|
||||
const agentPolicies = useMemo(
|
||||
() =>
|
||||
data
|
||||
? data.items.filter((item) => {
|
||||
return item.package_policies.some(
|
||||
(p: string | PackagePolicy) =>
|
||||
(p as PackagePolicy).package?.name === FLEET_SERVER_PACKAGE
|
||||
);
|
||||
return false;
|
||||
})
|
||||
: [],
|
||||
[data]
|
||||
);
|
||||
|
||||
const options = useMemo(() => {
|
||||
return agentPolicies.map((policy) => ({ text: policy.name, value: policy.id }));
|
||||
}, [agentPolicies]);
|
||||
|
||||
useEffect(() => {
|
||||
// Select default value
|
||||
if (agentPolicies.length && !policyId) {
|
||||
const defaultPolicy =
|
||||
agentPolicies.find((p) => p.is_default_fleet_server) || agentPolicies[0];
|
||||
setPolicyId(defaultPolicy.id);
|
||||
}
|
||||
}, [options, agentPolicies, policyId, setPolicyId]);
|
||||
|
||||
const onChangeCallback = useCallback(
|
||||
(e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setPolicyId(e.target.value);
|
||||
},
|
||||
[setPolicyId]
|
||||
);
|
||||
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.fleetServerSetup.stepSelectAgentPolicyTitle', {
|
||||
defaultMessage: 'Select an Agent policy',
|
||||
}),
|
||||
status: undefined,
|
||||
children: (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.selectAgentPolicyDescriptionText"
|
||||
defaultMessage="Agent policies allow you to configure and mange your agents remotely. We recommend using the “Default Fleet Server policy” which includes the necessary configuration to run a Fleet Server."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiSelect
|
||||
prepend={
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.agentPolicySelectLabel"
|
||||
defaultMessage="Agent policy"
|
||||
/>
|
||||
</EuiText>
|
||||
}
|
||||
options={options}
|
||||
value={policyId}
|
||||
onChange={onChangeCallback}
|
||||
aria-label={i18n.translate('xpack.fleet.fleetServerSetup.agentPolicySelectAraiLabel', {
|
||||
defaultMessage: 'Agent policy',
|
||||
})}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
export const addFleetServerHostStep = ({
|
||||
addFleetServerHost,
|
||||
}: {
|
||||
addFleetServerHost: (v: string) => Promise<void>;
|
||||
}): EuiStepProps => {
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.fleetServerSetup.addFleetServerHostStepTitle', {
|
||||
defaultMessage: 'Add your Fleet Server host',
|
||||
}),
|
||||
status: undefined,
|
||||
children: <AddFleetServerHostStepContent addFleetServerHost={addFleetServerHost} />,
|
||||
};
|
||||
};
|
||||
|
||||
export const AddFleetServerHostStepContent = ({
|
||||
addFleetServerHost,
|
||||
}: {
|
||||
addFleetServerHost: (v: string) => Promise<void>;
|
||||
}) => {
|
||||
const [calloutHost, setCalloutHost] = useState<string | undefined>();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [fleetServerHost, setFleetServerHost] = useState('');
|
||||
const [error, setError] = useState<undefined | string>();
|
||||
const { getModalHref } = useUrlModal();
|
||||
|
||||
const validate = useCallback(
|
||||
(host: string) => {
|
||||
if (host.match(URL_REGEX)) {
|
||||
setError(undefined);
|
||||
return true;
|
||||
} else {
|
||||
setError(
|
||||
i18n.translate('xpack.fleet.fleetServerSetup.addFleetServerHostInvalidUrlError', {
|
||||
defaultMessage: 'Invalid URL',
|
||||
})
|
||||
);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[setError]
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
if (validate(fleetServerHost)) {
|
||||
await addFleetServerHost(fleetServerHost);
|
||||
}
|
||||
setCalloutHost(fleetServerHost);
|
||||
setFleetServerHost('');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [fleetServerHost, addFleetServerHost, validate]);
|
||||
|
||||
const onChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setFleetServerHost(e.target.value);
|
||||
if (error) {
|
||||
validate(e.target.value);
|
||||
}
|
||||
},
|
||||
[error, validate, setFleetServerHost]
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiForm onSubmit={onSubmit}>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.addFleetServerHostStepDescription"
|
||||
defaultMessage="Specify the URL your agents will use to connect to Fleet Server. This should match the public IP address or domain of the host where Fleet Server will run. By default, Fleet Server uses port {port}."
|
||||
values={{ port: <EuiCode>8220</EuiCode> }}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiFieldText
|
||||
fullWidth
|
||||
placeholder={'http://127.0.0.1:8220'}
|
||||
value={calloutHost}
|
||||
isInvalid={!!error}
|
||||
onChange={onChange}
|
||||
prepend={
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.addFleetServerHostInputLabel"
|
||||
defaultMessage="Fleet Server host"
|
||||
/>
|
||||
</EuiText>
|
||||
}
|
||||
/>
|
||||
{error && <EuiFormErrorText>{error}</EuiFormErrorText>}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton isLoading={isLoading} onClick={onSubmit}>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.addFleetServerHostButton"
|
||||
defaultMessage="Add host"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
{calloutHost && (
|
||||
<>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiCallOut
|
||||
iconType="check"
|
||||
size="s"
|
||||
color="success"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.addFleetServerHostSuccessTitle"
|
||||
defaultMessage="Added Fleet Server host"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.addFleetServerHostSuccessText"
|
||||
defaultMessage="Added {host}. You can edit your Fleet Server hosts in {fleetSettingsLink}."
|
||||
values={{
|
||||
host: calloutHost,
|
||||
fleetSettingsLink: (
|
||||
<EuiLink href={getModalHref('settings')}>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.fleetSettingsLink"
|
||||
defaultMessage="Fleet Settings"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiCallOut>
|
||||
</>
|
||||
)}
|
||||
</EuiForm>
|
||||
);
|
||||
};
|
||||
|
||||
export const deploymentModeStep = ({
|
||||
deploymentMode,
|
||||
setDeploymentMode,
|
||||
}: {
|
||||
deploymentMode: DeploymentMode;
|
||||
setDeploymentMode: (v: DeploymentMode) => void;
|
||||
}): EuiStepProps => {
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.fleetServerSetup.stepDeploymentModeTitle', {
|
||||
defaultMessage: 'Choose a deployment mode for security',
|
||||
}),
|
||||
status: undefined,
|
||||
children: (
|
||||
<DeploymentModeStepContent
|
||||
deploymentMode={deploymentMode}
|
||||
setDeploymentMode={setDeploymentMode}
|
||||
/>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const DeploymentModeStepContent = ({
|
||||
deploymentMode,
|
||||
setDeploymentMode,
|
||||
}: {
|
||||
deploymentMode: DeploymentMode;
|
||||
setDeploymentMode: (v: DeploymentMode) => void;
|
||||
}) => {
|
||||
const onChangeCallback = useCallback(
|
||||
(v: string) => {
|
||||
if (v === 'production' || v === 'quickstart') {
|
||||
setDeploymentMode(v);
|
||||
}
|
||||
},
|
||||
[setDeploymentMode]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.stepDeploymentModeDescriptionText"
|
||||
defaultMessage="Fleet uses Transport Layer Security (TLS) to encrypt traffic between Elastic Agents and other components in the Elastic Stack. Choose a deployment mode to determine how you wish to handle certificates. Your selection will affect the Fleet Server set up command shown in a later step."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiRadioGroup
|
||||
options={[
|
||||
{
|
||||
id: 'quickstart',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.deploymentModeQuickStartOption"
|
||||
defaultMessage="{quickStart} – Fleet Server will generate a self-signed certificate. Subsequent agents must be enrolled using the --insecure flag. Not recommended for production use cases."
|
||||
values={{
|
||||
quickStart: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.quickStartText"
|
||||
defaultMessage="Quick start"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'production',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.deploymentModeProductionOption"
|
||||
defaultMessage="{production} – Provide your own certificates. This option will require agents to specify a cert key when enrolling with Fleet"
|
||||
values={{
|
||||
production: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.productionText"
|
||||
defaultMessage="Production"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
idSelected={deploymentMode}
|
||||
onChange={onChangeCallback}
|
||||
name="radio group"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const WaitingForFleetServerStep = ({
|
||||
status,
|
||||
}: {
|
||||
status: 'loading' | 'disabled' | 'complete';
|
||||
}): EuiStepProps => {
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.fleetServerSetup.stepWaitingForFleetServerTitle', {
|
||||
defaultMessage: 'Waiting for Fleet Server to connect...',
|
||||
}),
|
||||
status,
|
||||
children: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
const CompleteStep = (): EuiStepProps => {
|
||||
const fleetStatus = useFleetStatus();
|
||||
|
||||
const onContinueClick = () => {
|
||||
fleetStatus.refresh();
|
||||
};
|
||||
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.fleetServerSetup.stepFleetServerCompleteTitle', {
|
||||
defaultMessage: 'Fleet Server connected',
|
||||
}),
|
||||
status: 'complete',
|
||||
children: (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.stepFleetServerCompleteDescription"
|
||||
defaultMessage="You can now enroll agents with Fleet."
|
||||
/>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiButton fill onClick={onContinueClick}>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.continueButton"
|
||||
defaultMessage="Continue"
|
||||
/>
|
||||
</EuiButton>
|
||||
</>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
export const OnPremInstructions: React.FC = () => {
|
||||
const { notifications } = useStartServices();
|
||||
const [policyId, setPolicyId] = useState<string | undefined>();
|
||||
|
||||
const {
|
||||
serviceToken,
|
||||
getServiceToken,
|
||||
isLoadingServiceToken,
|
||||
installCommand,
|
||||
platform,
|
||||
setPlatform,
|
||||
refresh,
|
||||
deploymentMode,
|
||||
setDeploymentMode,
|
||||
fleetServerHost,
|
||||
addFleetServerHost,
|
||||
} = useFleetServerInstructions(policyId);
|
||||
|
||||
const { modal } = useUrlModal();
|
||||
useEffect(() => {
|
||||
// Refresh settings when the settings modal is closed
|
||||
if (!modal) {
|
||||
refresh();
|
||||
}
|
||||
}, [modal, refresh]);
|
||||
|
||||
const { docLinks } = useStartServices();
|
||||
|
||||
const [isWaitingForFleetServer, setIsWaitingForFleetServer] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const res = await sendGetFleetStatus();
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
if (res.data?.isReady && !res.data?.missing_requirements?.includes('fleet_server')) {
|
||||
setIsWaitingForFleetServer(false);
|
||||
}
|
||||
} catch (err) {
|
||||
notifications.toasts.addError(err, {
|
||||
title: i18n.translate('xpack.fleet.fleetServerSetup.errorRefreshingFleetServerStatus', {
|
||||
defaultMessage: 'Error refreshing Fleet Server status',
|
||||
}),
|
||||
});
|
||||
}
|
||||
}, REFRESH_INTERVAL);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [notifications.toasts]);
|
||||
|
||||
return (
|
||||
<EuiPanel paddingSize="l" grow={false} hasShadow={false} hasBorder={true}>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText className="eui-textCenter">
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.setupTitle"
|
||||
defaultMessage="Add a Fleet Server"
|
||||
/>
|
||||
</h2>
|
||||
<EuiSpacer size="m" />
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.setupText"
|
||||
defaultMessage="A Fleet Server is required before you can enroll agents with Fleet. Follow the instructions below to set up a Fleet Server. For more information, see the {userGuideLink}."
|
||||
values={{
|
||||
userGuideLink: (
|
||||
<EuiLink
|
||||
href={docLinks.links.fleet.fleetServerAddFleetServer}
|
||||
external
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.setupGuideLink"
|
||||
defaultMessage="Fleet User Guide"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiSteps
|
||||
className="eui-textLeft"
|
||||
steps={[
|
||||
DownloadStep(),
|
||||
AgentPolicySelectionStep({ policyId, setPolicyId }),
|
||||
deploymentModeStep({ deploymentMode, setDeploymentMode }),
|
||||
addFleetServerHostStep({ addFleetServerHost }),
|
||||
ServiceTokenStep({
|
||||
disabled: deploymentMode === 'production' && !fleetServerHost,
|
||||
serviceToken,
|
||||
getServiceToken,
|
||||
isLoadingServiceToken,
|
||||
}),
|
||||
FleetServerCommandStep({ serviceToken, installCommand, platform, setPlatform }),
|
||||
isWaitingForFleetServer
|
||||
? WaitingForFleetServerStep({
|
||||
status: !serviceToken ? 'disabled' : 'loading',
|
||||
})
|
||||
: CompleteStep(),
|
||||
]}
|
||||
/>
|
||||
</EuiPanel>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { CloudInstructions } from './fleet_server_cloud_instructions';
|
||||
export * from './fleet_server_on_prem_instructions';
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* 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 { getInstallCommandForPlatform } from './install_command_utils';
|
||||
|
||||
describe('getInstallCommandForPlatform', () => {
|
||||
describe('without policy id', () => {
|
||||
it('should return the correct command if the the policyId is not set for linux-mac', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'linux-mac',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
"sudo ./elastic-agent install -f \\\\
|
||||
--fleet-server-es=http://elasticsearch:9200 \\\\
|
||||
--fleet-server-service-token=service-token-1"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should return the correct command if the the policyId is not set for windows', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'windows',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
".\\\\elastic-agent.exe install -f \\\\
|
||||
--fleet-server-es=http://elasticsearch:9200 \\\\
|
||||
--fleet-server-service-token=service-token-1"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should return the correct command if the the policyId is not set for rpm-deb', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'rpm-deb',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
"sudo elastic-agent enroll -f \\\\
|
||||
--fleet-server-es=http://elasticsearch:9200 \\\\
|
||||
--fleet-server-service-token=service-token-1"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with policy id', () => {
|
||||
it('should return the correct command if the the policyId is set for linux-mac', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'linux-mac',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1',
|
||||
'policy-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
"sudo ./elastic-agent install -f \\\\
|
||||
--fleet-server-es=http://elasticsearch:9200 \\\\
|
||||
--fleet-server-service-token=service-token-1 \\\\
|
||||
--fleet-server-policy=policy-1"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should return the correct command if the the policyId is set for windows', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'windows',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1',
|
||||
'policy-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
".\\\\elastic-agent.exe install -f \\\\
|
||||
--fleet-server-es=http://elasticsearch:9200 \\\\
|
||||
--fleet-server-service-token=service-token-1 \\\\
|
||||
--fleet-server-policy=policy-1"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should return the correct command if the the policyId is set for rpm-deb', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'rpm-deb',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1',
|
||||
'policy-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
"sudo elastic-agent enroll -f \\\\
|
||||
--fleet-server-es=http://elasticsearch:9200 \\\\
|
||||
--fleet-server-service-token=service-token-1 \\\\
|
||||
--fleet-server-policy=policy-1"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with policy id and fleet server host and production deployment', () => {
|
||||
it('should return the correct command if the the policyId is set for linux-mac', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'linux-mac',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1',
|
||||
'policy-1',
|
||||
'http://fleetserver:8220',
|
||||
true
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
"sudo ./elastic-agent install --url=http://fleetserver:8220 \\\\
|
||||
-f \\\\
|
||||
--fleet-server-es=http://elasticsearch:9200 \\\\
|
||||
--fleet-server-service-token=service-token-1 \\\\
|
||||
--fleet-server-policy=policy-1 \\\\
|
||||
--certificate-authorities=<PATH_TO_CA> \\\\
|
||||
--fleet-server-es-ca=<PATH_TO_ES_CERT> \\\\
|
||||
--fleet-server-cert=<PATH_TO_FLEET_SERVER_CERT> \\\\
|
||||
--fleet-server-cert-key=<PATH_TO_FLEET_SERVER_CERT_KEY>"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should return the correct command if the the policyId is set for windows', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'windows',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1',
|
||||
'policy-1',
|
||||
'http://fleetserver:8220',
|
||||
true
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
".\\\\elastic-agent.exe install --url=http://fleetserver:8220 \\\\
|
||||
-f \\\\
|
||||
--fleet-server-es=http://elasticsearch:9200 \\\\
|
||||
--fleet-server-service-token=service-token-1 \\\\
|
||||
--fleet-server-policy=policy-1 \\\\
|
||||
--certificate-authorities=<PATH_TO_CA> \\\\
|
||||
--fleet-server-es-ca=<PATH_TO_ES_CERT> \\\\
|
||||
--fleet-server-cert=<PATH_TO_FLEET_SERVER_CERT> \\\\
|
||||
--fleet-server-cert-key=<PATH_TO_FLEET_SERVER_CERT_KEY>"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should return the correct command if the the policyId is set for rpm-deb', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'rpm-deb',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1',
|
||||
'policy-1',
|
||||
'http://fleetserver:8220',
|
||||
true
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
"sudo elastic-agent enroll --url=http://fleetserver:8220 \\\\
|
||||
-f \\\\
|
||||
--fleet-server-es=http://elasticsearch:9200 \\\\
|
||||
--fleet-server-service-token=service-token-1 \\\\
|
||||
--fleet-server-policy=policy-1 \\\\
|
||||
--certificate-authorities=<PATH_TO_CA> \\\\
|
||||
--fleet-server-es-ca=<PATH_TO_ES_CERT> \\\\
|
||||
--fleet-server-cert=<PATH_TO_FLEET_SERVER_CERT> \\\\
|
||||
--fleet-server-cert-key=<PATH_TO_FLEET_SERVER_CERT_KEY>"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return nothing for an invalid platform', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'rpm-deb',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
"sudo elastic-agent enroll -f \\\\
|
||||
--fleet-server-es=http://elasticsearch:9200 \\\\
|
||||
--fleet-server-service-token=service-token-1"
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { PLATFORM_TYPE } from '../../../../hooks';
|
||||
|
||||
export function getInstallCommandForPlatform(
|
||||
platform: PLATFORM_TYPE,
|
||||
esHost: string,
|
||||
serviceToken: string,
|
||||
policyId?: string,
|
||||
fleetServerHost?: string,
|
||||
isProductionDeployment?: boolean
|
||||
) {
|
||||
let commandArguments = '';
|
||||
|
||||
if (isProductionDeployment && fleetServerHost) {
|
||||
commandArguments += `--url=${fleetServerHost} \\\n`;
|
||||
}
|
||||
|
||||
commandArguments += ` -f \\\n --fleet-server-es=${esHost}`;
|
||||
commandArguments += ` \\\n --fleet-server-service-token=${serviceToken}`;
|
||||
if (policyId) {
|
||||
commandArguments += ` \\\n --fleet-server-policy=${policyId}`;
|
||||
}
|
||||
|
||||
if (isProductionDeployment) {
|
||||
commandArguments += ` \\\n --certificate-authorities=<PATH_TO_CA>`;
|
||||
commandArguments += ` \\\n --fleet-server-es-ca=<PATH_TO_ES_CERT>`;
|
||||
commandArguments += ` \\\n --fleet-server-cert=<PATH_TO_FLEET_SERVER_CERT>`;
|
||||
commandArguments += ` \\\n --fleet-server-cert-key=<PATH_TO_FLEET_SERVER_CERT_KEY>`;
|
||||
}
|
||||
|
||||
switch (platform) {
|
||||
case 'linux-mac':
|
||||
return `sudo ./elastic-agent install ${commandArguments}`;
|
||||
case 'windows':
|
||||
return `.\\elastic-agent.exe install ${commandArguments}`;
|
||||
case 'rpm-deb':
|
||||
return `sudo elastic-agent enroll ${commandArguments}`;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* 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 { getInstallCommandForPlatform } from './fleet_server_requirement_page';
|
||||
|
||||
describe('getInstallCommandForPlatform', () => {
|
||||
describe('without policy id', () => {
|
||||
it('should return the correct command if the the policyId is not set for linux-mac', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'linux-mac',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(
|
||||
`"sudo ./elastic-agent install -f --fleet-server-es=http://elasticsearch:9200 --fleet-server-service-token=service-token-1"`
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the correct command if the the policyId is not set for windows', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'windows',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(
|
||||
`".\\\\elastic-agent.exe install -f --fleet-server-es=http://elasticsearch:9200 --fleet-server-service-token=service-token-1"`
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the correct command if the the policyId is not set for rpm-deb', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'rpm-deb',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(
|
||||
`"sudo elastic-agent enroll -f --fleet-server-es=http://elasticsearch:9200 --fleet-server-service-token=service-token-1"`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with policy id', () => {
|
||||
it('should return the correct command if the the policyId is set for linux-mac', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'linux-mac',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1',
|
||||
'policy-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(
|
||||
`"sudo ./elastic-agent install -f --fleet-server-es=http://elasticsearch:9200 --fleet-server-service-token=service-token-1 --fleet-server-policy=policy-1"`
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the correct command if the the policyId is set for windows', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'windows',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1',
|
||||
'policy-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(
|
||||
`".\\\\elastic-agent.exe install -f --fleet-server-es=http://elasticsearch:9200 --fleet-server-service-token=service-token-1 --fleet-server-policy=policy-1"`
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the correct command if the the policyId is set for rpm-deb', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'rpm-deb',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1',
|
||||
'policy-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(
|
||||
`"sudo elastic-agent enroll -f --fleet-server-es=http://elasticsearch:9200 --fleet-server-service-token=service-token-1 --fleet-server-policy=policy-1"`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return nothing for an invalid platform', () => {
|
||||
const res = getInstallCommandForPlatform(
|
||||
'rpm-deb',
|
||||
'http://elasticsearch:9200',
|
||||
'service-token-1'
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(
|
||||
`"sudo elastic-agent enroll -f --fleet-server-es=http://elasticsearch:9200 --fleet-server-service-token=service-token-1"`
|
||||
);
|
||||
});
|
||||
});
|
|
@ -5,398 +5,25 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useState, useMemo, useCallback } from 'react';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiLoadingSpinner,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiLink,
|
||||
EuiEmptyPrompt,
|
||||
EuiSteps,
|
||||
EuiCodeBlock,
|
||||
EuiCallOut,
|
||||
EuiSelect,
|
||||
} from '@elastic/eui';
|
||||
import type { EuiStepProps } from '@elastic/eui/src/components/steps/step';
|
||||
import React from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import styled from 'styled-components';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { DownloadStep } from '../../../components';
|
||||
import {
|
||||
useStartServices,
|
||||
useGetOutputs,
|
||||
sendGenerateServiceToken,
|
||||
usePlatform,
|
||||
PLATFORM_OPTIONS,
|
||||
} from '../../../hooks';
|
||||
import type { PLATFORM_TYPE } from '../../../hooks';
|
||||
import { useStartServices } from '../../../hooks';
|
||||
|
||||
import { CloudInstructions, OnPremInstructions } from './components';
|
||||
|
||||
const FlexItemWithMinWidth = styled(EuiFlexItem)`
|
||||
min-width: 0px;
|
||||
max-width: 100%;
|
||||
`;
|
||||
|
||||
export const ContentWrapper = styled(EuiFlexGroup)`
|
||||
const ContentWrapper = styled(EuiFlexGroup)`
|
||||
height: 100%;
|
||||
margin: 0 auto;
|
||||
max-width: 800px;
|
||||
`;
|
||||
|
||||
// Otherwise the copy button is over the text
|
||||
const CommandCode = styled.pre({
|
||||
overflow: 'scroll',
|
||||
});
|
||||
|
||||
export const ServiceTokenStep = ({
|
||||
serviceToken,
|
||||
getServiceToken,
|
||||
isLoadingServiceToken,
|
||||
}: {
|
||||
serviceToken?: string;
|
||||
getServiceToken: () => void;
|
||||
isLoadingServiceToken: boolean;
|
||||
}): EuiStepProps => {
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.fleetServerSetup.stepGenerateServiceTokenTitle', {
|
||||
defaultMessage: 'Generate a service token',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.generateServiceTokenDescription"
|
||||
defaultMessage="A service token grants Fleet Server permissions to write to Elasticsearch."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
{!serviceToken ? (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
fill
|
||||
isLoading={isLoadingServiceToken}
|
||||
isDisabled={isLoadingServiceToken}
|
||||
onClick={() => {
|
||||
getServiceToken();
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.generateServiceTokenButton"
|
||||
defaultMessage="Generate service token"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<>
|
||||
<EuiCallOut size="s">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.saveServiceTokenDescription"
|
||||
defaultMessage="Save your service token information. This will be shown only once."
|
||||
/>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.serviceTokenLabel"
|
||||
defaultMessage="Service token"
|
||||
/>
|
||||
</strong>
|
||||
</EuiFlexItem>
|
||||
<FlexItemWithMinWidth>
|
||||
<EuiCodeBlock paddingSize="m" isCopyable>
|
||||
<CommandCode>{serviceToken}</CommandCode>
|
||||
</EuiCodeBlock>
|
||||
</FlexItemWithMinWidth>
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
export const FleetServerCommandStep = ({
|
||||
serviceToken,
|
||||
installCommand,
|
||||
platform,
|
||||
setPlatform,
|
||||
}: {
|
||||
serviceToken?: string;
|
||||
installCommand: string;
|
||||
platform: string;
|
||||
setPlatform: (platform: PLATFORM_TYPE) => void;
|
||||
}): EuiStepProps => {
|
||||
const { docLinks } = useStartServices();
|
||||
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.fleetServerSetup.stepInstallAgentTitle', {
|
||||
defaultMessage: 'Start Fleet Server',
|
||||
}),
|
||||
status: !serviceToken ? 'disabled' : undefined,
|
||||
children: serviceToken ? (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.installAgentDescription"
|
||||
defaultMessage="From the agent directory, copy and run the appropriate quick start command to start an Elastic Agent as a Fleet Server using the generated token and a self-signed certificate. See the {userGuideLink} for instructions on using your own certificates for production deployment. All commands require administrator privileges."
|
||||
values={{
|
||||
userGuideLink: (
|
||||
<EuiLink
|
||||
href={docLinks.links.fleet.fleetServerAddFleetServer}
|
||||
external
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.setupGuideLink"
|
||||
defaultMessage="Fleet User Guide"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiSelect
|
||||
prepend={
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.platformSelectLabel"
|
||||
defaultMessage="Platform"
|
||||
/>
|
||||
</EuiText>
|
||||
}
|
||||
options={PLATFORM_OPTIONS}
|
||||
value={platform}
|
||||
onChange={(e) => setPlatform(e.target.value as PLATFORM_TYPE)}
|
||||
aria-label={i18n.translate('xpack.fleet.fleetServerSetup.platformSelectAriaLabel', {
|
||||
defaultMessage: 'Platform',
|
||||
})}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiCodeBlock
|
||||
fontSize="m"
|
||||
isCopyable={true}
|
||||
paddingSize="m"
|
||||
language="console"
|
||||
whiteSpace="pre"
|
||||
>
|
||||
<CommandCode>{installCommand}</CommandCode>
|
||||
</EuiCodeBlock>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.enrollmentInstructions.troubleshootingText"
|
||||
defaultMessage="If you are having trouble connecting, see our {link}."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink target="_blank" external href={docLinks.links.fleet.troubleshooting}>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.enrollmentInstructions.troubleshootingLink"
|
||||
defaultMessage="troubleshooting guide"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
</>
|
||||
) : null,
|
||||
};
|
||||
};
|
||||
|
||||
export function getInstallCommandForPlatform(
|
||||
platform: PLATFORM_TYPE,
|
||||
esHost: string,
|
||||
serviceToken: string,
|
||||
policyId?: string
|
||||
) {
|
||||
const commandArguments = `-f --fleet-server-es=${esHost} --fleet-server-service-token=${serviceToken}${
|
||||
policyId ? ` --fleet-server-policy=${policyId}` : ''
|
||||
}`;
|
||||
|
||||
switch (platform) {
|
||||
case 'linux-mac':
|
||||
return `sudo ./elastic-agent install ${commandArguments}`;
|
||||
case 'windows':
|
||||
return `.\\elastic-agent.exe install ${commandArguments}`;
|
||||
case 'rpm-deb':
|
||||
return `sudo elastic-agent enroll ${commandArguments}`;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export const useFleetServerInstructions = (policyId?: string) => {
|
||||
const outputsRequest = useGetOutputs();
|
||||
const { notifications } = useStartServices();
|
||||
const [serviceToken, setServiceToken] = useState<string>();
|
||||
const [isLoadingServiceToken, setIsLoadingServiceToken] = useState<boolean>(false);
|
||||
const { platform, setPlatform } = usePlatform();
|
||||
|
||||
const output = outputsRequest.data?.items?.[0];
|
||||
const esHost = output?.hosts?.[0];
|
||||
|
||||
const installCommand = useMemo((): string => {
|
||||
if (!serviceToken || !esHost) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return getInstallCommandForPlatform(platform, esHost, serviceToken, policyId);
|
||||
}, [serviceToken, esHost, platform, policyId]);
|
||||
|
||||
const getServiceToken = useCallback(async () => {
|
||||
setIsLoadingServiceToken(true);
|
||||
try {
|
||||
const { data } = await sendGenerateServiceToken();
|
||||
if (data?.value) {
|
||||
setServiceToken(data?.value);
|
||||
}
|
||||
} catch (err) {
|
||||
notifications.toasts.addError(err, {
|
||||
title: i18n.translate('xpack.fleet.fleetServerSetup.errorGeneratingTokenTitleText', {
|
||||
defaultMessage: 'Error generating token',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
setIsLoadingServiceToken(false);
|
||||
}, [notifications]);
|
||||
|
||||
return {
|
||||
serviceToken,
|
||||
getServiceToken,
|
||||
isLoadingServiceToken,
|
||||
installCommand,
|
||||
platform,
|
||||
setPlatform,
|
||||
};
|
||||
};
|
||||
|
||||
const OnPremInstructions: React.FC = () => {
|
||||
const {
|
||||
serviceToken,
|
||||
getServiceToken,
|
||||
isLoadingServiceToken,
|
||||
installCommand,
|
||||
platform,
|
||||
setPlatform,
|
||||
} = useFleetServerInstructions();
|
||||
const { docLinks } = useStartServices();
|
||||
|
||||
return (
|
||||
<EuiPanel paddingSize="l" grow={false} hasShadow={false} hasBorder={true}>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText className="eui-textCenter">
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.setupTitle"
|
||||
defaultMessage="Add a Fleet Server"
|
||||
/>
|
||||
</h2>
|
||||
<EuiSpacer size="m" />
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.setupText"
|
||||
defaultMessage="A Fleet Server is required before you can enroll agents with Fleet. See the {userGuideLink} for more information."
|
||||
values={{
|
||||
userGuideLink: (
|
||||
<EuiLink
|
||||
href={docLinks.links.fleet.fleetServerAddFleetServer}
|
||||
external
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.setupGuideLink"
|
||||
defaultMessage="Fleet User Guide"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiSteps
|
||||
className="eui-textLeft"
|
||||
steps={[
|
||||
DownloadStep(),
|
||||
ServiceTokenStep({ serviceToken, getServiceToken, isLoadingServiceToken }),
|
||||
FleetServerCommandStep({ serviceToken, installCommand, platform, setPlatform }),
|
||||
]}
|
||||
/>
|
||||
</EuiPanel>
|
||||
);
|
||||
};
|
||||
|
||||
const CloudInstructions: React.FC<{ deploymentUrl: string }> = ({ deploymentUrl }) => {
|
||||
const { docLinks } = useStartServices();
|
||||
|
||||
return (
|
||||
<EuiPanel
|
||||
paddingSize="none"
|
||||
grow={false}
|
||||
hasShadow={false}
|
||||
hasBorder={true}
|
||||
className="eui-textCenter"
|
||||
>
|
||||
<EuiEmptyPrompt
|
||||
title={
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.cloudSetupTitle"
|
||||
defaultMessage="Enable APM & Fleet"
|
||||
/>
|
||||
</h2>
|
||||
}
|
||||
body={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.cloudSetupText"
|
||||
defaultMessage="A Fleet Server is required before you can enroll agents with Fleet. You can add one to your deployment by enabling APM & Fleet. For more information see the {link}"
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink
|
||||
href={docLinks.links.fleet.fleetServerAddFleetServer}
|
||||
target="_blank"
|
||||
external
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.settings.userGuideLink"
|
||||
defaultMessage="Fleet User Guide"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
<EuiButton
|
||||
iconSide="right"
|
||||
iconType="popout"
|
||||
fill
|
||||
isLoading={false}
|
||||
type="submit"
|
||||
href={`${deploymentUrl}/edit`}
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.cloudDeploymentLink"
|
||||
defaultMessage="Edit deployment"
|
||||
/>
|
||||
</EuiButton>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</EuiPanel>
|
||||
);
|
||||
};
|
||||
|
||||
export const FleetServerRequirementPage = () => {
|
||||
const startService = useStartServices();
|
||||
const deploymentUrl = startService.cloud?.deploymentUrl;
|
||||
|
@ -411,23 +38,7 @@ export const FleetServerRequirementPage = () => {
|
|||
<OnPremInstructions />
|
||||
)}
|
||||
</FlexItemWithMinWidth>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner size="m" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.fleetServerSetup.waitingText"
|
||||
defaultMessage="Waiting for a Fleet Server to connect..."
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</ContentWrapper>
|
||||
<EuiSpacer size="xxl" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,9 +6,4 @@
|
|||
*/
|
||||
|
||||
export { MissingESRequirementsPage } from './es_requirements_page';
|
||||
export {
|
||||
FleetServerRequirementPage,
|
||||
ServiceTokenStep,
|
||||
FleetServerCommandStep,
|
||||
useFleetServerInstructions,
|
||||
} from './fleet_server_requirement_page';
|
||||
export { FleetServerRequirementPage } from './fleet_server_requirement_page';
|
||||
|
|
|
@ -5,18 +5,20 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState, useMemo } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { HashRouter as Router, Route, Switch } from 'react-router-dom';
|
||||
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPortal } from '@elastic/eui';
|
||||
|
||||
import { FLEET_ROUTING_PATHS } from '../../constants';
|
||||
import { Loading, Error } from '../../components';
|
||||
import { Loading, Error, AgentEnrollmentFlyout } from '../../components';
|
||||
import {
|
||||
useConfig,
|
||||
useFleetStatus,
|
||||
useBreadcrumbs,
|
||||
useCapabilities,
|
||||
useGetSettings,
|
||||
useGetAgentPolicies,
|
||||
} from '../../hooks';
|
||||
import { DefaultLayout, WithoutHeaderLayout } from '../../layouts';
|
||||
|
||||
|
@ -26,18 +28,26 @@ import { AgentDetailsPage } from './agent_details_page';
|
|||
import { NoAccessPage } from './error_pages/no_access';
|
||||
import { FleetServerUpgradeModal } from './components/fleet_server_upgrade_modal';
|
||||
|
||||
const REFRESH_INTERVAL_MS = 30000;
|
||||
|
||||
export const AgentsApp: React.FunctionComponent = () => {
|
||||
useBreadcrumbs('agent_list');
|
||||
|
||||
const { agents } = useConfig();
|
||||
const capabilities = useCapabilities();
|
||||
|
||||
const agentPoliciesRequest = useGetAgentPolicies({
|
||||
page: 1,
|
||||
perPage: 1000,
|
||||
});
|
||||
|
||||
const agentPolicies = useMemo(() => agentPoliciesRequest.data?.items || [], [
|
||||
agentPoliciesRequest.data,
|
||||
]);
|
||||
|
||||
const fleetStatus = useFleetStatus();
|
||||
|
||||
const settings = useGetSettings();
|
||||
|
||||
const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false);
|
||||
const [fleetServerModalVisible, setFleetServerModalVisible] = useState(false);
|
||||
const onCloseFleetServerModal = useCallback(() => {
|
||||
setFleetServerModalVisible(false);
|
||||
|
@ -50,25 +60,6 @@ export const AgentsApp: React.FunctionComponent = () => {
|
|||
}
|
||||
}, [settings.data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!agents.enabled ||
|
||||
fleetStatus.isLoading ||
|
||||
!fleetStatus.missingRequirements ||
|
||||
!fleetStatus.missingRequirements.includes('fleet_server')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
fleetStatus.refresh();
|
||||
}, REFRESH_INTERVAL_MS);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [fleetStatus, agents.enabled]);
|
||||
|
||||
if (!agents.enabled) return null;
|
||||
if (!fleetStatus.missingRequirements && fleetStatus.isLoading) {
|
||||
return <Loading />;
|
||||
|
@ -105,6 +96,27 @@ export const AgentsApp: React.FunctionComponent = () => {
|
|||
return <NoAccessPage />;
|
||||
}
|
||||
|
||||
const rightColumn = hasOnlyFleetServerMissingRequirement ? (
|
||||
<>
|
||||
{isEnrollmentFlyoutOpen && (
|
||||
<EuiPortal>
|
||||
<AgentEnrollmentFlyout
|
||||
defaultMode="standalone"
|
||||
agentPolicies={agentPolicies}
|
||||
onClose={() => setIsEnrollmentFlyoutOpen(false)}
|
||||
/>
|
||||
</EuiPortal>
|
||||
)}
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton fill iconType="plusInCircle" onClick={() => setIsEnrollmentFlyoutOpen(true)}>
|
||||
<FormattedMessage id="xpack.fleet.addAgentButton" defaultMessage="Add Agent" />
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
) : undefined;
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<Switch>
|
||||
|
@ -112,7 +124,7 @@ export const AgentsApp: React.FunctionComponent = () => {
|
|||
<AgentDetailsPage />
|
||||
</Route>
|
||||
<Route path={FLEET_ROUTING_PATHS.agents}>
|
||||
<DefaultLayout section="agents">
|
||||
<DefaultLayout section="agents" rightColumn={rightColumn}>
|
||||
{fleetServerModalVisible && (
|
||||
<FleetServerUpgradeModal onClose={onCloseFleetServerModal} />
|
||||
)}
|
||||
|
|
|
@ -14,6 +14,19 @@ jest.mock('../../hooks/use_request', () => {
|
|||
};
|
||||
});
|
||||
|
||||
jest.mock(
|
||||
'../../applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions',
|
||||
() => {
|
||||
const module = jest.requireActual(
|
||||
'../../applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions'
|
||||
);
|
||||
return {
|
||||
...module,
|
||||
useFleetServerInstructions: jest.fn(),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
jest.mock(
|
||||
'../../applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page',
|
||||
() => {
|
||||
|
@ -23,7 +36,6 @@ jest.mock(
|
|||
return {
|
||||
...module,
|
||||
FleetServerRequirementPage: jest.fn(),
|
||||
useFleetServerInstructions: jest.fn(),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
|
@ -19,7 +19,7 @@ import type { AgentPolicy } from '../../../common';
|
|||
import { useGetSettings, sendGetFleetStatus } from '../../hooks/use_request';
|
||||
import { FleetStatusProvider, ConfigContext } from '../../hooks';
|
||||
|
||||
import { useFleetServerInstructions } from '../../applications/fleet/sections/agents/agent_requirements_page';
|
||||
import { useFleetServerInstructions } from '../../applications/fleet/sections/agents/agent_requirements_page/components';
|
||||
|
||||
import { AgentEnrollmentKeySelectionStep, AgentPolicySelectionStep, ViewDataStep } from './steps';
|
||||
|
||||
|
|
|
@ -29,8 +29,11 @@ import { StandaloneInstructions } from './standalone_instructions';
|
|||
import { MissingFleetServerHostCallout } from './missing_fleet_server_host_callout';
|
||||
import type { BaseProps } from './types';
|
||||
|
||||
type FlyoutMode = 'managed' | 'standalone';
|
||||
|
||||
export interface Props extends BaseProps {
|
||||
onClose: () => void;
|
||||
defaultMode?: FlyoutMode;
|
||||
}
|
||||
|
||||
export * from './agent_policy_selection';
|
||||
|
@ -43,8 +46,9 @@ export const AgentEnrollmentFlyout: React.FunctionComponent<Props> = ({
|
|||
agentPolicy,
|
||||
agentPolicies,
|
||||
viewDataStepContent,
|
||||
defaultMode = 'managed',
|
||||
}) => {
|
||||
const [mode, setMode] = useState<'managed' | 'standalone'>('managed');
|
||||
const [mode, setMode] = useState<FlyoutMode>(defaultMode);
|
||||
|
||||
const { modal } = useUrlModal();
|
||||
const [lastModal, setLastModal] = useState(modal);
|
||||
|
|
|
@ -15,11 +15,13 @@ import { useGetOneEnrollmentAPIKey, useGetSettings, useLink, useFleetStatus } fr
|
|||
|
||||
import { ManualInstructions } from '../../components/enrollment_instructions';
|
||||
import {
|
||||
FleetServerRequirementPage,
|
||||
deploymentModeStep,
|
||||
ServiceTokenStep,
|
||||
FleetServerCommandStep,
|
||||
useFleetServerInstructions,
|
||||
} from '../../applications/fleet/sections/agents/agent_requirements_page';
|
||||
addFleetServerHostStep,
|
||||
} from '../../applications/fleet/sections/agents/agent_requirements_page/components';
|
||||
import { FleetServerRequirementPage } from '../../applications/fleet/sections/agents/agent_requirements_page';
|
||||
|
||||
import {
|
||||
DownloadStep,
|
||||
|
@ -69,7 +71,7 @@ export const ManagedInstructions = React.memo<Props>(
|
|||
const settings = useGetSettings();
|
||||
const fleetServerInstructions = useFleetServerInstructions(apiKey?.data?.item?.policy_id);
|
||||
|
||||
const steps = useMemo(() => {
|
||||
const fleetServerSteps = useMemo(() => {
|
||||
const {
|
||||
serviceToken,
|
||||
getServiceToken,
|
||||
|
@ -77,7 +79,20 @@ export const ManagedInstructions = React.memo<Props>(
|
|||
installCommand,
|
||||
platform,
|
||||
setPlatform,
|
||||
deploymentMode,
|
||||
setDeploymentMode,
|
||||
addFleetServerHost,
|
||||
} = fleetServerInstructions;
|
||||
|
||||
return [
|
||||
deploymentModeStep({ deploymentMode, setDeploymentMode }),
|
||||
addFleetServerHostStep({ addFleetServerHost }),
|
||||
ServiceTokenStep({ serviceToken, getServiceToken, isLoadingServiceToken }),
|
||||
FleetServerCommandStep({ serviceToken, installCommand, platform, setPlatform }),
|
||||
];
|
||||
}, [fleetServerInstructions]);
|
||||
|
||||
const steps = useMemo(() => {
|
||||
const fleetServerHosts = settings.data?.item?.fleet_server_hosts || [];
|
||||
const baseSteps: EuiContainedStepProps[] = [
|
||||
DownloadStep(),
|
||||
|
@ -91,12 +106,7 @@ export const ManagedInstructions = React.memo<Props>(
|
|||
: AgentEnrollmentKeySelectionStep({ agentPolicy, selectedApiKeyId, setSelectedAPIKeyId }),
|
||||
];
|
||||
if (isFleetServerPolicySelected) {
|
||||
baseSteps.push(
|
||||
...[
|
||||
ServiceTokenStep({ serviceToken, getServiceToken, isLoadingServiceToken }),
|
||||
FleetServerCommandStep({ serviceToken, installCommand, platform, setPlatform }),
|
||||
]
|
||||
);
|
||||
baseSteps.push(...fleetServerSteps);
|
||||
} else {
|
||||
baseSteps.push({
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle', {
|
||||
|
@ -115,13 +125,14 @@ export const ManagedInstructions = React.memo<Props>(
|
|||
return baseSteps;
|
||||
}, [
|
||||
agentPolicy,
|
||||
agentPolicies,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
viewDataStepContent,
|
||||
agentPolicies,
|
||||
apiKey.data,
|
||||
fleetServerSteps,
|
||||
isFleetServerPolicySelected,
|
||||
settings.data?.item?.fleet_server_hosts,
|
||||
fleetServerInstructions,
|
||||
viewDataStepContent,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
|
|
@ -27,7 +27,7 @@ export const DownloadStep = () => {
|
|||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.downloadDescription"
|
||||
defaultMessage="You can download the agent binaries and their verification signatures from the Elastic Agent download page."
|
||||
defaultMessage="Fleet Server runs on an Elastic Agent. You can download the Elastic Agent binaries and verification signatures from Elastic’s download page."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import React, { useState, useContext, useEffect, useCallback } from 'react';
|
||||
|
||||
import type { GetFleetStatusResponse } from '../types';
|
||||
|
||||
|
@ -33,30 +33,35 @@ export const FleetStatusProvider: React.FC = ({ children }) => {
|
|||
isLoading: false,
|
||||
isReady: false,
|
||||
});
|
||||
async function sendGetStatus() {
|
||||
try {
|
||||
setState((s) => ({ ...s, isLoading: true }));
|
||||
const res = await sendGetFleetStatus();
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
const sendGetStatus = useCallback(
|
||||
async function sendGetStatus() {
|
||||
try {
|
||||
setState((s) => ({ ...s, isLoading: true }));
|
||||
const res = await sendGetFleetStatus();
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
|
||||
setState((s) => ({
|
||||
...s,
|
||||
isLoading: false,
|
||||
isReady: res.data?.isReady ?? false,
|
||||
missingRequirements: res.data?.missing_requirements,
|
||||
}));
|
||||
} catch (error) {
|
||||
setState((s) => ({ ...s, isLoading: false, error }));
|
||||
}
|
||||
}
|
||||
setState((s) => ({
|
||||
...s,
|
||||
isLoading: false,
|
||||
isReady: res.data?.isReady ?? false,
|
||||
missingRequirements: res.data?.missing_requirements,
|
||||
}));
|
||||
} catch (error) {
|
||||
setState((s) => ({ ...s, isLoading: false, error }));
|
||||
}
|
||||
},
|
||||
[setState]
|
||||
);
|
||||
useEffect(() => {
|
||||
sendGetStatus();
|
||||
}, []);
|
||||
}, [sendGetStatus]);
|
||||
|
||||
const refresh = useCallback(() => sendGetStatus(), [sendGetStatus]);
|
||||
|
||||
return (
|
||||
<FleetStatusContext.Provider value={{ ...state, refresh: () => sendGetStatus() }}>
|
||||
<FleetStatusContext.Provider value={{ ...state, refresh }}>
|
||||
{children}
|
||||
</FleetStatusContext.Provider>
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue