From 0d62213ca4073821d7855d76d9679a8a131b34eb Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Fri, 21 Feb 2020 15:23:09 +1100 Subject: [PATCH] License removal leads back to a basic license (#52407) 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 --- .../elasticsearch/license/LicenseService.java | 26 ++---------- .../license/StartBasicClusterTask.java | 42 +++++++++++-------- .../license/TransportDeleteLicenseAction.java | 7 ++-- .../license/LicenseServiceClusterTests.java | 7 ++-- .../license/LicensesManagerServiceTests.java | 9 ++-- .../license/LicensesTransportTests.java | 2 +- .../license/StartBasicLicenseTests.java | 6 --- .../test/license/20_put_license.yml | 12 +++++- 8 files changed, 49 insertions(+), 62 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java index cecdecb64a851..ddb97958b196a 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java @@ -14,7 +14,6 @@ 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; @@ -340,29 +339,10 @@ public void triggered(SchedulerEngine.Event event) { /** * Remove license from the cluster state metadata */ - public void removeLicense(final DeleteLicenseRequest request, final ActionListener listener) { + public void removeLicense(final DeleteLicenseRequest request, final ActionListener listener) { + final PostStartBasicRequest startBasicRequest = new PostStartBasicRequest().acknowledge(true); clusterService.submitStateUpdateTask("delete license", - new AckedClusterStateUpdateTask(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() { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartBasicClusterTask.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartBasicClusterTask.java index 3c6c00a5f86d1..1846c9f32603d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartBasicClusterTask.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartBasicClusterTask.java @@ -61,19 +61,10 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS @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 ackMessages = LicenseService.getAckMessages(selfGeneratedLicense, currentLicense); if (ackMessages.isEmpty() == false) { @@ -81,11 +72,9 @@ public ClusterState execute(ClusterState currentState) throws Exception { 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 void onFailure(String source, @Nullable Exception e) { 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()); + } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportDeleteLicenseAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportDeleteLicenseAction.java index beb3008f21b68..b78c65df7b9ab 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportDeleteLicenseAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportDeleteLicenseAction.java @@ -11,7 +11,6 @@ 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; @@ -56,10 +55,10 @@ protected ClusterBlockException checkBlock(DeleteLicenseRequest request, Cluster @Override protected void masterOperation(Task task, final DeleteLicenseRequest request, ClusterState state, final ActionListener listener) throws ElasticsearchException { - licenseService.removeLicense(request, new ActionListener() { + licenseService.removeLicense(request, new ActionListener() { @Override - public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - listener.onResponse(new AcknowledgedResponse(clusterStateUpdateResponse.isAcknowledged())); + public void onResponse(PostStartBasicResponse postStartBasicResponse) { + listener.onResponse(new AcknowledgedResponse(postStartBasicResponse.isAcknowledged())); } @Override diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicenseServiceClusterTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicenseServiceClusterTests.java index 467a859b6c2d6..ad3e8a2670135 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicenseServiceClusterTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicenseServiceClusterTests.java @@ -23,7 +23,6 @@ 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) public class LicenseServiceClusterTests extends AbstractLicensesIntegrationTestCase { @@ -73,14 +72,14 @@ public void testClusterRestartWithLicense() throws Exception { 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(); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesManagerServiceTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesManagerServiceTests.java index f3fbab1026efd..2e8ecbcde333f 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesManagerServiceTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesManagerServiceTests.java @@ -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 void testRemoveLicenses() throws Exception { // 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() { + licenseService.removeLicense(new DeleteLicenseRequest(), new ActionListener() { @Override - public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - if (clusterStateUpdateResponse.isAcknowledged()) { + public void onResponse(PostStartBasicResponse postStartBasicResponse) { + if (postStartBasicResponse.isAcknowledged()) { success.set(true); } latch.countDown(); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesTransportTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesTransportTests.java index abaa7e1c5d36e..244c982bc21c1 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesTransportTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesTransportTests.java @@ -187,7 +187,7 @@ public void testRemoveLicensesSimple() throws Exception { 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 { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/StartBasicLicenseTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/StartBasicLicenseTests.java index 9a2cb24e48b72..aedff7694e13a 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/StartBasicLicenseTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/StartBasicLicenseTests.java @@ -55,12 +55,6 @@ public void testStartBasicLicense() throws Exception { 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)); diff --git a/x-pack/plugin/src/test/resources/rest-api-spec/test/license/20_put_license.yml b/x-pack/plugin/src/test/resources/rest-api-spec/test/license/20_put_license.yml index 8b24d73fcb476..bdc1463ce374e 100644 --- a/x-pack/plugin/src/test/resources/rest-api-spec/test/license/20_put_license.yml +++ b/x-pack/plugin/src/test/resources/rest-api-spec/test/license/20_put_license.yml @@ -66,7 +66,7 @@ teardown: - length: { license: 12 } - 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":