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 dcbb17815df..d6b7edd8532 100644 --- a/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java +++ b/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java @@ -46,15 +46,19 @@ import java.util.concurrent.atomic.AtomicReference; import static org.elasticsearch.license.core.ESLicenses.reduceAndMap; /** - * Service responsible for managing {@link org.elasticsearch.license.plugin.core.LicensesMetaData} + * Service responsible for managing {@link LicensesMetaData} * Interfaces through which this is exposed are: * - LicensesManagerService - responsible for managing signed and one-time-trial licenses * - LicensesClientService - responsible for feature registration and notification to consumer plugin(s) + * *

* Registration Scheme: + *

* A consumer plugin (feature) is registered with {@link LicensesClientService#register(String, TrialLicenseOptions, LicensesClientService.Listener)} * This method can be called at any time during the life-cycle of the consumer plugin. - * + * If the feature can not be registered immediately, it is queued up and registered on the first clusterChanged event with + * no {@link GatewayService#STATE_NOT_RECOVERED_BLOCK} block + * Upon successful registration, the feature(s) are notified appropriately using the notification scheme * *

* Notification Scheme: @@ -69,7 +73,7 @@ import static org.elasticsearch.license.core.ESLicenses.reduceAndMap; *

* The {@link LicensingClientNotificationJob} calls {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} to schedule * another delayed {@link LicensingClientNotificationJob} as stated above. It is a no-op in case of a global block on - * {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK} + * {@link GatewayService#STATE_NOT_RECOVERED_BLOCK} *

* Upon successful registration of a new feature: * - {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} is called @@ -78,8 +82,8 @@ import static org.elasticsearch.license.core.ESLicenses.reduceAndMap; * - {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} is called if: * - new trial/signed license(s) are found in the cluster state meta data * - if new feature(s) are added to the registeredListener - * - if the previous cluster state had a global block on {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK} - * - no-op in case of global block on {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK} + * - if the previous cluster state had a global block on {@link GatewayService#STATE_NOT_RECOVERED_BLOCK} + * - no-op in case of global block on {@link GatewayService#STATE_NOT_RECOVERED_BLOCK} */ @Singleton public class LicensesService extends AbstractLifecycleComponent implements ClusterStateListener, LicensesManagerService, LicensesClientService { @@ -380,10 +384,10 @@ public class LicensesService extends AbstractLifecycleComponent } /** - * When there is no global block on {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK}: + * When there is no global block on {@link GatewayService#STATE_NOT_RECOVERED_BLOCK}: * - tries to register any {@link #pendingListeners} by calling {@link #registeredListeners} * - if any {@link #pendingListeners} are registered successfully or if previous cluster state had a block on - * {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK}, calls + * {@link GatewayService#STATE_NOT_RECOVERED_BLOCK}, calls * {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} * - else calls {@link #notifyFeaturesAndScheduleNotificationIfNeeded(LicensesMetaData)} */ @@ -435,6 +439,7 @@ public class LicensesService extends AbstractLifecycleComponent return; } notifyFeaturesAndScheduleNotification(currentLicensesMetaData); + logLicenseMetaDataStats("Setting last observed metaData", currentLicensesMetaData); lastObservedLicensesState.set(currentLicensesMetaData); } @@ -493,7 +498,6 @@ public class LicensesService extends AbstractLifecycleComponent } if (logger.isDebugEnabled()) { - logLicenseMetaDataStats("Setting last observed metaData", currentLicensesMetaData); if (nextScheduleFrequency == -1l) { logger.debug("no need to schedule next notification"); } else { @@ -506,10 +510,22 @@ public class LicensesService extends AbstractLifecycleComponent } private void logLicenseMetaDataStats(String prefix, LicensesMetaData licensesMetaData) { - if (licensesMetaData != null) { - logger.debug(prefix + " LicensesMetaData: signedLicenses: " + licensesMetaData.getSignatures().size() + " trialLicenses: " + licensesMetaData.getEncodedTrialLicenses().size()); - } else { - logger.debug(prefix + " LicensesMetaData: signedLicenses: 0 trialLicenses: 0"); + if (logger.isDebugEnabled()) { + if (licensesMetaData != null) { + StringBuilder signedFeatures = new StringBuilder(); + for (ESLicense license : licenseManager.fromSignatures(licensesMetaData.getSignatures())) { + signedFeatures.append(license.feature()); + signedFeatures.append(", "); + } + StringBuilder trialFeatures = new StringBuilder(); + for (ESLicense license : TrialLicenseUtils.fromEncodedTrialLicenses(licensesMetaData.getEncodedTrialLicenses())) { + trialFeatures.append(license.feature()); + trialFeatures.append(", "); + } + logger.debug(prefix + " LicensesMetaData: signedLicenses: [" + signedFeatures.toString() + "] trialLicenses: [" + trialFeatures.toString() + "]"); + } else { + logger.debug(prefix + " LicensesMetaData: signedLicenses: [] trialLicenses: []"); + } } } @@ -537,7 +553,7 @@ public class LicensesService extends AbstractLifecycleComponent * * @param listenerHolder of the feature to register * @return true if registration has been completed, false otherwise (if masterNode is not available & trail license spec is provided) - * or if there is a global block on {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK} + * or if there is a global block on {@link GatewayService#STATE_NOT_RECOVERED_BLOCK} */ private boolean registerListener(final ListenerHolder listenerHolder) { logger.debug("Registering listener for " + listenerHolder.feature); @@ -644,7 +660,7 @@ public class LicensesService extends AbstractLifecycleComponent /** * Job for notifying on expired license(s) to registered feature(s) - * In case of a global block on {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK}, + * In case of a global block on {@link GatewayService#STATE_NOT_RECOVERED_BLOCK}, * the notification is not run, instead the feature(s) would be notified on the next * {@link #clusterChanged(org.elasticsearch.cluster.ClusterChangedEvent)} with no global block */ @@ -717,12 +733,14 @@ public class LicensesService extends AbstractLifecycleComponent private void enableFeatureIfNeeded() { if (enabled.compareAndSet(false, true)) { + logger.debug("feature: " + feature + " calling onEnabled"); listener.onEnabled(); } } private void disableFeatureIfNeeded() { if (enabled.compareAndSet(true, false)) { + logger.debug("feature: " + feature + " calling onDisabled"); listener.onDisabled(); } } @@ -733,7 +751,7 @@ public class LicensesService extends AbstractLifecycleComponent } /** - * Thin wrapper to work with {@link org.elasticsearch.license.plugin.core.LicensesMetaData} + * Thin wrapper to work with {@link LicensesMetaData} * Never mutates the wrapped metaData */ private static class LicensesWrapper { diff --git a/src/test/java/org/elasticsearch/license/plugin/AbstractLicensesIntegrationTests.java b/src/test/java/org/elasticsearch/license/plugin/AbstractLicensesIntegrationTests.java index 89792500ab8..529887ad467 100644 --- a/src/test/java/org/elasticsearch/license/plugin/AbstractLicensesIntegrationTests.java +++ b/src/test/java/org/elasticsearch/license/plugin/AbstractLicensesIntegrationTests.java @@ -11,16 +11,20 @@ import org.elasticsearch.cluster.ProcessedClusterStateUpdateTask; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.Nullable; 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.action.put.PutLicenseRequestBuilder; +import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService; import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationPluginService; import org.elasticsearch.license.plugin.consumer.TestPluginServiceBase; import org.elasticsearch.license.plugin.core.LicensesManagerService; import org.elasticsearch.license.plugin.core.LicensesMetaData; +import org.elasticsearch.license.plugin.core.LicensesStatus; import org.elasticsearch.test.ElasticsearchIntegrationTest; import org.elasticsearch.test.InternalTestCluster; @@ -92,6 +96,14 @@ public abstract class AbstractLicensesIntegrationTests extends ElasticsearchInte return signer.sign(licenseSpec); } + protected void putLicense(String feature, TimeValue expiryDuration) throws Exception { + ESLicense license1 = generateSignedLicense(feature, expiryDuration); + final PutLicenseResponse putLicenseResponse = new PutLicenseRequestBuilder(client().admin().cluster()).setLicense(Lists.newArrayList(license1)).get(); + assertThat(putLicenseResponse.isAcknowledged(), equalTo(true)); + assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID)); + } + + protected void assertLicenseManagerEnabledFeatureFor(final String feature) throws InterruptedException { assertLicenseManagerStatusFor(feature, true); } @@ -101,7 +113,7 @@ public abstract class AbstractLicensesIntegrationTests extends ElasticsearchInte } protected void assertLicenseManagerStatusFor(final String feature, final boolean expectedEnabled) throws InterruptedException { - assertThat(awaitBusy(new Predicate() { + assertThat("LicenseManager for feature " + feature + " should have enabled status of " + expectedEnabled, awaitBusy(new Predicate() { @Override public boolean apply(Object o) { for (LicensesManagerService managerService : licensesManagerServices()) { @@ -133,17 +145,17 @@ public abstract class AbstractLicensesIntegrationTests extends ElasticsearchInte protected void assertLazyConsumerPluginNotification(final boolean expectedEnabled, int timeoutInSec) throws InterruptedException { final List consumerPluginServices = consumerLazyPluginServices(); assertThat("At least one instance has to be present", consumerPluginServices.size(), greaterThan(0)); - assertConsumerPluginNotification(consumerPluginServices, expectedEnabled, timeoutInSec); + assertConsumerPluginNotification("LazyConsumer should have license status of: " + expectedEnabled, consumerPluginServices, expectedEnabled, timeoutInSec); } protected void assertEagerConsumerPluginNotification(final boolean expectedEnabled, int timeoutInSec) throws InterruptedException { final List consumerPluginServices = consumerEagerPluginServices(); assertThat("At least one instance has to be present", consumerPluginServices.size(), greaterThan(0)); - assertConsumerPluginNotification(consumerPluginServices, expectedEnabled, timeoutInSec); + assertConsumerPluginNotification("EagerConsumer should have license status of: " + expectedEnabled, consumerPluginServices, expectedEnabled, timeoutInSec); } - private void assertConsumerPluginNotification(final Iterable consumerPluginServices, final boolean expectedEnabled, int timeoutInSec) throws InterruptedException { - assertThat(awaitBusy(new Predicate() { + private void assertConsumerPluginNotification(String msg, final Iterable consumerPluginServices, final boolean expectedEnabled, int timeoutInSec) throws InterruptedException { + assertThat(msg, awaitBusy(new Predicate() { @Override public boolean apply(Object o) { for (TestPluginServiceBase pluginService : consumerPluginServices) { diff --git a/src/test/java/org/elasticsearch/license/plugin/LicensesPluginIntegrationTests.java b/src/test/java/org/elasticsearch/license/plugin/LicensesPluginIntegrationTests.java index 5b5c0fa0a56..f7c4ae04e0b 100644 --- a/src/test/java/org/elasticsearch/license/plugin/LicensesPluginIntegrationTests.java +++ b/src/test/java/org/elasticsearch/license/plugin/LicensesPluginIntegrationTests.java @@ -6,19 +6,14 @@ 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.gateway.GatewayService; -import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin; import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService; -import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder; -import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationConsumerPlugin; import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationPluginService; -import org.elasticsearch.license.plugin.core.LicensesStatus; import org.junit.After; import org.junit.Test; @@ -31,7 +26,7 @@ public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationT private final boolean useEagerLicenseRegistrationPlugin = randomBoolean(); - private final int trialLicenseDurationInSeconds = 2; + private final int trialLicenseDurationInSeconds = 5; protected Settings nodeSettings(int nodeOrdinal) { return ImmutableSettings.settingsBuilder() @@ -69,10 +64,7 @@ public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationT assertLicenseManagerDisabledFeatureFor(getCurrentFeatureName()); logger.info(" --> put signed license"); - ESLicense license = generateSignedLicense(getCurrentFeatureName(), TimeValue.timeValueSeconds(trialLicenseDurationInSeconds)); - final PutLicenseResponse putLicenseResponse = new PutLicenseRequestBuilder(client().admin().cluster()).setLicense(Lists.newArrayList(license)).get(); - assertThat(putLicenseResponse.isAcknowledged(), equalTo(true)); - assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID)); + putLicense(getCurrentFeatureName(), TimeValue.timeValueSeconds(trialLicenseDurationInSeconds)); logger.info(" --> check signed license enabled notification"); // consumer plugin should notify onEnabled on all data nodes (signed license) @@ -111,10 +103,7 @@ public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationT assertConsumerPluginEnabledNotification(1); logger.info(" --> put signed license while trial license is in effect"); - ESLicense license = generateSignedLicense(getCurrentFeatureName(), TimeValue.timeValueSeconds(trialLicenseDurationInSeconds * 2)); - final PutLicenseResponse putLicenseResponse = new PutLicenseRequestBuilder(client().admin().cluster()).setLicense(Lists.newArrayList(license)).get(); - assertThat(putLicenseResponse.isAcknowledged(), equalTo(true)); - assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID)); + putLicense(getCurrentFeatureName(), TimeValue.timeValueSeconds(trialLicenseDurationInSeconds * 2)); logger.info(" --> check signed license enabled notification"); // consumer plugin should notify onEnabled on all data nodes (signed license) diff --git a/src/test/java/org/elasticsearch/license/plugin/LicensesPluginsIntegrationTests.java b/src/test/java/org/elasticsearch/license/plugin/LicensesPluginsIntegrationTests.java index 303ba0dc127..dc038a33b1e 100644 --- a/src/test/java/org/elasticsearch/license/plugin/LicensesPluginsIntegrationTests.java +++ b/src/test/java/org/elasticsearch/license/plugin/LicensesPluginsIntegrationTests.java @@ -5,38 +5,30 @@ */ package org.elasticsearch.license.plugin; -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.plugin.action.put.PutLicenseRequestBuilder; -import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin; import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService; import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationConsumerPlugin; import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationPluginService; -import org.elasticsearch.license.plugin.core.LicensesStatus; import org.junit.After; import org.junit.Test; 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 = 0, numClientNodes = 0) public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegrationTests { - private final int trialLicenseDurationInSeconds = 2; - private final String FEATURE_NAME_1 = EagerLicenseRegistrationPluginService.FEATURE_NAME; private final String FEATURE_NAME_2 = LazyLicenseRegistrationPluginService.FEATURE_NAME; protected Settings nodeSettings(int nodeOrdinal) { return ImmutableSettings.settingsBuilder() .put(super.nodeSettings(nodeOrdinal)) - .put(EagerLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds) - .put(LazyLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds) + .put(EagerLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", 2) + .put(LazyLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", 2) .putArray("plugin.types", LicensePlugin.class.getName(), EagerLicenseRegistrationConsumerPlugin.class.getName(), LazyLicenseRegistrationConsumerPlugin.class.getName()) .build(); } @@ -59,10 +51,10 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration @Test public void testWithNoTrialLicense() throws Exception { int nNodes = randomIntBetween(2, 10); - String[] nodes = startNodesWithConsumerPlugins(nNodes, -1, -1); + startNodesWithConsumerPlugins(nNodes, -1, -1); - assertEagerConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2); - assertLazyConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2); + assertEagerConsumerPluginDisableNotification(1); + assertLazyConsumerPluginDisableNotification(1); assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1); assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2); } @@ -71,7 +63,7 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration public void testOneTrialAndNonTrialConsumer() throws Exception { int nNodes = randomIntBetween(2, 10); int consumer2TrialLicenseDuration = 2; - String[] nodes = startNodesWithConsumerPlugins(nNodes, -1, consumer2TrialLicenseDuration); + startNodesWithConsumerPlugins(nNodes, -1, consumer2TrialLicenseDuration); logger.info(" --> trial license generated for " + FEATURE_NAME_2 + " no trial license for " + FEATURE_NAME_1); // managerService should report feature to be enabled on all data nodes @@ -82,16 +74,11 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration assertLazyConsumerPluginEnableNotification(1); logger.info(" --> put signed license for " + FEATURE_NAME_1); - ESLicense license1 = generateSignedLicense(FEATURE_NAME_1, TimeValue.timeValueSeconds(consumer2TrialLicenseDuration)); - final PutLicenseResponse putLicenseResponse = new PutLicenseRequestBuilder(client().admin().cluster()).setLicense(Lists.newArrayList(license1)).get(); - assertThat(putLicenseResponse.isAcknowledged(), equalTo(true)); - assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID)); + putLicense(FEATURE_NAME_1, TimeValue.timeValueSeconds(consumer2TrialLicenseDuration)); logger.info(" --> check that both " + FEATURE_NAME_1 + " and " + FEATURE_NAME_2 + " are enabled"); assertEagerConsumerPluginEnableNotification(1); - assertLazyConsumerPluginEnableNotification(1); assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_1); - assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_2); logger.info(" --> check signed license expiry notification"); // consumer plugin should notify onDisabled on all data nodes (expired signed license) @@ -105,7 +92,10 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration public void testMultipleConsumerPlugins() throws Exception { int nNodes = randomIntBetween(2, 10); - String[] nodes = startNodesWithConsumerPlugins(nNodes, trialLicenseDurationInSeconds, trialLicenseDurationInSeconds); + int consumer1TrialLicenseExpiry = 2; + int consumer2TrialLicenseExpiry = 2; + startNodesWithConsumerPlugins(nNodes, consumer1TrialLicenseExpiry, consumer2TrialLicenseExpiry); + logger.info(" --> trial license generated"); // managerService should report feature to be enabled on all data nodes assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_1); @@ -116,14 +106,14 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration logger.info(" --> check trial license expiry notification"); // consumer plugin should notify onDisabled on all data nodes (expired trial license) - assertEagerConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2); - assertLazyConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2); + assertEagerConsumerPluginDisableNotification(consumer1TrialLicenseExpiry * 2); + assertLazyConsumerPluginDisableNotification(consumer2TrialLicenseExpiry * 2); assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1); assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2); logger.info(" --> put signed license"); - putLicense(FEATURE_NAME_1, TimeValue.timeValueSeconds(trialLicenseDurationInSeconds)); - putLicense(FEATURE_NAME_2, TimeValue.timeValueSeconds(trialLicenseDurationInSeconds)); + putLicense(FEATURE_NAME_1, TimeValue.timeValueSeconds(consumer1TrialLicenseExpiry)); + putLicense(FEATURE_NAME_2, TimeValue.timeValueSeconds(consumer2TrialLicenseExpiry)); logger.info(" --> check signed license enabled notification"); // consumer plugin should notify onEnabled on all data nodes (signed license) @@ -134,8 +124,8 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration logger.info(" --> check signed license expiry notification"); // consumer plugin should notify onDisabled on all data nodes (expired signed license) - assertEagerConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2); - assertLazyConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2); + assertEagerConsumerPluginDisableNotification(consumer1TrialLicenseExpiry * 2); + assertLazyConsumerPluginDisableNotification(consumer2TrialLicenseExpiry * 2); assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1); assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2); } @@ -143,27 +133,27 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration @Test public void testRandomFeatureLicensesActions() throws Exception { int nNodes = randomIntBetween(2, 10); - int trialLicenseDuration1 = rarely() ? -1 : randomIntBetween(1, 2); - int trialLicenseDuration2 = rarely() ? -1 : randomIntBetween(1, 2); + int trialLicenseDuration1 = rarely() ? -1 : randomIntBetween(2, 3); + int trialLicenseDuration2 = rarely() ? -1 : randomIntBetween(2, 3); - String[] nodes = startNodesWithConsumerPlugins(nNodes, trialLicenseDuration1, trialLicenseDuration2); + startNodesWithConsumerPlugins(nNodes, trialLicenseDuration1, trialLicenseDuration2); if (trialLicenseDuration1 != -1) { - assertEagerConsumerPluginEnableNotification(1); + assertEagerConsumerPluginEnableNotification(trialLicenseDuration1); assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_1); } else { - assertEagerConsumerPluginDisableNotification(1); + assertEagerConsumerPluginDisableNotification(3 * 2); assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1); - putLicense(FEATURE_NAME_1, TimeValue.timeValueMillis(300 * 2)); + putLicense(FEATURE_NAME_1, TimeValue.timeValueMillis(500 * 2)); } if (trialLicenseDuration2 != -1) { - assertLazyConsumerPluginEnableNotification(1); + assertLazyConsumerPluginEnableNotification(trialLicenseDuration2); assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_2); } else { - assertLazyConsumerPluginDisableNotification(1); + assertLazyConsumerPluginDisableNotification(3 * 2); assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2); - putLicense(FEATURE_NAME_2, TimeValue.timeValueMillis(300 * 2)); + putLicense(FEATURE_NAME_2, TimeValue.timeValueMillis(500 * 2)); } logger.info(" --> check license enabled notification"); @@ -174,25 +164,16 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration logger.info(" --> check license expiry notification"); // consumer plugin should notify onDisabled on all data nodes (expired signed license) - assertEagerConsumerPluginDisableNotification(2 * 2); - assertLazyConsumerPluginDisableNotification(2 * 2); + assertEagerConsumerPluginDisableNotification(3 * 2); + assertLazyConsumerPluginDisableNotification(3 * 2); assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1); assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2); } - private String[] startNodesWithConsumerPlugins(int nNodes, int consumer1TrialLicenseDuration, int consumer2TrialLicenseDuration) { - String[] nodes = new String[nNodes]; + private void startNodesWithConsumerPlugins(int nNodes, int consumer1TrialLicenseDuration, int consumer2TrialLicenseDuration) { for (int i = 0; i < nNodes; i++) { - nodes[i] = internalCluster().startNode(nodeSettingsWithConsumerPlugin(consumer1TrialLicenseDuration, consumer2TrialLicenseDuration)); + internalCluster().startNode(nodeSettingsWithConsumerPlugin(consumer1TrialLicenseDuration, consumer2TrialLicenseDuration)); } - return nodes; - } - - private void putLicense(String feature, TimeValue expiryDuration) throws Exception { - ESLicense license1 = generateSignedLicense(feature, expiryDuration); - final PutLicenseResponse putLicenseResponse = new PutLicenseRequestBuilder(client().admin().cluster()).setLicense(Lists.newArrayList(license1)).get(); - assertThat(putLicenseResponse.isAcknowledged(), equalTo(true)); - assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID)); } } diff --git a/src/test/java/org/elasticsearch/license/plugin/LicensesServiceTests.java b/src/test/java/org/elasticsearch/license/plugin/LicensesServiceTests.java index 9ea15eeb0ef..c0cd707bd08 100644 --- a/src/test/java/org/elasticsearch/license/plugin/LicensesServiceTests.java +++ b/src/test/java/org/elasticsearch/license/plugin/LicensesServiceTests.java @@ -109,8 +109,9 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests { List actions = new ArrayList<>(); final TimeValue expiryDuration = TimeValue.timeValueSeconds(2); - actions.add(registerWithTrialLicense(clientService, clientListener, "feature1", expiryDuration)); - actions.add(assertExpiryAction("trial", expiryDuration)); + String feature1 = "feature1"; + actions.add(registerWithTrialLicense(clientService, clientListener, feature1, expiryDuration)); + actions.add(assertExpiryAction(feature1, "trial", expiryDuration)); assertClientListenerNotificationCount(clientListener, actions); } @@ -126,14 +127,16 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests { List secondClientActions = new ArrayList<>(); final TimeValue firstExpiryDuration = TimeValue.timeValueSeconds(2); - firstClientActions.add(registerWithoutTrialLicense(licensesService, clientListener1, "feature1")); - firstClientActions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, "feature1", firstExpiryDuration)); - firstClientActions.add(assertExpiryAction("signed", firstExpiryDuration)); + String feature1 = "feature1"; + firstClientActions.add(registerWithoutTrialLicense(licensesService, clientListener1, feature1)); + firstClientActions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, feature1, firstExpiryDuration)); + firstClientActions.add(assertExpiryAction(feature1, "signed", firstExpiryDuration)); final TimeValue secondExpiryDuration = TimeValue.timeValueSeconds(1); - secondClientActions.add(registerWithoutTrialLicense(licensesService, clientListener2, "feature2")); - secondClientActions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, "feature2", secondExpiryDuration)); - secondClientActions.add(assertExpiryAction("signed", secondExpiryDuration)); + String feature2 = "feature2"; + secondClientActions.add(registerWithoutTrialLicense(licensesService, clientListener2, feature2)); + secondClientActions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, feature2, secondExpiryDuration)); + secondClientActions.add(assertExpiryAction(feature2, "signed", secondExpiryDuration)); if (randomBoolean()) { assertClientListenerNotificationCount(clientListener1, firstClientActions); @@ -156,13 +159,15 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests { List secondClientActions = new ArrayList<>(); final TimeValue firstExpiryDuration = TimeValue.timeValueSeconds(2); - firstClientActions.add(registerWithoutTrialLicense(licensesService, clientListener1, "feature1")); - firstClientActions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, "feature1", firstExpiryDuration)); - firstClientActions.add(assertExpiryAction("signed", firstExpiryDuration)); + String feature1 = "feature1"; + firstClientActions.add(registerWithoutTrialLicense(licensesService, clientListener1, feature1)); + firstClientActions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, feature1, firstExpiryDuration)); + firstClientActions.add(assertExpiryAction(feature1, "signed", firstExpiryDuration)); final TimeValue secondExpiryDuration = TimeValue.timeValueSeconds(1); - secondClientActions.add(registerWithTrialLicense(licensesService, clientListener2, "feature2", secondExpiryDuration)); - secondClientActions.add(assertExpiryAction("trial", secondExpiryDuration)); + String feature2 = "feature2"; + secondClientActions.add(registerWithTrialLicense(licensesService, clientListener2, feature2, secondExpiryDuration)); + secondClientActions.add(assertExpiryAction(feature2, "trial", secondExpiryDuration)); if (randomBoolean()) { assertClientListenerNotificationCount(clientListener1, firstClientActions); @@ -184,12 +189,14 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests { List secondClientActions = new ArrayList<>(); TimeValue firstExpiryDuration = TimeValue.timeValueSeconds(1); - firstClientActions.add(registerWithTrialLicense(licensesService, clientListener1, "feature1", firstExpiryDuration)); - firstClientActions.add(assertExpiryAction("trial", firstExpiryDuration)); + String feature1 = "feature1"; + firstClientActions.add(registerWithTrialLicense(licensesService, clientListener1, feature1, firstExpiryDuration)); + firstClientActions.add(assertExpiryAction(feature1, "trial", firstExpiryDuration)); TimeValue secondExpiryDuration = TimeValue.timeValueSeconds(2); - secondClientActions.add(registerWithTrialLicense(licensesService, clientListener2, "feature2", secondExpiryDuration)); - secondClientActions.add(assertExpiryAction("trial", secondExpiryDuration)); + String feature2 = "feature2"; + secondClientActions.add(registerWithTrialLicense(licensesService, clientListener2, feature2, secondExpiryDuration)); + secondClientActions.add(assertExpiryAction(feature2, "trial", secondExpiryDuration)); if (randomBoolean()) { assertClientListenerNotificationCount(clientListener1, firstClientActions); @@ -218,8 +225,9 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests { List actions = new ArrayList<>(); TimeValue expiryDuration = TimeValue.timeValueSeconds(2); - actions.add(registerWithTrialLicense(clientService, clientListener, "feature1", expiryDuration)); - actions.add(assertExpiryAction("trial", expiryDuration)); + String feature = "feature1"; + actions.add(registerWithTrialLicense(clientService, clientListener, feature, expiryDuration)); + actions.add(assertExpiryAction(feature, "trial", expiryDuration)); assertClientListenerNotificationCount(clientListener, actions); } @@ -231,19 +239,19 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests { Map> clientListenersWithActions = new HashMap<>(); TimeValue expiryDuration = TimeValue.timeValueSeconds(0); - for (int i = 0; i < randomIntBetween(10, 20); i++) { + for (int i = 0; i < randomIntBetween(5, 10); i++) { final TestTrackingClientListener clientListener = new TestTrackingClientListener(); - String feature = randomRealisticUnicodeOfCodepointLengthBetween(2, 10); - expiryDuration = TimeValue.timeValueMillis(randomIntBetween(1, 5) * 100l + expiryDuration.millis()); + String feature = "feature_" + String.valueOf(i); + expiryDuration = TimeValue.timeValueMillis(randomIntBetween(1, 3) * 1000l + expiryDuration.millis()); List actions = new ArrayList<>(); if (randomBoolean()) { actions.add(registerWithTrialLicense(licensesService, clientListener, feature, expiryDuration)); - actions.add(assertExpiryAction("trial", expiryDuration)); + actions.add(assertExpiryAction(feature, "trial", expiryDuration)); } else { actions.add(registerWithoutTrialLicense(licensesService, clientListener, feature)); actions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, feature, expiryDuration)); - actions.add(assertExpiryAction("signed", expiryDuration)); + actions.add(assertExpiryAction(feature, "signed", expiryDuration)); } clientListenersWithActions.put(clientListener, actions); } @@ -274,8 +282,9 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests { } }); try { - assertThat(latch.await(100, TimeUnit.MILLISECONDS), equalTo(true)); + latch.await(); } catch (InterruptedException e) { + fail(e.getMessage()); } assertThat(success.get(), equalTo(true)); } @@ -320,51 +329,49 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests { }, 0, 1, "should trigger onEnable for " + feature + " once [trial license]"); } - private Action assertExpiryAction(String licenseType, TimeValue expiryDuration) { + private Action assertExpiryAction(String feature, String licenseType, TimeValue expiryDuration) { return new Action(new Runnable() { @Override public void run() {} }, 1, 0, TimeValue.timeValueMillis(expiryDuration.getMillis() * 2), - "should trigger onDisable once [" + licenseType + " license expiry]"); + "should trigger onDisable for " + feature + " once [" + licenseType + " license expiry]"); } private void assertClientListenerNotificationCount(final TestTrackingClientListener clientListener, List actions) throws Exception { - assertThat(clientListener.onDisabledCount.get(), equalTo(0)); - assertThat(clientListener.onEnabledCount.get(), equalTo(0)); - for (final Action action : actions) { - final AtomicBoolean actionPerformed = new AtomicBoolean(false); - assertThat(action.msg, awaitBusy(new Predicate() { - AtomicInteger previousOnEnabledCount = new AtomicInteger(0); - AtomicInteger previousOnDisabledCount = new AtomicInteger(0); + final CountDownLatch enableLatch = new CountDownLatch(action.expectedEnabledCount); + final CountDownLatch disableLatch = new CountDownLatch(action.expectedDisabledCount); - @Override - public boolean apply(Object o) { - if (actionPerformed.compareAndSet(false, true)) { - previousOnEnabledCount.set(clientListener.onEnabledCount.get()); - previousOnDisabledCount.set(clientListener.onDisabledCount.get()); - action.run(); - } + clientListener.latch(enableLatch, disableLatch); + action.run(); + + if (action.expectedEnabledCount > 0) { + assertThat(action.msg, enableLatch.await(action.timeout.getMillis() * 2, TimeUnit.MILLISECONDS), equalTo(true)); + } else if (action.expectedDisabledCount > 0) { + assertThat(action.msg, disableLatch.await(action.timeout.getMillis() * 2, TimeUnit.MILLISECONDS), equalTo(true)); + + } - return ((clientListener.onEnabledCount.get() - previousOnEnabledCount.get()) == action.expectedEnabledCount - && (clientListener.onDisabledCount.get() - previousOnDisabledCount.get()) == action.expectedDisabledCount); - } - }, action.timeout.getMillis(), TimeUnit.MILLISECONDS), equalTo(true)); } } private class TestTrackingClientListener implements LicensesClientService.Listener { - AtomicInteger onEnabledCount = new AtomicInteger(0); - AtomicInteger onDisabledCount = new AtomicInteger(0); + CountDownLatch enableLatch; + CountDownLatch disableLatch; + + public synchronized void latch(CountDownLatch enableLatch, CountDownLatch disableLatch) { + this.enableLatch = enableLatch; + this.disableLatch = disableLatch; + } @Override public void onEnabled() { - onEnabledCount.incrementAndGet(); + this.enableLatch.countDown(); } @Override public void onDisabled() { - onDisabledCount.incrementAndGet(); + this.disableLatch.countDown(); } }