WIP: improve plugin integration test; minor changes

Original commit: elastic/x-pack-elasticsearch@39888be13a
This commit is contained in:
Areek Zillur 2014-10-28 18:58:47 -04:00
parent 783970f0e7
commit 05c5e7f48e
4 changed files with 57 additions and 115 deletions

View File

@ -157,7 +157,6 @@ public class ESLicense implements Comparable<ESLicense>, ToXContent {
builder.issuedTo(in.readString()); builder.issuedTo(in.readString());
builder.issuer(in.readString()); builder.issuer(in.readString());
builder.signature(in.readOptionalString()); builder.signature(in.readOptionalString());
builder.verify();
return builder.build(); return builder.build();
} }

View File

@ -60,6 +60,9 @@ public class LicensesMetaData implements MetaData.Custom {
if (obj == null) { if (obj == null) {
return false; return false;
} }
if (obj == this) {
return true;
}
if (obj instanceof LicensesMetaData) { if (obj instanceof LicensesMetaData) {
LicensesMetaData other = (LicensesMetaData) obj; LicensesMetaData other = (LicensesMetaData) obj;
boolean signaturesEqual; boolean signaturesEqual;
@ -136,7 +139,6 @@ public class LicensesMetaData implements MetaData.Custom {
*/ */
@Override @Override
public LicensesMetaData fromXContent(XContentParser parser) throws IOException { public LicensesMetaData fromXContent(XContentParser parser) throws IOException {
XContentParser.Token token; XContentParser.Token token;
String fieldName = null; String fieldName = null;
Set<String> encodedTrialLicenses = new HashSet<>(); Set<String> encodedTrialLicenses = new HashSet<>();

View File

@ -37,7 +37,6 @@ import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -80,7 +79,6 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
this.threadPool = threadPool; this.threadPool = threadPool;
this.transportService = transportService; this.transportService = transportService;
this.lastObservedLicensesState = new AtomicReference<>(null); this.lastObservedLicensesState = new AtomicReference<>(null);
//this.notificationScheduler = new AtomicReference<>(null);
transportService.registerHandler(REGISTER_TRIAL_LICENSE_ACTION_NAME, new RegisterTrialLicenseRequestHandler()); transportService.registerHandler(REGISTER_TRIAL_LICENSE_ACTION_NAME, new RegisterTrialLicenseRequestHandler());
} }
@ -90,7 +88,6 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
* This method can be only called on the master node. It tries to create a new licenses on the master * This method can be only called on the master node. It tries to create a new licenses on the master
* and if provided license(s) is VALID it is added to cluster metadata. * and if provided license(s) is VALID it is added to cluster metadata.
* *
* @return LicensesStatus indicating if the provided license(s) is VALID (accepted), INVALID (tampered license) or EXPIRED
*/ */
@Override @Override
public void registerLicenses(final PutLicenseRequestHolder requestHolder, final ActionListener<LicensesUpdateResponse> listener) { public void registerLicenses(final PutLicenseRequestHolder requestHolder, final ActionListener<LicensesUpdateResponse> listener) {
@ -282,12 +279,6 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
@Override @Override
protected void doClose() throws ElasticsearchException { protected void doClose() throws ElasticsearchException {
logger.info("Closing LicensesService"); logger.info("Closing LicensesService");
/*
if (notificationScheduler.get() != null) {
notificationScheduler.get().cancel(true);
notificationScheduler.set(null);
}
*/
clusterService.remove(this); clusterService.remove(this);
if (registeredListeners != null) { if (registeredListeners != null) {
@ -390,7 +381,7 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
} }
LicensesMetaData currentMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE); LicensesMetaData currentMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
if (!hasLicenseForFeature(listenerHolder.feature, currentMetaData)) { if (expiryDateForFeature(listenerHolder.feature, currentMetaData) == -1l) {
// does not have any license so generate a trial license // does not have any license so generate a trial license
TrialLicenseOptions options = listenerHolder.trialLicenseOptions; TrialLicenseOptions options = listenerHolder.trialLicenseOptions;
if (options != null) { if (options != null) {
@ -428,15 +419,15 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
return true; return true;
} }
private boolean hasLicenseForFeature(String feature, LicensesMetaData currentLicensesMetaData) { private long expiryDateForFeature(String feature, LicensesMetaData currentLicensesMetaData) {
final Map<String, ESLicense> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData); final Map<String, ESLicense> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
ESLicense featureLicense; ESLicense featureLicense;
if ((featureLicense = effectiveLicenses.get(feature)) != null) { if ((featureLicense = effectiveLicenses.get(feature)) != null) {
if (featureLicense.expiryDate() > System.currentTimeMillis()) { logger.info("effective license for "+ feature + " relative expiry: " + TimeValue.timeValueMillis(effectiveLicenses.get(feature).expiryDate() - System.currentTimeMillis()));
return true; return featureLicense.expiryDate();
}
} }
return false; logger.info("no effective license for " + feature);
return -1l;
} }
@ -486,25 +477,6 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
logger.info("Reschedule licensing client notification job was rejected", ex); logger.info("Reschedule licensing client notification job was rejected", ex);
} }
} }
/*
LicensesMetaData currentLicensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
// Change to debug
logger.info("calling notifyFeaturesIfNeeded from LicensingClientNotificationJob");
long nextScheduleFrequency;
if ((nextScheduleFrequency = notifyFeaturesAndScheduleNotification(currentLicensesMetaData)) == -1l) {
return;
}
TimeValue updateFrequency = TimeValue.timeValueMillis(nextScheduleFrequency);
logger.trace("Scheduling next run for licensing client notification job in: {}", updateFrequency.toString());
try {
threadPool.schedule(updateFrequency, executorName(), new SubmitReschedulingLicensingClientNotificationJob());
} catch (EsRejectedExecutionException ex) {
logger.info("Reschedule licensing client notification job was rejected", ex);
}*/
} }
} }
@ -519,13 +491,8 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
sb.append(listenerHolder.feature); sb.append(listenerHolder.feature);
sb.append(", "); sb.append(", ");
long expiryDate = -1l; long expiryDate;
if (hasLicenseForFeature(listenerHolder.feature, currentLicensesMetaData)) { if ((expiryDate = expiryDateForFeature(listenerHolder.feature, currentLicensesMetaData)) != -1l) {
final Map<String, ESLicense> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
final ESLicense license = effectiveLicenses.get(listenerHolder.feature);
expiryDate = license.expiryDate();
sb.append((license.signature() != null) ? "signed" : "trial");
sb.append(" license expiry: "); sb.append(" license expiry: ");
sb.append(expiryDate); sb.append(expiryDate);
sb.append(", "); sb.append(", ");
@ -616,17 +583,13 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
} }
private void enableFeatureIfNeeded() { private void enableFeatureIfNeeded() {
//logger.info("enabled flag: " + enabled.get());
if (enabled.compareAndSet(false, true)) { if (enabled.compareAndSet(false, true)) {
//logger.info("calling onEnabled on listener");
listener.onEnabled(); listener.onEnabled();
} }
} }
private void disableFeatureIfNeeded() { private void disableFeatureIfNeeded() {
//logger.info("enabled flag: " + enabled.get());
if (enabled.compareAndSet(true, false)) { if (enabled.compareAndSet(true, false)) {
//logger.info("calling onDisabled on listener");
listener.onDisabled(); listener.onDisabled();
} }
} }
@ -638,10 +601,12 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
return new LicensesWrapper(licensesMetaData); return new LicensesWrapper(licensesMetaData);
} }
private final LicensesMetaData oldState;
private ImmutableSet<String> signatures = ImmutableSet.of(); private ImmutableSet<String> signatures = ImmutableSet.of();
private ImmutableSet<String> encodedTrialLicenses = ImmutableSet.of(); private ImmutableSet<String> encodedTrialLicenses = ImmutableSet.of();
private LicensesWrapper(LicensesMetaData licensesMetaData) { private LicensesWrapper(LicensesMetaData licensesMetaData) {
this.oldState = licensesMetaData;
if (licensesMetaData != null) { if (licensesMetaData != null) {
this.signatures = ImmutableSet.copyOf(licensesMetaData.getSignatures()); this.signatures = ImmutableSet.copyOf(licensesMetaData.getSignatures());
this.encodedTrialLicenses = ImmutableSet.copyOf(licensesMetaData.getEncodedTrialLicenses()); this.encodedTrialLicenses = ImmutableSet.copyOf(licensesMetaData.getEncodedTrialLicenses());
@ -679,17 +644,15 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
public void addSignedLicenses(ESLicenseManager licenseManager, Set<ESLicense> newLicenses) { public void addSignedLicenses(ESLicenseManager licenseManager, Set<ESLicense> newLicenses) {
Set<ESLicense> currentSignedLicenses = signedLicenses(licenseManager); Set<ESLicense> currentSignedLicenses = signedLicenses(licenseManager);
final ImmutableMap<String, ESLicense> licenseMap = reduceAndMap(Sets.union(currentSignedLicenses, newLicenses)); this.signatures = licenseManager.toSignatures(Sets.union(currentSignedLicenses, newLicenses));
this.signatures = licenseManager.toSignatures(licenseMap.values());
} }
public void removeFeatures(ESLicenseManager licenseManager, Set<String> featuresToDelete) { public void removeFeatures(ESLicenseManager licenseManager, Set<String> featuresToDelete) {
Set<ESLicense> currentSignedLicenses = signedLicenses(licenseManager); Set<ESLicense> currentSignedLicenses = signedLicenses(licenseManager);
final ImmutableMap<String, ESLicense> licenseMap = reduceAndMap(currentSignedLicenses);
Set<ESLicense> licensesToDelete = new HashSet<>(); Set<ESLicense> licensesToDelete = new HashSet<>();
for (Map.Entry<String, ESLicense> entry : licenseMap.entrySet()) { for (ESLicense license : currentSignedLicenses) {
if (featuresToDelete.contains(entry.getKey())) { if (featuresToDelete.contains(license.feature())) {
licensesToDelete.add(entry.getValue()); licensesToDelete.add(license);
} }
} }
Set<ESLicense> reducedLicenses = Sets.difference(currentSignedLicenses, licensesToDelete); Set<ESLicense> reducedLicenses = Sets.difference(currentSignedLicenses, licensesToDelete);
@ -697,6 +660,11 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
} }
public LicensesMetaData get() { public LicensesMetaData get() {
if (oldState != null) {
if (oldState.getSignatures().equals(signatures) && oldState.getEncodedTrialLicenses().equals(encodedTrialLicenses)) {
return oldState;
}
}
return new LicensesMetaData(signatures, encodedTrialLicenses); return new LicensesMetaData(signatures, encodedTrialLicenses);
} }
} }
@ -753,10 +721,6 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
//Should not be exposed; used by testing only //Should not be exposed; used by testing only
public void clear() { public void clear() {
/*if (notificationScheduler.get() != null) {
notificationScheduler.get().cancel(true);
notificationScheduler.set(null);
}*/
registeredListeners.clear(); registeredListeners.clear();
} }
} }

