Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fleet] Enrollment keys list page #61346

Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export const AGENT_CONFIG_DETAILS_PATH = `${AGENT_CONFIG_PATH}/`;
export const FLEET_PATH = '/fleet';
export const FLEET_AGENTS_PATH = `${FLEET_PATH}/agents`;
export const FLEET_AGENT_DETAIL_PATH = `${FLEET_AGENTS_PATH}/`;
export const FLEET_ENROLLMENT_TOKENS_PATH = `/fleet/enrollment-tokens`;

export const INDEX_NAME = '.kibana';
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { useRequest, UseRequestConfig } from './use_request';
import { useRequest, UseRequestConfig, sendRequest } from './use_request';
import { enrollmentAPIKeyRouteService } from '../../services';
import { GetOneEnrollmentAPIKeyResponse, GetEnrollmentAPIKeysResponse } from '../../types';
import {
GetOneEnrollmentAPIKeyResponse,
GetEnrollmentAPIKeysResponse,
GetEnrollmentAPIKeysRequest,
} from '../../types';

type RequestOptions = Pick<Partial<UseRequestConfig>, 'pollIntervalMs'>;

Expand All @@ -18,10 +22,30 @@ export function useGetOneEnrollmentAPIKey(keyId: string, options?: RequestOption
});
}

export function useGetEnrollmentAPIKeys(options?: RequestOptions) {
export function sendGetOneEnrollmentAPIKey(keyId: string, options?: RequestOptions) {
return sendRequest<GetOneEnrollmentAPIKeyResponse>({
method: 'get',
path: enrollmentAPIKeyRouteService.getInfoPath(keyId),
...options,
});
}

export function sendDeleteOneEnrollmentAPIKey(keyId: string, options?: RequestOptions) {
return sendRequest({
method: 'delete',
path: enrollmentAPIKeyRouteService.getDeletePath(keyId),
...options,
});
}

export function useGetEnrollmentAPIKeys(
query: GetEnrollmentAPIKeysRequest['query'],
options?: RequestOptions
) {
return useRequest<GetEnrollmentAPIKeysResponse>({
method: 'get',
path: enrollmentAPIKeyRouteService.getListPath(),
query,
...options,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ export const ConfigYamlView = memo<{ config: AgentConfig }>(({ config }) => {
const core = useCore();

const fullConfigRequest = useGetOneAgentConfigFull(config.id);
const apiKeysRequest = useGetEnrollmentAPIKeys();
const apiKeysRequest = useGetEnrollmentAPIKeys({
page: 1,
perPage: 1000,
});
const apiKeyRequest = useGetOneEnrollmentAPIKey(apiKeysRequest.data?.list?.[0]?.id as string);

if (fullConfigRequest.isLoading && !fullConfigRequest.data) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import {
} from '../../../../../hooks';

export function useEnrollmentApiKeys(pagination: Pagination) {
const request = useGetEnrollmentAPIKeys();
const request = useGetEnrollmentAPIKeys({
page: pagination.currentPage,
perPage: pagination.pageSize,
});

return {
data: request.data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useState, useCallback } from 'react';
import styled, { CSSProperties } from 'styled-components';
import {
EuiBasicTable,
EuiButton,
Expand All @@ -17,21 +16,16 @@ import {
EuiLink,
EuiPopover,
EuiSpacer,
EuiSwitch,
EuiText,
EuiTitle,
EuiStat,
EuiI18nNumber,
EuiHealth,
EuiButtonIcon,
EuiContextMenuPanel,
EuiContextMenuItem,
EuiIcon,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, FormattedRelative } from '@kbn/i18n/react';
import { CSSProperties } from 'styled-components';
import { AgentEnrollmentFlyout } from './components';
import { WithHeaderLayout } from '../../../layouts';
import { Agent } from '../../../types';
import {
usePagination,
Expand All @@ -45,17 +39,9 @@ import { ConnectedLink } from '../components';
import { SearchBar } from '../../../components/search_bar';
import { AgentHealth } from '../components/agent_health';
import { AgentUnenrollProvider } from '../components/agent_unenroll_provider';
import { DonutChart } from './components/donut_chart';
import { useGetAgentStatus } from '../../agent_config/details_page/hooks';
import { AgentStatusKueryHelper } from '../../../services';
import { FLEET_AGENT_DETAIL_PATH, AGENT_CONFIG_DETAILS_PATH } from '../../../constants';

const Divider = styled.div`
width: 0;
height: 100%;
border-left: ${props => props.theme.eui.euiBorderThin};
height: 45px;
`;
const NO_WRAP_TRUNCATE_STYLE: CSSProperties = Object.freeze({
overflow: 'hidden',
textOverflow: 'ellipsis',
Expand Down Expand Up @@ -204,11 +190,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
.join(' or ');
}

const agentStatusRequest = useGetAgentStatus(undefined, {
pollIntervalMs: REFRESH_INTERVAL_MS,
});
const agentStatus = agentStatusRequest.data?.results;

const agentsRequest = useGetAgents(
{
page: pagination.currentPage,
Expand Down Expand Up @@ -399,154 +380,15 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
}
/>
);
const headerRightColumn = (
<EuiFlexGroup justifyContent={'flexEnd'} direction="row">
<EuiFlexItem grow={false}>
<EuiStat
titleSize="xs"
textAlign="right"
title={<EuiI18nNumber value={agentStatus?.total ?? 0} />}
description={i18n.translate('xpack.ingestManager.agentListStatus.totalLabel', {
defaultMessage: 'Agents',
})}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<DonutChart
width={40}
height={40}
data={{
online: agentStatus?.online || 0,
offline: agentStatus?.offline || 0,
error: agentStatus?.error || 0,
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiStat
textAlign="right"
titleSize="xs"
title={
<EuiHealth color="success">
{' '}
<EuiI18nNumber value={agentStatus?.online ?? 0} />
</EuiHealth>
}
description={i18n.translate('xpack.ingestManager.agentListStatus.onlineLabel', {
defaultMessage: 'Online',
})}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiStat
textAlign="right"
titleSize="xs"
title={<EuiI18nNumber value={agentStatus?.offline ?? 0} />}
description={i18n.translate('xpack.ingestManager.agentListStatus.offlineLabel', {
defaultMessage: 'Offline',
})}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiStat
textAlign="right"
titleSize="xs"
title={<EuiI18nNumber value={agentStatus?.error ?? 0} />}
description={i18n.translate('xpack.ingestManager.agentListStatus.errorLabel', {
defaultMessage: 'Error',
})}
/>
</EuiFlexItem>
{hasWriteCapabilites && (
<>
<EuiFlexItem grow={false}>
<Divider />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton fill iconType="plusInCircle" onClick={() => setIsEnrollmentFlyoutOpen(true)}>
<FormattedMessage
id="xpack.ingestManager.agentList.enrollButton"
defaultMessage="Enroll new agents"
/>
</EuiButton>
</EuiFlexItem>
</>
)}
</EuiFlexGroup>
);
const headerLeftColumn = (
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem>
<EuiText>
<h1>
<FormattedMessage id="xpack.ingestManager.fleet.pageTitle" defaultMessage="Fleet" />
</h1>
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiText color="subdued">
<p>
<FormattedMessage
id="xpack.ingestManager.fleet.pageSubtitle"
defaultMessage="Manage and deploy configuration updates to a group of agents of any size."
/>
</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);

return (
<WithHeaderLayout
leftColumn={headerLeftColumn}
rightColumn={headerRightColumn}
tabs={[
{
id: 'agents',
name: 'Agents',
isSelected: true,
},
{
id: 'enrollment_keys',
name: 'Enrollment keys',
},
]}
>
<>
{isEnrollmentFlyoutOpen ? (
<AgentEnrollmentFlyout
agentConfigs={agentConfigs}
onClose={() => setIsEnrollmentFlyoutOpen(false)}
/>
) : null}
<EuiTitle size="l">
<h1>
<FormattedMessage id="xpack.ingestManager.agentList.pageTitle" defaultMessage="Agents" />
</h1>
</EuiTitle>
<EuiSpacer size="s" />
<EuiFlexGroup alignItems={'center'} justifyContent={'spaceBetween'}>
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<EuiText color="subdued">
<FormattedMessage
id="xpack.ingestManager.agentList.pageDescription"
defaultMessage="Use agents to faciliate data collection for your Elastic stack."
/>
</EuiText>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSwitch
label={i18n.translate('xpack.ingestManager.agentList.showInactiveSwitchLabel', {
defaultMessage: 'Show inactive agents',
})}
checked={showInactive}
onChange={() => setShowInactive(!showInactive)}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />

<EuiFlexGroup alignItems={'center'}>
{selectedAgents.length ? (
<EuiFlexItem>
Expand Down Expand Up @@ -591,7 +433,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
) : null}
<EuiFlexItem grow={4}>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={3}>
<EuiFlexItem grow={6}>
<SearchBar
value={search}
onChange={newSearch => {
Expand All @@ -604,7 +446,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
fieldPrefix="agents"
/>
</EuiFlexItem>
<EuiFlexItem grow={1}>
<EuiFlexItem grow={2}>
<EuiFilterGroup>
<EuiPopover
ownFocus
Expand Down Expand Up @@ -685,6 +527,15 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
))}
</div>
</EuiPopover>
<EuiFilterButton
hasActiveFilters={showInactive}
onClick={() => setShowInactive(!showInactive)}
>
<FormattedMessage
id="xpack.ingestManager.agentList.showInactiveSwitchLabel"
defaultMessage="Show inactive"
/>
</EuiFilterButton>
</EuiFilterGroup>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down Expand Up @@ -747,6 +598,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
setPagination(newPagination);
}}
/>
</WithHeaderLayout>
</>
);
};
Loading