LicensesService:

- notification management
  - dont notify when NOT_RECOVERED_BLOCK is on
  - clean up & fixes
 - improve tests

Original commit: elastic/x-pack-elasticsearch@907af6d308
This commit is contained in:
Areek Zillur 2014-10-29 23:14:17 -04:00
parent d1b39f2c8e
commit 50fb5250ee
5 changed files with 194 additions and 231 deletions

View File

@ -26,8 +26,6 @@ public interface LicensesManagerService {
public void unregisterLicenses(final DeleteLicenseRequestHolder requestHolder, final ActionListener<ClusterStateUpdateResponse> listener);
public LicensesStatus checkLicenses(Set<ESLicense> licenses);
public Set<String> enabledFeatures();
public List<ESLicense> getLicenses();

View File

@ -37,6 +37,7 @@ 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;
@ -50,7 +51,6 @@ import static org.elasticsearch.license.core.ESLicenses.reduceAndMap;
* - LicensesClientService - allow interested plugins (features) to register to licensing notifications
* <p/>
* TODO: documentation
* TODO: figure out when to check GatewayService.STATE_NOT_RECOVERED_BLOCK
*/
@Singleton
public class LicensesService extends AbstractLifecycleComponent<LicensesService> implements ClusterStateListener, LicensesManagerService, LicensesClientService {
@ -65,9 +65,11 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
private final TransportService transportService;
private List<ListenerHolder> registeredListeners = new CopyOnWriteArrayList<>();
private final List<ListenerHolder> registeredListeners = new CopyOnWriteArrayList<>();
private Queue<ListenerHolder> pendingRegistrations = new ConcurrentLinkedQueue<>();
private final Queue<ListenerHolder> pendingListeners = new ConcurrentLinkedQueue<>();
private final Queue<ScheduledFuture> scheduledNotifications = new ConcurrentLinkedQueue<>();
private final AtomicReference<LicensesMetaData> lastObservedLicensesState;
@ -175,32 +177,12 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
});
}
@Override
public LicensesStatus checkLicenses(Set<ESLicense> licenses) {
final ImmutableMap<String, ESLicense> map = reduceAndMap(licenses);
return checkLicenses(map);
}
private LicensesStatus checkLicenses(Map<String, ESLicense> licenseMap) {
LicensesStatus status = LicensesStatus.VALID;
try {
licenseManager.verifyLicenses(licenseMap);
} catch (ExpiredLicenseException e) {
status = LicensesStatus.EXPIRED;
} catch (InvalidLicenseException e) {
status = LicensesStatus.INVALID;
}
return status;
}
@Override
public Set<String> enabledFeatures() {
Set<String> enabledFeatures = Sets.newHashSet();
if (registeredListeners != null) {
for (ListenerHolder holder : registeredListeners) {
if (holder.enabled.get()) {
enabledFeatures.add(holder.feature);
}
for (ListenerHolder holder : registeredListeners) {
if (holder.enabled.get()) {
enabledFeatures.add(holder.feature);
}
}
return enabledFeatures;
@ -240,6 +222,22 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
return Collections.emptyList();
}
private LicensesStatus checkLicenses(Set<ESLicense> licenses) {
final ImmutableMap<String, ESLicense> map = reduceAndMap(licenses);
return checkLicenses(map);
}
private LicensesStatus checkLicenses(Map<String, ESLicense> licenseMap) {
LicensesStatus status = LicensesStatus.VALID;
try {
licenseManager.verifyLicenses(licenseMap);
} catch (ExpiredLicenseException e) {
status = LicensesStatus.EXPIRED;
} catch (InvalidLicenseException e) {
status = LicensesStatus.INVALID;
}
return status;
}
private void registerTrialLicense(final RegisterTrialLicenseRequest request) {
clusterService.submitStateUpdateTask("register trial license []", new ProcessedClusterStateUpdateTask() {
@ -310,7 +308,7 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
@Override
protected void doStop() throws ElasticsearchException {
// Should notificationScheduler be cancelled on stop as well?
// Should scheduledNotifications be cancelled on stop as well?
}
@Override
@ -318,15 +316,21 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
logger.info("Closing LicensesService");
clusterService.remove(this);
if (registeredListeners != null) {
// notify features to be disabled
for (ListenerHolder holder : registeredListeners) {
holder.disableFeatureIfNeeded();
}
// clear all handlers
registeredListeners.clear();
// cancel all notifications
for (ScheduledFuture scheduledNotification : scheduledNotifications) {
scheduledNotification.cancel(true);
}
// notify features to be disabled
for (ListenerHolder holder : registeredListeners) {
holder.disableFeatureIfNeeded();
}
// clear all handlers
registeredListeners.clear();
// empty out notification queue
scheduledNotifications.clear();
lastObservedLicensesState.set(null);
}
@ -338,27 +342,39 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
logLicenseMetaDataStats("old", oldLicensesMetaData);
logLicenseMetaDataStats("new", currentLicensesMetaData);
// Check pending feature registrations and try to complete registrations
if (!pendingRegistrations.isEmpty()) {
if (!pendingListeners.isEmpty()) {
ListenerHolder pendingRegistrationLister;
while ((pendingRegistrationLister = pendingRegistrations.poll()) != null) {
boolean masterAvailable = registerListener(pendingRegistrationLister);
boolean masterAvailable = false;
while ((pendingRegistrationLister = pendingListeners.poll()) != null) {
masterAvailable = registerListener(pendingRegistrationLister);
logger.info("trying to register pending listener for " + pendingRegistrationLister.feature + " masterAvailable: " + masterAvailable);
if (!masterAvailable) {
// if the master is not available do not, break out of trying pendingRegistrations
pendingRegistrations.add(pendingRegistrationLister);
// if the master is not available do not, break out of trying pendingListeners
pendingListeners.add(pendingRegistrationLister);
break;
} else {
logger.info("successfully registered listener for: " + pendingRegistrationLister.feature);
registeredListeners.add(pendingRegistrationLister);
}
}
if (masterAvailable) {
// make sure to notify new registered feature
// notifications could have been scheduled for it before it was registered
notifyFeaturesAndScheduleNotification(currentLicensesMetaData);
}
}
clearFinishedNotifications();
// notify all interested plugins
// notifyFeaturesIfNeeded will short-circuit with -1 if the currentLicensesMetaData has been notified on earlier
// Change to debug
logger.info("calling notifyFeaturesAndScheduleNotificationIfNeeded from clusterChanged");
notifyFeaturesAndScheduleNotificationIfNeeded(currentLicensesMetaData);
if (event.previousState().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) {
notifyFeaturesAndScheduleNotification(currentLicensesMetaData);
} else {
notifyFeaturesAndScheduleNotificationIfNeeded(currentLicensesMetaData);
}
} else {
logger.info("clusterChanged: no action [has STATE_NOT_RECOVERED_BLOCK]");
}
@ -368,8 +384,7 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
final LicensesMetaData lastNotifiedLicensesMetaData = lastObservedLicensesState.get();
if (lastNotifiedLicensesMetaData != null && lastNotifiedLicensesMetaData.equals(currentLicensesMetaData)) {
logger.info("currentLicensesMetaData has been already notified on");
// TODO: figure out when we do not want to notify when clusterChanged() is triggered
//return;
return;
}
notifyFeaturesAndScheduleNotification(currentLicensesMetaData);
}
@ -381,6 +396,65 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
}
}
private long notifyFeatures(LicensesMetaData currentLicensesMetaData) {
long nextScheduleFrequency = -1l;
long offset = TimeValue.timeValueMillis(100).getMillis();
StringBuilder sb = new StringBuilder("Registered listeners: [ ");
for (ListenerHolder listenerHolder : registeredListeners) {
sb.append("( ");
sb.append("feature:");
sb.append(listenerHolder.feature);
sb.append(", ");
long expiryDate;
if ((expiryDate = expiryDateForFeature(listenerHolder.feature, currentLicensesMetaData)) != -1l) {
sb.append(" license expiry: ");
sb.append(expiryDate);
sb.append(", ");
}
long expiryDuration = expiryDate - System.currentTimeMillis();
if (expiryDate == -1l) {
sb.append("no trial/signed license found");
sb.append(", ");
} else {
sb.append("license expires in: ");
sb.append(TimeValue.timeValueMillis(expiryDuration).toString());
sb.append(", ");
}
if (expiryDuration > 0l) {
sb.append("calling enableFeatureIfNeeded");
listenerHolder.enableFeatureIfNeeded();
if (nextScheduleFrequency == -1l) {
nextScheduleFrequency = expiryDuration + offset;
} else {
nextScheduleFrequency = Math.min(expiryDuration + offset, nextScheduleFrequency);
}
} else {
sb.append("calling disableFeatureIfNeeded");
listenerHolder.disableFeatureIfNeeded();
}
sb.append(" )");
}
sb.append("]");
// Change to debug
logger.info(sb.toString());
logLicenseMetaDataStats("Setting last observed metaData", currentLicensesMetaData);
lastObservedLicensesState.set(currentLicensesMetaData);
if (nextScheduleFrequency == -1l) {
logger.info("no need to schedule next notification");
} else {
logger.info("next notification time: " + TimeValue.timeValueMillis(nextScheduleFrequency).toString());
}
return nextScheduleFrequency;
}
private void logLicenseMetaDataStats(String prefix, LicensesMetaData licensesMetaData) {
if (licensesMetaData != null) {
logger.info(prefix + " LicensesMetaData: signedLicenses: " + licensesMetaData.getSignatures().size() + " trialLicenses: " + licensesMetaData.getEncodedTrialLicenses().size());
@ -397,7 +471,7 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
registeredListeners.add(listenerHolder);
} else {
logger.info("add listener for: " + listenerHolder.feature + " to pending registration queue");
pendingRegistrations.add(listenerHolder);
pendingListeners.add(listenerHolder);
}
}
@ -468,11 +542,6 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
return -1l;
}
private String executorName() {
return ThreadPool.Names.GENERIC;
}
private Map<String, ESLicense> getEffectiveLicenses(LicensesMetaData metaData) {
Map<String, ESLicense> map = new HashMap<>();
if (metaData != null) {
@ -485,11 +554,29 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
}
private void clearFinishedNotifications() {
while (!scheduledNotifications.isEmpty()) {
ScheduledFuture notification = scheduledNotifications.peek();
if (notification.isDone()) {
// remove the notifications that are done
scheduledNotifications.poll();
} else {
// stop emptying out the queue as soon as the first undone future hits
break;
}
}
}
private String executorName() {
return ThreadPool.Names.GENERIC;
}
private void scheduleNextNotification(long nextScheduleDelay) {
clearFinishedNotifications();
try {
final TimeValue delay = TimeValue.timeValueMillis(nextScheduleDelay);
// TODO: enqueue Future for management
threadPool.schedule(delay, executorName(), new LicensingClientNotificationJob());
scheduledNotifications.add(threadPool.schedule(delay, executorName(), new LicensingClientNotificationJob()));
logger.info("Scheduling next notification after: " + delay);
} catch (EsRejectedExecutionException ex) {
logger.info("Couldn't re-schedule licensing client notification job", ex);
@ -498,8 +585,7 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
public class LicensingClientNotificationJob implements Runnable {
public LicensingClientNotificationJob() {
}
public LicensingClientNotificationJob() {}
@Override
public void run() {
@ -507,76 +593,21 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
logger.trace("Performing LicensingClientNotificationJob");
}
LicensesMetaData currentLicensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
long nextScheduleDelay = notifyFeatures(currentLicensesMetaData);
if (nextScheduleDelay != -1l) {
try {
scheduleNextNotification(nextScheduleDelay);
} catch (EsRejectedExecutionException ex) {
logger.info("Reschedule licensing client notification job was rejected", ex);
// next clusterChanged event will deal with the missed notifications
if (!clusterService.state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) {
LicensesMetaData currentLicensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
long nextScheduleDelay = notifyFeatures(currentLicensesMetaData);
if (nextScheduleDelay != -1l) {
try {
scheduleNextNotification(nextScheduleDelay);
} catch (EsRejectedExecutionException ex) {
logger.info("Reschedule licensing client notification job was rejected", ex);
}
}
}
}
}
private long notifyFeatures(LicensesMetaData currentLicensesMetaData) {
long nextScheduleFrequency = -1l;
long offset = TimeValue.timeValueMillis(100).getMillis();
StringBuilder sb = new StringBuilder("Registered listeners: [ ");
for (ListenerHolder listenerHolder : registeredListeners) {
sb.append("( ");
sb.append("feature:");
sb.append(listenerHolder.feature);
sb.append(", ");
long expiryDate;
if ((expiryDate = expiryDateForFeature(listenerHolder.feature, currentLicensesMetaData)) != -1l) {
sb.append(" license expiry: ");
sb.append(expiryDate);
sb.append(", ");
}
long expiryDuration = expiryDate - System.currentTimeMillis();
if (expiryDate == -1l) {
sb.append("no trial/signed license found");
sb.append(", ");
} else {
sb.append("license expires in: ");
sb.append(TimeValue.timeValueMillis(expiryDuration).toString());
sb.append(", ");
}
if (expiryDuration > 0l) {
sb.append("calling enableFeatureIfNeeded");
listenerHolder.enableFeatureIfNeeded();
if (nextScheduleFrequency == -1l) {
nextScheduleFrequency = expiryDuration + offset;
} else {
nextScheduleFrequency = Math.min(expiryDuration + offset, nextScheduleFrequency);
}
} else {
sb.append("calling disableFeatureIfNeeded");
listenerHolder.disableFeatureIfNeeded();
}
sb.append(" )");
}
sb.append("]");
// Change to debug
logger.info(sb.toString());
lastObservedLicensesState.set(currentLicensesMetaData);
if (nextScheduleFrequency == -1l) {
logger.info("no need to schedule next notification");
} else {
logger.info("next notification time: " + TimeValue.timeValueMillis(nextScheduleFrequency).toString());
}
return nextScheduleFrequency;
}
public static class PutLicenseRequestHolder {
private final PutLicenseRequest request;
@ -708,9 +739,4 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
return ThreadPool.Names.SAME;
}
}
//Should not be exposed; used by testing only
public void clear() {
registeredListeners.clear();
}
}

View File

@ -5,36 +5,30 @@
*/
package org.elasticsearch.license.plugin;
import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.ClusterAdminClient;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.TestUtils;
import org.elasticsearch.license.core.ESLicense;
import org.elasticsearch.license.core.ESLicenses;
import org.elasticsearch.license.plugin.action.get.GetLicenseRequestBuilder;
import org.elasticsearch.license.plugin.action.get.GetLicenseResponse;
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.junit.Before;
import org.junit.Test;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.SUITE;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
import static org.hamcrest.CoreMatchers.equalTo;
@ClusterScope(scope = SUITE, numDataNodes = 0)
@ClusterScope(scope = TEST, numDataNodes = 0)
public class LicensesServiceClusterRestartTest extends AbstractLicensesIntegrationTests {
static String priKeyPath;
static String pubKeyPath;
@Override
protected Settings transportClientSettings() {
return super.transportClientSettings();
@ -49,71 +43,47 @@ public class LicensesServiceClusterRestartTest extends AbstractLicensesIntegrati
.build();
}
@Test
public void test() throws Exception {
priKeyPath = Paths.get(LicensesServiceClusterRestartTest.class.getResource("/private.key").toURI()).toAbsolutePath().toString();
pubKeyPath = Paths.get(LicensesServiceClusterRestartTest.class.getResource("/public.key").toURI()).toAbsolutePath().toString();
@Before
public void beforeTest() throws Exception {
wipeAllLicenses();
}
logger.info("--> starting 1 nodes");
String node1 = internalCluster().startNode();
@Test
public void testClusterRestart() throws Exception {
logger.info("--> starting 1 node");
internalCluster().startNode();
ensureGreen();
wipeAllLicenses();
final List<ESLicense> esLicenses = putLicense(node1);
final Client startNodeClient = internalCluster().startNodeClient(settingsBuilder().build());
//TODO: just pass node name instead
getAndCheckLicense(startNodeClient, esLicenses);
logger.info("--> cluster state before full cluster restart");
ClusterState clusterState = clusterService().state();
logger.info("Cluster state: {}", clusterState);
final List<ESLicense> esLicenses = generateAndPutLicense();
getAndCheckLicense(esLicenses);
logger.info("--> restart all nodes");
internalCluster().fullRestart();
ensureYellow();
logger.info("--> cluster state after full cluster restart");
clusterState = client().admin().cluster().prepareState().get().getState();
logger.info("Cluster state: {}", clusterState);
getAndCheckLicense(internalCluster().startNodeClient(settingsBuilder().build()), esLicenses);
getAndCheckLicense(esLicenses);
}
private List<ESLicense> putLicense(String node) throws Exception {
final ClusterAdminClient cluster = internalCluster().client(node).admin().cluster();
Map<String, TestUtils.FeatureAttributes> map = new HashMap<>();
TestUtils.FeatureAttributes featureAttributes =
new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-12-13");
map.put(TestUtils.SHIELD, featureAttributes);
String licenseString = TestUtils.generateESLicenses(map);
String licenseOutput = TestUtils.runLicenseGenerationTool(licenseString, pubKeyPath, priKeyPath);
private List<ESLicense> generateAndPutLicense() throws Exception {
ClusterAdminClient cluster = internalCluster().client().admin().cluster();
ESLicense license = generateSignedLicense("shield", TimeValue.timeValueMinutes(1));
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(cluster);
final List<ESLicense> putLicenses = ESLicenses.fromSource(licenseOutput);
assertThat(putLicenses.size(), equalTo(1));
final List<ESLicense> putLicenses = Arrays.asList(license);
putLicenseRequestBuilder.setLicense(putLicenses);
ensureGreen();
final ActionFuture<PutLicenseResponse> putLicenseFuture = putLicenseRequestBuilder.execute();
final PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.execute().get();
final PutLicenseResponse putLicenseResponse = putLicenseFuture.get();
assertTrue(putLicenseResponse.isAcknowledged());
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
return putLicenses;
}
private void getAndCheckLicense(Client client, List<ESLicense> license) {
final ClusterAdminClient cluster = client.admin().cluster();
private void getAndCheckLicense(List<ESLicense> license) {
ClusterAdminClient cluster = internalCluster().client().admin().cluster();
final GetLicenseResponse response = new GetLicenseRequestBuilder(cluster).get();
assertThat(response.licenses().size(), equalTo(1));
TestUtils.isSame(license, response.licenses());
}
private ImmutableSettings.Builder settingsBuilder() {
return ImmutableSettings.settingsBuilder()
.put("gateway.type", "local")
.put("plugin.types", LicensePlugin.class.getName());
}
}

View File

@ -47,7 +47,7 @@ public class LicensesServiceNodeTests extends AbstractLicensesIntegrationTests {
}
return true;
}
}, 1, TimeUnit.MINUTES), equalTo(true));
}, 10, TimeUnit.SECONDS), equalTo(true));
}

