diff --git a/src/plugins/home/public/application/components/tutorial/__snapshots__/instruction_set.test.js.snap b/src/plugins/home/public/application/components/tutorial/__snapshots__/instruction_set.test.js.snap
index a9f9823047c0b..073d20b4bf804 100644
--- a/src/plugins/home/public/application/components/tutorial/__snapshots__/instruction_set.test.js.snap
+++ b/src/plugins/home/public/application/components/tutorial/__snapshots__/instruction_set.test.js.snap
@@ -52,8 +52,10 @@ exports[`render 1`] = `
"do stuff in command line",
]
}
+ isCloudEnabled={false}
paramValues={Object {}}
replaceTemplateStrings={[Function]}
+ variantId="OSX"
/>,
"key": 0,
"title": "step 1",
@@ -65,8 +67,10 @@ exports[`render 1`] = `
"do more stuff in command line",
]
}
+ isCloudEnabled={false}
paramValues={Object {}}
replaceTemplateStrings={[Function]}
+ variantId="OSX"
/>,
"key": 1,
"title": "step 2",
@@ -129,8 +133,10 @@ exports[`statusCheckState checking status 1`] = `
"do stuff in command line",
]
}
+ isCloudEnabled={false}
paramValues={Object {}}
replaceTemplateStrings={[Function]}
+ variantId="OSX"
/>,
"key": 0,
"title": "step 1",
@@ -142,8 +148,10 @@ exports[`statusCheckState checking status 1`] = `
"do more stuff in command line",
]
}
+ isCloudEnabled={false}
paramValues={Object {}}
replaceTemplateStrings={[Function]}
+ variantId="OSX"
/>,
"key": 1,
"title": "step 2",
@@ -236,8 +244,10 @@ exports[`statusCheckState failed status check - error 1`] = `
"do stuff in command line",
]
}
+ isCloudEnabled={false}
paramValues={Object {}}
replaceTemplateStrings={[Function]}
+ variantId="OSX"
/>,
"key": 0,
"title": "step 1",
@@ -249,8 +259,10 @@ exports[`statusCheckState failed status check - error 1`] = `
"do more stuff in command line",
]
}
+ isCloudEnabled={false}
paramValues={Object {}}
replaceTemplateStrings={[Function]}
+ variantId="OSX"
/>,
"key": 1,
"title": "step 2",
@@ -347,8 +359,10 @@ exports[`statusCheckState failed status check - no data 1`] = `
"do stuff in command line",
]
}
+ isCloudEnabled={false}
paramValues={Object {}}
replaceTemplateStrings={[Function]}
+ variantId="OSX"
/>,
"key": 0,
"title": "step 1",
@@ -360,8 +374,10 @@ exports[`statusCheckState failed status check - no data 1`] = `
"do more stuff in command line",
]
}
+ isCloudEnabled={false}
paramValues={Object {}}
replaceTemplateStrings={[Function]}
+ variantId="OSX"
/>,
"key": 1,
"title": "step 2",
@@ -458,8 +474,10 @@ exports[`statusCheckState initial state - no check has been attempted 1`] = `
"do stuff in command line",
]
}
+ isCloudEnabled={false}
paramValues={Object {}}
replaceTemplateStrings={[Function]}
+ variantId="OSX"
/>,
"key": 0,
"title": "step 1",
@@ -471,8 +489,10 @@ exports[`statusCheckState initial state - no check has been attempted 1`] = `
"do more stuff in command line",
]
}
+ isCloudEnabled={false}
paramValues={Object {}}
replaceTemplateStrings={[Function]}
+ variantId="OSX"
/>,
"key": 1,
"title": "step 2",
@@ -565,8 +585,10 @@ exports[`statusCheckState successful status check 1`] = `
"do stuff in command line",
]
}
+ isCloudEnabled={false}
paramValues={Object {}}
replaceTemplateStrings={[Function]}
+ variantId="OSX"
/>,
"key": 0,
"title": "step 1",
@@ -578,8 +600,10 @@ exports[`statusCheckState successful status check 1`] = `
"do more stuff in command line",
]
}
+ isCloudEnabled={false}
paramValues={Object {}}
replaceTemplateStrings={[Function]}
+ variantId="OSX"
/>,
"key": 1,
"title": "step 2",
diff --git a/src/plugins/home/public/application/components/tutorial/__snapshots__/tutorial.test.js.snap b/src/plugins/home/public/application/components/tutorial/__snapshots__/tutorial.test.js.snap
index f819569cd422d..ac697fae17f69 100644
--- a/src/plugins/home/public/application/components/tutorial/__snapshots__/tutorial.test.js.snap
+++ b/src/plugins/home/public/application/components/tutorial/__snapshots__/tutorial.test.js.snap
@@ -31,6 +31,7 @@ exports[`isCloudEnabled is false should not render instruction toggle when ON_PR
},
]
}
+ isCloudEnabled={false}
key="0"
offset={1}
onStatusCheck={[Function]}
@@ -107,6 +108,7 @@ exports[`isCloudEnabled is false should render ON_PREM instructions with instruc
},
]
}
+ isCloudEnabled={false}
key="0"
offset={1}
onStatusCheck={[Function]}
@@ -154,6 +156,7 @@ exports[`should render ELASTIC_CLOUD instructions when isCloudEnabled is true 1`
},
]
}
+ isCloudEnabled={true}
key="0"
offset={1}
onStatusCheck={[Function]}
diff --git a/src/plugins/home/public/application/components/tutorial/instruction.js b/src/plugins/home/public/application/components/tutorial/instruction.js
index 373f8c318a504..e4b3b3f321bf9 100644
--- a/src/plugins/home/public/application/components/tutorial/instruction.js
+++ b/src/plugins/home/public/application/components/tutorial/instruction.js
@@ -18,6 +18,7 @@ import {
EuiCopy,
EuiButton,
EuiLoadingSpinner,
+ EuiErrorBoundary,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
@@ -31,6 +32,8 @@ export function Instruction({
textPre,
replaceTemplateStrings,
customComponentName,
+ variantId,
+ isCloudEnabled,
}) {
const { tutorialService, http, uiSettings, getBasePath } = getServices();
@@ -96,18 +99,22 @@ export function Instruction({
{commandBlock}
- {post}
-
{LazyCustomComponent && (
}>
-
+
+
+
)}
+ {post}
+
);
@@ -120,4 +127,6 @@ Instruction.propTypes = {
textPre: PropTypes.string,
replaceTemplateStrings: PropTypes.func.isRequired,
customComponentName: PropTypes.string,
+ variantId: PropTypes.string,
+ isCloudEnabled: PropTypes.bool.isRequired,
};
diff --git a/src/plugins/home/public/application/components/tutorial/instruction_set.js b/src/plugins/home/public/application/components/tutorial/instruction_set.js
index da368120d493c..08b55a527b3cf 100644
--- a/src/plugins/home/public/application/components/tutorial/instruction_set.js
+++ b/src/plugins/home/public/application/components/tutorial/instruction_set.js
@@ -187,6 +187,8 @@ class InstructionSetUi extends React.Component {
textPost={instruction.textPost}
replaceTemplateStrings={this.props.replaceTemplateStrings}
customComponentName={instruction.customComponentName}
+ variantId={instructionVariant.id}
+ isCloudEnabled={this.props.isCloudEnabled}
/>
);
return {
@@ -320,6 +322,7 @@ InstructionSetUi.propTypes = {
paramValues: PropTypes.object.isRequired,
setParameter: PropTypes.func,
replaceTemplateStrings: PropTypes.func.isRequired,
+ isCloudEnabled: PropTypes.bool.isRequired,
};
export const InstructionSet = injectI18n(InstructionSetUi);
diff --git a/src/plugins/home/public/application/components/tutorial/instruction_set.test.js b/src/plugins/home/public/application/components/tutorial/instruction_set.test.js
index 539732a1c51a9..1bce4f72fde60 100644
--- a/src/plugins/home/public/application/components/tutorial/instruction_set.test.js
+++ b/src/plugins/home/public/application/components/tutorial/instruction_set.test.js
@@ -49,6 +49,7 @@ test('render', () => {
offset={1}
paramValues={{}}
replaceTemplateStrings={() => {}}
+ isCloudEnabled={false}
/>
);
expect(component).toMatchSnapshot(); // eslint-disable-line
@@ -74,6 +75,7 @@ describe('statusCheckState', () => {
statusCheckConfig={statusCheckConfig}
replaceTemplateStrings={() => {}}
statusCheckState={StatusCheckStates.FETCHING}
+ isCloudEnabled={false}
/>
);
expect(component).toMatchSnapshot(); // eslint-disable-line
@@ -90,6 +92,7 @@ describe('statusCheckState', () => {
statusCheckConfig={statusCheckConfig}
replaceTemplateStrings={() => {}}
statusCheckState={StatusCheckStates.FETCHING}
+ isCloudEnabled={false}
/>
);
expect(component).toMatchSnapshot(); // eslint-disable-line
@@ -106,6 +109,7 @@ describe('statusCheckState', () => {
statusCheckConfig={statusCheckConfig}
replaceTemplateStrings={() => {}}
statusCheckState={StatusCheckStates.ERROR}
+ isCloudEnabled={false}
/>
);
expect(component).toMatchSnapshot(); // eslint-disable-line
@@ -122,6 +126,7 @@ describe('statusCheckState', () => {
statusCheckConfig={statusCheckConfig}
replaceTemplateStrings={() => {}}
statusCheckState={StatusCheckStates.NO_DATA}
+ isCloudEnabled={false}
/>
);
expect(component).toMatchSnapshot(); // eslint-disable-line
@@ -138,6 +143,7 @@ describe('statusCheckState', () => {
statusCheckConfig={statusCheckConfig}
replaceTemplateStrings={() => {}}
statusCheckState={StatusCheckStates.HAS_DATA}
+ isCloudEnabled={false}
/>
);
expect(component).toMatchSnapshot(); // eslint-disable-line
diff --git a/src/plugins/home/public/application/components/tutorial/tutorial.js b/src/plugins/home/public/application/components/tutorial/tutorial.js
index 92bbb92fa0850..52daa53d4585c 100644
--- a/src/plugins/home/public/application/components/tutorial/tutorial.js
+++ b/src/plugins/home/public/application/components/tutorial/tutorial.js
@@ -301,6 +301,7 @@ class TutorialUi extends React.Component {
setParameter={this.setParameter}
replaceTemplateStrings={this.props.replaceTemplateStrings}
key={index}
+ isCloudEnabled={this.props.isCloudEnabled}
/>
);
});
diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts
index 012856ca9213c..0f0d072799061 100644
--- a/x-pack/plugins/apm/public/plugin.ts
+++ b/x-pack/plugins/apm/public/plugin.ts
@@ -175,6 +175,16 @@ export class ApmPlugin implements Plugin {
() => import('./tutorial/tutorial_fleet_instructions')
);
+ pluginSetupDeps.home?.tutorials.registerCustomComponent(
+ 'TutorialConfigAgent',
+ () => import('./tutorial/config_agent')
+ );
+
+ pluginSetupDeps.home?.tutorials.registerCustomComponent(
+ 'TutorialConfigAgentRumScript',
+ () => import('./tutorial/config_agent/rum_script')
+ );
+
plugins.observability.dashboard.register({
appName: 'apm',
hasData: async () => {
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/commands/django.ts b/x-pack/plugins/apm/public/tutorial/config_agent/commands/django.ts
new file mode 100644
index 0000000000000..97b5f3315bcdb
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/commands/django.ts
@@ -0,0 +1,72 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+
+export const django = `# ${i18n.translate(
+ 'xpack.apm.tutorial.djangoClient.configure.commands.addAgentComment',
+ {
+ defaultMessage: 'Add the agent to the installed apps',
+ }
+)}
+INSTALLED_APPS = (
+'elasticapm.contrib.django',
+# ...
+)
+
+ELASTIC_APM = {curlyOpen}
+# ${i18n.translate(
+ 'xpack.apm.tutorial.djangoClient.configure.commands.setRequiredServiceNameComment',
+ {
+ defaultMessage: 'Set the required service name. Allowed characters:',
+ }
+)}
+# ${i18n.translate(
+ 'xpack.apm.tutorial.djangoClient.configure.commands.allowedCharactersComment',
+ {
+ defaultMessage: 'a-z, A-Z, 0-9, -, _, and space',
+ }
+)}
+'SERVICE_NAME': '',
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.djangoClient.configure.commands.useIfApmServerRequiresTokenComment',
+ {
+ defaultMessage: 'Use if APM Server requires a secret token',
+ }
+)}
+'SECRET_TOKEN': '{{{secretToken}}}',
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.djangoClient.configure.commands.setCustomApmServerUrlComment',
+ {
+ defaultMessage:
+ 'Set the custom APM Server URL (default: {defaultApmServerUrl})',
+ values: { defaultApmServerUrl: 'http://localhost:8200' },
+ }
+)}
+'SERVER_URL': '{{{apmServerUrl}}}',
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.djangoClient.configure.commands.setServiceEnvironmentComment',
+ {
+ defaultMessage: 'Set the service environment',
+ }
+)}
+'ENVIRONMENT': 'production',
+{curlyClose}
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.djangoClient.configure.commands.addTracingMiddlewareComment',
+ {
+ defaultMessage: 'To send performance metrics, add our tracing middleware:',
+ }
+)}
+MIDDLEWARE = (
+'elasticapm.contrib.django.middleware.TracingMiddleware',
+#...
+)`;
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/commands/dotnet.ts b/x-pack/plugins/apm/public/tutorial/config_agent/commands/dotnet.ts
new file mode 100644
index 0000000000000..e083a2b45c716
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/commands/dotnet.ts
@@ -0,0 +1,14 @@
+/*
+ * 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 const dotnet = `{
+"ElasticApm": {
+"SecretToken": "{{{secretToken}}}",
+"ServerUrls": "{{{apmServerUrl}}}", //Set custom APM Server URL (default: http://localhost:8200)
+"ServiceName": "MyApp", //allowed characters: a-z, A-Z, 0-9, -, _, and space. Default is the entry assembly of the application
+"Environment": "production", // Set the service environment
+}
+}`;
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/commands/flask.ts b/x-pack/plugins/apm/public/tutorial/config_agent/commands/flask.ts
new file mode 100644
index 0000000000000..e4d7fd188e7c6
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/commands/flask.ts
@@ -0,0 +1,70 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+
+export const flask = `# ${i18n.translate(
+ 'xpack.apm.tutorial.flaskClient.configure.commands.initializeUsingEnvironmentVariablesComment',
+ {
+ defaultMessage: 'initialize using environment variables',
+ }
+)}
+from elasticapm.contrib.flask import ElasticAPM
+app = Flask(__name__)
+apm = ElasticAPM(app)
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.flaskClient.configure.commands.configureElasticApmComment',
+ {
+ defaultMessage:
+ "or configure to use ELASTIC_APM in your application's settings",
+ }
+)}
+from elasticapm.contrib.flask import ElasticAPM
+app.config['ELASTIC_APM'] = {curlyOpen}
+# ${i18n.translate(
+ 'xpack.apm.tutorial.flaskClient.configure.commands.setRequiredServiceNameComment',
+ {
+ defaultMessage: 'Set the required service name. Allowed characters:',
+ }
+)}
+# ${i18n.translate(
+ 'xpack.apm.tutorial.flaskClient.configure.commands.allowedCharactersComment',
+ {
+ defaultMessage: 'a-z, A-Z, 0-9, -, _, and space',
+ }
+)}
+'SERVICE_NAME': '',
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.flaskClient.configure.commands.useIfApmServerRequiresTokenComment',
+ {
+ defaultMessage: 'Use if APM Server requires a secret token',
+ }
+)}
+'SECRET_TOKEN': '{{{secretToken}}}',
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.flaskClient.configure.commands.setCustomApmServerUrlComment',
+ {
+ defaultMessage:
+ 'Set the custom APM Server URL (default: {defaultApmServerUrl})',
+ values: { defaultApmServerUrl: 'http://localhost:8200' },
+ }
+)}
+'SERVER_URL': '{{{apmServerUrl}}}',
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.flaskClient.configure.commands.setServiceEnvironmentComment',
+ {
+ defaultMessage: 'Set the service environment',
+ }
+)}
+'ENVIRONMENT': 'production',
+{curlyClose}
+
+apm = ElasticAPM(app)`;
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/commands/get_commands.test.ts b/x-pack/plugins/apm/public/tutorial/config_agent/commands/get_commands.test.ts
new file mode 100644
index 0000000000000..5dc66e2230524
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/commands/get_commands.test.ts
@@ -0,0 +1,536 @@
+/*
+ * 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 { getCommands } from './get_commands';
+
+describe('getCommands', () => {
+ describe('unknown agent', () => {
+ it('renders empty command', () => {
+ const commands = getCommands({
+ variantId: 'foo',
+ policyDetails: {
+ apmServerUrl: 'localhost:8220',
+ secretToken: 'foobar',
+ },
+ });
+ expect(commands).toBe('');
+ });
+ });
+ describe('java agent', () => {
+ it('renders empty commands', () => {
+ const commands = getCommands({
+ variantId: 'java',
+ policyDetails: {},
+ });
+ expect(commands).toMatchInlineSnapshot(`
+ "java -javaagent:/path/to/elastic-apm-agent-.jar \\\\
+ -Delastic.apm.service_name=my-application \\\\
+ -Delastic.apm.server_urls= \\\\
+ -Delastic.apm.secret_token= \\\\
+ -Delastic.apm.environment=production \\\\
+ -Delastic.apm.application_packages=org.example \\\\
+ -jar my-application.jar"
+ `);
+ });
+ it('renders with secret token and url', () => {
+ const commands = getCommands({
+ variantId: 'java',
+ policyDetails: {
+ apmServerUrl: 'localhost:8220',
+ secretToken: 'foobar',
+ },
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "java -javaagent:/path/to/elastic-apm-agent-.jar \\\\
+ -Delastic.apm.service_name=my-application \\\\
+ -Delastic.apm.server_urls=localhost:8220 \\\\
+ -Delastic.apm.secret_token=foobar \\\\
+ -Delastic.apm.environment=production \\\\
+ -Delastic.apm.application_packages=org.example \\\\
+ -jar my-application.jar"
+ `);
+ });
+ });
+ describe('RUM(js) agent', () => {
+ it('renders empty commands', () => {
+ const commands = getCommands({
+ variantId: 'js',
+ policyDetails: {},
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "import { init as initApm } from '@elastic/apm-rum'
+ var apm = initApm({
+
+ // Set required service name (allowed characters: a-z, A-Z, 0-9, -, _, and space)
+ serviceName: 'your-app-name',
+
+ // Set custom APM Server URL (default: http://localhost:8200)
+ serverUrl: '',
+
+ // Set the service version (required for source map feature)
+ serviceVersion: '',
+
+ // Set the service environment
+ environment: 'production'
+ })"
+ `);
+ });
+ it('renders with secret token and url', () => {
+ const commands = getCommands({
+ variantId: 'js',
+ policyDetails: {
+ apmServerUrl: 'localhost:8220',
+ secretToken: 'foobar',
+ },
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "import { init as initApm } from '@elastic/apm-rum'
+ var apm = initApm({
+
+ // Set required service name (allowed characters: a-z, A-Z, 0-9, -, _, and space)
+ serviceName: 'your-app-name',
+
+ // Set custom APM Server URL (default: http://localhost:8200)
+ serverUrl: 'localhost:8220',
+
+ // Set the service version (required for source map feature)
+ serviceVersion: '',
+
+ // Set the service environment
+ environment: 'production'
+ })"
+ `);
+ });
+ });
+ describe('Node.js agent', () => {
+ it('renders empty commands', () => {
+ const commands = getCommands({
+ variantId: 'node',
+ policyDetails: {},
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "// Add this to the VERY top of the first file loaded in your app
+ var apm = require('elastic-apm-node').start({
+
+ // Override the service name from package.json
+ // Allowed characters: a-z, A-Z, 0-9, -, _, and space
+ serviceName: '',
+
+ // Use if APM Server requires a secret token
+ secretToken: '',
+
+ // Set the custom APM Server URL (default: http://localhost:8200)
+ serverUrl: '',
+
+ // Set the service environment
+ environment: 'production'
+ })"
+ `);
+ });
+ it('renders with secret token and url', () => {
+ const commands = getCommands({
+ variantId: 'node',
+ policyDetails: {
+ apmServerUrl: 'localhost:8220',
+ secretToken: 'foobar',
+ },
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "// Add this to the VERY top of the first file loaded in your app
+ var apm = require('elastic-apm-node').start({
+
+ // Override the service name from package.json
+ // Allowed characters: a-z, A-Z, 0-9, -, _, and space
+ serviceName: '',
+
+ // Use if APM Server requires a secret token
+ secretToken: 'foobar',
+
+ // Set the custom APM Server URL (default: http://localhost:8200)
+ serverUrl: 'localhost:8220',
+
+ // Set the service environment
+ environment: 'production'
+ })"
+ `);
+ });
+ });
+ describe('Django agent', () => {
+ it('renders empty commands', () => {
+ const commands = getCommands({
+ variantId: 'django',
+ policyDetails: {},
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "# Add the agent to the installed apps
+ INSTALLED_APPS = (
+ 'elasticapm.contrib.django',
+ # ...
+ )
+
+ ELASTIC_APM = {curlyOpen}
+ # Set the required service name. Allowed characters:
+ # a-z, A-Z, 0-9, -, _, and space
+ 'SERVICE_NAME': '',
+
+ # Use if APM Server requires a secret token
+ 'SECRET_TOKEN': '',
+
+ # Set the custom APM Server URL (default: http://localhost:8200)
+ 'SERVER_URL': '',
+
+ # Set the service environment
+ 'ENVIRONMENT': 'production',
+ {curlyClose}
+
+ # To send performance metrics, add our tracing middleware:
+ MIDDLEWARE = (
+ 'elasticapm.contrib.django.middleware.TracingMiddleware',
+ #...
+ )"
+ `);
+ });
+ it('renders with secret token and url', () => {
+ const commands = getCommands({
+ variantId: 'django',
+ policyDetails: {
+ apmServerUrl: 'localhost:8220',
+ secretToken: 'foobar',
+ },
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "# Add the agent to the installed apps
+ INSTALLED_APPS = (
+ 'elasticapm.contrib.django',
+ # ...
+ )
+
+ ELASTIC_APM = {curlyOpen}
+ # Set the required service name. Allowed characters:
+ # a-z, A-Z, 0-9, -, _, and space
+ 'SERVICE_NAME': '',
+
+ # Use if APM Server requires a secret token
+ 'SECRET_TOKEN': 'foobar',
+
+ # Set the custom APM Server URL (default: http://localhost:8200)
+ 'SERVER_URL': 'localhost:8220',
+
+ # Set the service environment
+ 'ENVIRONMENT': 'production',
+ {curlyClose}
+
+ # To send performance metrics, add our tracing middleware:
+ MIDDLEWARE = (
+ 'elasticapm.contrib.django.middleware.TracingMiddleware',
+ #...
+ )"
+ `);
+ });
+ });
+ describe('Flask agent', () => {
+ it('renders empty commands', () => {
+ const commands = getCommands({
+ variantId: 'flask',
+ policyDetails: {},
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "# initialize using environment variables
+ from elasticapm.contrib.flask import ElasticAPM
+ app = Flask(__name__)
+ apm = ElasticAPM(app)
+
+ # or configure to use ELASTIC_APM in your application's settings
+ from elasticapm.contrib.flask import ElasticAPM
+ app.config['ELASTIC_APM'] = {curlyOpen}
+ # Set the required service name. Allowed characters:
+ # a-z, A-Z, 0-9, -, _, and space
+ 'SERVICE_NAME': '',
+
+ # Use if APM Server requires a secret token
+ 'SECRET_TOKEN': '',
+
+ # Set the custom APM Server URL (default: http://localhost:8200)
+ 'SERVER_URL': '',
+
+ # Set the service environment
+ 'ENVIRONMENT': 'production',
+ {curlyClose}
+
+ apm = ElasticAPM(app)"
+ `);
+ });
+ it('renders with secret token and url', () => {
+ const commands = getCommands({
+ variantId: 'flask',
+ policyDetails: {
+ apmServerUrl: 'localhost:8220',
+ secretToken: 'foobar',
+ },
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "# initialize using environment variables
+ from elasticapm.contrib.flask import ElasticAPM
+ app = Flask(__name__)
+ apm = ElasticAPM(app)
+
+ # or configure to use ELASTIC_APM in your application's settings
+ from elasticapm.contrib.flask import ElasticAPM
+ app.config['ELASTIC_APM'] = {curlyOpen}
+ # Set the required service name. Allowed characters:
+ # a-z, A-Z, 0-9, -, _, and space
+ 'SERVICE_NAME': '',
+
+ # Use if APM Server requires a secret token
+ 'SECRET_TOKEN': 'foobar',
+
+ # Set the custom APM Server URL (default: http://localhost:8200)
+ 'SERVER_URL': 'localhost:8220',
+
+ # Set the service environment
+ 'ENVIRONMENT': 'production',
+ {curlyClose}
+
+ apm = ElasticAPM(app)"
+ `);
+ });
+ });
+ describe('Ruby on Rails agent', () => {
+ it('renders empty commands', () => {
+ const commands = getCommands({
+ variantId: 'rails',
+ policyDetails: {},
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "# config/elastic_apm.yml:
+
+ # Set the service name - allowed characters: a-z, A-Z, 0-9, -, _ and space
+ # Defaults to the name of your Rails app
+ service_name: 'my-service'
+
+ # Use if APM Server requires a secret token
+ secret_token: ''
+
+ # Set the custom APM Server URL (default: http://localhost:8200)
+ server_url: ''
+
+ # Set the service environment
+ environment: 'production'"
+ `);
+ });
+ it('renders with secret token and url', () => {
+ const commands = getCommands({
+ variantId: 'rails',
+ policyDetails: {
+ apmServerUrl: 'localhost:8220',
+ secretToken: 'foobar',
+ },
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "# config/elastic_apm.yml:
+
+ # Set the service name - allowed characters: a-z, A-Z, 0-9, -, _ and space
+ # Defaults to the name of your Rails app
+ service_name: 'my-service'
+
+ # Use if APM Server requires a secret token
+ secret_token: 'foobar'
+
+ # Set the custom APM Server URL (default: http://localhost:8200)
+ server_url: 'localhost:8220'
+
+ # Set the service environment
+ environment: 'production'"
+ `);
+ });
+ });
+ describe('Rack agent', () => {
+ it('renders empty commands', () => {
+ const commands = getCommands({
+ variantId: 'rack',
+ policyDetails: {},
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "# config/elastic_apm.yml:
+
+ # Set the service name - allowed characters: a-z, A-Z, 0-9, -, _ and space
+ # Defaults to the name of your Rack app's class.
+ service_name: 'my-service'
+
+ # Use if APM Server requires a token
+ secret_token: ''
+
+ # Set custom APM Server URL (default: http://localhost:8200)
+ server_url: '',
+
+ # Set the service environment
+ environment: 'production'"
+ `);
+ });
+ it('renders with secret token and url', () => {
+ const commands = getCommands({
+ variantId: 'rack',
+ policyDetails: {
+ apmServerUrl: 'localhost:8220',
+ secretToken: 'foobar',
+ },
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "# config/elastic_apm.yml:
+
+ # Set the service name - allowed characters: a-z, A-Z, 0-9, -, _ and space
+ # Defaults to the name of your Rack app's class.
+ service_name: 'my-service'
+
+ # Use if APM Server requires a token
+ secret_token: 'foobar'
+
+ # Set custom APM Server URL (default: http://localhost:8200)
+ server_url: 'localhost:8220',
+
+ # Set the service environment
+ environment: 'production'"
+ `);
+ });
+ });
+ describe('Go agent', () => {
+ it('renders empty commands', () => {
+ const commands = getCommands({
+ variantId: 'go',
+ policyDetails: {},
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "# Initialize using environment variables:
+
+ # Set the service name. Allowed characters: # a-z, A-Z, 0-9, -, _, and space.
+ # If ELASTIC_APM_SERVICE_NAME is not specified, the executable name will be used.
+ export ELASTIC_APM_SERVICE_NAME=
+
+ # Set custom APM Server URL (default: http://localhost:8200)
+ export ELASTIC_APM_SERVER_URL=
+
+ # Use if APM Server requires a secret token
+ export ELASTIC_APM_SECRET_TOKEN=
+
+ # Set the service environment
+ export ELASTIC_APM_ENVIRONMENT=
+ "
+ `);
+ });
+ it('renders with secret token and url', () => {
+ const commands = getCommands({
+ variantId: 'go',
+ policyDetails: {
+ apmServerUrl: 'localhost:8220',
+ secretToken: 'foobar',
+ },
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "# Initialize using environment variables:
+
+ # Set the service name. Allowed characters: # a-z, A-Z, 0-9, -, _, and space.
+ # If ELASTIC_APM_SERVICE_NAME is not specified, the executable name will be used.
+ export ELASTIC_APM_SERVICE_NAME=
+
+ # Set custom APM Server URL (default: http://localhost:8200)
+ export ELASTIC_APM_SERVER_URL=localhost:8220
+
+ # Use if APM Server requires a secret token
+ export ELASTIC_APM_SECRET_TOKEN=foobar
+
+ # Set the service environment
+ export ELASTIC_APM_ENVIRONMENT=
+ "
+ `);
+ });
+ });
+ describe('dotNet agent', () => {
+ it('renders empty commands', () => {
+ const commands = getCommands({
+ variantId: 'dotnet',
+ policyDetails: {},
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "{
+ \\"ElasticApm\\": {
+ \\"SecretToken\\": \\"\\",
+ \\"ServerUrls\\": \\"\\", //Set custom APM Server URL (default: http://localhost:8200)
+ \\"ServiceName\\": \\"MyApp\\", //allowed characters: a-z, A-Z, 0-9, -, _, and space. Default is the entry assembly of the application
+ \\"Environment\\": \\"production\\", // Set the service environment
+ }
+ }"
+ `);
+ });
+ it('renders with secret token and url', () => {
+ const commands = getCommands({
+ variantId: 'dotnet',
+ policyDetails: {
+ apmServerUrl: 'localhost:8220',
+ secretToken: 'foobar',
+ },
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "{
+ \\"ElasticApm\\": {
+ \\"SecretToken\\": \\"foobar\\",
+ \\"ServerUrls\\": \\"localhost:8220\\", //Set custom APM Server URL (default: http://localhost:8200)
+ \\"ServiceName\\": \\"MyApp\\", //allowed characters: a-z, A-Z, 0-9, -, _, and space. Default is the entry assembly of the application
+ \\"Environment\\": \\"production\\", // Set the service environment
+ }
+ }"
+ `);
+ });
+ });
+ describe('PHP agent', () => {
+ it('renders empty commands', () => {
+ const commands = getCommands({
+ variantId: 'php',
+ policyDetails: {},
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "elastic_apm.server_url=\\"\\"
+ elastic.apm.secret_token=\\"\\"
+ elastic_apm.service_name=\\"My service\\"
+ "
+ `);
+ });
+ it('renders with secret token and url', () => {
+ const commands = getCommands({
+ variantId: 'php',
+ policyDetails: {
+ apmServerUrl: 'localhost:8220',
+ secretToken: 'foobar',
+ },
+ });
+ expect(commands).not.toBe('');
+ expect(commands).toMatchInlineSnapshot(`
+ "elastic_apm.server_url=\\"localhost:8220\\"
+ elastic.apm.secret_token=\\"foobar\\"
+ elastic_apm.service_name=\\"My service\\"
+ "
+ `);
+ });
+ });
+});
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/commands/get_commands.ts b/x-pack/plugins/apm/public/tutorial/config_agent/commands/get_commands.ts
new file mode 100644
index 0000000000000..73a388c3f735e
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/commands/get_commands.ts
@@ -0,0 +1,48 @@
+/*
+ * 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 Mustache from 'mustache';
+import { java } from './java';
+import { node } from './node';
+import { django } from './django';
+import { flask } from './flask';
+import { rails } from './rails';
+import { rack } from './rack';
+import { go } from './go';
+import { dotnet } from './dotnet';
+import { php } from './php';
+import { rum, rumScript } from './rum';
+
+const commandsMap: Record = {
+ java,
+ node,
+ django,
+ flask,
+ rails,
+ rack,
+ go,
+ dotnet,
+ php,
+ js: rum,
+ js_script: rumScript,
+};
+
+export function getCommands({
+ variantId,
+ policyDetails,
+}: {
+ variantId: string;
+ policyDetails: {
+ apmServerUrl?: string;
+ secretToken?: string;
+ };
+}) {
+ const commands = commandsMap[variantId];
+ if (!commands) {
+ return '';
+ }
+ return Mustache.render(commands, policyDetails);
+}
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/commands/go.ts b/x-pack/plugins/apm/public/tutorial/config_agent/commands/go.ts
new file mode 100644
index 0000000000000..a3900420d6fde
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/commands/go.ts
@@ -0,0 +1,58 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+
+export const go = `# ${i18n.translate(
+ 'xpack.apm.tutorial.goClient.configure.commands.initializeUsingEnvironmentVariablesComment',
+ {
+ defaultMessage: 'Initialize using environment variables:',
+ }
+)}
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.goClient.configure.commands.setServiceNameComment',
+ {
+ defaultMessage:
+ 'Set the service name. Allowed characters: # a-z, A-Z, 0-9, -, _, and space.',
+ }
+)}
+# ${i18n.translate(
+ 'xpack.apm.tutorial.goClient.configure.commands.usedExecutableNameComment',
+ {
+ defaultMessage:
+ 'If ELASTIC_APM_SERVICE_NAME is not specified, the executable name will be used.',
+ }
+)}
+export ELASTIC_APM_SERVICE_NAME=
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.goClient.configure.commands.setCustomApmServerUrlComment',
+ {
+ defaultMessage:
+ 'Set custom APM Server URL (default: {defaultApmServerUrl})',
+ values: { defaultApmServerUrl: 'http://localhost:8200' },
+ }
+)}
+export ELASTIC_APM_SERVER_URL={{{apmServerUrl}}}
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.goClient.configure.commands.useIfApmRequiresTokenComment',
+ {
+ defaultMessage: 'Use if APM Server requires a secret token',
+ }
+)}
+export ELASTIC_APM_SECRET_TOKEN={{{secretToken}}}
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.goClient.configure.commands.setServiceEnvironment',
+ {
+ defaultMessage: 'Set the service environment',
+ }
+)}
+export ELASTIC_APM_ENVIRONMENT=
+`;
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/commands/java.ts b/x-pack/plugins/apm/public/tutorial/config_agent/commands/java.ts
new file mode 100644
index 0000000000000..249907a9b0c4b
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/commands/java.ts
@@ -0,0 +1,14 @@
+/*
+ * 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 const java = `java -javaagent:/path/to/elastic-apm-agent-.jar \\
+-Delastic.apm.service_name=my-application \\
+-Delastic.apm.server_urls={{{apmServerUrl}}} \\
+-Delastic.apm.secret_token={{{secretToken}}} \\
+-Delastic.apm.environment=production \\
+-Delastic.apm.application_packages=org.example \\
+-jar my-application.jar`;
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/commands/node.ts b/x-pack/plugins/apm/public/tutorial/config_agent/commands/node.ts
new file mode 100644
index 0000000000000..31f9fac0ed480
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/commands/node.ts
@@ -0,0 +1,57 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+
+export const node = `// ${i18n.translate(
+ 'xpack.apm.tutorial.nodeClient.configure.commands.addThisToTheFileTopComment',
+ {
+ defaultMessage:
+ 'Add this to the VERY top of the first file loaded in your app',
+ }
+)}
+var apm = require('elastic-apm-node').start({
+
+// ${i18n.translate(
+ 'xpack.apm.tutorial.nodeClient.configure.commands.setRequiredServiceNameComment',
+ {
+ defaultMessage: 'Override the service name from package.json',
+ }
+)}
+// ${i18n.translate(
+ 'xpack.apm.tutorial.nodeClient.configure.commands.allowedCharactersComment',
+ {
+ defaultMessage: 'Allowed characters: a-z, A-Z, 0-9, -, _, and space',
+ }
+)}
+serviceName: '',
+
+// ${i18n.translate(
+ 'xpack.apm.tutorial.nodeClient.configure.commands.useIfApmRequiresTokenComment',
+ {
+ defaultMessage: 'Use if APM Server requires a secret token',
+ }
+)}
+secretToken: '{{{secretToken}}}',
+
+// ${i18n.translate(
+ 'xpack.apm.tutorial.nodeClient.configure.commands.setCustomApmServerUrlComment',
+ {
+ defaultMessage:
+ 'Set the custom APM Server URL (default: {defaultApmServerUrl})',
+ values: { defaultApmServerUrl: 'http://localhost:8200' },
+ }
+)}
+serverUrl: '{{{apmServerUrl}}}',
+
+// ${i18n.translate(
+ 'xpack.apm.tutorial.nodeClient.configure.commands.setCustomServiceEnvironmentComment',
+ {
+ defaultMessage: 'Set the service environment',
+ }
+)}
+environment: 'production'
+})`;
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/commands/php.ts b/x-pack/plugins/apm/public/tutorial/config_agent/commands/php.ts
new file mode 100644
index 0000000000000..ea7e8764f89ad
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/commands/php.ts
@@ -0,0 +1,11 @@
+/*
+ * 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 const php = `elastic_apm.server_url="{{{apmServerUrl}}}"
+elastic.apm.secret_token="{{{secretToken}}}"
+elastic_apm.service_name="My service"
+`;
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/commands/rack.ts b/x-pack/plugins/apm/public/tutorial/config_agent/commands/rack.ts
new file mode 100644
index 0000000000000..9195ad9f15666
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/commands/rack.ts
@@ -0,0 +1,50 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+
+export const rack = `# config/elastic_apm.yml:
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.rackClient.createConfig.commands.setServiceNameComment',
+ {
+ defaultMessage:
+ 'Set the service name - allowed characters: a-z, A-Z, 0-9, -, _ and space',
+ }
+)}
+# ${i18n.translate(
+ 'xpack.apm.tutorial.rackClient.createConfig.commands.defaultsToTheNameOfRackAppClassComment',
+ {
+ defaultMessage: "Defaults to the name of your Rack app's class.",
+ }
+)}
+service_name: 'my-service'
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.rackClient.createConfig.commands.useIfApmServerRequiresTokenComment',
+ {
+ defaultMessage: 'Use if APM Server requires a token',
+ }
+)}
+secret_token: '{{{secretToken}}}'
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.rackClient.createConfig.commands.setCustomApmServerComment',
+ {
+ defaultMessage: 'Set custom APM Server URL (default: {defaultServerUrl})',
+ values: { defaultServerUrl: 'http://localhost:8200' },
+ }
+)}
+server_url: '{{{apmServerUrl}}}',
+
+# ${i18n.translate(
+ 'xpack.apm.tutorial.rackClient.createConfig.commands.setServiceEnvironment',
+ {
+ defaultMessage: 'Set the service environment',
+ }
+)}
+environment: 'production'`;
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/commands/rails.ts b/x-pack/plugins/apm/public/tutorial/config_agent/commands/rails.ts
new file mode 100644
index 0000000000000..0f8a5508e1ceb
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/commands/rails.ts
@@ -0,0 +1,21 @@
+/*
+ * 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 const rails = `# config/elastic_apm.yml:
+
+# Set the service name - allowed characters: a-z, A-Z, 0-9, -, _ and space
+# Defaults to the name of your Rails app
+service_name: 'my-service'
+
+# Use if APM Server requires a secret token
+secret_token: '{{{secretToken}}}'
+
+# Set the custom APM Server URL (default: http://localhost:8200)
+server_url: '{{{apmServerUrl}}}'
+
+# Set the service environment
+environment: 'production'`;
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/commands/rum.ts b/x-pack/plugins/apm/public/tutorial/config_agent/commands/rum.ts
new file mode 100644
index 0000000000000..f5de61f64c63a
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/commands/rum.ts
@@ -0,0 +1,58 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+
+export const rum = `import { init as initApm } from '@elastic/apm-rum'
+var apm = initApm({
+
+ // ${i18n.translate(
+ 'xpack.apm.tutorial.jsClient.installDependency.commands.setRequiredServiceNameComment',
+ {
+ defaultMessage:
+ 'Set required service name (allowed characters: a-z, A-Z, 0-9, -, _, and space)',
+ }
+ )}
+ serviceName: 'your-app-name',
+
+ // ${i18n.translate(
+ 'xpack.apm.tutorial.jsClient.installDependency.commands.setCustomApmServerUrlComment',
+ {
+ defaultMessage:
+ 'Set custom APM Server URL (default: {defaultApmServerUrl})',
+ values: { defaultApmServerUrl: 'http://localhost:8200' },
+ }
+ )}
+ serverUrl: '{{{apmServerUrl}}}',
+
+ // ${i18n.translate(
+ 'xpack.apm.tutorial.jsClient.installDependency.commands.setServiceVersionComment',
+ {
+ defaultMessage:
+ 'Set the service version (required for source map feature)',
+ }
+ )}
+ serviceVersion: '',
+
+ // ${i18n.translate(
+ 'xpack.apm.tutorial.jsClient.installDependency.commands.setServiceEnvironmentComment',
+ {
+ defaultMessage: 'Set the service environment',
+ }
+ )}
+ environment: 'production'
+})`;
+
+export const rumScript = `\
+
+
+`;
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/config_agent.stories.tsx b/x-pack/plugins/apm/public/tutorial/config_agent/config_agent.stories.tsx
new file mode 100644
index 0000000000000..33f171ab88247
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/config_agent.stories.tsx
@@ -0,0 +1,117 @@
+/*
+ * 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 { Story } from '@storybook/react';
+import { HttpStart } from 'kibana/public';
+import React from 'react';
+import TutorialConfigAgent from './';
+import { APIReturnType } from '../..//services/rest/createCallApmApi';
+
+export type APIResponseType = APIReturnType<'GET /api/apm/fleet/agents'>;
+
+interface Args {
+ apmAgent: string;
+ onPrem: boolean;
+ hasFleetPoliciesWithApmIntegration: boolean;
+ hasCloudPolicyWithApmIntegration: boolean;
+}
+
+const policyElasticAgentOnCloudAgent: APIResponseType['fleetAgents'][0] = {
+ id: 'policy-elastic-agent-on-cloud',
+ name: 'Elastic Cloud agent policy',
+ apmServerUrl: 'apm_cloud_url',
+ secretToken: 'apm_cloud_token',
+};
+
+const fleetAgents: APIResponseType['fleetAgents'] = [
+ {
+ id: '1',
+ name: 'agent foo',
+ apmServerUrl: 'foo',
+ secretToken: 'foo',
+ },
+ {
+ id: '2',
+ name: 'agent bar',
+ apmServerUrl: 'bar',
+ secretToken: 'bar',
+ },
+];
+
+function Wrapper({
+ hasFleetPoliciesWithApmIntegration,
+ apmAgent,
+ onPrem,
+ hasCloudPolicyWithApmIntegration,
+}: Args) {
+ const http = ({
+ get: () => ({
+ fleetAgents: [
+ ...(hasFleetPoliciesWithApmIntegration ? fleetAgents : []),
+ ...(hasCloudPolicyWithApmIntegration
+ ? [policyElasticAgentOnCloudAgent]
+ : []),
+ ],
+ cloudStandaloneSetup: {
+ apmServerUrl: 'cloud_url',
+ secretToken: 'foo',
+ },
+ }),
+ } as unknown) as HttpStart;
+ return (
+
+ );
+}
+export const Integration: Story = (args) => {
+ return ;
+};
+
+Integration.args = {
+ apmAgent: 'java',
+ onPrem: true,
+ hasFleetPoliciesWithApmIntegration: false,
+ hasCloudPolicyWithApmIntegration: false,
+};
+
+export default {
+ title: 'app/Tutorial/AgentConfig',
+ component: TutorialConfigAgent,
+ argTypes: {
+ apmAgent: {
+ control: {
+ type: 'select',
+ options: [
+ 'java',
+ 'node',
+ 'django',
+ 'flask',
+ 'rails',
+ 'rack',
+ 'go',
+ 'dotnet',
+ 'php',
+ 'js',
+ 'js_script',
+ ],
+ },
+ },
+ onPrem: {
+ control: { type: 'boolean', options: [true, false] },
+ },
+ hasFleetPoliciesWithApmIntegration: {
+ control: { type: 'boolean', options: [true, false] },
+ },
+ hasCloudPolicyWithApmIntegration: {
+ control: { type: 'boolean', options: [true, false] },
+ },
+ },
+};
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/copy_commands.tsx b/x-pack/plugins/apm/public/tutorial/config_agent/copy_commands.tsx
new file mode 100644
index 0000000000000..c5261cfc1dc04
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/copy_commands.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 { EuiButton, EuiCopy } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import React from 'react';
+
+interface Props {
+ commands: string;
+}
+export function CopyCommands({ commands }: Props) {
+ return (
+
+ {(copy) => (
+
+ {i18n.translate('xpack.apm.tutorial.copySnippet', {
+ defaultMessage: 'Copy snippet',
+ })}
+
+ )}
+
+ );
+}
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/get_policy_options.test.ts b/x-pack/plugins/apm/public/tutorial/config_agent/get_policy_options.test.ts
new file mode 100644
index 0000000000000..90c9aab80f6f5
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/get_policy_options.test.ts
@@ -0,0 +1,318 @@
+/*
+ * 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 { getPolicyOptions } from './get_policy_options';
+import { APIReturnType } from '../../services/rest/createCallApmApi';
+
+type APIResponseType = APIReturnType<'GET /api/apm/fleet/agents'>;
+
+const policyElasticAgentOnCloudAgent = {
+ id: 'policy-elastic-agent-on-cloud',
+ name: 'Elastic Cloud agent policy',
+ apmServerUrl: 'apm_cloud_url',
+ secretToken: 'apm_cloud_token',
+};
+
+const fleetAgents = [
+ {
+ id: '1',
+ name: 'agent foo',
+ apmServerUrl: 'foo',
+ secretToken: 'foo',
+ },
+ {
+ id: '2',
+ name: 'agent bar',
+ apmServerUrl: 'bar',
+ secretToken: 'bar',
+ },
+];
+
+describe('getPolicyOptions', () => {
+ describe('running on cloud', () => {
+ describe('with APM on cloud', () => {
+ it('shows apm on cloud standalone option', () => {
+ const data: APIResponseType = {
+ fleetAgents: [],
+ cloudStandaloneSetup: {
+ apmServerUrl: 'cloud_url',
+ secretToken: 'cloud_token',
+ },
+ };
+ const options = getPolicyOptions({
+ isCloudEnabled: true,
+ data,
+ });
+ expect(options).toEqual([
+ {
+ key: 'cloud',
+ type: 'standalone',
+ label: 'Default Standalone configuration',
+ apmServerUrl: 'cloud_url',
+ secretToken: 'cloud_token',
+ isVisible: true,
+ isSelected: true,
+ },
+ ]);
+ });
+ it('shows apm on cloud standalone option and fleet agents options', () => {
+ const data: APIResponseType = {
+ fleetAgents,
+ cloudStandaloneSetup: {
+ apmServerUrl: 'cloud_url',
+ secretToken: 'cloud_token',
+ },
+ };
+ const options = getPolicyOptions({
+ isCloudEnabled: true,
+ data,
+ });
+
+ expect(options).toEqual([
+ {
+ key: 'cloud',
+ type: 'standalone',
+ label: 'Default Standalone configuration',
+ apmServerUrl: 'cloud_url',
+ secretToken: 'cloud_token',
+ isVisible: true,
+ isSelected: true,
+ },
+
+ {
+ key: '1',
+ type: 'fleetAgents',
+ label: 'agent foo',
+ apmServerUrl: 'foo',
+ secretToken: 'foo',
+ isVisible: true,
+ isSelected: false,
+ },
+ {
+ key: '2',
+ type: 'fleetAgents',
+ label: 'agent bar',
+ apmServerUrl: 'bar',
+ secretToken: 'bar',
+ isVisible: true,
+ isSelected: false,
+ },
+ ]);
+ });
+ it('selects policy elastic agent on cloud when available', () => {
+ const data: APIResponseType = {
+ fleetAgents: [policyElasticAgentOnCloudAgent, ...fleetAgents],
+ cloudStandaloneSetup: {
+ apmServerUrl: 'cloud_url',
+ secretToken: 'cloud_token',
+ },
+ };
+ const options = getPolicyOptions({
+ isCloudEnabled: true,
+ data,
+ });
+
+ expect(options).toEqual([
+ {
+ key: 'policy-elastic-agent-on-cloud',
+ type: 'fleetAgents',
+ label: 'Elastic Cloud agent policy',
+ apmServerUrl: 'apm_cloud_url',
+ secretToken: 'apm_cloud_token',
+ isVisible: true,
+ isSelected: true,
+ },
+ {
+ key: '1',
+ type: 'fleetAgents',
+ label: 'agent foo',
+ apmServerUrl: 'foo',
+ secretToken: 'foo',
+ isVisible: true,
+ isSelected: false,
+ },
+ {
+ key: '2',
+ type: 'fleetAgents',
+ label: 'agent bar',
+ apmServerUrl: 'bar',
+ secretToken: 'bar',
+ isVisible: true,
+ isSelected: false,
+ },
+ ]);
+ });
+ });
+ describe('with APM on prem', () => {
+ it('shows apm on prem standalone option', () => {
+ const data: APIResponseType = {
+ fleetAgents: [],
+ cloudStandaloneSetup: undefined,
+ };
+ const options = getPolicyOptions({
+ isCloudEnabled: true,
+ data,
+ });
+
+ expect(options).toEqual([
+ {
+ key: 'onPrem',
+ type: 'standalone',
+ label: 'Default Standalone configuration',
+ apmServerUrl: 'http://localhost:8200',
+ secretToken: '',
+ isVisible: true,
+ isSelected: true,
+ },
+ ]);
+ });
+ it('shows apm on prem standalone option and fleet agents options', () => {
+ const data: APIResponseType = {
+ fleetAgents,
+ cloudStandaloneSetup: undefined,
+ };
+ const options = getPolicyOptions({
+ isCloudEnabled: true,
+ data,
+ });
+ expect(options).toEqual([
+ {
+ key: 'onPrem',
+ type: 'standalone',
+ label: 'Default Standalone configuration',
+ apmServerUrl: 'http://localhost:8200',
+ secretToken: '',
+ isVisible: true,
+ isSelected: true,
+ },
+
+ {
+ key: '1',
+ type: 'fleetAgents',
+ label: 'agent foo',
+ apmServerUrl: 'foo',
+ secretToken: 'foo',
+ isVisible: true,
+ isSelected: false,
+ },
+ {
+ key: '2',
+ type: 'fleetAgents',
+ label: 'agent bar',
+ apmServerUrl: 'bar',
+ secretToken: 'bar',
+ isVisible: true,
+ isSelected: false,
+ },
+ ]);
+ });
+ it('selects policy elastic agent on cloud when available', () => {
+ const data: APIResponseType = {
+ fleetAgents: [policyElasticAgentOnCloudAgent, ...fleetAgents],
+ cloudStandaloneSetup: undefined,
+ };
+ const options = getPolicyOptions({
+ isCloudEnabled: true,
+ data,
+ });
+
+ expect(options).toEqual([
+ {
+ key: 'policy-elastic-agent-on-cloud',
+ type: 'fleetAgents',
+ label: 'Elastic Cloud agent policy',
+ apmServerUrl: 'apm_cloud_url',
+ secretToken: 'apm_cloud_token',
+ isVisible: true,
+ isSelected: true,
+ },
+ {
+ key: '1',
+ type: 'fleetAgents',
+ label: 'agent foo',
+ apmServerUrl: 'foo',
+ secretToken: 'foo',
+ isVisible: true,
+ isSelected: false,
+ },
+ {
+ key: '2',
+ type: 'fleetAgents',
+ label: 'agent bar',
+ apmServerUrl: 'bar',
+ secretToken: 'bar',
+ isVisible: true,
+ isSelected: false,
+ },
+ ]);
+ });
+ });
+ });
+ describe('Running on prem', () => {
+ it('shows apm on prem standalone option', () => {
+ const data: APIResponseType = {
+ fleetAgents: [],
+ cloudStandaloneSetup: undefined,
+ };
+ const options = getPolicyOptions({
+ isCloudEnabled: false,
+ data,
+ });
+
+ expect(options).toEqual([
+ {
+ key: 'onPrem',
+ type: 'standalone',
+ label: 'Default Standalone configuration',
+ apmServerUrl: 'http://localhost:8200',
+ secretToken: '',
+ isVisible: true,
+ isSelected: true,
+ },
+ ]);
+ });
+ it('shows apm on prem standalone option and fleet agents options', () => {
+ const data: APIResponseType = {
+ fleetAgents,
+ cloudStandaloneSetup: undefined,
+ };
+ const options = getPolicyOptions({
+ isCloudEnabled: false,
+ data,
+ });
+
+ expect(options).toEqual([
+ {
+ key: 'onPrem',
+ type: 'standalone',
+ label: 'Default Standalone configuration',
+ apmServerUrl: 'http://localhost:8200',
+ secretToken: '',
+ isVisible: true,
+ isSelected: true,
+ },
+ {
+ key: '1',
+ type: 'fleetAgents',
+ label: 'agent foo',
+ apmServerUrl: 'foo',
+ secretToken: 'foo',
+ isVisible: true,
+ isSelected: false,
+ },
+ {
+ key: '2',
+ type: 'fleetAgents',
+ label: 'agent bar',
+ apmServerUrl: 'bar',
+ secretToken: 'bar',
+ isVisible: true,
+ isSelected: false,
+ },
+ ]);
+ });
+ });
+});
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/get_policy_options.ts b/x-pack/plugins/apm/public/tutorial/config_agent/get_policy_options.ts
new file mode 100644
index 0000000000000..afbdc867d3e0a
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/get_policy_options.ts
@@ -0,0 +1,67 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import { APIResponseType } from './';
+
+const POLICY_ELASTIC_AGENT_ON_CLOUD = 'policy-elastic-agent-on-cloud';
+
+const DEFAULT_STANDALONE_CONFIG_LABEL = i18n.translate(
+ 'xpack.apm.tutorial.agent_config.defaultStandaloneConfig',
+ { defaultMessage: 'Default Standalone configuration' }
+);
+
+export type PolicyOption = ReturnType[0];
+
+export function getPolicyOptions({
+ isCloudEnabled,
+ data,
+}: {
+ isCloudEnabled: boolean;
+ data: APIResponseType;
+}) {
+ const isCloudVisible = !!(
+ isCloudEnabled && data.cloudStandaloneSetup?.apmServerUrl
+ );
+
+ const fleetAgentsOptions = data.fleetAgents.map((agent) => {
+ return {
+ key: agent.id,
+ type: 'fleetAgents',
+ label: agent.name,
+ apmServerUrl: agent.apmServerUrl,
+ secretToken: agent.secretToken,
+ isVisible: true,
+ isSelected: agent.id === POLICY_ELASTIC_AGENT_ON_CLOUD,
+ };
+ });
+
+ const hasFleetAgentsSelected = fleetAgentsOptions.some(
+ ({ isSelected }) => isSelected
+ );
+
+ return [
+ {
+ key: 'cloud',
+ type: 'standalone',
+ label: DEFAULT_STANDALONE_CONFIG_LABEL,
+ apmServerUrl: data.cloudStandaloneSetup?.apmServerUrl,
+ secretToken: data.cloudStandaloneSetup?.secretToken,
+ isVisible: isCloudVisible && !hasFleetAgentsSelected,
+ isSelected: !hasFleetAgentsSelected,
+ },
+ {
+ key: 'onPrem',
+ type: 'standalone',
+ label: DEFAULT_STANDALONE_CONFIG_LABEL,
+ apmServerUrl: 'http://localhost:8200',
+ secretToken: '',
+ isVisible: !isCloudVisible && !hasFleetAgentsSelected,
+ isSelected: !hasFleetAgentsSelected,
+ },
+ ...fleetAgentsOptions,
+ ].filter(({ isVisible }) => isVisible);
+}
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/index.test.tsx b/x-pack/plugins/apm/public/tutorial/config_agent/index.test.tsx
new file mode 100644
index 0000000000000..8f8afe58506a6
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/index.test.tsx
@@ -0,0 +1,205 @@
+/*
+ * 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 { fireEvent, render, screen } from '@testing-library/react';
+import { HttpStart } from 'kibana/public';
+import React from 'react';
+import TutorialConfigAgent from './';
+
+const policyElasticAgentOnCloudAgent = {
+ id: 'policy-elastic-agent-on-cloud',
+ name: 'Elastic Cloud agent policy',
+ apmServerUrl: 'apm_cloud_url',
+ secretToken: 'apm_cloud_token',
+};
+
+const fleetAgents = [
+ {
+ id: '1',
+ name: 'agent foo',
+ apmServerUrl: 'foo',
+ secretToken: 'foo',
+ },
+ {
+ id: '2',
+ name: 'agent bar',
+ apmServerUrl: 'bar',
+ secretToken: 'bar',
+ },
+];
+
+describe('TutorialConfigAgent', () => {
+ it('renders loading component while API is being called', () => {
+ const component = render(
+
+ );
+ expect(component.getByTestId('loading')).toBeInTheDocument();
+ });
+ it('updates commands when a different policy is selected', async () => {
+ const component = render(
+
+ );
+ 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-<version>.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-<version>.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(
+
+ );
+ 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-<version>.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('running on cloud', () => {
+ it('selects defaul standalone by defauls', async () => {
+ const component = render(
+
+ );
+ 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-<version>.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(
+
+ );
+ 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-<version>.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"
+ `);
+ });
+ });
+});
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/index.tsx b/x-pack/plugins/apm/public/tutorial/config_agent/index.tsx
new file mode 100644
index 0000000000000..755c3eca55868
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/index.tsx
@@ -0,0 +1,144 @@
+/*
+ * 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 {
+ EuiCodeBlock,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiLoadingSpinner,
+ EuiSpacer,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { HttpStart } from 'kibana/public';
+import React, { useEffect, useMemo, useState } from 'react';
+import styled from 'styled-components';
+import { APIReturnType } from '../..//services/rest/createCallApmApi';
+import { getCommands } from './commands/get_commands';
+import { CopyCommands } from './copy_commands';
+import { getPolicyOptions, PolicyOption } from './get_policy_options';
+import { PolicySelector } from './policy_selector';
+
+export type APIResponseType = APIReturnType<'GET /api/apm/fleet/agents'>;
+
+const CentralizedContainer = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
+
+const MANAGE_FLEET_POLICIES_LABEL = i18n.translate(
+ 'xpack.apm.tutorial.agent_config.manageFleetPolicies',
+ { defaultMessage: 'Manage fleet policies' }
+);
+
+const GET_STARTED_WITH_FLEET_LABEL = i18n.translate(
+ 'xpack.apm.tutorial.agent_config.getStartedWithFleet',
+ { defaultMessage: 'Get started with fleet' }
+);
+
+interface Props {
+ variantId: string;
+ http: HttpStart;
+ basePath: string;
+ isCloudEnabled: boolean;
+}
+
+function TutorialConfigAgent({
+ variantId,
+ http,
+ basePath,
+ isCloudEnabled,
+}: Props) {
+ const [data, setData] = useState({
+ fleetAgents: [],
+ cloudStandaloneSetup: undefined,
+ });
+ const [isLoading, setIsLoading] = useState(true);
+ const [selectedOption, setSelectedOption] = useState();
+
+ useEffect(() => {
+ async function fetchData() {
+ setIsLoading(true);
+ try {
+ const response = await http.get('/api/apm/fleet/agents');
+ if (response) {
+ setData(response as APIResponseType);
+ }
+ } catch (e) {
+ console.error('Error while fetching fleet agents.', e);
+ }
+ }
+ fetchData();
+ }, [http]);
+
+ // Depending the environment running (onPrem/Cloud) different values must be available and automatically selected
+ const options = useMemo(() => {
+ const availableOptions = getPolicyOptions({
+ isCloudEnabled,
+ data,
+ });
+ const defaultSelectedOption = availableOptions.find(
+ ({ isSelected }) => isSelected
+ );
+ setSelectedOption(defaultSelectedOption);
+ setIsLoading(false);
+ return availableOptions;
+ }, [data, isCloudEnabled]);
+
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ const commands = getCommands({
+ variantId,
+ policyDetails: {
+ apmServerUrl: selectedOption?.apmServerUrl,
+ secretToken: selectedOption?.secretToken,
+ },
+ });
+
+ 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 (
+ <>
+
+
+
+ setSelectedOption(newSelectedOption)
+ }
+ fleetLink={fleetLink}
+ />
+
+
+
+
+
+
+
+ {commands}
+
+ >
+ );
+}
+
+// eslint-disable-next-line import/no-default-export
+export default TutorialConfigAgent;
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/policy_selector.tsx b/x-pack/plugins/apm/public/tutorial/config_agent/policy_selector.tsx
new file mode 100644
index 0000000000000..3a0c6d70db82b
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/policy_selector.tsx
@@ -0,0 +1,92 @@
+/*
+ * 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 {
+ EuiComboBox,
+ EuiComboBoxOptionOption,
+ EuiFormRow,
+ EuiLink,
+ EuiText,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { groupBy } from 'lodash';
+import React from 'react';
+import { PolicyOption } from './get_policy_options';
+
+interface Props {
+ options: PolicyOption[];
+ selectedOption?: PolicyOption;
+ onChange: (selectedOption?: PolicyOption) => void;
+ fleetLink: {
+ label: string;
+ href: string;
+ };
+}
+
+export function PolicySelector({
+ options,
+ selectedOption,
+ onChange,
+ fleetLink,
+}: Props) {
+ const { fleetAgents, standalone } = groupBy(options, 'type');
+
+ const standaloneComboboxOptions: EuiComboBoxOptionOption[] =
+ standalone?.map(({ key, label }) => ({ key, label })) || [];
+
+ const fleetAgentsComboboxOptions = fleetAgents?.length
+ ? [
+ {
+ key: 'fleet_policies',
+ label: i18n.translate(
+ 'xpack.apm.tutorial.agent_config.fleetPoliciesLabel',
+ { defaultMessage: 'Fleet policies' }
+ ),
+ options: fleetAgents.map(({ key, label }) => ({ key, label })),
+ },
+ ]
+ : [];
+
+ return (
+
+ {fleetLink.label}
+
+ }
+ helpText={i18n.translate(
+ 'xpack.apm.tutorial.agent_config.choosePolicy.helper',
+ {
+ defaultMessage:
+ 'Adds the selected policy configuration to the snippet below.',
+ }
+ )}
+ >
+ {
+ const newSelectedOption = options.find(
+ ({ key }) => key === selectedOptions[0].key
+ );
+ onChange(newSelectedOption);
+ }}
+ />
+
+ );
+}
diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/rum_script.tsx b/x-pack/plugins/apm/public/tutorial/config_agent/rum_script.tsx
new file mode 100644
index 0000000000000..ae3ec803ac588
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/config_agent/rum_script.tsx
@@ -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 { HttpStart } from 'kibana/public';
+import React from 'react';
+import TutorialConfigAgent from './';
+
+interface Props {
+ http: HttpStart;
+ basePath: string;
+ isCloudEnabled: boolean;
+}
+
+function TutorialConfigAgentRumScript({
+ http,
+ basePath,
+ isCloudEnabled,
+}: Props) {
+ return (
+
+ );
+}
+
+// eslint-disable-next-line import/no-default-export
+export default TutorialConfigAgentRumScript;
diff --git a/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/tutorial_fleet_instructions.stories.tsx b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/tutorial_fleet_instructions.stories.tsx
new file mode 100644
index 0000000000000..40b72f06654ff
--- /dev/null
+++ b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/tutorial_fleet_instructions.stories.tsx
@@ -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 { Story } from '@storybook/react';
+import React from 'react';
+import { HttpStart } from 'kibana/public';
+import TutorialFleetInstructions from '.';
+
+interface Args {
+ hasFleetPoliciesWithApmIntegration: boolean;
+}
+
+function Wrapper({ hasFleetPoliciesWithApmIntegration }: Args) {
+ const http = ({
+ get: () => ({ hasData: hasFleetPoliciesWithApmIntegration }),
+ } as unknown) as HttpStart;
+ return (
+
+ );
+}
+
+export default {
+ title: 'app/Tutorial/FleetInstructions',
+ component: TutorialFleetInstructions,
+ argTypes: {
+ hasFleetPoliciesWithApmIntegration: {
+ control: { type: 'boolean', options: [true, false] },
+ },
+ },
+};
+
+export const Instructions: Story = (args) => {
+ return ;
+};
+
+Instructions.args = {
+ hasFleetPoliciesWithApmIntegration: true,
+};
diff --git a/x-pack/plugins/apm/server/lib/fleet/get_agents.ts b/x-pack/plugins/apm/server/lib/fleet/get_agents.ts
new file mode 100644
index 0000000000000..5ee44bb3ad174
--- /dev/null
+++ b/x-pack/plugins/apm/server/lib/fleet/get_agents.ts
@@ -0,0 +1,34 @@
+/*
+ * 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 {
+ CoreSetup,
+ CoreStart,
+ SavedObjectsClientContract,
+} from 'kibana/server';
+import { APMPluginStartDependencies } from '../../types';
+import { getInternalSavedObjectsClient } from '../helpers/get_internal_saved_objects_client';
+
+export async function getFleetAgents({
+ policyIds,
+ core,
+ fleetPluginStart,
+}: {
+ policyIds: string[];
+ core: { setup: CoreSetup; start: () => Promise };
+ fleetPluginStart: NonNullable;
+}) {
+ // @ts-ignore
+ const savedObjectsClient: SavedObjectsClientContract = await getInternalSavedObjectsClient(
+ core.setup
+ );
+
+ return await fleetPluginStart.agentPolicyService.getByIds(
+ savedObjectsClient,
+ policyIds
+ );
+}
diff --git a/x-pack/plugins/apm/server/routes/fleet.ts b/x-pack/plugins/apm/server/routes/fleet.ts
index 74ca8dc368dad..01323add276df 100644
--- a/x-pack/plugins/apm/server/routes/fleet.ts
+++ b/x-pack/plugins/apm/server/routes/fleet.ts
@@ -5,23 +5,28 @@
* 2.0.
*/
+import { keyBy } from 'lodash';
import Boom from '@hapi/boom';
import { i18n } from '@kbn/i18n';
+import { getFleetAgents } from '../lib/fleet/get_agents';
import { getApmPackgePolicies } from '../lib/fleet/get_apm_package_policies';
import { createApmServerRoute } from './create_apm_server_route';
import { createApmServerRouteRepository } from './create_apm_server_route_repository';
+const FLEET_REQUIRED_MESSAGE = i18n.translate(
+ 'xpack.apm.fleet_has_data.fleetRequired',
+ {
+ defaultMessage: `Fleet plugin is required`,
+ }
+);
+
const hasFleetDataRoute = createApmServerRoute({
endpoint: 'GET /api/apm/fleet/has_data',
options: { tags: [] },
handler: async ({ core, plugins }) => {
const fleetPluginStart = await plugins.fleet?.start();
if (!fleetPluginStart) {
- throw Boom.internal(
- i18n.translate('xpack.apm.fleet_has_data.fleetRequired', {
- defaultMessage: `Fleet plugin is required`,
- })
- );
+ throw Boom.internal(FLEET_REQUIRED_MESSAGE);
}
const packagePolicies = await getApmPackgePolicies({
core,
@@ -31,6 +36,54 @@ const hasFleetDataRoute = createApmServerRoute({
},
});
-export const ApmFleetRouteRepository = createApmServerRouteRepository().add(
- hasFleetDataRoute
-);
+const fleetAgentsRoute = createApmServerRoute({
+ endpoint: 'GET /api/apm/fleet/agents',
+ options: { tags: [] },
+ handler: async ({ core, plugins }) => {
+ const cloudSetup = plugins.cloud?.setup;
+ const cloudStandaloneSetup = cloudSetup
+ ? {
+ apmServerUrl: cloudSetup?.apm.url,
+ secretToken: cloudSetup?.apm.secretToken,
+ }
+ : undefined;
+
+ const fleetPluginStart = await plugins.fleet?.start();
+ if (!fleetPluginStart) {
+ throw Boom.internal(FLEET_REQUIRED_MESSAGE);
+ }
+ // fetches package policies that contains APM integrations
+ const packagePolicies = await getApmPackgePolicies({
+ core,
+ fleetPluginStart,
+ });
+
+ const policiesGroupedById = keyBy(packagePolicies.items, 'policy_id');
+
+ // fetches all agents with the found package policies
+ const fleetAgents = await getFleetAgents({
+ policyIds: Object.keys(policiesGroupedById),
+ core,
+ fleetPluginStart,
+ });
+
+ return {
+ cloudStandaloneSetup,
+ fleetAgents: fleetAgents.map((agent) => {
+ const packagePolicy = policiesGroupedById[agent.id];
+ const apmServerCompiledInputs =
+ packagePolicy.inputs[0].compiled_input['apm-server'];
+ return {
+ id: agent.id,
+ name: agent.name,
+ apmServerUrl: apmServerCompiledInputs?.url,
+ secretToken: apmServerCompiledInputs?.secret_token,
+ };
+ }),
+ };
+ },
+});
+
+export const ApmFleetRouteRepository = createApmServerRouteRepository()
+ .add(hasFleetDataRoute)
+ .add(fleetAgentsRoute);
diff --git a/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts b/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts
index ba11a996f00df..e2bf09dae5bed 100644
--- a/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts
+++ b/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts
@@ -31,55 +31,7 @@ export const createNodeAgentInstructions = (
APM services are created programmatically based on the `serviceName`. \
This agent supports a variety of frameworks but can also be used with your custom stack.',
}),
- commands: `// ${i18n.translate(
- 'xpack.apm.tutorial.nodeClient.configure.commands.addThisToTheFileTopComment',
- {
- defaultMessage:
- 'Add this to the VERY top of the first file loaded in your app',
- }
- )}
-var apm = require('elastic-apm-node').start({curlyOpen}
-
- // ${i18n.translate(
- 'xpack.apm.tutorial.nodeClient.configure.commands.setRequiredServiceNameComment',
- {
- defaultMessage: 'Override the service name from package.json',
- }
- )}
- // ${i18n.translate(
- 'xpack.apm.tutorial.nodeClient.configure.commands.allowedCharactersComment',
- {
- defaultMessage: 'Allowed characters: a-z, A-Z, 0-9, -, _, and space',
- }
- )}
- serviceName: '',
-
- // ${i18n.translate(
- 'xpack.apm.tutorial.nodeClient.configure.commands.useIfApmRequiresTokenComment',
- {
- defaultMessage: 'Use if APM Server requires a secret token',
- }
- )}
- secretToken: '${secretToken}',
-
- // ${i18n.translate(
- 'xpack.apm.tutorial.nodeClient.configure.commands.setCustomApmServerUrlComment',
- {
- defaultMessage:
- 'Set the custom APM Server URL (default: {defaultApmServerUrl})',
- values: { defaultApmServerUrl: 'http://localhost:8200' },
- }
- )}
- serverUrl: '${apmServerUrl}',
-
- // ${i18n.translate(
- 'xpack.apm.tutorial.nodeClient.configure.commands.setCustomServiceEnvironmentComment',
- {
- defaultMessage: 'Set the service environment',
- }
- )}
- environment: 'production'
-{curlyClose})`.split('\n'),
+ customComponentName: 'TutorialConfigAgent',
textPost: i18n.translate(
'xpack.apm.tutorial.nodeClient.configure.textPost',
{
@@ -122,70 +74,7 @@ export const createDjangoAgentInstructions = (
APM services are created programmatically based on the `SERVICE_NAME`.',
}
),
- commands: `# ${i18n.translate(
- 'xpack.apm.tutorial.djangoClient.configure.commands.addAgentComment',
- {
- defaultMessage: 'Add the agent to the installed apps',
- }
- )}
-INSTALLED_APPS = (
- 'elasticapm.contrib.django',
- # ...
-)
-
-ELASTIC_APM = {curlyOpen}
- # ${i18n.translate(
- 'xpack.apm.tutorial.djangoClient.configure.commands.setRequiredServiceNameComment',
- {
- defaultMessage: 'Set the required service name. Allowed characters:',
- }
- )}
- # ${i18n.translate(
- 'xpack.apm.tutorial.djangoClient.configure.commands.allowedCharactersComment',
- {
- defaultMessage: 'a-z, A-Z, 0-9, -, _, and space',
- }
- )}
- 'SERVICE_NAME': '',
-
- # ${i18n.translate(
- 'xpack.apm.tutorial.djangoClient.configure.commands.useIfApmServerRequiresTokenComment',
- {
- defaultMessage: 'Use if APM Server requires a secret token',
- }
- )}
- 'SECRET_TOKEN': '${secretToken}',
-
- # ${i18n.translate(
- 'xpack.apm.tutorial.djangoClient.configure.commands.setCustomApmServerUrlComment',
- {
- defaultMessage:
- 'Set the custom APM Server URL (default: {defaultApmServerUrl})',
- values: { defaultApmServerUrl: 'http://localhost:8200' },
- }
- )}
- 'SERVER_URL': '${apmServerUrl}',
-
- # ${i18n.translate(
- 'xpack.apm.tutorial.djangoClient.configure.commands.setServiceEnvironmentComment',
- {
- defaultMessage: 'Set the service environment',
- }
- )}
- 'ENVIRONMENT': 'production',
-{curlyClose}
-
-# ${i18n.translate(
- 'xpack.apm.tutorial.djangoClient.configure.commands.addTracingMiddlewareComment',
- {
- defaultMessage:
- 'To send performance metrics, add our tracing middleware:',
- }
- )}
-MIDDLEWARE = (
- 'elasticapm.contrib.django.middleware.TracingMiddleware',
- #...
-)`.split('\n'),
+ customComponentName: 'TutorialConfigAgent',
textPost: i18n.translate(
'xpack.apm.tutorial.djangoClient.configure.textPost',
{
@@ -225,67 +114,7 @@ export const createFlaskAgentInstructions = (
APM services are created programmatically based on the `SERVICE_NAME`.',
}
),
- commands: `# ${i18n.translate(
- 'xpack.apm.tutorial.flaskClient.configure.commands.initializeUsingEnvironmentVariablesComment',
- {
- defaultMessage: 'initialize using environment variables',
- }
- )}
-from elasticapm.contrib.flask import ElasticAPM
-app = Flask(__name__)
-apm = ElasticAPM(app)
-
-# ${i18n.translate(
- 'xpack.apm.tutorial.flaskClient.configure.commands.configureElasticApmComment',
- {
- defaultMessage:
- "or configure to use ELASTIC_APM in your application's settings",
- }
- )}
-from elasticapm.contrib.flask import ElasticAPM
-app.config['ELASTIC_APM'] = {curlyOpen}
- # ${i18n.translate(
- 'xpack.apm.tutorial.flaskClient.configure.commands.setRequiredServiceNameComment',
- {
- defaultMessage: 'Set the required service name. Allowed characters:',
- }
- )}
- # ${i18n.translate(
- 'xpack.apm.tutorial.flaskClient.configure.commands.allowedCharactersComment',
- {
- defaultMessage: 'a-z, A-Z, 0-9, -, _, and space',
- }
- )}
- 'SERVICE_NAME': '',
-
- # ${i18n.translate(
- 'xpack.apm.tutorial.flaskClient.configure.commands.useIfApmServerRequiresTokenComment',
- {
- defaultMessage: 'Use if APM Server requires a secret token',
- }
- )}
- 'SECRET_TOKEN': '${secretToken}',
-
- # ${i18n.translate(
- 'xpack.apm.tutorial.flaskClient.configure.commands.setCustomApmServerUrlComment',
- {
- defaultMessage:
- 'Set the custom APM Server URL (default: {defaultApmServerUrl})',
- values: { defaultApmServerUrl: 'http://localhost:8200' },
- }
- )}
- 'SERVER_URL': '${apmServerUrl}',
-
- # ${i18n.translate(
- 'xpack.apm.tutorial.flaskClient.configure.commands.setServiceEnvironmentComment',
- {
- defaultMessage: 'Set the service environment',
- }
- )}
- 'ENVIRONMENT': 'production',
-{curlyClose}
-
-apm = ElasticAPM(app)`.split('\n'),
+ customComponentName: 'TutorialConfigAgent',
textPost: i18n.translate(
'xpack.apm.tutorial.flaskClient.configure.textPost',
{
@@ -325,20 +154,7 @@ export const createRailsAgentInstructions = (
values: { configFile: '`config/elastic_apm.yml`' },
}
),
- commands: `# config/elastic_apm.yml:
-
-# Set the service name - allowed characters: a-z, A-Z, 0-9, -, _ and space
-# Defaults to the name of your Rails app
-service_name: 'my-service'
-
-# Use if APM Server requires a secret token
-secret_token: '${secretToken}'
-
-# Set the custom APM Server URL (default: http://localhost:8200)
-server_url: '${apmServerUrl || 'http://localhost:8200'}'
-
-# Set the service environment
-environment: 'production'`.split('\n'),
+ customComponentName: 'TutorialConfigAgent',
textPost: i18n.translate(
'xpack.apm.tutorial.railsClient.configure.textPost',
{
@@ -413,48 +229,7 @@ export const createRackAgentInstructions = (
values: { configFile: '`config/elastic_apm.yml`' },
}
),
- commands: `# config/elastic_apm.yml:
-
-# ${i18n.translate(
- 'xpack.apm.tutorial.rackClient.createConfig.commands.setServiceNameComment',
- {
- defaultMessage:
- 'Set the service name - allowed characters: a-z, A-Z, 0-9, -, _ and space',
- }
- )}
-# ${i18n.translate(
- 'xpack.apm.tutorial.rackClient.createConfig.commands.defaultsToTheNameOfRackAppClassComment',
- {
- defaultMessage: "Defaults to the name of your Rack app's class.",
- }
- )}
-service_name: 'my-service'
-
-# ${i18n.translate(
- 'xpack.apm.tutorial.rackClient.createConfig.commands.useIfApmServerRequiresTokenComment',
- {
- defaultMessage: 'Use if APM Server requires a token',
- }
- )}
-secret_token: '${secretToken}'
-
-# ${i18n.translate(
- 'xpack.apm.tutorial.rackClient.createConfig.commands.setCustomApmServerComment',
- {
- defaultMessage:
- 'Set custom APM Server URL (default: {defaultServerUrl})',
- values: { defaultServerUrl: 'http://localhost:8200' },
- }
- )}
-server_url: '${apmServerUrl || 'http://localhost:8200'}',
-
-# ${i18n.translate(
- 'xpack.apm.tutorial.rackClient.createConfig.commands.setServiceEnvironment',
- {
- defaultMessage: 'Set the service environment',
- }
- )}
-environment: 'production'`.split('\n'),
+ customComponentName: 'TutorialConfigAgent',
textPost: i18n.translate(
'xpack.apm.tutorial.rackClient.createConfig.textPost',
{
@@ -506,45 +281,7 @@ for details on how to enable RUM support.',
The Agent can then be initialized and configured in your application like this:',
}
),
- commands: `import {curlyOpen} init as initApm {curlyClose} from '@elastic/apm-rum'
-var apm = initApm({curlyOpen}
-
- // ${i18n.translate(
- 'xpack.apm.tutorial.jsClient.installDependency.commands.setRequiredServiceNameComment',
- {
- defaultMessage:
- 'Set required service name (allowed characters: a-z, A-Z, 0-9, -, _, and space)',
- }
- )}
- serviceName: 'your-app-name',
-
- // ${i18n.translate(
- 'xpack.apm.tutorial.jsClient.installDependency.commands.setCustomApmServerUrlComment',
- {
- defaultMessage:
- 'Set custom APM Server URL (default: {defaultApmServerUrl})',
- values: { defaultApmServerUrl: 'http://localhost:8200' },
- }
- )}
- serverUrl: '${apmServerUrl}',
-
- // ${i18n.translate(
- 'xpack.apm.tutorial.jsClient.installDependency.commands.setServiceVersionComment',
- {
- defaultMessage:
- 'Set the service version (required for source map feature)',
- }
- )}
- serviceVersion: '',
-
- // ${i18n.translate(
- 'xpack.apm.tutorial.jsClient.installDependency.commands.setServiceEnvironmentComment',
- {
- defaultMessage: 'Set the service environment',
- }
- )}
- environment: 'production'
-{curlyClose})`.split('\n'),
+ customComponentName: 'TutorialConfigAgent',
textPost: i18n.translate(
'xpack.apm.tutorial.jsClient.installDependency.textPost',
{
@@ -575,15 +312,7 @@ and host the file on your Server/CDN before deploying to production.",
'https://unpkg.com/@elastic/apm-rum/dist/bundles/elastic-apm-rum.umd.min.js',
},
}),
- commands: `\
-
-
-`.split('\n'),
+ customComponentName: 'TutorialConfigAgentRumScript',
},
];
@@ -610,55 +339,7 @@ export const createGoAgentInstructions = (
APM services are created programmatically based on the executable \
file name, or the `ELASTIC_APM_SERVICE_NAME` environment variable.',
}),
- commands: `# ${i18n.translate(
- 'xpack.apm.tutorial.goClient.configure.commands.initializeUsingEnvironmentVariablesComment',
- {
- defaultMessage: 'Initialize using environment variables:',
- }
- )}
-
-# ${i18n.translate(
- 'xpack.apm.tutorial.goClient.configure.commands.setServiceNameComment',
- {
- defaultMessage:
- 'Set the service name. Allowed characters: # a-z, A-Z, 0-9, -, _, and space.',
- }
- )}
-# ${i18n.translate(
- 'xpack.apm.tutorial.goClient.configure.commands.usedExecutableNameComment',
- {
- defaultMessage:
- 'If ELASTIC_APM_SERVICE_NAME is not specified, the executable name will be used.',
- }
- )}
-export ELASTIC_APM_SERVICE_NAME=
-
-# ${i18n.translate(
- 'xpack.apm.tutorial.goClient.configure.commands.setCustomApmServerUrlComment',
- {
- defaultMessage:
- 'Set custom APM Server URL (default: {defaultApmServerUrl})',
- values: { defaultApmServerUrl: 'http://localhost:8200' },
- }
- )}
-export ELASTIC_APM_SERVER_URL=${apmServerUrl}
-
-# ${i18n.translate(
- 'xpack.apm.tutorial.goClient.configure.commands.useIfApmRequiresTokenComment',
- {
- defaultMessage: 'Use if APM Server requires a secret token',
- }
- )}
-export ELASTIC_APM_SECRET_TOKEN=${secretToken}
-
-# ${i18n.translate(
- 'xpack.apm.tutorial.goClient.configure.commands.setServiceEnvironment',
- {
- defaultMessage: 'Set the service environment',
- }
- )}
-export ELASTIC_APM_ENVIRONMENT=
-`.split('\n'),
+ customComponentName: 'TutorialConfigAgent',
textPost: i18n.translate('xpack.apm.tutorial.goClient.configure.textPost', {
defaultMessage:
'See the [documentation]({documentationLink}) for advanced configuration.',
@@ -743,13 +424,7 @@ Do **not** add the agent as a dependency to your application.',
values: { customApmServerUrl: 'http://localhost:8200' },
}
),
- commands: `java -javaagent:/path/to/elastic-apm-agent-.jar \\
- -Delastic.apm.service_name=my-application \\
- -Delastic.apm.server_urls=${apmServerUrl || 'http://localhost:8200'} \\
- -Delastic.apm.secret_token=${secretToken} \\
- -Delastic.apm.environment=production \\
- -Delastic.apm.application_packages=org.example \\
- -jar my-application.jar`.split('\n'),
+ customComponentName: 'TutorialConfigAgent',
textPost: i18n.translate(
'xpack.apm.tutorial.javaClient.startApplication.textPost',
{
@@ -837,16 +512,7 @@ export const createDotNetAgentInstructions = (
defaultMessage: 'Sample appsettings.json file:',
}
),
- commands: `{curlyOpen}
- "ElasticApm": {curlyOpen}
- "SecretToken": "${secretToken}",
- "ServerUrls": "${
- apmServerUrl || 'http://localhost:8200'
- }", //Set custom APM Server URL (default: http://localhost:8200)
- "ServiceName": "MyApp", //allowed characters: a-z, A-Z, 0-9, -, _, and space. Default is the entry assembly of the application
- "Environment": "production", // Set the service environment
- {curlyClose}
-{curlyClose}`.split('\n'),
+ customComponentName: 'TutorialConfigAgent',
textPost: i18n.translate(
'xpack.apm.tutorial.dotNetClient.configureAgent.textPost',
{
@@ -913,12 +579,7 @@ export const createPhpAgentInstructions = (
'APM is automatically started when your app boots. Configure the agent either via `php.ini` file:',
}
),
- commands: `elastic_apm.server_url="${
- apmServerUrl || 'http://localhost:8200'
- }"
-elastic.apm.secret_token="${secretToken}"
-elastic_apm.service_name="My service"
-`.split('\n'),
+ customComponentName: 'TutorialConfigAgent',
textPost: i18n.translate(
'xpack.apm.tutorial.phpClient.configure.textPost',
{
diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts
index 43a5a14b425b5..2632b7f9dd85a 100644
--- a/x-pack/plugins/fleet/server/mocks/index.ts
+++ b/x-pack/plugins/fleet/server/mocks/index.ts
@@ -88,6 +88,7 @@ export const createMockAgentPolicyService = (): jest.Mocked {
diff --git a/x-pack/plugins/fleet/server/services/index.ts b/x-pack/plugins/fleet/server/services/index.ts
index ebddb695d695b..f82415987e5ac 100644
--- a/x-pack/plugins/fleet/server/services/index.ts
+++ b/x-pack/plugins/fleet/server/services/index.ts
@@ -69,6 +69,7 @@ export interface AgentPolicyServiceInterface {
list: typeof agentPolicyService['list'];
getDefaultAgentPolicyId: typeof agentPolicyService['getDefaultAgentPolicyId'];
getFullAgentPolicy: typeof agentPolicyService['getFullAgentPolicy'];
+ getByIds: typeof agentPolicyService['getByIDs'];
}
// Saved object services