Skip to content

Commit

Permalink
[Fleet] Enrollment list page (#61346)
Browse files Browse the repository at this point in the history
  • Loading branch information
nchaulet authored Apr 1, 2020
1 parent 70fcaaa commit 36ff703
Show file tree
Hide file tree
Showing 13 changed files with 800 additions and 175 deletions.
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

0 comments on commit 36ff703

Please sign in to comment.