diff --git a/src/plugins/home/public/application/components/tutorial/instruction.js b/src/plugins/home/public/application/components/tutorial/instruction.js index 61aeb22107215..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'; @@ -100,13 +101,15 @@ export function Instruction({ {LazyCustomComponent && ( }> - + + + )} diff --git a/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/commands/get_commands.test.ts b/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/commands/get_commands.test.ts new file mode 100644 index 0000000000000..e178d1fd0d517 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/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', + environmentDetails: { + apmServerUrl: 'localhost:8220', + secretToken: 'foobar', + }, + }); + expect(commands).toBe(''); + }); + }); + describe('java agent', () => { + it('renders empty commands', () => { + const commands = getCommands({ + variantId: 'java', + environmentDetails: {}, + }); + 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', + environmentDetails: { + 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', + environmentDetails: {}, + }); + 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', + environmentDetails: { + 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', + environmentDetails: {}, + }); + 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', + environmentDetails: { + 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', + environmentDetails: {}, + }); + 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', + environmentDetails: { + 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', + environmentDetails: {}, + }); + 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', + environmentDetails: { + 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', + environmentDetails: {}, + }); + 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', + environmentDetails: { + 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', + environmentDetails: {}, + }); + 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', + environmentDetails: { + 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', + environmentDetails: {}, + }); + 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', + environmentDetails: { + 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', + environmentDetails: {}, + }); + 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', + environmentDetails: { + 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', + environmentDetails: {}, + }); + 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', + environmentDetails: { + 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/components/shared/tutorial/config_agent/commands/get_commands.ts b/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/commands/get_commands.ts index 5fa869b019f24..b76c5f37cb001 100644 --- a/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/commands/get_commands.ts +++ b/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/commands/get_commands.ts @@ -16,7 +16,7 @@ import { dotnet } from './dotnet'; import { php } from './php'; import { rum, rumScript } from './rum'; -const commands: Record = { +const commandsMap: Record = { java, node, django, @@ -40,5 +40,9 @@ export function getCommands({ secretToken?: string; }; }) { - return Mustache.render(commands[variantId], environmentDetails); + const commands = commandsMap[variantId]; + if (!commands) { + return ''; + } + return Mustache.render(commands, environmentDetails); } diff --git a/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/commands/java.ts b/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/commands/java.ts index 28590f889b4a4..249907a9b0c4b 100644 --- a/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/commands/java.ts +++ b/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/commands/java.ts @@ -7,8 +7,8 @@ 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.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/components/shared/tutorial/config_agent/commands/rack.ts b/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/commands/rack.ts index 7adf99366f615..9195ad9f15666 100644 --- a/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/commands/rack.ts +++ b/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/commands/rack.ts @@ -39,7 +39,7 @@ secret_token: '{{{secretToken}}}' values: { defaultServerUrl: 'http://localhost:8200' }, } )} -server_url: {{{apmServerUrl}}}, +server_url: '{{{apmServerUrl}}}', # ${i18n.translate( 'xpack.apm.tutorial.rackClient.createConfig.commands.setServiceEnvironment', diff --git a/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/environment_configuration_selector.tsx b/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/environment_configuration_selector.tsx index bab616ea8f012..745e6cd0ec70b 100644 --- a/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/environment_configuration_selector.tsx +++ b/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/environment_configuration_selector.tsx @@ -13,21 +13,21 @@ import { EuiPopoverFooter, EuiPopoverTitle, EuiSelectable, + EuiSelectableOption, + EuiButton, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import { px } from '../../../../style/variables'; -export interface EnvironmentConfigurationOption { - key: string; - label: string; +export type EnvironmentConfigurationOption = EuiSelectableOption & { apmServerUrl?: string; secretToken?: string; checked?: 'on'; -} +}; -const StyledEuiButtomEmpty = styled(EuiButtonEmpty)` +const StyledEuiButtomEmpty = styled(EuiButton)` .euiButtonContent { display: flex; justify-content: space-between; @@ -70,6 +70,7 @@ export function EnvironmentConfigurationSelector({ return ( ; @@ -79,6 +79,11 @@ describe('Tutorial config agent', () => { secretToken: 'bar', checked: 'on', }, + { + isGroupLabel: true, + key: 'fleet_policies', + label: 'Fleet policies', + }, { key: '1', label: 'agent foo', @@ -116,6 +121,11 @@ describe('Tutorial config agent', () => { secretToken: 'bar', checked: undefined, }, + { + isGroupLabel: true, + key: 'fleet_policies', + label: 'Fleet policies', + }, { key: '3', label: 'policy-elastic-agent-on-cloud', @@ -179,6 +189,11 @@ describe('Tutorial config agent', () => { secretToken: '', checked: 'on', }, + { + isGroupLabel: true, + key: 'fleet_policies', + label: 'Fleet policies', + }, { key: '1', label: 'agent foo', @@ -213,6 +228,11 @@ describe('Tutorial config agent', () => { secretToken: '', checked: undefined, }, + { + isGroupLabel: true, + key: 'fleet_policies', + label: 'Fleet policies', + }, { key: '3', label: 'policy-elastic-agent-on-cloud', @@ -277,6 +297,11 @@ describe('Tutorial config agent', () => { secretToken: '', checked: 'on', }, + { + isGroupLabel: true, + key: 'fleet_policies', + label: 'Fleet policies', + }, { key: '1', label: 'agent foo', diff --git a/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/index.tsx b/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/index.tsx index 290ee78bd1e94..34719e3f0a712 100644 --- a/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/index.tsx @@ -84,23 +84,34 @@ export function getEnvironmentConfigurationOptions({ }); } - // remaining agents with APM integration - newOptions.push( - ...data.agents.map( - ({ - id, - name, - apmServerUrl, - secretToken, - }): EnvironmentConfigurationOption => ({ - key: id, - label: name, - apmServerUrl, - secretToken, - checked: name === POLICY_ELASTIC_AGENT_ON_CLOUD ? 'on' : undefined, - }) - ) - ); + if (data.agents.length) { + // Adds fleet policies group label + newOptions.push({ + key: 'fleet_policies', + label: i18n.translate( + 'xpack.apm.tutorial.agent_config.fleetPoliciesLabel', + { defaultMessage: 'Fleet policies' } + ), + isGroupLabel: true, + }); + // remaining agents with APM integration + newOptions.push( + ...data.agents.map( + ({ + id, + name, + apmServerUrl, + secretToken, + }): EnvironmentConfigurationOption => ({ + key: id, + label: name, + apmServerUrl, + secretToken, + checked: name === POLICY_ELASTIC_AGENT_ON_CLOUD ? 'on' : undefined, + }) + ) + ); + } return newOptions; } @@ -149,7 +160,7 @@ function TutorialAgentSecretTokenSelector({ ); } - const command = getCommands({ + const commands = getCommands({ variantId, environmentDetails: { apmServerUrl: selectedOption?.apmServerUrl, @@ -182,11 +193,13 @@ function TutorialAgentSecretTokenSelector({ /> - + - {command} + + {commands} + ); } diff --git a/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/rum_script.tsx b/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/rum_script.tsx index ac9ca53bdee6c..4b8cdcc932fe7 100644 --- a/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/rum_script.tsx +++ b/x-pack/plugins/apm/public/components/shared/tutorial/config_agent/rum_script.tsx @@ -4,11 +4,29 @@ * 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 TutorialAgentSecretTokenSelector from './'; -function TutorialConfigAgentRumScript() { - return ; +interface Props { + http: HttpStart; + basePath: string; + isCloudEnabled: boolean; +} + +function TutorialConfigAgentRumScript({ + http, + basePath, + isCloudEnabled, +}: Props) { + return ( + + ); } // eslint-disable-next-line import/no-default-export