diff --git a/src/main/java/org/elasticsearch/license/core/ESLicense.java b/src/main/java/org/elasticsearch/license/core/ESLicense.java index 06c9fef0b2b..764ca257417 100644 --- a/src/main/java/org/elasticsearch/license/core/ESLicense.java +++ b/src/main/java/org/elasticsearch/license/core/ESLicense.java @@ -157,7 +157,6 @@ public class ESLicense implements Comparable, ToXContent { builder.issuedTo(in.readString()); builder.issuer(in.readString()); builder.signature(in.readOptionalString()); - builder.verify(); return builder.build(); } diff --git a/src/main/java/org/elasticsearch/license/plugin/core/LicensesMetaData.java b/src/main/java/org/elasticsearch/license/plugin/core/LicensesMetaData.java index e5a79bceb8c..2f8dd110db9 100644 --- a/src/main/java/org/elasticsearch/license/plugin/core/LicensesMetaData.java +++ b/src/main/java/org/elasticsearch/license/plugin/core/LicensesMetaData.java @@ -60,6 +60,9 @@ public class LicensesMetaData implements MetaData.Custom { if (obj == null) { return false; } + if (obj == this) { + return true; + } if (obj instanceof LicensesMetaData) { LicensesMetaData other = (LicensesMetaData) obj; boolean signaturesEqual; @@ -136,7 +139,6 @@ public class LicensesMetaData implements MetaData.Custom { */ @Override public LicensesMetaData fromXContent(XContentParser parser) throws IOException { - XContentParser.Token token; String fieldName = null; Set encodedTrialLicenses = new HashSet<>(); diff --git a/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java b/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java index 54c38ae2471..93c35f11394 100644 --- a/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java +++ b/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java @@ -37,7 +37,6 @@ import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -80,7 +79,6 @@ public class LicensesService extends AbstractLifecycleComponent this.threadPool = threadPool; this.transportService = transportService; this.lastObservedLicensesState = new AtomicReference<>(null); - //this.notificationScheduler = new AtomicReference<>(null); transportService.registerHandler(REGISTER_TRIAL_LICENSE_ACTION_NAME, new RegisterTrialLicenseRequestHandler()); } @@ -90,7 +88,6 @@ public class LicensesService extends AbstractLifecycleComponent * This method can be only called on the master node. It tries to create a new licenses on the master * and if provided license(s) is VALID it is added to cluster metadata. * - * @return LicensesStatus indicating if the provided license(s) is VALID (accepted), INVALID (tampered license) or EXPIRED */ @Override public void registerLicenses(final PutLicenseRequestHolder requestHolder, final ActionListener listener) { @@ -282,12 +279,6 @@ public class LicensesService extends AbstractLifecycleComponent @Override protected void doClose() throws ElasticsearchException { logger.info("Closing LicensesService"); - /* - if (notificationScheduler.get() != null) { - notificationScheduler.get().cancel(true); - notificationScheduler.set(null); - } - */ clusterService.remove(this); if (registeredListeners != null) { @@ -390,7 +381,7 @@ public class LicensesService extends AbstractLifecycleComponent } LicensesMetaData currentMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE); - if (!hasLicenseForFeature(listenerHolder.feature, currentMetaData)) { + if (expiryDateForFeature(listenerHolder.feature, currentMetaData) == -1l) { // does not have any license so generate a trial license TrialLicenseOptions options = listenerHolder.trialLicenseOptions; if (options != null) { @@ -428,15 +419,15 @@ public class LicensesService extends AbstractLifecycleComponent return true; } - private boolean hasLicenseForFeature(String feature, LicensesMetaData currentLicensesMetaData) { + private long expiryDateForFeature(String feature, LicensesMetaData currentLicensesMetaData) { final Map effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData); ESLicense featureLicense; if ((featureLicense = effectiveLicenses.get(feature)) != null) { - if (featureLicense.expiryDate() > System.currentTimeMillis()) { - return true; - } + logger.info("effective license for "+ feature + " relative expiry: " + TimeValue.timeValueMillis(effectiveLicenses.get(feature).expiryDate() - System.currentTimeMillis())); + return featureLicense.expiryDate(); } - return false; + logger.info("no effective license for " + feature); + return -1l; } @@ -486,25 +477,6 @@ public class LicensesService extends AbstractLifecycleComponent logger.info("Reschedule licensing client notification job was rejected", ex); } } - /* - - LicensesMetaData currentLicensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE); - - // Change to debug - logger.info("calling notifyFeaturesIfNeeded from LicensingClientNotificationJob"); - - long nextScheduleFrequency; - if ((nextScheduleFrequency = notifyFeaturesAndScheduleNotification(currentLicensesMetaData)) == -1l) { - return; - } - - TimeValue updateFrequency = TimeValue.timeValueMillis(nextScheduleFrequency); - logger.trace("Scheduling next run for licensing client notification job in: {}", updateFrequency.toString()); - try { - threadPool.schedule(updateFrequency, executorName(), new SubmitReschedulingLicensingClientNotificationJob()); - } catch (EsRejectedExecutionException ex) { - logger.info("Reschedule licensing client notification job was rejected", ex); - }*/ } } @@ -519,13 +491,8 @@ public class LicensesService extends AbstractLifecycleComponent sb.append(listenerHolder.feature); sb.append(", "); - long expiryDate = -1l; - if (hasLicenseForFeature(listenerHolder.feature, currentLicensesMetaData)) { - final Map effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData); - final ESLicense license = effectiveLicenses.get(listenerHolder.feature); - expiryDate = license.expiryDate(); - - sb.append((license.signature() != null) ? "signed" : "trial"); + long expiryDate; + if ((expiryDate = expiryDateForFeature(listenerHolder.feature, currentLicensesMetaData)) != -1l) { sb.append(" license expiry: "); sb.append(expiryDate); sb.append(", "); @@ -616,17 +583,13 @@ public class LicensesService extends AbstractLifecycleComponent } private void enableFeatureIfNeeded() { - //logger.info("enabled flag: " + enabled.get()); if (enabled.compareAndSet(false, true)) { - //logger.info("calling onEnabled on listener"); listener.onEnabled(); } } private void disableFeatureIfNeeded() { - //logger.info("enabled flag: " + enabled.get()); if (enabled.compareAndSet(true, false)) { - //logger.info("calling onDisabled on listener"); listener.onDisabled(); } } @@ -638,10 +601,12 @@ public class LicensesService extends AbstractLifecycleComponent return new LicensesWrapper(licensesMetaData); } + private final LicensesMetaData oldState; private ImmutableSet signatures = ImmutableSet.of(); private ImmutableSet encodedTrialLicenses = ImmutableSet.of(); private LicensesWrapper(LicensesMetaData licensesMetaData) { + this.oldState = licensesMetaData; if (licensesMetaData != null) { this.signatures = ImmutableSet.copyOf(licensesMetaData.getSignatures()); this.encodedTrialLicenses = ImmutableSet.copyOf(licensesMetaData.getEncodedTrialLicenses()); @@ -679,17 +644,15 @@ public class LicensesService extends AbstractLifecycleComponent public void addSignedLicenses(ESLicenseManager licenseManager, Set newLicenses) { Set currentSignedLicenses = signedLicenses(licenseManager); - final ImmutableMap licenseMap = reduceAndMap(Sets.union(currentSignedLicenses, newLicenses)); - this.signatures = licenseManager.toSignatures(licenseMap.values()); + this.signatures = licenseManager.toSignatures(Sets.union(currentSignedLicenses, newLicenses)); } public void removeFeatures(ESLicenseManager licenseManager, Set featuresToDelete) { Set currentSignedLicenses = signedLicenses(licenseManager); - final ImmutableMap licenseMap = reduceAndMap(currentSignedLicenses); Set licensesToDelete = new HashSet<>(); - for (Map.Entry entry : licenseMap.entrySet()) { - if (featuresToDelete.contains(entry.getKey())) { - licensesToDelete.add(entry.getValue()); + for (ESLicense license : currentSignedLicenses) { + if (featuresToDelete.contains(license.feature())) { + licensesToDelete.add(license); } } Set reducedLicenses = Sets.difference(currentSignedLicenses, licensesToDelete); @@ -697,6 +660,11 @@ public class LicensesService extends AbstractLifecycleComponent } public LicensesMetaData get() { + if (oldState != null) { + if (oldState.getSignatures().equals(signatures) && oldState.getEncodedTrialLicenses().equals(encodedTrialLicenses)) { + return oldState; + } + } return new LicensesMetaData(signatures, encodedTrialLicenses); } } @@ -753,10 +721,6 @@ public class LicensesService extends AbstractLifecycleComponent //Should not be exposed; used by testing only public void clear() { - /*if (notificationScheduler.get() != null) { - notificationScheduler.get().cancel(true); - notificationScheduler.set(null); - }*/ registeredListeners.clear(); } } diff --git a/src/test/java/org/elasticsearch/license/plugin/LicensesPluginIntegrationTests.java b/src/test/java/org/elasticsearch/license/plugin/LicensesPluginIntegrationTests.java index 316242e2a63..c0f38398e48 100644 --- a/src/test/java/org/elasticsearch/license/plugin/LicensesPluginIntegrationTests.java +++ b/src/test/java/org/elasticsearch/license/plugin/LicensesPluginIntegrationTests.java @@ -6,12 +6,16 @@ package org.elasticsearch.license.plugin; import org.elasticsearch.common.base.Predicate; +import org.elasticsearch.common.collect.Lists; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.licensor.ESLicenseSigner; import org.elasticsearch.license.plugin.core.LicensesManagerService; +import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder; +import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; +import org.elasticsearch.license.plugin.core.LicensesStatus; import org.elasticsearch.test.InternalTestCluster; import org.junit.Test; @@ -24,7 +28,7 @@ import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST; import static org.hamcrest.CoreMatchers.equalTo; -@ClusterScope(scope = TEST, numDataNodes = 3, numClientNodes = 0) +@ClusterScope(scope = TEST, numDataNodes = 10, numClientNodes = 0) public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationTests { private final int trialLicenseDurationInSeconds = 5; @@ -39,82 +43,55 @@ public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationT } @Test - public void test() throws Exception { + public void test1() throws Exception { + logger.info(" --> trial license generated"); // managerService should report feature to be enabled on all data nodes - assertThat(awaitBusy(new Predicate() { - @Override - public boolean apply(Object o) { - for (LicensesManagerService managerService : licensesManagerServices()) { - if (!managerService.enabledFeatures().contains(TestPluginService.FEATURE_NAME)) { - return false; - } - } - return true; - } - }, 2, TimeUnit.SECONDS), equalTo(true)); - - + assertLicenseManagerEnabledFeatureFor(TestPluginService.FEATURE_NAME); // consumer plugin service should return enabled on all data nodes - /*assertThat(awaitBusy(new Predicate() { - @Override - public boolean apply(Object o) { - for (TestPluginService pluginService : consumerPluginServices()) { - if (!pluginService.enabled()) { - return false; - } - } - return true; - } - }, 2, TimeUnit.SECONDS), equalTo(true));*/ assertConsumerPluginEnableNotification(2); - + logger.info(" --> check trial license expiry notification"); // consumer plugin should notify onDisabled on all data nodes (expired trial license) - assertThat(awaitBusy(new Predicate() { - @Override - public boolean apply(Object o) { - for (TestPluginService pluginService : consumerPluginServices()) { - if(pluginService.enabled()) { - return false; - } + assertConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2); + assertLicenseManagerDisabledFeatureFor(TestPluginService.FEATURE_NAME); - } - return true; - } - }, trialLicenseDurationInSeconds * 2, TimeUnit.SECONDS), equalTo(true)); - - // consumer plugin should notify onEnabled on all data nodes (signed license) - /* + logger.info(" --> put signed license"); ESLicense license = generateSignedLicense(TestPluginService.FEATURE_NAME, TimeValue.timeValueSeconds(5)); final PutLicenseResponse putLicenseResponse = new PutLicenseRequestBuilder(client().admin().cluster()).setLicense(Lists.newArrayList(license)).get(); assertThat(putLicenseResponse.isAcknowledged(), equalTo(true)); assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID)); - logger.info(" --> put signed license"); + logger.info(" --> check signed license enabled notification"); + // consumer plugin should notify onEnabled on all data nodes (signed license) + assertConsumerPluginEnableNotification(2); + assertLicenseManagerEnabledFeatureFor(TestPluginService.FEATURE_NAME); + + logger.info(" --> check signed license expiry notification"); + // consumer plugin should notify onDisabled on all data nodes (expired signed license) + assertConsumerPluginDisableNotification(5); + assertLicenseManagerDisabledFeatureFor(TestPluginService.FEATURE_NAME); + } + + private void assertLicenseManagerEnabledFeatureFor(final String feature) throws InterruptedException { + assertLicenseManagerStatusFor(feature, true); + } + + private void assertLicenseManagerDisabledFeatureFor(final String feature) throws InterruptedException { + assertLicenseManagerStatusFor(feature, false); + } + + private void assertLicenseManagerStatusFor(final String feature, final boolean expectedEnabled) throws InterruptedException { assertThat(awaitBusy(new Predicate() { @Override public boolean apply(Object o) { - for (TestPluginService pluginService : consumerPluginServices()) { - if (!pluginService.enabled()) { + for (LicensesManagerService managerService : licensesManagerServices()) { + if (expectedEnabled != managerService.enabledFeatures().contains(feature)) { return false; } } return true; } }, 2, TimeUnit.SECONDS), equalTo(true)); - - assertThat(awaitBusy(new Predicate() { - @Override - public boolean apply(Object o) { - for (TestPluginService pluginService : consumerPluginServices()) { - if (pluginService.enabled()) { - return false; - } - } - return true; - } - }, 5, TimeUnit.SECONDS), equalTo(true)); - */ } private void assertConsumerPluginDisableNotification(int timeoutInSec) throws InterruptedException { @@ -142,7 +119,7 @@ public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationT final ESLicense licenseSpec = ESLicense.builder() .uid(UUID.randomUUID().toString()) .feature(feature) - .expiryDate(expiryDate.getMillis()) + .expiryDate(System.currentTimeMillis() + expiryDate.getMillis()) .issueDate(System.currentTimeMillis()) .type("subscription") .subscriptionType("gold")