From 8c1485bbe52b6f883b7a2de2f6266f60bfef5126 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cau=C3=AA=20Marcondes?=
<55978943+cauemarcondes@users.noreply.github.com>
Date: Wed, 20 Nov 2024 12:53:14 +0000
Subject: [PATCH] [APM][9.0] Adding kibana upgrade deprecation warning apm_user
removed (#200163)
Related to: https://github.com/elastic/elasticsearch/pull/116712
Meta issue: https://github.com/elastic/kibana/issues/116760
The apm_user role was
https://github.com/elastic/elasticsearch/pull/68749 in 7.13 and was
supposed to be removed in 8.0.
All mentions of apm_user role were finally removed in
https://github.com/elastic/kibana/pull/132790.
This PR adds some deprecation steps for users are using the `apm_user`.
---
.../__snapshots__/apm_user_role.test.ts.snap | 37 ++++
.../server/deprecations/apm_user_role.test.ts | 102 ++++++++++
.../apm/server/deprecations/apm_user_role.ts | 181 ++++++++++++++++++
.../server/deprecations/deprecations.test.ts | 110 -----------
.../apm/server/deprecations/index.ts | 88 ++-------
.../apm/server/lib/deprecations/index.ts | 82 ++++++++
.../apm/server/lib/deprecations/types.ts | 12 ++
.../apm/server/plugin.ts | 51 +++--
.../translations/translations/fr-FR.json | 6 -
.../translations/translations/ja-JP.json | 6 -
.../translations/translations/zh-CN.json | 6 -
11 files changed, 463 insertions(+), 218 deletions(-)
create mode 100644 x-pack/plugins/observability_solution/apm/server/deprecations/__snapshots__/apm_user_role.test.ts.snap
create mode 100644 x-pack/plugins/observability_solution/apm/server/deprecations/apm_user_role.test.ts
create mode 100644 x-pack/plugins/observability_solution/apm/server/deprecations/apm_user_role.ts
delete mode 100644 x-pack/plugins/observability_solution/apm/server/deprecations/deprecations.test.ts
create mode 100644 x-pack/plugins/observability_solution/apm/server/lib/deprecations/index.ts
create mode 100644 x-pack/plugins/observability_solution/apm/server/lib/deprecations/types.ts
diff --git a/x-pack/plugins/observability_solution/apm/server/deprecations/__snapshots__/apm_user_role.test.ts.snap b/x-pack/plugins/observability_solution/apm/server/deprecations/__snapshots__/apm_user_role.test.ts.snap
new file mode 100644
index 0000000000000..4d200ed6f1c63
--- /dev/null
+++ b/x-pack/plugins/observability_solution/apm/server/deprecations/__snapshots__/apm_user_role.test.ts.snap
@@ -0,0 +1,37 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`apm_user deprecation roles mapped to a removed role logs a deprecation when a role was found that maps to the removed apm_user role 1`] = `
+Array [
+ Object {
+ "correctiveActions": Object {
+ "manualSteps": Array [
+ "Go to Management > Security > Role Mappings to find roles mappings with the \\"apm_user\\" role.",
+ "Remove the \\"apm_user\\" role from all role mappings and add the built-in \\"viewer\\" role",
+ ],
+ },
+ "deprecationType": "feature",
+ "documentationUrl": "https://www.elastic.co/guide/en/kibana/main/kibana-privileges.html",
+ "level": "critical",
+ "message": "The \\"apm_user\\" role has been deprecated. Remove the \\"apm_user\\" role from affected role mappings in this cluster including: dungeon_master",
+ "title": "Check for role mappings using the deprecated \\"apm_user\\" role",
+ },
+]
+`;
+
+exports[`apm_user deprecation users assigned to a removed role logs a deprecation when a user was found with a removed apm_user role 1`] = `
+Array [
+ Object {
+ "correctiveActions": Object {
+ "manualSteps": Array [
+ "Go to Management > Security > Users to find users with the \\"apm_user\\" role.",
+ "Remove the \\"apm_user\\" role from all users and add the built-in \\"viewer\\" role.",
+ ],
+ },
+ "deprecationType": "feature",
+ "documentationUrl": "https://www.elastic.co/guide/en/kibana/main/kibana-privileges.html",
+ "level": "critical",
+ "message": "The \\"apm_user\\" role has been deprecated. Remove the \\"apm_user\\" role from affected users in this cluster including: foo",
+ "title": "Check for users assigned the deprecated \\"apm_user\\" role",
+ },
+]
+`;
diff --git a/x-pack/plugins/observability_solution/apm/server/deprecations/apm_user_role.test.ts b/x-pack/plugins/observability_solution/apm/server/deprecations/apm_user_role.test.ts
new file mode 100644
index 0000000000000..c7b48846520bd
--- /dev/null
+++ b/x-pack/plugins/observability_solution/apm/server/deprecations/apm_user_role.test.ts
@@ -0,0 +1,102 @@
+/*
+ * 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 type { GetDeprecationsContext, IScopedClusterClient, CoreSetup } from '@kbn/core/server';
+import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks';
+import { getDeprecationsInfo } from './apm_user_role';
+import { SecurityPluginSetup } from '@kbn/security-plugin/server';
+
+let context: GetDeprecationsContext;
+let esClient: jest.Mocked;
+const core = { docLinks: { version: 'main' } } as unknown as CoreSetup;
+const logger = loggingSystemMock.createLogger();
+const security = { license: { isEnabled: () => true } } as unknown as SecurityPluginSetup;
+
+describe('apm_user deprecation', () => {
+ beforeEach(async () => {
+ esClient = elasticsearchServiceMock.createScopedClusterClient();
+ esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({
+ xyz: { username: 'normal_user', roles: ['data_analyst'] },
+ });
+ esClient.asCurrentUser.security.getRoleMapping = jest.fn().mockResolvedValue({});
+
+ context = { esClient } as unknown as GetDeprecationsContext;
+ });
+
+ test('logs no deprecations when setup has no issues', async () => {
+ expect(await getDeprecationsInfo(context, core, { logger, security })).toMatchInlineSnapshot(
+ `Array []`
+ );
+ });
+
+ describe('users assigned to a removed role', () => {
+ test('logs a deprecation when a user was found with a removed apm_user role', async () => {
+ esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({
+ foo: {
+ username: 'foo',
+ roles: ['kibana_admin', 'apm_user'],
+ },
+ });
+
+ expect(await getDeprecationsInfo(context, core, { logger, security })).toMatchSnapshot();
+ });
+ });
+
+ describe('roles mapped to a removed role', () => {
+ test('logs a deprecation when a role was found that maps to the removed apm_user role', async () => {
+ esClient.asCurrentUser.security.getRoleMapping = jest
+ .fn()
+ .mockResolvedValue({ dungeon_master: { roles: ['apm_user'] } });
+
+ expect(await getDeprecationsInfo(context, core, { logger, security })).toMatchSnapshot();
+ });
+ });
+
+ describe('check deprecations when security is disabled', () => {
+ test('logs no deprecations', async () => {
+ expect(
+ await getDeprecationsInfo(context, core, { logger, security: undefined })
+ ).toMatchInlineSnapshot(`Array []`);
+ });
+ });
+
+ it('insufficient permissions', async () => {
+ const permissionsError = new Error('you shall not pass');
+ (permissionsError as unknown as { statusCode: number }).statusCode = 403;
+ esClient.asCurrentUser.security.getUser = jest.fn().mockRejectedValue(permissionsError);
+ esClient.asCurrentUser.security.getRoleMapping = jest.fn().mockRejectedValue(permissionsError);
+
+ expect(await getDeprecationsInfo(context, core, { logger, security })).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "correctiveActions": Object {
+ "manualSteps": Array [
+ "Make sure you have a \\"manage_security\\" cluster privilege assigned.",
+ ],
+ },
+ "deprecationType": "feature",
+ "documentationUrl": "https://www.elastic.co/guide/en/kibana/main/xpack-security.html#_required_permissions_7",
+ "level": "fetch_error",
+ "message": "You do not have enough permissions to fix this deprecation.",
+ "title": "Check for users assigned the deprecated \\"apm_user\\" role",
+ },
+ Object {
+ "correctiveActions": Object {
+ "manualSteps": Array [
+ "Make sure you have a \\"manage_security\\" cluster privilege assigned.",
+ ],
+ },
+ "deprecationType": "feature",
+ "documentationUrl": "https://www.elastic.co/guide/en/kibana/main/xpack-security.html#_required_permissions_7",
+ "level": "fetch_error",
+ "message": "You do not have enough permissions to fix this deprecation.",
+ "title": "Check for role mappings using the deprecated \\"apm_user\\" role",
+ },
+ ]
+ `);
+ });
+});
diff --git a/x-pack/plugins/observability_solution/apm/server/deprecations/apm_user_role.ts b/x-pack/plugins/observability_solution/apm/server/deprecations/apm_user_role.ts
new file mode 100644
index 0000000000000..d99e6a0a39f94
--- /dev/null
+++ b/x-pack/plugins/observability_solution/apm/server/deprecations/apm_user_role.ts
@@ -0,0 +1,181 @@
+/*
+ * 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 {
+ SecurityGetRoleMappingResponse,
+ SecurityGetUserResponse,
+} from '@elastic/elasticsearch/lib/api/types';
+import type {
+ CoreSetup,
+ DeprecationsDetails,
+ DocLinksServiceSetup,
+ ElasticsearchClient,
+ GetDeprecationsContext,
+} from '@kbn/core/server';
+import { i18n } from '@kbn/i18n';
+import type { DeprecationApmDeps } from '.';
+import { deprecations } from '../lib/deprecations';
+
+const APM_USER_ROLE_NAME = 'apm_user';
+const getKibanaPrivilegesDocumentationUrl = (branch: string) => {
+ return `https://www.elastic.co/guide/en/kibana/${branch}/kibana-privileges.html`;
+};
+
+export async function getDeprecationsInfo(
+ { esClient }: GetDeprecationsContext,
+ core: CoreSetup,
+ apmDeps: DeprecationApmDeps
+) {
+ const client = esClient.asCurrentUser;
+ const { docLinks } = core;
+ const { security } = apmDeps;
+
+ // Nothing to do if security is disabled
+ if (!security?.license.isEnabled()) {
+ return [];
+ }
+
+ const [userDeprecations, roleMappingDeprecations] = await Promise.all([
+ getUsersDeprecations(client, apmDeps, docLinks),
+ getRoleMappingsDeprecations(client, apmDeps, docLinks),
+ ]);
+
+ return [...userDeprecations, ...roleMappingDeprecations];
+}
+
+async function getUsersDeprecations(
+ client: ElasticsearchClient,
+ apmDeps: DeprecationApmDeps,
+ docLinks: DocLinksServiceSetup
+): Promise {
+ const title = i18n.translate('xpack.apm.deprecations.apmUser.title', {
+ defaultMessage: `Check for users assigned the deprecated "{apmUserRoleName}" role`,
+ values: { apmUserRoleName: APM_USER_ROLE_NAME },
+ });
+
+ let users: SecurityGetUserResponse;
+ try {
+ users = await client.security.getUser();
+ } catch (err) {
+ const { logger } = apmDeps;
+ if (deprecations.getErrorStatusCode(err) === 403) {
+ logger.warn(
+ 'Failed to retrieve users when checking for deprecations: the "read_security" or "manage_security" cluster privilege is required.'
+ );
+ } else {
+ logger.error(
+ `Failed to retrieve users when checking for deprecations, unexpected error: ${deprecations.getDetailedErrorMessage(
+ err
+ )}.`
+ );
+ }
+ return deprecations.deprecationError(title, err, docLinks);
+ }
+
+ const apmUsers = Object.values(users).flatMap((user) =>
+ user.roles.find(hasApmUserRole) ? user.username : []
+ );
+
+ if (apmUsers.length === 0) {
+ return [];
+ }
+
+ return [
+ {
+ title,
+ message: i18n.translate('xpack.apm.deprecations.apmUser.description', {
+ defaultMessage: `The "{apmUserRoleName}" role has been deprecated. Remove the "{apmUserRoleName}" role from affected users in this cluster including: {users}`,
+ values: { apmUserRoleName: APM_USER_ROLE_NAME, users: apmUsers.join() },
+ }),
+ correctiveActions: {
+ manualSteps: [
+ i18n.translate('xpack.apm.deprecations.apmUser.manualStepOne', {
+ defaultMessage: `Go to Management > Security > Users to find users with the "{apmUserRoleName}" role.`,
+ values: { apmUserRoleName: APM_USER_ROLE_NAME },
+ }),
+ i18n.translate('xpack.apm.deprecations.apmUser.manualStepTwo', {
+ defaultMessage:
+ 'Remove the "{apmUserRoleName}" role from all users and add the built-in "viewer" role.',
+ values: { apmUserRoleName: APM_USER_ROLE_NAME },
+ }),
+ ],
+ },
+ level: 'critical',
+ deprecationType: 'feature',
+ documentationUrl: getKibanaPrivilegesDocumentationUrl(docLinks.version),
+ },
+ ];
+}
+
+async function getRoleMappingsDeprecations(
+ client: ElasticsearchClient,
+ apmDeps: DeprecationApmDeps,
+ docLinks: DocLinksServiceSetup
+): Promise {
+ const title = i18n.translate('xpack.apm.deprecations.apmUserRoleMappings.title', {
+ defaultMessage: `Check for role mappings using the deprecated "{apmUserRoleName}" role`,
+ values: { apmUserRoleName: APM_USER_ROLE_NAME },
+ });
+
+ let roleMappings: SecurityGetRoleMappingResponse;
+ try {
+ roleMappings = await client.security.getRoleMapping();
+ } catch (err) {
+ const { logger } = apmDeps;
+ if (deprecations.getErrorStatusCode(err) === 403) {
+ logger.warn(
+ 'Failed to retrieve role mappings when checking for deprecations: the "manage_security" cluster privilege is required.'
+ );
+ } else {
+ logger.error(
+ `Failed to retrieve role mappings when checking for deprecations, unexpected error: ${deprecations.getDetailedErrorMessage(
+ err
+ )}.`
+ );
+ }
+ return deprecations.deprecationError(title, err, docLinks);
+ }
+
+ const roleMappingsWithApmUserRole = Object.entries(roleMappings).flatMap(([roleName, role]) =>
+ role.roles?.find(hasApmUserRole) ? roleName : []
+ );
+
+ if (roleMappingsWithApmUserRole.length === 0) {
+ return [];
+ }
+
+ return [
+ {
+ title,
+ message: i18n.translate('xpack.apm.deprecations.apmUserRoleMappings.description', {
+ defaultMessage: `The "{apmUserRoleName}" role has been deprecated. Remove the "{apmUserRoleName}" role from affected role mappings in this cluster including: {roles}`,
+ values: {
+ apmUserRoleName: APM_USER_ROLE_NAME,
+ roles: roleMappingsWithApmUserRole.join(),
+ },
+ }),
+ correctiveActions: {
+ manualSteps: [
+ i18n.translate('xpack.apm.deprecations.apmUserRoleMappings.manualStepOne', {
+ defaultMessage: `Go to Management > Security > Role Mappings to find roles mappings with the "{apmUserRoleName}" role.`,
+ values: { apmUserRoleName: APM_USER_ROLE_NAME },
+ }),
+ i18n.translate('xpack.apm.deprecations.apmUserRoleMappings.manualStepTwo', {
+ defaultMessage:
+ 'Remove the "{apmUserRoleName}" role from all role mappings and add the built-in "viewer" role',
+ values: { apmUserRoleName: APM_USER_ROLE_NAME },
+ }),
+ ],
+ },
+ level: 'critical',
+ deprecationType: 'feature',
+ documentationUrl: getKibanaPrivilegesDocumentationUrl(docLinks.version),
+ },
+ ];
+}
+
+const hasApmUserRole = (role: string) => role === APM_USER_ROLE_NAME;
diff --git a/x-pack/plugins/observability_solution/apm/server/deprecations/deprecations.test.ts b/x-pack/plugins/observability_solution/apm/server/deprecations/deprecations.test.ts
deleted file mode 100644
index 9252ed46aa6df..0000000000000
--- a/x-pack/plugins/observability_solution/apm/server/deprecations/deprecations.test.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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 { kibanaPackageJson } from '@kbn/repo-info';
-
-import { GetDeprecationsContext } from '@kbn/core/server';
-import { CloudSetup } from '@kbn/cloud-plugin/server';
-import { getDeprecations } from '.';
-import { AgentPolicy } from '@kbn/fleet-plugin/common';
-import { APMRouteHandlerResources } from '../routes/apm_routes/register_apm_server_routes';
-
-const deprecationContext = {
- esClient: {},
- savedObjectsClient: {},
-} as GetDeprecationsContext;
-
-describe('getDeprecations', () => {
- describe('when fleet is disabled', () => {
- it('returns no deprecations', async () => {
- const deprecationsCallback = getDeprecations({ branch: 'main' });
- const deprecations = await deprecationsCallback(deprecationContext);
- expect(deprecations).toEqual([]);
- });
- });
-
- describe('when running on cloud without cloud agent policy', () => {
- it('returns no deprecations', async () => {
- const deprecationsCallback = getDeprecations({
- branch: 'main',
- cloudSetup: { isCloudEnabled: true } as unknown as CloudSetup,
- fleet: {
- start: () => ({
- agentPolicyService: { get: () => undefined },
- }),
- } as unknown as APMRouteHandlerResources['plugins']['fleet'],
- });
- const deprecations = await deprecationsCallback(deprecationContext);
- expect(deprecations).toEqual([]);
- });
- });
-
- describe('when running on cloud with cloud agent policy and without apm integration', () => {
- it('returns deprecations', async () => {
- const deprecationsCallback = getDeprecations({
- branch: 'main',
- cloudSetup: { isCloudEnabled: true } as unknown as CloudSetup,
- fleet: {
- start: () => ({
- agentPolicyService: {
- get: () =>
- ({
- id: 'foo',
- package_policies: [{ package: { name: 'system' } }],
- } as AgentPolicy),
- },
- }),
- } as unknown as APMRouteHandlerResources['plugins']['fleet'],
- });
- const deprecations = await deprecationsCallback(deprecationContext);
- expect(deprecations).not.toEqual([]);
- // TODO: remove when docs support "main"
- if (kibanaPackageJson.branch === 'main') {
- for (const { documentationUrl } of deprecations) {
- expect(documentationUrl).toMatch(/\/master\//);
- expect(documentationUrl).not.toMatch(/\/main\//);
- }
- }
- });
- });
-
- describe('when running on cloud with cloud agent policy and apm integration', () => {
- it('returns no deprecations', async () => {
- const deprecationsCallback = getDeprecations({
- branch: 'main',
- cloudSetup: { isCloudEnabled: true } as unknown as CloudSetup,
- fleet: {
- start: () => ({
- agentPolicyService: {
- get: () =>
- ({
- id: 'foo',
- package_policies: [{ package: { name: 'apm' } }],
- } as AgentPolicy),
- },
- }),
- } as unknown as APMRouteHandlerResources['plugins']['fleet'],
- });
- const deprecations = await deprecationsCallback(deprecationContext);
- expect(deprecations).toEqual([]);
- });
- });
-
- describe('when running on prem', () => {
- it('returns no deprecations', async () => {
- const deprecationsCallback = getDeprecations({
- branch: 'main',
- cloudSetup: { isCloudEnabled: false } as unknown as CloudSetup,
- fleet: {
- start: () => ({ agentPolicyService: { get: () => undefined } }),
- } as unknown as APMRouteHandlerResources['plugins']['fleet'],
- });
- const deprecations = await deprecationsCallback(deprecationContext);
- expect(deprecations).toEqual([]);
- });
- });
-});
diff --git a/x-pack/plugins/observability_solution/apm/server/deprecations/index.ts b/x-pack/plugins/observability_solution/apm/server/deprecations/index.ts
index cafbdaf1ef8f0..1d3dd0b3a5b57 100644
--- a/x-pack/plugins/observability_solution/apm/server/deprecations/index.ts
+++ b/x-pack/plugins/observability_solution/apm/server/deprecations/index.ts
@@ -4,75 +4,25 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
+import { CoreSetup, Logger } from '@kbn/core/server';
+import { SecurityPluginSetup } from '@kbn/security-plugin/server';
+import { getDeprecationsInfo as getApmUserRoleDeprecationsInfo } from './apm_user_role';
-import { GetDeprecationsContext, DeprecationsDetails } from '@kbn/core/server';
-import { i18n } from '@kbn/i18n';
-import { isEmpty } from 'lodash';
-import { CloudSetup } from '@kbn/cloud-plugin/server';
-import {
- getCloudAgentPolicy,
- getApmPackagePolicy,
-} from '../routes/fleet/get_cloud_apm_package_policy';
-import { APMRouteHandlerResources } from '../routes/apm_routes/register_apm_server_routes';
+export interface DeprecationApmDeps {
+ logger: Logger;
+ security?: SecurityPluginSetup;
+}
-export function getDeprecations({
- cloudSetup,
- fleet,
- branch,
+export const registerDeprecations = ({
+ core,
+ apmDeps,
}: {
- cloudSetup?: CloudSetup;
- fleet?: APMRouteHandlerResources['plugins']['fleet'];
- branch: string;
-}) {
- return async ({ savedObjectsClient }: GetDeprecationsContext): Promise => {
- const deprecations: DeprecationsDetails[] = [];
- if (!fleet) {
- return deprecations;
- }
- // TODO: remove when docs support "main"
- const docBranch = branch === 'main' ? 'master' : branch;
-
- const fleetPluginStart = await fleet.start();
- const cloudAgentPolicy = await getCloudAgentPolicy({
- fleetPluginStart,
- savedObjectsClient,
- });
-
- const isCloudEnabled = !!cloudSetup?.isCloudEnabled;
- const hasCloudAgentPolicy = !isEmpty(cloudAgentPolicy);
- const hasAPMPackagePolicy = !isEmpty(getApmPackagePolicy(cloudAgentPolicy));
-
- if (isCloudEnabled && hasCloudAgentPolicy && !hasAPMPackagePolicy) {
- deprecations.push({
- title: i18n.translate('xpack.apm.deprecations.legacyModeTitle', {
- defaultMessage: 'APM Server running in legacy mode',
- }),
- message: i18n.translate('xpack.apm.deprecations.message', {
- defaultMessage:
- 'Running the APM Server binary directly is considered a legacy option and will be deprecated and removed in the future.',
- }),
- documentationUrl: `https://www.elastic.co/guide/en/apm/server/${docBranch}/apm-integration.html`,
- level: 'warning',
- correctiveActions: {
- manualSteps: [
- i18n.translate('xpack.apm.deprecations.steps.apm', {
- defaultMessage: 'Navigate to Observability/APM',
- }),
- i18n.translate('xpack.apm.deprecations.steps.settings', {
- defaultMessage: 'Click on "Settings"',
- }),
- i18n.translate('xpack.apm.deprecations.steps.schema', {
- defaultMessage: 'Select "Schema" tab',
- }),
- i18n.translate('xpack.apm.deprecations.steps.switch', {
- defaultMessage:
- 'Click "Switch to Elastic Agent". You will be guided through the process',
- }),
- ],
- },
- });
- }
-
- return deprecations;
- };
-}
+ core: CoreSetup;
+ apmDeps: DeprecationApmDeps;
+}) => {
+ core.deprecations.registerDeprecations({
+ getDeprecations: async (ctx) => {
+ return [...(await getApmUserRoleDeprecationsInfo(ctx, core, apmDeps))];
+ },
+ });
+};
diff --git a/x-pack/plugins/observability_solution/apm/server/lib/deprecations/index.ts b/x-pack/plugins/observability_solution/apm/server/lib/deprecations/index.ts
new file mode 100644
index 0000000000000..f6e75bf5aa301
--- /dev/null
+++ b/x-pack/plugins/observability_solution/apm/server/lib/deprecations/index.ts
@@ -0,0 +1,82 @@
+/*
+ * 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 { errors } from '@elastic/elasticsearch';
+import Boom from '@hapi/boom';
+import { i18n } from '@kbn/i18n';
+import { DeprecationsDetails, DocLinksServiceSetup } from '@kbn/core/server';
+
+function deprecationError(
+ title: string,
+ error: Error,
+ docLinks: DocLinksServiceSetup
+): DeprecationsDetails[] {
+ if (getErrorStatusCode(error) === 403) {
+ return [
+ {
+ title,
+ level: 'fetch_error',
+ deprecationType: 'feature',
+ message: i18n.translate('xpack.apm.deprecations.apmRole.forbiddenErrorMessage', {
+ defaultMessage: 'You do not have enough permissions to fix this deprecation.',
+ }),
+ documentationUrl: `https://www.elastic.co/guide/en/kibana/${docLinks.version}/xpack-security.html#_required_permissions_7`,
+ correctiveActions: {
+ manualSteps: [
+ i18n.translate('xpack.apm.deprecations.apmRole.forbiddenErrorCorrectiveAction', {
+ defaultMessage: 'Make sure you have a "manage_security" cluster privilege assigned.',
+ }),
+ ],
+ },
+ },
+ ];
+ }
+
+ return [
+ {
+ title,
+ level: 'fetch_error',
+ deprecationType: 'feature',
+ message: i18n.translate('xpack.apm.deprecations.apmRole.unknownErrorMessage', {
+ defaultMessage: 'Failed to perform deprecation check. Check Kibana logs for more details.',
+ }),
+ correctiveActions: {
+ manualSteps: [
+ i18n.translate('xpack.apm.deprecations.apmRole.unknownErrorCorrectiveAction', {
+ defaultMessage: 'Check Kibana logs for more details.',
+ }),
+ ],
+ },
+ },
+ ];
+}
+
+function getErrorStatusCode(error: any): number | undefined {
+ if (error instanceof errors.ResponseError) {
+ return error.statusCode;
+ }
+
+ return Boom.isBoom(error) ? error.output.statusCode : error.statusCode || error.status;
+}
+
+function getDetailedErrorMessage(error: any): string {
+ if (error instanceof errors.ResponseError) {
+ return JSON.stringify(error.body);
+ }
+
+ if (Boom.isBoom(error)) {
+ return JSON.stringify(error.output.payload);
+ }
+
+ return error.message;
+}
+
+export const deprecations = {
+ deprecationError,
+ getDetailedErrorMessage,
+ getErrorStatusCode,
+};
diff --git a/x-pack/plugins/observability_solution/apm/server/lib/deprecations/types.ts b/x-pack/plugins/observability_solution/apm/server/lib/deprecations/types.ts
new file mode 100644
index 0000000000000..5f572f89911ef
--- /dev/null
+++ b/x-pack/plugins/observability_solution/apm/server/lib/deprecations/types.ts
@@ -0,0 +1,12 @@
+/*
+ * 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 type { ElasticsearchClient } from '@kbn/core/server';
+
+export interface DeprecationsDependencies {
+ elasticsearchClient: ElasticsearchClient;
+}
diff --git a/x-pack/plugins/observability_solution/apm/server/plugin.ts b/x-pack/plugins/observability_solution/apm/server/plugin.ts
index 7e93a5f3c3324..1142a5c69a51f 100644
--- a/x-pack/plugins/observability_solution/apm/server/plugin.ts
+++ b/x-pack/plugins/observability_solution/apm/server/plugin.ts
@@ -5,42 +5,43 @@
* 2.0.
*/
-import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from '@kbn/core/server';
-import { isEmpty, mapValues } from 'lodash';
-import { Dataset } from '@kbn/rule-registry-plugin/server';
import { mappingFromFieldMap } from '@kbn/alerting-plugin/common';
+import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from '@kbn/core/server';
import { alertsLocatorID } from '@kbn/observability-plugin/common';
+import { Dataset } from '@kbn/rule-registry-plugin/server';
+import { isEmpty, mapValues } from 'lodash';
import { APMConfig, APM_SERVER_FEATURE_ID } from '.';
+import { apmTutorialCustomIntegration } from '../common/tutorial/tutorials';
+import { registerAssistantFunctions } from './assistant_functions';
+import { registerDeprecations } from './deprecations';
import { APM_FEATURE, registerFeaturesUsage } from './feature';
+import { createApmTelemetry } from './lib/apm_telemetry';
+import { getInternalSavedObjectsClient } from './lib/helpers/get_internal_saved_objects_client';
import {
- registerApmRuleTypes,
- apmRuleTypeAlertFieldMap,
APM_RULE_TYPE_ALERT_CONTEXT,
+ apmRuleTypeAlertFieldMap,
+ registerApmRuleTypes,
} from './routes/alerts/register_apm_rule_types';
+import { getGlobalApmServerRouteRepository } from './routes/apm_routes/get_global_apm_server_route_repository';
+import {
+ APMRouteHandlerResources,
+ registerRoutes,
+} from './routes/apm_routes/register_apm_server_routes';
+import { getAlertDetailsContextHandler } from './routes/assistant_functions/get_observability_alert_details_context';
+import { addApiKeysToEveryPackagePolicyIfMissing } from './routes/fleet/api_keys/add_api_keys_to_policies_if_missing';
import { registerFleetPolicyCallbacks } from './routes/fleet/register_fleet_policy_callbacks';
-import { createApmTelemetry } from './lib/apm_telemetry';
-import { getInternalSavedObjectsClient } from './lib/helpers/get_internal_saved_objects_client';
import { createApmAgentConfigurationIndex } from './routes/settings/agent_configuration/create_agent_config_index';
import { createApmCustomLinkIndex } from './routes/settings/custom_link/create_custom_link_index';
+import { createApmSourceMapIndexTemplate } from './routes/source_maps/create_apm_source_map_index_template';
+import { scheduleSourceMapMigration } from './routes/source_maps/schedule_source_map_migration';
import {
- apmTelemetry,
+ apmCustomDashboards,
apmServerSettings,
apmServiceGroups,
- apmCustomDashboards,
+ apmTelemetry,
} from './saved_objects';
-import { APMPluginSetup, APMPluginSetupDependencies, APMPluginStartDependencies } from './types';
-import {
- APMRouteHandlerResources,
- registerRoutes,
-} from './routes/apm_routes/register_apm_server_routes';
-import { getGlobalApmServerRouteRepository } from './routes/apm_routes/get_global_apm_server_route_repository';
import { tutorialProvider } from './tutorial';
-import { scheduleSourceMapMigration } from './routes/source_maps/schedule_source_map_migration';
-import { createApmSourceMapIndexTemplate } from './routes/source_maps/create_apm_source_map_index_template';
-import { addApiKeysToEveryPackagePolicyIfMissing } from './routes/fleet/api_keys/add_api_keys_to_policies_if_missing';
-import { apmTutorialCustomIntegration } from '../common/tutorial/tutorials';
-import { registerAssistantFunctions } from './assistant_functions';
-import { getAlertDetailsContextHandler } from './routes/assistant_functions/get_observability_alert_details_context';
+import { APMPluginSetup, APMPluginSetupDependencies, APMPluginStartDependencies } from './types';
export class APMPlugin
implements Plugin
@@ -226,6 +227,14 @@ export class APMPlugin
getAlertDetailsContextHandler(resourcePlugins, logger)
);
+ registerDeprecations({
+ core,
+ apmDeps: {
+ logger: this.logger,
+ security: plugins.security,
+ },
+ });
+
return { config$ };
}
diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json
index 6d1ba23a5f6c4..4764f793e9d9b 100644
--- a/x-pack/plugins/translations/translations/fr-FR.json
+++ b/x-pack/plugins/translations/translations/fr-FR.json
@@ -11021,12 +11021,6 @@
"xpack.apm.dependencyOperationDistributionChart.allSpansLegendLabel": "Tous les intervalles",
"xpack.apm.dependencyOperationDistributionChart.failedSpansLegendLabel": "Intervalles ayant échoué",
"xpack.apm.dependencyThroughputChart.chartTitle": "Rendement",
- "xpack.apm.deprecations.legacyModeTitle": "Le serveur APM fonctionne en mode hérité",
- "xpack.apm.deprecations.message": "L'exécution directe du binaire du serveur APM est considérée comme une option héritée et sera déclassée et retirée à l'avenir.",
- "xpack.apm.deprecations.steps.apm": "Naviguer vers Observabilité/APM",
- "xpack.apm.deprecations.steps.schema": "Sélectionner l'onglet \"Schema\"",
- "xpack.apm.deprecations.steps.settings": "Cliquer sur \"Settings\"",
- "xpack.apm.deprecations.steps.switch": "Cliquez sur \"Passer à Elastic Agent\". Vous serez guidé tout au long du processus",
"xpack.apm.diagnostics.loading": "Chargement des diagnostics",
"xpack.apm.diagnostics.tab.apmEvents": "Documents",
"xpack.apm.diagnostics.tab.datastreams": "Flux de données",
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 04bc03184b777..882c10f2f83fe 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -11005,12 +11005,6 @@
"xpack.apm.dependencyOperationDistributionChart.allSpansLegendLabel": "すべてのスパン",
"xpack.apm.dependencyOperationDistributionChart.failedSpansLegendLabel": "失敗したスパン",
"xpack.apm.dependencyThroughputChart.chartTitle": "スループット",
- "xpack.apm.deprecations.legacyModeTitle": "APMサーバーはレガシーモードで実行されています",
- "xpack.apm.deprecations.message": "APMサーバーバイナリの直接実行はレガシーオプションと見なされるため、廃止予定であり、将来は削除されます。",
- "xpack.apm.deprecations.steps.apm": "Observability/APMに移動",
- "xpack.apm.deprecations.steps.schema": "[スキーマ]タブを選択します",
- "xpack.apm.deprecations.steps.settings": "[設定]をクリックします",
- "xpack.apm.deprecations.steps.switch": "[Elasticエージェントに切り替える]をクリックします。手順が案内されます。",
"xpack.apm.diagnostics.loading": "診断を読み込んでいます",
"xpack.apm.diagnostics.tab.apmEvents": "ドキュメント",
"xpack.apm.diagnostics.tab.datastreams": "データストリーム",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 1d48e398dfd0c..80c1c97c301ea 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -10786,12 +10786,6 @@
"xpack.apm.dependencyOperationDistributionChart.allSpansLegendLabel": "所有跨度",
"xpack.apm.dependencyOperationDistributionChart.failedSpansLegendLabel": "失败的跨度",
"xpack.apm.dependencyThroughputChart.chartTitle": "吞吐量",
- "xpack.apm.deprecations.legacyModeTitle": "APM Server 正以旧版模式运行",
- "xpack.apm.deprecations.message": "直接运行 APM Server 二进制被视为是旧版选项,将被弃用并会在未来删除。",
- "xpack.apm.deprecations.steps.apm": "导航到 Observability/APM",
- "xpack.apm.deprecations.steps.schema": "选择'架构'选项卡",
- "xpack.apm.deprecations.steps.settings": "单击'设置'",
- "xpack.apm.deprecations.steps.switch": "单击'切换到 Elastic 代理'。将指导您完成此过程",
"xpack.apm.diagnostics.loading": "正在加载诊断",
"xpack.apm.diagnostics.tab.apmEvents": "文档",
"xpack.apm.diagnostics.tab.datastreams": "数据流",