LicensesService:
- Ensure license registration can be called through any stage of consumer plugin life cycle - Added randomized plugin integration tests - Run test on plugins with different license registration scheme Original commit: elastic/x-pack-elasticsearch@1cee85943d
This commit is contained in:
parent
6c116e2fb3
commit
d9d04fceaf
|
@ -18,6 +18,7 @@ import org.elasticsearch.common.collect.ImmutableMap;
|
|||
import org.elasticsearch.common.collect.ImmutableSet;
|
||||
import org.elasticsearch.common.collect.Sets;
|
||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||
import org.elasticsearch.common.component.Lifecycle;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.Singleton;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
|
@ -50,30 +51,30 @@ import static org.elasticsearch.license.core.ESLicenses.reduceAndMap;
|
|||
* - LicensesManagerService - responsible for managing signed and one-time-trial licenses
|
||||
* - LicensesClientService - responsible for feature registration and notification to consumer plugin(s)
|
||||
* <p/>
|
||||
*
|
||||
* <p/>
|
||||
* Notification Scheme:
|
||||
*
|
||||
* All registered feature(s) are notified using {@link #notifyFeatures(LicensesMetaData)} (depends on the current
|
||||
* {@link #registeredListeners}). It is idempotent with respect to all the feature listeners.
|
||||
*
|
||||
* The notification scheduling is done by {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} which does the following:
|
||||
* - calls {@link #notifyFeatures(LicensesMetaData)} to notify all registered feature(s)
|
||||
* - if there is any license(s) with a future expiry date in the current cluster state:
|
||||
* - schedules a delayed {@link LicensingClientNotificationJob} on the MIN of all the expiry dates of all the registered feature(s)
|
||||
*
|
||||
* 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}
|
||||
*
|
||||
* Upon successful registration of a new feature:
|
||||
* - {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} is called
|
||||
*
|
||||
* Upon clusterChanged():
|
||||
* - {@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}
|
||||
* <p/>
|
||||
* All registered feature(s) are notified using {@link #notifyFeatures(LicensesMetaData)} (depends on the current
|
||||
* {@link #registeredListeners}). It is idempotent with respect to all the feature listeners.
|
||||
* <p/>
|
||||
* The notification scheduling is done by {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} which does the following:
|
||||
* - calls {@link #notifyFeatures(LicensesMetaData)} to notify all registered feature(s)
|
||||
* - if there is any license(s) with a future expiry date in the current cluster state:
|
||||
* - schedules a delayed {@link LicensingClientNotificationJob} on the MIN of all the expiry dates of all the registered feature(s)
|
||||
* <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}
|
||||
* <p/>
|
||||
* Upon successful registration of a new feature:
|
||||
* - {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} is called
|
||||
* <p/>
|
||||
* Upon clusterChanged():
|
||||
* - {@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}
|
||||
*/
|
||||
@Singleton
|
||||
public class LicensesService extends AbstractLifecycleComponent<LicensesService> implements ClusterStateListener, LicensesManagerService, LicensesClientService {
|
||||
|
@ -378,46 +379,37 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
|
||||
/**
|
||||
* When there is no global block on {@link org.elasticsearch.gateway.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 #notifyFeaturesAndScheduleNotification(LicensesMetaData)}
|
||||
* - else calls {@link #notifyFeaturesAndScheduleNotificationIfNeeded(LicensesMetaData)}
|
||||
* - 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 #notifyFeaturesAndScheduleNotification(LicensesMetaData)}
|
||||
* - else calls {@link #notifyFeaturesAndScheduleNotificationIfNeeded(LicensesMetaData)}
|
||||
*/
|
||||
@Override
|
||||
public void clusterChanged(ClusterChangedEvent event) {
|
||||
final ClusterState currentClusterState = event.state();
|
||||
final ClusterState previousClusterState = event.previousState();
|
||||
final ClusterState currentClusterState = event.state();
|
||||
if (!currentClusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) {
|
||||
LicensesMetaData oldLicensesMetaData = previousClusterState.getMetaData().custom(LicensesMetaData.TYPE);
|
||||
LicensesMetaData currentLicensesMetaData = currentClusterState.getMetaData().custom(LicensesMetaData.TYPE);
|
||||
final LicensesMetaData oldLicensesMetaData = previousClusterState.getMetaData().custom(LicensesMetaData.TYPE);
|
||||
final LicensesMetaData currentLicensesMetaData = currentClusterState.getMetaData().custom(LicensesMetaData.TYPE);
|
||||
logLicenseMetaDataStats("old", oldLicensesMetaData);
|
||||
logLicenseMetaDataStats("new", currentLicensesMetaData);
|
||||
|
||||
// Check pending feature registrations and try to complete registrations
|
||||
boolean addedNewRegisteredListener = false;
|
||||
if (!pendingListeners.isEmpty()) {
|
||||
ListenerHolder pendingRegistrationLister;
|
||||
while ((pendingRegistrationLister = pendingListeners.poll()) != null) {
|
||||
boolean masterAvailable = registerListener(pendingRegistrationLister);
|
||||
logger.debug("trying to register pending listener for " + pendingRegistrationLister.feature + " masterAvailable: " + masterAvailable);
|
||||
ListenerHolder pendingRegistrationListener;
|
||||
while ((pendingRegistrationListener = pendingListeners.poll()) != null) {
|
||||
boolean masterAvailable = registerListener(pendingRegistrationListener);
|
||||
logger.debug("trying to register pending listener for " + pendingRegistrationListener.feature + " masterAvailable: " + masterAvailable);
|
||||
if (!masterAvailable) {
|
||||
// if the master is not available do not, break out of trying pendingListeners
|
||||
pendingListeners.add(pendingRegistrationLister);
|
||||
pendingListeners.add(pendingRegistrationListener);
|
||||
break;
|
||||
} else {
|
||||
logger.debug("successfully registered listener for: " + pendingRegistrationLister.feature);
|
||||
registeredListeners.add(pendingRegistrationLister);
|
||||
// make sure to notify new registered feature
|
||||
// notifications could have been scheduled for it before it was registered
|
||||
addedNewRegisteredListener = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// notify all interested plugins
|
||||
// Change to debug
|
||||
if (previousClusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK) || addedNewRegisteredListener) {
|
||||
if (previousClusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) {
|
||||
logger.debug("calling notifyFeaturesAndScheduleNotification from clusterChanged");
|
||||
notifyFeaturesAndScheduleNotification(currentLicensesMetaData);
|
||||
} else {
|
||||
|
@ -433,7 +425,7 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
* Calls {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} with <code>currentLicensesMetaData</code>
|
||||
* if it was not already notified on
|
||||
*/
|
||||
private void notifyFeaturesAndScheduleNotificationIfNeeded(LicensesMetaData currentLicensesMetaData) {
|
||||
private void notifyFeaturesAndScheduleNotificationIfNeeded(final LicensesMetaData currentLicensesMetaData) {
|
||||
final LicensesMetaData lastNotifiedLicensesMetaData = lastObservedLicensesState.get();
|
||||
if (lastNotifiedLicensesMetaData != null && lastNotifiedLicensesMetaData.equals(currentLicensesMetaData)) {
|
||||
logger.debug("currentLicensesMetaData has been already notified on");
|
||||
|
@ -446,7 +438,7 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
* Calls {@link #notifyFeatures(LicensesMetaData)} with <code>currentLicensesMetaData</code>
|
||||
* and schedules the earliest expiry (if any) notification for registered feature(s)
|
||||
*/
|
||||
private void notifyFeaturesAndScheduleNotification(LicensesMetaData currentLicensesMetaData) {
|
||||
private void notifyFeaturesAndScheduleNotification(final LicensesMetaData currentLicensesMetaData) {
|
||||
long nextScheduleFrequency = notifyFeatures(currentLicensesMetaData);
|
||||
if (nextScheduleFrequency != -1l) {
|
||||
scheduleNextNotification(nextScheduleFrequency);
|
||||
|
@ -463,7 +455,7 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
* returns the minimum of the expiry times of all the registered feature(s) to
|
||||
* schedule an expiry notification
|
||||
*/
|
||||
private long notifyFeatures(LicensesMetaData currentLicensesMetaData) {
|
||||
private long notifyFeatures(final LicensesMetaData currentLicensesMetaData) {
|
||||
long nextScheduleFrequency = -1l;
|
||||
for (ListenerHolder listenerHolder : registeredListeners) {
|
||||
long expiryDate = expiryDateForFeature(listenerHolder.feature, currentLicensesMetaData);
|
||||
|
@ -496,8 +488,6 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
}
|
||||
}
|
||||
|
||||
lastObservedLicensesState.set(currentLicensesMetaData);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logLicenseMetaDataStats("Setting last observed metaData", currentLicensesMetaData);
|
||||
if (nextScheduleFrequency == -1l) {
|
||||
|
@ -507,9 +497,11 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
}
|
||||
}
|
||||
|
||||
lastObservedLicensesState.set(currentLicensesMetaData);
|
||||
return nextScheduleFrequency;
|
||||
|
||||
}
|
||||
|
||||
private void logLicenseMetaDataStats(String prefix, LicensesMetaData licensesMetaData) {
|
||||
if (licensesMetaData != null) {
|
||||
logger.debug(prefix + " LicensesMetaData: signedLicenses: " + licensesMetaData.getSignatures().size() + " trialLicenses: " + licensesMetaData.getEncodedTrialLicenses().size());
|
||||
|
@ -524,23 +516,33 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
@Override
|
||||
public void register(String feature, TrialLicenseOptions trialLicenseOptions, Listener listener) {
|
||||
final ListenerHolder listenerHolder = new ListenerHolder(feature, trialLicenseOptions, listener);
|
||||
logger.debug("add listener for: " + listenerHolder.feature + " to pending registration queue");
|
||||
pendingListeners.add(listenerHolder);
|
||||
// don't trust the clusterState for blocks just yet!
|
||||
final Lifecycle.State state = clusterService.lifecycleState();
|
||||
if (state != Lifecycle.State.STARTED) {
|
||||
pendingListeners.add(listenerHolder);
|
||||
} else {
|
||||
if (!registerListener(listenerHolder)) {
|
||||
pendingListeners.add(listenerHolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies new feature listener if it already has a signed license
|
||||
* if new feature has a non-null trial license option, a master node request is made to generate the trial license
|
||||
* if no trial license option is specified for the feature and no signed license is found,
|
||||
* then notifies features to be disabled
|
||||
* then notifies features to be disabled
|
||||
* then notifies features if needed
|
||||
*
|
||||
* @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
|
||||
* @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}
|
||||
*/
|
||||
private boolean registerListener(final ListenerHolder listenerHolder) {
|
||||
logger.debug("Registering listener for " + listenerHolder.feature);
|
||||
ClusterState currentState = clusterService.state();
|
||||
final ClusterState currentState = clusterService.state();
|
||||
if (currentState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LicensesMetaData currentMetaData = currentState.metaData().custom(LicensesMetaData.TYPE);
|
||||
if (expiryDateForFeature(listenerHolder.feature, currentMetaData) == -1l) {
|
||||
// does not have any license so generate a trial license
|
||||
|
@ -564,17 +566,20 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
return false;
|
||||
}
|
||||
}
|
||||
registeredListeners.add(listenerHolder);
|
||||
} else {
|
||||
// notify feature as clusterChangedEvent may not happen
|
||||
// as no trial or signed license has been found for feature
|
||||
// Change to debug
|
||||
logger.debug("Calling notifyFeaturesAndScheduleNotification [no trial license spec provided]");
|
||||
registeredListeners.add(listenerHolder);
|
||||
notifyFeaturesAndScheduleNotification(currentMetaData);
|
||||
}
|
||||
} else {
|
||||
// signed license already found for the new registered
|
||||
// feature, notify feature on registration
|
||||
logger.debug("Calling notifyFeaturesAndScheduleNotification [signed/trial license available]");
|
||||
registeredListeners.add(listenerHolder);
|
||||
notifyFeaturesAndScheduleNotification(currentMetaData);
|
||||
}
|
||||
return true;
|
||||
|
@ -584,8 +589,6 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
final Map<String, ESLicense> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
ESLicense featureLicense;
|
||||
if ((featureLicense = effectiveLicenses.get(feature)) != null) {
|
||||
logger.debug("effective license for " + feature + " relative expiry: " +
|
||||
TimeValue.timeValueMillis(effectiveLicenses.get(feature).expiryDate() - System.currentTimeMillis()));
|
||||
return featureLicense.expiryDate();
|
||||
}
|
||||
logger.debug("no effective license for " + feature);
|
||||
|
@ -648,7 +651,8 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
*/
|
||||
private class LicensingClientNotificationJob implements Runnable {
|
||||
|
||||
public LicensingClientNotificationJob() {}
|
||||
public LicensingClientNotificationJob() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -766,7 +770,8 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
|||
private String feature;
|
||||
private TimeValue duration;
|
||||
|
||||
private RegisterTrialLicenseRequest() {}
|
||||
private RegisterTrialLicenseRequest() {
|
||||
}
|
||||
|
||||
private RegisterTrialLicenseRequest(String feature, TimeValue duration, int maxNodes) {
|
||||
this.maxNodes = maxNodes;
|
||||
|
|
|
@ -16,14 +16,13 @@ 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.consumer.TestPluginService1;
|
||||
import org.elasticsearch.license.plugin.consumer.TestPluginService2;
|
||||
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.test.ElasticsearchIntegrationTest;
|
||||
import org.elasticsearch.test.InternalTestCluster;
|
||||
import org.hamcrest.Matchers;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
@ -115,30 +114,30 @@ public abstract class AbstractLicensesIntegrationTests extends ElasticsearchInte
|
|||
}, 2, TimeUnit.SECONDS), equalTo(true));
|
||||
}
|
||||
|
||||
protected void assertConsumerPlugin1DisableNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertConsumerPlugin1Notification(false, timeoutInSec);
|
||||
protected void assertEagerConsumerPluginDisableNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertEagerConsumerPluginNotification(false, timeoutInSec);
|
||||
}
|
||||
|
||||
protected void assertConsumerPlugin1EnableNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertConsumerPlugin1Notification(true, timeoutInSec);
|
||||
protected void assertEagerConsumerPluginEnableNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertEagerConsumerPluginNotification(true, timeoutInSec);
|
||||
}
|
||||
|
||||
protected void assertConsumerPlugin2DisableNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertConsumerPlugin2Notification(false, timeoutInSec);
|
||||
protected void assertLazyConsumerPluginDisableNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertLazyConsumerPluginNotification(false, timeoutInSec);
|
||||
}
|
||||
|
||||
protected void assertConsumerPlugin2EnableNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertConsumerPlugin2Notification(true, timeoutInSec);
|
||||
protected void assertLazyConsumerPluginEnableNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertLazyConsumerPluginNotification(true, timeoutInSec);
|
||||
}
|
||||
|
||||
protected void assertConsumerPlugin2Notification(final boolean expectedEnabled, int timeoutInSec) throws InterruptedException {
|
||||
final List<TestPluginServiceBase> consumerPluginServices = consumerPlugin2Services();
|
||||
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);
|
||||
}
|
||||
|
||||
protected void assertConsumerPlugin1Notification(final boolean expectedEnabled, int timeoutInSec) throws InterruptedException {
|
||||
final List<TestPluginServiceBase> consumerPluginServices = consumerPlugin1Services();
|
||||
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);
|
||||
}
|
||||
|
@ -158,19 +157,19 @@ public abstract class AbstractLicensesIntegrationTests extends ElasticsearchInte
|
|||
|
||||
}
|
||||
|
||||
private List<TestPluginServiceBase> consumerPlugin2Services() {
|
||||
private List<TestPluginServiceBase> consumerLazyPluginServices() {
|
||||
final InternalTestCluster clients = internalCluster();
|
||||
List<TestPluginServiceBase> consumerPluginServices = new ArrayList<>();
|
||||
for (TestPluginServiceBase service : clients.getDataNodeInstances(TestPluginService2.class)) {
|
||||
for (TestPluginServiceBase service : clients.getDataNodeInstances(LazyLicenseRegistrationPluginService.class)) {
|
||||
consumerPluginServices.add(service);
|
||||
}
|
||||
return consumerPluginServices;
|
||||
}
|
||||
|
||||
private List<TestPluginServiceBase> consumerPlugin1Services() {
|
||||
private List<TestPluginServiceBase> consumerEagerPluginServices() {
|
||||
final InternalTestCluster clients = internalCluster();
|
||||
List<TestPluginServiceBase> consumerPluginServices = new ArrayList<>();
|
||||
for (TestPluginServiceBase service : clients.getDataNodeInstances(TestPluginService1.class)) {
|
||||
for (TestPluginServiceBase service : clients.getDataNodeInstances(EagerLicenseRegistrationPluginService.class)) {
|
||||
consumerPluginServices.add(service);
|
||||
}
|
||||
return consumerPluginServices;
|
||||
|
|
|
@ -5,15 +5,19 @@
|
|||
*/
|
||||
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.TestConsumerPlugin1;
|
||||
import org.elasticsearch.license.plugin.consumer.TestPluginService1;
|
||||
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;
|
||||
|
@ -25,100 +29,134 @@ import static org.hamcrest.CoreMatchers.equalTo;
|
|||
@ClusterScope(scope = TEST, numDataNodes = 10, numClientNodes = 0)
|
||||
public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
private final int trialLicenseDurationInSeconds = 2;
|
||||
private final boolean useEagerLicenseRegistrationPlugin = randomBoolean();
|
||||
|
||||
private final String FEATURE_NAME = TestPluginService1.FEATURE_NAME;
|
||||
private final int trialLicenseDurationInSeconds = 2;
|
||||
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
return ImmutableSettings.settingsBuilder()
|
||||
.put(super.nodeSettings(nodeOrdinal))
|
||||
.put(TestConsumerPlugin1.NAME + ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds)
|
||||
.put("plugin.types", LicensePlugin.class.getName() + "," + TestConsumerPlugin1.class.getName())
|
||||
.put(((useEagerLicenseRegistrationPlugin) ? EagerLicenseRegistrationConsumerPlugin.NAME : LazyLicenseRegistrationConsumerPlugin.NAME)
|
||||
+ ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(),
|
||||
(useEagerLicenseRegistrationPlugin) ? EagerLicenseRegistrationConsumerPlugin.class.getName() : LazyLicenseRegistrationConsumerPlugin.class.getName())
|
||||
.build();
|
||||
}
|
||||
|
||||
@After
|
||||
public void beforeTest() throws Exception {
|
||||
public void afterTest() throws Exception {
|
||||
wipeAllLicenses();
|
||||
assertThat(awaitBusy(new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object o) {
|
||||
return !clusterService().state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK);
|
||||
}
|
||||
}), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrialLicenseAndSignedLicenseNotification() throws Exception {
|
||||
logger.info("using "+ ((useEagerLicenseRegistrationPlugin) ? "eager" : "lazy") + " consumer plugin");
|
||||
logger.info(" --> trial license generated");
|
||||
// managerService should report feature to be enabled on all data nodes
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME);
|
||||
assertLicenseManagerEnabledFeatureFor(getCurrentFeatureName());
|
||||
// consumer plugin service should return enabled on all data nodes
|
||||
assertConsumerPlugin1EnableNotification(1);
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
|
||||
logger.info(" --> check trial license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired trial license)
|
||||
assertConsumerPlugin1DisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME);
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(getCurrentFeatureName());
|
||||
|
||||
logger.info(" --> put signed license");
|
||||
ESLicense license = generateSignedLicense(FEATURE_NAME, TimeValue.timeValueSeconds(trialLicenseDurationInSeconds));
|
||||
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));
|
||||
|
||||
logger.info(" --> check signed license enabled notification");
|
||||
// consumer plugin should notify onEnabled on all data nodes (signed license)
|
||||
assertConsumerPlugin1EnableNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME);
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(getCurrentFeatureName());
|
||||
|
||||
logger.info(" --> check signed license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertConsumerPlugin1DisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME);
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(getCurrentFeatureName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrialLicenseNotification() throws Exception {
|
||||
logger.info(" --> check onEnabled for trial license");
|
||||
// managerService should report feature to be enabled on all data nodes
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME);
|
||||
assertLicenseManagerEnabledFeatureFor(getCurrentFeatureName());
|
||||
// consumer plugin service should return enabled on all data nodes
|
||||
assertConsumerPlugin1EnableNotification(1);
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
|
||||
logger.info(" --> sleep for rest of trailLicense duration");
|
||||
Thread.sleep(trialLicenseDurationInSeconds * 1000l);
|
||||
|
||||
logger.info(" --> check trial license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertConsumerPlugin1DisableNotification(trialLicenseDurationInSeconds);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME);
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds);
|
||||
assertLicenseManagerDisabledFeatureFor(getCurrentFeatureName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverlappingTrialAndSignedLicenseNotification() throws Exception {
|
||||
logger.info(" --> check onEnabled for trial license");
|
||||
// managerService should report feature to be enabled on all data nodes
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME);
|
||||
assertLicenseManagerEnabledFeatureFor(getCurrentFeatureName());
|
||||
// consumer plugin service should return enabled on all data nodes
|
||||
assertConsumerPlugin1EnableNotification(1);
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
|
||||
logger.info(" --> put signed license while trial license is in effect");
|
||||
ESLicense license = generateSignedLicense(FEATURE_NAME, TimeValue.timeValueSeconds(trialLicenseDurationInSeconds * 2));
|
||||
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));
|
||||
|
||||
logger.info(" --> check signed license enabled notification");
|
||||
// consumer plugin should notify onEnabled on all data nodes (signed license)
|
||||
assertConsumerPlugin1EnableNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME);
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(getCurrentFeatureName());
|
||||
|
||||
logger.info(" --> sleep for rest of trailLicense duration");
|
||||
Thread.sleep(trialLicenseDurationInSeconds * 1000l);
|
||||
|
||||
logger.info(" --> check consumer is still enabled [signed license]");
|
||||
// consumer plugin should notify onEnabled on all data nodes (signed license)
|
||||
assertConsumerPlugin1EnableNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME);
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(getCurrentFeatureName());
|
||||
|
||||
logger.info(" --> check signed license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertConsumerPlugin1DisableNotification(trialLicenseDurationInSeconds * 2 * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME);
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2 * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(getCurrentFeatureName());
|
||||
}
|
||||
|
||||
private String getCurrentFeatureName() {
|
||||
if (useEagerLicenseRegistrationPlugin) {
|
||||
return EagerLicenseRegistrationPluginService.FEATURE_NAME;
|
||||
} else {
|
||||
return LazyLicenseRegistrationPluginService.FEATURE_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
private void assertConsumerPluginEnabledNotification(int timeoutInSec) throws InterruptedException {
|
||||
if (useEagerLicenseRegistrationPlugin) {
|
||||
assertEagerConsumerPluginEnableNotification(timeoutInSec);
|
||||
} else {
|
||||
assertLazyConsumerPluginEnableNotification(timeoutInSec);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertConsumerPluginDisabledNotification(int timeoutInSec) throws InterruptedException {
|
||||
if (useEagerLicenseRegistrationPlugin) {
|
||||
assertEagerConsumerPluginDisableNotification(timeoutInSec);
|
||||
} else {
|
||||
assertLazyConsumerPluginDisableNotification(timeoutInSec);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,17 +5,19 @@
|
|||
*/
|
||||
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.action.put.PutLicenseRequestBuilder;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseResponse;
|
||||
import org.elasticsearch.license.plugin.consumer.TestConsumerPlugin1;
|
||||
import org.elasticsearch.license.plugin.consumer.TestConsumerPlugin2;
|
||||
import org.elasticsearch.license.plugin.consumer.TestPluginService1;
|
||||
import org.elasticsearch.license.plugin.consumer.TestPluginService2;
|
||||
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;
|
||||
|
@ -24,29 +26,30 @@ import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
|||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
|
||||
//@Ignore
|
||||
@ClusterScope(scope = TEST, numDataNodes = 0, numClientNodes = 0)
|
||||
public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
private final int trialLicenseDurationInSeconds = 2;
|
||||
|
||||
private final String FEATURE_NAME_1 = TestPluginService1.FEATURE_NAME;
|
||||
private final String FEATURE_NAME_2 = TestPluginService2.FEATURE_NAME;
|
||||
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(TestConsumerPlugin1.NAME + ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds)
|
||||
.put(TestConsumerPlugin2.NAME + ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), TestConsumerPlugin1.class.getName(), TestConsumerPlugin2.class.getName())
|
||||
.put(EagerLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds)
|
||||
.put(LazyLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), EagerLicenseRegistrationConsumerPlugin.class.getName(), LazyLicenseRegistrationConsumerPlugin.class.getName())
|
||||
.build();
|
||||
}
|
||||
|
||||
private Settings nodeSettingsWithConsumerPlugin(int consumer1TrialLicenseDuration, int consumer2TrialLicenseDuration) {
|
||||
return ImmutableSettings.settingsBuilder()
|
||||
.put(super.nodeSettings(0))
|
||||
.put(TestConsumerPlugin1.NAME + ".trial_license_duration_in_seconds", consumer1TrialLicenseDuration)
|
||||
.put(TestConsumerPlugin2.NAME + ".trial_license_duration_in_seconds", consumer2TrialLicenseDuration)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), TestConsumerPlugin1.class.getName(), TestConsumerPlugin2.class.getName())
|
||||
.put(EagerLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", consumer1TrialLicenseDuration)
|
||||
.put(LazyLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", consumer2TrialLicenseDuration)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), EagerLicenseRegistrationConsumerPlugin.class.getName(), LazyLicenseRegistrationConsumerPlugin.class.getName())
|
||||
.build();
|
||||
|
||||
}
|
||||
|
@ -61,8 +64,8 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration
|
|||
int nNodes = randomIntBetween(2, 10);
|
||||
String[] nodes = startNodesWithConsumerPlugins(nNodes, -1, -1);
|
||||
|
||||
assertConsumerPlugin1DisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertConsumerPlugin2DisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertEagerConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLazyConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2);
|
||||
}
|
||||
|
@ -78,8 +81,8 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration
|
|||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_2);
|
||||
// consumer plugin service should return enabled on all data nodes
|
||||
assertConsumerPlugin1DisableNotification(1);
|
||||
assertConsumerPlugin2EnableNotification(1);
|
||||
assertEagerConsumerPluginDisableNotification(1);
|
||||
assertLazyConsumerPluginEnableNotification(1);
|
||||
|
||||
logger.info(" --> put signed license for " + FEATURE_NAME_1);
|
||||
ESLicense license1 = generateSignedLicense(FEATURE_NAME_1, TimeValue.timeValueSeconds(consumer2TrialLicenseDuration));
|
||||
|
@ -88,15 +91,15 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration
|
|||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
|
||||
logger.info(" --> check that both " + FEATURE_NAME_1 + " and " + FEATURE_NAME_2 + " are enabled");
|
||||
assertConsumerPlugin1EnableNotification(1);
|
||||
assertConsumerPlugin2EnableNotification(1);
|
||||
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)
|
||||
assertConsumerPlugin1DisableNotification(consumer2TrialLicenseDuration * 2);
|
||||
assertConsumerPlugin2DisableNotification(consumer2TrialLicenseDuration * 2);
|
||||
assertEagerConsumerPluginDisableNotification(consumer2TrialLicenseDuration * 2);
|
||||
assertLazyConsumerPluginDisableNotification(consumer2TrialLicenseDuration * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2);
|
||||
}
|
||||
|
@ -106,19 +109,30 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration
|
|||
|
||||
int nNodes = randomIntBetween(2, 10);
|
||||
String[] nodes = startNodesWithConsumerPlugins(nNodes, trialLicenseDurationInSeconds, trialLicenseDurationInSeconds);
|
||||
|
||||
waitUntilClusterRecovered();
|
||||
assertThat(awaitBusy(new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object o) {
|
||||
for (LazyLicenseRegistrationPluginService service : internalCluster().getDataNodeInstances(LazyLicenseRegistrationPluginService.class)) {
|
||||
if (!service.registered.get()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}), equalTo(true));
|
||||
logger.info(" --> trial license generated");
|
||||
// managerService should report feature to be enabled on all data nodes
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_2);
|
||||
// consumer plugin service should return enabled on all data nodes
|
||||
assertConsumerPlugin1EnableNotification(1);
|
||||
assertConsumerPlugin2EnableNotification(1);
|
||||
assertEagerConsumerPluginEnableNotification(1);
|
||||
assertLazyConsumerPluginEnableNotification(1);
|
||||
|
||||
logger.info(" --> check trial license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired trial license)
|
||||
assertConsumerPlugin1DisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertConsumerPlugin2DisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertEagerConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLazyConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2);
|
||||
|
||||
|
@ -128,15 +142,15 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration
|
|||
|
||||
logger.info(" --> check signed license enabled notification");
|
||||
// consumer plugin should notify onEnabled on all data nodes (signed license)
|
||||
assertConsumerPlugin1EnableNotification(1);
|
||||
assertConsumerPlugin2EnableNotification(1);
|
||||
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)
|
||||
assertConsumerPlugin1DisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertConsumerPlugin2DisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertEagerConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLazyConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2);
|
||||
}
|
||||
|
@ -150,33 +164,33 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration
|
|||
String[] nodes = startNodesWithConsumerPlugins(nNodes, trialLicenseDuration1, trialLicenseDuration2);
|
||||
|
||||
if (trialLicenseDuration1 != -1) {
|
||||
assertConsumerPlugin1EnableNotification(1);
|
||||
assertEagerConsumerPluginEnableNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_1);
|
||||
} else {
|
||||
assertConsumerPlugin1DisableNotification(1);
|
||||
assertEagerConsumerPluginDisableNotification(1);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
putLicense(FEATURE_NAME_1, TimeValue.timeValueMillis(300 * 2));
|
||||
}
|
||||
|
||||
if (trialLicenseDuration2 != -1) {
|
||||
assertConsumerPlugin2EnableNotification(1);
|
||||
assertLazyConsumerPluginEnableNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_2);
|
||||
} else {
|
||||
assertConsumerPlugin2DisableNotification(1);
|
||||
assertLazyConsumerPluginDisableNotification(1);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2);
|
||||
putLicense(FEATURE_NAME_2, TimeValue.timeValueMillis(300 * 2));
|
||||
}
|
||||
|
||||
logger.info(" --> check license enabled notification");
|
||||
assertConsumerPlugin1EnableNotification(1);
|
||||
assertConsumerPlugin2EnableNotification(1);
|
||||
assertEagerConsumerPluginEnableNotification(1);
|
||||
assertLazyConsumerPluginEnableNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_2);
|
||||
|
||||
logger.info(" --> check license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertConsumerPlugin1DisableNotification(2 * 2);
|
||||
assertConsumerPlugin2DisableNotification(2 * 2);
|
||||
assertEagerConsumerPluginDisableNotification(2 * 2);
|
||||
assertLazyConsumerPluginDisableNotification(2 * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2);
|
||||
|
||||
|
@ -196,4 +210,13 @@ public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegration
|
|||
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
}
|
||||
|
||||
private void waitUntilClusterRecovered() throws InterruptedException {
|
||||
assertThat(awaitBusy(new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object o) {
|
||||
return !clusterService().state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK);
|
||||
}
|
||||
}), equalTo(true));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,22 +6,17 @@
|
|||
package org.elasticsearch.license.plugin;
|
||||
|
||||
import org.elasticsearch.client.ClusterAdminClient;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||
import org.elasticsearch.common.base.Predicate;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.discovery.DiscoverySettings;
|
||||
import org.elasticsearch.gateway.GatewayService;
|
||||
import org.elasticsearch.license.TestUtils;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
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.consumer.TestConsumerPlugin1;
|
||||
import org.elasticsearch.license.plugin.consumer.TestPluginService1;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesStatus;
|
||||
import org.elasticsearch.node.internal.InternalNode;
|
||||
import org.junit.Test;
|
||||
|
@ -37,7 +32,7 @@ import static org.hamcrest.CoreMatchers.equalTo;
|
|||
@ClusterScope(scope = TEST, numDataNodes = 0, numClientNodes = 0, maxNumDataNodes = 0, transportClientRatio = 0)
|
||||
public class LicensesServiceClusterTest extends AbstractLicensesIntegrationTests {
|
||||
|
||||
private final String FEATURE_NAME = TestPluginService1.FEATURE_NAME;
|
||||
private final String FEATURE_NAME = EagerLicenseRegistrationPluginService.FEATURE_NAME;
|
||||
|
||||
private final int trialLicenseDurationInSeconds = 2;
|
||||
|
||||
|
@ -57,8 +52,8 @@ public class LicensesServiceClusterTest extends AbstractLicensesIntegrationTests
|
|||
.put("plugins.load_classpath_plugins", false)
|
||||
.put("node.data", true)
|
||||
.put("format", "json")
|
||||
.put(TestConsumerPlugin1.NAME + ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), TestConsumerPlugin1.class.getName())
|
||||
.put(EagerLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), EagerLicenseRegistrationConsumerPlugin.class.getName())
|
||||
.put(InternalNode.HTTP_ENABLED, true);
|
||||
}
|
||||
|
||||
|
@ -91,12 +86,12 @@ public class LicensesServiceClusterTest extends AbstractLicensesIntegrationTests
|
|||
logger.info("--> start one master out of two [recovery state]");
|
||||
internalCluster().startNode(nodeSettingsBuilder(0).put("discovery.zen.minimum_master_nodes", 2).put("node.master", true));
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME);
|
||||
assertConsumerPlugin1DisableNotification(1);
|
||||
assertEagerConsumerPluginDisableNotification(1);
|
||||
|
||||
logger.info("--> start second master out of two [recovered state]");
|
||||
internalCluster().startNode(nodeSettingsBuilder(1).put("discovery.zen.minimum_master_nodes", 2).put("node.master", true));
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME);
|
||||
assertConsumerPlugin1EnableNotification(1);
|
||||
assertEagerConsumerPluginEnableNotification(1);
|
||||
}
|
||||
|
||||
private List<ESLicense> generateAndPutLicense() throws Exception {
|
||||
|
|
|
@ -8,8 +8,8 @@ package org.elasticsearch.license.plugin;
|
|||
import org.elasticsearch.common.base.Predicate;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.consumer.TestConsumerPlugin1;
|
||||
import org.elasticsearch.license.plugin.consumer.TestPluginService1;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService;
|
||||
import org.elasticsearch.node.internal.InternalNode;
|
||||
import org.elasticsearch.test.ElasticsearchIntegrationTest;
|
||||
import org.elasticsearch.test.junit.annotations.TestLogging;
|
||||
|
@ -29,8 +29,8 @@ public class LicensesServiceNodeTests extends AbstractLicensesIntegrationTests {
|
|||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
return ImmutableSettings.settingsBuilder()
|
||||
.put(super.nodeSettings(nodeOrdinal))
|
||||
.put(TestConsumerPlugin1.NAME + ".trial_license_duration_in_seconds", 5)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), TestConsumerPlugin1.class.getName())
|
||||
.put(EagerLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", 5)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), EagerLicenseRegistrationConsumerPlugin.class.getName())
|
||||
.put(InternalNode.HTTP_ENABLED, true)
|
||||
.build();
|
||||
}
|
||||
|
@ -38,11 +38,11 @@ public class LicensesServiceNodeTests extends AbstractLicensesIntegrationTests {
|
|||
@Test
|
||||
@TestLogging("_root:DEBUG")
|
||||
public void testPluginStatus() throws Exception {
|
||||
final Iterable<TestPluginService1> testPluginServices = internalCluster().getDataNodeInstances(TestPluginService1.class);
|
||||
final Iterable<EagerLicenseRegistrationPluginService> testPluginServices = internalCluster().getDataNodeInstances(EagerLicenseRegistrationPluginService.class);
|
||||
assertThat(awaitBusy(new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object o) {
|
||||
for (TestPluginService1 pluginService : testPluginServices) {
|
||||
for (EagerLicenseRegistrationPluginService pluginService : testPluginServices) {
|
||||
if (!pluginService.enabled()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.elasticsearch.cluster.node.DiscoveryNodes;
|
|||
import org.elasticsearch.common.base.Predicate;
|
||||
import org.elasticsearch.common.collect.ImmutableSet;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.gateway.GatewayService;
|
||||
import org.elasticsearch.license.TestUtils;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.manager.ESLicenseManager;
|
||||
|
@ -224,13 +225,13 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRandomMultipleClientMultipleFeature() throws Exception {
|
||||
public void testRandomActionSequenceMultipleFeature() throws Exception {
|
||||
LicensesService licensesService = randomLicensesService();
|
||||
LicensesManagerService masterLicensesManagerService = masterLicensesManagerService();
|
||||
Map<TestTrackingClientListener, List<Action>> clientListenersWithActions = new HashMap<>();
|
||||
|
||||
TimeValue expiryDuration = TimeValue.timeValueSeconds(0);
|
||||
for (int i = 0; i < randomIntBetween(3, 10); i++) {
|
||||
for (int i = 0; i < randomIntBetween(10, 20); i++) {
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener();
|
||||
String feature = randomRealisticUnicodeOfCodepointLengthBetween(2, 10);
|
||||
expiryDuration = TimeValue.timeValueMillis(randomIntBetween(1, 5) * 100l + expiryDuration.millis());
|
||||
|
@ -313,6 +314,17 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
|
|||
|
||||
// invoke clusterChanged event to flush out pendingRegistration
|
||||
LicensesService licensesService = (LicensesService) clientService;
|
||||
/*
|
||||
try {
|
||||
assertThat(awaitBusy(new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object o) {
|
||||
return !clusterService().state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK);
|
||||
}
|
||||
}), equalTo(true));
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Exception while trying to registerWithTrialLicense", e);
|
||||
}*/
|
||||
ClusterChangedEvent event = new ClusterChangedEvent("", clusterService().state(), clusterService().state());
|
||||
licensesService.clusterChanged(event);
|
||||
}
|
||||
|
|
|
@ -9,18 +9,24 @@ import org.elasticsearch.common.component.LifecycleComponent;
|
|||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
||||
public class TestConsumerPlugin1 extends TestConsumerPluginBase {
|
||||
/**
|
||||
* Registers licenses upon the start of the service lifecycle
|
||||
* see {@link org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService}
|
||||
*
|
||||
* License registration might happen before clusterService start()
|
||||
*/
|
||||
public class EagerLicenseRegistrationConsumerPlugin extends TestConsumerPluginBase {
|
||||
|
||||
public final static String NAME = "test_consumer_plugin_1";
|
||||
|
||||
@Inject
|
||||
public TestConsumerPlugin1(Settings settings) {
|
||||
public EagerLicenseRegistrationConsumerPlugin(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends LifecycleComponent> service() {
|
||||
return TestPluginService1.class;
|
||||
return EagerLicenseRegistrationPluginService.class;
|
||||
}
|
||||
|
||||
@Override
|
|
@ -11,13 +11,13 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
|
||||
@Singleton
|
||||
public class TestPluginService1 extends TestPluginServiceBase {
|
||||
public class EagerLicenseRegistrationPluginService extends TestPluginServiceBase {
|
||||
|
||||
public static String FEATURE_NAME = "feature1";
|
||||
|
||||
@Inject
|
||||
public TestPluginService1(Settings settings, LicensesClientService licensesClientService) {
|
||||
super(settings, licensesClientService);
|
||||
public EagerLicenseRegistrationPluginService(Settings settings, LicensesClientService licensesClientService) {
|
||||
super(true, settings, licensesClientService, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -27,6 +27,6 @@ public class TestPluginService1 extends TestPluginServiceBase {
|
|||
|
||||
@Override
|
||||
public String settingPrefix() {
|
||||
return TestConsumerPlugin1.NAME;
|
||||
return EagerLicenseRegistrationConsumerPlugin.NAME;
|
||||
}
|
||||
}
|
|
@ -9,18 +9,24 @@ import org.elasticsearch.common.component.LifecycleComponent;
|
|||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
||||
public class TestConsumerPlugin2 extends TestConsumerPluginBase {
|
||||
/**
|
||||
* Registers licenses only after cluster has recovered
|
||||
* see {@link org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationPluginService}
|
||||
*
|
||||
* License registration happens after clusterservice start()
|
||||
*/
|
||||
public class LazyLicenseRegistrationConsumerPlugin extends TestConsumerPluginBase {
|
||||
|
||||
public static String NAME = "test_consumer_plugin_2";
|
||||
|
||||
@Inject
|
||||
public TestConsumerPlugin2(Settings settings) {
|
||||
public LazyLicenseRegistrationConsumerPlugin(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends LifecycleComponent> service() {
|
||||
return TestPluginService2.class;
|
||||
return LazyLicenseRegistrationPluginService.class;
|
||||
}
|
||||
|
||||
@Override
|
|
@ -5,20 +5,21 @@
|
|||
*/
|
||||
package org.elasticsearch.license.plugin.consumer;
|
||||
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.Singleton;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
|
||||
@Singleton
|
||||
public class TestPluginService2 extends TestPluginServiceBase {
|
||||
public class LazyLicenseRegistrationPluginService extends TestPluginServiceBase {
|
||||
|
||||
|
||||
public static String FEATURE_NAME = "feature2";
|
||||
|
||||
@Inject
|
||||
public TestPluginService2(Settings settings, LicensesClientService licensesClientService) {
|
||||
super(settings, licensesClientService);
|
||||
public LazyLicenseRegistrationPluginService(Settings settings, LicensesClientService licensesClientService, ClusterService clusterService) {
|
||||
super(false, settings, licensesClientService, clusterService);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -28,6 +29,6 @@ public class TestPluginService2 extends TestPluginServiceBase {
|
|||
|
||||
@Override
|
||||
public String settingPrefix() {
|
||||
return TestConsumerPlugin2.NAME;
|
||||
return LazyLicenseRegistrationConsumerPlugin.NAME;
|
||||
}
|
||||
}
|
|
@ -6,26 +6,36 @@
|
|||
package org.elasticsearch.license.plugin.consumer;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.cluster.ClusterChangedEvent;
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
import org.elasticsearch.cluster.ClusterStateListener;
|
||||
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.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public abstract class TestPluginServiceBase extends AbstractLifecycleComponent<TestPluginServiceBase> {
|
||||
public abstract class TestPluginServiceBase extends AbstractLifecycleComponent<TestPluginServiceBase> implements ClusterStateListener {
|
||||
|
||||
private LicensesClientService licensesClientService;
|
||||
|
||||
private final ClusterService clusterService;
|
||||
// specify the trial license spec for the feature
|
||||
// example: 30 day trial on 1000 nodes
|
||||
final LicensesService.TrialLicenseOptions trialLicenseOptions;
|
||||
|
||||
final boolean eagerLicenseRegistration;
|
||||
|
||||
public final AtomicBoolean registered = new AtomicBoolean(false);
|
||||
|
||||
private AtomicBoolean enabled = new AtomicBoolean(false);
|
||||
|
||||
public TestPluginServiceBase(Settings settings, LicensesClientService licensesClientService) {
|
||||
public TestPluginServiceBase(boolean eagerLicenseRegistration, Settings settings, LicensesClientService licensesClientService, ClusterService clusterService) {
|
||||
super(settings);
|
||||
this.eagerLicenseRegistration = eagerLicenseRegistration;
|
||||
this.licensesClientService = licensesClientService;
|
||||
int durationInSec = settings.getAsInt(settingPrefix() + ".trial_license_duration_in_seconds", -1);
|
||||
if (durationInSec == -1) {
|
||||
|
@ -33,6 +43,12 @@ public abstract class TestPluginServiceBase extends AbstractLifecycleComponent<T
|
|||
} else {
|
||||
this.trialLicenseOptions = new LicensesService.TrialLicenseOptions(TimeValue.timeValueSeconds(durationInSec), 1000);
|
||||
}
|
||||
if (!eagerLicenseRegistration) {
|
||||
this.clusterService = clusterService;
|
||||
clusterService.add(this);
|
||||
} else {
|
||||
this.clusterService = null;
|
||||
}
|
||||
}
|
||||
|
||||
// should be the same string used by the license Manger to generate
|
||||
|
@ -46,14 +62,34 @@ public abstract class TestPluginServiceBase extends AbstractLifecycleComponent<T
|
|||
return enabled.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clusterChanged(ClusterChangedEvent event) {
|
||||
if (!eagerLicenseRegistration && !event.state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) {
|
||||
if (registered.compareAndSet(false, true)) {
|
||||
logger.info("Registering to licensesService [lazy]");
|
||||
licensesClientService.register(featureName(),
|
||||
trialLicenseOptions,
|
||||
new LicensingClientListener());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void doStart() throws ElasticsearchException {
|
||||
licensesClientService.register(featureName(),
|
||||
trialLicenseOptions,
|
||||
new LicensingClientListener());
|
||||
if (eagerLicenseRegistration) {
|
||||
if (registered.compareAndSet(false, true)) {
|
||||
logger.info("Registering to licensesService [eager]");
|
||||
licensesClientService.register(featureName(),
|
||||
trialLicenseOptions,
|
||||
new LicensingClientListener());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws ElasticsearchException {
|
||||
if (clusterService != null) {
|
||||
clusterService.remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue