[APM] Make fleet plugin dependency optional (#104967)

* fixing tutorial when fleet plugin is disabled

* addressing PR comments
This commit is contained in:
Cauê Marcondes 2021-07-09 13:46:52 -04:00 committed by GitHub
parent 5b207d8484
commit d2ce8d5223
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 401 additions and 166 deletions

View file

@ -7,7 +7,6 @@
"data", "data",
"embeddable", "embeddable",
"features", "features",
"fleet",
"infra", "infra",
"licensing", "licensing",
"observability", "observability",
@ -24,11 +23,15 @@
"security", "security",
"spaces", "spaces",
"taskManager", "taskManager",
"usageCollection" "usageCollection",
"fleet"
], ],
"server": true, "server": true,
"ui": true, "ui": true,
"configPath": ["xpack", "apm"], "configPath": [
"xpack",
"apm"
],
"requiredBundles": [ "requiredBundles": [
"fleet", "fleet",
"home", "home",
@ -38,4 +41,4 @@
"ml", "ml",
"observability" "observability"
] ]
} }

View file

@ -74,7 +74,7 @@ export interface ApmPluginStartDeps {
ml?: MlPluginStart; ml?: MlPluginStart;
triggersActionsUi: TriggersAndActionsUIPublicPluginStart; triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
observability: ObservabilityPublicStart; observability: ObservabilityPublicStart;
fleet: FleetStart; fleet?: FleetStart;
} }
export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> { export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
@ -311,20 +311,21 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
} }
public start(core: CoreStart, plugins: ApmPluginStartDeps) { public start(core: CoreStart, plugins: ApmPluginStartDeps) {
const { fleet } = plugins; const { fleet } = plugins;
if (fleet) {
const agentEnrollmentExtensionData = getApmEnrollmentFlyoutData();
const agentEnrollmentExtensionData = getApmEnrollmentFlyoutData(); fleet.registerExtension({
package: 'apm',
view: 'agent-enrollment-flyout',
title: agentEnrollmentExtensionData.title,
Component: agentEnrollmentExtensionData.Component,
});
fleet.registerExtension({ fleet.registerExtension({
package: 'apm', package: 'apm',
view: 'agent-enrollment-flyout', view: 'package-detail-assets',
title: agentEnrollmentExtensionData.title, Component: LazyApmCustomAssetsExtension,
Component: agentEnrollmentExtensionData.Component, });
}); }
fleet.registerExtension({
package: 'apm',
view: 'package-detail-assets',
Component: LazyApmCustomAssetsExtension,
});
} }
} }

View file

@ -18,6 +18,7 @@ interface Args {
onPrem: boolean; onPrem: boolean;
hasFleetPoliciesWithApmIntegration: boolean; hasFleetPoliciesWithApmIntegration: boolean;
hasCloudPolicyWithApmIntegration: boolean; hasCloudPolicyWithApmIntegration: boolean;
isFleetEnabled: boolean;
} }
const policyElasticAgentOnCloudAgent: APIResponseType['fleetAgents'][0] = { const policyElasticAgentOnCloudAgent: APIResponseType['fleetAgents'][0] = {
@ -47,6 +48,7 @@ function Wrapper({
apmAgent, apmAgent,
onPrem, onPrem,
hasCloudPolicyWithApmIntegration, hasCloudPolicyWithApmIntegration,
isFleetEnabled,
}: Args) { }: Args) {
const http = ({ const http = ({
get: () => ({ get: () => ({
@ -56,6 +58,7 @@ function Wrapper({
? [policyElasticAgentOnCloudAgent] ? [policyElasticAgentOnCloudAgent]
: []), : []),
], ],
isFleetEnabled,
cloudStandaloneSetup: { cloudStandaloneSetup: {
apmServerUrl: 'cloud_url', apmServerUrl: 'cloud_url',
secretToken: 'foo', secretToken: 'foo',
@ -80,6 +83,7 @@ Integration.args = {
onPrem: true, onPrem: true,
hasFleetPoliciesWithApmIntegration: false, hasFleetPoliciesWithApmIntegration: false,
hasCloudPolicyWithApmIntegration: false, hasCloudPolicyWithApmIntegration: false,
isFleetEnabled: true,
}; };
export default { export default {
@ -113,5 +117,8 @@ export default {
hasCloudPolicyWithApmIntegration: { hasCloudPolicyWithApmIntegration: {
control: { type: 'boolean', options: [true, false] }, control: { type: 'boolean', options: [true, false] },
}, },
isFleetEnabled: {
control: { type: 'boolean', options: [true, false], defaultValue: true },
},
}, },
}; };

View file

@ -41,6 +41,7 @@ describe('getPolicyOptions', () => {
apmServerUrl: 'cloud_url', apmServerUrl: 'cloud_url',
secretToken: 'cloud_token', secretToken: 'cloud_token',
}, },
isFleetEnabled: true,
}; };
const options = getPolicyOptions({ const options = getPolicyOptions({
isCloudEnabled: true, isCloudEnabled: true,
@ -65,6 +66,7 @@ describe('getPolicyOptions', () => {
apmServerUrl: 'cloud_url', apmServerUrl: 'cloud_url',
secretToken: 'cloud_token', secretToken: 'cloud_token',
}, },
isFleetEnabled: true,
}; };
const options = getPolicyOptions({ const options = getPolicyOptions({
isCloudEnabled: true, isCloudEnabled: true,
@ -109,6 +111,7 @@ describe('getPolicyOptions', () => {
apmServerUrl: 'cloud_url', apmServerUrl: 'cloud_url',
secretToken: 'cloud_token', secretToken: 'cloud_token',
}, },
isFleetEnabled: true,
}; };
const options = getPolicyOptions({ const options = getPolicyOptions({
isCloudEnabled: true, isCloudEnabled: true,
@ -151,6 +154,7 @@ describe('getPolicyOptions', () => {
const data: APIResponseType = { const data: APIResponseType = {
fleetAgents: [], fleetAgents: [],
cloudStandaloneSetup: undefined, cloudStandaloneSetup: undefined,
isFleetEnabled: true,
}; };
const options = getPolicyOptions({ const options = getPolicyOptions({
isCloudEnabled: true, isCloudEnabled: true,
@ -173,6 +177,7 @@ describe('getPolicyOptions', () => {
const data: APIResponseType = { const data: APIResponseType = {
fleetAgents, fleetAgents,
cloudStandaloneSetup: undefined, cloudStandaloneSetup: undefined,
isFleetEnabled: true,
}; };
const options = getPolicyOptions({ const options = getPolicyOptions({
isCloudEnabled: true, isCloudEnabled: true,
@ -213,6 +218,7 @@ describe('getPolicyOptions', () => {
const data: APIResponseType = { const data: APIResponseType = {
fleetAgents: [policyElasticAgentOnCloudAgent, ...fleetAgents], fleetAgents: [policyElasticAgentOnCloudAgent, ...fleetAgents],
cloudStandaloneSetup: undefined, cloudStandaloneSetup: undefined,
isFleetEnabled: true,
}; };
const options = getPolicyOptions({ const options = getPolicyOptions({
isCloudEnabled: true, isCloudEnabled: true,
@ -256,6 +262,7 @@ describe('getPolicyOptions', () => {
const data: APIResponseType = { const data: APIResponseType = {
fleetAgents: [], fleetAgents: [],
cloudStandaloneSetup: undefined, cloudStandaloneSetup: undefined,
isFleetEnabled: true,
}; };
const options = getPolicyOptions({ const options = getPolicyOptions({
isCloudEnabled: false, isCloudEnabled: false,
@ -278,6 +285,7 @@ describe('getPolicyOptions', () => {
const data: APIResponseType = { const data: APIResponseType = {
fleetAgents, fleetAgents,
cloudStandaloneSetup: undefined, cloudStandaloneSetup: undefined,
isFleetEnabled: true,
}; };
const options = getPolicyOptions({ const options = getPolicyOptions({
isCloudEnabled: false, isCloudEnabled: false,

View file

@ -7,6 +7,10 @@
import { fireEvent, render, screen } from '@testing-library/react'; import { fireEvent, render, screen } from '@testing-library/react';
import { HttpStart } from 'kibana/public'; import { HttpStart } from 'kibana/public';
import React from 'react'; import React from 'react';
import {
expectTextsInDocument,
expectTextsNotInDocument,
} from '../../utils/testHelpers';
import TutorialConfigAgent from './'; import TutorialConfigAgent from './';
const policyElasticAgentOnCloudAgent = { const policyElasticAgentOnCloudAgent = {
@ -32,68 +36,32 @@ const fleetAgents = [
]; ];
describe('TutorialConfigAgent', () => { describe('TutorialConfigAgent', () => {
it('renders loading component while API is being called', () => { beforeAll(() => {
const component = render( // Mocks console.error so it won't polute tests output when testing the api throwing error
<TutorialConfigAgent jest.spyOn(console, 'error').mockImplementation(() => null);
variantId="java"
http={
({
get: jest.fn(),
} as unknown) as HttpStart
}
basePath="http://localhost:5601"
isCloudEnabled
/>
);
expect(component.getByTestId('loading')).toBeInTheDocument();
}); });
it('updates commands when a different policy is selected', async () => {
const component = render(
<TutorialConfigAgent
variantId="java"
http={
({
get: jest.fn().mockReturnValue({
cloudStandaloneSetup: undefined,
fleetAgents,
}),
} as unknown) as HttpStart
}
basePath="http://localhost:5601"
isCloudEnabled={false}
/>
);
expect(
await screen.findByText('Default Standalone configuration')
).toBeInTheDocument();
let commands = component.getByTestId('commands').innerHTML;
expect(commands).not.toEqual('');
expect(commands).toMatchInlineSnapshot(`
"java -javaagent:/path/to/elastic-apm-agent-&lt;version&gt;.jar \\\\
-Delastic.apm.service_name=my-application \\\\
-Delastic.apm.server_urls=http://localhost:8200 \\\\
-Delastic.apm.secret_token= \\\\
-Delastic.apm.environment=production \\\\
-Delastic.apm.application_packages=org.example \\\\
-jar my-application.jar"
`);
fireEvent.click(component.getByTestId('comboBoxToggleListButton')); afterAll(() => {
fireEvent.click(component.getByText('agent foo')); jest.restoreAllMocks();
commands = component.getByTestId('commands').innerHTML;
expect(commands).not.toEqual('');
expect(commands).toMatchInlineSnapshot(`
"java -javaagent:/path/to/elastic-apm-agent-&lt;version&gt;.jar \\\\
-Delastic.apm.service_name=my-application \\\\
-Delastic.apm.server_urls=foo \\\\
-Delastic.apm.secret_token=foo \\\\
-Delastic.apm.environment=production \\\\
-Delastic.apm.application_packages=org.example \\\\
-jar my-application.jar"
`);
}); });
describe('running on prem', () => {
it('selects defaul standalone by defauls', async () => { describe('when fleet plugin is enabled', () => {
it('renders loading component while API is being called', () => {
const component = render(
<TutorialConfigAgent
variantId="java"
http={
({
get: jest.fn(),
} as unknown) as HttpStart
}
basePath="http://localhost:5601"
isCloudEnabled
/>
);
expect(component.getByTestId('loading')).toBeInTheDocument();
});
it('updates commands when a different policy is selected', async () => {
const component = render( const component = render(
<TutorialConfigAgent <TutorialConfigAgent
variantId="java" variantId="java"
@ -102,6 +70,7 @@ describe('TutorialConfigAgent', () => {
get: jest.fn().mockReturnValue({ get: jest.fn().mockReturnValue({
cloudStandaloneSetup: undefined, cloudStandaloneSetup: undefined,
fleetAgents, fleetAgents,
isFleetEnabled: true,
}), }),
} as unknown) as HttpStart } as unknown) as HttpStart
} }
@ -112,6 +81,259 @@ describe('TutorialConfigAgent', () => {
expect( expect(
await screen.findByText('Default Standalone configuration') await screen.findByText('Default Standalone configuration')
).toBeInTheDocument(); ).toBeInTheDocument();
let commands = component.getByTestId('commands').innerHTML;
expect(commands).not.toEqual('');
expect(commands).toMatchInlineSnapshot(`
"java -javaagent:/path/to/elastic-apm-agent-&lt;version&gt;.jar \\\\
-Delastic.apm.service_name=my-application \\\\
-Delastic.apm.server_urls=http://localhost:8200 \\\\
-Delastic.apm.secret_token= \\\\
-Delastic.apm.environment=production \\\\
-Delastic.apm.application_packages=org.example \\\\
-jar my-application.jar"
`);
fireEvent.click(component.getByTestId('comboBoxToggleListButton'));
fireEvent.click(component.getByText('agent foo'));
commands = component.getByTestId('commands').innerHTML;
expect(commands).not.toEqual('');
expect(commands).toMatchInlineSnapshot(`
"java -javaagent:/path/to/elastic-apm-agent-&lt;version&gt;.jar \\\\
-Delastic.apm.service_name=my-application \\\\
-Delastic.apm.server_urls=foo \\\\
-Delastic.apm.secret_token=foo \\\\
-Delastic.apm.environment=production \\\\
-Delastic.apm.application_packages=org.example \\\\
-jar my-application.jar"
`);
});
describe('running on prem', () => {
it('selects defaul standalone by defauls', async () => {
const component = render(
<TutorialConfigAgent
variantId="java"
http={
({
get: jest.fn().mockReturnValue({
cloudStandaloneSetup: undefined,
fleetAgents,
isFleetEnabled: true,
}),
} as unknown) as HttpStart
}
basePath="http://localhost:5601"
isCloudEnabled={false}
/>
);
expect(
await screen.findByText('Default Standalone configuration')
).toBeInTheDocument();
expect(
component.getByTestId('policySelector_onPrem')
).toBeInTheDocument();
const commands = component.getByTestId('commands').innerHTML;
expect(commands).not.toEqual('');
expect(commands).toMatchInlineSnapshot(`
"java -javaagent:/path/to/elastic-apm-agent-&lt;version&gt;.jar \\\\
-Delastic.apm.service_name=my-application \\\\
-Delastic.apm.server_urls=http://localhost:8200 \\\\
-Delastic.apm.secret_token= \\\\
-Delastic.apm.environment=production \\\\
-Delastic.apm.application_packages=org.example \\\\
-jar my-application.jar"
`);
});
it('shows get started with fleet link when there are no fleet agents', async () => {
const component = render(
<TutorialConfigAgent
variantId="java"
http={
({
get: jest.fn().mockReturnValue({
cloudStandaloneSetup: undefined,
fleetAgents: [],
isFleetEnabled: true,
}),
} as unknown) as HttpStart
}
basePath="http://localhost:5601"
isCloudEnabled
/>
);
expect(
await screen.findByText('Default Standalone configuration')
).toBeInTheDocument();
expect(
component.getByTestId('policySelector_onPrem')
).toBeInTheDocument();
const commands = component.getByTestId('commands').innerHTML;
expect(commands).not.toEqual('');
expect(commands).toMatchInlineSnapshot(`
"java -javaagent:/path/to/elastic-apm-agent-&lt;version&gt;.jar \\\\
-Delastic.apm.service_name=my-application \\\\
-Delastic.apm.server_urls=http://localhost:8200 \\\\
-Delastic.apm.secret_token= \\\\
-Delastic.apm.environment=production \\\\
-Delastic.apm.application_packages=org.example \\\\
-jar my-application.jar"
`);
expectTextsInDocument(component, ['Get started with fleet']);
});
});
describe('running on cloud', () => {
it('selects defaul standalone by defauls', async () => {
const component = render(
<TutorialConfigAgent
variantId="java"
http={
({
get: jest.fn().mockReturnValue({
cloudStandaloneSetup: {
apmServerUrl: 'cloud_url',
secretToken: 'cloud_token',
},
fleetAgents,
isFleetEnabled: true,
}),
} as unknown) as HttpStart
}
basePath="http://localhost:5601"
isCloudEnabled
/>
);
expect(
await screen.findByText('Default Standalone configuration')
).toBeInTheDocument();
expect(
component.getByTestId('policySelector_cloud')
).toBeInTheDocument();
const commands = component.getByTestId('commands').innerHTML;
expect(commands).not.toEqual('');
expect(commands).toMatchInlineSnapshot(`
"java -javaagent:/path/to/elastic-apm-agent-&lt;version&gt;.jar \\\\
-Delastic.apm.service_name=my-application \\\\
-Delastic.apm.server_urls=cloud_url \\\\
-Delastic.apm.secret_token=cloud_token \\\\
-Delastic.apm.environment=production \\\\
-Delastic.apm.application_packages=org.example \\\\
-jar my-application.jar"
`);
});
it('selects policy elastic agent on cloud when available by default', async () => {
const component = render(
<TutorialConfigAgent
variantId="java"
http={
({
get: jest.fn().mockReturnValue({
cloudStandaloneSetup: {
apmServerUrl: 'cloud_url',
secretToken: 'cloud_token',
},
fleetAgents: [...fleetAgents, policyElasticAgentOnCloudAgent],
isFleetEnabled: true,
}),
} as unknown) as HttpStart
}
basePath="http://localhost:5601"
isCloudEnabled
/>
);
expect(
await screen.findByText('Elastic Cloud agent policy')
).toBeInTheDocument();
expect(
component.getByTestId('policySelector_policy-elastic-agent-on-cloud')
).toBeInTheDocument();
const commands = component.getByTestId('commands').innerHTML;
expect(commands).not.toEqual('');
expect(commands).toMatchInlineSnapshot(`
"java -javaagent:/path/to/elastic-apm-agent-&lt;version&gt;.jar \\\\
-Delastic.apm.service_name=my-application \\\\
-Delastic.apm.server_urls=apm_cloud_url \\\\
-Delastic.apm.secret_token=apm_cloud_token \\\\
-Delastic.apm.environment=production \\\\
-Delastic.apm.application_packages=org.example \\\\
-jar my-application.jar"
`);
});
it('shows default standalone option when api throws an error', async () => {
const component = render(
<TutorialConfigAgent
variantId="java"
http={
({
get: () => {
throw new Error('Boom');
},
} as unknown) as HttpStart
}
basePath="http://localhost:5601"
isCloudEnabled
/>
);
expect(
await screen.findByText('Default Standalone configuration')
).toBeInTheDocument();
const commands = component.getByTestId('commands').innerHTML;
expect(commands).not.toEqual('');
expect(commands).toMatchInlineSnapshot(`
"java -javaagent:/path/to/elastic-apm-agent-&lt;version&gt;.jar \\\\
-Delastic.apm.service_name=my-application \\\\
-Delastic.apm.server_urls=http://localhost:8200 \\\\
-Delastic.apm.secret_token= \\\\
-Delastic.apm.environment=production \\\\
-Delastic.apm.application_packages=org.example \\\\
-jar my-application.jar"
`);
});
});
});
describe('when fleet plugin is disabled', () => {
it('hides fleet links', async () => {
const component = render(
<TutorialConfigAgent
variantId="java"
http={
({
get: jest.fn().mockReturnValue({
cloudStandaloneSetup: undefined,
fleetAgents: [],
isFleetEnabled: false,
}),
} as unknown) as HttpStart
}
basePath="http://localhost:5601"
isCloudEnabled
/>
);
expectTextsNotInDocument(component, [
'Get started with fleet',
'Manage fleet policies',
]);
});
it('shows default standalone on prem', async () => {
const component = render(
<TutorialConfigAgent
variantId="java"
http={
({
get: jest.fn().mockReturnValue({
cloudStandaloneSetup: undefined,
fleetAgents: [],
isFleetEnabled: false,
}),
} as unknown) as HttpStart
}
basePath="http://localhost:5601"
isCloudEnabled
/>
);
expect(
await screen.findByText('Default Standalone configuration')
).toBeInTheDocument();
expect( expect(
component.getByTestId('policySelector_onPrem') component.getByTestId('policySelector_onPrem')
).toBeInTheDocument(); ).toBeInTheDocument();
@ -127,9 +349,7 @@ describe('TutorialConfigAgent', () => {
-jar my-application.jar" -jar my-application.jar"
`); `);
}); });
}); it('shows default standalone on cloud', async () => {
describe('running on cloud', () => {
it('selects defaul standalone by defauls', async () => {
const component = render( const component = render(
<TutorialConfigAgent <TutorialConfigAgent
variantId="java" variantId="java"
@ -140,7 +360,8 @@ describe('TutorialConfigAgent', () => {
apmServerUrl: 'cloud_url', apmServerUrl: 'cloud_url',
secretToken: 'cloud_token', secretToken: 'cloud_token',
}, },
fleetAgents, fleetAgents: [],
isFleetEnabled: false,
}), }),
} as unknown) as HttpStart } as unknown) as HttpStart
} }
@ -164,42 +385,5 @@ describe('TutorialConfigAgent', () => {
-jar my-application.jar" -jar my-application.jar"
`); `);
}); });
it('selects policy elastic agent on cloud when available by default', async () => {
const component = render(
<TutorialConfigAgent
variantId="java"
http={
({
get: jest.fn().mockReturnValue({
cloudStandaloneSetup: {
apmServerUrl: 'cloud_url',
secretToken: 'cloud_token',
},
fleetAgents: [...fleetAgents, policyElasticAgentOnCloudAgent],
}),
} as unknown) as HttpStart
}
basePath="http://localhost:5601"
isCloudEnabled
/>
);
expect(
await screen.findByText('Elastic Cloud agent policy')
).toBeInTheDocument();
expect(
component.getByTestId('policySelector_policy-elastic-agent-on-cloud')
).toBeInTheDocument();
const commands = component.getByTestId('commands').innerHTML;
expect(commands).not.toEqual('');
expect(commands).toMatchInlineSnapshot(`
"java -javaagent:/path/to/elastic-apm-agent-&lt;version&gt;.jar \\\\
-Delastic.apm.service_name=my-application \\\\
-Delastic.apm.server_urls=apm_cloud_url \\\\
-Delastic.apm.secret_token=apm_cloud_token \\\\
-Delastic.apm.environment=production \\\\
-Delastic.apm.application_packages=org.example \\\\
-jar my-application.jar"
`);
});
}); });
}); });

View file

@ -46,16 +46,43 @@ interface Props {
isCloudEnabled: boolean; isCloudEnabled: boolean;
} }
const INITIAL_STATE = {
fleetAgents: [],
cloudStandaloneSetup: undefined,
isFleetEnabled: false,
};
function getFleetLink({
isFleetEnabled,
hasFleetAgents,
basePath,
}: {
isFleetEnabled: boolean;
hasFleetAgents: boolean;
basePath: string;
}) {
if (!isFleetEnabled) {
return;
}
return hasFleetAgents
? {
label: MANAGE_FLEET_POLICIES_LABEL,
href: `${basePath}/app/fleet#/policies`,
}
: {
label: GET_STARTED_WITH_FLEET_LABEL,
href: `${basePath}/app/integrations#/detail/apm-0.3.0/overview`,
};
}
function TutorialConfigAgent({ function TutorialConfigAgent({
variantId, variantId,
http, http,
basePath, basePath,
isCloudEnabled, isCloudEnabled,
}: Props) { }: Props) {
const [data, setData] = useState<APIResponseType>({ const [data, setData] = useState<APIResponseType>(INITIAL_STATE);
fleetAgents: [],
cloudStandaloneSetup: undefined,
});
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [selectedOption, setSelectedOption] = useState<PolicyOption>(); const [selectedOption, setSelectedOption] = useState<PolicyOption>();
@ -68,6 +95,7 @@ function TutorialConfigAgent({
setData(response as APIResponseType); setData(response as APIResponseType);
} }
} catch (e) { } catch (e) {
setIsLoading(false);
console.error('Error while fetching fleet agents.', e); console.error('Error while fetching fleet agents.', e);
} }
} }
@ -105,15 +133,6 @@ function TutorialConfigAgent({
}); });
const hasFleetAgents = !!data.fleetAgents.length; const hasFleetAgents = !!data.fleetAgents.length;
const fleetLink = hasFleetAgents
? {
label: MANAGE_FLEET_POLICIES_LABEL,
href: `${basePath}/app/fleet#/policies`,
}
: {
label: GET_STARTED_WITH_FLEET_LABEL,
href: `${basePath}/app/integrations#/detail/apm-0.3.0/overview`,
};
return ( return (
<> <>
@ -125,7 +144,11 @@ function TutorialConfigAgent({
onChange={(newSelectedOption) => onChange={(newSelectedOption) =>
setSelectedOption(newSelectedOption) setSelectedOption(newSelectedOption)
} }
fleetLink={fleetLink} fleetLink={getFleetLink({
isFleetEnabled: data.isFleetEnabled,
hasFleetAgents,
basePath,
})}
/> />
</EuiFlexItem> </EuiFlexItem>
<EuiFlexItem grow={false}> <EuiFlexItem grow={false}>

View file

@ -21,7 +21,7 @@ interface Props {
options: PolicyOption[]; options: PolicyOption[];
selectedOption?: PolicyOption; selectedOption?: PolicyOption;
onChange: (selectedOption?: PolicyOption) => void; onChange: (selectedOption?: PolicyOption) => void;
fleetLink: { fleetLink?: {
label: string; label: string;
href: string; href: string;
}; };
@ -58,9 +58,11 @@ export function PolicySelector({
{ defaultMessage: 'Choose policy' } { defaultMessage: 'Choose policy' }
)} )}
labelAppend={ labelAppend={
<EuiText size="xs"> fleetLink && (
<EuiLink href={fleetLink.href}>{fleetLink.label}</EuiLink> <EuiText size="xs">
</EuiText> <EuiLink href={fleetLink.href}>{fleetLink.label}</EuiLink>
</EuiText>
)
} }
helpText={i18n.translate( helpText={i18n.translate(
'xpack.apm.tutorial.agent_config.choosePolicy.helper', 'xpack.apm.tutorial.agent_config.choosePolicy.helper',

View file

@ -42,6 +42,7 @@ function TutorialFleetInstructions({ http, basePath, isDarkTheme }: Props) {
const response = await http.get('/api/apm/fleet/has_data'); const response = await http.get('/api/apm/fleet/has_data');
setData(response as APIResponseType); setData(response as APIResponseType);
} catch (e) { } catch (e) {
setIsLoading(false);
console.error('Error while fetching fleet details.', e); console.error('Error while fetching fleet details.', e);
} }
setIsLoading(false); setIsLoading(false);

View file

@ -15,7 +15,7 @@ import {
Plugin, Plugin,
PluginInitializerContext, PluginInitializerContext,
} from 'src/core/server'; } from 'src/core/server';
import { mapValues, once } from 'lodash'; import { isEmpty, mapValues, once } from 'lodash';
import { TECHNICAL_COMPONENT_TEMPLATE_NAME } from '../../rule_registry/common/assets'; import { TECHNICAL_COMPONENT_TEMPLATE_NAME } from '../../rule_registry/common/assets';
import { mappingFromFieldMap } from '../../rule_registry/common/mapping_from_field_map'; import { mappingFromFieldMap } from '../../rule_registry/common/mapping_from_field_map';
import { APMConfig, APMXPackConfig, APM_SERVER_FEATURE_ID } from '.'; import { APMConfig, APMXPackConfig, APM_SERVER_FEATURE_ID } from '.';
@ -104,21 +104,6 @@ export class APMPlugin
}); });
} }
plugins.home?.tutorials.registerTutorial(
tutorialProvider({
isEnabled: this.currentConfig['xpack.apm.ui.enabled'],
indexPatternTitle: this.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'],
},
})
);
plugins.features.registerKibanaFeature(APM_FEATURE); plugins.features.registerKibanaFeature(APM_FEATURE);
registerFeaturesUsage({ licensingPlugin: plugins.licensing }); registerFeaturesUsage({ licensingPlugin: plugins.licensing });
@ -206,6 +191,22 @@ export class APMPlugin
}; };
}) as APMRouteHandlerResources['plugins']; }) as APMRouteHandlerResources['plugins'];
plugins.home?.tutorials.registerTutorial(
tutorialProvider({
isEnabled: this.currentConfig['xpack.apm.ui.enabled'],
indexPatternTitle: this.currentConfig['apm_oss.indexPattern'],
cloud: plugins.cloud,
isFleetPluginEnabled: !isEmpty(resourcePlugins.fleet),
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'],
},
})
);
const telemetryUsageCounter = resourcePlugins.usageCollection?.setup.createUsageCounter( const telemetryUsageCounter = resourcePlugins.usageCollection?.setup.createUsageCounter(
APM_SERVER_FEATURE_ID APM_SERVER_FEATURE_ID
); );

View file

@ -32,7 +32,7 @@ const hasFleetDataRoute = createApmServerRoute({
handler: async ({ core, plugins }) => { handler: async ({ core, plugins }) => {
const fleetPluginStart = await plugins.fleet?.start(); const fleetPluginStart = await plugins.fleet?.start();
if (!fleetPluginStart) { if (!fleetPluginStart) {
throw Boom.internal(FLEET_REQUIRED_MESSAGE); return { hasData: false };
} }
const packagePolicies = await getApmPackgePolicies({ const packagePolicies = await getApmPackgePolicies({
core, core,
@ -56,7 +56,7 @@ const fleetAgentsRoute = createApmServerRoute({
const fleetPluginStart = await plugins.fleet?.start(); const fleetPluginStart = await plugins.fleet?.start();
if (!fleetPluginStart) { if (!fleetPluginStart) {
throw Boom.internal(FLEET_REQUIRED_MESSAGE); return { cloudStandaloneSetup, fleetAgents: [], isFleetEnabled: false };
} }
// fetches package policies that contains APM integrations // fetches package policies that contains APM integrations
const packagePolicies = await getApmPackgePolicies({ const packagePolicies = await getApmPackgePolicies({
@ -75,6 +75,7 @@ const fleetAgentsRoute = createApmServerRoute({
return { return {
cloudStandaloneSetup, cloudStandaloneSetup,
isFleetEnabled: true,
fleetAgents: fleetAgents.map((agent) => { fleetAgents: fleetAgents.map((agent) => {
const packagePolicy = policiesGroupedById[agent.id]; const packagePolicy = policiesGroupedById[agent.id];
const packagePolicyVars = packagePolicy.inputs[0]?.vars; const packagePolicyVars = packagePolicy.inputs[0]?.vars;
@ -190,11 +191,6 @@ export const apmFleetRouteRepository = createApmServerRouteRepository()
.add(getMigrationCheckRoute) .add(getMigrationCheckRoute)
.add(createCloudApmPackagePolicyRoute); .add(createCloudApmPackagePolicyRoute);
const FLEET_REQUIRED_MESSAGE = i18n.translate(
'xpack.apm.fleet_has_data.fleetRequired',
{ defaultMessage: `Fleet plugin is required` }
);
const FLEET_SECURITY_REQUIRED_MESSAGE = i18n.translate( const FLEET_SECURITY_REQUIRED_MESSAGE = i18n.translate(
'xpack.apm.api.fleet.fleetSecurityRequired', 'xpack.apm.api.fleet.fleetSecurityRequired',
{ defaultMessage: `Fleet and Security plugins are required` } { defaultMessage: `Fleet and Security plugins are required` }

View file

@ -38,12 +38,14 @@ export function onPremInstructions({
metricsIndices, metricsIndices,
sourcemapIndices, sourcemapIndices,
onboardingIndices, onboardingIndices,
isFleetPluginEnabled,
}: { }: {
errorIndices: string; errorIndices: string;
transactionIndices: string; transactionIndices: string;
metricsIndices: string; metricsIndices: string;
sourcemapIndices: string; sourcemapIndices: string;
onboardingIndices: string; onboardingIndices: string;
isFleetPluginEnabled: boolean;
}): InstructionsSchema { }): InstructionsSchema {
const EDIT_CONFIG = createEditConfig(); const EDIT_CONFIG = createEditConfig();
const START_SERVER_UNIX = createStartServerUnix(); const START_SERVER_UNIX = createStartServerUnix();
@ -69,12 +71,17 @@ export function onPremInstructions({
iconType: 'alert', iconType: 'alert',
}, },
instructionVariants: [ instructionVariants: [
{ // hides fleet section when plugin is disabled
id: INSTRUCTION_VARIANT.FLEET, ...(isFleetPluginEnabled
instructions: [ ? [
{ customComponentName: 'TutorialFleetInstructions' }, {
], id: INSTRUCTION_VARIANT.FLEET,
}, instructions: [
{ customComponentName: 'TutorialFleetInstructions' },
],
},
]
: []),
{ {
id: INSTRUCTION_VARIANT.OSX, id: INSTRUCTION_VARIANT.OSX,
instructions: [ instructions: [

View file

@ -28,6 +28,7 @@ export const tutorialProvider = ({
indexPatternTitle, indexPatternTitle,
indices, indices,
cloud, cloud,
isFleetPluginEnabled,
}: { }: {
isEnabled: boolean; isEnabled: boolean;
indexPatternTitle: string; indexPatternTitle: string;
@ -39,6 +40,7 @@ export const tutorialProvider = ({
sourcemapIndices: string; sourcemapIndices: string;
onboardingIndices: string; onboardingIndices: string;
}; };
isFleetPluginEnabled: boolean;
}) => () => { }) => () => {
const savedObjects = [ const savedObjects = [
{ {
@ -104,7 +106,7 @@ It allows you to monitor the performance of thousands of applications in real ti
euiIconType: 'apmApp', euiIconType: 'apmApp',
artifacts, artifacts,
customStatusCheckName: 'apm_fleet_server_status_check', customStatusCheckName: 'apm_fleet_server_status_check',
onPrem: onPremInstructions(indices), onPrem: onPremInstructions({ ...indices, isFleetPluginEnabled }),
elasticCloud: createElasticCloudInstructions(cloud), elasticCloud: createElasticCloudInstructions(cloud),
previewImagePath: '/plugins/apm/assets/apm.png', previewImagePath: '/plugins/apm/assets/apm.png',
savedObjects, savedObjects,