- Fix test concurrency bugs

- Minor test fixes
- LicensesService documentation

Original commit: elastic/x-pack-elasticsearch@ab98ba8a2d
This commit is contained in:
Areek Zillur 2014-11-04 22:54:27 -05:00
parent 93944f27f2
commit 3aec0fd1f0
5 changed files with 139 additions and 132 deletions

View File

@ -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)
*
* <p/>
* Registration Scheme:
* <p/>
* 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
*
* <p/>
* Notification Scheme:
@ -69,7 +73,7 @@ import static org.elasticsearch.license.core.ESLicenses.reduceAndMap;
* <p/>
* 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}
* <p/>
* 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<LicensesService> implements ClusterStateListener, LicensesManagerService, LicensesClientService {
@ -380,10 +384,10 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
}
/**
* 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<LicensesService>
return;
}
notifyFeaturesAndScheduleNotification(currentLicensesMetaData);
logLicenseMetaDataStats("Setting last observed metaData", currentLicensesMetaData);
lastObservedLicensesState.set(currentLicensesMetaData);
}
@ -493,7 +498,6 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
}
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<LicensesService>
}
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<LicensesService>
*
* @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<LicensesService>
/**
* 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<LicensesService>
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<LicensesService>
}
/**
* 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 {

View File

@ -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<Object>() {
assertThat("LicenseManager for feature " + feature + " should have enabled status of " + expectedEnabled, awaitBusy(new Predicate<Object>() {
@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<TestPluginServiceBase> 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<TestPluginServiceBase> 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<TestPluginServiceBase> consumerPluginServices, final boolean expectedEnabled, int timeoutInSec) throws InterruptedException {
assertThat(awaitBusy(new Predicate<Object>() {
private void assertConsumerPluginNotification(String msg, final Iterable<TestPluginServiceBase> consumerPluginServices, final boolean expectedEnabled, int timeoutInSec) throws InterruptedException {
assertThat(msg, awaitBusy(new Predicate<Object>() {
@Override
public boolean apply(Object o) {
for (TestPluginServiceBase pluginService : consumerPluginServices) {

View File

@ -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)

View File

@ -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));
}
}

View File

@ -109,8 +109,9 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
List<Action> 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<Action> 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<Action> 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<Action> 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<Action> 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<TestTrackingClientListener, List<Action>> 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<Action> 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<Action> 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<Object>() {
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();
}
}