Make LicensesService tests more robust

- split out LicensesManagerService & LicensesClientService tests
 - add removeLicenses tests
 - other refactoring

Original commit: elastic/x-pack-elasticsearch@a47dc586d7
This commit is contained in:
Areek Zillur 2014-11-06 14:49:35 -05:00
parent 42d47a1bcc
commit 8e9574a925
11 changed files with 382 additions and 269 deletions

View File

@ -15,7 +15,7 @@
</parent> </parent>
<properties> <properties>
<elasticsearch.version>1.4.0-SNAPSHOT</elasticsearch.version> <elasticsearch.version>1.4.0</elasticsearch.version>
<lucene.version>4.10.2</lucene.version> <lucene.version>4.10.2</lucene.version>
<!-- TODO: do we want to always enforce non-default keys --> <!-- TODO: do we want to always enforce non-default keys -->
<keys.path>${basedir}/src/test/resources</keys.path> <keys.path>${basedir}/src/test/resources</keys.path>

View File

@ -30,7 +30,7 @@ import java.util.Set;
public class ESLicenseSigner { public class ESLicenseSigner {
public static String DEFAULT_PASS_PHRASE = "elasticsearch-license"; public final static String DEFAULT_PASS_PHRASE = "elasticsearch-license";
private final static int VERSION_START = 0; private final static int VERSION_START = 0;
private final static int VERSION = VERSION_START; private final static int VERSION = VERSION_START;

View File

@ -43,7 +43,7 @@ public class KeyPairGeneratorTool extends CliTool {
private static class KeyPairGenerator extends Command { private static class KeyPairGenerator extends Command {
public static String DEFAULT_PASS_PHRASE = "elasticsearch-license"; public static final String DEFAULT_PASS_PHRASE = "elasticsearch-license";
private static final String NAME = "key-pair-generator"; private static final String NAME = "key-pair-generator";
private static final CliToolConfig.Cmd CMD = cmd(NAME, KeyPairGenerator.class) private static final CliToolConfig.Cmd CMD = cmd(NAME, KeyPairGenerator.class)
.options( .options(

View File

@ -76,9 +76,9 @@ public class LicenseGeneratorTool extends CliTool {
String[] licenseSpecSources = commandLine.getOptionValues("license"); String[] licenseSpecSources = commandLine.getOptionValues("license");
String[] licenseSpecSourceFiles = commandLine.getOptionValues("licenseFile"); String[] licenseSpecSourceFiles = commandLine.getOptionValues("licenseFile");
if (!exists(privateKeyPath)) { if (doesNotExist(privateKeyPath)) {
return exitCmd(ExitStatus.USAGE, terminal, privateKeyPath + " does not exist"); return exitCmd(ExitStatus.USAGE, terminal, privateKeyPath + " does not exist");
} else if (!exists(publicKeyPath)) { } else if (doesNotExist(publicKeyPath)) {
return exitCmd(ExitStatus.USAGE, terminal, publicKeyPath + " does not exist"); return exitCmd(ExitStatus.USAGE, terminal, publicKeyPath + " does not exist");
} }
@ -92,7 +92,7 @@ public class LicenseGeneratorTool extends CliTool {
if (licenseSpecSourceFiles != null) { if (licenseSpecSourceFiles != null) {
for (String licenseSpecFilePath : licenseSpecSourceFiles) { for (String licenseSpecFilePath : licenseSpecSourceFiles) {
Path licenseSpecPath = Paths.get(licenseSpecFilePath); Path licenseSpecPath = Paths.get(licenseSpecFilePath);
if (!exists(licenseSpecFilePath)) { if (doesNotExist(licenseSpecFilePath)) {
return exitCmd(ExitStatus.USAGE, terminal, licenseSpecFilePath + " does not exist"); return exitCmd(ExitStatus.USAGE, terminal, licenseSpecFilePath + " does not exist");
} }
licenseSpecs.addAll(ESLicenses.fromSource(Files.readAllBytes(licenseSpecPath), false)); licenseSpecs.addAll(ESLicenses.fromSource(Files.readAllBytes(licenseSpecPath), false));
@ -122,8 +122,8 @@ public class LicenseGeneratorTool extends CliTool {
} }
private static boolean exists(String filePath) { private static boolean doesNotExist(String filePath) {
return new File(filePath).exists(); return !new File(filePath).exists();
} }
} }

View File

@ -169,7 +169,7 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
} }
public static class LicensesUpdateResponse extends ClusterStateUpdateResponse { public static class LicensesUpdateResponse extends ClusterStateUpdateResponse {
private LicensesStatus status; private final LicensesStatus status;
public LicensesUpdateResponse(boolean acknowledged, LicensesStatus status) { public LicensesUpdateResponse(boolean acknowledged, LicensesStatus status) {
super(acknowledged); super(acknowledged);

View File

@ -12,7 +12,7 @@ public enum LicensesStatus {
INVALID((byte) 1), INVALID((byte) 1),
EXPIRED((byte) 2); EXPIRED((byte) 2);
private byte id; private final byte id;
LicensesStatus(byte id) { LicensesStatus(byte id) {
this.id = id; this.id = id;

View File

@ -30,6 +30,7 @@ import static org.elasticsearch.license.licensor.tools.LicenseGeneratorTool.Comm
import static org.elasticsearch.license.licensor.tools.LicenseGeneratorTool.LicenseGenerator; import static org.elasticsearch.license.licensor.tools.LicenseGeneratorTool.LicenseGenerator;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsEqual.equalTo;
public class LicenseGenerationToolTests extends CliToolTestCase { public class LicenseGenerationToolTests extends CliToolTestCase {
@ -134,13 +135,14 @@ public class LicenseGenerationToolTests extends CliToolTestCase {
@Test @Test
public void testParsingMultipleLicense() throws Exception { public void testParsingMultipleLicense() throws Exception {
int n = randomIntBetween(2, 5); int n = randomIntBetween(2, 5);
List<LicenseSpec> inputLicenseSpecs = new ArrayList<>(); Map<String, LicenseSpec> inputLicenseSpecs = new HashMap<>();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
inputLicenseSpecs.add(generateRandomLicenseSpec()); LicenseSpec licenseSpec = generateRandomLicenseSpec();
inputLicenseSpecs.put(licenseSpec.feature, licenseSpec);
} }
LicenseGeneratorTool licenseGeneratorTool = new LicenseGeneratorTool(); LicenseGeneratorTool licenseGeneratorTool = new LicenseGeneratorTool();
Command command = licenseGeneratorTool.parse(LicenseGeneratorTool.NAME, Command command = licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
args("--license " + generateESLicenseSpecString(inputLicenseSpecs) args("--license " + generateESLicenseSpecString(new ArrayList<>(inputLicenseSpecs.values()))
+ " --publicKeyPath " + pubKeyPath + " --publicKeyPath " + pubKeyPath
+ " --privateKeyPath " + priKeyPath)); + " --privateKeyPath " + priKeyPath));
@ -148,44 +150,33 @@ public class LicenseGenerationToolTests extends CliToolTestCase {
LicenseGenerator licenseGenerator = (LicenseGenerator) command; LicenseGenerator licenseGenerator = (LicenseGenerator) command;
assertThat(licenseGenerator.publicKeyFilePath, equalTo(pubKeyPath)); assertThat(licenseGenerator.publicKeyFilePath, equalTo(pubKeyPath));
assertThat(licenseGenerator.privateKeyFilePath, equalTo(priKeyPath)); assertThat(licenseGenerator.privateKeyFilePath, equalTo(priKeyPath));
assertThat(licenseGenerator.licenseSpecs.size(), equalTo(n)); assertThat(licenseGenerator.licenseSpecs.size(), equalTo(inputLicenseSpecs.size()));
for (LicenseSpec inputSpec : inputLicenseSpecs) { for (ESLicense outputLicenseSpec : licenseGenerator.licenseSpecs) {
boolean found = false; LicenseSpec inputLicenseSpec = inputLicenseSpecs.get(outputLicenseSpec.feature());
for (ESLicense outputSpec : licenseGenerator.licenseSpecs) { assertThat(inputLicenseSpec, notNullValue());
if (inputSpec.uid.equals(outputSpec.uid())) { assertLicenseSpec(inputLicenseSpec, outputLicenseSpec);
assertLicenseSpec(inputSpec, outputSpec);
found = true;
break;
}
}
assertThat(found, equalTo(true));
} }
} }
@Test @Test
public void testTool() throws Exception { public void testTool() throws Exception {
int n = randomIntBetween(1, 5); int n = randomIntBetween(1, 5);
List<LicenseSpec> inputLicenseSpecs = new ArrayList<>(); Map<String, LicenseSpec> inputLicenseSpecs = new HashMap<>();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
inputLicenseSpecs.add(generateRandomLicenseSpec()); LicenseSpec licenseSpec = generateRandomLicenseSpec();
inputLicenseSpecs.put(licenseSpec.feature, licenseSpec);
} }
List<ESLicense> licenseSpecs = ESLicenses.fromSource(generateESLicenseSpecString(inputLicenseSpecs).getBytes(StandardCharsets.UTF_8), false); List<ESLicense> licenseSpecs = ESLicenses.fromSource(generateESLicenseSpecString(new ArrayList<>(inputLicenseSpecs.values())).getBytes(StandardCharsets.UTF_8), false);
String output = runLicenseGenerationTool(pubKeyPath, priKeyPath, new HashSet<>(licenseSpecs), ExitStatus.OK); String output = runLicenseGenerationTool(pubKeyPath, priKeyPath, new HashSet<>(licenseSpecs), ExitStatus.OK);
List<ESLicense> outputLicenses = ESLicenses.fromSource(output.getBytes(StandardCharsets.UTF_8), true); List<ESLicense> outputLicenses = ESLicenses.fromSource(output.getBytes(StandardCharsets.UTF_8), true);
assertThat(outputLicenses.size(), equalTo(n)); assertThat(outputLicenses.size(), equalTo(inputLicenseSpecs.size()));
for (LicenseSpec inputSpec : inputLicenseSpecs) { for (ESLicense outputLicense : outputLicenses) {
boolean found = false; LicenseSpec inputLicenseSpec = inputLicenseSpecs.get(outputLicense.feature());
for (ESLicense license : outputLicenses) { assertThat(inputLicenseSpec, notNullValue());
if (inputSpec.uid.equals(license.uid())) { assertLicenseSpec(inputLicenseSpec, outputLicense);
assertLicenseSpec(inputSpec, license);
found = true;
break;
}
}
assertThat(found, equalTo(true));
} }
} }

View File

@ -25,16 +25,14 @@ import org.junit.rules.TemporaryFolder;
import java.io.File; import java.io.File;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Collections; import java.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.elasticsearch.common.cli.CliTool.Command; import static org.elasticsearch.common.cli.CliTool.Command;
import static org.elasticsearch.common.cli.CliTool.ExitStatus; import static org.elasticsearch.common.cli.CliTool.ExitStatus;
import static org.elasticsearch.license.AbstractLicensingTestBase.generateSignedLicense; import static org.elasticsearch.license.AbstractLicensingTestBase.generateSignedLicense;
import static org.elasticsearch.license.licensor.tools.LicenseVerificationTool.LicenseVerifier; import static org.elasticsearch.license.licensor.tools.LicenseVerificationTool.LicenseVerifier;
import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsEqual.equalTo;
public class LicenseVerificationToolTests extends CliToolTestCase { public class LicenseVerificationToolTests extends CliToolTestCase {
@ -85,14 +83,15 @@ public class LicenseVerificationToolTests extends CliToolTestCase {
@Test @Test
public void testParsingMultipleLicense() throws Exception { public void testParsingMultipleLicense() throws Exception {
int n = randomIntBetween(2, 5); int n = randomIntBetween(2, 5);
Set<ESLicense> inputLicenses = new HashSet<>(); Map<String, ESLicense> inputLicenses = new HashMap<>();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
inputLicenses.add(generateSignedLicense(randomRealisticUnicodeOfCodepointLengthBetween(5, 15), ESLicense esLicense = generateSignedLicense(randomRealisticUnicodeOfCodepointLengthBetween(5, 15),
TimeValue.timeValueHours(1))); TimeValue.timeValueHours(1));
inputLicenses.put(esLicense.feature(), esLicense);
} }
StringBuilder argsBuilder = new StringBuilder(); StringBuilder argsBuilder = new StringBuilder();
for (ESLicense inputLicense : inputLicenses) { for (ESLicense inputLicense : inputLicenses.values()) {
argsBuilder.append(" --license ") argsBuilder.append(" --license ")
.append(dumpLicense(inputLicense)); .append(dumpLicense(inputLicense));
} }
@ -101,44 +100,33 @@ public class LicenseVerificationToolTests extends CliToolTestCase {
assertThat(command, instanceOf(LicenseVerifier.class)); assertThat(command, instanceOf(LicenseVerifier.class));
LicenseVerifier licenseVerifier = (LicenseVerifier) command; LicenseVerifier licenseVerifier = (LicenseVerifier) command;
assertThat(licenseVerifier.licenses.size(), equalTo(n)); assertThat(licenseVerifier.licenses.size(), equalTo(inputLicenses.size()));
for (ESLicense inputLicense : inputLicenses) {
boolean found = false;
for (ESLicense outputLicense : licenseVerifier.licenses) { for (ESLicense outputLicense : licenseVerifier.licenses) {
if (inputLicense.uid().equals(outputLicense.uid())) { ESLicense inputLicense = inputLicenses.get(outputLicense.feature());
assertThat(inputLicense, notNullValue());
TestUtils.isSame(inputLicense, outputLicense); TestUtils.isSame(inputLicense, outputLicense);
found = true;
break;
}
}
assertThat(found, equalTo(true));
} }
} }
@Test @Test
public void testToolSimple() throws Exception { public void testToolSimple() throws Exception {
int n = randomIntBetween(2, 5); int n = randomIntBetween(2, 5);
Set<ESLicense> inputLicenses = new HashSet<>(); Map<String, ESLicense> inputLicenses = new HashMap<>();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
inputLicenses.add(generateSignedLicense(randomRealisticUnicodeOfCodepointLengthBetween(5, 15), ESLicense esLicense = generateSignedLicense(randomRealisticUnicodeOfCodepointLengthBetween(5, 15),
TimeValue.timeValueHours(1))); TimeValue.timeValueHours(1));
inputLicenses.put(esLicense.feature(), esLicense);
} }
String output = runLicenseVerificationTool(inputLicenses, ExitStatus.OK); String output = runLicenseVerificationTool(new HashSet<>(inputLicenses.values()), ExitStatus.OK);
List<ESLicense> outputLicenses = ESLicenses.fromSource(output.getBytes(StandardCharsets.UTF_8), true); List<ESLicense> outputLicenses = ESLicenses.fromSource(output.getBytes(StandardCharsets.UTF_8), true);
assertThat(outputLicenses.size(), equalTo(n)); assertThat(outputLicenses.size(), equalTo(inputLicenses.size()));
for (ESLicense inputLicense : inputLicenses) {
boolean found = false;
for (ESLicense outputLicense : outputLicenses) { for (ESLicense outputLicense : outputLicenses) {
if (inputLicense.uid().equals(outputLicense.uid())) { ESLicense inputLicense = inputLicenses.get(outputLicense.feature());
assertThat(inputLicense, notNullValue());
TestUtils.isSame(inputLicense, outputLicense); TestUtils.isSame(inputLicense, outputLicense);
found = true;
break;
}
}
assertThat(found, equalTo(true));
} }
} }

View File

@ -0,0 +1,171 @@
/*
* 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;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.core.ESLicense;
import org.elasticsearch.license.plugin.action.put.PutLicenseRequest;
import org.elasticsearch.license.plugin.core.LicensesClientService;
import org.elasticsearch.license.plugin.core.LicensesManagerService;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.license.plugin.core.LicensesStatus;
import org.elasticsearch.test.InternalTestCluster;
import org.junit.Before;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.elasticsearch.license.plugin.core.LicensesService.LicensesUpdateResponse;
import static org.hamcrest.Matchers.equalTo;
public class AbstractLicensesServiceTests extends AbstractLicensesIntegrationTests {
private static String node = null;
private static String[] nodes;
@Before
public void beforeTest() throws Exception {
wipeAllLicenses();
DiscoveryNodes discoveryNodes = masterClusterService().state().getNodes();
Set<String> dataNodeSet = new HashSet<>();
for (DiscoveryNode discoveryNode : discoveryNodes) {
if (discoveryNode.dataNode()) {
dataNodeSet.add(discoveryNode.getName());
}
}
nodes = dataNodeSet.toArray(new String[dataNodeSet.size()]);
node = nodes[randomIntBetween(0, nodes.length - 1)];
}
protected void registerAndAckSignedLicenses(final LicensesManagerService masterLicensesManagerService, final List<ESLicense> license, final LicensesStatus expectedStatus) {
PutLicenseRequest putLicenseRequest = new PutLicenseRequest().licenses(license);
LicensesService.PutLicenseRequestHolder requestHolder = new LicensesService.PutLicenseRequestHolder(putLicenseRequest, "test");
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean success = new AtomicBoolean(false);
masterLicensesManagerService.registerLicenses(requestHolder, new ActionListener<LicensesUpdateResponse>() {
@Override
public void onResponse(LicensesUpdateResponse licensesUpdateResponse) {
if (licensesUpdateResponse.isAcknowledged() && licensesUpdateResponse.status() == expectedStatus) {
success.set(true);
}
latch.countDown();
}
@Override
public void onFailure(Throwable e) {
latch.countDown();
}
});
try {
latch.await();
} catch (InterruptedException e) {
fail(e.getMessage());
}
assertThat("register license(s) failed", success.get(), equalTo(true));
}
protected Action registerWithTrialLicense(final LicensesClientService clientService, final LicensesClientService.Listener clientListener, final String feature, final TimeValue expiryDuration) {
return new Action(new Runnable() {
@Override
public void run() {
clientService.register(feature, new LicensesService.TrialLicenseOptions(expiryDuration, 10),
clientListener);
// invoke clusterChanged event to flush out pendingRegistration
LicensesService licensesService = (LicensesService) clientService;
ClusterChangedEvent event = new ClusterChangedEvent("", clusterService().state(), clusterService().state());
licensesService.clusterChanged(event);
}
}, 0, 1, "should trigger onEnable for " + feature + " once [trial license]");
}
protected class TestTrackingClientListener implements LicensesClientService.Listener {
CountDownLatch enableLatch;
CountDownLatch disableLatch;
final boolean track;
public TestTrackingClientListener(boolean track) {
this.track = track;
}
public TestTrackingClientListener() {
this(true);
}
public synchronized void latch(CountDownLatch enableLatch, CountDownLatch disableLatch) {
this.enableLatch = enableLatch;
this.disableLatch = disableLatch;
}
@Override
public void onEnabled() {
if (track) {
this.enableLatch.countDown();
}
}
@Override
public void onDisabled() {
if (track) {
this.disableLatch.countDown();
}
}
}
protected class Action {
final int expectedDisabledCount;
final int expectedEnabledCount;
final TimeValue timeout;
final Runnable action;
final String msg;
protected Action(Runnable action, int expectedEnabledCount, int expectedDisabledCount, String msg) {
this(action, expectedEnabledCount, expectedDisabledCount, TimeValue.timeValueSeconds(1), msg);
}
protected Action(Runnable action, int expectedDisabledCount, int expectedEnabledCount, TimeValue timeout, String msg) {
this.expectedDisabledCount = expectedDisabledCount;
this.expectedEnabledCount = expectedEnabledCount;
this.action = action;
this.timeout = timeout;
this.msg = msg;
}
public void run() {
action.run();
}
}
protected LicensesManagerService masterLicensesManagerService() {
final InternalTestCluster clients = internalCluster();
return clients.getInstance(LicensesManagerService.class, clients.getMasterName());
}
protected LicensesClientService licensesClientService() {
return internalCluster().getInstance(LicensesClientService.class, node);
}
protected LicensesService randomLicensesService() {
String randomNode = randomFrom(nodes);
return internalCluster().getInstance(LicensesService.class, randomNode);
}
protected static ClusterService masterClusterService() {
final InternalTestCluster clients = internalCluster();
return clients.getInstance(ClusterService.class, clients.getMasterName());
}
}

View File

@ -5,98 +5,26 @@
*/ */
package org.elasticsearch.license.plugin; package org.elasticsearch.license.plugin;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.common.collect.ImmutableSet;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.TestUtils;
import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.core.ESLicense;
import org.elasticsearch.license.manager.ESLicenseManager; import org.elasticsearch.license.plugin.core.LicensesClientService;
import org.elasticsearch.license.plugin.action.put.PutLicenseRequest; import org.elasticsearch.license.plugin.core.LicensesManagerService;
import org.elasticsearch.license.plugin.core.*; import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.license.plugin.core.LicensesStatus;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.*; import java.util.*;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import static org.elasticsearch.license.plugin.core.LicensesService.LicensesUpdateResponse;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; 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.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
@ClusterScope(scope = TEST, numDataNodes = 10) @ClusterScope(scope = TEST, numDataNodes = 10)
public class LicensesServiceTests extends AbstractLicensesIntegrationTests { public class LicensesClientServiceTests extends AbstractLicensesServiceTests {
private static String node = null;
private static String[] nodes;
@Before
public void beforeTest() throws Exception {
wipeAllLicenses();
DiscoveryNodes discoveryNodes = LicensesServiceTests.masterClusterService().state().getNodes();
Set<String> dataNodeSet = new HashSet<>();
for (DiscoveryNode discoveryNode : discoveryNodes) {
if (discoveryNode.dataNode()) {
dataNodeSet.add(discoveryNode.getName());
}
}
nodes = dataNodeSet.toArray(new String[dataNodeSet.size()]);
node = nodes[randomIntBetween(0, nodes.length - 1)];
}
@Test
public void testStoreAndGetLicenses() throws Exception {
LicensesManagerService licensesManagerService = masterLicensesManagerService();
ESLicense shieldShortLicense = generateSignedLicense("shield", TimeValue.timeValueHours(1));
ESLicense shieldLongLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2));
ESLicense marvelShortLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(1));
ESLicense marvelLongLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(2));
List<ESLicense> licenses = Arrays.asList(shieldLongLicense, shieldShortLicense, marvelLongLicense, marvelShortLicense);
Collections.shuffle(licenses);
putAndCheckSignedLicensesAction(licensesManagerService, licenses, LicensesStatus.VALID);
final ImmutableSet<String> licenseSignatures = masterLicenseManager().toSignatures(licenses);
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
// all licenses should be stored in the metaData
assertThat(licenseSignatures, equalTo(licensesMetaData.getSignatures()));
// only the latest expiry date license for each feature should be returned by getLicenses()
final List<ESLicense> getLicenses = licensesManagerService.getLicenses();
TestUtils.isSame(getLicenses, Arrays.asList(shieldLongLicense, marvelLongLicense));
}
@Test
public void testInvalidLicenseStorage() throws Exception {
LicensesManagerService licensesManagerService = masterLicensesManagerService();
ESLicense signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
// modify content of signed license
ESLicense tamperedLicense = ESLicense.builder()
.fromLicenseSpec(signedLicense, signedLicense.signature())
.expiryDate(signedLicense.expiryDate() + 10 * 24 * 60 * 60 * 1000l)
.verify()
.build();
putAndCheckSignedLicensesAction(licensesManagerService, Arrays.asList(tamperedLicense), LicensesStatus.INVALID);
// ensure that the invalid license never made it to cluster state
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
if (licensesMetaData != null) {
assertThat(licensesMetaData.getSignatures().size(), equalTo(0));
}
}
@Test @Test
public void testTrialLicenseEnforcement() throws Exception { public void testTrialLicenseEnforcement() throws Exception {
@ -260,33 +188,6 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
} }
private void putAndCheckSignedLicensesAction(final LicensesManagerService masterLicensesManagerService, final List<ESLicense> license, final LicensesStatus expectedStatus) {
PutLicenseRequest putLicenseRequest = new PutLicenseRequest().licenses(license);
LicensesService.PutLicenseRequestHolder requestHolder = new LicensesService.PutLicenseRequestHolder(putLicenseRequest, "test");
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean success = new AtomicBoolean(false);
masterLicensesManagerService.registerLicenses(requestHolder, new ActionListener<LicensesUpdateResponse>() {
@Override
public void onResponse(LicensesUpdateResponse licensesUpdateResponse) {
if (licensesUpdateResponse.isAcknowledged() && licensesUpdateResponse.status() == expectedStatus) {
success.set(true);
latch.countDown();
}
}
@Override
public void onFailure(Throwable e) {
latch.countDown();
}
});
try {
latch.await();
} catch (InterruptedException e) {
fail(e.getMessage());
}
assertThat(success.get(), equalTo(true));
}
private Action generateAndPutSignedLicenseAction(final LicensesManagerService masterLicensesManagerService, final String feature, final TimeValue expiryDuration) throws Exception { private Action generateAndPutSignedLicenseAction(final LicensesManagerService masterLicensesManagerService, final String feature, final TimeValue expiryDuration) throws Exception {
return new Action(new Runnable() { return new Action(new Runnable() {
@Override @Override
@ -298,7 +199,7 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
fail(e.getMessage()); fail(e.getMessage());
return; return;
} }
putAndCheckSignedLicensesAction(masterLicensesManagerService, Arrays.asList(license), LicensesStatus.VALID); registerAndAckSignedLicenses(masterLicensesManagerService, Arrays.asList(license), LicensesStatus.VALID);
} }
}, 0, 1, "should trigger onEnable for " + feature + " once [signed license]"); }, 0, 1, "should trigger onEnable for " + feature + " once [signed license]");
} }
@ -312,21 +213,6 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
}, 0, 0, "should not trigger any notification [disabled by default]"); }, 0, 0, "should not trigger any notification [disabled by default]");
} }
private Action registerWithTrialLicense(final LicensesClientService clientService, final LicensesClientService.Listener clientListener, final String feature, final TimeValue expiryDuration) {
return new Action(new Runnable() {
@Override
public void run() {
clientService.register(feature, new LicensesService.TrialLicenseOptions(expiryDuration, 10),
clientListener);
// invoke clusterChanged event to flush out pendingRegistration
LicensesService licensesService = (LicensesService) clientService;
ClusterChangedEvent event = new ClusterChangedEvent("", clusterService().state(), clusterService().state());
licensesService.clusterChanged(event);
}
}, 0, 1, "should trigger onEnable for " + feature + " once [trial license]");
}
private Action assertExpiryAction(String feature, String licenseType, TimeValue expiryDuration) { private Action assertExpiryAction(String feature, String licenseType, TimeValue expiryDuration) {
return new Action(new Runnable() { return new Action(new Runnable() {
@Override @Override
@ -362,83 +248,14 @@ public class LicensesServiceTests extends AbstractLicensesIntegrationTests {
} }
} }
private static String getActionMsg(final boolean enabledCount,final long latchCount,final List<Action> actions) { private static String getActionMsg(final boolean enabledCount, final long latchCount, final List<Action> actions) {
AtomicLong cumulativeCount = new AtomicLong(0); AtomicLong cumulativeCount = new AtomicLong(0);
for (Action action : actions) { for (Action action : actions) {
cumulativeCount.addAndGet((enabledCount)? action.expectedEnabledCount : action.expectedDisabledCount); cumulativeCount.addAndGet((enabledCount) ? action.expectedEnabledCount : action.expectedDisabledCount);
if (latchCount <= cumulativeCount.get()) { if (latchCount <= cumulativeCount.get()) {
return action.msg; return action.msg;
} }
} }
return "there should be no errors"; return "there should be no errors";
} }
private class TestTrackingClientListener implements LicensesClientService.Listener {
CountDownLatch enableLatch;
CountDownLatch disableLatch;
public synchronized void latch(CountDownLatch enableLatch, CountDownLatch disableLatch) {
this.enableLatch = enableLatch;
this.disableLatch = disableLatch;
}
@Override
public void onEnabled() {
this.enableLatch.countDown();
}
@Override
public void onDisabled() {
this.disableLatch.countDown();
}
}
private class Action {
final int expectedDisabledCount;
final int expectedEnabledCount;
final TimeValue timeout;
final Runnable action;
final String msg;
private Action(Runnable action, int expectedEnabledCount, int expectedDisabledCount, String msg) {
this(action, expectedEnabledCount, expectedDisabledCount, TimeValue.timeValueSeconds(1), msg);
}
private Action(Runnable action, int expectedDisabledCount, int expectedEnabledCount, TimeValue timeout, String msg) {
this.expectedDisabledCount = expectedDisabledCount;
this.expectedEnabledCount = expectedEnabledCount;
this.action = action;
this.timeout = timeout;
this.msg = msg;
}
public void run() {
action.run();
}
}
private LicensesManagerService masterLicensesManagerService() {
final InternalTestCluster clients = internalCluster();
return clients.getInstance(LicensesManagerService.class, clients.getMasterName());
}
private ESLicenseManager masterLicenseManager() {
final InternalTestCluster clients = internalCluster();
return clients.getInstance(ESLicenseManager.class, clients.getMasterName());
}
private LicensesClientService licensesClientService() {
return internalCluster().getInstance(LicensesClientService.class, node);
}
private LicensesService randomLicensesService() {
String randomNode = randomFrom(nodes);
return internalCluster().getInstance(LicensesService.class, randomNode);
}
private static ClusterService masterClusterService() {
final InternalTestCluster clients = internalCluster();
return clients.getInstance(ClusterService.class, clients.getMasterName());
}
} }

View File

@ -0,0 +1,146 @@
/*
* 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;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
import org.elasticsearch.common.collect.ImmutableSet;
import org.elasticsearch.common.collect.Sets;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.TestUtils;
import org.elasticsearch.license.core.ESLicense;
import org.elasticsearch.license.manager.ESLicenseManager;
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest;
import org.elasticsearch.license.plugin.core.*;
import org.elasticsearch.test.InternalTestCluster;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
import static org.hamcrest.Matchers.equalTo;
@ClusterScope(scope = TEST, numDataNodes = 10)
public class LicensesManagerServiceTests extends AbstractLicensesServiceTests {
@Test
public void testStoreAndGetLicenses() throws Exception {
LicensesManagerService licensesManagerService = masterLicensesManagerService();
ESLicense shieldShortLicense = generateSignedLicense("shield", TimeValue.timeValueHours(1));
ESLicense shieldLongLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2));
ESLicense marvelShortLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(1));
ESLicense marvelLongLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(2));
List<ESLicense> licenses = Arrays.asList(shieldLongLicense, shieldShortLicense, marvelLongLicense, marvelShortLicense);
Collections.shuffle(licenses);
registerAndAckSignedLicenses(licensesManagerService, licenses, LicensesStatus.VALID);
final ImmutableSet<String> licenseSignatures = masterLicenseManager().toSignatures(licenses);
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
// all licenses should be stored in the metaData
assertThat(licenseSignatures, equalTo(licensesMetaData.getSignatures()));
// only the latest expiry date license for each feature should be returned by getLicenses()
final List<ESLicense> getLicenses = licensesManagerService.getLicenses();
TestUtils.isSame(getLicenses, Arrays.asList(shieldLongLicense, marvelLongLicense));
}
@Test
public void testInvalidLicenseStorage() throws Exception {
LicensesManagerService licensesManagerService = masterLicensesManagerService();
ESLicense signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
// modify content of signed license
ESLicense tamperedLicense = ESLicense.builder()
.fromLicenseSpec(signedLicense, signedLicense.signature())
.expiryDate(signedLicense.expiryDate() + 10 * 24 * 60 * 60 * 1000l)
.verify()
.build();
registerAndAckSignedLicenses(licensesManagerService, Arrays.asList(tamperedLicense), LicensesStatus.INVALID);
// ensure that the invalid license never made it to cluster state
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
if (licensesMetaData != null) {
assertThat(licensesMetaData.getSignatures().size(), equalTo(0));
}
}
@Test
public void testRemoveLicenses() throws Exception {
LicensesManagerService licensesManagerService = masterLicensesManagerService();
// generate a trial license for one feature
final LicensesClientService clientService = licensesClientService();
final TestTrackingClientListener clientListener = new TestTrackingClientListener(false);
registerWithTrialLicense(clientService, clientListener, "shield", TimeValue.timeValueHours(1)).run();
// generate signed licenses for multiple features
ESLicense shieldShortLicense = generateSignedLicense("shield", TimeValue.timeValueHours(1));
ESLicense shieldLongLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2));
ESLicense marvelShortLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(1));
ESLicense marvelLongLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(2));
List<ESLicense> licenses = Arrays.asList(shieldLongLicense, shieldShortLicense, marvelLongLicense, marvelShortLicense);
Collections.shuffle(licenses);
registerAndAckSignedLicenses(licensesManagerService, licenses, LicensesStatus.VALID);
// remove license(s) for one feature out of two
removeAndAckSignedLicenses(licensesManagerService, Sets.newHashSet("shield"));
final ImmutableSet<String> licenseSignatures = masterLicenseManager().toSignatures(Arrays.asList(marvelLongLicense, marvelShortLicense));
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
assertThat(licenseSignatures, equalTo(licensesMetaData.getSignatures()));
// check that trial license is not removed
assertThat(licensesMetaData.getEncodedTrialLicenses().size(), equalTo(1));
// remove license(s) for all features
removeAndAckSignedLicenses(licensesManagerService, Sets.newHashSet("shield", "marvel"));
licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
assertThat(licensesMetaData.getSignatures().size(), equalTo(0));
// check that trial license is not removed
assertThat(licensesMetaData.getEncodedTrialLicenses().size(), equalTo(1));
}
private void removeAndAckSignedLicenses(final LicensesManagerService masterLicensesManagerService, final Set<String> featuresToDelete) {
DeleteLicenseRequest deleteLicenseRequest = new DeleteLicenseRequest(featuresToDelete.toArray(new String[featuresToDelete.size()]));
LicensesService.DeleteLicenseRequestHolder requestHolder = new LicensesService.DeleteLicenseRequestHolder(deleteLicenseRequest, "test");
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean success = new AtomicBoolean(false);
masterLicensesManagerService.removeLicenses(requestHolder, new ActionListener<ClusterStateUpdateResponse>() {
@Override
public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) {
if (clusterStateUpdateResponse.isAcknowledged()) {
success.set(true);
}
latch.countDown();
}
@Override
public void onFailure(Throwable throwable) {
latch.countDown();
}
});
try {
latch.await();
} catch (InterruptedException e) {
fail(e.getMessage());
}
assertThat("remove license(s) failed", success.get(), equalTo(true));
}
private ESLicenseManager masterLicenseManager() {
InternalTestCluster clients = internalCluster();
return clients.getInstance(ESLicenseManager.class, clients.getMasterName());
}
}