Skip to content

Commit

Permalink
[Fleet] [Feature Branch] Create Fleet _debug UI (#131322)
Browse files Browse the repository at this point in the history
* Bootstrap Fleet /_debug route + React Query

* [Fleet] [Debug UI] Implement "Agent Policy Debugger" UI (#131335)

* Add initial agent policy debugger module

* Fix clear button in agent policy select

* Implement deletion of selected policy

* Fix layout of combo-box/button

* Add searchable ID to agent policy labels

* Add description text to debugger module

* Fixup loading/error logic

* [Fleet] Saved Objects Debugger (#131370)

* saved objects debugger

* converted so names to combobox

* types fix

* extracted combo box component

* fixed error display

* [Fleet] [Debug UI] Implement "Integration Debugger" UI (#131354)

* Implement integrations debugger UI

* Clean up + add link to integration settings

* Add divider below integration debugger

* Clean up loading states

* Fix flex spacing for saved objects debugger

* [Fleet] Added fleet indices query to debug UI (#131395)

* fleet indices

* keeping the type and name combo close in saved objects

* fixed prettier

* removed useEffects, simplified use of useQuery (#131482)

* using different query for saved objects (#131491)

* [Fleet] [Debug UI] Implement "Preconfiguration debugger" UI (#131436)

* Implement preconfiguration debugger UI

* Add code block view

* Added missing newline

Co-authored-by: Julia Bardi <[email protected]>

* Prevent flicker in saved objects code block

* added links including health check report (#131503)

* added links including health check report

* experiment with accordion

Co-authored-by: Kibana Machine <[email protected]>

* Refactor panel rendering + danger zone callout

* Convert panels to all singular

* confirm modal for reset preconfig (#131596)

* Add icons to useful links + fix reset all -> title case

* Fix disabled health check link

* added orphaned policies api and to debug page (#131697)

* Language fixes around orphaned policy module

* Add some basic dev docs around the debugger

* increasing page load bundle limit slightly (#132690)

* Remove health check link as it's not implemented

* Fix agents link + disable reset all button when no preconfigured policies

* Update doc title on debug page

* Translate everything

* Remove delete orphaned endpoint + fix force flag in existing delete endpoint

* Fix type

* Add API integration tests for orphaned policies

Co-authored-by: Julia Bardi <[email protected]>
Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
3 people authored Jun 21, 2022
1 parent e1eb3db commit d8558fc
Show file tree
Hide file tree
Showing 27 changed files with 2,007 additions and 76 deletions.
2 changes: 2 additions & 0 deletions x-pack/plugins/fleet/common/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const PACKAGE_POLICY_API_ROUTES = {
DELETE_PATTERN: `${PACKAGE_POLICY_API_ROOT}/delete`,
UPGRADE_PATTERN: `${PACKAGE_POLICY_API_ROOT}/upgrade`,
DRYRUN_PATTERN: `${PACKAGE_POLICY_API_ROOT}/upgrade/dryrun`,
ORPHANED_INTEGRATION_POLICIES: `${INTERNAL_ROOT}/orphaned_integration_policies`,
};

// Agent policy API routes
Expand Down Expand Up @@ -92,6 +93,7 @@ export const SETTINGS_API_ROUTES = {

// App API routes
export const APP_API_ROUTES = {
HEALTH_CHECK_PATTERN: `${API_ROOT}/health_check`,
CHECK_PERMISSIONS_PATTERN: `${API_ROOT}/check-permissions`,
GENERATE_SERVICE_TOKEN_PATTERN: `${API_ROOT}/service_tokens`,
// deprecated since 8.0
Expand Down
13 changes: 13 additions & 0 deletions x-pack/plugins/fleet/common/services/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
SETTINGS_API_ROUTES,
APP_API_ROUTES,
K8S_API_ROUTES,
PRECONFIGURATION_API_ROUTES,
} from '../constants';

export const epmRouteService = {
Expand Down Expand Up @@ -105,6 +106,10 @@ export const packagePolicyRouteService = {
getDryRunPath: () => {
return PACKAGE_POLICY_API_ROUTES.DRYRUN_PATTERN;
},

getOrphanedIntegrationPoliciesPath: () => {
return PACKAGE_POLICY_API_ROUTES.ORPHANED_INTEGRATION_POLICIES;
},
};

export const agentPolicyRouteService = {
Expand Down Expand Up @@ -150,6 +155,14 @@ export const agentPolicyRouteService = {
getK8sFullDownloadPath: () => {
return K8S_API_ROUTES.K8S_DOWNLOAD_PATTERN;
},

getResetOnePreconfiguredAgentPolicyPath: (agentPolicyId: string) => {
return PRECONFIGURATION_API_ROUTES.RESET_ONE_PATTERN.replace(`{agentPolicyId}`, agentPolicyId);
},

getResetAllPreconfiguredAgentPolicyPath: () => {
return PRECONFIGURATION_API_ROUTES.RESET_PATTERN;
},
};

export const dataStreamRouteService = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export type UpdatePackagePolicyResponse = CreatePackagePolicyResponse;
export interface DeletePackagePoliciesRequest {
body: {
packagePolicyIds: string[];
force?: boolean;
};
}

Expand Down
27 changes: 27 additions & 0 deletions x-pack/plugins/fleet/dev_docs/fleet_debugger.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Fleet Debugger

Fleet includes a "debug" interface that provides some insight and data management capabilities around Fleet's underlying data. This interface can be used to diagnose issues, assist support engineers, and restore functionality to broken Fleet installations.

![Fleet Debugger UI Screenshot](https://user-images.githubusercontent.com/6766512/167193984-fcb100c4-729d-4a0b-ae64-2b280272da96.png)

## Accessing the Fleet debugger

The debugger is served at `<kibana>/app/fleet/_debug`. This page shares the same permissions requirement as other `/fleet` pages, so you'll need a user with the `fleet.all` permission.

## Using the Fleet debugger

The Fleet debugger provides debugger modules for the following Fleet data:

- Agent Policies
- Installed Integrations
- Saved Objects
- System Indices
- Preconfiguration
- "Orphaned" Integration Policies

Each module contains an explanation of its functionality and behavior, but generally the goal of each module is to

1. Provide visibility into the underlying data for the given object
1. Provide cleanup/reset functionality in order to recover from malformed data that may be preventing normal usage of Fleet

The debugger should be used when possible to assist with SDH's when we request things like a copy/paste of a given policy object or for some cleanup operation to be run via `cURL`. As common SDH tasks are identified, the debugger should be expanded to suit the Fleet UI team's and the support team's needs.
5 changes: 5 additions & 0 deletions x-pack/plugins/fleet/public/applications/fleet/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import { MissingESRequirementsPage } from './sections/agents/agent_requirements_
import { CreatePackagePolicyPage } from './sections/agent_policy/create_package_policy_page';
import { EnrollmentTokenListPage } from './sections/agents/enrollment_token_list_page';
import { SettingsApp } from './sections/settings';
import { DebugPage } from './sections/debug';

const FEEDBACK_URL = 'https://ela.st/fleet-feedback';

Expand Down Expand Up @@ -326,6 +327,10 @@ export const AppRoutes = memo(
<SettingsApp />
</Route>

<Route path={FLEET_ROUTING_PATHS.debug}>
<DebugPage />
</Route>

{/* TODO: Move this route to the Integrations app */}
<Route path={FLEET_ROUTING_PATHS.add_integration_to_policy}>
<CreatePackagePolicyPage />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* 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 {
EuiButton,
EuiCallOut,
EuiCode,
EuiComboBox,
EuiFlexGroup,
EuiFlexItem,
EuiLink,
EuiSpacer,
EuiText,
} from '@elastic/eui';
import { useQuery } from 'react-query';

import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';

import { sendGetAgentPolicies, useLink } from '../../../hooks';
import { SO_SEARCH_LIMIT } from '../../../constants';

import { policyHasFleetServer } from '../../../services';
import type { AgentPolicy } from '../../../types';
import { AgentPolicyDeleteProvider } from '../../agent_policy/components';

import { queryClient } from '..';

import { CodeBlock } from './code_block';

const fetchAgentPolicies = async () => {
const response = await sendGetAgentPolicies({
full: true,
perPage: SO_SEARCH_LIMIT,
sortOrder: 'asc',
});

if (response.error) {
throw new Error(response.error.message);
}

return response;
};

export const AgentPolicyDebugger: React.FunctionComponent = () => {
const { getHref } = useLink();
const [selectedPolicyId, setSelectedPolicyId] = useState<string>();

// TODO: Depending on the number of agent policies, this might need to be switched to
// `useInfinite` query with an infinite scrolling approach in the dropdown options.
const { data, status } = useQuery('debug-agent-policies', fetchAgentPolicies);

const agentPolicies = data?.data?.items ?? [];
const comboBoxOptions = agentPolicies.map((policy) => ({
label: `${policy.name} - ${policy.id}`,
value: policy.id,
}));

const selectedOptions = selectedPolicyId
? [comboBoxOptions.find((option) => option.value === selectedPolicyId)!]
: [];

const selectedAgentPolicy = agentPolicies.find((policy) => policy.id === selectedPolicyId);

const onDelete = () => {
setSelectedPolicyId(undefined);
queryClient.invalidateQueries('debug-agent-policies');
};

if (status === 'error') {
return (
<EuiCallOut title="Error" color="danger">
<FormattedMessage
id="xpack.fleet.debug.agentPolicyDebugger.fetchError"
defaultMessage="Error fetching Agent Policies"
/>
</EuiCallOut>
);
}

return (
<>
<EuiText grow={false}>
<p>
<FormattedMessage
id="xpack.fleet.debug.agentPolicyDebugger.description"
defaultMessage="Search for an Agent Policy using its name or {codeId} value. Use the code block below to diagnose any potential issues with the policy's configuration."
values={{ codeId: <EuiCode>id</EuiCode> }}
/>
</p>
</EuiText>

<EuiSpacer size="m" />

<EuiFlexGroup alignItems="center" justifyContent="flexStart">
<EuiFlexItem
grow={false}
css={`
min-width: 600px;
`}
>
<EuiComboBox
aria-label={i18n.translate('xpack.fleet.debug.agentPolicyDebugger.selectLabel', {
defaultMessage: 'Select an Agent Policy',
})}
placeholder={i18n.translate('xpack.fleet.debug.agentPolicyDebugger.selectLabel', {
defaultMessage: 'Select an Agent Policy',
})}
fullWidth
options={comboBoxOptions}
singleSelection={{ asPlainText: true }}
selectedOptions={selectedOptions}
isLoading={status === 'loading'}
onChange={(newSelectedOptions) => {
// Handle "clear" action
if (!newSelectedOptions.length) {
setSelectedPolicyId(undefined);
} else {
setSelectedPolicyId(newSelectedOptions[0].value);
}
}}
/>
</EuiFlexItem>

{selectedPolicyId && (
<AgentPolicyDeleteProvider
hasFleetServer={policyHasFleetServer(selectedAgentPolicy as AgentPolicy)}
>
{(deleteAgentPolicyPrompt) => {
return (
<EuiFlexItem grow={false}>
<div>
<EuiButton
color="danger"
onClick={() => deleteAgentPolicyPrompt(selectedPolicyId, onDelete)}
>
<FormattedMessage
id="xpack.fleet.policyForm.deletePolicyActionText"
defaultMessage="Delete policy"
/>
</EuiButton>
</div>
</EuiFlexItem>
);
}}
</AgentPolicyDeleteProvider>
)}
</EuiFlexGroup>

{selectedPolicyId && (
<>
<EuiSpacer size="m" />

<EuiLink target="_blank" href={getHref('policy_details', { policyId: selectedPolicyId })}>
<FormattedMessage
id="xpack.fleet.debug.agentPolicyDebugger.viewAgentPolicyLink"
defaultMessage="View Agent Policy in Fleet UI"
/>
</EuiLink>

<EuiSpacer size="m" />

<CodeBlock value={JSON.stringify(selectedAgentPolicy, null, 2)} />
</>
)}
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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 { CodeEditor } from '@kbn/kibana-react-plugin/public';

/**
* A read-only code block with various default settings suitable for displaying API responses, etc
*/
export const CodeBlock: React.FunctionComponent<{ value: string }> = ({ value }) => {
return (
<CodeEditor
isCopyable
languageId=""
height="600px"
width="100%"
options={{
minimap: {
enabled: false,
},
scrollBeyondLastLine: false,
readOnly: true,
tabSize: 2,
lineNumbers: 'off',
lineNumbersMinChars: 0,
glyphMargin: false,
lineDecorationsWidth: 0,
overviewRulerBorder: false,
}}
value={value}
/>
);
};
Loading

0 comments on commit d8558fc

Please sign in to comment.