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

[APM] Link to Fleet APM Server Configuration when managed by Elastic Agent w/Fleet #100816

Merged
merged 37 commits into from
Jun 24, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
515c1c9
Register tutorial on APM plugin
cauemarcondes May 26, 2021
25fec3f
using files from apm
cauemarcondes May 26, 2021
3afb580
removing tutorial from apm_oss
cauemarcondes May 26, 2021
7195da0
removing export
cauemarcondes May 27, 2021
e38e390
fixing i18n
cauemarcondes May 27, 2021
92d34b1
Merge branch 'master' into apm-moving-tutorial
kibanamachine May 27, 2021
3f484a7
adding fleet section
cauemarcondes May 27, 2021
07f91b5
adding fleet information on APM tutorial
cauemarcondes May 31, 2021
23695bc
Merge branch 'master' of github.com:elastic/kibana into apm-fleet-tut…
cauemarcondes May 31, 2021
e854f34
adding fleet typing
cauemarcondes May 31, 2021
2a00cc7
fixing i18n
cauemarcondes May 31, 2021
0f0f458
adding fleet information on APM tutorial
cauemarcondes Jun 1, 2021
2f81e72
checks apm fleet integration when pushing button
cauemarcondes Jun 1, 2021
66b9351
adding fleet information on APM tutorial
cauemarcondes Jun 2, 2021
9990697
Merge branch 'master' into apm-fleet-tutorial
kibanamachine Jun 4, 2021
6b326e8
Merge branch 'master' into apm-fleet-tutorial
kibanamachine Jun 7, 2021
71a43d8
Merge branch 'master' of github.com:elastic/kibana into apm-fleet-tut…
cauemarcondes Jun 9, 2021
831e2c0
Merge branch 'apm-fleet-tutorial' of github.com:cauemarcondes/kibana …
cauemarcondes Jun 9, 2021
2c4112c
refactoring
cauemarcondes Jun 9, 2021
35c5672
registering status check callback
cauemarcondes Jun 10, 2021
7dadd85
addin custom component registration function
cauemarcondes Jun 10, 2021
ba03e53
fixing TS issue
cauemarcondes Jun 10, 2021
610aebb
Merge branch 'master' into apm-fleet-tutorial
kibanamachine Jun 14, 2021
1746467
addressing PR comments
cauemarcondes Jun 14, 2021
82e61eb
fixing tests
cauemarcondes Jun 14, 2021
c051953
adding i18n
cauemarcondes Jun 15, 2021
7e441d2
fixing issues
cauemarcondes Jun 15, 2021
f5c3464
Merge branch 'master' into apm-fleet-tutorial
kibanamachine Jun 16, 2021
5dddd48
adding unit test
cauemarcondes Jun 21, 2021
e9a4463
Merge branch 'master' of github.com:elastic/kibana into apm-fleet-tut…
cauemarcondes Jun 21, 2021
496f713
adding unit test
cauemarcondes Jun 21, 2021
2087a9d
addressing PR comments
cauemarcondes Jun 21, 2021
75e6e28
fixing TS issue
cauemarcondes Jun 22, 2021
98f89a1
Merge branch 'master' of github.com:elastic/kibana into apm-fleet-tut…
cauemarcondes Jun 22, 2021
157d7a4
moving tutorial to a common directory
cauemarcondes Jun 23, 2021
d7315f5
Merge branch 'master' into apm-fleet-tutorial
kibanamachine Jun 23, 2021
7795d73
Merge branch 'master' into apm-fleet-tutorial
kibanamachine Jun 24, 2021
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
2 changes: 2 additions & 0 deletions src/plugins/home/common/instruction_variant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const INSTRUCTION_VARIANT = {
DOTNET: 'dotnet',
LINUX: 'linux',
PHP: 'php',
FLEET: 'fleet',
};

