Skip to content

Commit

Permalink
adding fleet information on APM tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
cauemarcondes committed May 31, 2021
1 parent 3f484a7 commit 07f91b5
Show file tree
Hide file tree
Showing 12 changed files with 242 additions and 93 deletions.
101 changes: 101 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,101 @@
/*
* 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,
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 { getBasePath } = getServices();
const basePath = getBasePath();
const [data, setData] = useState<APIResponse | undefined>();
const [isLoading, setIsLoading] = useState(false);

useEffect(() => {
async function fetchData() {
setIsLoading(true);
try {
const response = await fetch(`${basePath}/api/apm/fleet/hasData`);
setData((await response.json()) as APIResponse);
} catch (e) {
// eslint-disable-next-line no-console
console.error('Error while fetching fleet details.', e);
}
setIsLoading(false);
}
fetchData();
}, [basePath]);

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('xpack.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('xpack.apm.tutorial.apmServer.fleet.title', {
defaultMessage: 'Elastic APM (beta) now available in Fleet!',
})}
description={i18n.translate('xpack.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('xpack.apm.tutorial.apmServer.fleet.apmIntegration.button', {
defaultMessage: 'APM integration',
})}
</EuiButton>
}
/>
</EuiFlexItem>
<EuiFlexItem grow={3} style={{ background: 'red' }} />
</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 />;
}

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
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const instructionSchema = schema.object({
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"
]
}
}
45 changes: 24 additions & 21 deletions x-pack/plugins/apm/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,24 +101,38 @@ export class APMPlugin
kibanaVersion: this.initContext.env.packageInfo.version,
});
}

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.features.registerKibanaFeature(APM_FEATURE);

plugins.home?.tutorials.registerTutorial(
tutorialProvider({
isEnabled: this.currentConfig['xpack.apm.ui.enabled'],
indexPatternTitle: this.currentConfig['apm_oss.indexPattern'],
isEnabled: currentConfig['xpack.apm.ui.enabled'],
indexPatternTitle: currentConfig['apm_oss.indexPattern'],
cloud: plugins.cloud,
indices: {
errorIndices: this.currentConfig['apm_oss.errorIndices'],
metricsIndices: this.currentConfig['apm_oss.metricsIndices'],
onboardingIndices: this.currentConfig['apm_oss.onboardingIndices'],
sourcemapIndices: this.currentConfig['apm_oss.sourcemapIndices'],
transactionIndices: this.currentConfig['apm_oss.transactionIndices'],
errorIndices: currentConfig['apm_oss.errorIndices'],
metricsIndices: currentConfig['apm_oss.metricsIndices'],
onboardingIndices: currentConfig['apm_oss.onboardingIndices'],
sourcemapIndices: currentConfig['apm_oss.sourcemapIndices'],
transactionIndices: currentConfig['apm_oss.transactionIndices'],
},
basePath: core.http.basePath,
})
);

plugins.features.registerKibanaFeature(APM_FEATURE);

registerFeaturesUsage({ licensingPlugin: plugins.licensing });

const { ruleDataService } = plugins.ruleRegistry;
Expand Down Expand Up @@ -195,18 +209,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/hasData',
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
56 changes: 34 additions & 22 deletions x-pack/plugins/apm/server/tutorial/envs/elastic_cloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ export function createElasticCloudInstructions(
const apmServerUrl = cloudSetup?.apm.url;
const instructionSets = [];

if (!apmServerUrl) {
instructionSets.push(getApmServerInstructionSet(cloudSetup));
}
instructionSets.push(
getApmServerInstructionSet({ cloudSetup, hasApmServerUrl: !!apmServerUrl })
);

instructionSets.push(getApmAgentInstructionSet(cloudSetup));

Expand All @@ -43,29 +43,41 @@ export function createElasticCloudInstructions(
};
}

function getApmServerInstructionSet(
cloudSetup?: CloudSetup
): InstructionSetSchema {
const cloudId = cloudSetup?.cloudId;
function getApmServerInstructionSet({
cloudSetup,
hasApmServerUrl,
}: {
cloudSetup?: CloudSetup;
hasApmServerUrl: boolean;
}): InstructionSetSchema {
const instructionVariants: InstructionSetSchema['instructionVariants'] = [
{
id: INSTRUCTION_VARIANT.FLEET,
instructions: [{ customComponent: 'apm_fleet' }],
},
];

if (!hasApmServerUrl) {
instructionVariants.push({
id: INSTRUCTION_VARIANT.ESC,
instructions: [
{
title: 'Enable the APM Server in the ESS console',
textPre: i18n.translate('xpack.apm.tutorial.elasticCloud.textPre', {
defaultMessage:
'To enable the APM Server go to [the Elastic Cloud console](https://cloud.elastic.co/deployments?q={cloudId}) and enable APM in the deployment settings. Once enabled, refresh this page.',
values: { cloudId: cloudSetup?.cloudId },
}),
},
],
});
}

return {
title: i18n.translate('xpack.apm.tutorial.apmServer.title', {
defaultMessage: 'APM Server',
}),
instructionVariants: [
{
id: INSTRUCTION_VARIANT.ESC,
instructions: [
{
title: 'Enable the APM Server in the ESS console',
textPre: i18n.translate('xpack.apm.tutorial.elasticCloud.textPre', {
defaultMessage:
'To enable the APM Server go to [the Elastic Cloud console](https://cloud.elastic.co/deployments?q={cloudId}) and enable APM in the deployment settings. Once enabled, refresh this page.',
values: { cloudId },
}),
},
],
},
],
instructionVariants,
};
}

Expand Down
Loading

0 comments on commit 07f91b5

Please sign in to comment.