Skip to content

Commit

Permalink
Merge branch '7.x' into backport/7.x/pr-115355
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Oct 19, 2021
2 parents 3f7f1e0 + 8fec45c commit 53cabfe
Show file tree
Hide file tree
Showing 49 changed files with 1,394 additions and 355 deletions.
93 changes: 93 additions & 0 deletions packages/elastic-apm-generator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# @elastic/apm-generator

`@elastic/apm-generator` is an experimental tool to generate synthetic APM data. It is intended to be used for development and testing of the Elastic APM app in Kibana.

At a high-level, the module works by modeling APM events/metricsets with [a fluent API](https://en.wikipedia.org/wiki/Fluent_interface). The models can then be serialized and converted to Elasticsearch documents. In the future we might support APM Server as an output as well.

## Usage

This section assumes that you've installed Kibana's dependencies by running `yarn kbn bootstrap` in the repository's root folder.

This library can currently be used in two ways:

- Imported as a Node.js module, for instance to be used in Kibana's functional test suite.
- With a command line interface, to index data based on some example scenarios.

### Using the Node.js module

#### Concepts

- `Service`: a logical grouping for a monitored service. A `Service` object contains fields like `service.name`, `service.environment` and `agent.name`.
- `Instance`: a single instance of a monitored service. E.g., the workload for a monitored service might be spread across multiple containers. An `Instance` object contains fields like `service.node.name` and `container.id`.
- `Timerange`: an object that will return an array of timestamps based on an interval and a rate. These timestamps can be used to generate events/metricsets.
- `Transaction`, `Span`, `APMError` and `Metricset`: events/metricsets that occur on an instance. For more background, see the [explanation of the APM data model](https://www.elastic.co/guide/en/apm/get-started/7.15/apm-data-model.html)


#### Example

```ts
import { service, timerange, toElasticsearchOutput } from '@elastic/apm-generator';

const instance = service('synth-go', 'production', 'go')
.instance('instance-a');

const from = new Date('2021-01-01T12:00:00.000Z').getTime();
const to = new Date('2021-01-01T12:00:00.000Z').getTime() - 1;

const traceEvents = timerange(from, to)
.interval('1m')
.rate(10)
.flatMap(timestamp => instance.transaction('GET /api/product/list')
.timestamp(timestamp)
.duration(1000)
.success()
.children(
instance.span('GET apm-*/_search', 'db', 'elasticsearch')
.timestamp(timestamp + 50)
.duration(900)
.destination('elasticsearch')
.success()
).serialize()
);

const metricsets = timerange(from, to)
.interval('30s')
.rate(1)
.flatMap(timestamp => instance.appMetrics({
'system.memory.actual.free': 800,
'system.memory.total': 1000,
'system.cpu.total.norm.pct': 0.6,
'system.process.cpu.total.norm.pct': 0.7,
}).timestamp(timestamp)
.serialize()
);

const esEvents = toElasticsearchOutput(traceEvents.concat(metricsets));
```

#### Generating metricsets

`@elastic/apm-generator` can also automatically generate transaction metrics, span destination metrics and transaction breakdown metrics based on the generated trace events. If we expand on the previous example:

```ts
import { getTransactionMetrics, getSpanDestinationMetrics, getBreakdownMetrics } from '@elastic/apm-generator';

const esEvents = toElasticsearchOutput([
...traceEvents,
...getTransactionMetrics(traceEvents),
...getSpanDestinationMetrics(traceEvents),
...getBreakdownMetrics(traceEvents)
]);
```

### CLI

Via the CLI, you can upload examples. The supported examples are listed in `src/lib/es.ts`. A `--target` option that specifies the Elasticsearch URL should be defined when running the `example` command. Here's an example:

`$ node packages/elastic-apm-generator/src/scripts/es.js example simple-trace --target=http://admin:changeme@localhost:9200`

The following options are supported:
- `to`: the end of the time range, in ISO format. By default, the current time will be used.
- `from`: the start of the time range, in ISO format. By default, `to` minus 15 minutes will be used.
- `apm-server-version`: the version used in the index names bootstrapped by APM Server, e.g. `7.16.0`. __If these indices do not exist, the script will exit with an error. It will not bootstrap the indices itself.__

Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('Discover topnav component', () => {
const props = getProps(true);
const component = shallowWithIntl(<DiscoverTopNav {...props} />);
const topMenuConfig = component.props().config.map((obj: TopNavMenuData) => obj.id);
expect(topMenuConfig).toEqual(['options', 'new', 'save', 'open', 'share', 'inspect']);
expect(topMenuConfig).toEqual(['options', 'new', 'open', 'share', 'inspect', 'save']);
});

test('generated config of TopNavMenu config is correct when no discover save permissions are assigned', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,6 @@ test('getTopNavLinks result', () => {
"run": [Function],
"testId": "discoverNewButton",
},
Object {
"description": "Save Search",
"id": "save",
"label": "Save",
"run": [Function],
"testId": "discoverSaveButton",
},
Object {
"description": "Open Saved Search",
"id": "open",
Expand All @@ -81,6 +74,15 @@ test('getTopNavLinks result', () => {
"run": [Function],
"testId": "openInspectorButton",
},
Object {
"description": "Save Search",
"emphasize": true,
"iconType": "save",
"id": "save",
"label": "Save",
"run": [Function],
"testId": "discoverSaveButton",
},
]
`);
});
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ export const getTopNavLinks = ({
defaultMessage: 'Save Search',
}),
testId: 'discoverSaveButton',
iconType: 'save',
emphasize: true,
run: () => onSaveSearch({ savedSearch, services, indexPattern, navigateTo, state }),
};

Expand Down Expand Up @@ -153,9 +155,9 @@ export const getTopNavLinks = ({
return [
...(services.capabilities.advancedSettings.save ? [options] : []),
newSearch,
...(services.capabilities.discover.save ? [saveSearch] : []),
openSearch,
shareSearch,
inspectSearch,
...(services.capabilities.discover.save ? [saveSearch] : []),
];
};
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,19 @@ export async function onSaveSearch({
const onSave = async ({
newTitle,
newCopyOnSave,
newDescription,
isTitleDuplicateConfirmed,
onTitleDuplicate,
}: {
newTitle: string;
newCopyOnSave: boolean;
newDescription: string;
isTitleDuplicateConfirmed: boolean;
onTitleDuplicate: () => void;
}) => {
const currentTitle = savedSearch.title;
savedSearch.title = newTitle;
savedSearch.description = newDescription;
const saveOptions: SaveSavedSearchOptions = {
onTitleDuplicate,
copyOnSave: newCopyOnSave,
Expand Down Expand Up @@ -136,14 +139,11 @@ export async function onSaveSearch({
onClose={() => {}}
title={savedSearch.title ?? ''}
showCopyOnSave={!!savedSearch.id}
description={savedSearch.description}
objectType={i18n.translate('discover.localMenu.saveSaveSearchObjectType', {
defaultMessage: 'search',
})}
description={i18n.translate('discover.localMenu.saveSaveSearchDescription', {
defaultMessage:
'Save your Discover search so you can use it in visualizations and dashboards',
})}
showDescription={false}
showDescription={true}
/>
);
showSaveModal(saveModal, services.core.i18n.Context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,10 @@ export class SavedSearchEmbeddable
return this.inspectorAdapters;
}

public getDescription() {
return this.savedSearch.description;
}

public destroy() {
super.destroy();
if (this.searchProps) {
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/apm/common/fleet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
*/

export const POLICY_ELASTIC_AGENT_ON_CLOUD = 'policy-elastic-agent-on-cloud';

export const SUPPORTED_APM_PACKAGE_VERSION = '7.16.0';
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ export function Schema() {
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 cloudApmPackagePolicy = data.cloud_apm_package_policy;
const hasCloudApmPackagePolicy = !!cloudApmPackagePolicy;
const hasRequiredRole = !!data.has_required_role;

function updateLocalStorage(newStatus: FETCH_STATUS) {
Expand Down Expand Up @@ -90,6 +91,7 @@ export function Schema() {
cloudApmMigrationEnabled={cloudApmMigrationEnabled}
hasCloudAgentPolicy={hasCloudAgentPolicy}
hasRequiredRole={hasRequiredRole}
cloudApmPackagePolicy={cloudApmPackagePolicy}
/>
{isSwitchActive && (
<ConfirmSwitchModal
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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, EuiSpacer, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { APMLink } from '../../../../shared/Links/apm/APMLink';
import { useFleetCloudAgentPolicyHref } from '../../../../shared/Links/kibana';

export function CardFooterContent() {
const fleetCloudAgentPolicyHref = useFleetCloudAgentPolicyHref();

return (
<div>
<EuiButton href={fleetCloudAgentPolicyHref}>
{i18n.translate(
'xpack.apm.settings.schema.success.viewIntegrationInFleet.buttonText',
{ defaultMessage: 'View the APM integration in Fleet' }
)}
</EuiButton>
<EuiSpacer size="xs" />
<EuiText size="s">
<p>
<FormattedMessage
id="xpack.apm.settings.schema.success.returnText"
defaultMessage="or simply return to the {serviceInventoryLink}."
values={{
serviceInventoryLink: (
<APMLink path="/services">
{i18n.translate(
'xpack.apm.settings.schema.success.returnText.serviceInventoryLink',
{ defaultMessage: 'Service inventory' }
)}
</APMLink>
),
}}
/>
</p>
</EuiText>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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 { EuiCard, EuiIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { CardFooterContent } from './card_footer_content';

export function SuccessfulMigrationCard() {
return (
<EuiCard
icon={<EuiIcon size="xxl" type="checkInCircleFilled" color="success" />}
title={i18n.translate('xpack.apm.settings.schema.success.title', {
defaultMessage: 'Elastic Agent successfully setup!',
})}
description={i18n.translate(
'xpack.apm.settings.schema.success.description',
{
defaultMessage:
'Your APM integration is now setup and ready to receive data from your currently instrumented agents. Feel free to review the policies applied to your integtration.',
}
)}
footer={<CardFooterContent />}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 { EuiCard, EuiIcon, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { useUpgradeApmPackagePolicyHref } from '../../../../shared/Links/kibana';
import { CardFooterContent } from './card_footer_content';

export function UpgradeAvailableCard({
apmPackagePolicyId,
}: {
apmPackagePolicyId: string | undefined;
}) {
const upgradeApmPackagePolicyHref =
useUpgradeApmPackagePolicyHref(apmPackagePolicyId);

return (
<EuiCard
icon={<EuiIcon size="xxl" type="alert" color="warning" />}
title={i18n.translate(
'xpack.apm.settings.schema.upgradeAvailable.title',
{
defaultMessage: 'APM integration upgrade available!',
}
)}
description={
<FormattedMessage
id="xpack.apm.settings.upgradeAvailable.description"
defaultMessage="Even though your APM integration is setup, a new version of the APM integration is available for upgrade with your package policy. {upgradePackagePolicyLink} to get the most out of your setup."
values={{
upgradePackagePolicyLink: (
<EuiLink href={upgradeApmPackagePolicyHref}>
{i18n.translate(
'xpack.apm.settings.schema.upgradeAvailable.upgradePackagePolicyLink',
{ defaultMessage: 'Upgrade your APM integration' }
)}
</EuiLink>
),
}}
/>
}
footer={<CardFooterContent />}
/>
);
}
Loading

0 comments on commit 53cabfe

Please sign in to comment.