diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/ExpirationCallback.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/ExpirationCallback.java index 05fe42869d9..fdf7a9c66f8 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/ExpirationCallback.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/ExpirationCallback.java @@ -8,9 +8,14 @@ package org.elasticsearch.license.plugin.core; import org.elasticsearch.common.logging.LoggerMessageFormat; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.license.core.License; +import org.elasticsearch.xpack.scheduler.SchedulerEngine; + +import java.util.UUID; public abstract class ExpirationCallback { + final static String EXPIRATION_JOB_PREFIX = ".license_expiration_job_"; + public enum Orientation {PRE, POST} public static abstract class Pre extends ExpirationCallback { @@ -38,8 +43,8 @@ public abstract class ExpirationCallback { } @Override - public TimeValue delay(long expiryDuration) { - return TimeValue.timeValueMillis(expiryDuration - max.getMillis()); + public TimeValue delay(long expirationDate, long now) { + return TimeValue.timeValueMillis((expirationDate - now) - max.getMillis()); } } @@ -68,7 +73,8 @@ public abstract class ExpirationCallback { } @Override - public TimeValue delay(long expiryDuration) { + public TimeValue delay(long expirationDate, long now) { + long expiryDuration = expirationDate - now; final long delay; if (expiryDuration >= 0L) { delay = expiryDuration + min.getMillis(); @@ -83,6 +89,7 @@ public abstract class ExpirationCallback { } } + private final String id; protected final Orientation orientation; protected final TimeValue min; protected final TimeValue max; @@ -93,21 +100,57 @@ public abstract class ExpirationCallback { this.min = (min == null) ? TimeValue.timeValueMillis(0) : min; this.max = (max == null) ? TimeValue.timeValueMillis(Long.MAX_VALUE) : max; this.frequency = frequency; + this.id = String.join("", EXPIRATION_JOB_PREFIX, UUID.randomUUID().toString()); + } + + public String getId() { + return id; } public TimeValue frequency() { return frequency; } - public abstract TimeValue delay(long expiryDuration); + public abstract TimeValue delay(long expirationDate, long now); public abstract boolean matches(long expirationDate, long now); public abstract void on(License license); - @Override + public SchedulerEngine.Schedule schedule(long expiryDate) { + return new ExpirySchedule(expiryDate); + } + public String toString() { return LoggerMessageFormat.format(null, "ExpirationCallback:(orientation [{}], min [{}], max [{}], freq [{}])", orientation.name(), min, max, frequency); } -} + + private class ExpirySchedule implements SchedulerEngine.Schedule { + + private final long expiryDate; + + private ExpirySchedule(long expiryDate) { + this.expiryDate = expiryDate; + } + + @Override + public long nextScheduledTimeAfter(long startTime, long time) { + if (matches(expiryDate, time)) { + if (startTime == time) { + return time; + } else { + return time + frequency().getMillis(); + } + } else { + if (startTime == time) { + final TimeValue delay = delay(expiryDate, time); + if (delay != null) { + return time + delay.getMillis(); + } + } + return -1; + } + } + } +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseSchedule.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseSchedule.java new file mode 100644 index 00000000000..8dbfecb5510 --- /dev/null +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseSchedule.java @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.plugin.core; + +import org.elasticsearch.license.core.License; +import org.elasticsearch.xpack.scheduler.SchedulerEngine; + +import static org.elasticsearch.license.plugin.core.LicensesService.GRACE_PERIOD_DURATION; +import static org.elasticsearch.license.plugin.core.LicensesService.getLicenseState; + +public class LicenseSchedule implements SchedulerEngine.Schedule { + + private final License license; + + LicenseSchedule(License license) { + this.license = license; + } + + @Override + public long nextScheduledTimeAfter(long startTime, long time) { + long nextScheduledTime = -1; + switch (getLicenseState(license, time)) { + case ENABLED: + nextScheduledTime = license.expiryDate(); + break; + case GRACE_PERIOD: + nextScheduledTime = license.expiryDate() + GRACE_PERIOD_DURATION.getMillis(); + break; + case DISABLED: + if (license.issueDate() > time) { + nextScheduledTime = license.issueDate(); + } + break; + } + return nextScheduledTime; + } +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java index 8e99a9c0825..ba5817dc5de 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java @@ -27,8 +27,6 @@ import org.elasticsearch.common.joda.Joda; import org.elasticsearch.common.logging.LoggerMessageFormat; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; -import org.elasticsearch.common.util.concurrent.FutureUtils; import org.elasticsearch.gateway.GatewayService; import org.elasticsearch.license.core.License; import org.elasticsearch.license.core.LicenseVerifier; @@ -42,21 +40,20 @@ import org.elasticsearch.transport.TransportRequest; import org.elasticsearch.transport.TransportRequestHandler; import org.elasticsearch.transport.TransportResponse; import org.elasticsearch.transport.TransportService; +import org.elasticsearch.xpack.scheduler.SchedulerEngine; import org.elasticsearch.xpack.support.clock.Clock; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Queue; import java.util.UUID; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicReference; /** @@ -77,18 +74,16 @@ import java.util.concurrent.atomic.AtomicReference; *

* All registered listeners are notified of the current license upon registration or when a new license is installed in the cluster state. * When a new license is notified as enabled to the registered listener, a notification is scheduled at the time of license expiry. - * Registered listeners are notified using {@link #notifyAndSchedule(LicensesMetaData)} + * Registered listeners are notified using {@link #onUpdate(LicensesMetaData)} */ @Singleton public class LicensesService extends AbstractLifecycleComponent implements ClusterStateListener, LicensesManagerService, - LicenseeRegistry { + LicenseeRegistry, SchedulerEngine.Listener { public static final String REGISTER_TRIAL_LICENSE_ACTION_NAME = "internal:plugin/license/cluster/register_trial_license"; private final ClusterService clusterService; - private final ThreadPool threadPool; - private final TransportService transportService; /** @@ -96,21 +91,11 @@ public class LicensesService extends AbstractLifecycleComponent */ private final List registeredLicensees = new CopyOnWriteArrayList<>(); - /** - * Currently active expiry notifications - */ - private final Queue expiryNotifications = new ConcurrentLinkedQueue<>(); - - /** - * Currently active event notifications for every registered listener - */ - private final Queue eventNotifications = new ConcurrentLinkedQueue<>(); - /** * Currently active license */ private final AtomicReference currentLicense = new AtomicReference<>(); - + private SchedulerEngine scheduler; private final Clock clock; /** @@ -131,7 +116,9 @@ public class LicensesService extends AbstractLifecycleComponent /** * Duration of grace period after a license has expired */ - private TimeValue gracePeriodDuration = days(7); + public static final TimeValue GRACE_PERIOD_DURATION = days(7); + + private final static String LICENSE_JOB = "licenseJob"; private static final FormatDateTimeFormatter DATE_FORMATTER = Joda.forPattern("EEEE, MMMMM dd, yyyy", Locale.ROOT); @@ -139,10 +126,9 @@ public class LicensesService extends AbstractLifecycleComponent "please read the following messages and update the license again, this time with the \"acknowledge=true\" parameter:"; @Inject - public LicensesService(Settings settings, ClusterService clusterService, ThreadPool threadPool, TransportService transportService, Clock clock) { + public LicensesService(Settings settings, ClusterService clusterService, TransportService transportService, Clock clock) { super(settings); this.clusterService = clusterService; - this.threadPool = threadPool; this.transportService = transportService; if (DiscoveryNode.isMasterNode(settings)) { transportService.registerRequestHandler(REGISTER_TRIAL_LICENSE_ACTION_NAME, TransportRequest.Empty::new, @@ -152,6 +138,10 @@ public class LicensesService extends AbstractLifecycleComponent this.clock = clock; } + protected SchedulerEngine getScheduler() { + return new SchedulerEngine(clock); + } + private void populateExpirationCallbacks() { expirationCallbacks.add(new ExpirationCallback.Pre(days(7), days(25), days(1)) { @Override @@ -264,9 +254,8 @@ public class LicensesService extends AbstractLifecycleComponent listener.onResponse(new PutLicenseResponse(true, LicensesStatus.EXPIRED)); } else { if (!request.acknowledged()) { - final LicensesMetaData currentMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE); - final License currentLicense = getLicense(currentMetaData); - if (currentLicense != null && currentLicense != LicensesMetaData.LICENSE_TOMBSTONE) { + final License currentLicense = getLicense(); + if (currentLicense != null) { Map acknowledgeMessages = new HashMap<>(registeredLicensees.size() + 1); if (!License.isAutoGeneratedLicense(currentLicense.signature()) // current license is not auto-generated && currentLicense.issueDate() > newLicense.issueDate()) { // and has a later issue date @@ -290,22 +279,22 @@ public class LicensesService extends AbstractLifecycleComponent } clusterService.submitStateUpdateTask("register license [" + newLicense.uid() + "]", new AckedClusterStateUpdateTask(request, listener) { - @Override - protected PutLicenseResponse newResponse(boolean acknowledged) { - return new PutLicenseResponse(acknowledged, LicensesStatus.VALID); - } + @Override + protected PutLicenseResponse newResponse(boolean acknowledged) { + return new PutLicenseResponse(acknowledged, LicensesStatus.VALID); + } - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); - mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(newLicense)); - return ClusterState.builder(currentState).metaData(mdBuilder).build(); - } - }); + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); + mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(newLicense)); + return ClusterState.builder(currentState).metaData(mdBuilder).build(); + } + }); } } - private boolean verifyLicense(final License license) { + static boolean verifyLicense(final License license) { final byte[] publicKeyBytes; try (InputStream is = LicensesService.class.getResourceAsStream("/public.key")) { ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -317,42 +306,59 @@ public class LicensesService extends AbstractLifecycleComponent return LicenseVerifier.verifyLicense(license, publicKeyBytes); } + static TimeValue days(int days) { return TimeValue.timeValueHours(days * 24); } + @Override + public void triggered(SchedulerEngine.Event event) { + final LicensesMetaData licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE); + if (licensesMetaData != null) { + final License license = licensesMetaData.getLicense(); + if (event.getJobName().equals(LICENSE_JOB)) { + notifyLicensees(license); + } else if (event.getJobName().startsWith(ExpirationCallback.EXPIRATION_JOB_PREFIX)) { + expirationCallbacks.stream() + .filter(expirationCallback -> expirationCallback.getId().equals(event.getJobName())) + .forEach(expirationCallback -> expirationCallback.on(license)); + } + } + } + /** * Remove license from the cluster state metadata */ public void removeLicense(final DeleteLicenseRequest request, final ActionListener listener) { clusterService.submitStateUpdateTask("delete license", new AckedClusterStateUpdateTask(request, listener) { - @Override - protected ClusterStateUpdateResponse newResponse(boolean acknowledged) { - return new ClusterStateUpdateResponse(acknowledged); - } + @Override + protected ClusterStateUpdateResponse newResponse(boolean acknowledged) { + return new ClusterStateUpdateResponse(acknowledged); + } - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - MetaData metaData = currentState.metaData(); - final LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE); - if (currentLicenses.getLicense() != LicensesMetaData.LICENSE_TOMBSTONE) { - MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); - mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(LicensesMetaData.LICENSE_TOMBSTONE)); - return ClusterState.builder(currentState).metaData(mdBuilder).build(); - } else { - return currentState; - } - } - }); + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + MetaData metaData = currentState.metaData(); + final LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE); + if (currentLicenses.getLicense() != LicensesMetaData.LICENSE_TOMBSTONE) { + MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); + mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(LicensesMetaData.LICENSE_TOMBSTONE)); + return ClusterState.builder(currentState).metaData(mdBuilder).build(); + } else { + return currentState; + } + } + }); } @Override public LicenseState licenseState() { if (registeredLicensees.size() > 0) { return registeredLicensees.get(0).currentLicenseState; + } else { + return getLicenseState(getLicense(), clock.millis()); } - return null; } @Override @@ -407,27 +413,25 @@ public class LicensesService extends AbstractLifecycleComponent @Override protected void doStart() throws ElasticsearchException { + if (scheduler == null) { + this.scheduler = getScheduler(); + this.scheduler.register(this); + } clusterService.add(this); + try { + scheduler.start(Collections.emptyList()); + } catch (Exception e) { + logger.warn("failed to start license trigger service", e); + } } @Override protected void doStop() throws ElasticsearchException { clusterService.remove(this); - - // cancel all notifications - for (ScheduledFuture scheduledNotification : expiryNotifications) { - FutureUtils.cancel(scheduledNotification); - } - for (ScheduledFuture eventNotification : eventNotifications) { - FutureUtils.cancel(eventNotification); - } - + scheduler.stop(); // clear all handlers registeredLicensees.clear(); - // empty out notification queue - expiryNotifications.clear(); - // clear current license currentLicense.set(null); } @@ -454,14 +458,14 @@ public class LicensesService extends AbstractLifecycleComponent } // notify all interested plugins if (previousClusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) { - notifyAndSchedule(currentLicensesMetaData); + onUpdate(currentLicensesMetaData); } else { if (prevLicensesMetaData == null) { if (currentLicensesMetaData != null) { - notifyAndSchedule(currentLicensesMetaData); + onUpdate(currentLicensesMetaData); } } else if (!prevLicensesMetaData.equals(currentLicensesMetaData)) { - notifyAndSchedule(currentLicensesMetaData); + onUpdate(currentLicensesMetaData); } } // auto-generate license if no licenses ever existed @@ -475,140 +479,69 @@ public class LicensesService extends AbstractLifecycleComponent } } - /** - * Notifies registered licensees of license state change and/or new active license - * based on the license in currentLicensesMetaData. - * Additionally schedules license expiry notifications and event callbacks - * relative to the current license's expiry - */ - private void notifyAndSchedule(final LicensesMetaData currentLicensesMetaData) { - final License license = getLicense(currentLicensesMetaData); + private void notifyLicensees(final License license) { if (license == LicensesMetaData.LICENSE_TOMBSTONE) { // implies license has been explicitly deleted // update licensee states registeredLicensees.forEach(InternalLicensee::onRemove); return; } + if (license != null) { + logger.debug("notifying [{}] listeners", registeredLicensees.size()); + switch (getLicenseState(license, clock.millis())) { + case ENABLED: + for (InternalLicensee licensee : registeredLicensees) { + licensee.onChange(license, LicenseState.ENABLED); + } + logger.debug("license [{}] - valid", license.uid()); + break; + case GRACE_PERIOD: + for (InternalLicensee licensee : registeredLicensees) { + licensee.onChange(license, LicenseState.GRACE_PERIOD); + } + logger.warn("license [{}] - grace", license.uid()); + break; + case DISABLED: + for (InternalLicensee licensee : registeredLicensees) { + licensee.onChange(license, LicenseState.DISABLED); + } + logger.warn("license [{}] - expired", license.uid()); + break; + } + } + } + + static LicenseState getLicenseState(final License license, long time) { + if (license.issueDate() > time) { + return LicenseState.DISABLED; + } + if (license.expiryDate() > time) { + return LicenseState.ENABLED; + } + if ((license.expiryDate() + GRACE_PERIOD_DURATION.getMillis()) > time) { + return LicenseState.GRACE_PERIOD; + } + return LicenseState.DISABLED; + } + + /** + * Notifies registered licensees of license state change and/or new active license + * based on the license in currentLicensesMetaData. + * Additionally schedules license expiry notifications and event callbacks + * relative to the current license's expiry + */ + void onUpdate(final LicensesMetaData currentLicensesMetaData) { + final License license = getLicense(currentLicensesMetaData); // license can be null if the trial license is yet to be auto-generated // in this case, it is a no-op if (license != null) { - logger.debug("notifying [{}] listeners", registeredLicensees.size()); - long now = clock.millis(); - if (license.issueDate() > now) { - logger.warn("license [{}] - invalid", license.uid()); - return; - } - long expiryDuration = license.expiryDate() - now; - if (license.expiryDate() > now) { - for (InternalLicensee licensee : registeredLicensees) { - licensee.onChange(license, LicenseState.ENABLED); - } - logger.debug("license [{}] - valid", license.uid()); - final TimeValue delay = TimeValue.timeValueMillis(expiryDuration); - // cancel any previous notifications - cancelNotifications(expiryNotifications); - try { - logger.debug("schedule grace notification after [{}] for license [{}]", delay.toString(), license.uid()); - expiryNotifications.add(threadPool.schedule(delay, executorName(), new LicensingClientNotificationJob())); - } catch (EsRejectedExecutionException ex) { - logger.debug("couldn't schedule grace notification", ex); - } - } else if ((license.expiryDate() + gracePeriodDuration.getMillis()) > now) { - for (InternalLicensee licensee : registeredLicensees) { - licensee.onChange(license, LicenseState.GRACE_PERIOD); - } - logger.warn("license [{}] - grace", license.uid()); - final TimeValue delay = TimeValue.timeValueMillis(expiryDuration + gracePeriodDuration.getMillis()); - // cancel any previous notifications - cancelNotifications(expiryNotifications); - try { - logger.debug("schedule expiry notification after [{}] for license [{}]", delay.toString(), license.uid()); - expiryNotifications.add(threadPool.schedule(delay, executorName(), new LicensingClientNotificationJob())); - } catch (EsRejectedExecutionException ex) { - logger.debug("couldn't schedule expiry notification", ex); - } - } else { - for (InternalLicensee licensee : registeredLicensees) { - licensee.onChange(license, LicenseState.DISABLED); - } - logger.warn("license [{}] - expired", license.uid()); - } - if (!license.equals(currentLicense.get())) { + notifyLicensees(license); + if (license.equals(currentLicense.get()) == false) { currentLicense.set(license); - // cancel all scheduled event notifications - cancelNotifications(eventNotifications); - // schedule expiry callbacks - for (ExpirationCallback expirationCallback : this.expirationCallbacks) { - final TimeValue delay; - if (expirationCallback.matches(license.expiryDate(), now)) { - expirationCallback.on(license); - TimeValue frequency = expirationCallback.frequency(); - delay = frequency != null ? frequency : expirationCallback.delay(expiryDuration); - } else { - delay = expirationCallback.delay(expiryDuration); - } - if (delay != null) { - eventNotifications.add(threadPool.schedule(delay, executorName(), new EventNotificationJob(expirationCallback))); - } - if (logger.isDebugEnabled()) { - logger.debug("schedule [{}] after [{}]", expirationCallback, delay); - } - } - logger.debug("scheduled expiry callbacks for [{}] expiring after [{}]", license.uid(), - TimeValue.timeValueMillis(expiryDuration)); - } - } - } - - private class LicensingClientNotificationJob implements Runnable { - @Override - public void run() { - logger.debug("running expiry notification"); - final ClusterState currentClusterState = clusterService.state(); - if (!currentClusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) { - final LicensesMetaData currentLicensesMetaData = currentClusterState.metaData().custom(LicensesMetaData.TYPE); - notifyAndSchedule(currentLicensesMetaData); - } else if (logger.isDebugEnabled()) { - // next clusterChanged event will deal with the missed notifications - logger.debug("skip expiry notification [{}]", GatewayService.STATE_NOT_RECOVERED_BLOCK); - } - } - } - - private class EventNotificationJob implements Runnable { - private final ExpirationCallback expirationCallback; - - EventNotificationJob(ExpirationCallback expirationCallback) { - this.expirationCallback = expirationCallback; - } - - @Override - public void run() { - logger.debug("running event notification for [{}]", expirationCallback); - LicensesMetaData currentLicensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE); - License license = getLicense(currentLicensesMetaData); - if (license != null) { - long now = clock.millis(); - if (expirationCallback.matches(license.expiryDate(), now)) { - expirationCallback.on(license); - if (expirationCallback.frequency() != null) { - // schedule next event - eventNotifications.add(threadPool.schedule(expirationCallback.frequency(), executorName(), this)); - } - } else if (logger.isDebugEnabled()) { - logger.debug("skip scheduling notification for [{}] with license expiring after [{}]", expirationCallback, - TimeValue.timeValueMillis(license.expiryDate() - now)); - } - } - // clear out any finished event notifications - while (!eventNotifications.isEmpty()) { - ScheduledFuture notification = eventNotifications.peek(); - if (notification != null && notification.isDone()) { - // remove the notifications that are done - eventNotifications.poll(); - } else { - // stop emptying out the queue as soon as the first undone future hits - break; + scheduler.add(new SchedulerEngine.Job(LICENSE_JOB, new LicenseSchedule(license))); + for (ExpirationCallback expirationCallback : expirationCallbacks) { + scheduler.add(new SchedulerEngine.Job(expirationCallback.getId(), + expirationCallback.schedule(license.expiryDate()))); } } } @@ -632,8 +565,8 @@ public class LicensesService extends AbstractLifecycleComponent // triggers a cluster changed event // eventually notifying the current licensee requestTrialLicense(clusterState); - } else { - notifyAndSchedule(currentMetaData); + } else if (lifecycleState() == Lifecycle.State.STARTED) { + notifyLicensees(currentMetaData.getLicense()); } } } @@ -663,25 +596,6 @@ public class LicensesService extends AbstractLifecycleComponent return null; } - /** - * Cancels out all notification futures - */ - private static void cancelNotifications(Queue scheduledNotifications) { - // clear out notification queue - while (!scheduledNotifications.isEmpty()) { - ScheduledFuture notification = scheduledNotifications.peek(); - if (notification != null) { - // cancel - FutureUtils.cancel(notification); - scheduledNotifications.poll(); - } - } - } - - private String executorName() { - return ThreadPool.Names.GENERIC; - } - /** * Stores acknowledgement, expiration and license notification callbacks * for a registered listener @@ -747,19 +661,4 @@ public class LicensesService extends AbstractLifecycleComponent channel.sendResponse(TransportResponse.Empty.INSTANCE); } } - - // TODO - temporary hack for tests, should be removed once we introduce `ClockMock` - public void setGracePeriodDuration(TimeValue gracePeriodDuration) { - this.gracePeriodDuration = gracePeriodDuration; - } - - // only for adding expiration callbacks for tests - public void setExpirationCallbacks(List expirationCallbacks) { - this.expirationCallbacks = expirationCallbacks; - } - - // TODO - temporary hack for tests, should be removed once we introduce `ClockMock` - public void setTrialLicenseDuration(TimeValue trialLicenseDuration) { - this.trialLicenseDuration = trialLicenseDuration; - } -} +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/TestPluginServiceBase.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/TestPluginServiceBase.java index cfff3c3c24c..50c7f5de8f3 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/TestPluginServiceBase.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/TestPluginServiceBase.java @@ -36,11 +36,14 @@ public abstract class TestPluginServiceBase extends AbstractLifecycleComponentemptyMap(), latch)); - if (!latch.await(5, TimeUnit.SECONDS)) { - fail("waiting too long for a response to license registration"); - } - awaitNoPendingTasks(client()); + Collections.emptyMap())); + verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class)); assertThat(licensee.acknowledgementRequested.size(), equalTo(1)); assertThat(licensee.acknowledgementRequested.get(0).v2(), equalTo(signedLicense)); - assertThat(licensesService.getLicense(), equalTo(signedLicense)); licensesService.stop(); } public void testAcknowledgementMultipleLicensee() throws Exception { - final LicensesService licensesService = getInstanceFromNode(LicensesService.class); + setInitialState(TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2))); licensesService.start(); String id1 = "testAcknowledgementMultipleLicensee_1"; String[] acknowledgeMessages1 = new String[] {"testAcknowledgementMultipleLicensee_1"}; @@ -89,43 +67,32 @@ public class LicensesAcknowledgementTests extends ESSingleNodeTestCase { licensee1.setAcknowledgementMessages(acknowledgeMessages1); TestUtils.AssertingLicensee licensee2 = new TestUtils.AssertingLicensee(id2, logger); licensee2.setAcknowledgementMessages(acknowledgeMessages2); - awaitNoBlock(client()); licensesService.register(licensee1); licensesService.register(licensee2); - awaitNoPendingTasks(client()); // try installing a signed license License signedLicense = generateSignedLicense(TimeValue.timeValueHours(10)); PutLicenseRequest putLicenseRequest = new PutLicenseRequest().license(signedLicense); - CountDownLatch latch = new CountDownLatch(1); // ensure acknowledgement message was part of the response final HashMap expectedMessages = new HashMap<>(); expectedMessages.put(id1, acknowledgeMessages1); expectedMessages.put(id2, acknowledgeMessages2); licensesService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID, - expectedMessages, latch)); - if (!latch.await(5, TimeUnit.SECONDS)) { - fail("waiting too long for a response to license registration"); - } - awaitNoPendingTasks(client()); + expectedMessages)); + verify(clusterService, times(0)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class)); assertThat(licensee2.acknowledgementRequested.size(), equalTo(1)); assertThat(licensee2.acknowledgementRequested.get(0).v2(), equalTo(signedLicense)); assertThat(licensee1.acknowledgementRequested.size(), equalTo(1)); assertThat(licensee1.acknowledgementRequested.get(0).v2(), equalTo(signedLicense)); assertThat(licensesService.getLicense(), not(signedLicense)); - latch = new CountDownLatch(1); // try installing a signed license with acknowledgement putLicenseRequest = new PutLicenseRequest().license(signedLicense).acknowledge(true); // ensure license was installed and no acknowledgment message was returned licensee1.setAcknowledgementMessages(new String[0]); licensee2.setAcknowledgementMessages(new String[0]); licensesService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(true, LicensesStatus.VALID, - Collections.emptyMap(), latch)); - if (!latch.await(5, TimeUnit.SECONDS)) { - fail("waiting too long for a response to license registration"); - } - awaitNoPendingTasks(client()); - assertThat(licensesService.getLicense(), equalTo(signedLicense)); + Collections.emptyMap())); + verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class)); licensesService.stop(); } @@ -133,14 +100,12 @@ public class LicensesAcknowledgementTests extends ESSingleNodeTestCase { private final boolean expectedAcknowledgement; private final LicensesStatus expectedStatus; private final Map expectedAckMessages; - private final CountDownLatch latch; public AssertingLicensesUpdateResponse(boolean expectedAcknowledgement, LicensesStatus expectedStatus, - Map expectedAckMessages, CountDownLatch latch) { + Map expectedAckMessages) { this.expectedAcknowledgement = expectedAcknowledgement; this.expectedStatus = expectedStatus; this.expectedAckMessages = expectedAckMessages; - this.latch = latch; } @Override @@ -154,12 +119,10 @@ public class LicensesAcknowledgementTests extends ESSingleNodeTestCase { String[] actualMessages = actual.get(expectedEntry.getKey()); assertThat(actualMessages, equalTo(expectedEntry.getValue())); } - latch.countDown(); } @Override public void onFailure(Throwable throwable) { - latch.countDown(); } } -} +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesExpirationCallbackTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesExpirationCallbackTests.java deleted file mode 100644 index d5af4789ead..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesExpirationCallbackTests.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin.core; - -import org.elasticsearch.cluster.metadata.MetaData; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.license.core.License; -import org.elasticsearch.license.plugin.TestUtils; -import org.elasticsearch.test.ESSingleNodeTestCase; - -import java.util.Arrays; -import java.util.Collections; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.elasticsearch.common.unit.TimeValue.timeValueMillis; -import static org.hamcrest.Matchers.equalTo; - -public class LicensesExpirationCallbackTests extends ESSingleNodeTestCase { - static { - MetaData.registerPrototype(LicensesMetaData.TYPE, LicensesMetaData.PROTO); - } - - @Override - protected boolean resetNodeAfterTest() { - return true; - } - - 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 ExpirationCallback.Post post = new ExpirationCallback.Post(min, max, timeValueMillis(10)) { - @Override - public void on(License license) { - } - }; - long now = System.currentTimeMillis(); - assertThat(post.matches(now - postExpiryDuration.millis(), now), equalTo(true)); - assertThat(post.matches(now + postExpiryDuration.getMillis(), now), equalTo(false)); - } - - public void testPostExpirationWithNullMax() throws Exception { - int postExpirySeconds = randomIntBetween(5, 10); - TimeValue postExpiryDuration = TimeValue.timeValueSeconds(postExpirySeconds); - TimeValue min = TimeValue.timeValueSeconds(postExpirySeconds - randomIntBetween(1, 3)); - - final ExpirationCallback.Post post = new ExpirationCallback.Post(min, null, timeValueMillis(10)) { - @Override - public void on(License license) { - } - }; - long now = System.currentTimeMillis(); - assertThat(post.matches(now - postExpiryDuration.millis(), now), equalTo(true)); - } - - public void testPreExpirationWithNullMin() throws Exception { - int expirySeconds = randomIntBetween(5, 10); - TimeValue expiryDuration = TimeValue.timeValueSeconds(expirySeconds); - TimeValue max = TimeValue.timeValueSeconds(expirySeconds + randomIntBetween(1, 10)); - - final ExpirationCallback.Pre pre = new ExpirationCallback.Pre(null, max, timeValueMillis(10)) { - @Override - public void on(License license) { - } - }; - long now = System.currentTimeMillis(); - assertThat(pre.matches(expiryDuration.millis() + now, now), equalTo(true)); - } - - 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 ExpirationCallback.Pre pre = new ExpirationCallback.Pre(min, max, timeValueMillis(10)) { - @Override - public void on(License license) { - } - }; - long now = System.currentTimeMillis(); - assertThat(pre.matches(expiryDuration.millis() + now, now), equalTo(true)); - assertThat(pre.matches(now - expiryDuration.getMillis(), now), equalTo(false)); - } - - public void testPreExpirationNotification() throws Exception { - final LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(5)); - AtomicInteger counter = new AtomicInteger(0); - // 2000, 1600, 1200 - licensesService.setExpirationCallbacks(Collections.singletonList( - preCallbackLatch(TimeValue.timeValueSeconds(1), TimeValue.timeValueSeconds(2), timeValueMillis(400), counter)) - ); - licensesService.start(); - TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee("testPreExpirationNotification", logger); - licensesService.register(licensee); - boolean success = awaitBusy(() -> (counter.get() == 3 || counter.get() == 2)); - assertThat("counter: actual: " + counter.get() + "vs expected: 3", success, equalTo(true)); - licensesService.stop(); - } - - public void testPostExpirationNotification() throws Exception { - final LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(3)); - AtomicInteger counter = new AtomicInteger(0); - // 700, 1700, 2700 - licensesService.setExpirationCallbacks(Collections.singletonList( - postCallbackLatch(timeValueMillis(700), TimeValue.timeValueSeconds(3), TimeValue.timeValueSeconds(1), counter)) - ); - licensesService.start(); - TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee("testPostExpirationNotification", logger); - licensesService.register(licensee); - // callback can be called only twice if the third notification is triggered with a delay - // causing the trigger time to be out of the post expiry callback window - boolean success = awaitBusy(() -> (counter.get() == 3 || counter.get() == 2)); - assertThat("counter: actual: " + counter.get() + "vs expected: 3", success, equalTo(true)); - licensesService.stop(); - } - - public void testMultipleExpirationNotification() throws Exception { - final LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(4)); - AtomicInteger postCounter = new AtomicInteger(0); - AtomicInteger preCounter = new AtomicInteger(0); - licensesService.setExpirationCallbacks(Arrays.asList( - // 2000, 1600, 1200 - preCallbackLatch(TimeValue.timeValueSeconds(1), TimeValue.timeValueSeconds(2), timeValueMillis(400), preCounter), - // 100, 500, 900, 1300, 1700 - postCallbackLatch(timeValueMillis(100), TimeValue.timeValueSeconds(2), timeValueMillis(400), postCounter)) - ); - licensesService.start(); - TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee("testMultipleExpirationNotification", logger); - licensesService.register(licensee); - // callback can be called one less than expected if the last notification is triggered - // with a delay, causing the trigger time to be out of the expiry callback window - boolean success = awaitBusy(() -> ((preCounter.get() == 3 || preCounter.get() == 2) - && (postCounter.get() == 5 || postCounter.get() == 4))); - assertThat("post count: actual: " + postCounter.get() + "vs expected: 5 " + - "pre count: actual: " + preCounter.get() + " vs expected: 3", success, equalTo(true)); - licensesService.stop(); - } - - private static ExpirationCallback preCallbackLatch(TimeValue min, TimeValue max, TimeValue frequency, - final AtomicInteger count) { - return new ExpirationCallback.Pre(min, max, frequency) { - @Override - public void on(License license) { - count.incrementAndGet(); - } - }; - } - - private static ExpirationCallback postCallbackLatch(TimeValue min, TimeValue max, TimeValue frequency, - final AtomicInteger count) { - return new ExpirationCallback.Post(min, max, frequency) { - @Override - public void on(License license) { - count.incrementAndGet(); - } - }; - } -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesExpiryNotificationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesExpiryNotificationTests.java deleted file mode 100644 index dceab4627aa..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesExpiryNotificationTests.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin.core; - -import org.apache.lucene.util.LuceneTestCase.BadApple; -import org.elasticsearch.cluster.metadata.MetaData; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.license.plugin.TestUtils.AssertingLicensee; -import org.elasticsearch.test.ESSingleNodeTestCase; - -import java.util.List; - -import static org.elasticsearch.license.plugin.TestUtils.awaitNoBlock; -import static org.elasticsearch.license.plugin.TestUtils.awaitNoPendingTasks; -import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense; -import static org.elasticsearch.license.plugin.TestUtils.registerAndAckSignedLicenses; -import static org.hamcrest.Matchers.equalTo; - -//test is just too slow, please fix it to not be sleep-based -@BadApple(bugUrl = "https://github.com/elastic/x-plugins/issues/1007") -public class LicensesExpiryNotificationTests extends ESSingleNodeTestCase { - static { - MetaData.registerPrototype(LicensesMetaData.TYPE, LicensesMetaData.PROTO); - } - - @Override - protected boolean resetNodeAfterTest() { - return true; - } - - public void testTrialLicenseEnforcement() throws Exception { - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(5)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(3)); - licensesService.start(); - String id1 = "testTrialLicenseEnforcement"; - final AssertingLicensee licensee = new AssertingLicensee(id1, logger); - awaitNoBlock(client()); - licensesService.register(licensee); - awaitNoPendingTasks(client()); - boolean success = awaitBusy(() -> licensee.statuses.size() == 3); - // trail license: enable, grace, disabled - assertLicenseStates(licensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - assertTrue(dumpLicensingStates(licensee.statuses), success); - licensesService.stop(); - } - - public void testTrialLicenseEnforcementMultipleLicensees() throws Exception { - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(5)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(3)); - licensesService.start(); - String id1 = "testTrialLicenseEnforcementMultipleLicensees_1"; - final AssertingLicensee licensee1 = new AssertingLicensee(id1, logger); - String id12 = "testTrialLicenseEnforcementMultipleLicensees_2"; - final AssertingLicensee licensee2 = new AssertingLicensee(id12, logger); - awaitNoBlock(client()); - licensesService.register(licensee1); - licensesService.register(licensee2); - awaitNoPendingTasks(client()); - boolean success = awaitBusy(() -> licensee1.statuses.size() == 3); - assertTrue(dumpLicensingStates(licensee1.statuses), success); - success = awaitBusy(() -> licensee2.statuses.size() == 3); - assertTrue(dumpLicensingStates(licensee2.statuses), success); - // trail license: enable, grace, disabled - assertLicenseStates(licensee1, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - assertLicenseStates(licensee2, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - licensesService.stop(); - } - - public void testTrialSignedLicenseEnforcement() throws Exception { - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(2)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(3)); - licensesService.start(); - String id1 = "testTrialSignedLicenseEnforcement"; - final AssertingLicensee licensee = new AssertingLicensee(id1, logger); - awaitNoBlock(client()); - licensesService.register(licensee); - awaitNoPendingTasks(client()); - boolean success = awaitBusy(() -> licensee.statuses.size() == 1); - assertTrue(dumpLicensingStates(licensee.statuses), success); - registerAndAckSignedLicenses(licensesService, generateSignedLicense(TimeValue.timeValueSeconds(4)), LicensesStatus.VALID); - success = awaitBusy(() -> licensee.statuses.size() == 4); - // trial: enable, signed: enable, signed: grace, signed: disabled - assertLicenseStates(licensee, LicenseState.ENABLED, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - assertTrue(dumpLicensingStates(licensee.statuses), success); - licensesService.stop(); - } - - public void testSignedLicenseEnforcement() throws Exception { - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(4)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(3)); - licensesService.start(); - String id1 = "testSignedLicenseEnforcement"; - final AssertingLicensee licensee = new AssertingLicensee(id1, logger); - awaitNoBlock(client()); - registerAndAckSignedLicenses(licensesService, generateSignedLicense(TimeValue.timeValueSeconds(2)), LicensesStatus.VALID); - licensesService.register(licensee); - awaitNoPendingTasks(client()); - boolean success = awaitBusy(() -> licensee.statuses.size() == 3); - // signed: enable, signed: grace, signed: disabled - assertLicenseStates(licensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - assertTrue(dumpLicensingStates(licensee.statuses), success); - licensesService.stop(); - } - - public void testSingedLicenseEnforcementMultipleLicensees() throws Exception { - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(4)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(3)); - licensesService.start(); - String id1 = "testSingedLicenseEnforcementMultipleLicensees_1"; - final AssertingLicensee licensee1 = new AssertingLicensee(id1, logger); - String id12 = "testSingedLicenseEnforcementMultipleLicensees_2"; - final AssertingLicensee licensee2 = new AssertingLicensee(id12, logger); - awaitNoBlock(client()); - registerAndAckSignedLicenses(licensesService, generateSignedLicense(TimeValue.timeValueSeconds(2)), LicensesStatus.VALID); - licensesService.register(licensee1); - licensesService.register(licensee2); - awaitNoPendingTasks(client()); - boolean success = awaitBusy(() -> licensee1.statuses.size() == 3); - assertTrue(dumpLicensingStates(licensee1.statuses), success); - success = awaitBusy(() -> licensee2.statuses.size() == 3); - assertTrue(dumpLicensingStates(licensee2.statuses), success); - // signed license: enable, grace, disabled - assertLicenseStates(licensee1, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - assertLicenseStates(licensee2, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - licensesService.stop(); - } - - public void testMultipleSignedLicenseEnforcement() throws Exception { - // register with trial license and assert onEnable and onDisable notification - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(4)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(1)); - licensesService.start(); - String id1 = "testMultipleSignedLicenseEnforcement"; - final AssertingLicensee licensee = new AssertingLicensee(id1, logger); - awaitNoBlock(client()); - licensesService.register(licensee); - awaitNoPendingTasks(client()); - // trial license enabled - boolean success = awaitBusy(() -> licensee.statuses.size() == 1); - assertTrue(dumpLicensingStates(licensee.statuses), success); - registerAndAckSignedLicenses(licensesService, generateSignedLicense("basic", TimeValue.timeValueSeconds(3)), LicensesStatus.VALID); - // signed license enabled - success = awaitBusy(() -> licensee.statuses.size() == 2); - assertTrue(dumpLicensingStates(licensee.statuses), success); - registerAndAckSignedLicenses(licensesService, generateSignedLicense("gold", TimeValue.timeValueSeconds(2)), LicensesStatus.VALID); - // second signed license enabled, grace and expired - success = awaitBusy(() ->licensee.statuses.size() == 5); - assertLicenseStates(licensee, LicenseState.ENABLED, LicenseState.ENABLED, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, - LicenseState.DISABLED); - assertTrue(dumpLicensingStates(licensee.statuses), success); - licensesService.stop(); - } - - public void testNonOverlappingMultipleLicensesEnforcement() throws Exception { - // register with trial license and assert onEnable and onDisable notification - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(3)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(1)); - licensesService.start(); - String id1 = "testNonOverlappingMultipleLicensesEnforcement"; - final AssertingLicensee licensee = new AssertingLicensee(id1, logger); - awaitNoBlock(client()); - licensesService.register(licensee); - // trial license: enabled, grace, disabled - boolean success = awaitBusy(() -> licensee.statuses.size() == 3); - - assertTrue(dumpLicensingStates(licensee.statuses), success); - // install license - registerAndAckSignedLicenses(licensesService, generateSignedLicense("basic", TimeValue.timeValueSeconds(2)), LicensesStatus.VALID); - // trial license: enabled, grace, disabled, signed license: enabled, grace, disabled - success = awaitBusy(() -> licensee.statuses.size() == 6); - assertLicenseStates(licensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED, LicenseState.ENABLED, - LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - assertTrue(dumpLicensingStates(licensee.statuses), success); - licensesService.stop(); - } - - private void assertLicenseStates(AssertingLicensee licensee, LicenseState... states) { - StringBuilder msg = new StringBuilder(); - msg.append("Actual: "); - msg.append(dumpLicensingStates(licensee.statuses)); - msg.append(" Expected: "); - msg.append(dumpLicensingStates(states)); - assertThat(msg.toString(), licensee.statuses.size(), equalTo(states.length)); - for (int i = 0; i < states.length; i++) { - assertThat(msg.toString(), licensee.statuses.get(i).getLicenseState(), equalTo(states[i])); - } - } - - private String dumpLicensingStates(List statuses) { - return dumpLicensingStates(statuses.toArray(new Licensee.Status[statuses.size()])); - } - - private String dumpLicensingStates(Licensee.Status... statuses) { - LicenseState[] states = new LicenseState[statuses.length]; - for (int i = 0; i < statuses.length; i++) { - states[i] = statuses[i].getLicenseState(); - } - return dumpLicensingStates(states); - } - - private String dumpLicensingStates(LicenseState... states) { - StringBuilder sb = new StringBuilder(); - sb.append("["); - for (int i = 0; i < states.length; i++) { - sb.append(states[i].name()); - if (i != states.length - 1) { - sb.append(", "); - } - } - sb.append("]"); - return sb.toString(); - } -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesManagerServiceTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesManagerServiceTests.java index 9081d75aba2..bdecc6c2868 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesManagerServiceTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesManagerServiceTests.java @@ -9,12 +9,21 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.graph.Graph; import org.elasticsearch.license.core.License; import org.elasticsearch.license.plugin.TestUtils; import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest; +import org.elasticsearch.marvel.Monitoring; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.shield.Security; import org.elasticsearch.test.ESSingleNodeTestCase; +import org.elasticsearch.xpack.XPackPlugin; +import org.elasticsearch.xpack.watcher.Watcher; +import java.util.Collection; +import java.util.Collections; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; @@ -26,8 +35,19 @@ import static org.hamcrest.Matchers.not; public class LicensesManagerServiceTests extends ESSingleNodeTestCase { - static { - MetaData.registerPrototype(LicensesMetaData.TYPE, LicensesMetaData.PROTO); + @Override + protected Collection> getPlugins() { + return Collections.singletonList(XPackPlugin.class); + } + + @Override + protected Settings nodeSettings() { + return Settings.builder(). + put(XPackPlugin.featureEnabledSetting(Security.NAME), false) + .put(XPackPlugin.featureEnabledSetting(Monitoring.NAME), false) + .put(XPackPlugin.featureEnabledSetting(Watcher.NAME), false) + .put(XPackPlugin.featureEnabledSetting(Graph.NAME), false) + .build(); } @Override @@ -91,7 +111,7 @@ public class LicensesManagerServiceTests extends ESSingleNodeTestCase { // ensure that the invalid license never made it to cluster state LicensesMetaData licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE); if (licensesMetaData != null) { - assertThat(licensesMetaData.getLicense(), equalTo(LicensesMetaData.LICENSE_TOMBSTONE)); + assertThat(licensesMetaData.getLicense(), not(equalTo(tamperedLicense))); } } @@ -185,4 +205,4 @@ public class LicensesManagerServiceTests extends ESSingleNodeTestCase { } assertThat("remove license(s) failed", success.get(), equalTo(true)); } -} +} \ No newline at end of file