Skip to content

Commit

Permalink
Merge branch '7.x' into security/ml-privileges-deprecation
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Oct 19, 2021
2 parents 08a450a + fe17761 commit 3618092
Show file tree
Hide file tree
Showing 155 changed files with 2,615 additions and 1,410 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.__

2 changes: 1 addition & 1 deletion src/core/public/chrome/ui/header/collapsible_nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ export function CollapsibleNav({
iconType="plusInCircleFilled"
>
{i18n.translate('core.ui.primaryNav.addData', {
defaultMessage: 'Add data',
defaultMessage: 'Add integrations',
})}
</EuiButton>
</EuiCollapsibleNavGroup>
Expand Down
1 change: 1 addition & 0 deletions src/plugins/custom_integrations/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const INTEGRATION_CATEGORY_DISPLAY = {
project_management: 'Project Management',
software_development: 'Software Development',
upload_file: 'Upload a file',
website_search: 'Website Search',
};

/**
Expand Down
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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ beforeEach(() => {
jest.clearAllMocks();
});

const applicationStartMock = {} as unknown as ApplicationStart;
const applicationStartMock = {
capabilities: { navLinks: { integrations: true } },
} as unknown as ApplicationStart;

const addBasePathMock = jest.fn((path: string) => (path ? path : 'path'));

Expand Down
Loading

0 comments on commit 3618092

Please sign in to comment.