View File

@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomFrom;
import static org.elasticsearch.license.plugin.core.LicensesService.LicensesUpdateResponse;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
@ -41,7 +42,6 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
@Before
public void beforeTest() throws Exception {
wipeAllLicenses();
clear();
DiscoveryNodes discoveryNodes = LicensesServiceTests.masterClusterService().state().getNodes();
Set<String> dataNodeSet = new HashSet<>();
@ -117,21 +117,19 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
// multiple client registration with null trial license and then different expiry signed license
final LicensesManagerService masterLicensesManagerService = masterLicensesManagerService();
final List<LicensesService> licensesServices = licensesServices(2);
assertThat(licensesServices.size(), equalTo(2));
final LicensesService licensesService = randomLicensesService();
final TestTrackingClientListener clientListener1 = new TestTrackingClientListener();
final TestTrackingClientListener clientListener2 = new TestTrackingClientListener();
List<Action> firstClientActions = new ArrayList<>();
List<Action> secondClientActions = new ArrayList<>();
final TimeValue firstExpiryDuration = TimeValue.timeValueSeconds(2);
firstClientActions.add(registerWithoutTrialLicense(licensesServices.get(0), clientListener1, "feature1"));
firstClientActions.add(registerWithoutTrialLicense(licensesService, clientListener1, "feature1"));
firstClientActions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, "feature1", firstExpiryDuration));
firstClientActions.add(assertExpiryAction("signed", firstExpiryDuration));
final TimeValue secondExpiryDuration = TimeValue.timeValueSeconds(1);
secondClientActions.add(registerWithoutTrialLicense(licensesServices.get(1), clientListener2, "feature2"));
secondClientActions.add(registerWithoutTrialLicense(licensesService, clientListener2, "feature2"));
secondClientActions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, "feature2", secondExpiryDuration));
secondClientActions.add(assertExpiryAction("signed", secondExpiryDuration));
@ -149,24 +147,19 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
// multiple client registration: one with trial license and another with signed license (different expiry duration)
final LicensesManagerService masterLicensesManagerService = masterLicensesManagerService();
final List<LicensesService> licensesServices = licensesServices(2);
assertThat(licensesServices.size(), equalTo(2));
final LicensesService licensesService = randomLicensesService();
final TestTrackingClientListener clientListener1 = new TestTrackingClientListener();
final TestTrackingClientListener clientListener2 = new TestTrackingClientListener();
List<Action> firstClientActions = new ArrayList<>();
List<Action> secondClientActions = new ArrayList<>();
firstClientActions.add(registerWithoutTrialLicense(licensesServices.get(0), clientListener1, "feature1"));
final TimeValue firstExpiryDuration = TimeValue.timeValueSeconds(2);
firstClientActions.add(registerWithoutTrialLicense(licensesService, clientListener1, "feature1"));
firstClientActions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, "feature1", firstExpiryDuration));
firstClientActions.add(assertExpiryAction("signed", firstExpiryDuration));
final TimeValue secondExpiryDuration = TimeValue.timeValueSeconds(1);
secondClientActions.add(registerWithTrialLicense(licensesServices.get(1), clientListener2, "feature2", secondExpiryDuration));
secondClientActions.add(registerWithTrialLicense(licensesService, clientListener2, "feature2", secondExpiryDuration));
secondClientActions.add(assertExpiryAction("trial", secondExpiryDuration));
if (randomBoolean()) {
@ -182,22 +175,18 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
public void testMultipleClientTrialLicenseRegistration() throws Exception {
// multiple client registration: both with trail license of different expiryDuration
final List<LicensesService> licensesServices = licensesServices(2);
assertThat(licensesServices.size(), equalTo(2));
final LicensesService licensesService = randomLicensesService();
final TestTrackingClientListener clientListener1 = new TestTrackingClientListener();
final TestTrackingClientListener clientListener2 = new TestTrackingClientListener();
List<Action> firstClientActions = new ArrayList<>();
List<Action> secondClientActions = new ArrayList<>();
TimeValue firstExpiryDuration = TimeValue.timeValueSeconds(1);
firstClientActions.add(registerWithTrialLicense(licensesServices.get(0), clientListener1, "feature1", firstExpiryDuration));
firstClientActions.add(registerWithTrialLicense(licensesService, clientListener1, "feature1", firstExpiryDuration));
firstClientActions.add(assertExpiryAction("trial", firstExpiryDuration));
TimeValue secondExpiryDuration = TimeValue.timeValueSeconds(2);
secondClientActions.add(registerWithTrialLicense(licensesServices.get(1), clientListener2, "feature2", secondExpiryDuration));
secondClientActions.add(registerWithTrialLicense(licensesService, clientListener2, "feature2", secondExpiryDuration));
secondClientActions.add(assertExpiryAction("trial", secondExpiryDuration));
if (randomBoolean()) {
@ -229,19 +218,22 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
TimeValue expiryDuration = TimeValue.timeValueSeconds(2);
actions.add(registerWithTrialLicense(clientService, clientListener, "feature1", expiryDuration));
actions.add(assertExpiryAction("trial", expiryDuration));
assertClientListenerNotificationCount(clientListener, actions);
}
@Test
public void testRandomMultipleClientMultipleFeature() throws Exception {
List<LicensesService> licensesServices = licensesServices(10);
LicensesService licensesService = randomLicensesService();
LicensesManagerService masterLicensesManagerService = masterLicensesManagerService();
Map<TestTrackingClientListener, List<Action>> clientListenersWithActions = new HashMap<>();
for (LicensesService licensesService : licensesServices) {
for (int i = 0; i < randomIntBetween(3, 10); i++) {
final TestTrackingClientListener clientListener = new TestTrackingClientListener();
String feature = randomRealisticUnicodeOfCodepointLengthBetween(2, 10);
TimeValue expiryDuration = TimeValue.timeValueSeconds(randomIntBetween(1, 5));
List<Action> actions = new ArrayList<>();
if (randomBoolean()) {
actions.add(registerWithTrialLicense(licensesService, clientListener, feature, expiryDuration));
actions.add(assertExpiryAction("trial", expiryDuration));
@ -323,8 +315,7 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
private Action assertExpiryAction(String licenseType, TimeValue expiryDuration) {
return new Action(new Runnable() {
@Override
public void run() {
}
public void run() {}
}, 1, 0, TimeValue.timeValueMillis(expiryDuration.getMillis() * 2),
"should trigger onDisable once [" + licenseType + " license expiry]");
}
@ -408,35 +399,13 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
return internalCluster().getInstance(LicensesClientService.class, node);
}
private List<LicensesService> licensesServices(int count) {
List<LicensesService> licensesServices = new ArrayList<>(count);
Set<String> selectedNodes = new HashSet<>();
selectedNodes.add(node);
licensesServices.add(internalCluster().getInstance(LicensesService.class, node));
for (int i = 0; i < Math.min(count - 1, nodes.length - 1); i++) {
while (true) {
String newNode = randomFrom(nodes);
if (!selectedNodes.contains(newNode)) {
licensesServices.add(internalCluster().getInstance(LicensesService.class, newNode));
break;
}
}
}
return licensesServices;
private LicensesService randomLicensesService() {
String randomNode = randomFrom(nodes);
return internalCluster().getInstance(LicensesService.class, randomNode);
}
private static ClusterService masterClusterService() {
final InternalTestCluster clients = internalCluster();
return clients.getInstance(ClusterService.class, clients.getMasterName());
}
private void clear() {
final InternalTestCluster clients = internalCluster();
LicensesService masterService = clients.getInstance(LicensesService.class, clients.getMasterName());
masterService.clear();
if (node != null) {
LicensesService nodeService = clients.getInstance(LicensesService.class, node);
nodeService.clear();
}
}
}