Refactored & improved LicensesTransportTests

Ensure that invalid licenses never make it to clusterState

Original commit: elastic/x-pack-elasticsearch@c6dfb6226d
This commit is contained in:
Areek Zillur 2014-10-28 23:11:36 -04:00
parent d431c8b532
commit 9a64f00802
4 changed files with 145 additions and 136 deletions

View File

@ -96,35 +96,35 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
LicensesStatus status = checkLicenses(newLicenses);
switch (status) {
case VALID:
clusterService.submitStateUpdateTask(requestHolder.source, new AckedClusterStateUpdateTask<LicensesUpdateResponse>(request, listener) {
@Override
protected LicensesUpdateResponse newResponse(boolean acknowledged) {
return new LicensesUpdateResponse(acknowledged, LicensesStatus.VALID);
}
@Override
public ClusterState execute(ClusterState currentState) throws Exception {
MetaData metaData = currentState.metaData();
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE);
final LicensesWrapper licensesWrapper = LicensesWrapper.wrap(currentLicenses);
Set<String> newSignatures = licenseManager.toSignatures(newLicenses);
if (newSignatures.size() > 0) {
Set<String> newLicenseSignatures = Sets.union(licensesWrapper.signatures, newSignatures);
LicensesMetaData newLicensesMetaData = new LicensesMetaData(newLicenseSignatures, licensesWrapper.encodedTrialLicenses);
mdBuilder.putCustom(LicensesMetaData.TYPE, newLicensesMetaData);
} else {
mdBuilder.putCustom(LicensesMetaData.TYPE, currentLicenses);
}
return ClusterState.builder(currentState).metaData(mdBuilder).build();
}
});
break;
case INVALID:
case EXPIRED:
listener.onResponse(new LicensesUpdateResponse(true, status));
break;
}
clusterService.submitStateUpdateTask(requestHolder.source, new AckedClusterStateUpdateTask<LicensesUpdateResponse>(request, listener) {
@Override
protected LicensesUpdateResponse newResponse(boolean acknowledged) {
return new LicensesUpdateResponse(acknowledged, LicensesStatus.VALID);
}
@Override
public ClusterState execute(ClusterState currentState) throws Exception {
MetaData metaData = currentState.metaData();
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE);
final LicensesWrapper licensesWrapper = LicensesWrapper.wrap(currentLicenses);
Set<String> newSignatures = licenseManager.toSignatures(newLicenses);
if (newSignatures.size() > 0) {
Set<String> newLicenseSignatures = Sets.union(licensesWrapper.signatures, newSignatures);
LicensesMetaData newLicensesMetaData = new LicensesMetaData(newLicenseSignatures, licensesWrapper.encodedTrialLicenses);
mdBuilder.putCustom(LicensesMetaData.TYPE, newLicensesMetaData);
} else {
mdBuilder.putCustom(LicensesMetaData.TYPE, currentLicenses);
}
return ClusterState.builder(currentState).metaData(mdBuilder).build();
}
});
}
public static class LicensesUpdateResponse extends ClusterStateUpdateResponse {

View File

@ -12,11 +12,18 @@ import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.core.ESLicense;
import org.elasticsearch.license.licensor.ESLicenseSigner;
import org.elasticsearch.license.plugin.core.LicensesMetaData;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import static org.elasticsearch.license.AbstractLicensingTestBase.getTestPriKeyPath;
import static org.elasticsearch.license.AbstractLicensingTestBase.getTestPubKeyPath;
/**
*/
public abstract class AbstractLicensesIntegrationTests extends ElasticsearchIntegrationTest {
@ -59,4 +66,20 @@ public abstract class AbstractLicensesIntegrationTests extends ElasticsearchInte
latch.await();
}
public static ESLicense generateSignedLicense(String feature, TimeValue expiryDate) throws Exception {
final ESLicense licenseSpec = ESLicense.builder()
.uid(UUID.randomUUID().toString())
.feature(feature)
.expiryDate(System.currentTimeMillis() + expiryDate.getMillis())
.issueDate(System.currentTimeMillis())
.type("subscription")
.subscriptionType("gold")
.issuedTo("customer")
.issuer("elasticsearch")
.maxNodes(randomIntBetween(5, 100))
.build();
ESLicenseSigner signer = new ESLicenseSigner(getTestPriKeyPath(), getTestPubKeyPath());
return signer.sign(licenseSpec);
}
}

View File

