Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
c3be6f3
WIP new fleet server flyout
kpollich Mar 14, 2022
ad6ec87
WIP finish up quick start tab
kpollich Mar 15, 2022
4ed4f39
Refactor quick start steps into separate files:
kpollich Mar 15, 2022
a62ed8e
Initial refactor of existing fleet server instructions
kpollich Mar 16, 2022
b1e053b
Move quick start form return value to explicit type
kpollich Mar 17, 2022
dfefd46
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kpollich Mar 17, 2022
8200ced
Flesh out fleet server commands
kpollich Mar 18, 2022
54d5309
Fix translation error
kpollich Mar 18, 2022
f0a82f3
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kibanamachine Mar 18, 2022
95e76ba
Migrate on prem instructions component over to new file structure
kpollich Mar 21, 2022
3f736d9
Makes quick start tab actually create policy
kpollich Mar 21, 2022
ef9501d
Fix type errors
kpollich Mar 22, 2022
587a079
Fix missing hooks + update snapshots
kpollich Mar 22, 2022
ee9cad7
Fix paths in mocks
kpollich Mar 22, 2022
32c77f1
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kibanamachine Mar 22, 2022
d56dc36
Fix translations
kpollich Mar 23, 2022
861cab9
WIP test fixes
kpollich Mar 23, 2022
f2bdc98
Implement enabled/disabled state for new steps
kpollich Mar 23, 2022
e9c49b9
Fix cypress tests
kpollich Mar 24, 2022
4445ec0
Force re-render to get last test passing
kpollich Mar 24, 2022
55fa9af
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kibanamachine Mar 24, 2022
7803951
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kibanamachine Mar 29, 2022
0f1b433
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kpollich Apr 6, 2022
8b0b935
Fix failing tests
kpollich Apr 6, 2022
9efcc32
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kibanamachine Apr 7, 2022
6cb6159
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kpollich Apr 12, 2022
dbb8df6
Fix import errors after conflicts
kpollich Apr 12, 2022
4da4500
Fix snapshot tests
kpollich Apr 12, 2022
203d8d4
Use id instead of full policy for policy selector
kpollich Apr 12, 2022
6d00c52
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kibanamachine Apr 12, 2022
e8c49ec
Replace Fleet Server instructions w/ Advanced Tab contents
kpollich Apr 12, 2022
95aefe0
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kpollich Apr 13, 2022
b30c863
First pass at integrating add agent/fleet server flyouts
kpollich Apr 15, 2022
38e112f
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kpollich Apr 15, 2022
a14d03a
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kpollich Apr 18, 2022
180bcbb
Test commit
kpollich Apr 18, 2022
d3474f5
Fix imports
kpollich Apr 18, 2022
069fb53
Fix imports
kpollich Apr 18, 2022
cedcf9e
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 18, 2022
77f6694
Enforce https-only fleet server URL's + improve errors
kpollich Apr 18, 2022
463b9e6
Fix failing tests
kpollich Apr 18, 2022
14832b4
Fix fleet server command in quick start
kpollich Apr 19, 2022
2c13ec0
Show success state in Quick start when policy exists + use first flee…
kpollich Apr 19, 2022
8f6a0ca
Set initial service token value when Fleet Server policy ID is initia…
kpollich Apr 19, 2022
4025091
Generate service token instead of enrollment token
kpollich Apr 19, 2022
d9e5e76
Fix fleet server flyout opening from unhealthy callout
kpollich Apr 19, 2022
46fc10b
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kibanamachine Apr 19, 2022
7df14b3
Revert service token change + use EuiComboBox for fleet server host
kpollich Apr 20, 2022
558e9f9
Fix checks + use custom option text
kpollich Apr 20, 2022
aebd3f0
Move fleet server host combobox to component
kpollich Apr 20, 2022
0d51642
Use new combobox in advanced tab
kpollich Apr 20, 2022
6275fbe
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kpollich Apr 20, 2022
ebb4ab4
Fix translations
kpollich Apr 20, 2022
6a3fe45
Fix unused import
kpollich Apr 20, 2022
fccda94
Don't recreate quick start policy if it already exists
kpollich Apr 20, 2022
420586d
Actually use quick start policy fields 🙃
kpollich Apr 20, 2022
08edc05
Fix policy check
kpollich Apr 20, 2022
72e4f80
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kibanamachine Apr 25, 2022
3778c54
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kibanamachine Apr 25, 2022
42c43bd
Merge branch 'main' into 125640-redesign-fleet-server-flyout
kibanamachine Apr 25, 2022
cb0ee61
Update x-pack/plugins/fleet/public/applications/fleet/components/flee…
kpollich Apr 25, 2022
d7317a0
Update x-pack/plugins/fleet/public/applications/fleet/components/flee…
kpollich Apr 25, 2022
abe3524
Update x-pack/plugins/fleet/public/applications/fleet/components/flee…
kpollich Apr 25, 2022
1cfe1b7
Fix formatting issue
kpollich Apr 25, 2022
4d93d05
Clean up fleet server settings variable declaration per PR review
kpollich Apr 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions x-pack/plugins/fleet/.storybook/context/cloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ export const getCloud = ({ isCloudEnabled }: { isCloudEnabled: boolean }) => {
const cloud: CloudSetup = {
isCloudEnabled,
baseUrl: 'https://base.url',
cloudId: 'cloud-id',
cloudId: isCloudEnabled ? 'cloud-id' : undefined,
cname: 'found.io',
deploymentUrl: 'https://deployment.url',
deploymentUrl: isCloudEnabled ? 'https://deployment.url' : undefined,
organizationUrl: 'https://organization.url',
profileUrl: 'https://profile.url',
snapshotsUrl: 'https://snapshots.url',
Expand Down
56 changes: 53 additions & 3 deletions x-pack/plugins/fleet/.storybook/context/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ export const getHttp = (basepath = BASE_PATH) => {
serverBasePath: basepath,
},
get: (async (path: string, options: HttpFetchOptions) => {
action('get')(path, options);
action('get')(path, JSON.stringify(options));
// TODO: all of this needs revision, as it's far too clunky... but it works for now,
// with the few paths we're supporting.
if (path === '/api/fleet/agents/setup') {
if (!isReady) {
isReady = true;
return { isReady: false, missing_requirements: ['api_keys', 'fleet_server'] };
}
return { isInitialized: true, nonFatalErrors: [] };
return { isReady: true, isInitialized: true, nonFatalErrors: [], missing_requirements: [] };
}

if (path === '/api/fleet/epm/categories') {
Expand Down Expand Up @@ -79,9 +79,59 @@ export const getHttp = (basepath = BASE_PATH) => {
return { success: true };
}

action(path)('KP: UNSUPPORTED ROUTE');
if (path.match('/api/fleet/agent_policies')) {
return { items: [] };
}

if (path.match('/api/fleet/settings')) {
return { item: { fleet_server_hosts: [] } };
}

if (path.match('/api/fleet/outputs')) {
return {
items: [{ name: 'Default Output', is_default: true, hosts: ['https://test.es:9200'] }],
};
}

action(path)(`UNSUPPORTED ROUTE: GET ${path}`);
return {};
}) as HttpHandler,
post: (async (path: string, options: HttpFetchOptions) => {
action('post')(path, JSON.stringify(options));

if (path.match('/api/fleet/settings')) {
return { items: [] };
}

if (path.match('/api/fleet/service_tokens')) {
return {
name: 'test-token',
value: 'test-token-value',
};
}

if (path.match('/api/fleet/agent_policies')) {
return {
item: {
id: 'test-policy',
name: 'Test Policy',
namespace: 'default',
description: 'Test Policy',
monitoring_enabled: ['metrics'],
data_output_id: 'test-output',
monitoring_output_id: 'test-output',
status: 'active',
packagePolicies: ['test-package-policy'],
updated_on: new Date(),
updated_by: 'elastic',
revision: 0,
agents: 0,
},
};
}

action(path)(`UNSUPPORTED ROUTE: POST ${path}`);
}) as HttpHandler,
} as unknown as HttpStart;

return http;
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/fleet/.storybook/context/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const StorybookContext: React.FC<{ storyContext?: StoryContext }> = ({
chrome: getChrome(),
cloud: {
...getCloud({ isCloudEnabled }),
CloudContextProvider: () => <></>,
CloudContextProvider: ({ children }) => <>{children}</>,
},
customIntegrations: {
ContextProvider: getStorybookContextProvider(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,20 @@ describe('Edit settings', () => {

it('should update Fleet server hosts', () => {
cy.getBySel('editHostsBtn').click();
cy.get('[placeholder="Specify host URL"').type('http://localhost:8220');
cy.get('[placeholder="Specify host URL"').type('https://localhost:8220');

cy.intercept('/api/fleet/settings', {
item: { id: 'fleet-default-settings', fleet_server_hosts: ['http://localhost:8220'] },
item: { id: 'fleet-default-settings', fleet_server_hosts: ['https://localhost:8220'] },
});
cy.intercept('PUT', '/api/fleet/settings', {
fleet_server_hosts: ['http://localhost:8220'],
fleet_server_hosts: ['https://localhost:8220'],
}).as('updateSettings');

cy.getBySel('saveApplySettingsBtn').click();
cy.getBySel(CONFIRM_MODAL_BTN).click();

cy.wait('@updateSettings').then((interception) => {
expect(interception.request.body.fleet_server_hosts[0]).to.equal('http://localhost:8220');
expect(interception.request.body.fleet_server_hosts[0]).to.equal('https://localhost:8220');
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,15 @@ describe('Fleet startup', () => {
});

it('should create Fleet Server policy', () => {
cy.getBySel('fleetServerFlyoutTab-advanced').click();
cy.getBySel('createFleetServerPolicyBtn').click();

// verify policy is created and has fleet server and system package
verifyPolicy('Fleet Server policy 1', ['Fleet Server', 'System']);

navigateToTab(AGENTS_TAB);
cy.getBySel('fleetServerFlyoutTab-advanced').click();

// verify create button changed to dropdown
cy.getBySel('agentPolicyDropdown');

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* 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 { EuiSteps } from '@elastic/eui';
import React from 'react';

import { useAdvancedForm } from './hooks';

import {
getAddFleetServerHostStep,
getSelectAgentPolicyStep,
getGenerateServiceTokenStep,
getSetDeploymentModeStep,
getInstallFleetServerStep,
getConfirmFleetServerConnectionStep,
} from './steps';

export const AdvancedTab: React.FunctionComponent = () => {
const {
eligibleFleetServerPolicies,
refreshEligibleFleetServerPolicies,
fleetServerPolicyId,
setFleetServerPolicyId,
isFleetServerReady,
serviceToken,
isLoadingServiceToken,
generateServiceToken,
fleetServerHostForm,
deploymentMode,
setDeploymentMode,
} = useAdvancedForm();
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was a little on the fence about wrapping all of this data up into a single hook like this, and I could probably be convinced that it'd be better to simply compose multiple hooks in these "tab" files instead. The only real problem this solves is in managed_instructions.tsx where we import this same useAdvancedForm hook because we need almost all of this state again, so it made sense to simply ship the "composed" set of hooks all at once to prevent too much repetition between these two places.

However, I could see the argument that our hooks should be a little more specific than this, which is very coupled to a given UI.


const steps = [
getSelectAgentPolicyStep({
policyId: fleetServerPolicyId,
setPolicyId: setFleetServerPolicyId,
eligibleFleetServerPolicies,
refreshEligibleFleetServerPolicies,
}),
getSetDeploymentModeStep({
deploymentMode,
setDeploymentMode,
disabled: !Boolean(fleetServerPolicyId),
}),
getAddFleetServerHostStep({ fleetServerHostForm, disabled: !Boolean(fleetServerPolicyId) }),
getGenerateServiceTokenStep({
serviceToken,
generateServiceToken,
isLoadingServiceToken,
disabled: !Boolean(fleetServerHostForm.isFleetServerHostSubmitted),
}),
getInstallFleetServerStep({
isFleetServerReady,
serviceToken,
fleetServerHost: fleetServerHostForm.fleetServerHost,
fleetServerPolicyId,
disabled: !Boolean(serviceToken),
}),
getConfirmFleetServerConnectionStep({ isFleetServerReady, disabled: !Boolean(serviceToken) }),
];

return <EuiSteps steps={steps} className="eui-textLeft" />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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 } from 'react';
import type { EuiComboBoxOptionOption } from '@elastic/eui';
import { EuiComboBox, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';

interface Props {
fleetServerHost: string | undefined;
fleetServerHostSettings: string[];
isDisabled: boolean;
isInvalid: boolean;
onFleetServerHostChange: (host: string) => void;
}

export const FleetServerHostComboBox: React.FunctionComponent<Props> = ({
fleetServerHost,
fleetServerHostSettings,
isDisabled = false,
isInvalid = false,
onFleetServerHostChange,
}) => {
// Track options that are created inline
const [createdOptions, setCreatedOptions] = useState<string[]>([]);

const options = [...createdOptions, ...fleetServerHostSettings].map((option) => ({
label: option,
value: option,
}));

const handleChange = (selectedOptions: Array<EuiComboBoxOptionOption<string>>) => {
const host = selectedOptions[0].value ?? '';
onFleetServerHostChange(host);
};

const handleCreateOption = (option: string) => {
setCreatedOptions([...createdOptions, option]);
onFleetServerHostChange(option);
};

return (
<EuiComboBox<string>
fullWidth
isClearable={false}
singleSelection={{ asPlainText: true }}
placeholder="https://fleet-server-host.com:8220"
options={options}
customOptionText={i18n.translate(
'xpack.fleet.fleetServerSetup.addFleetServerHostCustomOptionText',
{
defaultMessage: 'Add {searchValuePlaceholder} as a new Fleet Server host',
values: { searchValuePlaceholder: '{searchValue}' },
}
)}
selectedOptions={fleetServerHost ? [{ label: fleetServerHost, value: fleetServerHost }] : []}
prepend={
<EuiText>
<FormattedMessage
id="xpack.fleet.fleetServerSetup.addFleetServerHostInputLabel"
defaultMessage="Fleet Server host"
/>
</EuiText>
}
noSuggestions={fleetServerHostSettings.length === 0}
data-test-subj="fleetServerHostInput"
isDisabled={isDisabled}
isInvalid={isInvalid}
onChange={handleChange}
onCreateOption={handleCreateOption}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* 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 './fleet_server_host_combobox';
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 { EuiButton } from '@elastic/eui';

import { FleetServerFlyout as FleetServerFlyoutComponent } from '.';

export const FleetServerFlyout = () => {
const [isOpen, setIsOpen] = React.useState(false);

return (
<div style={{ width: 900 }}>
<EuiButton size="m" fill color="primary" onClick={() => setIsOpen(true)}>
Show flyout
</EuiButton>
{isOpen && <FleetServerFlyoutComponent onClose={() => setIsOpen(false)} />}
</div>
);
};

FleetServerFlyout.args = {
isCloudEnabled: false,
};

export default {
component: FleetServerFlyout,
title: 'Sections/Fleet/Agents/Fleet Server Instructions/In Flyout',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* 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.
*/

// These hooks are tightly coupled to each tab of the Fleet server instructions component, and provide
// all necessary data to drive those UI's
export * from './use_advanced_form';
export * from './use_quick_start_form';

// These are individual hooks for one-off consumption. These are typically composed in the hooks above,
// but exported here to support individual usage.
export * from './use_wait_for_fleet_server';
export * from './use_select_fleet_server_policy';
export * from './use_service_token';
export * from './use_fleet_server_host';
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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 { useState } from 'react';

import type { DeploymentMode } from '../steps';

import { useFleetServerHost } from './use_fleet_server_host';
import { useSelectFleetServerPolicy } from './use_select_fleet_server_policy';
import { useServiceToken } from './use_service_token';
import { useWaitForFleetServer } from './use_wait_for_fleet_server';

/**
* Provides all data/state required for the "advanced" tab in the Fleet Server instructions/flyout
*/
export const useAdvancedForm = (defaultAgentPolicyId?: string) => {
const {
eligibleFleetServerPolicies,
refreshEligibleFleetServerPolicies,
fleetServerPolicyId,
setFleetServerPolicyId,
} = useSelectFleetServerPolicy(defaultAgentPolicyId);
const { isFleetServerReady } = useWaitForFleetServer();
const { serviceToken, isLoadingServiceToken, generateServiceToken } = useServiceToken();
const fleetServerHostForm = useFleetServerHost();

const [deploymentMode, setDeploymentMode] = useState<DeploymentMode>('quickstart');

return {
eligibleFleetServerPolicies,
refreshEligibleFleetServerPolicies,
fleetServerPolicyId,
setFleetServerPolicyId,
isFleetServerReady,
serviceToken,
isLoadingServiceToken,
generateServiceToken,
fleetServerHostForm,
deploymentMode,
setDeploymentMode,
};
};
Loading