Skip to content

Commit f0a0987

Browse files
committed
First pass at agent enrollment flyout with placeholders
1 parent 2f345dc commit f0a0987

6 files changed

Lines changed: 356 additions & 3 deletions

File tree

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import React, { useState, Fragment } from 'react';
7+
import { FormattedMessage } from '@kbn/i18n/react';
8+
import {
9+
EuiFlyout,
10+
EuiFlyoutHeader,
11+
EuiTitle,
12+
EuiFlyoutBody,
13+
EuiFlyoutFooter,
14+
EuiFlexGroup,
15+
EuiFlexItem,
16+
EuiButtonEmpty,
17+
EuiButton,
18+
EuiSpacer,
19+
EuiText,
20+
EuiFilterGroup,
21+
EuiFilterButton,
22+
} from '@elastic/eui';
23+
import { FrontendLibs } from '../../../lib/types';
24+
import {
25+
ShellEnrollmentInstructions,
26+
ContainerEnrollmentInstructions,
27+
ToolsEnrollmentInstructions,
28+
} from './enrollment_instructions';
29+
30+
interface RouterProps {
31+
libs: FrontendLibs;
32+
onClose: () => void;
33+
}
34+
35+
export const AgentEnrollmentFlyout: React.SFC<RouterProps> = ({ libs, onClose }) => {
36+
const [quickInstallType, setQuickInstallType] = useState<'shell' | 'container' | 'tools'>(
37+
'shell'
38+
);
39+
40+
const renderHeader = () => (
41+
<EuiFlyoutHeader hasBorder aria-labelledby="FleetAgentEnrollmentFlyoutTitle">
42+
<EuiTitle size="m">
43+
<h2 id="FleetAgentEnrollmentFlyoutTitle">
44+
<FormattedMessage
45+
id="xpack.fleet.agentEnrollment.flyoutTitle"
46+
defaultMessage="Install agent"
47+
/>
48+
</h2>
49+
</EuiTitle>
50+
<EuiSpacer size="s" />
51+
<EuiText color="subdued">
52+
<p>
53+
<FormattedMessage
54+
id="xpack.fleet.agentEnrollment.flyoutDescription"
55+
defaultMessage="Enroll a new agent into Fleet."
56+
/>
57+
</p>
58+
</EuiText>
59+
</EuiFlyoutHeader>
60+
);
61+
62+
const renderInstructions = () => (
63+
<Fragment>
64+
<EuiText>
65+
<h5>
66+
<FormattedMessage
67+
id="xpack.fleet.agentEnrollment.quickInstallTitle"
68+
defaultMessage="Quick installation"
69+
/>
70+
</h5>
71+
</EuiText>
72+
<EuiSpacer size="s" />
73+
<EuiFilterGroup>
74+
<EuiFilterButton
75+
hasActiveFilters={quickInstallType === 'shell'}
76+
onClick={() => setQuickInstallType('shell')}
77+
>
78+
<FormattedMessage
79+
id="xpack.fleet.agentEnrollment.shellInstallButtonText"
80+
defaultMessage="Shell"
81+
/>
82+
</EuiFilterButton>
83+
<EuiFilterButton
84+
hasActiveFilters={quickInstallType === 'container'}
85+
onClick={() => setQuickInstallType('container')}
86+
>
87+
<FormattedMessage
88+
id="xpack.fleet.agentEnrollment.containerInstallButtonText"
89+
defaultMessage="Container"
90+
/>
91+
</EuiFilterButton>
92+
<EuiFilterButton
93+
hasActiveFilters={quickInstallType === 'tools'}
94+
onClick={() => setQuickInstallType('tools')}
95+
>
96+
<FormattedMessage
97+
id="xpack.fleet.agentEnrollment.toolsInstallButtonText"
98+
defaultMessage="Tools"
99+
/>
100+
</EuiFilterButton>
101+
</EuiFilterGroup>
102+
<EuiSpacer size="m" />
103+
{quickInstallType === 'shell' ? (
104+
<ShellEnrollmentInstructions
105+
kibanaUrl={`${window.location.origin}${libs.framework.info.basePath}`}
106+
/>
107+
) : null}
108+
{quickInstallType === 'container' ? <ContainerEnrollmentInstructions /> : null}
109+
{quickInstallType === 'tools' ? <ToolsEnrollmentInstructions /> : null}
110+
</Fragment>
111+
);
112+
113+
const renderBody = () => <EuiFlyoutBody>{renderInstructions()}</EuiFlyoutBody>;
114+
115+
const renderFooter = () => (
116+
<EuiFlyoutFooter>
117+
<EuiFlexGroup justifyContent="spaceBetween">
118+
<EuiFlexItem grow={false}>
119+
<EuiButtonEmpty iconType="cross" onClick={onClose} flush="left">
120+
Close
121+
</EuiButtonEmpty>
122+
</EuiFlexItem>
123+
<EuiFlexItem grow={false}>
124+
<EuiButton fill onClick={onClose}>
125+
Continue
126+
</EuiButton>
127+
</EuiFlexItem>
128+
</EuiFlexGroup>
129+
</EuiFlyoutFooter>
130+
);
131+
132+
return (
133+
<EuiFlyout onClose={onClose} size="m" maxWidth={650}>
134+
{renderHeader()}
135+
{renderBody()}
136+
{renderFooter()}
137+
</EuiFlyout>
138+
);
139+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import React from 'react';
7+
8+
export const ContainerEnrollmentInstructions: React.SFC = () => {
9+
return <div>Container instructions</div>;
10+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import React from 'react';
7+
import { EuiSteps, EuiText, EuiCodeBlock } from '@elastic/eui';
8+
9+
export { ShellEnrollmentInstructions } from './shell';
10+
export { ContainerEnrollmentInstructions } from './container';
11+
export { ToolsEnrollmentInstructions } from './tools';
12+
13+
export type ManualEnrollmentInstructions = Array<{
14+
title: string;
15+
textPre?: string;
16+
commands?: string[];
17+
}>;
18+
19+
export const ManualEnrollmentSteps: React.SFC<{ instructions: ManualEnrollmentInstructions }> = ({
20+
instructions,
21+
}) => (
22+
<EuiSteps
23+
steps={instructions.map(({ title, textPre, commands }) => ({
24+
title,
25+
children: (
26+
<EuiText size="s">
27+
{textPre ? <p>{textPre}</p> : null}
28+
{commands ? <EuiCodeBlock language="bash">{commands.join(`\n`)}</EuiCodeBlock> : null}
29+
</EuiText>
30+
),
31+
}))}
32+
/>
33+
);
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import React, { useState, Fragment } from 'react';
7+
import {
8+
EuiFieldText,
9+
EuiCopy,
10+
EuiButtonEmpty,
11+
EuiPopover,
12+
EuiSpacer,
13+
EuiContextMenuPanel,
14+
EuiContextMenuItem,
15+
} from '@elastic/eui';
16+
import { FormattedMessage } from '@kbn/i18n/react';
17+
import { i18n } from '@kbn/i18n';
18+
import { ManualEnrollmentInstructions, ManualEnrollmentSteps } from './';
19+
20+
// No need for i18n as these are platform names
21+
const PLATFORMS = {
22+
macos: 'macOS',
23+
windows: 'Windows',
24+
linux: 'Linux',
25+
};
26+
27+
// Manual instructions based on platform
28+
const PLATFORM_INSTRUCTIONS: {
29+
[key: string]: ManualEnrollmentInstructions;
30+
} = {
31+
macos: [
32+
{
33+
title: i18n.translate('xpack.fleet.agentEnrollment.typeShell.manualInstall.stepOneTitle', {
34+
defaultMessage: 'Download and install Elastic Agent',
35+
}),
36+
textPre: 'Lorem ipsum instructions here.',
37+
commands: [
38+
'curl -L -O https://artifacts.elastic.co/downloads/some-file-to-download.tar.gz',
39+
'tar xzvf some-file-to-download.tar.gz',
40+
'cd some-file-to-download/',
41+
],
42+
},
43+
{
44+
title: i18n.translate('xpack.fleet.agentEnrollment.typeShell.manualInstall.stepTwoTitle', {
45+
defaultMessage: 'Edit the configuration',
46+
}),
47+
textPre: 'Modify the configuration file to set the connection information:',
48+
commands: [
49+
'output.elasticsearch:',
50+
' hosts: ["<es_url>"]',
51+
' username: "elastic"',
52+
' password: "<password>"',
53+
'setup.kibana:',
54+
' host: "<kibana_url>"',
55+
],
56+
},
57+
{
58+
title: i18n.translate('xpack.fleet.agentEnrollment.typeShell.manualInstall.stepThreeTitle', {
59+
defaultMessage: 'Start the agent',
60+
}),
61+
commands: ['./somefile setup', './somefile -e'],
62+
},
63+
],
64+
};
65+
66+
interface Props {
67+
kibanaUrl: string;
68+
}
69+
70+
export const ShellEnrollmentInstructions: React.SFC<Props> = ({ kibanaUrl }) => {
71+
// Platform state
72+
const [currentPlatform, setCurrentPlatform] = useState<keyof typeof PLATFORMS>('macos');
73+
const [isPlatformOptionsOpen, setIsPlatformOptionsOpen] = useState<boolean>(false);
74+
const [isManualInstallationOpen, setIsManualInstallationOpen] = useState<boolean>(false);
75+
76+
// Build quick installation command
77+
const quickInstallInstructions = `curl ${kibanaUrl}/api/fleet/install/${currentPlatform} | bash`;
78+
79+
return (
80+
<Fragment>
81+
<EuiFieldText
82+
readOnly
83+
value={quickInstallInstructions}
84+
fullWidth
85+
prepend={
86+
<EuiPopover
87+
button={
88+
<EuiButtonEmpty
89+
size="xs"
90+
iconType="arrowDown"
91+
iconSide="right"
92+
onClick={() => setIsPlatformOptionsOpen(true)}
93+
>
94+
{PLATFORMS[currentPlatform]}
95+
</EuiButtonEmpty>
96+
}
97+
isOpen={isPlatformOptionsOpen}
98+
closePopover={() => setIsPlatformOptionsOpen(false)}
99+
>
100+
<EuiContextMenuPanel
101+
items={Object.entries(PLATFORMS).map(([platform, name]) => (
102+
<EuiContextMenuItem
103+
key={platform}
104+
onClick={() => {
105+
setCurrentPlatform(platform as typeof currentPlatform);
106+
setIsPlatformOptionsOpen(false);
107+
}}
108+
>
109+
{name}
110+
</EuiContextMenuItem>
111+
))}
112+
/>
113+
</EuiPopover>
114+
}
115+
append={
116+
<EuiCopy textToCopy={quickInstallInstructions}>
117+
{copy => (
118+
<EuiButtonEmpty onClick={copy} color="primary" size="s">
119+
<FormattedMessage
120+
id="xpack.fleet.agentEnrollment.copyInstructionsButtonText"
121+
defaultMessage="copy"
122+
/>
123+
</EuiButtonEmpty>
124+
)}
125+
</EuiCopy>
126+
}
127+
/>
128+
129+
<EuiSpacer size="m" />
130+
131+
<EuiButtonEmpty
132+
onClick={() => setIsManualInstallationOpen(!isManualInstallationOpen)}
133+
iconType={isManualInstallationOpen ? 'arrowUp' : 'arrowDown'}
134+
iconSide="right"
135+
size="xs"
136+
flush="left"
137+
>
138+
<FormattedMessage
139+
id="xpack.fleet.agentEnrollment.manualInstructionsToggleLinkText"
140+
defaultMessage="Manual installation"
141+
/>
142+
</EuiButtonEmpty>
143+
144+
{isManualInstallationOpen ? (
145+
<Fragment>
146+
<EuiSpacer size="m" />
147+
<ManualEnrollmentSteps
148+
instructions={PLATFORM_INSTRUCTIONS[currentPlatform] || PLATFORM_INSTRUCTIONS.macos}
149+
/>
150+
</Fragment>
151+
) : null}
152+
</Fragment>
153+
);
154+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import React from 'react';
7+
8+
export const ToolsEnrollmentInstructions: React.SFC = () => {
9+
return <div>Tools instructions</div>;
10+
};

0 commit comments

Comments
 (0)