Add support for License Expiration event triggers
This enhancement allows consumer plugins to configure event notifications from the licensing plugin relative to its license expiry. Original commit: elastic/x-pack-elasticsearch@11b53dd78d
This commit is contained in:
parent
26fa372056
commit
5be5b1915b
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>elasticsearch-license</artifactId>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<version>1.0.0-beta2</version>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>elasticsearch-license</artifactId>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<version>1.0.0-beta2</version>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
|
@ -17,7 +17,7 @@
|
|||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch-license-core-shaded</artifactId>
|
||||
<version>1.0.0-beta2</version>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
|
|
|
@ -142,7 +142,7 @@ public final class Licenses {
|
|||
if (license.expiryDate() > previousLicense.expiryDate()) {
|
||||
licenseMap.put(featureType, license);
|
||||
}
|
||||
} else if (license.expiryDate() > System.currentTimeMillis()) {
|
||||
} else {
|
||||
licenseMap.put(featureType, license);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>elasticsearch-license</artifactId>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<version>1.0.0-beta2</version>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
|||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch-license-core</artifactId>
|
||||
<version>1.0.0-beta2</version>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>elasticsearch-license</artifactId>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<version>1.0.0-beta2</version>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -21,13 +21,13 @@
|
|||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch-license-licensor</artifactId>
|
||||
<version>1.0.0-beta2</version>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch-license-core</artifactId>
|
||||
<version>1.0.0-beta2</version>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
|
@ -6,7 +6,11 @@
|
|||
package org.elasticsearch.license.plugin.core;
|
||||
|
||||
import org.elasticsearch.common.inject.ImplementedBy;
|
||||
import org.elasticsearch.license.core.License;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.elasticsearch.license.plugin.core.LicensesService.*;
|
||||
import static org.elasticsearch.license.plugin.core.LicensesService.TrialLicenseOptions;
|
||||
|
||||
@ImplementedBy(LicensesService.class)
|
||||
|
@ -17,12 +21,13 @@ public interface LicensesClientService {
|
|||
/**
|
||||
* Called to enable a feature
|
||||
*/
|
||||
public void onEnabled();
|
||||
public void onEnabled(License license);
|
||||
|
||||
/**
|
||||
* Called to disable a feature
|
||||
*/
|
||||
public void onDisabled();
|
||||
public void onDisabled(License license);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,7 +36,8 @@ public interface LicensesClientService {
|
|||
* @param feature - name of the feature to register (must be in sync with license Generator feature name)
|
||||
* @param trialLicenseOptions - Trial license specification used to generate a one-time trial license for the feature;
|
||||
* use <code>null</code> if no trial license should be generated for the feature
|
||||
* @param expirationCallbacks - A collection of Pre and/or Post expiration callbacks
|
||||
* @param listener - used to notify on feature enable/disable
|
||||
*/
|
||||
void register(String feature, TrialLicenseOptions trialLicenseOptions, Listener listener);
|
||||
void register(String feature, TrialLicenseOptions trialLicenseOptions, Collection<ExpirationCallback> expirationCallbacks, Listener listener);
|
||||
}
|
||||
|
|
|
@ -36,11 +36,9 @@ import org.elasticsearch.transport.*;
|
|||
|
||||
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.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.elasticsearch.license.core.Licenses.reduceAndMap;
|
||||
|
@ -54,7 +52,7 @@ import static org.elasticsearch.license.core.Licenses.reduceAndMap;
|
|||
* <p/>
|
||||
* Registration Scheme:
|
||||
* <p/>
|
||||
* A consumer plugin (feature) is registered with {@link LicensesClientService#register(String, TrialLicenseOptions, LicensesClientService.Listener)}
|
||||
* A consumer plugin (feature) is registered with {@link LicensesClientService#register(String, TrialLicenseOptions, java.util.Collection<org.elasticsearch.license.plugin.core.LicensesService.ExpirationCallback>, 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 org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK} block
|
||||
|
@ -112,6 +110,11 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
*/
|
||||
private final Queue<ScheduledFuture> scheduledNotifications = new ConcurrentLinkedQueue<>();
|
||||
|
||||
/**
|
||||
* Currently active event notifications for every registered feature
|
||||
*/
|
||||
private final Map<String, Queue<ScheduledFuture>> eventNotificationsMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The last licensesMetaData that has been notified by {@link #notifyFeatures(LicensesMetaData)}
|
||||
*/
|
||||
|
@ -352,9 +355,18 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
scheduledNotification.cancel(true);
|
||||
}
|
||||
|
||||
for (Queue<ScheduledFuture> queue : eventNotificationsMap.values()) {
|
||||
for (ScheduledFuture scheduledFuture : queue) {
|
||||
scheduledFuture.cancel(true);
|
||||
}
|
||||
queue.clear();
|
||||
}
|
||||
|
||||
LicensesMetaData currentLicensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
// notify features to be disabled
|
||||
for (ListenerHolder holder : registeredListeners) {
|
||||
holder.disableFeatureIfNeeded(false);
|
||||
holder.disableFeatureIfNeeded(effectiveLicenses.get(holder.feature), false);
|
||||
}
|
||||
// clear all handlers
|
||||
registeredListeners.clear();
|
||||
|
@ -410,6 +422,10 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
} else {
|
||||
notifyFeaturesAndScheduleNotificationIfNeeded(currentLicensesMetaData);
|
||||
}
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
for (ListenerHolder listenerHolder : registeredListeners) {
|
||||
listenerHolder.scheduleNotificationIfNeeded(effectiveLicenses.get(listenerHolder.feature));
|
||||
}
|
||||
} else if (logger.isDebugEnabled()) {
|
||||
logger.debug("clusterChanged: no action [has STATE_NOT_RECOVERED_BLOCK]");
|
||||
}
|
||||
|
@ -457,20 +473,25 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
private long notifyFeatures(final LicensesMetaData currentLicensesMetaData) {
|
||||
long nextScheduleFrequency = -1l;
|
||||
for (ListenerHolder listenerHolder : registeredListeners) {
|
||||
long expiryDate = expiryDateForFeature(listenerHolder.feature, currentLicensesMetaData);
|
||||
long issueDate = issueDateForFeature(listenerHolder.feature, currentLicensesMetaData);
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
License license = effectiveLicenses.get(listenerHolder.feature);
|
||||
if (license == null) {
|
||||
continue;
|
||||
}
|
||||
long expiryDate = license.expiryDate();
|
||||
long issueDate = license.issueDate();
|
||||
long now = System.currentTimeMillis();
|
||||
long expiryDuration = expiryDate - now;
|
||||
|
||||
if (expiryDuration > 0l && (now - issueDate) >= 0l) {
|
||||
listenerHolder.enableFeatureIfNeeded();
|
||||
listenerHolder.enableFeatureIfNeeded(license);
|
||||
if (nextScheduleFrequency == -1l) {
|
||||
nextScheduleFrequency = expiryDuration;
|
||||
} else {
|
||||
nextScheduleFrequency = Math.min(expiryDuration, nextScheduleFrequency);
|
||||
}
|
||||
} else {
|
||||
listenerHolder.disableFeatureIfNeeded(true);
|
||||
listenerHolder.disableFeatureIfNeeded(license, true);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
@ -529,13 +550,15 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void register(String feature, TrialLicenseOptions trialLicenseOptions, Listener listener) {
|
||||
public void register(String feature, TrialLicenseOptions trialLicenseOptions, Collection<ExpirationCallback> expirationCallbacks, Listener listener) {
|
||||
for (final ListenerHolder listenerHolder : Iterables.concat(registeredListeners, pendingListeners)) {
|
||||
if (listenerHolder.feature.equals(feature)) {
|
||||
throw new IllegalStateException("feature: [" + feature + "] has been already registered");
|
||||
}
|
||||
}
|
||||
final ListenerHolder listenerHolder = new ListenerHolder(feature, trialLicenseOptions, listener);
|
||||
Queue<ScheduledFuture> notificationQueue = new ConcurrentLinkedQueue<>();
|
||||
eventNotificationsMap.put(feature, notificationQueue);
|
||||
final ListenerHolder listenerHolder = new ListenerHolder(feature, trialLicenseOptions, expirationCallbacks, listener, notificationQueue);
|
||||
// don't trust the clusterState for blocks just yet!
|
||||
final Lifecycle.State clusterServiceState = clusterService.lifecycleState();
|
||||
if (clusterServiceState != Lifecycle.State.STARTED) {
|
||||
|
@ -581,8 +604,9 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
} else {
|
||||
// could not sent register trial license request to master
|
||||
logger.debug("Store as pendingRegistration [master not available yet]");
|
||||
return false;
|
||||
}
|
||||
// make sure trial license is available before registration
|
||||
return false;
|
||||
}
|
||||
} else if (logger.isDebugEnabled()) {
|
||||
// notify feature as clusterChangedEvent may not happen
|
||||
|
@ -594,21 +618,18 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
// feature, notify feature on registration
|
||||
logger.debug("Calling notifyFeaturesAndScheduleNotification [signed/trial license available]");
|
||||
}
|
||||
|
||||
if (currentMetaData == null) {
|
||||
return false;
|
||||
}
|
||||
registeredListeners.add(listenerHolder);
|
||||
notifyFeaturesAndScheduleNotification(currentMetaData);
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentMetaData);
|
||||
listenerHolder.scheduleNotificationIfNeeded(effectiveLicenses.get(listenerHolder.feature));
|
||||
return true;
|
||||
}
|
||||
|
||||
private long issueDateForFeature(String feature, final LicensesMetaData currentLicensesMetaData) {
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
License featureLicense;
|
||||
if ((featureLicense = effectiveLicenses.get(feature)) != null) {
|
||||
return featureLicense.issueDate();
|
||||
}
|
||||
return -1l;
|
||||
}
|
||||
|
||||
private long expiryDateForFeature(String feature, final LicensesMetaData currentLicensesMetaData) {
|
||||
private static long expiryDateForFeature(String feature, final LicensesMetaData currentLicensesMetaData) {
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
License featureLicense;
|
||||
if ((featureLicense = effectiveLicenses.get(feature)) != null) {
|
||||
|
@ -617,7 +638,7 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
return -1l;
|
||||
}
|
||||
|
||||
private Map<String, License> getEffectiveLicenses(final LicensesMetaData metaData) {
|
||||
private static Map<String, License> getEffectiveLicenses(final LicensesMetaData metaData) {
|
||||
Map<String, License> map = new HashMap<>();
|
||||
if (metaData != null) {
|
||||
Set<License> licenses = new HashSet<>();
|
||||
|
@ -634,10 +655,9 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
}
|
||||
|
||||
/**
|
||||
* Clears out any completed notification future from
|
||||
* {@link #scheduledNotifications}
|
||||
* Clears out any completed notification futures
|
||||
*/
|
||||
private void clearFinishedNotifications() {
|
||||
private static void clearFinishedNotifications(Queue<ScheduledFuture> scheduledNotifications) {
|
||||
while (!scheduledNotifications.isEmpty()) {
|
||||
ScheduledFuture notification = scheduledNotifications.peek();
|
||||
if (notification != null && notification.isDone()) {
|
||||
|
@ -658,7 +678,7 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
* Schedules an expiry notification with a delay of <code>nextScheduleDelay</code>
|
||||
*/
|
||||
private void scheduleNextNotification(long nextScheduleDelay) {
|
||||
clearFinishedNotifications();
|
||||
clearFinishedNotifications(scheduledNotifications);
|
||||
|
||||
try {
|
||||
final TimeValue delay = TimeValue.timeValueMillis(nextScheduleDelay);
|
||||
|
@ -697,7 +717,6 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static class PutLicenseRequestHolder {
|
||||
private final PutLicenseRequest request;
|
||||
private final String source;
|
||||
|
@ -728,38 +747,223 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
}
|
||||
}
|
||||
|
||||
public static class ExpirationStatus {
|
||||
private final boolean expired;
|
||||
private final TimeValue time;
|
||||
|
||||
private ExpirationStatus(boolean expired, TimeValue time) {
|
||||
this.expired = expired;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public boolean expired() {
|
||||
return expired;
|
||||
}
|
||||
|
||||
public TimeValue time() {
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface LicenseCallback {
|
||||
void on(License license, ExpirationStatus status);
|
||||
}
|
||||
|
||||
public static abstract class ExpirationCallback implements LicenseCallback {
|
||||
|
||||
public enum Orientation { PRE, POST }
|
||||
|
||||
public static abstract class Pre extends ExpirationCallback {
|
||||
|
||||
/**
|
||||
* Callback schedule prior to license expiry
|
||||
*
|
||||
* @param min latest relative time to execute before license expiry
|
||||
* @param max earliest relative time to execute before license expiry
|
||||
* @param frequency interval between execution
|
||||
*/
|
||||
public Pre(TimeValue min, TimeValue max, TimeValue frequency) {
|
||||
super(Orientation.PRE, min, max, frequency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(long expirationDate, long now) {
|
||||
long expiryDuration = expirationDate - now;
|
||||
if (expiryDuration > 0l) {
|
||||
if (expiryDuration <= max().getMillis()) {
|
||||
return expiryDuration >= min().getMillis();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class Post extends ExpirationCallback {
|
||||
|
||||
/**
|
||||
* Callback schedule after license expiry
|
||||
*
|
||||
* @param min earliest relative time to execute after license expiry
|
||||
* @param max latest relative time to execute after license expiry
|
||||
* @param frequency interval between execution
|
||||
*/
|
||||
public Post(TimeValue min, TimeValue max, TimeValue frequency) {
|
||||
super(Orientation.POST, min, max, frequency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(long expirationDate, long now) {
|
||||
long postExpiryDuration = now - expirationDate;
|
||||
if (postExpiryDuration > 0l) {
|
||||
if (postExpiryDuration <= max().getMillis()) {
|
||||
return postExpiryDuration >= min().getMillis();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private final Orientation orientation;
|
||||
private final TimeValue min;
|
||||
private final TimeValue max;
|
||||
private final TimeValue frequency;
|
||||
|
||||
private ExpirationCallback(Orientation orientation, TimeValue min, TimeValue max, TimeValue frequency) {
|
||||
this.orientation = orientation;
|
||||
this.min = (min == null) ? TimeValue.timeValueMillis(0) : min;
|
||||
this.max = (max == null) ? TimeValue.timeValueMillis(Long.MAX_VALUE) : max;
|
||||
this.frequency = frequency;
|
||||
if (frequency == null) {
|
||||
throw new IllegalArgumentException("frequency can not be null");
|
||||
}
|
||||
}
|
||||
|
||||
public Orientation orientation() {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
public TimeValue min() {
|
||||
return min;
|
||||
}
|
||||
|
||||
public TimeValue max() {
|
||||
return max;
|
||||
}
|
||||
|
||||
public TimeValue frequency() {
|
||||
return frequency;
|
||||
}
|
||||
|
||||
public abstract boolean matches(long expirationDate, long now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores configuration and listener for a feature
|
||||
*/
|
||||
private class ListenerHolder {
|
||||
final String feature;
|
||||
final TrialLicenseOptions trialLicenseOptions;
|
||||
final Collection<ExpirationCallback> expirationCallbacks;
|
||||
final Listener listener;
|
||||
final AtomicLong currentExpiryDate = new AtomicLong(-1l);
|
||||
final Queue<ScheduledFuture> notificationQueue;
|
||||
|
||||
volatile AtomicBoolean enabled = new AtomicBoolean(false); // by default, a consumer plugin should be disabled
|
||||
|
||||
private ListenerHolder(String feature, TrialLicenseOptions trialLicenseOptions, Listener listener) {
|
||||
private ListenerHolder(String feature, TrialLicenseOptions trialLicenseOptions, Collection<ExpirationCallback> expirationCallbacks, Listener listener, Queue<ScheduledFuture> notificationQueue) {
|
||||
this.feature = feature;
|
||||
this.trialLicenseOptions = trialLicenseOptions;
|
||||
this.expirationCallbacks = expirationCallbacks;
|
||||
this.listener = listener;
|
||||
this.notificationQueue = notificationQueue;
|
||||
}
|
||||
|
||||
private void enableFeatureIfNeeded() {
|
||||
private void enableFeatureIfNeeded(License license) {
|
||||
if (enabled.compareAndSet(false, true)) {
|
||||
listener.onEnabled();
|
||||
listener.onEnabled(license);
|
||||
logger.info("license for [" + feature + "] - valid");
|
||||
}
|
||||
}
|
||||
|
||||
private void disableFeatureIfNeeded(boolean log) {
|
||||
private void disableFeatureIfNeeded(License license, boolean log) {
|
||||
if (enabled.compareAndSet(true, false)) {
|
||||
listener.onDisabled();
|
||||
listener.onDisabled(license);
|
||||
if (log) {
|
||||
logger.info("license for [" + feature + "] - expired");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable triggerJob() {
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LicensesMetaData currentLicensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
triggerEvents(effectiveLicenses.get(feature), System.currentTimeMillis());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void scheduleNotificationIfNeeded(final License license) {
|
||||
long expiryDate = ((license != null) ? license.expiryDate() : -1l);
|
||||
if (currentExpiryDate.get() == expiryDate) {
|
||||
return;
|
||||
}
|
||||
currentExpiryDate.set(expiryDate);
|
||||
|
||||
// clear out notification queue
|
||||
while (!notificationQueue.isEmpty()) {
|
||||
ScheduledFuture notification = notificationQueue.peek();
|
||||
if (notification != null) {
|
||||
// cancel
|
||||
notification.cancel(true);
|
||||
notificationQueue.poll();
|
||||
}
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
// Schedule the first for all the callbacks
|
||||
long expiryDuration = expiryDate - now;
|
||||
|
||||
//schedule first event of callbacks that will be activated in the future
|
||||
for (ExpirationCallback expirationCallback : expirationCallbacks) {
|
||||
if (!expirationCallback.matches(expiryDate, now)) {
|
||||
long delay = -1l;
|
||||
switch (expirationCallback.orientation()) {
|
||||
case PRE:
|
||||
delay = expiryDuration - expirationCallback.max().getMillis();
|
||||
break;
|
||||
case POST:
|
||||
if (expiryDuration > 0l) {
|
||||
delay = expiryDuration + expirationCallback.min().getMillis();
|
||||
} else {
|
||||
delay = (-1l * expiryDuration) - expirationCallback.min().getMillis();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (delay > 0l) {
|
||||
notificationQueue.add(threadPool.schedule(TimeValue.timeValueMillis(delay), executorName(), triggerJob()));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (license != null) {
|
||||
// schedule first event of callbacks that match
|
||||
triggerEvents(license, now);
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerEvents(final License license, long now) {
|
||||
for (ExpirationCallback expirationCallback : expirationCallbacks) {
|
||||
if (expirationCallback.matches(license.expiryDate(), now)) {
|
||||
long expiryDuration = license.expiryDate() - now;
|
||||
boolean expired = expiryDuration <= 0l;
|
||||
expirationCallback.on(license, new ExpirationStatus(expired, TimeValue.timeValueMillis((!expired) ? expiryDuration : (-1l * expiryDuration))));
|
||||
notificationQueue.add(threadPool.schedule(expirationCallback.frequency(), executorName(), triggerJob()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "(feature: " + feature + ", enabled: " + enabled.get() + ")";
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ public abstract class AbstractLicensesIntegrationTests extends ElasticsearchInte
|
|||
}
|
||||
|
||||
private void assertConsumerPluginNotification(String msg, final Iterable<TestPluginServiceBase> consumerPluginServices, final boolean expectedEnabled, int timeoutInSec) throws InterruptedException {
|
||||
assertThat(msg, awaitBusy(new Predicate<Object>() {
|
||||
boolean success = awaitBusy(new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object o) {
|
||||
for (TestPluginServiceBase pluginService : consumerPluginServices) {
|
||||
|
@ -162,7 +162,9 @@ public abstract class AbstractLicensesIntegrationTests extends ElasticsearchInte
|
|||
}
|
||||
return true;
|
||||
}
|
||||
}, timeoutInSec, TimeUnit.SECONDS), equalTo(true));
|
||||
}, timeoutInSec + 1, TimeUnit.SECONDS);
|
||||
logger.debug("Notification assertion complete");
|
||||
assertThat(msg, success, equalTo(true));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -20,11 +20,10 @@ import org.elasticsearch.license.plugin.core.LicensesStatus;
|
|||
import org.elasticsearch.test.InternalTestCluster;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.elasticsearch.license.plugin.core.LicensesService.LicensesUpdateResponse;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
@ -80,7 +79,22 @@ public abstract class AbstractLicensesServiceTests extends AbstractLicensesInteg
|
|||
return new Action(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clientService.register(feature, new LicensesService.TrialLicenseOptions(expiryDuration, 10),
|
||||
clientService.register(feature, new LicensesService.TrialLicenseOptions(expiryDuration, 10), Collections.<LicensesService.ExpirationCallback>emptyList(),
|
||||
clientListener);
|
||||
|
||||
// invoke clusterChanged event to flush out pendingRegistration
|
||||
LicensesService licensesService = (LicensesService) clientService;
|
||||
ClusterChangedEvent event = new ClusterChangedEvent("", clusterService().state(), clusterService().state());
|
||||
licensesService.clusterChanged(event);
|
||||
}
|
||||
}, 0, 1, "should trigger onEnable for " + feature + " once [trial license]");
|
||||
}
|
||||
|
||||
protected Action registerWithEventNotification(final LicensesClientService clientService, final LicensesClientService.Listener clientListener, final String feature, final TimeValue expiryDuration, final Collection<LicensesService.ExpirationCallback> expirationCallbacks) {
|
||||
return new Action(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clientService.register(feature, new LicensesService.TrialLicenseOptions(expiryDuration, 10), expirationCallbacks,
|
||||
clientListener);
|
||||
|
||||
// invoke clusterChanged event to flush out pendingRegistration
|
||||
|
@ -94,15 +108,18 @@ public abstract class AbstractLicensesServiceTests extends AbstractLicensesInteg
|
|||
protected class TestTrackingClientListener implements LicensesClientService.Listener {
|
||||
CountDownLatch enableLatch;
|
||||
CountDownLatch disableLatch;
|
||||
AtomicBoolean enabled = new AtomicBoolean(false);
|
||||
|
||||
final boolean track;
|
||||
final String featureName;
|
||||
|
||||
public TestTrackingClientListener(boolean track) {
|
||||
this.track = track;
|
||||
public TestTrackingClientListener(String featureName) {
|
||||
this(featureName, true);
|
||||
}
|
||||
|
||||
public TestTrackingClientListener() {
|
||||
this(true);
|
||||
public TestTrackingClientListener(String featureName, boolean track) {
|
||||
this.track = track;
|
||||
this.featureName = featureName;
|
||||
}
|
||||
|
||||
public synchronized void latch(CountDownLatch enableLatch, CountDownLatch disableLatch) {
|
||||
|
@ -111,14 +128,20 @@ public abstract class AbstractLicensesServiceTests extends AbstractLicensesInteg
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onEnabled() {
|
||||
public void onEnabled(License license) {
|
||||
assertNotNull(license);
|
||||
assertThat(license.feature(), equalTo(featureName));
|
||||
enabled.set(true);
|
||||
if (track) {
|
||||
this.enableLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled() {
|
||||
public void onDisabled(License license) {
|
||||
assertNotNull(license);
|
||||
assertThat(license.feature(), equalTo(featureName));
|
||||
enabled.set(false);
|
||||
if (track) {
|
||||
this.disableLatch.countDown();
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||
import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
@ClusterScope(scope = TEST, numDataNodes = 10)
|
||||
public class LicensesClientServiceTests extends AbstractLicensesServiceTests {
|
||||
|
@ -32,11 +32,11 @@ public class LicensesClientServiceTests extends AbstractLicensesServiceTests {
|
|||
// register with trial license and assert onEnable and onDisable notification
|
||||
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener();
|
||||
String feature1 = "feature1";
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener(feature1);
|
||||
List<Action> actions = new ArrayList<>();
|
||||
|
||||
final TimeValue expiryDuration = TimeValue.timeValueSeconds(2);
|
||||
String feature1 = "feature1";
|
||||
actions.add(registerWithTrialLicense(clientService, clientListener, feature1, expiryDuration));
|
||||
actions.add(assertExpiryAction(feature1, "trial", expiryDuration));
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
|
@ -45,37 +45,212 @@ public class LicensesClientServiceTests extends AbstractLicensesServiceTests {
|
|||
@Test
|
||||
public void testLicenseWithFutureIssueDate() throws Exception {
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener();
|
||||
String feature = "feature";
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener(feature);
|
||||
List<Action> actions = new ArrayList<>();
|
||||
long now = System.currentTimeMillis();
|
||||
long issueDate = dateMath("now+10d/d", now);
|
||||
|
||||
actions.add(registerWithTrialLicense(clientService, clientListener, feature, TimeValue.timeValueSeconds(2)));
|
||||
actions.add(registerWithoutTrialLicense(clientService, clientListener, feature));
|
||||
actions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService(), feature, issueDate, TimeValue.timeValueHours(24 * 20)));
|
||||
actions.add(assertExpiryAction(feature, "trial", TimeValue.timeValueSeconds(2)));
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
assertThat(clientListener.enabled.get(), equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostExpiration() throws Exception {
|
||||
int postExpirySeconds = randomIntBetween(5, 10);
|
||||
TimeValue postExpiryDuration = TimeValue.timeValueSeconds(postExpirySeconds);
|
||||
TimeValue min = TimeValue.timeValueSeconds(postExpirySeconds - randomIntBetween(1, 3));
|
||||
TimeValue max = TimeValue.timeValueSeconds(postExpirySeconds + randomIntBetween(1, 10));
|
||||
|
||||
final LicensesService.ExpirationCallback.Post post = new LicensesService.ExpirationCallback.Post(min, max, TimeValue.timeValueMillis(10)) {
|
||||
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
long now = System.currentTimeMillis();
|
||||
assertThat(post.matches(now - postExpiryDuration.millis(), now), equalTo(true));
|
||||
assertThat(post.matches(now + postExpiryDuration.getMillis(), now), equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostExpirationWithNullMax() throws Exception {
|
||||
int postExpirySeconds = randomIntBetween(5, 10);
|
||||
TimeValue postExpiryDuration = TimeValue.timeValueSeconds(postExpirySeconds);
|
||||
TimeValue min = TimeValue.timeValueSeconds(postExpirySeconds - randomIntBetween(1, 3));
|
||||
|
||||
final LicensesService.ExpirationCallback.Post post = new LicensesService.ExpirationCallback.Post(min, null, TimeValue.timeValueMillis(10)) {
|
||||
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
long now = System.currentTimeMillis();
|
||||
assertThat(post.matches(now - postExpiryDuration.millis(), now), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreExpirationWithNullMin() throws Exception {
|
||||
int expirySeconds = randomIntBetween(5, 10);
|
||||
TimeValue expiryDuration = TimeValue.timeValueSeconds(expirySeconds);
|
||||
TimeValue max = TimeValue.timeValueSeconds(expirySeconds + randomIntBetween(1, 10));
|
||||
|
||||
final LicensesService.ExpirationCallback.Pre pre = new LicensesService.ExpirationCallback.Pre(null, max, TimeValue.timeValueMillis(10)) {
|
||||
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
long now = System.currentTimeMillis();
|
||||
assertThat(pre.matches(expiryDuration.millis() + now, now), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreExpiration() throws Exception {
|
||||
int expirySeconds = randomIntBetween(5, 10);
|
||||
TimeValue expiryDuration = TimeValue.timeValueSeconds(expirySeconds);
|
||||
TimeValue min = TimeValue.timeValueSeconds(expirySeconds - randomIntBetween(0, 3));
|
||||
TimeValue max = TimeValue.timeValueSeconds(expirySeconds + randomIntBetween(1, 10));
|
||||
|
||||
final LicensesService.ExpirationCallback.Pre pre = new LicensesService.ExpirationCallback.Pre(min, max, TimeValue.timeValueMillis(10)) {
|
||||
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
long now = System.currentTimeMillis();
|
||||
assertThat(pre.matches(expiryDuration.millis() + now, now), equalTo(true));
|
||||
assertThat(pre.matches(now - expiryDuration.getMillis(), now), equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleEventNotification() throws Exception {
|
||||
final LicensesManagerService licensesManagerService = masterLicensesManagerService();
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
final String feature = "feature";
|
||||
TestTrackingClientListener clientListener = new TestTrackingClientListener(feature, true);
|
||||
|
||||
List<LicensesService.ExpirationCallback> callbacks = new ArrayList<>();
|
||||
final AtomicInteger triggerCount1 = new AtomicInteger(0);
|
||||
callbacks.add(preCallbackLatch(TimeValue.timeValueMillis(500), TimeValue.timeValueSeconds(1), TimeValue.timeValueMillis(100), triggerCount1));
|
||||
final AtomicInteger triggerCount2 = new AtomicInteger(0);
|
||||
callbacks.add(postCallbackLatch(TimeValue.timeValueMillis(0), null, TimeValue.timeValueMillis(200), triggerCount2));
|
||||
final AtomicInteger triggerCount3 = new AtomicInteger(0);
|
||||
callbacks.add(preCallbackLatch(TimeValue.timeValueSeconds(1), TimeValue.timeValueSeconds(2), TimeValue.timeValueMillis(500), triggerCount3));
|
||||
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(registerWithEventNotification(clientService, clientListener, feature, TimeValue.timeValueSeconds(3), callbacks));
|
||||
actions.add(assertExpiryAction(feature, "trial", TimeValue.timeValueMinutes(1)));
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
assertThat(triggerCount3.get(), equalTo(2));
|
||||
assertThat(triggerCount1.get(), greaterThan(4));
|
||||
Thread.sleep(2000);
|
||||
assertThat(triggerCount2.get(), greaterThan(8));
|
||||
int previousTriggerCount = triggerCount2.get();
|
||||
|
||||
// Update license
|
||||
generateAndPutSignedLicenseAction(licensesManagerService, feature, TimeValue.timeValueSeconds(10)).run();
|
||||
Thread.sleep(500);
|
||||
assertThat(previousTriggerCount, lessThanOrEqualTo(triggerCount2.get() + 1));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testPostEventNotification2() throws Exception {
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
final String feature = "feature";
|
||||
TestTrackingClientListener clientListener = new TestTrackingClientListener(feature, true);
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(
|
||||
registerWithEventNotification(clientService, clientListener, feature, TimeValue.timeValueSeconds(3),
|
||||
Arrays.asList(
|
||||
postCallbackLatch(TimeValue.timeValueMillis(0), TimeValue.timeValueSeconds(2), TimeValue.timeValueMillis(500), counter)
|
||||
))
|
||||
);
|
||||
actions.add(assertExpiryAction(feature, "trial", TimeValue.timeValueSeconds(3)));
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
Thread.sleep(50 + 2000);
|
||||
assertThat(counter.get(), equalTo(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreEventNotification() throws Exception {
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
final String feature = "feature";
|
||||
TestTrackingClientListener clientListener = new TestTrackingClientListener(feature, true);
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(
|
||||
registerWithEventNotification(clientService, clientListener, feature, TimeValue.timeValueSeconds(3),
|
||||
Arrays.asList(
|
||||
preCallbackLatch(TimeValue.timeValueMillis(500), TimeValue.timeValueSeconds(2), TimeValue.timeValueMillis(500), counter)
|
||||
))
|
||||
);
|
||||
actions.add(assertExpiryAction(feature, "trial", TimeValue.timeValueSeconds(3)));
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
assertThat(counter.get(), equalTo(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostEventNotification() throws Exception {
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
final String feature = "feature";
|
||||
TestTrackingClientListener clientListener = new TestTrackingClientListener(feature, true);
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(
|
||||
registerWithEventNotification(clientService, clientListener, feature, TimeValue.timeValueSeconds(1),
|
||||
Arrays.asList(
|
||||
postCallbackLatch(TimeValue.timeValueMillis(500), TimeValue.timeValueSeconds(2), TimeValue.timeValueMillis(500), counter)
|
||||
))
|
||||
);
|
||||
actions.add(assertExpiryAction(feature, "trial", TimeValue.timeValueSeconds(1)));
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
Thread.sleep(50 + 2000);
|
||||
assertThat(counter.get(), equalTo(3));
|
||||
}
|
||||
|
||||
private LicensesService.ExpirationCallback preCallbackLatch(TimeValue min, TimeValue max, TimeValue frequency, final AtomicInteger triggerCount) {
|
||||
return new LicensesService.ExpirationCallback.Pre(min, max, frequency) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
triggerCount.incrementAndGet();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private LicensesService.ExpirationCallback postCallbackLatch(TimeValue min, TimeValue max, TimeValue frequency, final AtomicInteger triggerCount) {
|
||||
return new LicensesService.ExpirationCallback.Post(min, max, frequency) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
triggerCount.incrementAndGet();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMultipleClientSignedLicenseEnforcement() throws Exception {
|
||||
// multiple client registration with null trial license and then different expiry signed license
|
||||
|
||||
final LicensesManagerService masterLicensesManagerService = masterLicensesManagerService();
|
||||
final LicensesService licensesService = randomLicensesService();
|
||||
final TestTrackingClientListener clientListener1 = new TestTrackingClientListener();
|
||||
final TestTrackingClientListener clientListener2 = new TestTrackingClientListener();
|
||||
String feature1 = "feature1";
|
||||
String feature2 = "feature2";
|
||||
final TestTrackingClientListener clientListener1 = new TestTrackingClientListener(feature1);
|
||||
final TestTrackingClientListener clientListener2 = new TestTrackingClientListener(feature2);
|
||||
List<Action> firstClientActions = new ArrayList<>();
|
||||
List<Action> secondClientActions = new ArrayList<>();
|
||||
|
||||
final TimeValue firstExpiryDuration = TimeValue.timeValueSeconds(2);
|
||||
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);
|
||||
String feature2 = "feature2";
|
||||
secondClientActions.add(registerWithoutTrialLicense(licensesService, clientListener2, feature2));
|
||||
secondClientActions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, feature2, secondExpiryDuration));
|
||||
secondClientActions.add(assertExpiryAction(feature2, "signed", secondExpiryDuration));
|
||||
|
@ -95,19 +270,19 @@ public class LicensesClientServiceTests extends AbstractLicensesServiceTests {
|
|||
|
||||
final LicensesManagerService masterLicensesManagerService = masterLicensesManagerService();
|
||||
final LicensesService licensesService = randomLicensesService();
|
||||
final TestTrackingClientListener clientListener1 = new TestTrackingClientListener();
|
||||
final TestTrackingClientListener clientListener2 = new TestTrackingClientListener();
|
||||
String feature1 = "feature1";
|
||||
String feature2 = "feature2";
|
||||
final TestTrackingClientListener clientListener1 = new TestTrackingClientListener(feature1);
|
||||
final TestTrackingClientListener clientListener2 = new TestTrackingClientListener(feature2);
|
||||
List<Action> firstClientActions = new ArrayList<>();
|
||||
List<Action> secondClientActions = new ArrayList<>();
|
||||
|
||||
final TimeValue firstExpiryDuration = TimeValue.timeValueSeconds(2);
|
||||
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);
|
||||
String feature2 = "feature2";
|
||||
secondClientActions.add(registerWithTrialLicense(licensesService, clientListener2, feature2, secondExpiryDuration));
|
||||
secondClientActions.add(assertExpiryAction(feature2, "trial", secondExpiryDuration));
|
||||
|
||||
|
@ -125,18 +300,18 @@ public class LicensesClientServiceTests extends AbstractLicensesServiceTests {
|
|||
// multiple client registration: both with trail license of different expiryDuration
|
||||
|
||||
final LicensesService licensesService = randomLicensesService();
|
||||
final TestTrackingClientListener clientListener1 = new TestTrackingClientListener();
|
||||
final TestTrackingClientListener clientListener2 = new TestTrackingClientListener();
|
||||
String feature1 = "feature1";
|
||||
String feature2 = "feature2";
|
||||
final TestTrackingClientListener clientListener1 = new TestTrackingClientListener(feature1);
|
||||
final TestTrackingClientListener clientListener2 = new TestTrackingClientListener(feature2);
|
||||
List<Action> firstClientActions = new ArrayList<>();
|
||||
List<Action> secondClientActions = new ArrayList<>();
|
||||
|
||||
TimeValue firstExpiryDuration = TimeValue.timeValueSeconds(1);
|
||||
String feature1 = "feature1";
|
||||
firstClientActions.add(registerWithTrialLicense(licensesService, clientListener1, feature1, firstExpiryDuration));
|
||||
firstClientActions.add(assertExpiryAction(feature1, "trial", firstExpiryDuration));
|
||||
|
||||
TimeValue secondExpiryDuration = TimeValue.timeValueSeconds(2);
|
||||
String feature2 = "feature2";
|
||||
secondClientActions.add(registerWithTrialLicense(licensesService, clientListener2, feature2, secondExpiryDuration));
|
||||
secondClientActions.add(assertExpiryAction(feature2, "trial", secondExpiryDuration));
|
||||
|
||||
|
@ -153,21 +328,22 @@ public class LicensesClientServiceTests extends AbstractLicensesServiceTests {
|
|||
public void testFeatureWithoutLicense() throws Exception {
|
||||
// client registration with no trial license + no signed license
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener();
|
||||
String feature = "feature1";
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener(feature);
|
||||
List<Action> actions = new ArrayList<>();
|
||||
|
||||
actions.add(registerWithoutTrialLicense(clientService, clientListener, "feature1"));
|
||||
actions.add(registerWithoutTrialLicense(clientService, clientListener, feature));
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLicenseExpiry() throws Exception {
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener();
|
||||
String feature = "feature1";
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener(feature);
|
||||
List<Action> actions = new ArrayList<>();
|
||||
|
||||
TimeValue expiryDuration = TimeValue.timeValueSeconds(2);
|
||||
String feature = "feature1";
|
||||
actions.add(registerWithTrialLicense(clientService, clientListener, feature, expiryDuration));
|
||||
actions.add(assertExpiryAction(feature, "trial", expiryDuration));
|
||||
|
||||
|
@ -182,8 +358,8 @@ public class LicensesClientServiceTests extends AbstractLicensesServiceTests {
|
|||
|
||||
TimeValue expiryDuration = TimeValue.timeValueSeconds(0);
|
||||
for (int i = 0; i < randomIntBetween(5, 10); i++) {
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener();
|
||||
String feature = "feature_" + String.valueOf(i);
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener(feature);
|
||||
expiryDuration = TimeValue.timeValueMillis(randomIntBetween(1, 3) * 1000l + expiryDuration.millis());
|
||||
List<Action> actions = new ArrayList<>();
|
||||
|
||||
|
@ -227,7 +403,7 @@ public class LicensesClientServiceTests extends AbstractLicensesServiceTests {
|
|||
return new Action(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clientService.register(feature, null, clientListener);
|
||||
clientService.register(feature, null, Collections.<LicensesService.ExpirationCallback>emptyList(), clientListener);
|
||||
}
|
||||
}, 0, 0, "should not trigger any notification [disabled by default]");
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ public class LicensesManagerServiceTests extends AbstractLicensesServiceTests {
|
|||
|
||||
// generate a trial license for one feature
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener(false);
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener("shield", false);
|
||||
registerWithTrialLicense(clientService, clientListener, "shield", TimeValue.timeValueHours(1)).run();
|
||||
|
||||
// generate signed licenses for multiple features
|
||||
|
|
|
@ -90,7 +90,7 @@ public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationT
|
|||
|
||||
logger.info(" --> check trial license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds);
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(getCurrentFeatureName());
|
||||
}
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ public class LicensesTransportTests extends AbstractLicensesIntegrationTests {
|
|||
|
||||
@Test
|
||||
public void testPutExpiredLicense() throws Exception {
|
||||
License expiredLicense = generateSignedLicense("expiredFeature", dateMath("now-10d/d", System.currentTimeMillis()), TimeValue.timeValueMinutes(2));
|
||||
License expiredLicense = generateSignedLicense("feature", dateMath("now-10d/d", System.currentTimeMillis()), TimeValue.timeValueMinutes(2));
|
||||
License signedLicense = generateSignedLicense("feature", TimeValue.timeValueMinutes(2));
|
||||
|
||||
PutLicenseRequestBuilder builder = new PutLicenseRequestBuilder(client().admin().cluster());
|
||||
|
|
|
@ -13,9 +13,13 @@ import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.gateway.GatewayService;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public abstract class TestPluginServiceBase extends AbstractLifecycleComponent<TestPluginServiceBase> implements ClusterStateListener {
|
||||
|
@ -29,6 +33,37 @@ public abstract class TestPluginServiceBase extends AbstractLifecycleComponent<T
|
|||
|
||||
final boolean eagerLicenseRegistration;
|
||||
|
||||
final static Collection<LicensesService.ExpirationCallback> expirationCallbacks;
|
||||
|
||||
static {
|
||||
// Callback triggered every 24 hours from 30 days to 7 days of license expiry
|
||||
final LicensesService.ExpirationCallback.Pre LEVEL_1 = new LicensesService.ExpirationCallback.Pre(TimeValue.timeValueHours(7 * 24), TimeValue.timeValueHours(30 * 24), TimeValue.timeValueHours(24)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
|
||||
// Callback triggered every 10 minutes from 7 days to license expiry
|
||||
final LicensesService.ExpirationCallback.Pre LEVEL_2 = new LicensesService.ExpirationCallback.Pre(null, TimeValue.timeValueHours(7 * 24), TimeValue.timeValueMinutes(10)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
|
||||
// Callback triggered every 10 minutes after license expiry
|
||||
final LicensesService.ExpirationCallback.Post LEVEL_3 = new LicensesService.ExpirationCallback.Post(TimeValue.timeValueMillis(0), null, TimeValue.timeValueMinutes(10)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
|
||||
expirationCallbacks = new ArrayList<>();
|
||||
expirationCallbacks.add(LEVEL_1);
|
||||
expirationCallbacks.add(LEVEL_2);
|
||||
expirationCallbacks.add(LEVEL_3);
|
||||
}
|
||||
|
||||
|
||||
public final AtomicBoolean registered = new AtomicBoolean(false);
|
||||
|
||||
private volatile AtomicBoolean enabled = new AtomicBoolean(false);
|
||||
|
@ -68,7 +103,7 @@ public abstract class TestPluginServiceBase extends AbstractLifecycleComponent<T
|
|||
if (registered.compareAndSet(false, true)) {
|
||||
logger.info("Registering to licensesService [lazy]");
|
||||
licensesClientService.register(featureName(),
|
||||
trialLicenseOptions,
|
||||
trialLicenseOptions, expirationCallbacks,
|
||||
new LicensingClientListener());
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +114,7 @@ public abstract class TestPluginServiceBase extends AbstractLifecycleComponent<T
|
|||
if (registered.compareAndSet(false, true)) {
|
||||
logger.info("Registering to licensesService [eager]");
|
||||
licensesClientService.register(featureName(),
|
||||
trialLicenseOptions,
|
||||
trialLicenseOptions, expirationCallbacks,
|
||||
new LicensingClientListener());
|
||||
}
|
||||
}
|
||||
|
@ -99,13 +134,18 @@ public abstract class TestPluginServiceBase extends AbstractLifecycleComponent<T
|
|||
private class LicensingClientListener implements LicensesClientService.Listener {
|
||||
|
||||
@Override
|
||||
public void onEnabled() {
|
||||
public void onEnabled(License license) {
|
||||
logger.info("TestConsumerPlugin: " + license.feature() + " enabled");
|
||||
enabled.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled() {
|
||||
public void onDisabled(License license) {
|
||||
if (license != null) {
|
||||
logger.info("TestConsumerPlugin: " + license.feature() + " disabled");
|
||||
}
|
||||
enabled.set(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue