Skip to content

Commit

Permalink
[APM] Migrate to data streams - Fleet on Cloud (#102682)
Browse files Browse the repository at this point in the history
* [APM] Adds migration to fleet-managed APM server in APM UI Settings (#100657)

* adds useStrictParams option to apm server routes to allow unknown record type in param.bopy

* Adds checks for required roles, policies, and config before allowing user to initiate migration

* refactored and cleaned up server-side code

* i18n and link to Fleet

* fixes linting issues and unit tests

* updates the apm package policy to 0.3.0 and adds some missing config mappings

* PR feedback

* Handles case where the cloud policy doesn't exist

* Reverts the addition of the useStrictParams option since strictKeysRt now supports records

* fixes default input var values and uses correct published package version

* displays reasons the switch to data streams is disabled

* Store apm-server schema with the internal saved objects client
  • Loading branch information
ogupte authored Jun 29, 2021
1 parent 06eb881 commit bfd8010
Show file tree
Hide file tree
Showing 21 changed files with 1,179 additions and 17 deletions.
4 changes: 4 additions & 0 deletions x-pack/plugins/apm/common/apm_saved_object_constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ export const APM_INDICES_SAVED_OBJECT_ID = 'apm-indices';
// APM telemetry
export const APM_TELEMETRY_SAVED_OBJECT_TYPE = 'apm-telemetry';
export const APM_TELEMETRY_SAVED_OBJECT_ID = 'apm-telemetry';

// APM Server schema
export const APM_SERVER_SCHEMA_SAVED_OBJECT_TYPE = 'apm-server-schema';
export const APM_SERVER_SCHEMA_SAVED_OBJECT_ID = 'apm-server-schema';
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* 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 React, { useState } from 'react';
import {
EuiConfirmModal,
EuiCallOut,
EuiCheckbox,
EuiSpacer,
EuiCodeBlock,
htmlIdGenerator,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { ElasticDocsLink } from '../../../shared/Links/ElasticDocsLink';

interface Props {
onConfirm: () => void;
onCancel: () => void;
unsupportedConfigs: Array<{ key: string; value: string }>;
isLoading: boolean;
}
export function ConfirmSwitchModal({
onConfirm,
onCancel,
unsupportedConfigs,
isLoading,
}: Props) {
const [isConfirmChecked, setIsConfirmChecked] = useState(false);
const hasUnsupportedConfigs = !!unsupportedConfigs.length;
return (
<EuiConfirmModal
title={i18n.translate('xpack.apm.settings.schema.confirm.title', {
defaultMessage: 'Please confirm your choice',
})}
cancelButtonText={i18n.translate(
'xpack.apm.settings.schema.confirm.cancelText',
{
defaultMessage: 'Cancel',
}
)}
onCancel={onCancel}
confirmButtonText={i18n.translate(
'xpack.apm.settings.schema.confirm.switchButtonText',
{
defaultMessage: 'Switch to data streams',
}
)}
defaultFocusedButton="confirm"
onConfirm={onConfirm}
confirmButtonDisabled={!isConfirmChecked}
isLoading={isLoading}
>
<p>
{i18n.translate('xpack.apm.settings.schema.confirm.descriptionText', {
defaultMessage:
'If you have custom dashboards, machine learning jobs, or source maps that use classic APM indices, you must reconfigure them for data streams. Stack monitoring is not currently supported with Fleet-managed APM.',
})}
</p>
{!hasUnsupportedConfigs && (
<p>
{i18n.translate(
'xpack.apm.settings.schema.confirm.unsupportedConfigs.descriptionText',
{
defaultMessage: `Compatible custom apm-server.yml user settings will be moved to Fleet Server settings for you. We'll let you know which settings are incompatible before removing them.`,
}
)}
</p>
)}
<EuiCallOut
title={i18n.translate(
'xpack.apm.settings.schema.confirm.irreversibleWarning.title',
{
defaultMessage: `Switching to data streams is an irreversible action`,
}
)}
color="warning"
iconType="help"
>
<p>
{i18n.translate(
'xpack.apm.settings.schema.confirm.irreversibleWarning.message',
{
defaultMessage: `It might temporarily affect your APM data collection while the migration is in progress. The process of migrating should only take a few minutes.`,
}
)}
</p>
</EuiCallOut>
<EuiSpacer size="m" />
{hasUnsupportedConfigs && (
<>
<EuiCallOut
title={i18n.translate(
'xpack.apm.settings.schema.confirm.unsupportedConfigs.title',
{
defaultMessage: `The following apm-server.yml user settings are incompatible and will be removed`,
}
)}
iconType="iInCircle"
>
<EuiCodeBlock language="yaml">
{unsupportedConfigs
.map(({ key, value }) => `${key}: ${JSON.stringify(value)}`)
.join('\n')}
</EuiCodeBlock>
<p>
<ElasticDocsLink
section="/cloud"
path="/ec-manage-apm-settings.html"
target="_blank"
>
{i18n.translate(
'xpack.apm.settings.schema.confirm.apmServerSettingsCloudLinkText',
{ defaultMessage: 'Go to APM Server settings in Cloud' }
)}
</ElasticDocsLink>
</p>
</EuiCallOut>
<EuiSpacer size="m" />
</>
)}
<p>
<EuiCheckbox
id={htmlIdGenerator()()}
label={i18n.translate(
'xpack.apm.settings.schema.confirm.checkboxLabel',
{
defaultMessage: `I confirm that I wish to switch to data streams`,
}
)}
checked={isConfirmChecked}
onChange={(e) => {
setIsConfirmChecked(e.target.checked);
}}
disabled={isLoading}
/>
</p>
</EuiConfirmModal>
);
}
137 changes: 137 additions & 0 deletions x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* 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 React, { useState } from 'react';
import { i18n } from '@kbn/i18n';
import { NotificationsStart } from 'kibana/public';
import { SchemaOverview } from './schema_overview';
import { ConfirmSwitchModal } from './confirm_switch_modal';
import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher';
import {
callApmApi,
APIReturnType,
} from '../../../../services/rest/createCallApmApi';
import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context';

type FleetMigrationCheckResponse = APIReturnType<'GET /api/apm/fleet/migration_check'>;

export function Schema() {
const { toasts } = useApmPluginContext().core.notifications;
const [isSwitchActive, setIsSwitchActive] = useState(false);
const [isLoadingMigration, setIsLoadingMigration] = useState(false);
const [isLoadingConfirmation, setIsLoadingConfirmation] = useState(false);
const [unsupportedConfigs, setUnsupportedConfigs] = useState<
Array<{ key: string; value: any }>
>([]);

const {
refetch,
data = {} as FleetMigrationCheckResponse,
status,
} = useFetcher(
(callApi) => callApi({ endpoint: 'GET /api/apm/fleet/migration_check' }),
[],
{ preservePreviousData: false }
);
const isLoading = status !== FETCH_STATUS.SUCCESS;
const cloudApmMigrationEnabled = !!data.cloud_apm_migration_enabled;
const hasCloudAgentPolicy = !!data.has_cloud_agent_policy;
const hasCloudApmPackagePolicy = !!data.has_cloud_apm_package_policy;
const hasRequiredRole = !!data.has_required_role;
return (
<>
<SchemaOverview
onSwitch={async () => {
setIsLoadingConfirmation(true);
const unsupported = await getUnsupportedApmServerConfigs(toasts);
if (!unsupported) {
setIsLoadingConfirmation(false);
return;
}
setUnsupportedConfigs(unsupported);
setIsLoadingConfirmation(false);
setIsSwitchActive(true);
}}
isMigrated={hasCloudApmPackagePolicy}
isLoading={isLoading}
isLoadingConfirmation={isLoadingConfirmation}
cloudApmMigrationEnabled={cloudApmMigrationEnabled}
hasCloudAgentPolicy={hasCloudAgentPolicy}
hasRequiredRole={hasRequiredRole}
/>
{isSwitchActive && (
<ConfirmSwitchModal
isLoading={isLoadingMigration}
onConfirm={async () => {
setIsLoadingMigration(true);
const apmPackagePolicy = await createCloudApmPackagePolicy(toasts);
if (!apmPackagePolicy) {
setIsLoadingMigration(false);
return;
}
setIsSwitchActive(false);
refetch();
}}
onCancel={() => {
if (isLoadingMigration) {
return;
}
setIsSwitchActive(false);
}}
unsupportedConfigs={unsupportedConfigs}
/>
)}
</>
);
}

async function getUnsupportedApmServerConfigs(
toasts: NotificationsStart['toasts']
) {
try {
const { unsupported } = await callApmApi({
endpoint: 'GET /api/apm/fleet/apm_server_schema/unsupported',
signal: null,
});
return unsupported;
} catch (error) {
toasts.addDanger({
title: i18n.translate(
'xpack.apm.settings.unsupportedConfigs.errorToast.title',
{
defaultMessage: 'Unable to fetch APM Server settings',
}
),
text: error.body?.message || error.message,
});
}
}

async function createCloudApmPackagePolicy(
toasts: NotificationsStart['toasts']
) {
try {
const {
cloud_apm_package_policy: cloudApmPackagePolicy,
} = await callApmApi({
endpoint: 'POST /api/apm/fleet/cloud_apm_package_policy',
signal: null,
});
return cloudApmPackagePolicy;
} catch (error) {
toasts.addDanger({
title: i18n.translate(
'xpack.apm.settings.createApmPackagePolicy.errorToast.title',
{
defaultMessage:
'Unable to create APM package policy on cloud agent policy',
}
),
text: error.body?.message || error.message,
});
}
}
Loading

0 comments on commit bfd8010

Please sign in to comment.