@ -6,26 +6,21 @@
package org.elasticsearch.license.plugin;
import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.action.ListenableActionFuture;
import org.elasticsearch.common.collect.ImmutableSet;
import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.TestUtils;
import org.elasticsearch.license.core.ESLicense;
import org.elasticsearch.license.core.ESLicenses;
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequestBuilder;
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseResponse;
import org.elasticsearch.license.plugin.action.get.GetLicenseRequestBuilder;
import org.elasticsearch.license.plugin.action.get.GetLicenseResponse;
import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder;
import org.elasticsearch.license.plugin.action.put.PutLicenseResponse;
import org.junit.BeforeClass;
import org.elasticsearch.license.plugin.core.LicensesStatus;
import org.junit.After;
import org.junit.Test;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
@ -35,111 +30,124 @@ import static org.hamcrest.CoreMatchers.notNullValue;
@ClusterScope(scope = TEST, numDataNodes = 10)
public class LicenseTransportTests extends AbstractLicensesIntegrationTests {
private static String pubKeyPath = null;
private static String priKeyPath = null;
@BeforeClass
public static void setup() throws IOException, URISyntaxException {
priKeyPath = Paths.get(LicenseTransportTests.class.getResource("/private.key").toURI()).toAbsolutePath().toString();
pubKeyPath = Paths.get(LicenseTransportTests.class.getResource("/public.key").toURI()).toAbsolutePath().toString();
@After
public void beforeTest() throws Exception {
wipeAllLicenses();
}
/*
* TODO:
* - add more delete tests
* - add put invalid licenses tests
* - add multiple licenses of the same feature tests
*/
@Test
public void testEmptyGetLicense() throws Exception {
DeleteLicenseRequestBuilder deleteLicenseRequestBuilder = new DeleteLicenseRequestBuilder(client().admin().cluster()).setFeatures(ImmutableSet.of("marvel", "shield"));
final ActionFuture<DeleteLicenseResponse> deleteFuture = deleteLicenseRequestBuilder.execute();
final DeleteLicenseResponse deleteLicenseResponse = deleteFuture.get();
assertTrue(deleteLicenseResponse.isAcknowledged());
final ActionFuture<GetLicenseResponse> getLicenseFuture = new GetLicenseRequestBuilder(client().admin().cluster()).execute();
final GetLicenseResponse getLicenseResponse = getLicenseFuture.get();
assertThat("expected 0 licenses; but got: " + getLicenseResponse.licenses().size(), getLicenseResponse.licenses().size(), equalTo(0));
}
@Test
public void testPutLicense() throws ParseException, ExecutionException, InterruptedException, IOException {
Map<String, TestUtils.FeatureAttributes> map = new HashMap<>();
TestUtils.FeatureAttributes featureAttributes =
new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-12-13");
map.put(TestUtils.SHIELD, featureAttributes);
String licenseString = TestUtils.generateESLicenses(map);
String licenseOutput = TestUtils.runLicenseGenerationTool(licenseString, pubKeyPath, priKeyPath);
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster());
//putLicenseRequest.license(licenseString);
final List<ESLicense> putLicenses = ESLicenses.fromSource(licenseOutput);
putLicenseRequestBuilder.setLicense(putLicenses);
//LicenseUtils.printLicense(putLicenses);
ensureGreen();
final ActionFuture<PutLicenseResponse> putLicenseFuture = putLicenseRequestBuilder.execute();
final PutLicenseResponse putLicenseResponse = putLicenseFuture.get();
public void testPutLicense() throws Exception {
ESLicense signedLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(2));
List<ESLicense> actualLicenses = Collections.singletonList(signedLicense);
// put license
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster())
.setLicense(actualLicenses);
PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.execute().get();
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
ActionFuture<GetLicenseResponse> getLicenseFuture = new GetLicenseRequestBuilder(client().admin().cluster()).execute();
GetLicenseResponse getLicenseResponse = getLicenseFuture.get();
// get license
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster()).get();
assertThat(getLicenseResponse.licenses(), notNullValue());
//LicenseUtils.printLicense(getLicenseResponse.licenses());
TestUtils.isSame(new HashSet<>(putLicenses), new HashSet<>(getLicenseResponse.licenses()));
final ActionFuture<DeleteLicenseResponse> deleteFuture = new DeleteLicenseRequestBuilder(client().admin().cluster())
.setFeatures(ImmutableSet.of("marvel", "shield")).execute();
final DeleteLicenseResponse deleteLicenseResponse = deleteFuture.get();
assertTrue(deleteLicenseResponse.isAcknowledged());
//getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster()).execute().get();
//TestUtils.isSame(getLicenseResponse.licenses(), LicenseBuilders.licensesBuilder().verifyAndBuild());
// check license
TestUtils.isSame(actualLicenses, getLicenseResponse.licenses());
}
@Test
public void testPutInvalidLicense() throws Exception {
Map<String, TestUtils.FeatureAttributes> map = new HashMap<>();
TestUtils.FeatureAttributes featureAttributes =
new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-12-13");
map.put(TestUtils.SHIELD, featureAttributes);
String licenseString = TestUtils.generateESLicenses(map);
String licenseOutput = TestUtils.runLicenseGenerationTool(licenseString, pubKeyPath, priKeyPath);
ESLicense signedLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(2));
Set<ESLicense> esLicenses = new HashSet<>(ESLicenses.fromSource(licenseOutput));
ESLicense esLicense = ESLicenses.reduceAndMap(esLicenses).get(TestUtils.SHIELD);
final ESLicense tamperedLicense = ESLicense.builder()
.fromLicenseSpec(esLicense, esLicense.signature())
.expiryDate(esLicense.expiryDate() + 10 * 24 * 60 * 60 * 1000l)
// modify content of signed license
ESLicense tamperedLicense = ESLicense.builder()
.fromLicenseSpec(signedLicense, signedLicense.signature())
.expiryDate(signedLicense.expiryDate() + 10 * 24 * 60 * 60 * 1000l)
.verify()
.build();
PutLicenseRequestBuilder builder = new PutLicenseRequestBuilder(client().admin().cluster());
builder.setLicense(Collections.singletonList(tamperedLicense));
final ListenableActionFuture<PutLicenseResponse> execute = builder.execute();
// try to put license (should be invalid)
final PutLicenseResponse putLicenseResponse = builder.execute().get();
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.INVALID));
try {
execute.get();
fail("Invalid License should throw exception");
} catch (Throwable e) {
/* TODO: figure out error handling
String msg =e.getCause().getCause().getCause().getMessage();//e.getCause().getCause().getMessage();// e.getCause().getCause().getCause().getMessage();
assertTrue("Error message: " + msg, msg.contains("Invalid License(s)"));
*/
}
// try to get invalid license
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster()).get();
assertThat(getLicenseResponse.licenses().size(), equalTo(0));
}
@Test
public void testPutLicensesForSameFeature() throws Exception {
ESLicense shortedSignedLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(2));
ESLicense longerSignedLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(5));
List<ESLicense> actualLicenses = Arrays.asList(longerSignedLicense, shortedSignedLicense);
// put license
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster())
.setLicense(actualLicenses);
PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.execute().get();
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
// get should return only one license (with longer expiry date)
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster()).get();
assertThat(getLicenseResponse.licenses(), notNullValue());
// check license
TestUtils.isSame(Collections.singletonList(longerSignedLicense), getLicenseResponse.licenses());
}
@Test
public void testPutLicensesForMultipleFeatures() throws Exception {
ESLicense shieldLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(2));
ESLicense marvelLicense = generateSignedLicense(TestUtils.MARVEL, TimeValue.timeValueMinutes(5));
List<ESLicense> actualLicenses = Arrays.asList(marvelLicense, shieldLicense);
// put license
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster())
.setLicense(actualLicenses);
PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.execute().get();
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
// get should return both the licenses
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster()).get();
assertThat(getLicenseResponse.licenses(), notNullValue());
// check license
TestUtils.isSame(actualLicenses, getLicenseResponse.licenses());
}
@Test
public void testPutMultipleLicensesForMultipleFeatures() throws Exception {
ESLicense shortedSignedLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(2));
ESLicense longerSignedLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(5));
ESLicense marvelLicense = generateSignedLicense(TestUtils.MARVEL, TimeValue.timeValueMinutes(5));
List<ESLicense> actualLicenses = Arrays.asList(marvelLicense, shortedSignedLicense, longerSignedLicense);
// put license
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster())
.setLicense(actualLicenses);
PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.execute().get();
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
// get should return both the licenses
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster()).get();
assertThat(getLicenseResponse.licenses(), notNullValue());
// check license (should get the longest expiry time for all unique features)
TestUtils.isSame(Arrays.asList(marvelLicense, longerSignedLicense), getLicenseResponse.licenses());
}
}

