License removal leads back to a basic license (#52407) (#52683)

A new basic license will be generated when existing license is deleted.
In addition, deleting an existing basic license is a no-op.

Resolves: #45022
This commit is contained in:
Yang Wang 2020-02-24 11:02:40 +11:00 committed by GitHub
parent d26d7721ea
commit 7cefba78c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 49 additions and 62 deletions

View File

@ -14,7 +14,6 @@ import org.elasticsearch.cluster.AckedClusterStateUpdateTask;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ClusterStateListener;
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.service.ClusterService;
@ -343,29 +342,10 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
/**
* Remove license from the cluster state metadata
*/
public void removeLicense(final DeleteLicenseRequest request, final ActionListener<ClusterStateUpdateResponse> listener) {
public void removeLicense(final DeleteLicenseRequest request, final ActionListener<PostStartBasicResponse> listener) {
final PostStartBasicRequest startBasicRequest = new PostStartBasicRequest().acknowledge(true);
clusterService.submitStateUpdateTask("delete license",
new AckedClusterStateUpdateTask<ClusterStateUpdateResponse>(request, listener) {
@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());
LicensesMetaData newMetadata = new LicensesMetaData(LicensesMetaData.LICENSE_TOMBSTONE,
currentLicenses.getMostRecentTrialVersion());
mdBuilder.putCustom(LicensesMetaData.TYPE, newMetadata);
return ClusterState.builder(currentState).metaData(mdBuilder).build();
} else {
return currentState;
}
}
});
new StartBasicClusterTask(logger, clusterService.getClusterName().value(), clock, startBasicRequest, listener));
}
public License getLicense() {

View File

@ -61,19 +61,10 @@ public class StartBasicClusterTask extends ClusterStateUpdateTask {
@Override
public ClusterState execute(ClusterState currentState) throws Exception {
XPackPlugin.checkReadyForXPackCustomMetadata(currentState);
LicensesMetaData licensesMetaData = currentState.metaData().custom(LicensesMetaData.TYPE);
License currentLicense = LicensesMetaData.extractLicense(licensesMetaData);
if (currentLicense == null || License.LicenseType.isBasic(currentLicense.type()) == false) {
long issueDate = clock.millis();
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
License.Builder specBuilder = License.builder()
.uid(UUID.randomUUID().toString())
.issuedTo(clusterName)
.maxNodes(LicenseService.SELF_GENERATED_LICENSE_MAX_NODES)
.issueDate(issueDate)
.type(License.LicenseType.BASIC)
.expiryDate(LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS);
License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder, currentState.nodes());
LicensesMetaData currentLicensesMetaData = currentState.metaData().custom(LicensesMetaData.TYPE);
License currentLicense = LicensesMetaData.extractLicense(currentLicensesMetaData);
if (shouldGenerateNewBasicLicense(currentLicense)) {
License selfGeneratedLicense = generateBasicLicense(currentState);
if (request.isAcknowledged() == false && currentLicense != null) {
Map<String, String[]> ackMessages = LicenseService.getAckMessages(selfGeneratedLicense, currentLicense);
if (ackMessages.isEmpty() == false) {
@ -81,11 +72,9 @@ public class StartBasicClusterTask extends ClusterStateUpdateTask {
return currentState;
}
}
Version trialVersion = null;
if (licensesMetaData != null) {
trialVersion = licensesMetaData.getMostRecentTrialVersion();
}
Version trialVersion = currentLicensesMetaData != null ? currentLicensesMetaData.getMostRecentTrialVersion() : null;
LicensesMetaData newLicensesMetaData = new LicensesMetaData(selfGeneratedLicense, trialVersion);
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
mdBuilder.putCustom(LicensesMetaData.TYPE, newLicensesMetaData);
return ClusterState.builder(currentState).metaData(mdBuilder).build();
} else {
@ -98,4 +87,23 @@ public class StartBasicClusterTask extends ClusterStateUpdateTask {
logger.error(new ParameterizedMessage("unexpected failure during [{}]", source), e);
listener.onFailure(e);
}
private boolean shouldGenerateNewBasicLicense(License currentLicense) {
return currentLicense == null
|| License.LicenseType.isBasic(currentLicense.type()) == false
|| LicenseService.SELF_GENERATED_LICENSE_MAX_NODES != currentLicense.maxNodes()
|| LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS != currentLicense.expiryDate();
}
private License generateBasicLicense(ClusterState currentState) {
final License.Builder specBuilder = License.builder()
.uid(UUID.randomUUID().toString())
.issuedTo(clusterName)
.maxNodes(LicenseService.SELF_GENERATED_LICENSE_MAX_NODES)
.issueDate(clock.millis())
.type(License.LicenseType.BASIC)
.expiryDate(LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS);
return SelfGeneratedLicense.create(specBuilder, currentState.nodes());
}
}

View File

@ -11,7 +11,6 @@ import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
import org.elasticsearch.cluster.block.ClusterBlockException;
import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
@ -55,10 +54,10 @@ public class TransportDeleteLicenseAction extends TransportMasterNodeAction<Dele
@Override
protected void masterOperation(final DeleteLicenseRequest request, ClusterState state, final ActionListener<AcknowledgedResponse>
listener) throws ElasticsearchException {
licenseService.removeLicense(request, new ActionListener<ClusterStateUpdateResponse>() {
licenseService.removeLicense(request, new ActionListener<PostStartBasicResponse>() {
@Override
public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) {
listener.onResponse(new AcknowledgedResponse(clusterStateUpdateResponse.isAcknowledged()));
public void onResponse(PostStartBasicResponse postStartBasicResponse) {
listener.onResponse(new AcknowledgedResponse(postStartBasicResponse.isAcknowledged()));
}
@Override

View File

@ -23,7 +23,6 @@ import java.util.Collection;
import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.nullValue;
@ClusterScope(scope = TEST, numDataNodes = 0, numClientNodes = 0, maxNumDataNodes = 0, transportClientRatio = 0)
public class LicenseServiceClusterTests extends AbstractLicensesIntegrationTestCase {
@ -78,14 +77,14 @@ public class LicenseServiceClusterTests extends AbstractLicensesIntegrationTestC
assertThat(licensingClient.prepareGetLicense().get().license(), equalTo(license));
logger.info("--> remove licenses");
licensingClient.prepareDeleteLicense().get();
assertOperationMode(License.OperationMode.MISSING);
assertOperationMode(License.OperationMode.BASIC);
logger.info("--> restart all nodes");
internalCluster().fullRestart();
licensingClient = new LicensingClient(client());
ensureYellow();
assertThat(licensingClient.prepareGetLicense().get().license(), nullValue());
assertOperationMode(License.OperationMode.MISSING);
assertTrue(License.LicenseType.isBasic(licensingClient.prepareGetLicense().get().license().type()));
assertOperationMode(License.OperationMode.BASIC);
wipeAllLicenses();

View File

@ -6,7 +6,6 @@
package org.elasticsearch.license;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
@ -120,16 +119,16 @@ public class LicensesManagerServiceTests extends ESSingleNodeTestCase {
// remove signed licenses
removeAndAckSignedLicenses(licenseService);
licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
assertThat(licensesMetaData.getLicense(), equalTo(LicensesMetaData.LICENSE_TOMBSTONE));
assertTrue(License.LicenseType.isBasic(licensesMetaData.getLicense().type()));
}
private void removeAndAckSignedLicenses(final LicenseService licenseService) {
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean success = new AtomicBoolean(false);
licenseService.removeLicense(new DeleteLicenseRequest(), new ActionListener<ClusterStateUpdateResponse>() {
licenseService.removeLicense(new DeleteLicenseRequest(), new ActionListener<PostStartBasicResponse>() {
@Override
public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) {
if (clusterStateUpdateResponse.isAcknowledged()) {
public void onResponse(PostStartBasicResponse postStartBasicResponse) {
if (postStartBasicResponse.isAcknowledged()) {
success.set(true);
}
latch.countDown();

View File

@ -187,7 +187,7 @@ public class LicensesTransportTests extends ESSingleNodeTestCase {
assertThat(deleteLicenseResponse.isAcknowledged(), equalTo(true));
// get licenses (expected no licenses)
getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).get();
assertNull(getLicenseResponse.license());
assertTrue(License.LicenseType.isBasic(getLicenseResponse.license().type()));
}
public void testLicenseIsRejectWhenStartDateLaterThanNow() throws Exception {

View File

@ -61,12 +61,6 @@ public class StartBasicLicenseTests extends AbstractLicensesIntegrationTestCase
assertEquals("trial", getLicenseResponse.license().type());
});
// Testing that you can start a basic license when you have no license
if (randomBoolean()) {
licensingClient.prepareDeleteLicense().get();
assertNull(licensingClient.prepareGetLicense().get().license());
}
RestClient restClient = getRestClient();
Response response = restClient.performRequest(new Request("GET", "/_license/basic_status"));
String body = Streams.copyToString(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8));

View File

@ -66,7 +66,7 @@ teardown:
- length: { license: 11 }
- match: { license.uid: "893361dc-9749-4997-93cb-802e3dofh7aa" }
---
"Should throw 404 after license deletion":
"Should revert back to basic license after license deletion":
- do:
license.delete: {}
@ -74,7 +74,15 @@ teardown:
- do:
license.get: {}
catch: missing
- match: { license.type: "basic" }
- set: { license.uid: id }
- do: # delete an existing basic license is a no-op
license.delete: {}
- do:
license.get: {}
- match: { license.uid: $id}
---
"Should install a feature type license":