-
Notifications
You must be signed in to change notification settings - Fork 24.9k
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
Adjust SyntheticSourceLicenseService #116647
Changes from 23 commits
b66645f
73e96f2
452b0d9
3d5abfe
83d19cd
4270f88
5bc5ca5
bed3e78
9b900bb
5ead140
cb62a4c
4856939
5285645
ae4276f
17af39a
33a8e59
53293bc
bd21333
88fa30d
cd410cd
f020aad
5ac8f74
657a64a
77b0c79
2f19b01
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,8 +81,13 @@ public Settings getAdditionalIndexSettings( | |
// This index name is used when validating component and index templates, we should skip this check in that case. | ||
// (See MetadataIndexTemplateService#validateIndexTemplateV2(...) method) | ||
boolean isTemplateValidation = "validate-index-name".equals(indexName); | ||
boolean legacyLicensedUsageOfSyntheticSourceAllowed = isLegacyLicensedUsageOfSyntheticSourceAllowed( | ||
templateIndexMode, | ||
indexName, | ||
dataStreamName | ||
); | ||
if (newIndexHasSyntheticSourceUsage(indexName, templateIndexMode, indexTemplateAndCreateRequestSettings, combinedTemplateMappings) | ||
&& syntheticSourceLicenseService.fallbackToStoredSource(isTemplateValidation)) { | ||
&& syntheticSourceLicenseService.fallbackToStoredSource(isTemplateValidation, legacyLicensedUsageOfSyntheticSourceAllowed)) { | ||
LOGGER.debug("creation of index [{}] with synthetic source without it being allowed", indexName); | ||
return Settings.builder() | ||
.put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), SourceFieldMapper.Mode.STORED.toString()) | ||
|
@@ -167,4 +172,29 @@ private IndexMetadata buildIndexMetadataForMapperService( | |
tmpIndexMetadata.settings(finalResolvedSettings); | ||
return tmpIndexMetadata.build(); | ||
} | ||
|
||
/** | ||
* The GA-ed use cases in which synthetic source usage is allowed with gold or platinum license. | ||
*/ | ||
boolean isLegacyLicensedUsageOfSyntheticSourceAllowed(IndexMode templateIndexMode, String indexName, String dataStreamName) { | ||
if (templateIndexMode == IndexMode.TIME_SERIES) { | ||
return true; | ||
} | ||
|
||
// To allow the following patterns: profiling-metrics and profiling-events | ||
if (dataStreamName != null && dataStreamName.startsWith("profiling-")) { | ||
return true; | ||
} | ||
// To allow the following patterns: .profiling-sq-executables, .profiling-sq-leafframes and .profiling-stacktraces | ||
if (indexName.startsWith(".profiling-")) { | ||
return true; | ||
} | ||
// To allow the following patterns: metrics-apm.transaction.*, metrics-apm.service_transaction.*, metrics-apm.service_summary.*, | ||
martijnvg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// metrics-apm.service_destination.*, "metrics-apm.internal-* and metrics-apm.app.* | ||
if (dataStreamName != null && dataStreamName.startsWith("metrics-apm.")) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @felixbarny Double checking, is the pattern sufficient in selecting the apm data streams that currently use synthetic source? Synthetic source will require an enterprise license from 8.17.0, but for apm's synthetic source usage we will allow gold/platinum license for a little longer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, the pattern looks good. For reference, all the APM templates are in Elasticsearch: https://github.com/elastic/elasticsearch/tree/main/x-pack/plugin/apm-data/src/main/resources/index-templates |
||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,18 +7,30 @@ | |
|
||
package org.elasticsearch.xpack.logsdb; | ||
|
||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
import org.elasticsearch.common.settings.Setting; | ||
import org.elasticsearch.common.settings.Settings; | ||
import org.elasticsearch.license.License; | ||
import org.elasticsearch.license.LicenseService; | ||
import org.elasticsearch.license.LicensedFeature; | ||
import org.elasticsearch.license.XPackLicenseState; | ||
|
||
import java.time.Instant; | ||
import java.time.LocalDateTime; | ||
import java.time.ZoneOffset; | ||
|
||
/** | ||
* Determines based on license and fallback setting whether synthetic source usages should fallback to stored source. | ||
*/ | ||
final class SyntheticSourceLicenseService { | ||
|
||
private static final String MAPPINGS_FEATURE_FAMILY = "mappings"; | ||
static final String MAPPINGS_FEATURE_FAMILY = "mappings"; | ||
// You can only override this property if you received explicit approval from Elastic. | ||
private static final String CUTOFF_DATE_SYS_PROP_NAME = | ||
"es.mapping.synthetic_source_fallback_to_stored_source.cutoff_date_restricted_override"; | ||
private static final Logger LOGGER = LogManager.getLogger(SyntheticSourceLicenseService.class); | ||
static final long DEFAULT_CUTOFF_DATE = LocalDateTime.of(2024, 12, 12, 0, 0).toInstant(ZoneOffset.UTC).toEpochMilli(); | ||
|
||
/** | ||
* A setting that determines whether source mode should always be stored source. Regardless of licence. | ||
|
@@ -30,39 +42,98 @@ final class SyntheticSourceLicenseService { | |
Setting.Property.Dynamic | ||
); | ||
|
||
private static final LicensedFeature.Momentary SYNTHETIC_SOURCE_FEATURE = LicensedFeature.momentary( | ||
static final LicensedFeature.Momentary SYNTHETIC_SOURCE_FEATURE = LicensedFeature.momentary( | ||
MAPPINGS_FEATURE_FAMILY, | ||
"synthetic-source", | ||
License.OperationMode.ENTERPRISE | ||
); | ||
|
||
static final LicensedFeature.Momentary SYNTHETIC_SOURCE_FEATURE_LEGACY = LicensedFeature.momentary( | ||
MAPPINGS_FEATURE_FAMILY, | ||
"synthetic-source-legacy", | ||
License.OperationMode.GOLD | ||
); | ||
|
||
private final long cutoffDate; | ||
private LicenseService licenseService; | ||
private XPackLicenseState licenseState; | ||
private volatile boolean syntheticSourceFallback; | ||
|
||
SyntheticSourceLicenseService(Settings settings) { | ||
syntheticSourceFallback = FALLBACK_SETTING.get(settings); | ||
this(settings, System.getProperty(CUTOFF_DATE_SYS_PROP_NAME)); | ||
} | ||
|
||
SyntheticSourceLicenseService(Settings settings, String cutoffDate) { | ||
this.syntheticSourceFallback = FALLBACK_SETTING.get(settings); | ||
this.cutoffDate = getCutoffDate(cutoffDate); | ||
} | ||
|
||
/** | ||
* @return whether synthetic source mode should fallback to stored source. | ||
*/ | ||
public boolean fallbackToStoredSource(boolean isTemplateValidation) { | ||
public boolean fallbackToStoredSource(boolean isTemplateValidation, boolean legacyLicensedUsageOfSyntheticSourceAllowed) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've had a little more time to look at this, and I wanna propose a refactor: public boolean fallbackToStoredSource(boolean isTemplateValidation, boolean legacyLicensedUsageOfSyntheticSourceAllowed) {
if (syntheticSourceFallback) {
return true;
}
var licenseStateSnapshot = licenseState.copyCurrentLicenseState();
if (checkFeature(SYNTHETIC_SOURCE_FEATURE, licenseStateSnapshot, isTemplateValidation)) {
return false;
}
var license = licenseService.getLicense();
if (license == null) {
return true;
}
boolean beforeCutoffDate = license.startDate() <= cutoffDate;
if (legacyLicensedUsageOfSyntheticSourceAllowed
&& beforeCutoffDate
&& checkFeature(SYNTHETIC_SOURCE_FEATURE_LEGACY, licenseStateSnapshot, isTemplateValidation)) {
// platinum license will allow synthetic source with gold legacy licensed feature too.
LOGGER.debug("legacy license [{}] is allowed to use synthetic source", licenseStateSnapshot.getOperationMode().description());
return false;
}
return true;
}
private static boolean checkFeature(
LicensedFeature.Momentary licensedFeature,
XPackLicenseState licenseStateSnapshot,
boolean isTemplateValidation
) {
if (isTemplateValidation) {
return licensedFeature.checkWithoutTracking(licenseStateSnapshot);
} else {
return licensedFeature.check(licenseStateSnapshot);
}
} This has two advantages:
LMKWYT! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes sense! |
||
if (syntheticSourceFallback) { | ||
return true; | ||
} | ||
|
||
var licenseStateSnapshot = licenseState.copyCurrentLicenseState(); | ||
if (checkFeature(SYNTHETIC_SOURCE_FEATURE, licenseStateSnapshot, isTemplateValidation)) { | ||
return false; | ||
} | ||
|
||
var license = licenseService.getLicense(); | ||
if (license == null) { | ||
return true; | ||
} | ||
|
||
boolean beforeCutoffDate = license.startDate() <= cutoffDate; | ||
if (legacyLicensedUsageOfSyntheticSourceAllowed | ||
&& beforeCutoffDate | ||
&& checkFeature(SYNTHETIC_SOURCE_FEATURE_LEGACY, licenseStateSnapshot, isTemplateValidation)) { | ||
// platinum license will allow synthetic source with gold legacy licensed feature too. | ||
LOGGER.debug("legacy license [{}] is allowed to use synthetic source", licenseStateSnapshot.getOperationMode().description()); | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
private static boolean checkFeature( | ||
LicensedFeature.Momentary licensedFeature, | ||
XPackLicenseState licenseStateSnapshot, | ||
boolean isTemplateValidation | ||
) { | ||
if (isTemplateValidation) { | ||
return SYNTHETIC_SOURCE_FEATURE.checkWithoutTracking(licenseState) == false; | ||
return licensedFeature.checkWithoutTracking(licenseStateSnapshot); | ||
} else { | ||
return SYNTHETIC_SOURCE_FEATURE.check(licenseState) == false; | ||
return licensedFeature.check(licenseStateSnapshot); | ||
} | ||
} | ||
|
||
void setSyntheticSourceFallback(boolean syntheticSourceFallback) { | ||
this.syntheticSourceFallback = syntheticSourceFallback; | ||
} | ||
|
||
void setLicenseService(LicenseService licenseService) { | ||
this.licenseService = licenseService; | ||
} | ||
|
||
void setLicenseState(XPackLicenseState licenseState) { | ||
this.licenseState = licenseState; | ||
} | ||
|
||
private static long getCutoffDate(String cutoffDateAsString) { | ||
if (cutoffDateAsString != null) { | ||
long cutoffDate = LocalDateTime.parse(cutoffDateAsString).toInstant(ZoneOffset.UTC).toEpochMilli(); | ||
LOGGER.warn("Configuring [{}] is only allowed with explicit approval from Elastic.", CUTOFF_DATE_SYS_PROP_NAME); | ||
LOGGER.info( | ||
"Configuring [{}] to [{}]", | ||
CUTOFF_DATE_SYS_PROP_NAME, | ||
LocalDateTime.ofInstant(Instant.ofEpochSecond(cutoffDate), ZoneOffset.UTC) | ||
); | ||
return cutoffDate; | ||
} else { | ||
return DEFAULT_CUTOFF_DATE; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
/* | ||
* 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.logsdb; | ||
|
||
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest; | ||
import org.elasticsearch.common.settings.Settings; | ||
import org.elasticsearch.index.mapper.SourceFieldMapper; | ||
import org.elasticsearch.license.AbstractLicensesIntegrationTestCase; | ||
import org.elasticsearch.license.GetFeatureUsageRequest; | ||
import org.elasticsearch.license.GetFeatureUsageResponse; | ||
import org.elasticsearch.license.License; | ||
import org.elasticsearch.license.LicenseService; | ||
import org.elasticsearch.license.LicensedFeature; | ||
import org.elasticsearch.license.TransportGetFeatureUsageAction; | ||
import org.elasticsearch.license.XPackLicenseState; | ||
import org.elasticsearch.plugins.Plugin; | ||
import org.elasticsearch.test.ESIntegTestCase; | ||
import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin; | ||
import org.hamcrest.Matcher; | ||
import org.junit.Before; | ||
|
||
import java.nio.file.Path; | ||
import java.time.LocalDateTime; | ||
import java.time.ZoneOffset; | ||
import java.util.Collection; | ||
import java.util.List; | ||
|
||
import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST; | ||
import static org.elasticsearch.xpack.logsdb.SyntheticSourceLicenseServiceTests.createEnterpriseLicense; | ||
import static org.elasticsearch.xpack.logsdb.SyntheticSourceLicenseServiceTests.createGoldOrPlatinumLicense; | ||
import static org.hamcrest.Matchers.equalTo; | ||
import static org.hamcrest.Matchers.not; | ||
import static org.hamcrest.Matchers.nullValue; | ||
|
||
@ESIntegTestCase.ClusterScope(scope = TEST, numDataNodes = 1, numClientNodes = 0, supportsDedicatedMasters = false) | ||
public class LegacyLicenceIntegrationTests extends AbstractLicensesIntegrationTestCase { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've got a few suggestions to beef up the coverage in this class (included as diffs below) but basically:
We have these scenarios covered via unit tests but I think getting real integ coverage is important since there are quite a few moving parts where issues might get masked by mocking. diff --git a/x-pack/plugin/logsdb/src/test/java/org/elasticsearch/xpack/logsdb/LegacyLicenceIntegrationTests.java b/x-pack/plugin/logsdb/src/test/java/org/elasticsearch/xpack/logsdb/LegacyLicenceIntegrationTests.java
index d20697369a2..5e7dc0da458 100644
--- a/x-pack/plugin/logsdb/src/test/java/org/elasticsearch/xpack/logsdb/LegacyLicenceIntegrationTests.java
+++ b/x-pack/plugin/logsdb/src/test/java/org/elasticsearch/xpack/logsdb/LegacyLicenceIntegrationTests.java
@@ -11,21 +11,31 @@ import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.mapper.SourceFieldMapper;
import org.elasticsearch.license.AbstractLicensesIntegrationTestCase;
+import org.elasticsearch.license.GetFeatureUsageRequest;
+import org.elasticsearch.license.GetFeatureUsageResponse;
import org.elasticsearch.license.License;
import org.elasticsearch.license.LicenseService;
+import org.elasticsearch.license.LicensedFeature;
+import org.elasticsearch.license.TransportGetFeatureUsageAction;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin;
+import org.hamcrest.Matcher;
import org.junit.Before;
import java.nio.file.Path;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
import java.util.Collection;
import java.util.List;
import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST;
+import static org.elasticsearch.xpack.logsdb.SyntheticSourceLicenseServiceTests.createEnterpriseLicense;
import static org.elasticsearch.xpack.logsdb.SyntheticSourceLicenseServiceTests.createGoldOrPlatinumLicense;
import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
@ESIntegTestCase.ClusterScope(scope = TEST, numDataNodes = 1, numClientNodes = 0, supportsDedicatedMasters = false)
public class LegacyLicenceIntegrationTests extends AbstractLicensesIntegrationTestCase {
@@ -44,28 +54,77 @@ public class LegacyLicenceIntegrationTests extends AbstractLicensesIntegrationTe
ensureGreen();
}
- public void testSyntheticSourceUsageDisallowed() throws Exception {
- String indexName = "test";
- var settings = Settings.builder().put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), "synthetic").build();
- createIndex(indexName, settings);
- var response = admin().indices().getSettings(new GetSettingsRequest().indices(indexName)).actionGet();
- assertThat(
- response.getIndexToSettings().get(indexName).get(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey()),
- equalTo("STORED")
- );
+ public void testSyntheticSourceUsageDisallowed() {
+ createIndexAndAssertExpectedType("test", "STORED");
+
+ assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE_LEGACY, nullValue());
+ assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE, nullValue());
+ }
+
+ public void testSyntheticSourceUsageWithLegacyLicense() {
+ createIndexAndAssertExpectedType(".profiling-stacktraces", "synthetic");
+
+ assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE_LEGACY, not(nullValue()));
+ assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE, nullValue());
+ }
+
+ public void testSyntheticSourceUsageWithLegacyLicensePastCutoff() throws Exception {
+ long startPastCutoff = LocalDateTime.of(2025, 11, 12, 0, 0).toInstant(ZoneOffset.UTC).toEpochMilli();
+ putLicense(createGoldOrPlatinumLicense(startPastCutoff));
+ ensureGreen();
+
+ createIndexAndAssertExpectedType(".profiling-stacktraces", "STORED");
+ assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE_LEGACY, nullValue());
+ assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE, nullValue());
}
- public void testSyntheticSourceUsageWithLegacyLicense() throws Exception {
- String indexName = ".profiling-stacktraces";
+ public void testSyntheticSourceUsageWithEnterpriseLicensePastCutoff() throws Exception {
+ long startPastCutoff = LocalDateTime.of(2025, 11, 12, 0, 0).toInstant(ZoneOffset.UTC).toEpochMilli();
+ putLicense(createEnterpriseLicense(startPastCutoff));
+ ensureGreen();
+
+ createIndexAndAssertExpectedType(".profiling-traces", "synthetic");
+ // also supports non-exceptional indices
+ createIndexAndAssertExpectedType("test", "synthetic");
+ assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE_LEGACY, nullValue());
+ assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE, not(nullValue()));
+ }
+
+ public void testSyntheticSourceUsageTracksBothLegacyAndRegularFeature() throws Exception {
+ createIndexAndAssertExpectedType(".profiling-traces", "synthetic");
+
+ putLicense(createEnterpriseLicense());
+ ensureGreen();
+
+ createIndexAndAssertExpectedType(".profiling-traces-v2", "synthetic");
+
+ assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE_LEGACY, not(nullValue()));
+ assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE, not(nullValue()));
+ }
+
+ private void createIndexAndAssertExpectedType(String indexName, String expectedType) {
var settings = Settings.builder().put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), "synthetic").build();
createIndex(indexName, settings);
var response = admin().indices().getSettings(new GetSettingsRequest().indices(indexName)).actionGet();
assertThat(
response.getIndexToSettings().get(indexName).get(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey()),
- equalTo("synthetic")
+ equalTo(expectedType)
);
}
+ private List<GetFeatureUsageResponse.FeatureUsageInfo> getFeatureUsageInfo() {
+ return client().execute(TransportGetFeatureUsageAction.TYPE, new GetFeatureUsageRequest()).actionGet().getFeatures();
+ }
+
+ private void assertFeatureUsage(LicensedFeature.Momentary syntheticSourceFeature, Matcher<Object> matcher) {
+ GetFeatureUsageResponse.FeatureUsageInfo featureUsage = getFeatureUsageInfo().stream()
+ .filter(f -> f.getFamily().equals(SyntheticSourceLicenseService.MAPPINGS_FEATURE_FAMILY))
+ .filter(f -> f.getName().equals(syntheticSourceFeature.getName()))
+ .findAny()
+ .orElse(null);
+ assertThat(featureUsage, matcher);
+ }
+
public static class P extends LocalStateCompositeXPackPlugin {
public P(final Settings settings, final Path configPath) { And: diff --git a/x-pack/plugin/logsdb/src/test/java/org/elasticsearch/xpack/logsdb/SyntheticSourceLicenseServiceTests.java b/x-pack/plugin/logsdb/src/test/java/org/elasticsearch/xpack/logsdb/SyntheticSourceLicenseServiceTests.java
index e9f530ff868..682749bd436 100644
--- a/x-pack/plugin/logsdb/src/test/java/org/elasticsearch/xpack/logsdb/SyntheticSourceLicenseServiceTests.java
+++ b/x-pack/plugin/logsdb/src/test/java/org/elasticsearch/xpack/logsdb/SyntheticSourceLicenseServiceTests.java
@@ -34,7 +34,7 @@ public class SyntheticSourceLicenseServiceTests extends ESTestCase {
@Before
public void setup() throws Exception {
mockLicenseService = mock(LicenseService.class);
- License license = createEnterpirseLicense();
+ License license = createEnterpriseLicense();
when(mockLicenseService.getLicense()).thenReturn(license);
licenseService = new SyntheticSourceLicenseService(Settings.EMPTY);
}
@@ -163,15 +163,19 @@ public class SyntheticSourceLicenseServiceTests extends ESTestCase {
assertEquals("Provided cutoff date is beyond max cutoff date", e.getMessage());
}
- static License createEnterpirseLicense() throws Exception {
+ static License createEnterpriseLicense() throws Exception {
long start = LocalDateTime.of(2024, 11, 12, 0, 0).toInstant(ZoneOffset.UTC).toEpochMilli();
+ return createEnterpriseLicense(start);
+ }
+
+ static License createEnterpriseLicense(long start) throws Exception {
String uid = UUID.randomUUID().toString();
final License.Builder builder = License.builder()
.uid(uid)
.version(License.VERSION_CURRENT)
.expiryDate(dateMath("now+2d", System.currentTimeMillis()))
.startDate(start)
- .issueDate(start)
+ .issueDate(System.currentTimeMillis())
.type("enterprise")
.issuedTo("customer")
.issuer("elasticsearch")
@@ -191,7 +195,7 @@ public class SyntheticSourceLicenseServiceTests extends ESTestCase {
.version(License.VERSION_CURRENT)
.expiryDate(dateMath("now+100d", System.currentTimeMillis()))
.startDate(start)
- .issueDate(start)
+ .issueDate(System.currentTimeMillis())
.type(randomBoolean() ? "gold" : "platinum")
.issuedTo("customer")
.issuer("elasticsearch") There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed and thanks for beefing up the test coverage here. |
||
|
||
@Override | ||
protected Collection<Class<? extends Plugin>> nodePlugins() { | ||
return List.of(P.class); | ||
} | ||
|
||
@Before | ||
public void setup() throws Exception { | ||
wipeAllLicenses(); | ||
ensureGreen(); | ||
License license = createGoldOrPlatinumLicense(); | ||
putLicense(license); | ||
ensureGreen(); | ||
} | ||
|
||
public void testSyntheticSourceUsageDisallowed() { | ||
createIndexWithSyntheticSourceAndAssertExpectedType("test", "STORED"); | ||
|
||
assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE_LEGACY, nullValue()); | ||
assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE, nullValue()); | ||
} | ||
|
||
public void testSyntheticSourceUsageWithLegacyLicense() { | ||
createIndexWithSyntheticSourceAndAssertExpectedType(".profiling-stacktraces", "synthetic"); | ||
|
||
assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE_LEGACY, not(nullValue())); | ||
assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE, nullValue()); | ||
} | ||
|
||
public void testSyntheticSourceUsageWithLegacyLicensePastCutoff() throws Exception { | ||
long startPastCutoff = LocalDateTime.of(2025, 11, 12, 0, 0).toInstant(ZoneOffset.UTC).toEpochMilli(); | ||
putLicense(createGoldOrPlatinumLicense(startPastCutoff)); | ||
ensureGreen(); | ||
|
||
createIndexWithSyntheticSourceAndAssertExpectedType(".profiling-stacktraces", "STORED"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should test with synthetic? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, this method creates an index with source mode set to synthetic. The last argument here is what the expected source mode should be. |
||
assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE_LEGACY, nullValue()); | ||
assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE, nullValue()); | ||
} | ||
|
||
public void testSyntheticSourceUsageWithEnterpriseLicensePastCutoff() throws Exception { | ||
long startPastCutoff = LocalDateTime.of(2025, 11, 12, 0, 0).toInstant(ZoneOffset.UTC).toEpochMilli(); | ||
putLicense(createEnterpriseLicense(startPastCutoff)); | ||
ensureGreen(); | ||
|
||
createIndexWithSyntheticSourceAndAssertExpectedType(".profiling-traces", "synthetic"); | ||
// also supports non-exceptional indices | ||
createIndexWithSyntheticSourceAndAssertExpectedType("test", "synthetic"); | ||
assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE_LEGACY, nullValue()); | ||
assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE, not(nullValue())); | ||
} | ||
|
||
public void testSyntheticSourceUsageTracksBothLegacyAndRegularFeature() throws Exception { | ||
createIndexWithSyntheticSourceAndAssertExpectedType(".profiling-traces", "synthetic"); | ||
|
||
putLicense(createEnterpriseLicense()); | ||
ensureGreen(); | ||
|
||
createIndexWithSyntheticSourceAndAssertExpectedType(".profiling-traces-v2", "synthetic"); | ||
|
||
assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE_LEGACY, not(nullValue())); | ||
assertFeatureUsage(SyntheticSourceLicenseService.SYNTHETIC_SOURCE_FEATURE, not(nullValue())); | ||
} | ||
|
||
private void createIndexWithSyntheticSourceAndAssertExpectedType(String indexName, String expectedType) { | ||
var settings = Settings.builder().put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), "synthetic").build(); | ||
createIndex(indexName, settings); | ||
var response = admin().indices().getSettings(new GetSettingsRequest().indices(indexName)).actionGet(); | ||
assertThat( | ||
response.getIndexToSettings().get(indexName).get(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey()), | ||
equalTo(expectedType) | ||
); | ||
} | ||
|
||
private List<GetFeatureUsageResponse.FeatureUsageInfo> getFeatureUsageInfo() { | ||
return client().execute(TransportGetFeatureUsageAction.TYPE, new GetFeatureUsageRequest()).actionGet().getFeatures(); | ||
} | ||
|
||
private void assertFeatureUsage(LicensedFeature.Momentary syntheticSourceFeature, Matcher<Object> matcher) { | ||
GetFeatureUsageResponse.FeatureUsageInfo featureUsage = getFeatureUsageInfo().stream() | ||
.filter(f -> f.getFamily().equals(SyntheticSourceLicenseService.MAPPINGS_FEATURE_FAMILY)) | ||
.filter(f -> f.getName().equals(syntheticSourceFeature.getName())) | ||
.findAny() | ||
.orElse(null); | ||
assertThat(featureUsage, matcher); | ||
} | ||
|
||
public static class P extends LocalStateCompositeXPackPlugin { | ||
|
||
public P(final Settings settings, final Path configPath) { | ||
super(settings, configPath); | ||
plugins.add(new LogsDBPlugin(settings) { | ||
@Override | ||
protected XPackLicenseState getLicenseState() { | ||
return P.this.getLicenseState(); | ||
} | ||
|
||
@Override | ||
protected LicenseService getLicenseService() { | ||
return P.this.getLicenseService(); | ||
} | ||
}); | ||
} | ||
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@danielmitterdorfer Double checking, is the pattern sufficient in selecting the profiling data streams and indices that currently use synthetic source? Synthetic source will require an enterprise license from 8.17.0, but for profiling's synthetic source usage we will allow gold/platinum license for a little longer.