View File

@ -11,21 +11,16 @@ import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.core.ESLicense;
import org.elasticsearch.license.licensor.ESLicenseSigner;
import org.elasticsearch.license.plugin.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.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static org.elasticsearch.license.AbstractLicensingTestBase.getTestPriKeyPath;
import static org.elasticsearch.license.AbstractLicensingTestBase.getTestPubKeyPath;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
import static org.hamcrest.CoreMatchers.equalTo;
@ -172,23 +167,6 @@ public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationT
}, timeoutInSec, TimeUnit.SECONDS), equalTo(true));
}
private ESLicense generateSignedLicense(String feature, TimeValue expiryDate) throws Exception {
final ESLicense licenseSpec = ESLicense.builder()
.uid(UUID.randomUUID().toString())
.feature(feature)
.expiryDate(System.currentTimeMillis() + expiryDate.getMillis())
.issueDate(System.currentTimeMillis())
.type("subscription")
.subscriptionType("gold")
.issuedTo("customer")
.issuer("elasticsearch")
.maxNodes(10)
.build();
ESLicenseSigner signer = new ESLicenseSigner(getTestPriKeyPath(), getTestPubKeyPath());
return signer.sign(licenseSpec);
}
private Iterable<TestPluginService> consumerPluginServices() {
final InternalTestCluster clients = internalCluster();
return clients.getDataNodeInstances(TestPluginService.class);