mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Fleet] Redesign agent flyout (#128381)
* [Fleet] Redesign and refactor agent flyout * Further refactoring * More refactoring and some bug fixing * bug fixing * Fix step tests + skip standalone tab tests * Use test-subj for agent policy code block to fix cypress tests * Fixing small issues on rendering * Fix broken import; Fix button in incoming data comp * Fix loading; add button in incoming data step * Fix some failures * Fix tests * Fix checks * Address code review comments; add retry logic to incoming data component * Fix download link * Check enrolled agents in last 10 minutes * Filter out agents with data=false from incoming data Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kyle Pollich <kyle.pollich@elastic.co>
This commit is contained in:
parent
b239151646
commit
6e8d97948a
44 changed files with 1705 additions and 1114 deletions
|
@ -10,6 +10,7 @@ import {
|
|||
ADD_AGENT_BUTTON_TOP,
|
||||
AGENT_FLYOUT_CLOSE_BUTTON,
|
||||
STANDALONE_TAB,
|
||||
AGENT_POLICY_CODE_BLOCK,
|
||||
} from '../screens/fleet';
|
||||
import { cleanupAgentPolicies, unenrollAgent } from '../tasks/cleanup';
|
||||
import { verifyPolicy, verifyAgentPackage, navigateToTab } from '../tasks/fleet';
|
||||
|
@ -60,12 +61,11 @@ describe('Fleet startup', () => {
|
|||
cy.log('Create agent policy took: ' + (Date.now() - startTime) / 1000 + ' s');
|
||||
agentPolicyId = xhr.response.body.item.id;
|
||||
|
||||
cy.getBySel('agentPolicyCreateStatusCallOut').contains('Agent policy created');
|
||||
|
||||
// verify create button changed to dropdown
|
||||
cy.getBySel('agentPolicyDropdown');
|
||||
|
||||
// verify agent.yml code block has new policy id
|
||||
cy.get('.euiCodeBlock__code').contains(`id: ${agentPolicyId}`);
|
||||
cy.getBySel(AGENT_POLICY_CODE_BLOCK).contains(`id: ${agentPolicyId}`);
|
||||
|
||||
cy.getBySel(AGENT_FLYOUT_CLOSE_BUTTON).click();
|
||||
|
||||
|
@ -78,7 +78,6 @@ describe('Fleet startup', () => {
|
|||
|
||||
it('should create Fleet Server policy', () => {
|
||||
cy.getBySel('createFleetServerPolicyBtn').click();
|
||||
cy.getBySel('agentPolicyCreateStatusCallOut').contains('Agent policy created');
|
||||
|
||||
// verify policy is created and has fleet server and system package
|
||||
verifyPolicy('Fleet Server policy 1', ['Fleet Server', 'System']);
|
||||
|
|
|
@ -9,6 +9,7 @@ export const ADD_AGENT_BUTTON = 'addAgentButton';
|
|||
export const ADD_AGENT_BUTTON_TOP = 'addAgentBtnTop';
|
||||
export const CREATE_POLICY_BUTTON = 'createPolicyBtn';
|
||||
export const AGENT_FLYOUT_CLOSE_BUTTON = 'euiFlyoutCloseButton';
|
||||
export const AGENT_POLICY_CODE_BLOCK = 'agentPolicyCodeBlock';
|
||||
|
||||
export const AGENTS_TAB = 'fleet-agents-tab';
|
||||
export const AGENT_POLICIES_TAB = 'fleet-agent-policies-tab';
|
||||
|
|
|
@ -18,7 +18,6 @@ export function createAgentPolicy() {
|
|||
cy.getBySel(ADD_AGENT_BUTTON_TOP).click();
|
||||
cy.getBySel(STANDALONE_TAB).click();
|
||||
cy.getBySel(CREATE_POLICY_BUTTON).click();
|
||||
cy.getBySel('agentPolicyCreateStatusCallOut').contains('Agent policy created');
|
||||
cy.getBySel(AGENT_FLYOUT_CLOSE_BUTTON).click();
|
||||
}
|
||||
|
||||
|
|
|
@ -203,7 +203,6 @@ export const FleetServerCommandStep = ({
|
|||
windowsCommand={installCommand.windows}
|
||||
linuxDebCommand={installCommand.deb}
|
||||
linuxRpmCommand={installCommand.rpm}
|
||||
troubleshootLink={docLinks.links.fleet.troubleshooting}
|
||||
isK8s={false}
|
||||
/>
|
||||
</>
|
||||
|
@ -290,36 +289,16 @@ export const useFleetServerInstructions = (policyId?: string) => {
|
|||
};
|
||||
|
||||
const AgentPolicySelectionStep = ({
|
||||
policyId,
|
||||
selectedPolicy,
|
||||
setPolicyId,
|
||||
agentPolicies,
|
||||
refreshAgentPolicies,
|
||||
}: {
|
||||
policyId?: string;
|
||||
setPolicyId: (v: string) => void;
|
||||
selectedPolicy?: AgentPolicy;
|
||||
setPolicyId: (v?: string) => void;
|
||||
agentPolicies: AgentPolicy[];
|
||||
refreshAgentPolicies: () => void;
|
||||
}): EuiStepProps => {
|
||||
const { data, resendRequest: refreshAgentPolicies } = useGetAgentPolicies({ full: true });
|
||||
|
||||
const agentPolicies = useMemo(
|
||||
() => (data ? data.items.filter((item) => policyHasFleetServer(item)) : []),
|
||||
[data]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Select default value
|
||||
if (agentPolicies.length && !policyId) {
|
||||
setPolicyId(agentPolicies[0].id);
|
||||
}
|
||||
}, [agentPolicies, policyId, setPolicyId]);
|
||||
|
||||
const onChangeCallback = useCallback(
|
||||
(key: string | undefined, policy?: AgentPolicy) => {
|
||||
if (policy) {
|
||||
refreshAgentPolicies();
|
||||
}
|
||||
setPolicyId(key!);
|
||||
},
|
||||
[setPolicyId, refreshAgentPolicies]
|
||||
);
|
||||
|
||||
return {
|
||||
title:
|
||||
agentPolicies.length === 0
|
||||
|
@ -335,9 +314,11 @@ const AgentPolicySelectionStep = ({
|
|||
<SelectCreateAgentPolicy
|
||||
agentPolicies={agentPolicies}
|
||||
withKeySelection={false}
|
||||
onAgentPolicyChange={onChangeCallback}
|
||||
excludeFleetServer={false}
|
||||
isFleetServerPolicy={true}
|
||||
selectedPolicy={selectedPolicy}
|
||||
setSelectedPolicyId={setPolicyId}
|
||||
refreshAgentPolicies={refreshAgentPolicies}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
|
@ -642,9 +623,28 @@ const CompleteStep = (): EuiStepProps => {
|
|||
};
|
||||
};
|
||||
|
||||
const findPolicyById = (policies: AgentPolicy[], id: string | undefined) => {
|
||||
if (!id) return undefined;
|
||||
return policies.find((p) => p.id === id);
|
||||
};
|
||||
|
||||
export const OnPremInstructions: React.FC = () => {
|
||||
const { notifications } = useStartServices();
|
||||
const [policyId, setPolicyId] = useState<string | undefined>();
|
||||
|
||||
const { data, resendRequest: refreshAgentPolicies } = useGetAgentPolicies({ full: true });
|
||||
|
||||
const agentPolicies = useMemo(
|
||||
() => (data ? data.items.filter((item) => policyHasFleetServer(item)) : []),
|
||||
[data]
|
||||
);
|
||||
|
||||
// Select default value
|
||||
let defaultValue = '';
|
||||
if (agentPolicies.length) {
|
||||
defaultValue = agentPolicies[0].id;
|
||||
}
|
||||
const [policyId, setPolicyId] = useState<string | undefined>(defaultValue);
|
||||
const selectedPolicy = findPolicyById(agentPolicies, policyId);
|
||||
|
||||
const {
|
||||
serviceToken,
|
||||
|
@ -720,7 +720,12 @@ export const OnPremInstructions: React.FC = () => {
|
|||
<EuiSteps
|
||||
className="eui-textLeft"
|
||||
steps={[
|
||||
AgentPolicySelectionStep({ policyId, setPolicyId }),
|
||||
AgentPolicySelectionStep({
|
||||
selectedPolicy,
|
||||
setPolicyId,
|
||||
agentPolicies,
|
||||
refreshAgentPolicies,
|
||||
}),
|
||||
DownloadStep(true),
|
||||
deploymentModeStep({ deploymentMode, setDeploymentMode }),
|
||||
addFleetServerHostStep({ addFleetServerHost }),
|
||||
|
|
|
@ -85,6 +85,7 @@ export const AgentsApp: React.FunctionComponent = () => {
|
|||
<EuiPortal>
|
||||
<AgentEnrollmentFlyout
|
||||
defaultMode="standalone"
|
||||
isIntegrationFlow={true}
|
||||
onClose={() => setIsEnrollmentFlyoutOpen(false)}
|
||||
/>
|
||||
</EuiPortal>
|
||||
|
|
|
@ -7,11 +7,7 @@
|
|||
import { stringify, parse } from 'query-string';
|
||||
import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Redirect, useLocation, useHistory } from 'react-router-dom';
|
||||
import type {
|
||||
CriteriaWithPagination,
|
||||
EuiStepProps,
|
||||
EuiTableFieldDataColumnType,
|
||||
} from '@elastic/eui';
|
||||
import type { CriteriaWithPagination, EuiTableFieldDataColumnType } from '@elastic/eui';
|
||||
import {
|
||||
EuiBasicTable,
|
||||
EuiLink,
|
||||
|
@ -19,7 +15,6 @@ import {
|
|||
EuiFlexItem,
|
||||
EuiText,
|
||||
EuiButton,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedRelative, FormattedMessage } from '@kbn/i18n-react';
|
||||
|
@ -31,7 +26,6 @@ import {
|
|||
useUrlPagination,
|
||||
useGetPackageInstallStatus,
|
||||
AgentPolicyRefreshContext,
|
||||
useUIExtension,
|
||||
usePackageInstallations,
|
||||
useAuthz,
|
||||
} from '../../../../../hooks';
|
||||
|
@ -103,7 +97,6 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
|
|||
kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: ${name}`,
|
||||
});
|
||||
const { updatableIntegrations } = usePackageInstallations();
|
||||
const agentEnrollmentFlyoutExtension = useUIExtension(name, 'agent-enrollment-flyout');
|
||||
|
||||
const canWriteIntegrationPolicies = useAuthz().integrations.writeIntegrationPolicies;
|
||||
|
||||
|
@ -165,50 +158,6 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
|
|||
[setPagination]
|
||||
);
|
||||
|
||||
const viewDataStep = useMemo<EuiStepProps>(() => {
|
||||
if (agentEnrollmentFlyoutExtension) {
|
||||
return {
|
||||
title: agentEnrollmentFlyoutExtension.title,
|
||||
children: <agentEnrollmentFlyoutExtension.Component />,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepViewDataTitle', {
|
||||
defaultMessage: 'View your data',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.viewDataDescription"
|
||||
defaultMessage="After your agent starts, you can view your data in Kibana by using the integration's installed assets. {pleaseNote}: it may take a few minutes for the initial data to arrive."
|
||||
values={{
|
||||
pleaseNote: (
|
||||
<strong>
|
||||
{i18n.translate(
|
||||
'xpack.fleet.epm.agentEnrollment.viewDataDescription.pleaseNoteLabel',
|
||||
{ defaultMessage: 'Please note' }
|
||||
)}
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiButton
|
||||
fill
|
||||
href={getHref('integration_details_assets', { pkgkey: `${name}-${version}` })}
|
||||
>
|
||||
{i18n.translate('xpack.fleet.epm.agentEnrollment.viewDataAssetsLabel', {
|
||||
defaultMessage: 'View assets',
|
||||
})}
|
||||
</EuiButton>
|
||||
</>
|
||||
),
|
||||
};
|
||||
}, [name, version, getHref, agentEnrollmentFlyoutExtension]);
|
||||
|
||||
const columns: Array<EuiTableFieldDataColumnType<InMemoryPackagePolicyAndAgentPolicy>> = useMemo(
|
||||
() => [
|
||||
{
|
||||
|
@ -323,7 +272,6 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
|
|||
<PackagePolicyActionsMenu
|
||||
agentPolicy={agentPolicy}
|
||||
packagePolicy={packagePolicy}
|
||||
viewDataStep={viewDataStep}
|
||||
showAddAgent={true}
|
||||
upgradePackagePolicyHref={`${getHref('upgrade_package_policy', {
|
||||
policyId: agentPolicy.id,
|
||||
|
@ -334,7 +282,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
|
|||
},
|
||||
},
|
||||
],
|
||||
[getHref, showAddAgentHelpForPackagePolicyId, viewDataStep, canWriteIntegrationPolicies]
|
||||
[getHref, showAddAgentHelpForPackagePolicyId, canWriteIntegrationPolicies]
|
||||
);
|
||||
|
||||
const noItemsMessage = useMemo(() => {
|
||||
|
@ -362,6 +310,11 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
|
|||
<Redirect to={getPath('integration_details_overview', { pkgkey: `${name}-${version}` })} />
|
||||
);
|
||||
}
|
||||
const selectedPolicies = packageAndAgentPolicies.find(
|
||||
({ agentPolicy: policy }) => policy.id === flyoutOpenForPolicyId
|
||||
);
|
||||
const agentPolicy = selectedPolicies?.agentPolicy;
|
||||
const packagePolicy = selectedPolicies?.packagePolicy;
|
||||
|
||||
return (
|
||||
<AgentPolicyRefreshContext.Provider value={{ refresh: refreshPolicies }}>
|
||||
|
@ -379,19 +332,19 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
{flyoutOpenForPolicyId && !isLoading && (
|
||||
{flyoutOpenForPolicyId && agentPolicy && !isLoading && (
|
||||
<AgentEnrollmentFlyout
|
||||
onClose={() => {
|
||||
setFlyoutOpenForPolicyId(null);
|
||||
const { addAgentToPolicyId, ...rest } = parse(search);
|
||||
history.replace({ search: stringify(rest) });
|
||||
}}
|
||||
agentPolicy={
|
||||
packageAndAgentPolicies.find(
|
||||
({ agentPolicy }) => agentPolicy.id === flyoutOpenForPolicyId
|
||||
)?.agentPolicy
|
||||
}
|
||||
viewDataStep={viewDataStep}
|
||||
agentPolicy={agentPolicy}
|
||||
isIntegrationFlow={true}
|
||||
installedPackagePolicy={{
|
||||
name: packagePolicy?.package?.name || '',
|
||||
version: packagePolicy?.package?.version || '',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</AgentPolicyRefreshContext.Provider>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
jest.mock('../../hooks', () => {
|
||||
return {
|
||||
...jest.requireActual('../../hooks'),
|
||||
useFleetStatus: jest.fn(),
|
||||
useAgentEnrollmentFlyoutData: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
@ -24,6 +25,16 @@ jest.mock('../../hooks/use_request', () => {
|
|||
};
|
||||
});
|
||||
|
||||
jest.mock('../../applications/fleet/sections/agents/hooks/use_fleet_server_unhealthy', () => {
|
||||
const module = jest.requireActual(
|
||||
'../../applications/fleet/sections/agents/hooks/use_fleet_server_unhealthy'
|
||||
);
|
||||
return {
|
||||
...module,
|
||||
useFleetServerUnhealthy: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock(
|
||||
'../../applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions',
|
||||
() => {
|
||||
|
@ -57,18 +68,17 @@ jest.mock('./steps', () => {
|
|||
const module = jest.requireActual('./steps');
|
||||
return {
|
||||
...module,
|
||||
AgentPolicySelectionStep: jest.fn(),
|
||||
AgentEnrollmentKeySelectionStep: jest.fn(),
|
||||
ViewDataStep: jest.fn(),
|
||||
DownloadStep: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('@elastic/eui', () => {
|
||||
const module = jest.requireActual('@elastic/eui');
|
||||
return {
|
||||
...module,
|
||||
EuiSteps: 'eui-steps',
|
||||
AgentPolicySelectionStep: jest.fn().mockReturnValue({
|
||||
'data-test-subj': 'agent-policy-selection-step',
|
||||
title: 'agent-policy-selection-step',
|
||||
}),
|
||||
AgentEnrollmentKeySelectionStep: jest.fn().mockReturnValue({
|
||||
'data-test-subj': 'agent-enrollment-key-selection-step',
|
||||
title: 'agent-enrollment-key-selection-step',
|
||||
}),
|
||||
DownloadStep: jest
|
||||
.fn()
|
||||
.mockReturnValue({ 'data-test-subj': 'download-step', title: 'download-step' }),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -16,27 +16,23 @@ import { coreMock } from 'src/core/public/mocks';
|
|||
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
import type { AgentPolicy } from '../../../common';
|
||||
import {
|
||||
useGetSettings,
|
||||
sendGetFleetStatus,
|
||||
sendGetOneAgentPolicy,
|
||||
useGetAgents,
|
||||
} from '../../hooks/use_request';
|
||||
import { useGetSettings, sendGetOneAgentPolicy, useGetAgents } from '../../hooks/use_request';
|
||||
import {
|
||||
FleetStatusProvider,
|
||||
ConfigContext,
|
||||
useAgentEnrollmentFlyoutData,
|
||||
KibanaVersionContext,
|
||||
useFleetStatus,
|
||||
} from '../../hooks';
|
||||
|
||||
import { useFleetServerInstructions } from '../../applications/fleet/sections/agents/agent_requirements_page/components';
|
||||
|
||||
import { AgentEnrollmentKeySelectionStep, AgentPolicySelectionStep } from './steps';
|
||||
import { useFleetServerUnhealthy } from '../../applications/fleet/sections/agents/hooks/use_fleet_server_unhealthy';
|
||||
|
||||
import type { Props } from '.';
|
||||
import type { FlyOutProps } from './types';
|
||||
import { AgentEnrollmentFlyout } from '.';
|
||||
|
||||
const TestComponent = (props: Props) => (
|
||||
const TestComponent = (props: FlyOutProps) => (
|
||||
<KibanaContextProvider services={coreMock.createStart()}>
|
||||
<ConfigContext.Provider value={{ agents: { enabled: true, elasticsearch: {} }, enabled: true }}>
|
||||
<KibanaVersionContext.Provider value={'8.1.0'}>
|
||||
|
@ -48,7 +44,7 @@ const TestComponent = (props: Props) => (
|
|||
</KibanaContextProvider>
|
||||
);
|
||||
|
||||
const setup = async (props?: Props) => {
|
||||
const setup = async (props?: FlyOutProps) => {
|
||||
const testBed = await registerTestBed(TestComponent)(props);
|
||||
const { find, component } = testBed;
|
||||
|
||||
|
@ -87,8 +83,10 @@ describe('<AgentEnrollmentFlyout />', () => {
|
|||
data: { item: { fleet_server_hosts: ['test'] } },
|
||||
});
|
||||
|
||||
(sendGetFleetStatus as jest.Mock).mockResolvedValue({
|
||||
data: { isReady: true },
|
||||
(useFleetStatus as jest.Mock).mockReturnValue({ isReady: true });
|
||||
(useFleetServerUnhealthy as jest.Mock).mockReturnValue({
|
||||
isLoading: false,
|
||||
isUnhealthy: false,
|
||||
});
|
||||
|
||||
(sendGetOneAgentPolicy as jest.Mock).mockResolvedValue({
|
||||
|
@ -147,9 +145,10 @@ describe('<AgentEnrollmentFlyout />', () => {
|
|||
describe('managed instructions', () => {
|
||||
it('uses the agent policy selection step', async () => {
|
||||
const { exists } = testBed;
|
||||
|
||||
expect(exists('agentEnrollmentFlyout')).toBe(true);
|
||||
expect(AgentPolicySelectionStep).toHaveBeenCalled();
|
||||
expect(AgentEnrollmentKeySelectionStep).not.toHaveBeenCalled();
|
||||
expect(exists('agent-policy-selection-step')).toBe(true);
|
||||
expect(exists('agent-enrollment-key-selection-step')).toBe(false);
|
||||
});
|
||||
|
||||
describe('with a specific policy', () => {
|
||||
|
@ -167,8 +166,8 @@ describe('<AgentEnrollmentFlyout />', () => {
|
|||
it('uses the configure enrollment step, not the agent policy selection step', () => {
|
||||
const { exists } = testBed;
|
||||
expect(exists('agentEnrollmentFlyout')).toBe(true);
|
||||
expect(AgentPolicySelectionStep).not.toHaveBeenCalled();
|
||||
expect(AgentEnrollmentKeySelectionStep).toHaveBeenCalled();
|
||||
expect(exists('agent-policy-selection-step')).toBe(false);
|
||||
expect(exists('agent-enrollment-key-selection-step')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -187,58 +186,21 @@ describe('<AgentEnrollmentFlyout />', () => {
|
|||
it('should not show fleet server instructions', () => {
|
||||
const { exists } = testBed;
|
||||
expect(exists('agentEnrollmentFlyout')).toBe(true);
|
||||
expect(AgentEnrollmentKeySelectionStep).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
// Skipped due to implementation details in the step components. See https://github.com/elastic/kibana/issues/103894
|
||||
describe.skip('"View data" extension point', () => {
|
||||
it('shows the "View data" step when UI extension is provided', async () => {
|
||||
jest.clearAllMocks();
|
||||
await act(async () => {
|
||||
testBed = await setup({
|
||||
onClose: jest.fn(),
|
||||
viewDataStep: { title: 'View Data', children: <div /> },
|
||||
});
|
||||
testBed.component.update();
|
||||
});
|
||||
const { exists, actions } = testBed;
|
||||
expect(exists('agentEnrollmentFlyout')).toBe(true);
|
||||
expect(exists('view-data-step')).toBe(true);
|
||||
|
||||
jest.clearAllMocks();
|
||||
actions.goToStandaloneTab();
|
||||
expect(exists('agentEnrollmentFlyout')).toBe(true);
|
||||
expect(exists('view-data-step')).toBe(false);
|
||||
});
|
||||
|
||||
it('does not call the "View data" step when UI extension is not provided', async () => {
|
||||
jest.clearAllMocks();
|
||||
await act(async () => {
|
||||
testBed = await setup({
|
||||
onClose: jest.fn(),
|
||||
viewDataStep: undefined,
|
||||
});
|
||||
testBed.component.update();
|
||||
});
|
||||
const { exists, actions } = testBed;
|
||||
expect(exists('agentEnrollmentFlyout')).toBe(true);
|
||||
expect(exists('view-data-step')).toBe(false);
|
||||
|
||||
jest.clearAllMocks();
|
||||
actions.goToStandaloneTab();
|
||||
expect(exists('view-data-step')).toBe(false);
|
||||
expect(exists('agent-enrollment-key-selection-step')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('standalone instructions', () => {
|
||||
// Skipped due to UI changing in https://github.com/elastic/kibana/issues/125534. These tests should be rethought overall
|
||||
// to provide value around the new flyout structure
|
||||
describe.skip('standalone instructions', () => {
|
||||
it('uses the agent policy selection step', async () => {
|
||||
const { exists, actions } = testBed;
|
||||
actions.goToStandaloneTab();
|
||||
|
||||
expect(exists('agentEnrollmentFlyout')).toBe(true);
|
||||
expect(AgentPolicySelectionStep).toHaveBeenCalled();
|
||||
expect(AgentEnrollmentKeySelectionStep).not.toHaveBeenCalled();
|
||||
expect(exists('agent-policy-selection-step')).toBe(true);
|
||||
expect(exists('agent-enrollment-key-selection-step')).toBe(false);
|
||||
});
|
||||
|
||||
describe('with a specific policy', () => {
|
||||
|
@ -256,10 +218,12 @@ describe('<AgentEnrollmentFlyout />', () => {
|
|||
it('does not use either of the agent policy selection or enrollment key steps', () => {
|
||||
const { exists, actions } = testBed;
|
||||
jest.clearAllMocks();
|
||||
expect(exists('agentEnrollmentFlyout')).toBe(true);
|
||||
|
||||
actions.goToStandaloneTab();
|
||||
expect(AgentPolicySelectionStep).not.toHaveBeenCalled();
|
||||
expect(AgentEnrollmentKeySelectionStep).not.toHaveBeenCalled();
|
||||
|
||||
expect(exists('agentEnrollmentFlyout')).toBe(true);
|
||||
expect(exists('agent-policy-selection-step')).toBe(false);
|
||||
expect(exists('agent-enrollment-key-selection-step')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import React, { useState, useCallback, useEffect, useMemo } from 'react';
|
||||
|
||||
import type { AgentPolicyCreateState } from '../../applications/fleet/sections/agents/components';
|
||||
import {
|
||||
|
@ -16,43 +16,60 @@ import { AgentPolicyCreateInlineForm } from '../../applications/fleet/sections/a
|
|||
import type { AgentPolicy } from '../../types';
|
||||
import { incrementPolicyName } from '../../services';
|
||||
|
||||
import { EnrollmentStepAgentPolicy } from '.';
|
||||
import { AgentPolicySelection } from '.';
|
||||
|
||||
interface Props {
|
||||
agentPolicies: AgentPolicy[];
|
||||
selectedPolicy?: AgentPolicy;
|
||||
setSelectedPolicyId: (agentPolicyId?: string) => void;
|
||||
excludeFleetServer?: boolean;
|
||||
onAgentPolicyChange: (key?: string, policy?: AgentPolicy) => void;
|
||||
withKeySelection: boolean;
|
||||
selectedApiKeyId?: string;
|
||||
onKeyChange?: (key?: string) => void;
|
||||
isFleetServerPolicy?: boolean;
|
||||
policyId?: string;
|
||||
refreshAgentPolicies: () => void;
|
||||
}
|
||||
|
||||
export const SelectCreateAgentPolicy: React.FC<Props> = ({
|
||||
agentPolicies,
|
||||
excludeFleetServer,
|
||||
onAgentPolicyChange,
|
||||
setSelectedPolicyId,
|
||||
selectedPolicy,
|
||||
withKeySelection,
|
||||
selectedApiKeyId,
|
||||
onKeyChange,
|
||||
isFleetServerPolicy,
|
||||
policyId,
|
||||
refreshAgentPolicies,
|
||||
}) => {
|
||||
const [showCreatePolicy, setShowCreatePolicy] = useState(agentPolicies.length === 0);
|
||||
const regularAgentPolicies = useMemo(() => {
|
||||
return agentPolicies.filter(
|
||||
(policy) =>
|
||||
policy && !policy.is_managed && (!excludeFleetServer || !policy.is_default_fleet_server)
|
||||
);
|
||||
}, [agentPolicies, excludeFleetServer]);
|
||||
|
||||
const onAgentPolicyChange = useCallback(
|
||||
async (key?: string, policy?: AgentPolicy) => {
|
||||
if (policy) {
|
||||
refreshAgentPolicies();
|
||||
}
|
||||
},
|
||||
[refreshAgentPolicies]
|
||||
);
|
||||
const [showCreatePolicy, setShowCreatePolicy] = useState(regularAgentPolicies.length === 0);
|
||||
|
||||
const [createState, setCreateState] = useState<AgentPolicyCreateState>({
|
||||
status: CREATE_STATUS.INITIAL,
|
||||
});
|
||||
|
||||
const [newName, setNewName] = useState(incrementPolicyName(agentPolicies, isFleetServerPolicy));
|
||||
|
||||
const [selectedAgentPolicy, setSelectedAgentPolicy] = useState<string | undefined>(policyId);
|
||||
const [newName, setNewName] = useState(
|
||||
incrementPolicyName(regularAgentPolicies, isFleetServerPolicy)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setShowCreatePolicy(agentPolicies.length === 0);
|
||||
setNewName(incrementPolicyName(agentPolicies, isFleetServerPolicy));
|
||||
}, [agentPolicies, isFleetServerPolicy]);
|
||||
setShowCreatePolicy(regularAgentPolicies.length === 0);
|
||||
setNewName(incrementPolicyName(regularAgentPolicies, isFleetServerPolicy));
|
||||
}, [regularAgentPolicies, isFleetServerPolicy]);
|
||||
|
||||
const onAgentPolicyCreated = useCallback(
|
||||
async (policy: AgentPolicy | null, errorMessage?: JSX.Element) => {
|
||||
|
@ -65,9 +82,9 @@ export const SelectCreateAgentPolicy: React.FC<Props> = ({
|
|||
if (onAgentPolicyChange) {
|
||||
onAgentPolicyChange(policy.id, policy!);
|
||||
}
|
||||
setSelectedAgentPolicy(policy.id);
|
||||
setSelectedPolicyId(policy.id);
|
||||
},
|
||||
[onAgentPolicyChange]
|
||||
[setSelectedPolicyId, onAgentPolicyChange]
|
||||
);
|
||||
|
||||
const onClickCreatePolicy = () => {
|
||||
|
@ -87,15 +104,15 @@ export const SelectCreateAgentPolicy: React.FC<Props> = ({
|
|||
agentPolicyName={newName}
|
||||
/>
|
||||
) : (
|
||||
<EnrollmentStepAgentPolicy
|
||||
agentPolicies={agentPolicies}
|
||||
<AgentPolicySelection
|
||||
agentPolicies={regularAgentPolicies}
|
||||
withKeySelection={withKeySelection}
|
||||
selectedApiKeyId={selectedApiKeyId}
|
||||
onKeyChange={onKeyChange}
|
||||
onAgentPolicyChange={onAgentPolicyChange}
|
||||
excludeFleetServer={excludeFleetServer}
|
||||
onClickCreatePolicy={onClickCreatePolicy}
|
||||
selectedAgentPolicy={selectedAgentPolicy}
|
||||
selectedPolicy={selectedPolicy}
|
||||
setSelectedPolicyId={setSelectedPolicyId}
|
||||
isFleetServerPolicy={isFleetServerPolicy}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -13,7 +13,7 @@ import { createFleetTestRendererMock } from '../../mock';
|
|||
|
||||
import type { AgentPolicy } from '../../types';
|
||||
|
||||
import { EnrollmentStepAgentPolicy } from '.';
|
||||
import { AgentPolicySelection } from '.';
|
||||
|
||||
describe('step select agent policy', () => {
|
||||
let testRenderer: TestRenderer;
|
||||
|
@ -21,10 +21,11 @@ describe('step select agent policy', () => {
|
|||
let agentPolicies: AgentPolicy[] = [];
|
||||
const render = () =>
|
||||
(renderResult = testRenderer.render(
|
||||
<EnrollmentStepAgentPolicy
|
||||
<AgentPolicySelection
|
||||
setSelectedPolicyId={jest.fn()}
|
||||
selectedPolicy={agentPolicies[0]}
|
||||
agentPolicies={agentPolicies}
|
||||
withKeySelection={false}
|
||||
onAgentPolicyChange={jest.fn()}
|
||||
excludeFleetServer={true}
|
||||
onClickCreatePolicy={jest.fn()}
|
||||
isFleetServerPolicy={false}
|
||||
|
@ -35,7 +36,7 @@ describe('step select agent policy', () => {
|
|||
testRenderer = createFleetTestRendererMock();
|
||||
});
|
||||
|
||||
test('should not select agent policy by default if multiple exists', async () => {
|
||||
test('should select first agent policy by default if multiple exists', async () => {
|
||||
agentPolicies = [
|
||||
{ id: 'policy-1', name: 'Policy 1' } as AgentPolicy,
|
||||
{ id: 'policy-2', name: 'Policy 2' } as AgentPolicy,
|
||||
|
@ -45,7 +46,7 @@ describe('step select agent policy', () => {
|
|||
|
||||
await act(async () => {
|
||||
const select = renderResult.container.querySelector('[data-test-subj="agentPolicyDropdown"]');
|
||||
expect((select as any)?.value).toEqual('');
|
||||
expect((select as any)?.value).toEqual('policy-1');
|
||||
|
||||
expect(renderResult.getAllByRole('option').length).toBe(2);
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
@ -34,10 +34,10 @@ const AgentPolicyFormRow = styled(EuiFormRow)`
|
|||
|
||||
type Props = {
|
||||
agentPolicies: AgentPolicy[];
|
||||
onAgentPolicyChange: (key?: string, policy?: AgentPolicy) => void;
|
||||
selectedPolicy?: AgentPolicy;
|
||||
setSelectedPolicyId: (agentPolicyId?: string) => void;
|
||||
excludeFleetServer?: boolean;
|
||||
onClickCreatePolicy: () => void;
|
||||
selectedAgentPolicy?: string;
|
||||
isFleetServerPolicy?: boolean;
|
||||
} & (
|
||||
| {
|
||||
|
@ -63,44 +63,33 @@ const resolveAgentId = (
|
|||
return selectedAgentPolicyId;
|
||||
};
|
||||
|
||||
export const EnrollmentStepAgentPolicy: React.FC<Props> = (props) => {
|
||||
export const AgentPolicySelection: React.FC<Props> = (props) => {
|
||||
const {
|
||||
agentPolicies,
|
||||
onAgentPolicyChange,
|
||||
selectedPolicy,
|
||||
setSelectedPolicyId,
|
||||
excludeFleetServer,
|
||||
onClickCreatePolicy,
|
||||
selectedAgentPolicy,
|
||||
isFleetServerPolicy,
|
||||
} = props;
|
||||
|
||||
const [selectedAgentPolicyId, setSelectedAgentPolicyId] = useState<undefined | string>(
|
||||
() => resolveAgentId(agentPolicies, undefined) // no agent id selected yet
|
||||
);
|
||||
|
||||
const hasFleetAllPrivileges = useAuthz().fleet.all;
|
||||
|
||||
useEffect(
|
||||
function triggerOnAgentPolicyChangeEffect() {
|
||||
if (onAgentPolicyChange) {
|
||||
onAgentPolicyChange(selectedAgentPolicyId);
|
||||
}
|
||||
},
|
||||
[selectedAgentPolicyId, onAgentPolicyChange]
|
||||
);
|
||||
|
||||
useEffect(
|
||||
function useDefaultAgentPolicyEffect() {
|
||||
const resolvedId = resolveAgentId(agentPolicies, selectedAgentPolicyId);
|
||||
if (resolvedId !== selectedAgentPolicyId) {
|
||||
setSelectedAgentPolicyId(resolvedId);
|
||||
const resolvedId = resolveAgentId(agentPolicies, selectedPolicy?.id);
|
||||
// find AgentPolicy
|
||||
if (resolvedId !== selectedPolicy?.id) {
|
||||
setSelectedPolicyId(resolvedId);
|
||||
}
|
||||
},
|
||||
[agentPolicies, selectedAgentPolicyId]
|
||||
[agentPolicies, setSelectedPolicyId, selectedPolicy]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAgentPolicy) setSelectedAgentPolicyId(selectedAgentPolicy);
|
||||
}, [selectedAgentPolicy]);
|
||||
const onChangeCallback = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const { value } = event.target;
|
||||
setSelectedPolicyId(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -154,24 +143,24 @@ export const EnrollmentStepAgentPolicy: React.FC<Props> = (props) => {
|
|||
value: agentPolicy.id,
|
||||
text: agentPolicy.name,
|
||||
}))}
|
||||
value={selectedAgentPolicyId}
|
||||
onChange={(e) => setSelectedAgentPolicyId(e.target.value)}
|
||||
value={selectedPolicy?.id}
|
||||
onChange={onChangeCallback}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.fleet.enrollmentStepAgentPolicy.policySelectAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Agent policy',
|
||||
}
|
||||
)}
|
||||
hasNoInitialSelection={!selectedAgentPolicyId}
|
||||
hasNoInitialSelection={!selectedPolicy?.id}
|
||||
data-test-subj="agentPolicyDropdown"
|
||||
isInvalid={!selectedAgentPolicyId}
|
||||
isInvalid={!selectedPolicy?.id}
|
||||
/>
|
||||
</AgentPolicyFormRow>
|
||||
{selectedAgentPolicyId && !isFleetServerPolicy && (
|
||||
{selectedPolicy?.id && !isFleetServerPolicy && (
|
||||
<>
|
||||
<EuiSpacer size="m" />
|
||||
<AgentPolicyPackageBadges
|
||||
agentPolicyId={selectedAgentPolicyId}
|
||||
agentPolicyId={selectedPolicy?.id}
|
||||
excludeFleetServer={excludeFleetServer}
|
||||
/>
|
||||
</>
|
||||
|
@ -183,7 +172,7 @@ export const EnrollmentStepAgentPolicy: React.FC<Props> = (props) => {
|
|||
selectedApiKeyId={props.selectedApiKeyId}
|
||||
onKeyChange={props.onKeyChange}
|
||||
initialAuthenticationSettingsOpen={!props.selectedApiKeyId}
|
||||
agentPolicyId={selectedAgentPolicyId}
|
||||
agentPolicyId={selectedPolicy?.id}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -5,29 +5,99 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiCallOut, EuiButton } from '@elastic/eui';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { EuiCallOut, EuiButton, EuiText, EuiLink } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { useGetAgentStatus } from '../../hooks';
|
||||
import { sendGetAgents, useLink, useStartServices } from '../../hooks';
|
||||
import { AGENTS_PREFIX } from '../../constants';
|
||||
interface Props {
|
||||
policyId: string;
|
||||
onClickViewAgents: () => void;
|
||||
policyId?: string;
|
||||
troubleshootLink: string;
|
||||
onClickViewAgents?: () => void;
|
||||
agentCount: number;
|
||||
}
|
||||
|
||||
const POLLING_INTERVAL_MS = 5 * 1000; // 5 sec
|
||||
|
||||
/**
|
||||
* Hook for finding agents enrolled since component was rendered. Should be
|
||||
* used by parent component to power rendering
|
||||
* @param policyId
|
||||
* @returns agentIds
|
||||
*/
|
||||
export const usePollingAgentCount = (policyId: string) => {
|
||||
const [agentIds, setAgentIds] = useState<string[]>([]);
|
||||
|
||||
const timeout = useRef<number | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
let isAborted = false;
|
||||
|
||||
const poll = () => {
|
||||
timeout.current = window.setTimeout(async () => {
|
||||
const request = await sendGetAgents({
|
||||
kuery: `${AGENTS_PREFIX}.policy_id:"${policyId}" and ${AGENTS_PREFIX}.enrolled_at >= now-10m`,
|
||||
showInactive: false,
|
||||
});
|
||||
|
||||
const newAgentIds = request.data?.items.map((i) => i.id) ?? agentIds;
|
||||
if (newAgentIds.some((id) => !agentIds.includes(id))) {
|
||||
setAgentIds(newAgentIds);
|
||||
}
|
||||
if (!isAborted) {
|
||||
poll();
|
||||
}
|
||||
}, POLLING_INTERVAL_MS);
|
||||
};
|
||||
|
||||
poll();
|
||||
|
||||
if (isAborted || agentIds.length > 0) clearTimeout(timeout.current);
|
||||
|
||||
return () => {
|
||||
isAborted = true;
|
||||
};
|
||||
}, [agentIds, policyId]);
|
||||
return agentIds;
|
||||
};
|
||||
|
||||
export const ConfirmAgentEnrollment: React.FunctionComponent<Props> = ({
|
||||
policyId,
|
||||
troubleshootLink,
|
||||
onClickViewAgents,
|
||||
agentCount,
|
||||
}) => {
|
||||
// Check the agents enrolled in the last 10 minutes
|
||||
const enrolledAt = 'now-10m';
|
||||
const kuery = `${AGENTS_PREFIX}.enrolled_at >= "${enrolledAt}"`;
|
||||
const agentStatusRequest = useGetAgentStatus({ kuery, policyId });
|
||||
const agentsCount = agentStatusRequest.data?.results?.total;
|
||||
const { getHref } = useLink();
|
||||
const { application } = useStartServices();
|
||||
|
||||
if (!agentsCount) {
|
||||
return null;
|
||||
const onButtonClick = () => {
|
||||
if (onClickViewAgents) onClickViewAgents();
|
||||
const href = getHref('agent_list');
|
||||
application.navigateToUrl(href);
|
||||
};
|
||||
|
||||
if (!policyId || agentCount === 0) {
|
||||
return (
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
data-test-subj="ConfirmAgentEnrollmentCallOut.troubleshooting"
|
||||
id="xpack.fleet.enrollmentInstructions.troubleshootingText"
|
||||
defaultMessage="If you are having trouble connecting, see our {link}."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink target="_blank" external href={troubleshootLink}>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.enrollmentInstructions.troubleshootingLink"
|
||||
defaultMessage="troubleshooting guide"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -35,16 +105,16 @@ export const ConfirmAgentEnrollment: React.FunctionComponent<Props> = ({
|
|||
data-test-subj="ConfirmAgentEnrollmentCallOut"
|
||||
title={i18n.translate('xpack.fleet.agentEnrollment.confirmation.title', {
|
||||
defaultMessage:
|
||||
'{agentsCount} {agentsCount, plural, one {agent has} other {agents have}} been enrolled.',
|
||||
'{agentCount} {agentCount, plural, one {agent has} other {agents have}} been enrolled.',
|
||||
values: {
|
||||
agentsCount,
|
||||
agentCount,
|
||||
},
|
||||
})}
|
||||
color="success"
|
||||
iconType="check"
|
||||
>
|
||||
<EuiButton
|
||||
onClick={onClickViewAgents}
|
||||
onClick={onButtonClick}
|
||||
color="success"
|
||||
data-test-subj="ConfirmAgentEnrollmentButton"
|
||||
>
|
||||
|
|
|
@ -9,66 +9,73 @@ import React from 'react';
|
|||
import { EuiCallOut, EuiText, EuiSpacer, EuiButton } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type { InstalledIntegrationPolicy } from '../../hooks';
|
||||
import { useGetAgentIncomingData } from '../../hooks';
|
||||
import type { InstalledIntegrationPolicy } from './use_get_agent_incoming_data';
|
||||
import { useGetAgentIncomingData, usePollingIncomingData } from './use_get_agent_incoming_data';
|
||||
|
||||
interface Props {
|
||||
agentsIds: string[];
|
||||
agentIds: string[];
|
||||
installedPolicy?: InstalledIntegrationPolicy;
|
||||
agentDataConfirmed: boolean;
|
||||
setAgentDataConfirmed: (v: boolean) => void;
|
||||
}
|
||||
|
||||
export const ConfirmIncomingData: React.FunctionComponent<Props> = ({
|
||||
agentsIds,
|
||||
agentIds,
|
||||
installedPolicy,
|
||||
agentDataConfirmed,
|
||||
setAgentDataConfirmed,
|
||||
}) => {
|
||||
const { enrolledAgents, numAgentsWithData, isLoading, linkButton } = useGetAgentIncomingData(
|
||||
agentsIds,
|
||||
const { incomingData, isLoading } = usePollingIncomingData(agentIds);
|
||||
|
||||
const { enrolledAgents, numAgentsWithData, linkButton, message } = useGetAgentIncomingData(
|
||||
incomingData,
|
||||
installedPolicy
|
||||
);
|
||||
|
||||
if (!isLoading && enrolledAgents > 0 && numAgentsWithData > 0) {
|
||||
setAgentDataConfirmed(true);
|
||||
}
|
||||
if (!agentDataConfirmed) {
|
||||
return (
|
||||
<EuiText size="s">
|
||||
{i18n.translate('xpack.fleet.confirmIncomingData.loading', {
|
||||
defaultMessage:
|
||||
'It may take a few minutes for data to arrive in Elasticsearch. If the system is not generating data, it may help to generate some to ensure data is being collected correctly. If you’re having trouble, see our troubleshooting guide. You may close this dialog and check later by viewing our integration assets.',
|
||||
})}
|
||||
</EuiText>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading ? (
|
||||
<EuiText size="s">
|
||||
{i18n.translate('xpack.fleet.confirmIncomingData.loading', {
|
||||
defaultMessage:
|
||||
'It may take a few minutes for data to arrive in Elasticsearch. If the system is not generating data, it may help to generate some to ensure data is being collected correctly. If you’re having trouble, see our troubleshooting guide. You may close this dialog and check later by viewing our integration assets.',
|
||||
})}
|
||||
</EuiText>
|
||||
) : (
|
||||
<>
|
||||
<EuiCallOut
|
||||
data-test-subj="IncomingDataConfirmedCallOut"
|
||||
title={i18n.translate('xpack.fleet.confirmIncomingData.title', {
|
||||
defaultMessage:
|
||||
'Incoming data received from {numAgentsWithData} of {enrolledAgents} recently enrolled { enrolledAgents, plural, one {agent} other {agents}}.',
|
||||
values: {
|
||||
numAgentsWithData,
|
||||
enrolledAgents,
|
||||
},
|
||||
})}
|
||||
color="success"
|
||||
iconType="check"
|
||||
/>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiText size="s">
|
||||
{i18n.translate('xpack.fleet.confirmIncomingData.subtitle', {
|
||||
defaultMessage: 'Your agent is enrolled successfully and your data is received.',
|
||||
})}
|
||||
</EuiText>
|
||||
</>
|
||||
)}
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
<EuiCallOut
|
||||
data-test-subj="IncomingDataConfirmedCallOut"
|
||||
title={i18n.translate('xpack.fleet.confirmIncomingData.title', {
|
||||
defaultMessage:
|
||||
'Incoming data received from {numAgentsWithData} of {enrolledAgents} recently enrolled { enrolledAgents, plural, one {agent} other {agents}}.',
|
||||
values: {
|
||||
numAgentsWithData,
|
||||
enrolledAgents,
|
||||
},
|
||||
})}
|
||||
color="success"
|
||||
iconType="check"
|
||||
/>
|
||||
{installedPolicy && (
|
||||
<EuiButton
|
||||
href={linkButton.href}
|
||||
isDisabled={isLoading}
|
||||
color="primary"
|
||||
fill
|
||||
data-test-subj="IncomingDataConfirmedButton"
|
||||
>
|
||||
{linkButton.text}
|
||||
</EuiButton>
|
||||
<>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiText size="s">{message}</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiButton
|
||||
href={linkButton.href}
|
||||
isDisabled={isLoading}
|
||||
color="primary"
|
||||
fill
|
||||
data-test-subj="IncomingDataConfirmedButton"
|
||||
>
|
||||
{linkButton.text}
|
||||
</EuiButton>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { useLink } from '../../hooks';
|
||||
|
||||
export const DefaultMissingRequirements = () => {
|
||||
const { getHref } = useLink();
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.agentsNotInitializedText"
|
||||
defaultMessage="Before enrolling agents, {link}."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink href={getHref('overview')}>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.setUpAgentsLink"
|
||||
defaultMessage="set up central management for Elastic Agents"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -9,8 +9,11 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import type { PackagePolicy, AgentPolicy } from '../../types';
|
||||
import { sendGetOneAgentPolicy, useStartServices } from '../../hooks';
|
||||
|
||||
import { FLEET_KUBERNETES_PACKAGE } from '../../../common';
|
||||
|
||||
import type { K8sMode } from './types';
|
||||
|
||||
export function useAgentPolicyWithPackagePolicies(policyId?: string) {
|
||||
const [agentPolicyWithPackagePolicies, setAgentPolicy] = useState<AgentPolicy | null>(null);
|
||||
const core = useStartServices();
|
||||
|
@ -41,9 +44,7 @@ export function useAgentPolicyWithPackagePolicies(policyId?: string) {
|
|||
}
|
||||
|
||||
export function useIsK8sPolicy(agentPolicy?: AgentPolicy) {
|
||||
const [isK8s, setIsK8s] = useState<'IS_LOADING' | 'IS_KUBERNETES' | 'IS_NOT_KUBERNETES'>(
|
||||
'IS_LOADING'
|
||||
);
|
||||
const [isK8s, setIsK8s] = useState<K8sMode>('IS_LOADING');
|
||||
useEffect(() => {
|
||||
async function checkifK8s() {
|
||||
if (!agentPolicy) {
|
||||
|
|
|
@ -24,43 +24,42 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
|
||||
import { useGetSettings, useFleetStatus, useAgentEnrollmentFlyoutData } from '../../hooks';
|
||||
import { FLEET_SERVER_PACKAGE } from '../../constants';
|
||||
import type { PackagePolicy } from '../../types';
|
||||
import type { PackagePolicy, AgentPolicy } from '../../types';
|
||||
|
||||
import { Loading } from '..';
|
||||
|
||||
import { ManagedInstructions } from './managed_instructions';
|
||||
import { StandaloneInstructions } from './standalone_instructions';
|
||||
import { Instructions } from './instructions';
|
||||
import { MissingFleetServerHostCallout } from './missing_fleet_server_host_callout';
|
||||
import type { BaseProps } from './types';
|
||||
import type { FlyOutProps, SelectionType, FlyoutMode } from './types';
|
||||
|
||||
import { useIsK8sPolicy, useAgentPolicyWithPackagePolicies } from './hooks';
|
||||
|
||||
type FlyoutMode = 'managed' | 'standalone';
|
||||
|
||||
export interface Props extends BaseProps {
|
||||
onClose: () => void;
|
||||
defaultMode?: FlyoutMode;
|
||||
}
|
||||
|
||||
export * from './agent_policy_selection';
|
||||
export * from './agent_policy_select_create';
|
||||
export * from './managed_instructions';
|
||||
export * from './standalone_instructions';
|
||||
export * from './instructions';
|
||||
export * from './steps';
|
||||
|
||||
export const AgentEnrollmentFlyout: React.FunctionComponent<Props> = ({
|
||||
export const AgentEnrollmentFlyout: React.FunctionComponent<FlyOutProps> = ({
|
||||
onClose,
|
||||
agentPolicy,
|
||||
viewDataStep,
|
||||
defaultMode = 'managed',
|
||||
isIntegrationFlow,
|
||||
installedPackagePolicy,
|
||||
}) => {
|
||||
const [mode, setMode] = useState<FlyoutMode>(defaultMode);
|
||||
const findPolicyById = (policies: AgentPolicy[], id: string | undefined) => {
|
||||
if (!id) return undefined;
|
||||
return policies.find((p) => p.id === id);
|
||||
};
|
||||
|
||||
const settings = useGetSettings();
|
||||
const fleetStatus = useFleetStatus();
|
||||
const fleetServerHosts = settings.data?.item?.fleet_server_hosts || [];
|
||||
|
||||
const fleetStatus = useFleetStatus();
|
||||
const [policyId, setSelectedPolicyId] = useState(agentPolicy?.id);
|
||||
const [selectedPolicyId, setSelectedPolicyId] = useState(agentPolicy?.id);
|
||||
const [isFleetServerPolicySelected, setIsFleetServerPolicySelected] = useState<boolean>(false);
|
||||
const [selectedApiKeyId, setSelectedAPIKeyId] = useState<string | undefined>();
|
||||
const [mode, setMode] = useState<FlyoutMode>(defaultMode);
|
||||
const [selectionType, setSelectionType] = useState<SelectionType>('tabs');
|
||||
|
||||
const {
|
||||
agentPolicies,
|
||||
|
@ -68,11 +67,19 @@ export const AgentEnrollmentFlyout: React.FunctionComponent<Props> = ({
|
|||
isLoadingAgentPolicies,
|
||||
refreshAgentPolicies,
|
||||
} = useAgentEnrollmentFlyoutData();
|
||||
const { agentPolicyWithPackagePolicies } = useAgentPolicyWithPackagePolicies(policyId);
|
||||
|
||||
const { agentPolicyWithPackagePolicies } = useAgentPolicyWithPackagePolicies(selectedPolicyId);
|
||||
|
||||
const selectedPolicy = agentPolicyWithPackagePolicies
|
||||
? agentPolicyWithPackagePolicies
|
||||
: findPolicyById(agentPolicies, selectedPolicyId);
|
||||
|
||||
const hasNoFleetServerHost = fleetStatus.isReady && fleetServerHosts.length === 0;
|
||||
|
||||
useEffect(() => {
|
||||
if (agentPolicyWithPackagePolicies && setIsFleetServerPolicySelected) {
|
||||
if (selectedPolicy) {
|
||||
if (
|
||||
(agentPolicyWithPackagePolicies.package_policies as PackagePolicy[]).some(
|
||||
(selectedPolicy.package_policies as PackagePolicy[]).some(
|
||||
(packagePolicy) => packagePolicy.package?.name === FLEET_SERVER_PACKAGE
|
||||
)
|
||||
) {
|
||||
|
@ -81,11 +88,9 @@ export const AgentEnrollmentFlyout: React.FunctionComponent<Props> = ({
|
|||
setIsFleetServerPolicySelected(false);
|
||||
}
|
||||
}
|
||||
}, [agentPolicyWithPackagePolicies]);
|
||||
}, [selectedPolicy, isFleetServerPolicySelected]);
|
||||
|
||||
const { isK8s } = useIsK8sPolicy(
|
||||
agentPolicyWithPackagePolicies ? agentPolicyWithPackagePolicies : undefined
|
||||
);
|
||||
const { isK8s } = useIsK8sPolicy(selectedPolicy ? selectedPolicy : undefined);
|
||||
|
||||
const isLoadingInitialRequest = settings.isLoading && settings.isInitialRequest;
|
||||
|
||||
|
@ -107,61 +112,63 @@ export const AgentEnrollmentFlyout: React.FunctionComponent<Props> = ({
|
|||
defaultMessage="Add Elastic Agents to your hosts to collect data and send it to the Elastic Stack."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiTabs style={{ marginBottom: '-25px' }}>
|
||||
<EuiTab
|
||||
data-test-subj="managedTab"
|
||||
isSelected={mode === 'managed'}
|
||||
onClick={() => setMode('managed')}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.enrollFleetTabLabel"
|
||||
defaultMessage="Enroll in Fleet"
|
||||
/>
|
||||
</EuiTab>
|
||||
<EuiTab
|
||||
data-test-subj="standaloneTab"
|
||||
isSelected={mode === 'standalone'}
|
||||
onClick={() => setMode('standalone')}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.enrollStandaloneTabLabel"
|
||||
defaultMessage="Run standalone"
|
||||
/>
|
||||
</EuiTab>
|
||||
</EuiTabs>
|
||||
{selectionType === 'tabs' ? (
|
||||
<>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiTabs style={{ marginBottom: '-25px' }}>
|
||||
<EuiTab
|
||||
data-test-subj="managedTab"
|
||||
isSelected={mode === 'managed'}
|
||||
onClick={() => setMode('managed')}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.enrollFleetTabLabel"
|
||||
defaultMessage="Enroll in Fleet"
|
||||
/>
|
||||
</EuiTab>
|
||||
<EuiTab
|
||||
data-test-subj="standaloneTab"
|
||||
isSelected={mode === 'standalone'}
|
||||
onClick={() => setMode('standalone')}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.enrollStandaloneTabLabel"
|
||||
defaultMessage="Run standalone"
|
||||
/>
|
||||
</EuiTab>
|
||||
</EuiTabs>
|
||||
</>
|
||||
) : null}
|
||||
</EuiFlyoutHeader>
|
||||
|
||||
<EuiFlyoutBody
|
||||
banner={
|
||||
fleetStatus.isReady &&
|
||||
!isLoadingInitialRequest &&
|
||||
fleetServerHosts.length === 0 &&
|
||||
mode === 'managed' ? (
|
||||
hasNoFleetServerHost && !isLoadingInitialRequest && mode === 'managed' ? (
|
||||
<MissingFleetServerHostCallout />
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
{isLoadingInitialAgentPolicies ? (
|
||||
<Loading />
|
||||
) : mode === 'managed' ? (
|
||||
<ManagedInstructions
|
||||
<Loading size="l" />
|
||||
) : (
|
||||
<Instructions
|
||||
settings={settings.data?.item}
|
||||
setSelectedPolicyId={setSelectedPolicyId}
|
||||
policyId={policyId}
|
||||
agentPolicy={agentPolicy}
|
||||
selectedPolicy={selectedPolicy}
|
||||
agentPolicies={agentPolicies}
|
||||
viewDataStep={viewDataStep}
|
||||
isFleetServerPolicySelected={isFleetServerPolicySelected}
|
||||
isK8s={isK8s}
|
||||
refreshAgentPolicies={refreshAgentPolicies}
|
||||
isLoadingAgentPolicies={isLoadingAgentPolicies}
|
||||
/>
|
||||
) : (
|
||||
<StandaloneInstructions
|
||||
agentPolicy={agentPolicy}
|
||||
agentPolicies={agentPolicies}
|
||||
refreshAgentPolicies={refreshAgentPolicies}
|
||||
mode={mode}
|
||||
setMode={setMode}
|
||||
selectionType={selectionType}
|
||||
setSelectionType={setSelectionType}
|
||||
isIntegrationFlow={isIntegrationFlow}
|
||||
selectedApiKeyId={selectedApiKeyId}
|
||||
setSelectedAPIKeyId={setSelectedAPIKeyId}
|
||||
onClickViewAgents={onClose}
|
||||
installedPackagePolicy={installedPackagePolicy}
|
||||
/>
|
||||
)}
|
||||
</EuiFlyoutBody>
|
||||
|
|
|
@ -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 React, { useMemo } from 'react';
|
||||
import { EuiText, EuiLink } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import semverMajor from 'semver/functions/major';
|
||||
import semverMinor from 'semver/functions/minor';
|
||||
import semverPatch from 'semver/functions/patch';
|
||||
|
||||
import { useKibanaVersion } from '../../hooks';
|
||||
|
||||
export const InstallationMessage: React.FunctionComponent = () => {
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
const kibanaVersionURLString = useMemo(
|
||||
() =>
|
||||
`${semverMajor(kibanaVersion)}-${semverMinor(kibanaVersion)}-${semverPatch(kibanaVersion)}`,
|
||||
[kibanaVersion]
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.enrollmentInstructions.installationMessage"
|
||||
defaultMessage="Select the appropriate platform and run commands to install, enroll, and start Elastic Agent. Reuse commands to set up agents on more than one host. For aarch64, see our {downloadLink}. For additional guidance, see our {installationLink}."
|
||||
values={{
|
||||
downloadLink: (
|
||||
<EuiLink
|
||||
target="_blank"
|
||||
external
|
||||
href={`https://www.elastic.co/downloads/past-releases/elastic-agent-${kibanaVersionURLString}`}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.enrollmentInstructions.downloadLink"
|
||||
defaultMessage="downloads page"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
installationLink: (
|
||||
<EuiLink
|
||||
target="_blank"
|
||||
external
|
||||
href="https://www.elastic.co/guide/en/fleet/current/elastic-agent-installation.html"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.enrollmentInstructions.installationMessage.link"
|
||||
defaultMessage="installation docs"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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, { useMemo } from 'react';
|
||||
import { EuiText, EuiSpacer } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { useFleetStatus, useGetAgents } from '../../hooks';
|
||||
|
||||
import { FleetServerRequirementPage } from '../../applications/fleet/sections/agents/agent_requirements_page';
|
||||
|
||||
import { policyHasFleetServer } from '../../applications/fleet/sections/agents/services/has_fleet_server';
|
||||
|
||||
import { FLEET_SERVER_PACKAGE } from '../../constants';
|
||||
|
||||
import { useFleetServerUnhealthy } from '../../applications/fleet/sections/agents/hooks/use_fleet_server_unhealthy';
|
||||
|
||||
import { Loading } from '..';
|
||||
|
||||
import type { InstructionProps } from './types';
|
||||
|
||||
import { ManagedSteps, StandaloneSteps, FleetServerSteps } from './steps';
|
||||
import { DefaultMissingRequirements } from './default_missing_requirements';
|
||||
|
||||
export const Instructions = (props: InstructionProps) => {
|
||||
const {
|
||||
agentPolicies,
|
||||
isFleetServerPolicySelected,
|
||||
settings,
|
||||
isLoadingAgentPolicies,
|
||||
setSelectionType,
|
||||
mode,
|
||||
isIntegrationFlow,
|
||||
} = props;
|
||||
const fleetStatus = useFleetStatus();
|
||||
const { isUnhealthy: isFleetServerUnhealthy } = useFleetServerUnhealthy();
|
||||
|
||||
const { data: agents, isLoading: isLoadingAgents } = useGetAgents({
|
||||
page: 1,
|
||||
perPage: 1000,
|
||||
showInactive: false,
|
||||
});
|
||||
|
||||
const fleetServers = useMemo(() => {
|
||||
const fleetServerAgentPolicies: string[] = agentPolicies
|
||||
.filter((pol) => policyHasFleetServer(pol))
|
||||
.map((pol) => pol.id);
|
||||
return (agents?.items ?? []).filter((agent) =>
|
||||
fleetServerAgentPolicies.includes(agent.policy_id ?? '')
|
||||
);
|
||||
}, [agents, agentPolicies]);
|
||||
|
||||
const fleetServerHosts = useMemo(() => {
|
||||
return settings?.fleet_server_hosts || [];
|
||||
}, [settings]);
|
||||
|
||||
const hasNoFleetServerHost = fleetStatus.isReady && fleetServerHosts.length === 0;
|
||||
|
||||
const showAgentEnrollment =
|
||||
fleetStatus.isReady &&
|
||||
!isFleetServerUnhealthy &&
|
||||
fleetServers.length > 0 &&
|
||||
fleetServerHosts.length > 0;
|
||||
|
||||
const showFleetServerEnrollment =
|
||||
fleetServers.length === 0 ||
|
||||
isFleetServerUnhealthy ||
|
||||
(fleetStatus.missingRequirements ?? []).some((r) => r === FLEET_SERVER_PACKAGE);
|
||||
|
||||
if (!isIntegrationFlow && showAgentEnrollment) {
|
||||
setSelectionType('radio');
|
||||
} else {
|
||||
setSelectionType('tabs');
|
||||
}
|
||||
|
||||
if (isLoadingAgents || isLoadingAgentPolicies) return <Loading size="l" />;
|
||||
|
||||
if (hasNoFleetServerHost) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mode === 'managed') {
|
||||
if (showFleetServerEnrollment) {
|
||||
return <FleetServerRequirementPage />;
|
||||
} else if (showAgentEnrollment) {
|
||||
return (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.managedDescription"
|
||||
defaultMessage="Enroll an Elastic Agent in Fleet to automatically deploy updates and centrally manage the agent."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
{isFleetServerPolicySelected ? (
|
||||
<FleetServerSteps {...props} />
|
||||
) : (
|
||||
<ManagedSteps {...props} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <DefaultMissingRequirements />;
|
||||
} else {
|
||||
return <StandaloneInstructions {...props} />;
|
||||
}
|
||||
};
|
||||
|
||||
const StandaloneInstructions = (props: InstructionProps) => {
|
||||
return (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.standaloneDescription"
|
||||
defaultMessage="Run an Elastic Agent standalone to configure and update the agent manually on the host where the agent is installed."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
<StandaloneSteps {...props} />
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,206 +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 React, { useState, useMemo } from 'react';
|
||||
import { EuiSteps, EuiLink, EuiText, EuiSpacer } from '@elastic/eui';
|
||||
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { useGetOneEnrollmentAPIKey, useLink, useFleetStatus, useGetAgents } from '../../hooks';
|
||||
|
||||
import { ManualInstructions } from '../../components/enrollment_instructions';
|
||||
import {
|
||||
deploymentModeStep,
|
||||
ServiceTokenStep,
|
||||
FleetServerCommandStep,
|
||||
useFleetServerInstructions,
|
||||
addFleetServerHostStep,
|
||||
} from '../../applications/fleet/sections/agents/agent_requirements_page/components';
|
||||
import { FleetServerRequirementPage } from '../../applications/fleet/sections/agents/agent_requirements_page';
|
||||
|
||||
import { policyHasFleetServer } from '../../applications/fleet/sections/agents/services/has_fleet_server';
|
||||
|
||||
import { FLEET_SERVER_PACKAGE } from '../../constants';
|
||||
|
||||
import { DownloadStep, AgentPolicySelectionStep, AgentEnrollmentKeySelectionStep } from './steps';
|
||||
import type { InstructionProps } from './types';
|
||||
|
||||
const DefaultMissingRequirements = () => {
|
||||
const { getHref } = useLink();
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.agentsNotInitializedText"
|
||||
defaultMessage="Before enrolling agents, {link}."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink href={getHref('overview')}>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.setUpAgentsLink"
|
||||
defaultMessage="set up central management for Elastic Agents"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const FleetServerMissingRequirements = () => {
|
||||
return <FleetServerRequirementPage />;
|
||||
};
|
||||
|
||||
export const ManagedInstructions = React.memo<InstructionProps>(
|
||||
({
|
||||
agentPolicy,
|
||||
agentPolicies,
|
||||
viewDataStep,
|
||||
setSelectedPolicyId,
|
||||
policyId,
|
||||
isFleetServerPolicySelected,
|
||||
isK8s,
|
||||
settings,
|
||||
refreshAgentPolicies,
|
||||
isLoadingAgentPolicies,
|
||||
}) => {
|
||||
const fleetStatus = useFleetStatus();
|
||||
const [selectedApiKeyId, setSelectedAPIKeyId] = useState<string | undefined>();
|
||||
const apiKey = useGetOneEnrollmentAPIKey(selectedApiKeyId);
|
||||
const fleetServerInstructions = useFleetServerInstructions(apiKey?.data?.item?.policy_id);
|
||||
|
||||
const { data: agents, isLoading: isLoadingAgents } = useGetAgents({
|
||||
page: 1,
|
||||
perPage: 1000,
|
||||
showInactive: false,
|
||||
});
|
||||
|
||||
const fleetServers = useMemo(() => {
|
||||
const fleetServerAgentPolicies: string[] = agentPolicies
|
||||
.filter((pol) => policyHasFleetServer(pol))
|
||||
.map((pol) => pol.id);
|
||||
return (agents?.items ?? []).filter((agent) =>
|
||||
fleetServerAgentPolicies.includes(agent.policy_id ?? '')
|
||||
);
|
||||
}, [agents, agentPolicies]);
|
||||
|
||||
const fleetServerSteps = useMemo(() => {
|
||||
const {
|
||||
serviceToken,
|
||||
getServiceToken,
|
||||
isLoadingServiceToken,
|
||||
installCommand,
|
||||
platform,
|
||||
setPlatform,
|
||||
deploymentMode,
|
||||
setDeploymentMode,
|
||||
addFleetServerHost,
|
||||
} = fleetServerInstructions;
|
||||
|
||||
return [
|
||||
deploymentModeStep({ deploymentMode, setDeploymentMode }),
|
||||
addFleetServerHostStep({ addFleetServerHost }),
|
||||
ServiceTokenStep({ serviceToken, getServiceToken, isLoadingServiceToken }),
|
||||
FleetServerCommandStep({ serviceToken, installCommand, platform, setPlatform }),
|
||||
];
|
||||
}, [fleetServerInstructions]);
|
||||
|
||||
const enrolToken = apiKey.data ? apiKey.data.item.api_key : '';
|
||||
|
||||
const steps = useMemo(() => {
|
||||
const fleetServerHosts = settings?.fleet_server_hosts || [];
|
||||
const baseSteps: EuiContainedStepProps[] = [
|
||||
!agentPolicy
|
||||
? AgentPolicySelectionStep({
|
||||
agentPolicies,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
setSelectedPolicyId,
|
||||
refreshAgentPolicies,
|
||||
})
|
||||
: AgentEnrollmentKeySelectionStep({ agentPolicy, selectedApiKeyId, setSelectedAPIKeyId }),
|
||||
DownloadStep(isFleetServerPolicySelected || false, isK8s || '', enrolToken || ''),
|
||||
];
|
||||
if (isFleetServerPolicySelected) {
|
||||
baseSteps.push(...fleetServerSteps);
|
||||
} else {
|
||||
baseSteps.push({
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle', {
|
||||
defaultMessage: 'Enroll and start the Elastic Agent',
|
||||
}),
|
||||
children: selectedApiKeyId && apiKey.data && (
|
||||
<ManualInstructions
|
||||
apiKey={apiKey.data.item}
|
||||
fleetServerHosts={fleetServerHosts}
|
||||
policyId={policyId}
|
||||
isK8s={isK8s}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (viewDataStep) {
|
||||
baseSteps.push({ 'data-test-subj': 'view-data-step', ...viewDataStep });
|
||||
}
|
||||
|
||||
return baseSteps;
|
||||
}, [
|
||||
agentPolicy,
|
||||
selectedApiKeyId,
|
||||
setSelectedPolicyId,
|
||||
setSelectedAPIKeyId,
|
||||
agentPolicies,
|
||||
refreshAgentPolicies,
|
||||
apiKey.data,
|
||||
fleetServerSteps,
|
||||
isFleetServerPolicySelected,
|
||||
settings?.fleet_server_hosts,
|
||||
viewDataStep,
|
||||
enrolToken,
|
||||
isK8s,
|
||||
policyId,
|
||||
]);
|
||||
|
||||
if (fleetStatus.isReady && settings?.fleet_server_hosts.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
fleetStatus.isReady &&
|
||||
(isLoadingAgents || isLoadingAgentPolicies || fleetServers.length > 0)
|
||||
) {
|
||||
return (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.managedDescription"
|
||||
defaultMessage="Enroll an Elastic Agent in Fleet to automatically deploy updates and centrally manage the agent."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiSteps steps={steps} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const showFleetMissingRequirements =
|
||||
fleetServers.length === 0 ||
|
||||
(fleetStatus.missingRequirements ?? []).some((r) => r === FLEET_SERVER_PACKAGE);
|
||||
|
||||
return (
|
||||
<>
|
||||
{showFleetMissingRequirements ? (
|
||||
<FleetServerMissingRequirements />
|
||||
) : (
|
||||
<DefaultMissingRequirements />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
|
@ -1,281 +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 React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
EuiSteps,
|
||||
EuiText,
|
||||
EuiSpacer,
|
||||
EuiButton,
|
||||
EuiCode,
|
||||
EuiFlexItem,
|
||||
EuiFlexGroup,
|
||||
EuiCodeBlock,
|
||||
EuiCopy,
|
||||
EuiLink,
|
||||
} from '@elastic/eui';
|
||||
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { safeDump } from 'js-yaml';
|
||||
|
||||
import {
|
||||
useStartServices,
|
||||
useLink,
|
||||
sendGetOneAgentPolicyFull,
|
||||
useKibanaVersion,
|
||||
} from '../../hooks';
|
||||
import { fullAgentPolicyToYaml, agentPolicyRouteService } from '../../services';
|
||||
|
||||
import { PlatformSelector } from '../enrollment_instructions/manual/platform_selector';
|
||||
|
||||
import { DownloadStep, AgentPolicySelectionStep } from './steps';
|
||||
import type { InstructionProps } from './types';
|
||||
import { useIsK8sPolicy, useAgentPolicyWithPackagePolicies } from './hooks';
|
||||
|
||||
export const StandaloneInstructions = React.memo<InstructionProps>(
|
||||
({ agentPolicy, agentPolicies, refreshAgentPolicies }) => {
|
||||
const { getHref } = useLink();
|
||||
const core = useStartServices();
|
||||
const { notifications } = core;
|
||||
|
||||
const [selectedPolicyId, setSelectedPolicyId] = useState<string | undefined>(agentPolicy?.id);
|
||||
const [fullAgentPolicy, setFullAgentPolicy] = useState<any | undefined>();
|
||||
const [yaml, setYaml] = useState<string | string>('');
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
|
||||
const { agentPolicyWithPackagePolicies } = useAgentPolicyWithPackagePolicies(selectedPolicyId);
|
||||
const { isK8s } = useIsK8sPolicy(
|
||||
agentPolicyWithPackagePolicies ? agentPolicyWithPackagePolicies : undefined
|
||||
);
|
||||
|
||||
const KUBERNETES_RUN_INSTRUCTIONS = 'kubectl apply -f elastic-agent-standalone-kubernetes.yaml';
|
||||
|
||||
const STANDALONE_RUN_INSTRUCTIONS_LINUX = `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
|
||||
sudo ./elastic-agent install`;
|
||||
|
||||
const STANDALONE_RUN_INSTRUCTIONS_MAC = `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
|
||||
sudo ./elastic-agent install`;
|
||||
|
||||
const STANDALONE_RUN_INSTRUCTIONS_WINDOWS = `wget 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
|
||||
.\\elastic-agent.exe install`;
|
||||
|
||||
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`;
|
||||
|
||||
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 linuxCommand =
|
||||
isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_LINUX;
|
||||
const macCommand =
|
||||
isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_MAC;
|
||||
const windowsCommand =
|
||||
isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_WINDOWS;
|
||||
|
||||
const { docLinks } = useStartServices();
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchFullPolicy() {
|
||||
try {
|
||||
if (!selectedPolicyId) {
|
||||
return;
|
||||
}
|
||||
let query = { standalone: true, kubernetes: false };
|
||||
if (isK8s === 'IS_KUBERNETES') {
|
||||
query = { standalone: true, kubernetes: true };
|
||||
}
|
||||
const res = await sendGetOneAgentPolicyFull(selectedPolicyId, query);
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
|
||||
if (!res.data) {
|
||||
throw new Error('No data while fetching full agent policy');
|
||||
}
|
||||
setFullAgentPolicy(res.data.item);
|
||||
} catch (error) {
|
||||
notifications.toasts.addError(error, {
|
||||
title: 'Error',
|
||||
});
|
||||
}
|
||||
}
|
||||
if (isK8s !== 'IS_LOADING') {
|
||||
fetchFullPolicy();
|
||||
}
|
||||
}, [selectedPolicyId, notifications.toasts, isK8s, core.http.basePath]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isK8s === 'IS_KUBERNETES') {
|
||||
if (typeof fullAgentPolicy === 'object') {
|
||||
return;
|
||||
}
|
||||
setYaml(fullAgentPolicy);
|
||||
} else {
|
||||
if (typeof fullAgentPolicy === 'string') {
|
||||
return;
|
||||
}
|
||||
setYaml(fullAgentPolicyToYaml(fullAgentPolicy, safeDump));
|
||||
}
|
||||
}, [fullAgentPolicy, isK8s]);
|
||||
|
||||
const policyMsg =
|
||||
isK8s === 'IS_KUBERNETES' ? (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.stepConfigureAgentDescriptionk8s"
|
||||
defaultMessage="Copy or download the Kubernetes manifest inside the Kubernetes cluster. Modify {ESUsernameVariable} and {ESPasswordVariable} in the Daemonset environment variables and apply the manifest."
|
||||
values={{
|
||||
ESUsernameVariable: <EuiCode>ES_USERNAME</EuiCode>,
|
||||
ESPasswordVariable: <EuiCode>ES_PASSWORD</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.stepConfigureAgentDescription"
|
||||
defaultMessage="Copy this policy to the {fileName} on the host where the Elastic Agent is installed. Modify {ESUsernameVariable} and {ESPasswordVariable} in the {outputSection} section of {fileName} to use your Elasticsearch credentials."
|
||||
values={{
|
||||
fileName: <EuiCode>elastic-agent.yml</EuiCode>,
|
||||
ESUsernameVariable: <EuiCode>ES_USERNAME</EuiCode>,
|
||||
ESPasswordVariable: <EuiCode>ES_PASSWORD</EuiCode>,
|
||||
outputSection: <EuiCode>outputs</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
let downloadLink = '';
|
||||
if (selectedPolicyId) {
|
||||
downloadLink =
|
||||
isK8s === 'IS_KUBERNETES'
|
||||
? core.http.basePath.prepend(
|
||||
`${agentPolicyRouteService.getInfoFullDownloadPath(
|
||||
selectedPolicyId
|
||||
)}?kubernetes=true&standalone=true`
|
||||
)
|
||||
: core.http.basePath.prepend(
|
||||
`${agentPolicyRouteService.getInfoFullDownloadPath(selectedPolicyId)}?standalone=true`
|
||||
);
|
||||
}
|
||||
|
||||
const downloadMsg =
|
||||
isK8s === 'IS_KUBERNETES' ? (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.downloadPolicyButtonk8s"
|
||||
defaultMessage="Download Manifest"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.downloadPolicyButton"
|
||||
defaultMessage="Download Policy"
|
||||
/>
|
||||
);
|
||||
|
||||
const steps = [
|
||||
!agentPolicy
|
||||
? AgentPolicySelectionStep({
|
||||
agentPolicies,
|
||||
setSelectedPolicyId,
|
||||
excludeFleetServer: true,
|
||||
refreshAgentPolicies,
|
||||
})
|
||||
: undefined,
|
||||
DownloadStep(false),
|
||||
{
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepConfigureAgentTitle', {
|
||||
defaultMessage: 'Configure the agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiText>
|
||||
<>{policyMsg}</>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup gutterSize="m">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiCopy textToCopy={yaml}>
|
||||
{(copy) => (
|
||||
<EuiButton onClick={copy} iconType="copyClipboard">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.copyPolicyButton"
|
||||
defaultMessage="Copy to clipboard"
|
||||
/>
|
||||
</EuiButton>
|
||||
)}
|
||||
</EuiCopy>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton iconType="download" href={downloadLink} isDisabled={!downloadLink}>
|
||||
<>{downloadMsg}</>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiCodeBlock language="yaml" style={{ maxHeight: 300 }} fontSize="m">
|
||||
{yaml}
|
||||
</EuiCodeBlock>
|
||||
</EuiText>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepRunAgentTitle', {
|
||||
defaultMessage: 'Start the agent',
|
||||
}),
|
||||
children: (
|
||||
<PlatformSelector
|
||||
linuxCommand={linuxCommand}
|
||||
macCommand={macCommand}
|
||||
windowsCommand={windowsCommand}
|
||||
linuxDebCommand={linuxDebCommand}
|
||||
linuxRpmCommand={linuxRpmCommand}
|
||||
troubleshootLink={docLinks.links.fleet.troubleshooting}
|
||||
isK8s={isK8s === 'IS_KUBERNETES'}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepCheckForDataTitle', {
|
||||
defaultMessage: 'Check for data',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.stepCheckForDataDescription"
|
||||
defaultMessage="The agent should begin sending data. Go to {link} to view your data."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink href={getHref('data_streams')}>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.goToDataStreamsLink"
|
||||
defaultMessage="data streams"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
</>
|
||||
),
|
||||
},
|
||||
].filter(Boolean) as EuiContainedStepProps[];
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.standaloneDescription"
|
||||
defaultMessage="Run an Elastic Agent standalone to configure and update the agent manually on the host where the agent is installed."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiSteps steps={steps} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
|
||||
|
||||
import { ConfirmAgentEnrollment } from '../confirm_agent_enrollment';
|
||||
|
||||
export const AgentEnrollmentConfirmationStep = ({
|
||||
selectedPolicyId,
|
||||
troubleshootLink,
|
||||
onClickViewAgents,
|
||||
agentCount,
|
||||
}: {
|
||||
selectedPolicyId?: string;
|
||||
troubleshootLink: string;
|
||||
onClickViewAgents: () => void;
|
||||
agentCount: number;
|
||||
}): EuiContainedStepProps => {
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepAgentEnrollmentConfirmation', {
|
||||
defaultMessage: 'Confirm agent Enrollment',
|
||||
}),
|
||||
children: (
|
||||
<ConfirmAgentEnrollment
|
||||
policyId={selectedPolicyId}
|
||||
troubleshootLink={troubleshootLink}
|
||||
onClickViewAgents={onClickViewAgents}
|
||||
agentCount={agentCount}
|
||||
/>
|
||||
),
|
||||
status: !agentCount ? 'incomplete' : 'complete',
|
||||
};
|
||||
};
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { EuiText, EuiSpacer } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
|
||||
|
||||
import type { AgentPolicy } from '../../../types';
|
||||
|
||||
import { AdvancedAgentAuthenticationSettings } from '../advanced_agent_authentication_settings';
|
||||
|
||||
export const AgentEnrollmentKeySelectionStep = ({
|
||||
selectedPolicy,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
}: {
|
||||
selectedPolicy?: AgentPolicy;
|
||||
selectedApiKeyId?: string;
|
||||
setSelectedAPIKeyId: (key?: string) => void;
|
||||
}): EuiContainedStepProps => {
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepConfigurePolicyAuthenticationTitle', {
|
||||
defaultMessage: 'Select enrollment token',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.agentAuthenticationSettings"
|
||||
defaultMessage="{agentPolicyName} has been selected. Select which enrollment token to use when enrolling agents."
|
||||
values={{
|
||||
agentPolicyName: <strong>{selectedPolicy?.name}</strong>,
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
<AdvancedAgentAuthenticationSettings
|
||||
agentPolicyId={selectedPolicy?.id}
|
||||
selectedApiKeyId={selectedApiKeyId}
|
||||
initialAuthenticationSettingsOpen
|
||||
onKeyChange={setSelectedAPIKeyId}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
};
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
|
||||
|
||||
import type { AgentPolicy } from '../../../types';
|
||||
|
||||
import { SelectCreateAgentPolicy } from '../agent_policy_select_create';
|
||||
|
||||
export const AgentPolicySelectionStep = ({
|
||||
agentPolicies,
|
||||
selectedPolicy,
|
||||
setSelectedPolicyId,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
excludeFleetServer,
|
||||
refreshAgentPolicies,
|
||||
}: {
|
||||
agentPolicies: AgentPolicy[];
|
||||
selectedPolicy?: AgentPolicy;
|
||||
setSelectedPolicyId: (agentPolicyId?: string) => void;
|
||||
selectedApiKeyId?: string;
|
||||
setSelectedAPIKeyId?: (key?: string) => void;
|
||||
excludeFleetServer?: boolean;
|
||||
refreshAgentPolicies: () => void;
|
||||
}): EuiContainedStepProps => {
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepChooseAgentPolicyTitle', {
|
||||
defaultMessage: 'What type of host are you adding?',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<SelectCreateAgentPolicy
|
||||
agentPolicies={agentPolicies}
|
||||
selectedPolicy={selectedPolicy}
|
||||
setSelectedPolicyId={setSelectedPolicyId}
|
||||
withKeySelection={setSelectedAPIKeyId ? true : false}
|
||||
selectedApiKeyId={selectedApiKeyId}
|
||||
onKeyChange={setSelectedAPIKeyId}
|
||||
refreshAgentPolicies={refreshAgentPolicies}
|
||||
excludeFleetServer={excludeFleetServer}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
};
|
||||
};
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* 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, useEffect } from 'react';
|
||||
|
||||
import { EuiSteps } from '@elastic/eui';
|
||||
import { safeDump } from 'js-yaml';
|
||||
|
||||
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
|
||||
|
||||
import type { FullAgentPolicy } from '../../../../common/types/models/agent_policy';
|
||||
|
||||
import { fullAgentPolicyToYaml, agentPolicyRouteService } from '../../../services';
|
||||
|
||||
import { StandaloneInstructions } from '../../enrollment_instructions/standalone';
|
||||
|
||||
import {
|
||||
useGetOneEnrollmentAPIKey,
|
||||
useStartServices,
|
||||
useKibanaVersion,
|
||||
sendGetOneAgentPolicyFull,
|
||||
} from '../../../hooks';
|
||||
|
||||
import {
|
||||
deploymentModeStep,
|
||||
ServiceTokenStep,
|
||||
FleetServerCommandStep,
|
||||
useFleetServerInstructions,
|
||||
addFleetServerHostStep,
|
||||
} from '../../../applications/fleet/sections/agents/agent_requirements_page/components';
|
||||
|
||||
import type { InstructionProps } from '../types';
|
||||
import { usePollingAgentCount } from '../confirm_agent_enrollment';
|
||||
|
||||
import {
|
||||
InstallationModeSelectionStep,
|
||||
AgentEnrollmentKeySelectionStep,
|
||||
AgentPolicySelectionStep,
|
||||
InstallStandaloneAgentStep,
|
||||
ConfigureStandaloneAgentStep,
|
||||
AgentEnrollmentConfirmationStep,
|
||||
InstallManagedAgentStep,
|
||||
IncomingDataConfirmationStep,
|
||||
} from '.';
|
||||
|
||||
export const StandaloneSteps: React.FunctionComponent<InstructionProps> = ({
|
||||
agentPolicy,
|
||||
agentPolicies,
|
||||
selectedPolicy,
|
||||
setSelectedPolicyId,
|
||||
refreshAgentPolicies,
|
||||
mode,
|
||||
setMode,
|
||||
selectionType,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
isK8s,
|
||||
}) => {
|
||||
const core = useStartServices();
|
||||
const { notifications } = core;
|
||||
const [fullAgentPolicy, setFullAgentPolicy] = useState<FullAgentPolicy | undefined>();
|
||||
const [yaml, setYaml] = useState<any | undefined>('');
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
|
||||
let downloadLink = '';
|
||||
|
||||
if (selectedPolicy?.id) {
|
||||
downloadLink =
|
||||
isK8s === 'IS_KUBERNETES'
|
||||
? core.http.basePath.prepend(
|
||||
`${agentPolicyRouteService.getInfoFullDownloadPath(
|
||||
selectedPolicy?.id
|
||||
)}?kubernetes=true&standalone=true`
|
||||
)
|
||||
: core.http.basePath.prepend(
|
||||
`${agentPolicyRouteService.getInfoFullDownloadPath(selectedPolicy?.id)}?standalone=true`
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchFullPolicy() {
|
||||
try {
|
||||
if (!selectedPolicy?.id) {
|
||||
return;
|
||||
}
|
||||
let query = { standalone: true, kubernetes: false };
|
||||
if (isK8s === 'IS_KUBERNETES') {
|
||||
query = { standalone: true, kubernetes: true };
|
||||
}
|
||||
const res = await sendGetOneAgentPolicyFull(selectedPolicy?.id, query);
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
|
||||
if (!res.data) {
|
||||
throw new Error('No data while fetching full agent policy');
|
||||
}
|
||||
setFullAgentPolicy(res.data.item);
|
||||
} catch (error) {
|
||||
notifications.toasts.addError(error, {
|
||||
title: 'Error',
|
||||
});
|
||||
}
|
||||
}
|
||||
if (isK8s !== 'IS_LOADING') {
|
||||
fetchFullPolicy();
|
||||
}
|
||||
}, [selectedPolicy, notifications.toasts, isK8s, core.http.basePath]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!fullAgentPolicy) {
|
||||
return;
|
||||
}
|
||||
if (isK8s === 'IS_KUBERNETES') {
|
||||
if (typeof fullAgentPolicy === 'object') {
|
||||
return;
|
||||
}
|
||||
setYaml(fullAgentPolicy);
|
||||
} else {
|
||||
if (typeof fullAgentPolicy === 'string') {
|
||||
return;
|
||||
}
|
||||
setYaml(fullAgentPolicyToYaml(fullAgentPolicy, safeDump));
|
||||
}
|
||||
}, [fullAgentPolicy, isK8s]);
|
||||
|
||||
const instructionsSteps = useMemo(() => {
|
||||
const standaloneInstallCommands = StandaloneInstructions(kibanaVersion, isK8s);
|
||||
|
||||
const steps: EuiContainedStepProps[] = !agentPolicy
|
||||
? [
|
||||
AgentPolicySelectionStep({
|
||||
selectedPolicy,
|
||||
agentPolicies,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
setSelectedPolicyId,
|
||||
refreshAgentPolicies,
|
||||
}),
|
||||
]
|
||||
: [];
|
||||
|
||||
if (selectionType === 'radio') {
|
||||
steps.push(InstallationModeSelectionStep({ mode, setMode }));
|
||||
}
|
||||
|
||||
steps.push(
|
||||
ConfigureStandaloneAgentStep({
|
||||
isK8s,
|
||||
selectedPolicyId: selectedPolicy?.id,
|
||||
yaml,
|
||||
downloadLink,
|
||||
})
|
||||
);
|
||||
|
||||
steps.push(
|
||||
InstallStandaloneAgentStep({
|
||||
installCommand: standaloneInstallCommands,
|
||||
isK8s,
|
||||
selectedPolicyId: selectedPolicy?.id,
|
||||
})
|
||||
);
|
||||
|
||||
return steps;
|
||||
}, [
|
||||
kibanaVersion,
|
||||
isK8s,
|
||||
agentPolicy,
|
||||
selectedPolicy,
|
||||
agentPolicies,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
setSelectedPolicyId,
|
||||
refreshAgentPolicies,
|
||||
selectionType,
|
||||
yaml,
|
||||
downloadLink,
|
||||
mode,
|
||||
setMode,
|
||||
]);
|
||||
|
||||
return <EuiSteps steps={instructionsSteps} />;
|
||||
};
|
||||
|
||||
export const ManagedSteps: React.FunctionComponent<InstructionProps> = ({
|
||||
agentPolicy,
|
||||
agentPolicies,
|
||||
selectedPolicy,
|
||||
setSelectedPolicyId,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
settings,
|
||||
refreshAgentPolicies,
|
||||
mode,
|
||||
setMode,
|
||||
selectionType,
|
||||
onClickViewAgents,
|
||||
isK8s,
|
||||
installedPackagePolicy,
|
||||
}) => {
|
||||
const core = useStartServices();
|
||||
const { docLinks } = core;
|
||||
const link = docLinks.links.fleet.troubleshooting;
|
||||
const [agentDataConfirmed, setAgentDataConfirmed] = useState<boolean>(false);
|
||||
|
||||
const apiKey = useGetOneEnrollmentAPIKey(selectedApiKeyId);
|
||||
const apiKeyData = apiKey?.data;
|
||||
const enrolledAgentIds = usePollingAgentCount(selectedPolicy?.id || '');
|
||||
|
||||
const fleetServerHosts = useMemo(() => {
|
||||
return settings?.fleet_server_hosts || [];
|
||||
}, [settings]);
|
||||
|
||||
const instructionsSteps = useMemo(() => {
|
||||
const steps: EuiContainedStepProps[] = !agentPolicy
|
||||
? [
|
||||
AgentPolicySelectionStep({
|
||||
selectedPolicy,
|
||||
agentPolicies,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
setSelectedPolicyId,
|
||||
refreshAgentPolicies,
|
||||
}),
|
||||
]
|
||||
: [
|
||||
AgentEnrollmentKeySelectionStep({
|
||||
selectedPolicy,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
}),
|
||||
];
|
||||
|
||||
if (selectionType === 'radio') {
|
||||
steps.push(InstallationModeSelectionStep({ mode, setMode }));
|
||||
}
|
||||
|
||||
steps.push(
|
||||
InstallManagedAgentStep({
|
||||
apiKeyData,
|
||||
selectedApiKeyId,
|
||||
fleetServerHosts,
|
||||
isK8s,
|
||||
})
|
||||
);
|
||||
if (selectedApiKeyId && apiKeyData) {
|
||||
steps.push(
|
||||
AgentEnrollmentConfirmationStep({
|
||||
selectedPolicyId: selectedPolicy?.id,
|
||||
onClickViewAgents,
|
||||
troubleshootLink: link,
|
||||
agentCount: enrolledAgentIds.length,
|
||||
})
|
||||
);
|
||||
}
|
||||
if (selectedPolicy && enrolledAgentIds.length) {
|
||||
steps.push(
|
||||
IncomingDataConfirmationStep({
|
||||
agentIds: enrolledAgentIds,
|
||||
agentDataConfirmed,
|
||||
setAgentDataConfirmed,
|
||||
installedPolicy: installedPackagePolicy,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return steps;
|
||||
}, [
|
||||
agentPolicy,
|
||||
selectedPolicy,
|
||||
agentPolicies,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
setSelectedPolicyId,
|
||||
refreshAgentPolicies,
|
||||
selectionType,
|
||||
apiKeyData,
|
||||
fleetServerHosts,
|
||||
isK8s,
|
||||
mode,
|
||||
setMode,
|
||||
onClickViewAgents,
|
||||
link,
|
||||
enrolledAgentIds,
|
||||
agentDataConfirmed,
|
||||
installedPackagePolicy,
|
||||
]);
|
||||
|
||||
return <EuiSteps steps={instructionsSteps} />;
|
||||
};
|
||||
|
||||
export const FleetServerSteps: React.FunctionComponent<InstructionProps> = ({
|
||||
agentPolicy,
|
||||
agentPolicies,
|
||||
selectedPolicy,
|
||||
setSelectedPolicyId,
|
||||
refreshAgentPolicies,
|
||||
}) => {
|
||||
const [selectedApiKeyId, setSelectedAPIKeyId] = useState<string | undefined>();
|
||||
|
||||
const apiKey = useGetOneEnrollmentAPIKey(selectedApiKeyId);
|
||||
const apiKeyData = apiKey?.data;
|
||||
const fleetServerInstructions = useFleetServerInstructions(apiKeyData?.item?.policy_id);
|
||||
|
||||
const fleetServerSteps = useMemo(() => {
|
||||
const {
|
||||
serviceToken,
|
||||
getServiceToken,
|
||||
isLoadingServiceToken,
|
||||
installCommand: managedInstallCommands,
|
||||
platform,
|
||||
setPlatform,
|
||||
deploymentMode,
|
||||
setDeploymentMode,
|
||||
addFleetServerHost,
|
||||
} = fleetServerInstructions;
|
||||
|
||||
return [
|
||||
deploymentModeStep({ deploymentMode, setDeploymentMode }),
|
||||
addFleetServerHostStep({ addFleetServerHost }),
|
||||
ServiceTokenStep({ serviceToken, getServiceToken, isLoadingServiceToken }),
|
||||
FleetServerCommandStep({
|
||||
serviceToken,
|
||||
installCommand: managedInstallCommands,
|
||||
platform,
|
||||
setPlatform,
|
||||
}),
|
||||
];
|
||||
}, [fleetServerInstructions]);
|
||||
|
||||
const instructionsSteps = useMemo(() => {
|
||||
const steps: EuiContainedStepProps[] = !agentPolicy
|
||||
? [
|
||||
AgentPolicySelectionStep({
|
||||
selectedPolicy,
|
||||
agentPolicies,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
setSelectedPolicyId,
|
||||
refreshAgentPolicies,
|
||||
}),
|
||||
]
|
||||
: [
|
||||
AgentEnrollmentKeySelectionStep({
|
||||
selectedPolicy,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
}),
|
||||
];
|
||||
|
||||
steps.push(...fleetServerSteps);
|
||||
|
||||
return steps;
|
||||
}, [
|
||||
agentPolicy,
|
||||
selectedPolicy,
|
||||
agentPolicies,
|
||||
selectedApiKeyId,
|
||||
setSelectedPolicyId,
|
||||
refreshAgentPolicies,
|
||||
fleetServerSteps,
|
||||
]);
|
||||
|
||||
return <EuiSteps steps={instructionsSteps} />;
|
||||
};
|
|
@ -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 from 'react';
|
||||
import {
|
||||
EuiText,
|
||||
EuiButton,
|
||||
EuiSpacer,
|
||||
EuiCode,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiCopy,
|
||||
EuiCodeBlock,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
|
||||
|
||||
import type { K8sMode } from '../types';
|
||||
|
||||
export const ConfigureStandaloneAgentStep = ({
|
||||
isK8s,
|
||||
selectedPolicyId,
|
||||
yaml,
|
||||
downloadLink,
|
||||
}: {
|
||||
isK8s?: K8sMode;
|
||||
selectedPolicyId?: string;
|
||||
yaml: string;
|
||||
downloadLink: string;
|
||||
}): EuiContainedStepProps => {
|
||||
const policyMsg =
|
||||
isK8s === 'IS_KUBERNETES' ? (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.stepConfigureAgentDescriptionk8s"
|
||||
defaultMessage="Copy or download the Kubernetes manifest inside the Kubernetes cluster. Modify {ESUsernameVariable} and {ESPasswordVariable} in the Daemonset environment variables and apply the manifest."
|
||||
values={{
|
||||
ESUsernameVariable: <EuiCode>ES_USERNAME</EuiCode>,
|
||||
ESPasswordVariable: <EuiCode>ES_PASSWORD</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.stepConfigureAgentDescription"
|
||||
defaultMessage="Copy this policy to the {fileName} on the host where the Elastic Agent is installed. Modify {ESUsernameVariable} and {ESPasswordVariable} in the {outputSection} section of {fileName} to use your Elasticsearch credentials."
|
||||
values={{
|
||||
fileName: <EuiCode>elastic-agent.yml</EuiCode>,
|
||||
ESUsernameVariable: <EuiCode>ES_USERNAME</EuiCode>,
|
||||
ESPasswordVariable: <EuiCode>ES_PASSWORD</EuiCode>,
|
||||
outputSection: <EuiCode>outputs</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const downloadMsg =
|
||||
isK8s === 'IS_KUBERNETES' ? (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.downloadPolicyButtonk8s"
|
||||
defaultMessage="Download Manifest"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.downloadPolicyButton"
|
||||
defaultMessage="Download Policy"
|
||||
/>
|
||||
);
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepConfigureAgentTitle', {
|
||||
defaultMessage: 'Configure the agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
{!yaml ? null : (
|
||||
<EuiText>
|
||||
<>{policyMsg}</>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup gutterSize="m">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiCopy textToCopy={yaml}>
|
||||
{(copy) => (
|
||||
<EuiButton onClick={copy} iconType="copyClipboard">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.copyPolicyButton"
|
||||
defaultMessage="Copy to clipboard"
|
||||
/>
|
||||
</EuiButton>
|
||||
)}
|
||||
</EuiCopy>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton iconType="download" href={downloadLink} isDisabled={!downloadLink}>
|
||||
<>{downloadMsg}</>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiCodeBlock
|
||||
language="yaml"
|
||||
style={{ maxHeight: 300 }}
|
||||
fontSize="m"
|
||||
data-test-subj="agentPolicyCodeBlock"
|
||||
>
|
||||
{yaml}
|
||||
</EuiCodeBlock>
|
||||
</EuiText>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
status: !yaml ? 'loading' : 'incomplete',
|
||||
};
|
||||
};
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
EuiText,
|
||||
EuiButton,
|
||||
|
@ -22,21 +22,19 @@ import semverMajor from 'semver/functions/major';
|
|||
import semverMinor from 'semver/functions/minor';
|
||||
import semverPatch from 'semver/functions/patch';
|
||||
|
||||
import type { AgentPolicy } from '../../types';
|
||||
import { useGetSettings, useKibanaVersion, useStartServices } from '../../hooks';
|
||||
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
|
||||
|
||||
import { agentPolicyRouteService } from '../../../common';
|
||||
import { useGetSettings, useKibanaVersion, useStartServices } from '../../../hooks';
|
||||
|
||||
import { sendGetK8sManifest } from '../../hooks/use_request/k8s';
|
||||
import { agentPolicyRouteService } from '../../../../common';
|
||||
|
||||
import { AdvancedAgentAuthenticationSettings } from './advanced_agent_authentication_settings';
|
||||
import { SelectCreateAgentPolicy } from './agent_policy_select_create';
|
||||
import { sendGetK8sManifest } from '../../../hooks/use_request/k8s';
|
||||
|
||||
export const DownloadStep = (
|
||||
hasFleetServer: boolean,
|
||||
isK8s?: string,
|
||||
enrollmentAPIKey?: string
|
||||
) => {
|
||||
): EuiContainedStepProps => {
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
const core = useStartServices();
|
||||
const settings = useGetSettings();
|
||||
|
@ -49,6 +47,7 @@ export const DownloadStep = (
|
|||
|
||||
const [yaml, setYaml] = useState<any | undefined>();
|
||||
const [fleetServer, setFleetServer] = useState<string | ''>();
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchK8sManifest() {
|
||||
try {
|
||||
|
@ -132,6 +131,7 @@ export const DownloadStep = (
|
|||
) : (
|
||||
''
|
||||
);
|
||||
|
||||
const k8sCopyYaml =
|
||||
isK8s === 'IS_KUBERNETES' ? (
|
||||
<EuiCopy textToCopy={yaml}>
|
||||
|
@ -147,6 +147,7 @@ export const DownloadStep = (
|
|||
) : (
|
||||
''
|
||||
);
|
||||
|
||||
const k8sYaml =
|
||||
isK8s === 'IS_KUBERNETES' ? (
|
||||
<EuiCodeBlock language="yaml" style={{ maxHeight: 300 }} fontSize="m">
|
||||
|
@ -202,96 +203,3 @@ export const DownloadStep = (
|
|||
),
|
||||
};
|
||||
};
|
||||
|
||||
export const AgentPolicySelectionStep = ({
|
||||
agentPolicies,
|
||||
setSelectedPolicyId,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
excludeFleetServer,
|
||||
refreshAgentPolicies,
|
||||
}: {
|
||||
agentPolicies: AgentPolicy[];
|
||||
setSelectedPolicyId?: (policyId?: string) => void;
|
||||
selectedApiKeyId?: string;
|
||||
setSelectedAPIKeyId?: (key?: string) => void;
|
||||
excludeFleetServer?: boolean;
|
||||
refreshAgentPolicies: () => void;
|
||||
}) => {
|
||||
// storing the created agent policy id as the child component is being recreated
|
||||
const [policyId, setPolicyId] = useState<string | undefined>(undefined);
|
||||
const regularAgentPolicies = useMemo(() => {
|
||||
return agentPolicies.filter(
|
||||
(policy) =>
|
||||
policy && !policy.is_managed && (!excludeFleetServer || !policy.is_default_fleet_server)
|
||||
);
|
||||
}, [agentPolicies, excludeFleetServer]);
|
||||
|
||||
const onAgentPolicyChange = useCallback(
|
||||
async (key?: string, policy?: AgentPolicy) => {
|
||||
if (policy) {
|
||||
refreshAgentPolicies();
|
||||
}
|
||||
if (setSelectedPolicyId) {
|
||||
setSelectedPolicyId(key);
|
||||
setPolicyId(key);
|
||||
}
|
||||
},
|
||||
[setSelectedPolicyId, refreshAgentPolicies]
|
||||
);
|
||||
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepChooseAgentPolicyTitle', {
|
||||
defaultMessage: 'What type of host are you adding?',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<SelectCreateAgentPolicy
|
||||
agentPolicies={regularAgentPolicies}
|
||||
withKeySelection={setSelectedAPIKeyId ? true : false}
|
||||
selectedApiKeyId={selectedApiKeyId}
|
||||
onKeyChange={setSelectedAPIKeyId}
|
||||
onAgentPolicyChange={onAgentPolicyChange}
|
||||
excludeFleetServer={excludeFleetServer}
|
||||
policyId={policyId}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
export const AgentEnrollmentKeySelectionStep = ({
|
||||
agentPolicy,
|
||||
selectedApiKeyId,
|
||||
setSelectedAPIKeyId,
|
||||
}: {
|
||||
agentPolicy: AgentPolicy;
|
||||
selectedApiKeyId?: string;
|
||||
setSelectedAPIKeyId: (key?: string) => void;
|
||||
}) => {
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepConfigurePolicyAuthenticationTitle', {
|
||||
defaultMessage: 'Select enrollment token',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentEnrollment.agentAuthenticationSettings"
|
||||
defaultMessage="{agentPolicyName} has been selected. Select which enrollment token to use when enrolling agents."
|
||||
values={{
|
||||
agentPolicyName: <strong>{agentPolicy.name}</strong>,
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="l" />
|
||||
<AdvancedAgentAuthenticationSettings
|
||||
agentPolicyId={agentPolicy.id}
|
||||
selectedApiKeyId={selectedApiKeyId}
|
||||
initialAuthenticationSettingsOpen
|
||||
onKeyChange={setSelectedAPIKeyId}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
};
|
||||
};
|
|
@ -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 React from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
|
||||
|
||||
import type { InstalledIntegrationPolicy } from '../use_get_agent_incoming_data';
|
||||
|
||||
import { ConfirmIncomingData } from '../confirm_incoming_data';
|
||||
|
||||
export const IncomingDataConfirmationStep = ({
|
||||
agentIds,
|
||||
installedPolicy,
|
||||
agentDataConfirmed,
|
||||
setAgentDataConfirmed,
|
||||
}: {
|
||||
agentIds: string[];
|
||||
installedPolicy?: InstalledIntegrationPolicy;
|
||||
agentDataConfirmed: boolean;
|
||||
setAgentDataConfirmed: (v: boolean) => void;
|
||||
}): EuiContainedStepProps => {
|
||||
return {
|
||||
title: !agentDataConfirmed
|
||||
? i18n.translate('xpack.fleet.agentEnrollment.stepConfirmIncomingData', {
|
||||
defaultMessage: 'Confirm incoming data',
|
||||
})
|
||||
: i18n.translate('xpack.fleet.agentEnrollment.stepConfirmIncomingData.completed', {
|
||||
defaultMessage: 'Incoming data confirmed',
|
||||
}),
|
||||
children: (
|
||||
<ConfirmIncomingData
|
||||
agentIds={agentIds}
|
||||
installedPolicy={installedPolicy}
|
||||
agentDataConfirmed={agentDataConfirmed}
|
||||
setAgentDataConfirmed={setAgentDataConfirmed}
|
||||
/>
|
||||
),
|
||||
status: !agentDataConfirmed ? 'loading' : 'complete',
|
||||
};
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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 * from './agent_enrollment_confirmation_step';
|
||||
export * from './agent_enrollment_key_selection_step';
|
||||
export * from './agent_policy_selection_step';
|
||||
export * from './configure_standalone_agent_step';
|
||||
export * from './download_step';
|
||||
export * from './incoming_data_confirmation_step';
|
||||
export * from './install_managed_agent_step';
|
||||
export * from './install_standalone_agent_step';
|
||||
export * from './installation_mode_selection_step';
|
||||
export * from './compute_steps';
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
|
||||
|
||||
import type { GetOneEnrollmentAPIKeyResponse } from '../../../../common/types/rest_spec/enrollment_api_key';
|
||||
|
||||
import { ManualInstructions } from '../../enrollment_instructions';
|
||||
|
||||
export const InstallManagedAgentStep = ({
|
||||
selectedApiKeyId,
|
||||
apiKeyData,
|
||||
fleetServerHosts,
|
||||
isK8s,
|
||||
}: {
|
||||
fleetServerHosts: string[];
|
||||
selectedApiKeyId?: string;
|
||||
apiKeyData?: GetOneEnrollmentAPIKeyResponse | null;
|
||||
isK8s?: string;
|
||||
}): EuiContainedStepProps => {
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle', {
|
||||
defaultMessage: 'Install Elastic Agent on your host',
|
||||
}),
|
||||
children: selectedApiKeyId && apiKeyData && (
|
||||
<ManualInstructions
|
||||
apiKey={apiKeyData.item}
|
||||
fleetServerHosts={fleetServerHosts}
|
||||
isK8s={isK8s}
|
||||
/>
|
||||
),
|
||||
};
|
||||
};
|
|
@ -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 React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
|
||||
|
||||
import type { CommandsByPlatform } from '../../../applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils';
|
||||
import { PlatformSelector } from '../../enrollment_instructions/manual/platform_selector';
|
||||
|
||||
import { InstallationMessage } from '../installation_message';
|
||||
|
||||
import type { K8sMode } from '../types';
|
||||
|
||||
export const InstallStandaloneAgentStep = ({
|
||||
installCommand,
|
||||
isK8s,
|
||||
selectedPolicyId,
|
||||
}: {
|
||||
installCommand: CommandsByPlatform;
|
||||
isK8s?: K8sMode;
|
||||
selectedPolicyId?: string;
|
||||
}): EuiContainedStepProps => {
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle', {
|
||||
defaultMessage: 'Install Elastic Agent on your host',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<InstallationMessage />
|
||||
<PlatformSelector
|
||||
linuxCommand={installCommand.linux}
|
||||
macCommand={installCommand.mac}
|
||||
windowsCommand={installCommand.windows}
|
||||
linuxDebCommand={installCommand.deb}
|
||||
linuxRpmCommand={installCommand.rpm}
|
||||
isK8s={isK8s === 'IS_KUBERNETES'}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
};
|
||||
};
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { EuiRadioGroup } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
|
||||
|
||||
import type { FlyoutMode } from '../types';
|
||||
|
||||
export const InstallationModeSelectionStep = ({
|
||||
mode,
|
||||
setMode,
|
||||
}: {
|
||||
mode: FlyoutMode;
|
||||
setMode: (v: FlyoutMode) => void;
|
||||
}): EuiContainedStepProps => {
|
||||
// radio id has to be unique so that the component works even if appears twice in DOM
|
||||
const radioSuffix = 'installation_mode_agent_selection';
|
||||
|
||||
const onChangeCallback = (v: string) => {
|
||||
const value = v.split('_')[0];
|
||||
if (value === 'managed' || value === 'standalone') {
|
||||
setMode(value);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
title: i18n.translate('xpack.fleet.agentEnrollment.stepInstallType', {
|
||||
defaultMessage: 'Enroll in Fleet?',
|
||||
}),
|
||||
children: (
|
||||
<EuiRadioGroup
|
||||
options={[
|
||||
{
|
||||
id: `managed_${radioSuffix}`,
|
||||
label: (
|
||||
<FormattedMessage
|
||||
data-test-subj="agentFlyoutManagedRadioButtons"
|
||||
id="xpack.fleet.agentFlyout.managedRadioOption"
|
||||
defaultMessage="{managed} – Enroll in Elastic Agent in Fleet to automatically deploy updates and centrally manage the agent."
|
||||
values={{
|
||||
managed: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentFlyout.managedMessage"
|
||||
defaultMessage="Enroll in Fleet (recommended)"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: `standalone_${radioSuffix}`,
|
||||
label: (
|
||||
<FormattedMessage
|
||||
data-test-subj="agentFlyoutStandaloneRadioButtons"
|
||||
id="xpack.fleet.agentFlyout.standaloneRadioOption"
|
||||
defaultMessage="{standaloneMessage} – Run an Elastic Agent standalone to configure and update the agent manually on the host where the agent is installed."
|
||||
values={{
|
||||
standaloneMessage: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentFlyout.standaloneMessage"
|
||||
defaultMessage="Run standalone"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
idSelected={`${mode}_${radioSuffix}`}
|
||||
onChange={onChangeCallback}
|
||||
name={`radio group ${radioSuffix}`}
|
||||
/>
|
||||
),
|
||||
};
|
||||
};
|
|
@ -5,35 +5,50 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { EuiStepProps } from '@elastic/eui';
|
||||
|
||||
import type { AgentPolicy, Settings } from '../../types';
|
||||
|
||||
import type { InstalledIntegrationPolicy } from './use_get_agent_incoming_data';
|
||||
|
||||
export type K8sMode = 'IS_LOADING' | 'IS_KUBERNETES' | 'IS_NOT_KUBERNETES';
|
||||
export type FlyoutMode = 'managed' | 'standalone';
|
||||
export type SelectionType = 'tabs' | 'radio';
|
||||
|
||||
export interface BaseProps {
|
||||
/**
|
||||
* The user selected policy to be used. If this value is `undefined` a value must be provided for `agentPolicies`.
|
||||
*/
|
||||
agentPolicy?: AgentPolicy;
|
||||
|
||||
settings?: Settings;
|
||||
|
||||
isFleetServerPolicySelected?: boolean;
|
||||
|
||||
isK8s?: K8sMode;
|
||||
|
||||
/**
|
||||
* There is a step in the agent enrollment process that allows users to see the data from an integration represented in the UI
|
||||
* in some way. This is an area for consumers to render a button and text explaining how data can be viewed.
|
||||
*/
|
||||
viewDataStep?: EuiStepProps;
|
||||
isIntegrationFlow?: boolean;
|
||||
installedPackagePolicy?: InstalledIntegrationPolicy;
|
||||
}
|
||||
|
||||
settings?: Settings;
|
||||
|
||||
setSelectedPolicyId?: (policyId?: string) => void;
|
||||
|
||||
policyId?: string;
|
||||
|
||||
isFleetServerPolicySelected?: boolean;
|
||||
|
||||
isK8s?: string;
|
||||
export interface FlyOutProps extends BaseProps {
|
||||
onClose: () => void;
|
||||
defaultMode?: FlyoutMode;
|
||||
}
|
||||
|
||||
export interface InstructionProps extends BaseProps {
|
||||
agentPolicies: AgentPolicy[];
|
||||
selectedPolicy: AgentPolicy | undefined;
|
||||
setSelectedPolicyId: (policyId?: string) => void;
|
||||
refreshAgentPolicies: () => void;
|
||||
isLoadingAgentPolicies?: boolean;
|
||||
onClickViewAgents: () => void;
|
||||
mode: FlyoutMode;
|
||||
setMode: (v: FlyoutMode) => void;
|
||||
selectionType: SelectionType;
|
||||
setSelectionType: (type: SelectionType) => void;
|
||||
selectedApiKeyId?: string;
|
||||
setSelectedAPIKeyId: (key?: string) => void;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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, useMemo, useRef } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type { IncomingDataList } from '../../../common/types/rest_spec/agent';
|
||||
|
||||
import { sendGetAgentIncomingData, useLink } from '../../hooks/index';
|
||||
|
||||
export interface InstalledIntegrationPolicy {
|
||||
name: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export const useGetAgentIncomingData = (
|
||||
incomingData: IncomingDataList[],
|
||||
installedPolicy?: InstalledIntegrationPolicy
|
||||
) => {
|
||||
const enrolledAgents = useMemo(() => incomingData.length, [incomingData.length]);
|
||||
const numAgentsWithData = useMemo(
|
||||
() =>
|
||||
incomingData.reduce((acc, curr) => {
|
||||
const agentData = Object.values(curr)[0];
|
||||
return !!agentData.data ? acc + 1 : acc;
|
||||
}, 0),
|
||||
[incomingData]
|
||||
);
|
||||
const { getAbsolutePath, getHref } = useLink();
|
||||
|
||||
let href;
|
||||
let text;
|
||||
let message;
|
||||
|
||||
if (!installedPolicy) {
|
||||
href = '';
|
||||
text = '';
|
||||
message = '';
|
||||
}
|
||||
|
||||
if (installedPolicy?.name === 'apm') {
|
||||
href = getAbsolutePath('/app/home#/tutorial/apm');
|
||||
text = i18n.translate('xpack.fleet.confirmIncomingData.installApmAgentButtonText', {
|
||||
defaultMessage: 'Install APM Agent',
|
||||
});
|
||||
message = i18n.translate('xpack.fleet.confirmIncomingData.APMsubtitle', {
|
||||
defaultMessage:
|
||||
'Next, install APM agents on your hosts to collect data from your applications and services.',
|
||||
});
|
||||
} else {
|
||||
href = getHref('integration_details_assets', {
|
||||
pkgkey: `${installedPolicy?.name}-${installedPolicy?.version}`,
|
||||
});
|
||||
text = i18n.translate('xpack.fleet.confirmIncomingData.viewDataAssetsButtonText', {
|
||||
defaultMessage: 'View assets',
|
||||
});
|
||||
message = i18n.translate('xpack.fleet.confirmIncomingData.subtitle', {
|
||||
defaultMessage:
|
||||
'Next, analyze your data using our integration assets such as curated views, dashboards and more.',
|
||||
});
|
||||
}
|
||||
const linkButton = { href, text };
|
||||
|
||||
return {
|
||||
enrolledAgents,
|
||||
numAgentsWithData,
|
||||
linkButton,
|
||||
message,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook for polling incoming data for the selected agent policy.
|
||||
* @param agentIds
|
||||
* @returns incomingData, isLoading
|
||||
*/
|
||||
const POLLING_INTERVAL_MS = 5 * 1000; // 5 sec
|
||||
|
||||
export const usePollingIncomingData = (agentsIds: string[]) => {
|
||||
const timeout = useRef<number | undefined>(undefined);
|
||||
const [incomingData, setIncomingData] = useState<IncomingDataList[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
let isAborted = false;
|
||||
|
||||
const poll = () => {
|
||||
timeout.current = window.setTimeout(async () => {
|
||||
const { data } = await sendGetAgentIncomingData({ agentsIds });
|
||||
|
||||
if (data?.items) {
|
||||
// filter out agents that have `data = false` and keep polling
|
||||
const filtered = data?.items.filter((item) => {
|
||||
const key = Object.keys(item)[0];
|
||||
return item[key].data === true;
|
||||
});
|
||||
|
||||
if (filtered.length > 0) {
|
||||
setIncomingData(filtered);
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
if (!isAborted) {
|
||||
poll();
|
||||
}
|
||||
}, POLLING_INTERVAL_MS);
|
||||
};
|
||||
|
||||
poll();
|
||||
if (isAborted || incomingData.length > 0) clearTimeout(timeout.current);
|
||||
|
||||
return () => {
|
||||
isAborted = true;
|
||||
};
|
||||
}, [agentsIds, incomingData]);
|
||||
|
||||
return { incomingData, isLoading };
|
||||
};
|
|
@ -7,15 +7,16 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import { useStartServices, useKibanaVersion } from '../../../hooks';
|
||||
import { useKibanaVersion } from '../../../hooks';
|
||||
import type { EnrollmentAPIKey } from '../../../types';
|
||||
|
||||
import { InstallationMessage } from '../../agent_enrollment_flyout/installation_message';
|
||||
|
||||
import { PlatformSelector } from './platform_selector';
|
||||
|
||||
interface Props {
|
||||
fleetServerHosts: string[];
|
||||
apiKey: EnrollmentAPIKey;
|
||||
policyId: string | undefined;
|
||||
isK8s: string | undefined;
|
||||
}
|
||||
|
||||
|
@ -26,10 +27,8 @@ function getfleetServerHostsEnrollArgs(apiKey: EnrollmentAPIKey, fleetServerHost
|
|||
export const ManualInstructions: React.FunctionComponent<Props> = ({
|
||||
apiKey,
|
||||
fleetServerHosts,
|
||||
policyId,
|
||||
isK8s,
|
||||
}) => {
|
||||
const { docLinks } = useStartServices();
|
||||
const enrollArgs = getfleetServerHostsEnrollArgs(apiKey, fleetServerHosts);
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
|
||||
|
@ -57,14 +56,16 @@ sudo rpm -vi elastic-agent-${kibanaVersion}-x86_64.rpm
|
|||
sudo elastic-agent enroll ${enrollArgs} \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`;
|
||||
|
||||
return (
|
||||
<PlatformSelector
|
||||
linuxCommand={linuxCommand}
|
||||
macCommand={macCommand}
|
||||
windowsCommand={windowsCommand}
|
||||
linuxDebCommand={linuxDebCommand}
|
||||
linuxRpmCommand={linuxRpmCommand}
|
||||
troubleshootLink={docLinks.links.fleet.troubleshooting}
|
||||
isK8s={isK8s === 'IS_KUBERNETES'}
|
||||
/>
|
||||
<>
|
||||
<InstallationMessage />
|
||||
<PlatformSelector
|
||||
linuxCommand={linuxCommand}
|
||||
macCommand={macCommand}
|
||||
windowsCommand={windowsCommand}
|
||||
linuxDebCommand={linuxDebCommand}
|
||||
linuxRpmCommand={linuxRpmCommand}
|
||||
isK8s={isK8s === 'IS_KUBERNETES'}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -7,14 +7,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
EuiText,
|
||||
EuiSpacer,
|
||||
EuiLink,
|
||||
EuiCodeBlock,
|
||||
EuiButtonGroup,
|
||||
EuiCallOut,
|
||||
} from '@elastic/eui';
|
||||
import { EuiText, EuiSpacer, EuiCodeBlock, EuiButtonGroup, EuiCallOut } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
|
@ -27,7 +20,6 @@ interface Props {
|
|||
windowsCommand: string;
|
||||
linuxDebCommand: string;
|
||||
linuxRpmCommand: string;
|
||||
troubleshootLink: string;
|
||||
isK8s: boolean;
|
||||
}
|
||||
|
||||
|
@ -44,7 +36,6 @@ export const PlatformSelector: React.FunctionComponent<Props> = ({
|
|||
windowsCommand,
|
||||
linuxDebCommand,
|
||||
linuxRpmCommand,
|
||||
troubleshootLink,
|
||||
isK8s,
|
||||
}) => {
|
||||
const { platform, setPlatform } = usePlatform();
|
||||
|
@ -127,23 +118,6 @@ export const PlatformSelector: React.FunctionComponent<Props> = ({
|
|||
)}
|
||||
</>
|
||||
)}
|
||||
<EuiSpacer size="l" />
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.enrollmentInstructions.troubleshootingText"
|
||||
defaultMessage="If you are having trouble connecting, see our {link}."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink target="_blank" external href={troubleshootLink}>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.enrollmentInstructions.troubleshootingLink"
|
||||
defaultMessage="troubleshooting guide"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 { CommandsByPlatform } from '../../../applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils';
|
||||
import type { K8sMode } from '../../../components/agent_enrollment_flyout/types';
|
||||
|
||||
export const StandaloneInstructions = (
|
||||
kibanaVersion: string,
|
||||
isK8s?: K8sMode
|
||||
): CommandsByPlatform => {
|
||||
const KUBERNETES_RUN_INSTRUCTIONS = 'kubectl apply -f elastic-agent-standalone-kubernetes.yaml';
|
||||
|
||||
const STANDALONE_RUN_INSTRUCTIONS_LINUX = `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
|
||||
sudo ./elastic-agent install`;
|
||||
|
||||
const STANDALONE_RUN_INSTRUCTIONS_MAC = `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
|
||||
sudo ./elastic-agent install`;
|
||||
|
||||
const STANDALONE_RUN_INSTRUCTIONS_WINDOWS = `wget 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
|
||||
cd elastic-agent-${kibanaVersion}-windows-x86_64
|
||||
.\\elastic-agent.exe install`;
|
||||
|
||||
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`;
|
||||
|
||||
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 linuxCommand =
|
||||
isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_LINUX;
|
||||
const macCommand =
|
||||
isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_MAC;
|
||||
const windowsCommand =
|
||||
isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_WINDOWS;
|
||||
|
||||
return {
|
||||
linux: linuxCommand,
|
||||
mac: macCommand,
|
||||
windows: windowsCommand,
|
||||
deb: linuxDebCommand,
|
||||
rpm: linuxRpmCommand,
|
||||
};
|
||||
};
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { EuiContextMenuItem, EuiPortal } from '@elastic/eui';
|
||||
import type { EuiStepProps } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import type { AgentPolicy, InMemoryPackagePolicy } from '../types';
|
||||
|
@ -22,14 +21,12 @@ import { PackagePolicyDeleteProvider } from './package_policy_delete_provider';
|
|||
export const PackagePolicyActionsMenu: React.FunctionComponent<{
|
||||
agentPolicy: AgentPolicy;
|
||||
packagePolicy: InMemoryPackagePolicy;
|
||||
viewDataStep?: EuiStepProps;
|
||||
showAddAgent?: boolean;
|
||||
defaultIsOpen?: boolean;
|
||||
upgradePackagePolicyHref: string;
|
||||
}> = ({
|
||||
agentPolicy,
|
||||
packagePolicy,
|
||||
viewDataStep,
|
||||
showAddAgent,
|
||||
upgradePackagePolicyHref,
|
||||
defaultIsOpen = false,
|
||||
|
@ -43,7 +40,6 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{
|
|||
const onEnrollmentFlyoutClose = useMemo(() => {
|
||||
return () => setIsEnrollmentFlyoutOpen(false);
|
||||
}, []);
|
||||
|
||||
const menuItems = [
|
||||
// FIXME: implement View package policy action
|
||||
// <EuiContextMenuItem
|
||||
|
@ -142,8 +138,12 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{
|
|||
<EuiPortal>
|
||||
<AgentEnrollmentFlyout
|
||||
agentPolicy={agentPolicy}
|
||||
viewDataStep={viewDataStep}
|
||||
onClose={onEnrollmentFlyoutClose}
|
||||
isIntegrationFlow={true}
|
||||
installedPackagePolicy={{
|
||||
name: packagePolicy?.package?.name || '',
|
||||
version: packagePolicy?.package?.version || '',
|
||||
}}
|
||||
/>
|
||||
</EuiPortal>
|
||||
)}
|
||||
|
|
|
@ -27,4 +27,3 @@ export * from './use_platform';
|
|||
export * from './use_agent_policy_refresh';
|
||||
export * from './use_package_installations';
|
||||
export * from './use_agent_enrollment_flyout_data';
|
||||
export * from './use_get_agent_incoming_data';
|
||||
|
|
|
@ -1,78 +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 { useEffect, useState, useMemo } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type { IncomingDataList } from '../../common/types/rest_spec/agent';
|
||||
|
||||
import { sendGetAgentIncomingData, useLink } from './index';
|
||||
|
||||
export interface InstalledIntegrationPolicy {
|
||||
name: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export const useGetAgentIncomingData = (
|
||||
agentsIds: string[],
|
||||
installedPolicy?: InstalledIntegrationPolicy
|
||||
) => {
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [incomingData, setIncomingData] = useState<IncomingDataList[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const getIncomingData = async () => {
|
||||
const { data } = await sendGetAgentIncomingData({ agentsIds });
|
||||
if (data?.items) {
|
||||
setIncomingData(data?.items);
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
if (agentsIds) {
|
||||
getIncomingData();
|
||||
}
|
||||
}, [agentsIds]);
|
||||
|
||||
const enrolledAgents = useMemo(() => incomingData.length, [incomingData.length]);
|
||||
const numAgentsWithData = useMemo(
|
||||
() =>
|
||||
incomingData.reduce((acc, curr) => {
|
||||
const agentData = Object.values(curr)[0];
|
||||
return !!agentData.data ? acc + 1 : acc;
|
||||
}, 0),
|
||||
[incomingData]
|
||||
);
|
||||
const { getAbsolutePath, getHref } = useLink();
|
||||
|
||||
let href;
|
||||
let text;
|
||||
if (!installedPolicy) {
|
||||
href = '';
|
||||
text = '';
|
||||
}
|
||||
|
||||
if (installedPolicy?.name === 'apm') {
|
||||
href = getAbsolutePath('/app/home#/tutorial/apm');
|
||||
text = i18n.translate('xpack.fleet.confirmIncomingData.installApmAgentButtonText', {
|
||||
defaultMessage: 'Install APM Agent',
|
||||
});
|
||||
} else {
|
||||
href = getHref('integration_details_assets', {
|
||||
pkgkey: `${installedPolicy?.name}-${installedPolicy?.version}`,
|
||||
});
|
||||
text = i18n.translate('xpack.fleet.confirmIncomingData.viewDataAssetsButtonText', {
|
||||
defaultMessage: 'View assets',
|
||||
});
|
||||
}
|
||||
const linkButton = { href, text };
|
||||
|
||||
return {
|
||||
enrolledAgents,
|
||||
numAgentsWithData,
|
||||
isLoading,
|
||||
linkButton,
|
||||
};
|
||||
};
|
|
@ -18,7 +18,7 @@ export const PLATFORM_OPTIONS: Array<{
|
|||
{
|
||||
id: 'linux',
|
||||
label: i18n.translate('xpack.fleet.enrollmentInstructions.platformButtons.linux', {
|
||||
defaultMessage: 'Linux',
|
||||
defaultMessage: 'Linux Tar',
|
||||
}),
|
||||
'data-test-subj': 'platformTypeLinux',
|
||||
},
|
||||
|
|
|
@ -144,7 +144,7 @@ export async function getIncomingDataByAgentsId(
|
|||
agent_ids: {
|
||||
terms: {
|
||||
field: 'agent.id',
|
||||
size: 10,
|
||||
size: agentsIds.length,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -9843,19 +9843,14 @@
|
|||
"xpack.fleet.agentEnrollment.downloadLink": "Accéder à la page de téléchargement",
|
||||
"xpack.fleet.agentEnrollment.downloadPolicyButton": "Télécharger la stratégie",
|
||||
"xpack.fleet.agentEnrollment.downloadUseLinuxInstaller": "Utilisateurs de Linux : nous vous recommandons d'utiliser les programmes d'installation sur (RPM/DEB), car ils permettent la mise à niveau de votre agent dans Fleet.",
|
||||
"xpack.fleet.agentEnrollment.enrollFleetTabLabel": "Enregistrer dans Fleet",
|
||||
"xpack.fleet.agentEnrollment.enrollStandaloneTabLabel": "Exécuter de façon autonome",
|
||||
"xpack.fleet.agentEnrollment.fleetSettingsLink": "Paramètres de Fleet",
|
||||
"xpack.fleet.agentEnrollment.flyoutTitle": "Ajouter un agent",
|
||||
"xpack.fleet.agentEnrollment.goToDataStreamsLink": "flux de données",
|
||||
"xpack.fleet.agentEnrollment.managedDescription": "L'enregistrement d'un agent Elastic Agent dans Fleet permet de centraliser la gestion de ce dernier tout en déployant automatiquement les mises à jour.",
|
||||
"xpack.fleet.agentEnrollment.missingFleetHostCalloutText": "L'enregistrement d'agents dans Fleet nécessite l'URL de l'hôte de votre serveur Fleet. Vous pouvez ajouter ces informations dans Paramètres de Fleet. Pour en savoir plus, consultez {link}.",
|
||||
"xpack.fleet.agentEnrollment.missingFleetHostCalloutTitle": "URL de l'hôte du serveur Fleet manquante",
|
||||
"xpack.fleet.agentEnrollment.missingFleetHostGuideLink": "Guide de l'utilisateur de Fleet",
|
||||
"xpack.fleet.agentEnrollment.setUpAgentsLink": "configurer la gestion centralisée des agents Elastic",
|
||||
"xpack.fleet.agentEnrollment.standaloneDescription": "Exécutez un agent Elastic Agent de façon autonome pour le configurer et le mettre à jour manuellement sur l'hôte sur lequel il est installé.",
|
||||
"xpack.fleet.agentEnrollment.stepCheckForDataDescription": "L'agent devrait commencer à envoyer des données. Accédez à {link} pour consulter vos données.",
|
||||
"xpack.fleet.agentEnrollment.stepCheckForDataTitle": "Rechercher des données",
|
||||
"xpack.fleet.agentEnrollment.stepChooseAgentPolicyTitle": "Sélectionner une stratégie d'agent",
|
||||
"xpack.fleet.agentEnrollment.stepConfigureAgentDescription": "Copiez cette stratégie dans le fichier {fileName} de l'hôte sur lequel l'agent Elastic Agent est installé. Modifiez {ESUsernameVariable} et {ESPasswordVariable} dans la section {outputSection} du fichier {fileName} pour utiliser vos identifiants de connexion Elasticsearch.",
|
||||
"xpack.fleet.agentEnrollment.stepConfigureAgentTitle": "Configurer l'agent",
|
||||
|
@ -9863,9 +9858,6 @@
|
|||
"xpack.fleet.agentEnrollment.stepDownloadAgentTitle": "Télécharger l'agent Elastic Agent sur votre hôte",
|
||||
"xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle": "Enregistrer et démarrer l'agent Elastic Agent",
|
||||
"xpack.fleet.agentEnrollment.stepRunAgentDescription": "Depuis le répertoire des agents, exécutez cette commande pour installer, enregistrer et démarrer un agent Elastic Agent. Vous pouvez réutiliser cette commande pour configurer des agents sur plusieurs hôtes. Cette action nécessite de disposer de privilèges d'administrateur.",
|
||||
"xpack.fleet.agentEnrollment.stepRunAgentTitle": "Démarrer l'agent",
|
||||
"xpack.fleet.agentEnrollment.stepViewDataTitle": "Consulter vos données",
|
||||
"xpack.fleet.agentEnrollment.viewDataDescription": "Une fois que votre agent a démarré, vous pouvez consulter vos données dans Kibana en utilisant les composants de l'intégration installés. {pleaseNote} : l'affichage des données initiales peut prendre quelques minutes.",
|
||||
"xpack.fleet.agentHealth.checkInTooltipText": "Dernier archivage le {lastCheckIn}",
|
||||
"xpack.fleet.agentHealth.healthyStatusText": "Sain",
|
||||
"xpack.fleet.agentHealth.inactiveStatusText": "Inactif",
|
||||
|
@ -10133,8 +10125,6 @@
|
|||
"xpack.fleet.enrollmentTokensList.secretTitle": "Secret",
|
||||
"xpack.fleet.enrollmentTokensList.showTokenButtonLabel": "Afficher le jeton",
|
||||
"xpack.fleet.epm.addPackagePolicyButtonText": "Ajouter {packageName}",
|
||||
"xpack.fleet.epm.agentEnrollment.viewDataAssetsLabel": "Voir les ressources",
|
||||
"xpack.fleet.epm.agentEnrollment.viewDataDescription.pleaseNoteLabel": "Remarque",
|
||||
"xpack.fleet.epm.assetGroupTitle": "Ressources {assetType}",
|
||||
"xpack.fleet.epm.browseAllButtonText": "Parcourir toutes les intégrations",
|
||||
"xpack.fleet.epm.categoryLabel": "Catégorie",
|
||||
|
|
|
@ -11540,19 +11540,14 @@
|
|||
"xpack.fleet.agentEnrollment.downloadPolicyButton": "ポリシーのダウンロード",
|
||||
"xpack.fleet.agentEnrollment.downloadPolicyButtonk8s": "マニフェストのダウンロード",
|
||||
"xpack.fleet.agentEnrollment.downloadUseLinuxInstaller": "Linuxユーザー:Fleetでエージェントをアップグレードできるため、システムパッケージ(RPM/DEB)ではインストーラー(TAR)をお勧めします。",
|
||||
"xpack.fleet.agentEnrollment.enrollFleetTabLabel": "Fleetで登録",
|
||||
"xpack.fleet.agentEnrollment.enrollStandaloneTabLabel": "スタンドアロンで実行",
|
||||
"xpack.fleet.agentEnrollment.fleetSettingsLink": "Fleet設定",
|
||||
"xpack.fleet.agentEnrollment.flyoutTitle": "エージェントの追加",
|
||||
"xpack.fleet.agentEnrollment.goToDataStreamsLink": "データストリーム",
|
||||
"xpack.fleet.agentEnrollment.managedDescription": "ElasticエージェントをFleetに登録して、自動的に更新をデプロイしたり、一元的にエージェントを管理したりします。",
|
||||
"xpack.fleet.agentEnrollment.missingFleetHostCalloutText": "Fleetにエージェントを登録するには、FleetサーバーホストのURLが必要です。Fleet設定でこの情報を追加できます。詳細は{link}をご覧ください。",
|
||||
"xpack.fleet.agentEnrollment.missingFleetHostCalloutTitle": "FleetサーバーホストのURLが見つかりません",
|
||||
"xpack.fleet.agentEnrollment.missingFleetHostGuideLink": "FleetおよびElasticエージェントガイド",
|
||||
"xpack.fleet.agentEnrollment.setUpAgentsLink": "Elasticエージェントの集中管理を設定",
|
||||
"xpack.fleet.agentEnrollment.standaloneDescription": "Elasticエージェントをスタンドアロンで実行して、エージェントがインストールされているホストで、手動でエージェントを構成および更新します。",
|
||||
"xpack.fleet.agentEnrollment.stepCheckForDataDescription": "エージェントがデータの送信を開始します。{link}に移動して、データを表示してください。",
|
||||
"xpack.fleet.agentEnrollment.stepCheckForDataTitle": "データを確認",
|
||||
"xpack.fleet.agentEnrollment.stepChooseAgentPolicyTitle": "追加しているホストのタイプ",
|
||||
"xpack.fleet.agentEnrollment.stepConfigureAgentDescription": "Elasticエージェントがインストールされているホストで、このポリシーを{fileName}にコピーします。Elasticsearch資格情報を使用するには、{fileName}の{outputSection}セクションで、{ESUsernameVariable}と{ESPasswordVariable}を変更します。",
|
||||
"xpack.fleet.agentEnrollment.stepConfigureAgentDescriptionk8s": "Kubernetesクラスター内でKubernetesマニフェストをコピーしてダウンロードします。Daemonset環境変数で{ESUsernameVariable}と{ESPasswordVariable}を修正し、マニフェストを適用します。",
|
||||
|
@ -11563,9 +11558,6 @@
|
|||
"xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle": "Elasticエージェントを登録して実行",
|
||||
"xpack.fleet.agentEnrollment.stepRunAgentDescription": "エージェントのディレクトリから、このコマンドを実行し、Elasticエージェントを、インストール、登録、起動します。このコマンドを再利用すると、複数のホストでエージェントを設定できます。管理者権限が必要です。",
|
||||
"xpack.fleet.agentEnrollment.stepRunAgentDescriptionk8s": "Kubernetesマニフェストがダウンロードされるディレクトリから適用コマンドを実行します。",
|
||||
"xpack.fleet.agentEnrollment.stepRunAgentTitle": "エージェントの起動",
|
||||
"xpack.fleet.agentEnrollment.stepViewDataTitle": "データを表示",
|
||||
"xpack.fleet.agentEnrollment.viewDataDescription": "エージェントが起動した後、Kibanaでデータを表示するには、統合のインストールされたアセットを使用します。{pleaseNote}:初期データを受信するまでに数分かかる場合があります。",
|
||||
"xpack.fleet.agentHealth.checkInTooltipText": "前回のチェックイン {lastCheckIn}",
|
||||
"xpack.fleet.agentHealth.healthyStatusText": "正常",
|
||||
"xpack.fleet.agentHealth.inactiveStatusText": "非アクティブ",
|
||||
|
@ -11864,8 +11856,6 @@
|
|||
"xpack.fleet.epm.addPackagePolicyButtonPrivilegesRequiredTooltip": "Elasticエージェント統合には、Fleetの「すべて」権限と統合の「すべて」権限が必要です。管理者にお問い合わせください。",
|
||||
"xpack.fleet.epm.addPackagePolicyButtonSecurityRequiredTooltip": "Elasticエージェント統合を追加するには、セキュリティを有効にし、Fleetの「すべて」権限が必要です。管理者にお問い合わせください。",
|
||||
"xpack.fleet.epm.addPackagePolicyButtonText": "{packageName}の追加",
|
||||
"xpack.fleet.epm.agentEnrollment.viewDataAssetsLabel": "アセットを表示",
|
||||
"xpack.fleet.epm.agentEnrollment.viewDataDescription.pleaseNoteLabel": "注記:",
|
||||
"xpack.fleet.epm.assetGroupTitle": "{assetType}アセット",
|
||||
"xpack.fleet.epm.assetTitles.componentTemplates": "コンポーネントテンプレート",
|
||||
"xpack.fleet.epm.assetTitles.dashboards": "ダッシュボード",
|
||||
|
|
|
@ -11559,19 +11559,14 @@
|
|||
"xpack.fleet.agentEnrollment.downloadPolicyButton": "下载策略",
|
||||
"xpack.fleet.agentEnrollment.downloadPolicyButtonk8s": "下载清单",
|
||||
"xpack.fleet.agentEnrollment.downloadUseLinuxInstaller": "Linux 用户:我们建议使用安装程序 (TAR),而非系统软件包 (RPM/DEB),因为这允许您在 Fleet 中升级代理。",
|
||||
"xpack.fleet.agentEnrollment.enrollFleetTabLabel": "在 Fleet 中注册",
|
||||
"xpack.fleet.agentEnrollment.enrollStandaloneTabLabel": "独立运行",
|
||||
"xpack.fleet.agentEnrollment.fleetSettingsLink": "Fleet 设置",
|
||||
"xpack.fleet.agentEnrollment.flyoutTitle": "添加代理",
|
||||
"xpack.fleet.agentEnrollment.goToDataStreamsLink": "数据流",
|
||||
"xpack.fleet.agentEnrollment.managedDescription": "在 Fleet 中注册 Elastic 代理,以便自动部署更新并集中管理该代理。",
|
||||
"xpack.fleet.agentEnrollment.missingFleetHostCalloutText": "需要 Fleet 服务器主机的 URL,才能使用 Fleet 注册代理。可以在“Fleet 设置”中添加此信息。有关更多信息,请参阅{link}。",
|
||||
"xpack.fleet.agentEnrollment.missingFleetHostCalloutTitle": "Fleet 服务器主机的 URL 缺失",
|
||||
"xpack.fleet.agentEnrollment.missingFleetHostGuideLink": "Fleet 和 Elastic 代理指南",
|
||||
"xpack.fleet.agentEnrollment.setUpAgentsLink": "为 Elastic 代理设置集中管理",
|
||||
"xpack.fleet.agentEnrollment.standaloneDescription": "独立运行 Elastic 代理,以在安装代理的主机上手动配置和更新代理。",
|
||||
"xpack.fleet.agentEnrollment.stepCheckForDataDescription": "该代理应该开始发送数据。前往 {link} 以查看您的数据。",
|
||||
"xpack.fleet.agentEnrollment.stepCheckForDataTitle": "检查数据",
|
||||
"xpack.fleet.agentEnrollment.stepChooseAgentPolicyTitle": "您正在添加什么类型的主机?",
|
||||
"xpack.fleet.agentEnrollment.stepConfigureAgentDescription": "在安装 Elastic 代理的主机上将此策略复制到 {fileName}。在 {fileName} 的 {outputSection} 部分中修改 {ESUsernameVariable} 和 {ESPasswordVariable},以使用您的 Elasticsearch 凭据。",
|
||||
"xpack.fleet.agentEnrollment.stepConfigureAgentDescriptionk8s": "复制或下载 Kubernetes 集群内的 Kubernetes 清单。修改 Daemonset 环境变量中的 {ESUsernameVariable} 和 {ESPasswordVariable} 并应用该清单。",
|
||||
|
@ -11582,9 +11577,6 @@
|
|||
"xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle": "注册并启动 Elastic 代理",
|
||||
"xpack.fleet.agentEnrollment.stepRunAgentDescription": "从代理目录运行此命令,以安装、注册并启动 Elastic 代理。您可以重复使用此命令在多个主机上设置代理。需要管理员权限。",
|
||||
"xpack.fleet.agentEnrollment.stepRunAgentDescriptionk8s": "从下载 Kubernetes 清单的目录运行应用命令。",
|
||||
"xpack.fleet.agentEnrollment.stepRunAgentTitle": "启动代理",
|
||||
"xpack.fleet.agentEnrollment.stepViewDataTitle": "查看您的数据",
|
||||
"xpack.fleet.agentEnrollment.viewDataDescription": "代理启动后,可以通过使用集成的已安装资产来在 Kibana 中查看数据。{pleaseNote}:获得初始数据可能需要几分钟。",
|
||||
"xpack.fleet.agentHealth.checkInTooltipText": "上次签入时间 {lastCheckIn}",
|
||||
"xpack.fleet.agentHealth.healthyStatusText": "运行正常",
|
||||
"xpack.fleet.agentHealth.inactiveStatusText": "非活动",
|
||||
|
@ -11885,8 +11877,6 @@
|
|||
"xpack.fleet.epm.addPackagePolicyButtonPrivilegesRequiredTooltip": "Elastic 代理集成需要 Fleet 的所有权限和集成的所有权限。请联系您的管理员。",
|
||||
"xpack.fleet.epm.addPackagePolicyButtonSecurityRequiredTooltip": "要添加 Elastic 代理集成,必须启用安全功能并具有 Fleet 的所有权限。请联系您的管理员。",
|
||||
"xpack.fleet.epm.addPackagePolicyButtonText": "添加 {packageName}",
|
||||
"xpack.fleet.epm.agentEnrollment.viewDataAssetsLabel": "查看资产",
|
||||
"xpack.fleet.epm.agentEnrollment.viewDataDescription.pleaseNoteLabel": "请注意",
|
||||
"xpack.fleet.epm.assetGroupTitle": "{assetType} 资产",
|
||||
"xpack.fleet.epm.assetTitles.componentTemplates": "组件模板",
|
||||
"xpack.fleet.epm.assetTitles.dashboards": "仪表板",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue