diff --git a/docs/changelog/100333.yaml b/docs/changelog/100333.yaml new file mode 100644 index 0000000000000..96a2a62deffe5 --- /dev/null +++ b/docs/changelog/100333.yaml @@ -0,0 +1,5 @@ +pr: 100333 +summary: Enable Universal Profiling as Enterprise feature +area: Application +type: enhancement +issues: [] diff --git a/docs/reference/rest-api/info.asciidoc b/docs/reference/rest-api/info.asciidoc index ec424ca20d324..28b6df215a18d 100644 --- a/docs/reference/rest-api/info.asciidoc +++ b/docs/reference/rest-api/info.asciidoc @@ -168,6 +168,10 @@ Example response: "enterprise_search": { "available": true, "enabled": true + }, + "universal_profiling": { + "available": true, + "enabled": true } }, "tagline" : "You know, for X" diff --git a/docs/reference/rest-api/usage.asciidoc b/docs/reference/rest-api/usage.asciidoc index 99e432eb07e1c..c33d203f1415b 100644 --- a/docs/reference/rest-api/usage.asciidoc +++ b/docs/reference/rest-api/usage.asciidoc @@ -473,6 +473,10 @@ GET /_xpack/usage "min_rule_count": 0, "max_rule_count": 0 } + }, + "universal_profiling" : { + "available" : true, + "enabled" : true } } ------------------------------------------------------------ diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index e851434ac2cb7..373dab307f378 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -135,6 +135,8 @@ static TransportVersion def(int id) { public static final TransportVersion NESTED_KNN_VECTOR_QUERY_V = def(8_511_00_0); public static final TransportVersion ML_PACKAGE_LOADER_PLATFORM_ADDED = def(8_512_00_0); public static final TransportVersion PLUGIN_DESCRIPTOR_OPTIONAL_CLASSNAME = def(8_513_00_0); + public static final TransportVersion UNIVERSAL_PROFILING_LICENSE_ADDED = def(8_514_00_0); + /* * STOP! READ THIS FIRST! No, really, * ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _ diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java index 5610b18ac627c..6d019e50f9d5f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java @@ -23,6 +23,7 @@ import org.elasticsearch.xpack.core.aggregatemetric.AggregateMetricFeatureSetUsage; import org.elasticsearch.xpack.core.analytics.AnalyticsFeatureSetUsage; import org.elasticsearch.xpack.core.application.EnterpriseSearchFeatureSetUsage; +import org.elasticsearch.xpack.core.application.ProfilingUsage; import org.elasticsearch.xpack.core.archive.ArchiveFeatureSetUsage; import org.elasticsearch.xpack.core.ccr.AutoFollowMetadata; import org.elasticsearch.xpack.core.datastreams.DataStreamFeatureSetUsage; @@ -277,7 +278,8 @@ public List getNamedWriteables() { XPackFeatureSet.Usage.class, XPackField.ENTERPRISE_SEARCH, EnterpriseSearchFeatureSetUsage::new - ) + ), + new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.UNIVERSAL_PROFILING, ProfilingUsage::new) ).filter(Objects::nonNull).toList(); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackField.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackField.java index f78f755517d99..c8a78af429592 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackField.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackField.java @@ -85,6 +85,8 @@ public final class XPackField { /** Name constant for the redact processor feature. */ public static final String REDACT_PROCESSOR = "redact_processor"; + /* Name for Universal Profiling. */ + public static final String UNIVERSAL_PROFILING = "universal_profiling"; private XPackField() {} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackInfoFeatureAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackInfoFeatureAction.java index 1f943a5c68646..859950470f0e3 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackInfoFeatureAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackInfoFeatureAction.java @@ -50,6 +50,7 @@ public class XPackInfoFeatureAction extends ActionType public static final XPackInfoFeatureAction AGGREGATE_METRIC = new XPackInfoFeatureAction(XPackField.AGGREGATE_METRIC); public static final XPackInfoFeatureAction ARCHIVE = new XPackInfoFeatureAction(XPackField.ARCHIVE); public static final XPackInfoFeatureAction ENTERPRISE_SEARCH = new XPackInfoFeatureAction(XPackField.ENTERPRISE_SEARCH); + public static final XPackInfoFeatureAction UNIVERSAL_PROFILING = new XPackInfoFeatureAction(XPackField.UNIVERSAL_PROFILING); public static final List ALL; static { @@ -80,7 +81,8 @@ public class XPackInfoFeatureAction extends ActionType DATA_TIERS, AGGREGATE_METRIC, ARCHIVE, - ENTERPRISE_SEARCH + ENTERPRISE_SEARCH, + UNIVERSAL_PROFILING ) ); ALL = Collections.unmodifiableList(actions); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackUsageFeatureAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackUsageFeatureAction.java index a63d535e2a06c..d96fd91ed3f22 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackUsageFeatureAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackUsageFeatureAction.java @@ -50,6 +50,7 @@ public class XPackUsageFeatureAction extends ActionType ALL = List.of( AGGREGATE_METRIC, @@ -78,7 +79,8 @@ public class XPackUsageFeatureAction extends ActionType> nodePlugins() { return List.of( - LocalStateCompositeXPackPlugin.class, DataStreamsPlugin.class, - ProfilingPlugin.class, + LocalStateProfilingXPackPlugin.class, IndexLifecycle.class, UnsignedLongMapperPlugin.class, VersionFieldPlugin.class, @@ -58,6 +57,7 @@ protected Settings nodeSettings(int nodeOrdinal, Settings otherSettings) { // .put(LicenseSettings.SELF_GENERATED_LICENSE_TYPE.getKey(), "trial") // Disable ILM history index so that the tests don't have to clean it up .put(LifecycleSettings.LIFECYCLE_HISTORY_INDEX_ENABLED_SETTING.getKey(), false) + .put(LicenseSettings.SELF_GENERATED_LICENSE_TYPE.getKey(), "trial") .build(); } diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingInfoTransportAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingInfoTransportAction.java new file mode 100644 index 0000000000000..115b165f3e791 --- /dev/null +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingInfoTransportAction.java @@ -0,0 +1,49 @@ +/* + * 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.profiling; + +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.transport.TransportService; +import org.elasticsearch.xpack.core.XPackField; +import org.elasticsearch.xpack.core.XPackSettings; +import org.elasticsearch.xpack.core.action.XPackInfoFeatureAction; +import org.elasticsearch.xpack.core.action.XPackInfoFeatureTransportAction; + +public class ProfilingInfoTransportAction extends XPackInfoFeatureTransportAction { + private final boolean enabled; + private final ProfilingLicenseChecker licenseChecker; + + @Inject + public ProfilingInfoTransportAction( + TransportService transportService, + ActionFilters actionFilters, + Settings settings, + ProfilingLicenseChecker licenseChecker + ) { + super(XPackInfoFeatureAction.UNIVERSAL_PROFILING.name(), transportService, actionFilters); + this.enabled = XPackSettings.PROFILING_ENABLED.get(settings); + this.licenseChecker = licenseChecker; + } + + @Override + public String name() { + return XPackField.UNIVERSAL_PROFILING; + } + + @Override + public boolean available() { + return licenseChecker.isSupportedLicense(); + } + + @Override + public boolean enabled() { + return enabled; + } +} diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingLicenseChecker.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingLicenseChecker.java new file mode 100644 index 0000000000000..1100c6b10c5f7 --- /dev/null +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingLicenseChecker.java @@ -0,0 +1,39 @@ +/* + * 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.profiling; + +import org.elasticsearch.license.License; +import org.elasticsearch.license.LicenseUtils; +import org.elasticsearch.license.LicensedFeature; +import org.elasticsearch.license.XPackLicenseState; + +import java.util.function.Supplier; + +public final class ProfilingLicenseChecker { + private static final LicensedFeature.Momentary UNIVERSAL_PROFILING_FEATURE = LicensedFeature.momentary( + null, + "universal_profiling", + License.OperationMode.ENTERPRISE + ); + + private final Supplier licenseStateResolver; + + public ProfilingLicenseChecker(Supplier licenseStateResolver) { + this.licenseStateResolver = licenseStateResolver; + } + + public boolean isSupportedLicense() { + return UNIVERSAL_PROFILING_FEATURE.checkWithoutTracking(licenseStateResolver.get()); + } + + public void requireSupportedLicense() { + if (UNIVERSAL_PROFILING_FEATURE.check(licenseStateResolver.get()) == false) { + throw LicenseUtils.newComplianceException(UNIVERSAL_PROFILING_FEATURE.getName()); + } + } +} diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java index 9ef887ecf5639..d37a5be7543bd 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java @@ -39,7 +39,10 @@ import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; +import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.XPackSettings; +import org.elasticsearch.xpack.core.action.XPackInfoFeatureAction; +import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction; import java.util.ArrayList; import java.util.Collection; @@ -108,10 +111,12 @@ public Collection createComponents( registry.get().initialize(); indexManager.get().initialize(); dataStreamManager.get().initialize(); - return List.of(registry.get(), indexManager.get(), dataStreamManager.get()); - } else { - return Collections.emptyList(); } + return Collections.singletonList(createLicenseChecker()); + } + + protected ProfilingLicenseChecker createLicenseChecker() { + return new ProfilingLicenseChecker(XPackPlugin::getSharedLicenseState); } public void updateCheckOutdatedIndices(boolean newValue) { @@ -179,7 +184,9 @@ public static ExecutorBuilder responseExecutorBuilder() { return List.of( new ActionHandler<>(GetStackTracesAction.INSTANCE, TransportGetStackTracesAction.class), new ActionHandler<>(GetFlamegraphAction.INSTANCE, TransportGetFlamegraphAction.class), - new ActionHandler<>(GetStatusAction.INSTANCE, TransportGetStatusAction.class) + new ActionHandler<>(GetStatusAction.INSTANCE, TransportGetStatusAction.class), + new ActionHandler<>(XPackUsageFeatureAction.UNIVERSAL_PROFILING, ProfilingUsageTransportAction.class), + new ActionHandler<>(XPackInfoFeatureAction.UNIVERSAL_PROFILING, ProfilingInfoTransportAction.class) ); } diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingUsageTransportAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingUsageTransportAction.java new file mode 100644 index 0000000000000..7e7b431759cd4 --- /dev/null +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingUsageTransportAction.java @@ -0,0 +1,64 @@ +/* + * 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.profiling; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.protocol.xpack.XPackUsageRequest; +import org.elasticsearch.tasks.Task; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportService; +import org.elasticsearch.xpack.core.XPackSettings; +import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction; +import org.elasticsearch.xpack.core.action.XPackUsageFeatureResponse; +import org.elasticsearch.xpack.core.action.XPackUsageFeatureTransportAction; +import org.elasticsearch.xpack.core.application.ProfilingUsage; + +public class ProfilingUsageTransportAction extends XPackUsageFeatureTransportAction { + private final ProfilingLicenseChecker licenseChecker; + + private final boolean enabled; + + @Inject + public ProfilingUsageTransportAction( + TransportService transportService, + ClusterService clusterService, + ThreadPool threadPool, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver, + ProfilingLicenseChecker licenseChecker, + Settings settings + ) { + super( + XPackUsageFeatureAction.UNIVERSAL_PROFILING.name(), + transportService, + clusterService, + threadPool, + actionFilters, + indexNameExpressionResolver + ); + this.licenseChecker = licenseChecker; + this.enabled = XPackSettings.PROFILING_ENABLED.get(settings); + } + + @Override + protected void masterOperation( + Task task, + XPackUsageRequest request, + ClusterState state, + ActionListener listener + ) { + ProfilingUsage profilingUsage = new ProfilingUsage(licenseChecker.isSupportedLicense(), enabled); + listener.onResponse(new XPackUsageFeatureResponse(profilingUsage)); + } +} diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java index 438fcd1461df0..113b600f7702b 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java @@ -88,6 +88,7 @@ public class TransportGetStackTracesAction extends HandledTransportAction submitListener) { + licenseChecker.requireSupportedLicense(); long start = System.nanoTime(); Client client = new ParentTaskAssigningClient(this.nodeClient, transportService.getLocalNode(), submitTask); EventsIndex mediumDownsampled = EventsIndex.MEDIUM_DOWNSAMPLED; diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingInfoTransportActionTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingInfoTransportActionTests.java new file mode 100644 index 0000000000000..b66b8a3db50f9 --- /dev/null +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingInfoTransportActionTests.java @@ -0,0 +1,56 @@ +/* + * 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.profiling; + +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.license.License; +import org.elasticsearch.license.XPackLicenseState; +import org.elasticsearch.license.internal.XPackLicenseStatus; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.transport.TransportService; +import org.elasticsearch.xpack.core.XPackSettings; + +import static org.hamcrest.core.Is.is; +import static org.mockito.Mockito.mock; + +public class ProfilingInfoTransportActionTests extends ESTestCase { + public void testAvailable() { + // trial mode - allow feature + XPackLicenseState licenseState = new XPackLicenseState(() -> 0); + + boolean enabled = randomBoolean(); + Settings settings = Settings.builder().put(XPackSettings.PROFILING_ENABLED.getKey(), enabled).build(); + + ProfilingInfoTransportAction featureSet = new ProfilingInfoTransportAction( + mock(TransportService.class), + mock(ActionFilters.class), + settings, + new ProfilingLicenseChecker(() -> licenseState) + ); + assertThat(featureSet.available(), is(true)); + assertThat(featureSet.enabled(), is(enabled)); + } + + public void testUnavailable() { + // won't work in BASIC + XPackLicenseState licenseState = new XPackLicenseState(() -> 0, new XPackLicenseStatus(License.OperationMode.BASIC, true, null)); + + boolean enabled = randomBoolean(); + Settings settings = Settings.builder().put(XPackSettings.PROFILING_ENABLED.getKey(), enabled).build(); + + ProfilingInfoTransportAction featureSet = new ProfilingInfoTransportAction( + mock(TransportService.class), + mock(ActionFilters.class), + settings, + new ProfilingLicenseChecker(() -> licenseState) + ); + assertThat(featureSet.available(), is(false)); + assertThat(featureSet.enabled(), is(enabled)); + } +} diff --git a/x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java b/x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java index a9acf669fa750..9f490792d800f 100644 --- a/x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java +++ b/x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java @@ -347,6 +347,7 @@ public class Constants { "cluster:monitor/xpack/info/spatial", "cluster:monitor/xpack/info/sql", "cluster:monitor/xpack/info/transform", + "cluster:monitor/xpack/info/universal_profiling", "cluster:monitor/xpack/info/voting_only", "cluster:monitor/xpack/info/watcher", "cluster:monitor/xpack/license/get", @@ -410,6 +411,7 @@ public class Constants { "cluster:monitor/xpack/usage/spatial", "cluster:monitor/xpack/usage/sql", "cluster:monitor/xpack/usage/transform", + "cluster:monitor/xpack/usage/universal_profiling", "cluster:monitor/xpack/usage/voting_only", "cluster:monitor/xpack/usage/watcher", "cluster:monitor/xpack/watcher/stats/dist",