const DISPLAY_MAP = {
Expand All @@ -44,6 +45,7 @@ const DISPLAY_MAP = {
[INSTRUCTION_VARIANT.DOTNET]: '.NET',
[INSTRUCTION_VARIANT.LINUX]: 'Linux',
[INSTRUCTION_VARIANT.PHP]: 'PHP',
[INSTRUCTION_VARIANT.FLEET]: 'Elastic APM (beta) in Fleet',
cauemarcondes marked this conversation as resolved.
Show resolved Hide resolved
};

/**
Expand Down
10 changes: 6 additions & 4 deletions src/plugins/home/public/application/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { i18n } from '@kbn/i18n';
import { ScopedHistory, CoreStart } from 'kibana/public';
import { KibanaContextProvider } from '../../../kibana_react/public';
import { KibanaContextProvider, RedirectAppLinks } from '../../../kibana_react/public';
// @ts-ignore
import { HomeApp } from './components/home_app';
import { getServices } from './kibana_services';
Expand Down Expand Up @@ -44,9 +44,11 @@ export const renderApp = async (
});

render(
<KibanaContextProvider services={{ ...coreStart }}>
<HomeApp directories={directories} solutions={solutions} />
</KibanaContextProvider>,
<RedirectAppLinks application={coreStart.application}>
<KibanaContextProvider services={{ ...coreStart }}>
<HomeApp directories={directories} solutions={solutions} />
</KibanaContextProvider>
</RedirectAppLinks>,
element
);

Expand Down
112 changes: 112 additions & 0 deletions src/plugins/home/public/application/components/apm_fleet/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import {
EuiButton,
EuiCard,
EuiFlexGroup,
EuiFlexItem,
EuiImage,
EuiLoadingSpinner,
EuiPanel,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { getServices } from '../../kibana_services';

const CentralizedContainer = styled.div`
display: flex;
justify-content: center;
align-items: center;
`;

interface APIResponse {
hasData: boolean;
}

export function APMFleet() {
const { http, getBasePath, uiSettings } = getServices();
const isDarkTheme = uiSettings.get('theme:darkMode');
const basePath = getBasePath();
const [data, setData] = useState<APIResponse | undefined>();
const [isLoading, setIsLoading] = useState(false);

useEffect(() => {
async function fetchData() {
setIsLoading(true);
try {
const response = await http.get('/api/apm/fleet/has_data');
setData(response as APIResponse);
cauemarcondes marked this conversation as resolved.
Show resolved Hide resolved
} catch (e) {
// eslint-disable-next-line no-console
console.error('Error while fetching fleet details.', e);
}
setIsLoading(false);
}
fetchData();
}, [http]);

if (isLoading) {
return (
<CentralizedContainer>
<EuiLoadingSpinner />
</CentralizedContainer>
);
}
// When APM integration is enable in Fleet
if (data?.hasData) {
return (
<EuiButton iconType="gear" fill href={`${basePath}/app/fleet#/policies`}>
{i18n.translate('home.apm.tutorial.apmServer.fleet.manageApmIntegration.button', {
defaultMessage: 'Manage APM integration in Fleet',
})}
</EuiButton>
);
}
// When APM integration is not installed in Fleet or for some reason the API didn't work out
return (
<EuiPanel>
<EuiFlexGroup>
<EuiFlexItem grow={7}>
<EuiCard
display="plain"
textAlign="left"
title={i18n.translate('home.apm.tutorial.apmServer.fleet.title', {
defaultMessage: 'Elastic APM (beta) now available in Fleet!',
})}
description={i18n.translate('home.apm.tutorial.apmServer.fleet.message', {
defaultMessage:
'The APM integration installs Elasticsearch templates and Ingest Node pipelines for APM data.',
})}
footer={
<EuiButton
iconType="analyzeEvent"
color="secondary"
href={`${basePath}/app/fleet#/integrations/detail/apm-0.2.0/overview`}
>
{i18n.translate('home.apm.tutorial.apmServer.fleet.apmIntegration.button', {
defaultMessage: 'APM integration',
})}
</EuiButton>
}
/>
</EuiFlexItem>
<EuiFlexItem grow={3}>
<EuiImage
src={`${basePath}/plugins/apm/assets/${
isDarkTheme
? 'illustration_integrations_darkmode.svg'
: 'illustration_integrations_lightmode.svg'
}`}
alt="Illustration"
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,16 @@ import {

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

export function Instruction({ commands, paramValues, textPost, textPre, replaceTemplateStrings }) {
import { APMFleet } from '../apm_fleet';

export function Instruction({
commands,
paramValues,
textPost,
textPre,
replaceTemplateStrings,
customComponent,
}) {
let pre;
if (textPre) {
pre = <Content text={replaceTemplateStrings(textPre)} />;
Expand All @@ -36,6 +45,10 @@ export function Instruction({ commands, paramValues, textPost, textPre, replaceT
</div>
);
}
let custom;
if (customComponent === 'apm_fleet') {
custom = <APMFleet />;
}
cauemarcondes marked this conversation as resolved.
Show resolved Hide resolved

let copyButton;
let commandBlock;
Expand Down Expand Up @@ -79,6 +92,8 @@ export function Instruction({ commands, paramValues, textPost, textPre, replaceT

{post}

{custom}

<EuiSpacer />
</div>
);
Expand All @@ -90,4 +105,5 @@ Instruction.propTypes = {
textPost: PropTypes.string,
textPre: PropTypes.string,
replaceTemplateStrings: PropTypes.func.isRequired,
customComponent: PropTypes.string,
};
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class InstructionSetUi extends React.Component {
textPre={instruction.textPre}
textPost={instruction.textPost}
replaceTemplateStrings={this.props.replaceTemplateStrings}
customComponent={instruction.customComponent}
/>
);
return {
Expand Down Expand Up @@ -298,6 +299,7 @@ const statusCheckConfigShape = PropTypes.shape({
title: PropTypes.string,
text: PropTypes.string,
btnLabel: PropTypes.string,
customStatusCheck: PropTypes.string,
});

InstructionSetUi.propTypes = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,17 +171,31 @@ class TutorialUi extends React.Component {
checkInstructionSetStatus = async (instructionSetIndex) => {
const instructionSet = this.getInstructionSets()[instructionSetIndex];
const esHitsCheckConfig = _.get(instructionSet, `statusCheck.esHitsCheck`);
const customStatusCheck = _.get(instructionSet, `statusCheck.customStatusCheck`);

if (esHitsCheckConfig) {
const statusCheckState = await this.fetchEsHitsStatus(esHitsCheckConfig);
const [esHitsStatusCheck, apmFleetStatusCheck] = await Promise.all([
...(esHitsCheckConfig ? [this.fetchEsHitsStatus(esHitsCheckConfig)] : []),
...(customStatusCheck === 'apm-fleet-check' ? [this.fetchApmFleetStatus()] : []),
]);

this.setState((prevState) => ({
statusCheckStates: {
...prevState.statusCheckStates,
[instructionSetIndex]: statusCheckState,
},
}));
}
const nextStatusCheckState =
esHitsStatusCheck === StatusCheckStates.HAS_DATA ||
apmFleetStatusCheck === StatusCheckStates.HAS_DATA
? StatusCheckStates.HAS_DATA
: StatusCheckStates.NO_DATA;

this.setState((prevState) => ({
statusCheckStates: {
...prevState.statusCheckStates,
[instructionSetIndex]: nextStatusCheckState,
},
}));
};

fetchApmFleetStatus = async () => {
const { http } = getServices();
const response = await http.get('/api/apm/fleet/has_data');
return response?.hasData === true ? StatusCheckStates.HAS_DATA : StatusCheckStates.NO_DATA;
cauemarcondes marked this conversation as resolved.
Show resolved Hide resolved
};

/**
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/home/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ export const plugin = (initContext: PluginInitializerContext) => new HomeServerP

export { INSTRUCTION_VARIANT } from '../common/instruction_variant';
export { TutorialsCategory } from './services/tutorials';
export type { ArtifactsSchema } from './services/tutorials';
export type { ArtifactsSchema, TutorialSchema, InstructionSetSchema } from './services/tutorials';
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ const statusCheckSchema = schema.object({
index: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]),
query: schema.recordOf(schema.string(), schema.any()),
}),
customStatusCheck: schema.maybe(schema.string()),
});

const instructionSchema = schema.object({
title: schema.maybe(schema.string()),
textPre: schema.maybe(schema.string()),
commands: schema.maybe(schema.arrayOf(schema.string())),
textPost: schema.maybe(schema.string()),
customComponent: schema.maybe(schema.string()),
});
export type Instruction = TypeOf<typeof instructionSchema>;

Expand Down
5 changes: 3 additions & 2 deletions x-pack/plugins/apm/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"embeddable",
"infra",
"observability",
"ruleRegistry"
"ruleRegistry",
"fleet"
],
"optionalPlugins": [
"spaces",
Expand Down Expand Up @@ -42,4 +43,4 @@
"ml",
"observability"
]
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 14 additions & 12 deletions x-pack/plugins/apm/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,19 @@ export class APMPlugin
});
}

const resourcePlugins = mapValues(plugins, (value, key) => {
return {
setup: value,
start: () =>
core.getStartServices().then((services) => {
const [, pluginsStartContracts] = services;
return pluginsStartContracts[
key as keyof APMPluginStartDependencies
];
}),
};
}) as APMRouteHandlerResources['plugins'];

plugins.home?.tutorials.registerTutorial(
tutorialProvider({
isEnabled: this.currentConfig['xpack.apm.ui.enabled'],
Expand Down Expand Up @@ -195,18 +208,7 @@ export class APMPlugin
config: currentConfig,
repository: getGlobalApmServerRouteRepository(),
ruleDataClient,
plugins: mapValues(plugins, (value, key) => {
return {
setup: value,
start: () =>
core.getStartServices().then((services) => {
const [, pluginsStartContracts] = services;
return pluginsStartContracts[
key as keyof APMPluginStartDependencies
];
}),
};
}) as APMRouteHandlerResources['plugins'],
plugins: resourcePlugins,
});

const boundGetApmIndices = async () =>
Expand Down
29 changes: 29 additions & 0 deletions x-pack/plugins/apm/server/routes/fleet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 { createApmServerRoute } from './create_apm_server_route';
import { createApmServerRouteRepository } from './create_apm_server_route_repository';

const hasFleetDataRoute = createApmServerRoute({
endpoint: 'GET /api/apm/fleet/has_data',
options: { tags: [] },
handler: async (resources) => {
const { core } = resources.context;
const savedObjectsClient = core.savedObjects.client;
const fleetPluginStart = await resources.plugins.fleet.start();

const packagePolicies = await fleetPluginStart.packagePolicyService.list(
savedObjectsClient,
{ kuery: 'ingest-package-policies.package.name:apm' }
);
return { hasData: packagePolicies.total > 0 };
},
});

export const ApmFleetRouteRepository = createApmServerRouteRepository().add(
hasFleetDataRoute
);
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { customLinkRouteRepository } from './settings/custom_link';
import { traceRouteRepository } from './traces';
import { transactionRouteRepository } from './transactions';
import { APMRouteHandlerResources } from './typings';
import { ApmFleetRouteRepository } from './fleet';

const getTypedGlobalApmServerRouteRepository = () => {
const repository = createApmServerRouteRepository()
Expand All @@ -48,7 +49,8 @@ const getTypedGlobalApmServerRouteRepository = () => {
.merge(agentConfigurationRouteRepository)
.merge(anomalyDetectionRouteRepository)
.merge(apmIndicesRouteRepository)
.merge(customLinkRouteRepository);
.merge(customLinkRouteRepository)
.merge(ApmFleetRouteRepository);

return repository;
};
Expand Down
Loading