View File

@ -6,12 +6,16 @@
package org.elasticsearch.license.plugin; package org.elasticsearch.license.plugin;
import org.elasticsearch.common.base.Predicate; import org.elasticsearch.common.base.Predicate;
import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.core.ESLicense;
import org.elasticsearch.license.licensor.ESLicenseSigner; import org.elasticsearch.license.licensor.ESLicenseSigner;
import org.elasticsearch.license.plugin.core.LicensesManagerService; import org.elasticsearch.license.plugin.core.LicensesManagerService;
import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder;
import org.elasticsearch.license.plugin.action.put.PutLicenseResponse;
import org.elasticsearch.license.plugin.core.LicensesStatus;
import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.test.InternalTestCluster;
import org.junit.Test; import org.junit.Test;
@ -24,7 +28,7 @@ import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST; import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
@ClusterScope(scope = TEST, numDataNodes = 3, numClientNodes = 0) @ClusterScope(scope = TEST, numDataNodes = 10, numClientNodes = 0)
public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationTests { public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationTests {
private final int trialLicenseDurationInSeconds = 5; private final int trialLicenseDurationInSeconds = 5;
@ -39,82 +43,55 @@ public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationT
} }
@Test @Test
public void test() throws Exception { public void test1() throws Exception {
logger.info(" --> trial license generated");
// managerService should report feature to be enabled on all data nodes // managerService should report feature to be enabled on all data nodes
assertThat(awaitBusy(new Predicate<Object>() { assertLicenseManagerEnabledFeatureFor(TestPluginService.FEATURE_NAME);
@Override
public boolean apply(Object o) {
for (LicensesManagerService managerService : licensesManagerServices()) {
if (!managerService.enabledFeatures().contains(TestPluginService.FEATURE_NAME)) {
return false;
}
}
return true;
}
}, 2, TimeUnit.SECONDS), equalTo(true));
// consumer plugin service should return enabled on all data nodes // consumer plugin service should return enabled on all data nodes
/*assertThat(awaitBusy(new Predicate<Object>() {
@Override
public boolean apply(Object o) {
for (TestPluginService pluginService : consumerPluginServices()) {
if (!pluginService.enabled()) {
return false;
}
}
return true;
}
}, 2, TimeUnit.SECONDS), equalTo(true));*/
assertConsumerPluginEnableNotification(2); assertConsumerPluginEnableNotification(2);
logger.info(" --> check trial license expiry notification");
// consumer plugin should notify onDisabled on all data nodes (expired trial license) // consumer plugin should notify onDisabled on all data nodes (expired trial license)
assertThat(awaitBusy(new Predicate<Object>() { assertConsumerPluginDisableNotification(trialLicenseDurationInSeconds * 2);
@Override assertLicenseManagerDisabledFeatureFor(TestPluginService.FEATURE_NAME);
public boolean apply(Object o) {
for (TestPluginService pluginService : consumerPluginServices()) {
if(pluginService.enabled()) {
return false;
}
} logger.info(" --> put signed license");
return true;
}
}, trialLicenseDurationInSeconds * 2, TimeUnit.SECONDS), equalTo(true));
// consumer plugin should notify onEnabled on all data nodes (signed license)
/*
ESLicense license = generateSignedLicense(TestPluginService.FEATURE_NAME, TimeValue.timeValueSeconds(5)); ESLicense license = generateSignedLicense(TestPluginService.FEATURE_NAME, TimeValue.timeValueSeconds(5));
final PutLicenseResponse putLicenseResponse = new PutLicenseRequestBuilder(client().admin().cluster()).setLicense(Lists.newArrayList(license)).get(); final PutLicenseResponse putLicenseResponse = new PutLicenseRequestBuilder(client().admin().cluster()).setLicense(Lists.newArrayList(license)).get();
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true)); assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID)); assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
logger.info(" --> put signed license"); logger.info(" --> check signed license enabled notification");
// consumer plugin should notify onEnabled on all data nodes (signed license)
assertConsumerPluginEnableNotification(2);
assertLicenseManagerEnabledFeatureFor(TestPluginService.FEATURE_NAME);
logger.info(" --> check signed license expiry notification");
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
assertConsumerPluginDisableNotification(5);
assertLicenseManagerDisabledFeatureFor(TestPluginService.FEATURE_NAME);
}
private void assertLicenseManagerEnabledFeatureFor(final String feature) throws InterruptedException {
assertLicenseManagerStatusFor(feature, true);
}
private void assertLicenseManagerDisabledFeatureFor(final String feature) throws InterruptedException {
assertLicenseManagerStatusFor(feature, false);
}
private void assertLicenseManagerStatusFor(final String feature, final boolean expectedEnabled) throws InterruptedException {
assertThat(awaitBusy(new Predicate<Object>() { assertThat(awaitBusy(new Predicate<Object>() {
@Override @Override
public boolean apply(Object o) { public boolean apply(Object o) {
for (TestPluginService pluginService : consumerPluginServices()) { for (LicensesManagerService managerService : licensesManagerServices()) {
if (!pluginService.enabled()) { if (expectedEnabled != managerService.enabledFeatures().contains(feature)) {
return false; return false;
} }
} }
return true; return true;
} }
}, 2, TimeUnit.SECONDS), equalTo(true)); }, 2, TimeUnit.SECONDS), equalTo(true));
assertThat(awaitBusy(new Predicate<Object>() {
@Override
public boolean apply(Object o) {
for (TestPluginService pluginService : consumerPluginServices()) {
if (pluginService.enabled()) {
return false;
}
}
return true;
}
}, 5, TimeUnit.SECONDS), equalTo(true));
*/
} }
private void assertConsumerPluginDisableNotification(int timeoutInSec) throws InterruptedException { private void assertConsumerPluginDisableNotification(int timeoutInSec) throws InterruptedException {
@ -142,7 +119,7 @@ public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationT
final ESLicense licenseSpec = ESLicense.builder() final ESLicense licenseSpec = ESLicense.builder()
.uid(UUID.randomUUID().toString()) .uid(UUID.randomUUID().toString())
.feature(feature) .feature(feature)
.expiryDate(expiryDate.getMillis()) .expiryDate(System.currentTimeMillis() + expiryDate.getMillis())
.issueDate(System.currentTimeMillis()) .issueDate(System.currentTimeMillis())
.type("subscription") .type("subscription")
.subscriptionType("gold") .subscriptionType("gold")