diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/ClusterStateLicenseService.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/ClusterStateLicenseService.java index 891e8343ffe0f..d4d62a75e98c7 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/ClusterStateLicenseService.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/ClusterStateLicenseService.java @@ -31,6 +31,7 @@ import org.elasticsearch.core.TimeValue; import org.elasticsearch.gateway.GatewayService; import org.elasticsearch.license.internal.MutableLicenseService; +import org.elasticsearch.license.internal.TrialLicenseVersion; import org.elasticsearch.license.internal.XPackLicenseStatus; import org.elasticsearch.protocol.xpack.license.LicensesStatus; import org.elasticsearch.protocol.xpack.license.PutLicenseResponse; @@ -249,7 +250,7 @@ public ClusterState execute(ClusterState currentState) throws Exception { } Metadata currentMetadata = currentState.metadata(); LicensesMetadata licensesMetadata = currentMetadata.custom(LicensesMetadata.TYPE); - Version trialVersion = null; + TrialLicenseVersion trialVersion = null; if (licensesMetadata != null) { trialVersion = licensesMetadata.getMostRecentTrialVersion(); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensesMetadata.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensesMetadata.java index 08c30a1670f61..769c5b2847de6 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensesMetadata.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensesMetadata.java @@ -8,7 +8,6 @@ import org.elasticsearch.TransportVersion; import org.elasticsearch.TransportVersions; -import org.elasticsearch.Version; import org.elasticsearch.cluster.AbstractNamedDiffable; import org.elasticsearch.cluster.NamedDiff; import org.elasticsearch.cluster.metadata.Metadata; @@ -16,6 +15,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.core.Nullable; +import org.elasticsearch.license.internal.TrialLicenseVersion; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentParser; @@ -57,11 +57,11 @@ public class LicensesMetadata extends AbstractNamedDiffable imp // is null, then no trial has been exercised. We keep the version to leave open the possibility that we // may eventually allow a cluster to exercise a trial every time they upgrade to a new major version. @Nullable - private Version trialVersion; + private TrialLicenseVersion trialLicenseVersion; - public LicensesMetadata(License license, Version trialVersion) { + public LicensesMetadata(License license, TrialLicenseVersion trialLicenseVersion) { this.license = license; - this.trialVersion = trialVersion; + this.trialLicenseVersion = trialLicenseVersion; } public License getLicense() { @@ -69,19 +69,19 @@ public License getLicense() { } boolean isEligibleForTrial() { - if (trialVersion == null) { + if (trialLicenseVersion == null) { return true; } - return Version.CURRENT.major > trialVersion.major; + return trialLicenseVersion.ableToStartNewTrialSince(TrialLicenseVersion.CURRENT); } - Version getMostRecentTrialVersion() { - return trialVersion; + TrialLicenseVersion getMostRecentTrialVersion() { + return trialLicenseVersion; } @Override public String toString() { - return "LicensesMetadata{" + "license=" + license + ", trialVersion=" + trialVersion + '}'; + return "LicensesMetadata{" + "license=" + license + ", trialVersion=" + trialLicenseVersion + '}'; } @Override @@ -91,13 +91,13 @@ public boolean equals(Object o) { LicensesMetadata that = (LicensesMetadata) o; - return Objects.equals(license, that.license) && Objects.equals(trialVersion, that.trialVersion); + return Objects.equals(license, that.license) && Objects.equals(trialLicenseVersion, that.trialLicenseVersion); } @Override public int hashCode() { int result = license != null ? license.hashCode() : 0; - result = 31 * result + (trialVersion != null ? trialVersion.hashCode() : 0); + result = 31 * result + (trialLicenseVersion != null ? trialLicenseVersion.hashCode() : 0); return result; } @@ -118,7 +118,7 @@ public EnumSet context() { public static LicensesMetadata fromXContent(XContentParser parser) throws IOException { License license = LICENSE_TOMBSTONE; - Version trialLicense = null; + TrialLicenseVersion trialLicense = null; XContentParser.Token token; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { @@ -133,7 +133,7 @@ public static LicensesMetadata fromXContent(XContentParser parser) throws IOExce } } else if (fieldName.equals(Fields.TRIAL_LICENSE)) { parser.nextToken(); - trialLicense = Version.fromString(parser.text()); + trialLicense = TrialLicenseVersion.fromXContent(parser.text()); } } } @@ -151,8 +151,8 @@ public Iterator toXContentChunked(ToXContent.Params ignore license.toInnerXContent(builder, params); builder.endObject(); } - if (trialVersion != null) { - builder.field(Fields.TRIAL_LICENSE, trialVersion.toString()); + if (trialLicenseVersion != null) { + builder.field(Fields.TRIAL_LICENSE, trialLicenseVersion.toString()); } return builder; })); @@ -166,11 +166,11 @@ public void writeTo(StreamOutput streamOutput) throws IOException { streamOutput.writeBoolean(true); // has a license license.writeTo(streamOutput); } - if (trialVersion == null) { + if (trialLicenseVersion == null) { streamOutput.writeBoolean(false); } else { streamOutput.writeBoolean(true); - Version.writeVersion(trialVersion, streamOutput); + trialLicenseVersion.writeTo(streamOutput); } } @@ -182,7 +182,7 @@ public LicensesMetadata(StreamInput streamInput) throws IOException { } boolean hasExercisedTrial = streamInput.readBoolean(); if (hasExercisedTrial) { - this.trialVersion = Version.readVersion(streamInput); + this.trialLicenseVersion = new TrialLicenseVersion(streamInput); } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartBasicClusterTask.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartBasicClusterTask.java index adc0d66353608..1953a31c452ab 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartBasicClusterTask.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartBasicClusterTask.java @@ -7,7 +7,6 @@ package org.elasticsearch.license; import org.apache.logging.log4j.Logger; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateTaskExecutor; @@ -15,6 +14,7 @@ import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.core.Nullable; +import org.elasticsearch.license.internal.TrialLicenseVersion; import org.elasticsearch.xpack.core.XPackPlugin; import java.time.Clock; @@ -78,7 +78,7 @@ public LicensesMetadata execute( return currentLicensesMetadata; } } - Version trialVersion = currentLicensesMetadata != null ? currentLicensesMetadata.getMostRecentTrialVersion() : null; + TrialLicenseVersion trialVersion = currentLicensesMetadata != null ? currentLicensesMetadata.getMostRecentTrialVersion() : null; updatedLicensesMetadata = new LicensesMetadata(selfGeneratedLicense, trialVersion); } else { updatedLicensesMetadata = currentLicensesMetadata; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartTrialClusterTask.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartTrialClusterTask.java index 814634fc046ee..02b4bc15eaaee 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartTrialClusterTask.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartTrialClusterTask.java @@ -7,7 +7,6 @@ package org.elasticsearch.license; import org.apache.logging.log4j.Logger; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateTaskExecutor; @@ -15,6 +14,7 @@ import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.core.Nullable; +import org.elasticsearch.license.internal.TrialLicenseVersion; import org.elasticsearch.xpack.core.XPackPlugin; import java.time.Clock; @@ -62,6 +62,15 @@ private LicensesMetadata execute( ClusterStateTaskExecutor.TaskContext taskContext ) { assert taskContext.getTask() == this; + if (discoveryNodes.getMaxNodeVersion().after(discoveryNodes.getSmallestNonClientNodeVersion())) { + throw new IllegalStateException( + "Please ensure all nodes are on the same version before starting your trial, the highest node version in this cluster is [" + + discoveryNodes.getMaxNodeVersion() + + "] and the lowest node version is [" + + discoveryNodes.getMinNodeVersion() + + "]" + ); + } final var listener = ActionListener.runBefore(this.listener, () -> { logger.debug("started self generated trial license: {}", currentLicensesMetadata); }); @@ -88,7 +97,7 @@ private LicensesMetadata execute( specBuilder.maxNodes(LicenseSettings.SELF_GENERATED_LICENSE_MAX_NODES); } License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder, discoveryNodes); - LicensesMetadata newLicensesMetadata = new LicensesMetadata(selfGeneratedLicense, Version.CURRENT); + LicensesMetadata newLicensesMetadata = new LicensesMetadata(selfGeneratedLicense, TrialLicenseVersion.CURRENT); taskContext.success(() -> listener.onResponse(new PostStartTrialResponse(PostStartTrialResponse.Status.UPGRADED_TO_TRIAL))); return newLicensesMetadata; } else { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartupSelfGeneratedLicenseTask.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartupSelfGeneratedLicenseTask.java index a6d762f61e951..518b45dd027ad 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartupSelfGeneratedLicenseTask.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartupSelfGeneratedLicenseTask.java @@ -9,13 +9,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.util.Supplier; -import org.elasticsearch.Version; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Nullable; +import org.elasticsearch.license.internal.TrialLicenseVersion; import org.elasticsearch.xpack.core.XPackPlugin; import java.time.Clock; @@ -87,7 +87,7 @@ private ClusterState updateLicenseSignature(ClusterState currentState, LicensesM .type(type) .expiryDate(expiryDate); License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder, currentState.nodes()); - Version trialVersion = currentLicenseMetadata.getMostRecentTrialVersion(); + TrialLicenseVersion trialVersion = currentLicenseMetadata.getMostRecentTrialVersion(); LicensesMetadata newLicenseMetadata = new LicensesMetadata(selfGeneratedLicense, trialVersion); mdBuilder.putCustom(LicensesMetadata.TYPE, newLicenseMetadata); logger.info( @@ -129,7 +129,7 @@ private LicensesMetadata createBasicLicenseFromExistingLicense(LicensesMetadata .type(License.LicenseType.BASIC) .expiryDate(LicenseSettings.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS); License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder, currentLicense.version()); - Version trialVersion = currentLicenseMetadata.getMostRecentTrialVersion(); + TrialLicenseVersion trialVersion = currentLicenseMetadata.getMostRecentTrialVersion(); return new LicensesMetadata(selfGeneratedLicense, trialVersion); } @@ -152,7 +152,7 @@ private ClusterState updateWithLicense(ClusterState currentState, License.Licens License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder, currentState.nodes()); LicensesMetadata licensesMetadata; if (License.LicenseType.TRIAL.equals(type)) { - licensesMetadata = new LicensesMetadata(selfGeneratedLicense, Version.CURRENT); + licensesMetadata = new LicensesMetadata(selfGeneratedLicense, TrialLicenseVersion.CURRENT); } else { licensesMetadata = new LicensesMetadata(selfGeneratedLicense, null); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPostStartTrialAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPostStartTrialAction.java index 1d3b4a0698ad5..27f7cbff2e3ec 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPostStartTrialAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPostStartTrialAction.java @@ -55,6 +55,15 @@ protected void masterOperation( ClusterState state, ActionListener listener ) throws Exception { + if (state.nodes().getMaxNodeVersion().after(state.nodes().getSmallestNonClientNodeVersion())) { + throw new IllegalStateException( + "Please ensure all nodes are on the same version before starting your trial, the highest node version in this cluster is [" + + state.nodes().getMaxNodeVersion() + + "] and the lowest node version is [" + + state.nodes().getMinNodeVersion() + + "]" + ); + } licenseService.startTrialLicense(request, listener); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/internal/TrialLicenseVersion.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/internal/TrialLicenseVersion.java new file mode 100644 index 0000000000000..6de9fec098a78 --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/internal/TrialLicenseVersion.java @@ -0,0 +1,124 @@ +/* + * 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.license.internal; + +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.xcontent.ToXContentFragment; +import org.elasticsearch.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.Objects; + +/** + * Sometimes we release a version with a bunch of cool new features, and we want people to be able to start a new trial license in a cluster + * that's already used a trial and let it expire. This class controls when we do that. The serialization of this class is designed to + * maintain compatibility with old-school Elasticsearch versions (specifically the {@link org.elasticsearch.Version} class). + */ +public class TrialLicenseVersion implements ToXContentFragment, Writeable { + + // This was the highest version at the time we cut over to having a specific version for the trial license, rather than reusing the + // generic Elasticsearch version. While it's derived from the Elasticsearch version formula for BWC, it is independent of it going + // forward. When we want users to be able to start a new trial, increment this number. + // Pkg-private for testing only. + static final int TRIAL_VERSION_CUTOVER = 8_12_00_99; + public static final TrialLicenseVersion CURRENT = new TrialLicenseVersion(TRIAL_VERSION_CUTOVER); + + // The most recently released major version when we cut over. Here for maintaining BWC behavior. + static final int TRIAL_VERSION_CUTOVER_MAJOR = 8; + + private final int trialVersion; + + public TrialLicenseVersion(int trialVersion) { + this.trialVersion = trialVersion; + } + + public TrialLicenseVersion(StreamInput in) throws IOException { + this.trialVersion = in.readVInt(); + } + + public static TrialLicenseVersion fromXContent(String from) { + try { + return new TrialLicenseVersion(Integer.parseInt(from)); + } catch (NumberFormatException ex) { + return new TrialLicenseVersion(parseVersionString(from)); + } + } + + // copied from Version and simplified, for backwards compatibility parsing old version strings in LicensesMetadata XContent + private static int parseVersionString(String version) { + final boolean snapshot = version.endsWith("-SNAPSHOT"); // this is some BWC for 2.x and before indices + if (snapshot) { + version = version.substring(0, version.length() - 9); + } + String[] parts = version.split("[.-]"); + if (parts.length != 3) { + throw new IllegalArgumentException("unable to parse trial license version: " + version); + } + + try { + final int rawMajor = Integer.parseInt(parts[0]); + // we reverse the version id calculation based on some assumption as we can't reliably reverse the modulo + final int major = rawMajor * 1000000; + final int minor = Integer.parseInt(parts[1]) * 10000; + final int revision = Integer.parseInt(parts[2]) * 100; + + // 99 is leftover from alpha/beta/rc, it should be removed + return major + minor + revision + 99; + + } catch (NumberFormatException e) { + throw new IllegalArgumentException("unable to parse trial license version: " + version, e); + } + } + + int asInt() { + return trialVersion; + } + + public boolean ableToStartNewTrialSince(TrialLicenseVersion since) { + if (since.asInt() < TRIAL_VERSION_CUTOVER) { + int sinceMajorVersion = since.asInt() / 1_000_000; // integer division is intentional + return sinceMajorVersion < TRIAL_VERSION_CUTOVER_MAJOR; + } + return since.asInt() < trialVersion; + } + + @Override + public String toString() { + return Integer.toString(trialVersion); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.value(trialVersion); // suffix added for BWC + } + + // pkg-private for testing + String asVersionString() { + return this + ".0.0"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrialLicenseVersion that = (TrialLicenseVersion) o; + return trialVersion == that.trialVersion; + } + + @Override + public int hashCode() { + return Objects.hash(trialVersion); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeVInt(trialVersion); + } +} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesMetadataSerializationTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesMetadataSerializationTests.java index 564f4171569ad..4ed306bf734fc 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesMetadataSerializationTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesMetadataSerializationTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.util.CollectionUtils; import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.license.internal.TrialLicenseVersion; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ToXContent; @@ -49,7 +50,7 @@ public void testXContentSerializationOneSignedLicense() throws Exception { public void testXContentSerializationOneSignedLicenseWithUsedTrial() throws Exception { License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(2)); - LicensesMetadata licensesMetadata = new LicensesMetadata(license, Version.CURRENT); + LicensesMetadata licensesMetadata = new LicensesMetadata(license, TrialLicenseVersion.CURRENT); XContentBuilder builder = XContentFactory.jsonBuilder(); builder.startObject(); builder.startObject("licenses"); @@ -58,12 +59,12 @@ public void testXContentSerializationOneSignedLicenseWithUsedTrial() throws Exce builder.endObject(); LicensesMetadata licensesMetadataFromXContent = getLicensesMetadataFromXContent(createParser(builder)); assertThat(licensesMetadataFromXContent.getLicense(), equalTo(license)); - assertEquals(licensesMetadataFromXContent.getMostRecentTrialVersion(), Version.CURRENT); + assertEquals(licensesMetadataFromXContent.getMostRecentTrialVersion(), TrialLicenseVersion.CURRENT); } public void testLicenseMetadataParsingDoesNotSwallowOtherMetadata() throws Exception { License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(2)); - LicensesMetadata licensesMetadata = new LicensesMetadata(license, Version.CURRENT); + LicensesMetadata licensesMetadata = new LicensesMetadata(license, TrialLicenseVersion.CURRENT); RepositoryMetadata repositoryMetadata = new RepositoryMetadata("repo", "fs", Settings.EMPTY); RepositoriesMetadata repositoriesMetadata = new RepositoriesMetadata(Collections.singletonList(repositoryMetadata)); final Metadata.Builder metadataBuilder = Metadata.builder(); @@ -97,7 +98,7 @@ public void testXContentSerializationOneTrial() throws Exception { .type(randomBoolean() ? "trial" : "basic") .expiryDate(issueDate + TimeValue.timeValueHours(2).getMillis()); final License trialLicense = SelfGeneratedLicense.create(specBuilder, License.VERSION_CURRENT); - LicensesMetadata licensesMetadata = new LicensesMetadata(trialLicense, Version.CURRENT); + LicensesMetadata licensesMetadata = new LicensesMetadata(trialLicense, TrialLicenseVersion.CURRENT); XContentBuilder builder = XContentFactory.jsonBuilder(); builder.startObject(); builder.startObject("licenses"); @@ -106,7 +107,7 @@ public void testXContentSerializationOneTrial() throws Exception { builder.endObject(); LicensesMetadata licensesMetadataFromXContent = getLicensesMetadataFromXContent(createParser(builder)); assertThat(licensesMetadataFromXContent.getLicense(), equalTo(trialLicense)); - assertEquals(licensesMetadataFromXContent.getMostRecentTrialVersion(), Version.CURRENT); + assertEquals(licensesMetadataFromXContent.getMostRecentTrialVersion(), TrialLicenseVersion.CURRENT); } public void testLicenseTombstoneFromXContext() throws Exception { @@ -130,7 +131,7 @@ public void testLicenseTombstoneWithUsedTrialFromXContext() throws Exception { builder.endObject(); LicensesMetadata metadataFromXContent = getLicensesMetadataFromXContent(createParser(builder)); assertThat(metadataFromXContent.getLicense(), equalTo(LicensesMetadata.LICENSE_TOMBSTONE)); - assertEquals(metadataFromXContent.getMostRecentTrialVersion(), Version.CURRENT); + assertEquals(metadataFromXContent.getMostRecentTrialVersion(), TrialLicenseVersion.CURRENT); } private static LicensesMetadata getLicensesMetadataFromXContent(XContentParser parser) throws Exception { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/internal/TrialLicenseVersionTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/internal/TrialLicenseVersionTests.java new file mode 100644 index 0000000000000..ff62fbc4d4877 --- /dev/null +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/internal/TrialLicenseVersionTests.java @@ -0,0 +1,43 @@ +/* + * 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.license.internal; + +import org.elasticsearch.Version; +import org.elasticsearch.test.ESTestCase; + +import static org.elasticsearch.license.internal.TrialLicenseVersion.TRIAL_VERSION_CUTOVER; +import static org.elasticsearch.license.internal.TrialLicenseVersion.TRIAL_VERSION_CUTOVER_MAJOR; +import static org.hamcrest.Matchers.equalTo; + +public class TrialLicenseVersionTests extends ESTestCase { + + public void testCanParseAllVersions() { + for (var version : Version.getDeclaredVersions(Version.class)) { + TrialLicenseVersion parsedVersion = TrialLicenseVersion.fromXContent(version.toString()); + if (version.major < TRIAL_VERSION_CUTOVER_MAJOR) { + assertTrue(new TrialLicenseVersion(TRIAL_VERSION_CUTOVER).ableToStartNewTrialSince(parsedVersion)); + } else { + assertFalse(new TrialLicenseVersion(TRIAL_VERSION_CUTOVER).ableToStartNewTrialSince(parsedVersion)); + } + } + } + + public void testRoundTripParsing() { + var randomVersion = new TrialLicenseVersion(randomNonNegativeInt()); + assertThat(TrialLicenseVersion.fromXContent(randomVersion.toString()), equalTo(randomVersion)); + } + + public void testNewTrialAllowed() { + var randomVersion = new TrialLicenseVersion(randomNonNegativeInt()); + var subsequentVersion = new TrialLicenseVersion( + randomVersion.asInt() + randomIntBetween(0, Integer.MAX_VALUE - randomVersion.asInt()) + ); + assertFalse(randomVersion.ableToStartNewTrialSince(randomVersion)); + assertTrue(subsequentVersion.ableToStartNewTrialSince(randomVersion)); + } +} diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityImplicitBehaviorBootstrapCheckTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityImplicitBehaviorBootstrapCheckTests.java index 9775e461c4165..413358f784dea 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityImplicitBehaviorBootstrapCheckTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityImplicitBehaviorBootstrapCheckTests.java @@ -19,6 +19,7 @@ import org.elasticsearch.license.License; import org.elasticsearch.license.LicensesMetadata; import org.elasticsearch.license.TestUtils; +import org.elasticsearch.license.internal.TrialLicenseVersion; import org.elasticsearch.test.AbstractBootstrapCheckTestCase; import org.elasticsearch.test.VersionUtils; import org.elasticsearch.xpack.core.XPackSettings; @@ -38,7 +39,10 @@ public void testFailureUpgradeFrom7xWithImplicitSecuritySettings() throws Except NodeMetadata nodeMetadata = new NodeMetadata(randomAlphaOfLength(10), previousVersion, IndexVersion.current()); nodeMetadata = nodeMetadata.upgradeToCurrentVersion(); ClusterStateLicenseService licenseService = mock(ClusterStateLicenseService.class); - Metadata metadata = createLicensesMetadata(previousVersion, randomFrom("basic", "trial")); + Metadata metadata = createLicensesMetadata( + TrialLicenseVersion.fromXContent(previousVersion.toString()), + randomFrom("basic", "trial") + ); License license = mock(License.class); when(licenseService.getLicense(metadata)).thenReturn(license); when(license.operationMode()).thenReturn(randomFrom(License.OperationMode.BASIC, License.OperationMode.TRIAL)); @@ -70,7 +74,10 @@ public void testUpgradeFrom7xWithImplicitSecuritySettingsOnGoldPlus() throws Exc NodeMetadata nodeMetadata = new NodeMetadata(randomAlphaOfLength(10), previousVersion, IndexVersion.current()); nodeMetadata = nodeMetadata.upgradeToCurrentVersion(); ClusterStateLicenseService licenseService = mock(ClusterStateLicenseService.class); - Metadata metadata = createLicensesMetadata(previousVersion, randomFrom("gold", "platinum")); + Metadata metadata = createLicensesMetadata( + TrialLicenseVersion.fromXContent(previousVersion.toString()), + randomFrom("gold", "platinum") + ); License license = mock(License.class); when(licenseService.getLicense(metadata)).thenReturn(license); when(license.operationMode()).thenReturn(randomFrom(License.OperationMode.GOLD, License.OperationMode.PLATINUM)); @@ -91,7 +98,7 @@ public void testUpgradeFrom7xWithExplicitSecuritySettings() throws Exception { BootstrapCheck.BootstrapCheckResult result = new SecurityImplicitBehaviorBootstrapCheck(nodeMetadata, licenseService).check( createTestContext( Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build(), - createLicensesMetadata(previousVersion, randomFrom("basic", "trial")) + createLicensesMetadata(TrialLicenseVersion.fromXContent(previousVersion.toString()), randomFrom("basic", "trial")) ) ); assertThat(result.isSuccess(), is(true)); @@ -103,7 +110,10 @@ public void testUpgradeFrom8xWithImplicitSecuritySettings() throws Exception { nodeMetadata = nodeMetadata.upgradeToCurrentVersion(); ClusterStateLicenseService licenseService = mock(ClusterStateLicenseService.class); BootstrapCheck.BootstrapCheckResult result = new SecurityImplicitBehaviorBootstrapCheck(nodeMetadata, licenseService).check( - createTestContext(Settings.EMPTY, createLicensesMetadata(previousVersion, randomFrom("basic", "trial"))) + createTestContext( + Settings.EMPTY, + createLicensesMetadata(TrialLicenseVersion.fromXContent(previousVersion.toString()), randomFrom("basic", "trial")) + ) ); assertThat(result.isSuccess(), is(true)); } @@ -116,14 +126,14 @@ public void testUpgradeFrom8xWithExplicitSecuritySettings() throws Exception { BootstrapCheck.BootstrapCheckResult result = new SecurityImplicitBehaviorBootstrapCheck(nodeMetadata, licenseService).check( createTestContext( Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build(), - createLicensesMetadata(previousVersion, randomFrom("basic", "trial")) + createLicensesMetadata(TrialLicenseVersion.fromXContent(previousVersion.toString()), randomFrom("basic", "trial")) ) ); assertThat(result.isSuccess(), is(true)); } - private Metadata createLicensesMetadata(Version version, String licenseMode) throws Exception { + private Metadata createLicensesMetadata(TrialLicenseVersion era, String licenseMode) throws Exception { License license = TestUtils.generateSignedLicense(licenseMode, TimeValue.timeValueHours(2)); - return Metadata.builder().putCustom(LicensesMetadata.TYPE, new LicensesMetadata(license, version)).build(); + return Metadata.builder().putCustom(LicensesMetadata.TYPE, new LicensesMetadata(license, era)).build(); } }