Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x-pack/plugin/apm: introduce x-pack-apm plugin #97546

Merged
merged 53 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
2aebfcc
x-pack/plugin/apm: introduce x-pack-apm plugin
axw Jun 30, 2023
cc750ad
Dependency fix and tests
eyalkoren Sep 3, 2023
30d1541
Merge remote-tracking branch 'upstream/main' into apm-data-streams
eyalkoren Sep 3, 2023
2c5bb0b
Restore addition to ESRestTestCase
eyalkoren Sep 3, 2023
1c4aa5c
Replace IngestPipelineConfig instantiation
eyalkoren Sep 4, 2023
103f39d
Update DataStreamUpgradeRestIT to expect logs-apm.error
eyalkoren Sep 4, 2023
2775f2b
Adding rollover functionality
eyalkoren Sep 5, 2023
72e44d9
Extend basic rollover funtionality tests
eyalkoren Sep 6, 2023
36b7eae
Start adding integration test
eyalkoren Sep 7, 2023
338ed88
Hide rollup data streams
axw Sep 11, 2023
d766a2b
Apm ingest fixes
axw Sep 11, 2023
5b10dd4
Fix error.grouping_name script
axw Sep 11, 2023
3322b05
Only set event.ingested for traces-apm.sampled
axw Sep 12, 2023
7a7589c
Enabling APMRolloverIT
eyalkoren Sep 12, 2023
dca89e5
Merge remote-tracking branch 'axw/apm-data-streams' into apm-data-str…
eyalkoren Sep 12, 2023
eae5849
Spotless...
eyalkoren Sep 12, 2023
f6ec867
Assertion change
eyalkoren Sep 12, 2023
bac147f
Wait a bit before assertBusy
eyalkoren Sep 12, 2023
db19f78
Adjust template and pipeline names to convention
axw Sep 13, 2023
d0172bb
Merge remote-tracking branch 'upstream/main' into apm-data-streams
eyalkoren Sep 13, 2023
7c80c37
No need for manual cluster change events anymore
eyalkoren Sep 13, 2023
46313c1
Fix test
eyalkoren Sep 13, 2023
7744a3c
Merge remote-tracking branch 'upstream/main' into apm-data-streams
eyalkoren Sep 13, 2023
bf6444a
Lower template priority to 110
axw Sep 13, 2023
c926324
Add logging
eyalkoren Sep 13, 2023
93d425b
Remove explicit timeout for rollovers
eyalkoren Sep 13, 2023
437587e
Merge remote-tracking branch 'axw/apm-data-streams' into apm-data-str…
eyalkoren Sep 13, 2023
e26e06b
Add _meta to component templates too
axw Sep 13, 2023
1b750e9
Increase template priorities to 140
axw Sep 14, 2023
89fb221
Test multiple index template upgrades with rollovers
eyalkoren Sep 14, 2023
665dc9d
Enfore correct component template versions
eyalkoren Sep 14, 2023
22404af
Merge remote-tracking branch 'axw/apm-data-streams' into apm-data-str…
eyalkoren Sep 14, 2023
683d223
Rename to x-pack-apm-data, default to disabled
axw Sep 19, 2023
1385ae4
Fix checkstyle
axw Sep 19, 2023
b2f4424
Fix more checkstyle
axw Sep 19, 2023
e6ced72
Fix even more linting issues
axw Sep 19, 2023
e761177
Adjust test to disabled by default
eyalkoren Sep 19, 2023
3366b23
remove todo
eyalkoren Sep 19, 2023
70bf9bf
Fixing test
eyalkoren Sep 19, 2023
8cc4160
Refactor leftovers
eyalkoren Sep 19, 2023
41f2a3a
Merge remote-tracking branch 'upstream/main' into apm-data-streams
eyalkoren Sep 19, 2023
808af05
Fix constant keyword mapping conflict
eyalkoren Sep 19, 2023
873baa0
Merge remote-tracking branch 'upstream/main' into HEAD
eyalkoren Oct 16, 2023
7051040
Fix after merge
eyalkoren Oct 16, 2023
82d8728
Comment fixes
eyalkoren Oct 18, 2023
828f68b
Rename ApmIngestPipelineConfig to YamlIngestPipelineConfig
eyalkoren Oct 18, 2023
83a60d7
Always return a registry
eyalkoren Oct 18, 2023
fc045e7
spotless, what else
eyalkoren Oct 18, 2023
75a9a83
Add rollover integration test to core
eyalkoren Oct 19, 2023
cbc678b
Remove APMRolloverIT
eyalkoren Oct 19, 2023
d832ac6
Merge remote-tracking branch 'upstream/main' into apm-data-streams
eyalkoren Oct 19, 2023
44f860e
Merge remote-tracking branch 'upstream/main' into apm-data-streams
eyalkoren Oct 26, 2023
f878fc5
Refactor in renamed components
eyalkoren Oct 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ x-pack/plugin/core/src/main/resources/fleet-* @elastic/fleet

# Kibana Security
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/KibanaOwnedReservedRoleDescriptors.java @elastic/kibana-security

# APM
x-pack/plugin/apm-data @elastic/apm-server
Original file line number Diff line number Diff line change
Expand Up @@ -1882,6 +1882,13 @@ protected static boolean isXPackTemplate(String name) {
// We have a naming convention that internal component templates contain `@`. See also index-templates.asciidoc.
return true;
}
if (name.startsWith("apm@")
|| name.startsWith("apm-")
|| name.startsWith("traces-apm")
|| name.startsWith("metrics-apm")
|| name.startsWith("logs-apm")) {
return true;
}
switch (name) {
case ".watches":
case "security_audit_log":
Expand Down
33 changes: 33 additions & 0 deletions x-pack/plugin/apm-data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
## APM Data plugin

The APM data plugin installs index templates, component templates, and ingest pipelines for Elastic APM.

All resources are defined as YAML under [src/main/resources](src/main/resources).

The APM index templates rely on mappings from `x-pack-core`.
See [x-pack/plugin/core/src/main/resources](../core/src/main/resources).

This plugin is intended to work with data produced by https://github.com/elastic/apm-data.

## Testing

## Unit testing

Java unit tests cover basic, low-level details of the plugin, such as the parsing and loading of resources.
These can be run with:

```
./gradlew x-pack:plugin:apm-data:test
```

## Integration testing

The index templates and ingest pipeline functionality is tested using YAML REST tests.
These can be run with:

```
./gradlew x-pack:plugin:apm-data:yamlRestTest
```

Refer to the [rest-api-spec documentation](../../../rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/README.asciidoc)
for information about writing YAML REST tests.
34 changes: 34 additions & 0 deletions x-pack/plugin/apm-data/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
apply plugin: 'elasticsearch.internal-es-plugin'
apply plugin: 'elasticsearch.internal-yaml-rest-test'
apply plugin: 'elasticsearch.internal-cluster-test'

esplugin {
name 'x-pack-apm-data'
description 'The APM plugin defines APM data streams and ingest pipelines.'
classname 'org.elasticsearch.xpack.apmdata.APMPlugin'
extendedPlugins = ['x-pack-core']
}

dependencies {
compileOnly project(path: xpackModule('core'))
testImplementation project(path: ':x-pack:plugin:stack')
testImplementation(testArtifact(project(xpackModule('core'))))
clusterModules project(':modules:data-streams')
clusterModules project(':modules:ingest-common')
clusterModules project(':modules:ingest-geoip')
clusterModules project(':modules:ingest-user-agent')
clusterModules project(':modules:mapper-extras')
clusterModules project(xpackModule('analytics'))
clusterModules project(xpackModule('ilm'))
clusterModules project(xpackModule('mapper-aggregate-metric'))
clusterModules project(xpackModule('mapper-constant-keyword'))
clusterModules project(xpackModule('stack'))
clusterModules project(xpackModule('wildcard'))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* 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.
*/

package org.elasticsearch.xpack.apmdata;

import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.metadata.ComponentTemplate;
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xcontent.NamedXContentRegistry;
import org.elasticsearch.xcontent.XContentParserConfiguration;
import org.elasticsearch.xcontent.yaml.YamlXContent;
import org.elasticsearch.xpack.core.ClientHelper;
import org.elasticsearch.xpack.core.XPackSettings;
import org.elasticsearch.xpack.core.template.IndexTemplateRegistry;
import org.elasticsearch.xpack.core.template.IngestPipelineConfig;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static org.elasticsearch.xpack.apmdata.ResourceUtils.APM_TEMPLATE_VERSION_VARIABLE;
import static org.elasticsearch.xpack.apmdata.ResourceUtils.loadResource;
import static org.elasticsearch.xpack.apmdata.ResourceUtils.loadVersionedResourceUTF8;

/**
* Creates all index templates and ingest pipelines that are required for using Elastic APM.
*/
public class APMIndexTemplateRegistry extends IndexTemplateRegistry {
private final int version;

private final Map<String, ComponentTemplate> componentTemplates;
private final Map<String, ComposableIndexTemplate> composableIndexTemplates;
private final List<IngestPipelineConfig> ingestPipelines;
private final boolean enabled;

@SuppressWarnings("unchecked")
public APMIndexTemplateRegistry(
Settings nodeSettings,
ClusterService clusterService,
ThreadPool threadPool,
Client client,
NamedXContentRegistry xContentRegistry
) {
super(nodeSettings, clusterService, threadPool, client, xContentRegistry);

try {
final Map<String, Object> apmResources = XContentHelper.convertToMap(
YamlXContent.yamlXContent,
loadResource("/resources.yaml"),
false
);
version = (((Number) apmResources.get("version")).intValue());
final List<Object> componentTemplateNames = (List<Object>) apmResources.get("component-templates");
final List<Object> indexTemplateNames = (List<Object>) apmResources.get("index-templates");
final List<Object> ingestPipelineConfigs = (List<Object>) apmResources.get("ingest-pipelines");

componentTemplates = componentTemplateNames.stream()
.map(o -> (String) o)
.collect(Collectors.toMap(name -> name, name -> loadComponentTemplate(name, version)));
composableIndexTemplates = indexTemplateNames.stream()
.map(o -> (String) o)
.collect(Collectors.toMap(name -> name, name -> loadIndexTemplate(name, version)));
ingestPipelines = ingestPipelineConfigs.stream().map(o -> (Map<String, Map<String, Object>>) o).map(map -> {
Map.Entry<String, Map<String, Object>> pipelineConfig = map.entrySet().iterator().next();
return loadIngestPipeline(pipelineConfig.getKey(), version, (List<String>) pipelineConfig.getValue().get("dependencies"));
}).collect(Collectors.toList());

enabled = XPackSettings.APM_DATA_ENABLED.get(nodeSettings);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public int getVersion() {
return version;
}

public boolean isEnabled() {
return enabled;
}

public void close() {
clusterService.removeListener(this);
}

@Override
protected String getOrigin() {
return ClientHelper.APM_ORIGIN;
}

@Override
protected boolean requiresMasterNode() {
return true;
}

@Override
protected Map<String, ComponentTemplate> getComponentTemplateConfigs() {
if (enabled) {
return componentTemplates;
} else {
return Map.of();
}
}

@Override
protected Map<String, ComposableIndexTemplate> getComposableTemplateConfigs() {
if (enabled) {
return composableIndexTemplates;
} else {
return Map.of();
}
}

@Override
protected List<IngestPipelineConfig> getIngestPipelines() {
if (enabled) {
return ingestPipelines;
} else {
return Collections.emptyList();
}
}

private static ComponentTemplate loadComponentTemplate(String name, int version) {
try {
final byte[] content = loadVersionedResourceUTF8("/component-templates/" + name + ".yaml", version);
return ComponentTemplate.parse(YamlXContent.yamlXContent.createParser(XContentParserConfiguration.EMPTY, content));
} catch (Exception e) {
throw new RuntimeException("failed to load APM component template: " + name, e);
}
}

private static ComposableIndexTemplate loadIndexTemplate(String name, int version) {
try {
final byte[] content = loadVersionedResourceUTF8("/index-templates/" + name + ".yaml", version);
return ComposableIndexTemplate.parse(YamlXContent.yamlXContent.createParser(XContentParserConfiguration.EMPTY, content));
} catch (Exception e) {
throw new RuntimeException("failed to load APM index template: " + name, e);
}
}

private static IngestPipelineConfig loadIngestPipeline(String name, int version, @Nullable List<String> dependencies) {
if (dependencies == null) {
dependencies = Collections.emptyList();
}
return new YamlIngestPipelineConfig(
name,
"/ingest-pipelines/" + name + ".yaml",
version,
APM_TEMPLATE_VERSION_VARIABLE,
dependencies
);
}

@Override
protected boolean applyRolloverAfterTemplateV2Upgrade() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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.
*/

package org.elasticsearch.xpack.apmdata;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.SetOnce;
import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.routing.allocation.AllocationService;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.NodeEnvironment;
import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.plugins.ActionPlugin;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.repositories.RepositoriesService;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.telemetry.TelemetryProvider;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xcontent.NamedXContentRegistry;

import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;

public class APMPlugin extends Plugin implements ActionPlugin {
private static final Logger logger = LogManager.getLogger(APMPlugin.class);
private final Settings settings;

private final SetOnce<APMIndexTemplateRegistry> registry = new SetOnce<>();

public APMPlugin(Settings settings) {
this.settings = settings;
}

@Override
public Collection<Object> createComponents(
Client client,
ClusterService clusterService,
ThreadPool threadPool,
ResourceWatcherService resourceWatcherService,
ScriptService scriptService,
NamedXContentRegistry xContentRegistry,
Environment environment,
NodeEnvironment nodeEnvironment,
NamedWriteableRegistry namedWriteableRegistry,
IndexNameExpressionResolver indexNameExpressionResolver,
Supplier<RepositoriesService> repositoriesServiceSupplier,
TelemetryProvider telemetryProvider,
AllocationService allocationService,
IndicesService indicesService
) {
registry.set(new APMIndexTemplateRegistry(settings, clusterService, threadPool, client, xContentRegistry));
APMIndexTemplateRegistry registryInstance = registry.get();
logger.info("APM is {}", registryInstance.isEnabled() ? "enabled" : "disabled");
registryInstance.initialize();
return List.of(registryInstance);
}

@Override
public void close() {
registry.get().close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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.
*/

package org.elasticsearch.xpack.apmdata;

import org.elasticsearch.common.io.Streams;
import org.elasticsearch.xpack.core.template.TemplateUtils;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

public class ResourceUtils {

public static final String APM_TEMPLATE_VERSION_VARIABLE = "xpack.apmdata.template.version";

static byte[] loadVersionedResourceUTF8(String name, int version) {
eyalkoren marked this conversation as resolved.
Show resolved Hide resolved
try {
String content = loadResource(name);
content = TemplateUtils.replaceVariable(content, APM_TEMPLATE_VERSION_VARIABLE, String.valueOf(version));
return content.getBytes(StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

static String loadResource(String name) throws IOException {
InputStream is = APMIndexTemplateRegistry.class.getResourceAsStream(name);
if (is == null) {
throw new IOException("Resource [" + name + "] not found in classpath.");
}
return Streams.readFully(is).utf8ToString();
}
}
Loading