diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java index c2bfb91d805..138f4c341a3 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java @@ -15,7 +15,6 @@ import org.elasticsearch.cluster.ClusterStateListener; import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse; import org.elasticsearch.cluster.metadata.MetaData; -import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.component.Lifecycle; @@ -31,13 +30,6 @@ import org.elasticsearch.license.core.LicenseVerifier; import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest; import org.elasticsearch.license.plugin.action.put.PutLicenseRequest; import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.transport.EmptyTransportResponseHandler; -import org.elasticsearch.transport.TransportChannel; -import org.elasticsearch.transport.TransportRequest; -import org.elasticsearch.transport.TransportRequestHandler; -import org.elasticsearch.transport.TransportResponse; -import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.scheduler.SchedulerEngine; import org.elasticsearch.xpack.support.clock.Clock; @@ -74,12 +66,11 @@ import java.util.concurrent.atomic.AtomicReference; public class LicensesService extends AbstractLifecycleComponent implements ClusterStateListener, LicensesManagerService, LicenseeRegistry, SchedulerEngine.Listener { - public static final String REGISTER_TRIAL_LICENSE_ACTION_NAME = "internal:plugin/license/cluster/register_trial_license"; + // pkg private for tests + static final TimeValue TRIAL_LICENSE_DURATION = TimeValue.timeValueHours(30 * 24); private final ClusterService clusterService; - private final TransportService transportService; - /** * Currently active consumers to notify to */ @@ -97,11 +88,6 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust */ private List expirationCallbacks = new ArrayList<>(); - /** - * Duration of generated trial license - */ - private TimeValue trialLicenseDuration = TimeValue.timeValueHours(30 * 24); - /** * Max number of nodes licensed by generated trial license */ @@ -120,14 +106,9 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust "please read the following messages and update the license again, this time with the \"acknowledge=true\" parameter:"; @Inject - public LicensesService(Settings settings, ClusterService clusterService, TransportService transportService, Clock clock) { + public LicensesService(Settings settings, ClusterService clusterService, Clock clock) { super(settings); this.clusterService = clusterService; - this.transportService = transportService; - if (DiscoveryNode.isMasterNode(settings)) { - transportService.registerRequestHandler(REGISTER_TRIAL_LICENSE_ACTION_NAME, TransportRequest.Empty::new, - ThreadPool.Names.SAME, new RegisterTrialLicenseRequestHandler()); - } populateExpirationCallbacks(); this.clock = clock; this.scheduler = new SchedulerEngine(clock); @@ -354,7 +335,7 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust * has no signed/trial license */ private void registerTrialLicense() { - clusterService.submitStateUpdateTask("generate trial license for [" + trialLicenseDuration + "]", new ClusterStateUpdateTask() { + clusterService.submitStateUpdateTask("generate trial license for [" + TRIAL_LICENSE_DURATION + "]", new ClusterStateUpdateTask() { @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { LicensesMetaData licensesMetaData = newState.metaData().custom(LicensesMetaData.TYPE); @@ -376,7 +357,7 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust .issuedTo(clusterService.getClusterName().value()) .maxNodes(trialLicenseMaxNodes) .issueDate(issueDate) - .expiryDate(issueDate + trialLicenseDuration.getMillis()); + .expiryDate(issueDate + TRIAL_LICENSE_DURATION.getMillis()); License trialLicense = TrialLicense.create(specBuilder); mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(trialLicense)); return ClusterState.builder(currentState).metaData(mdBuilder).build(); @@ -411,7 +392,6 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust @Override protected void doClose() throws ElasticsearchException { - transportService.removeHandler(REGISTER_TRIAL_LICENSE_ACTION_NAME); } /** @@ -440,9 +420,10 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust } // auto-generate license if no licenses ever existed // this will trigger a subsequent cluster changed event - if (prevLicensesMetaData == null - && (currentLicensesMetaData == null || currentLicensesMetaData.getLicense() == null)) { - requestTrialLicense(currentClusterState); + if (currentClusterState.getNodes().isLocalNodeElectedMaster() && + prevLicensesMetaData == null && + (currentLicensesMetaData == null || currentLicensesMetaData.getLicense() == null)) { + registerTrialLicense(); } } else if (logger.isDebugEnabled()) { logger.debug("skipped license notifications reason: [{}]", GatewayService.STATE_NOT_RECOVERED_BLOCK); @@ -535,25 +516,17 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust && clusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK) == false && clusterState.nodes().getMasterNode() != null) { final LicensesMetaData currentMetaData = clusterState.metaData().custom(LicensesMetaData.TYPE); - if (currentMetaData == null || currentMetaData.getLicense() == null) { + if (clusterState.getNodes().isLocalNodeElectedMaster() && + (currentMetaData == null || currentMetaData.getLicense() == null)) { // triggers a cluster changed event // eventually notifying the current licensee - requestTrialLicense(clusterState); + registerTrialLicense(); } else if (lifecycleState() == Lifecycle.State.STARTED) { notifyLicensees(currentMetaData.getLicense()); } } } - private void requestTrialLicense(final ClusterState currentState) { - DiscoveryNode masterNode = currentState.nodes().getMasterNode(); - if (masterNode == null) { - throw new IllegalStateException("master not available when registering auto-generated license"); - } - transportService.sendRequest(masterNode, - REGISTER_TRIAL_LICENSE_ACTION_NAME, TransportRequest.Empty.INSTANCE, EmptyTransportResponseHandler.INSTANCE_SAME); - } - License getLicense(final LicensesMetaData metaData) { if (metaData != null) { License license = metaData.getLicense(); @@ -623,16 +596,4 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust } } } - - /** - * Request handler for trial license generation to master - */ - private class RegisterTrialLicenseRequestHandler implements TransportRequestHandler { - - @Override - public void messageReceived(TransportRequest.Empty empty, TransportChannel channel) throws Exception { - registerTrialLicense(); - channel.sendResponse(TransportResponse.Empty.INSTANCE); - } - } } \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java index 4f49c2bc116..9f4c228aef6 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java @@ -6,6 +6,7 @@ package org.elasticsearch.license.plugin.core; import org.elasticsearch.Version; +import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.metadata.MetaData; @@ -17,8 +18,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.LocalTransportAddress; import org.elasticsearch.license.core.License; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.transport.TransportService; -import org.elasticsearch.xpack.scheduler.SchedulerEngine; import org.elasticsearch.xpack.support.clock.ClockMock; import org.junit.Before; @@ -31,15 +30,15 @@ public abstract class AbstractLicenseServiceTestCase extends ESTestCase { protected LicensesService licensesService; protected ClusterService clusterService; - protected TransportService transportService; protected ClockMock clock; + protected DiscoveryNodes discoveryNodes; @Before public void init() throws Exception { clusterService = mock(ClusterService.class); - transportService = mock(TransportService.class); clock = new ClockMock(); - licensesService = new LicensesService(Settings.EMPTY, clusterService, transportService, clock); + licensesService = new LicensesService(Settings.EMPTY, clusterService, clock); + discoveryNodes = mock(DiscoveryNodes.class); } protected void setInitialState(License license) { @@ -49,11 +48,13 @@ public abstract class AbstractLicenseServiceTestCase extends ESTestCase { MetaData metaData = mock(MetaData.class); when(metaData.custom(LicensesMetaData.TYPE)).thenReturn(new LicensesMetaData(license)); when(state.metaData()).thenReturn(metaData); - final DiscoveryNodes discoveryNodes = mock(DiscoveryNodes.class); final DiscoveryNode mockNode = new DiscoveryNode("b", LocalTransportAddress.buildUnique(), emptyMap(), emptySet(), Version.CURRENT); when(discoveryNodes.getMasterNode()).thenReturn(mockNode); + when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(false); when(state.nodes()).thenReturn(discoveryNodes); + when(state.getNodes()).thenReturn(discoveryNodes); // it is really ridiculous we have nodes() and getNodes()... when(clusterService.state()).thenReturn(state); when(clusterService.lifecycleState()).thenReturn(Lifecycle.State.STARTED); + when(clusterService.getClusterName()).thenReturn(new ClusterName("a")); } } \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseClusterChangeTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseClusterChangeTests.java index 8aeec1fd457..6a0c1168ecd 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseClusterChangeTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseClusterChangeTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.Version; import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; @@ -16,18 +17,17 @@ import org.elasticsearch.common.transport.LocalTransportAddress; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.license.core.License; import org.elasticsearch.license.plugin.TestUtils; -import org.elasticsearch.transport.EmptyTransportResponseHandler; -import org.elasticsearch.transport.TransportRequest; import org.junit.After; import org.junit.Before; +import org.mockito.ArgumentCaptor; import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase { @@ -70,11 +70,16 @@ public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase { DiscoveryNode master = new DiscoveryNode("b", LocalTransportAddress.buildUnique(), emptyMap(), emptySet(), Version.CURRENT); ClusterState oldState = ClusterState.builder(new ClusterName("a")) .nodes(DiscoveryNodes.builder().masterNodeId(master.getId()).put(master)).build(); - ClusterState newState = ClusterState.builder(oldState).build(); + when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(true); + ClusterState newState = ClusterState.builder(oldState).nodes(discoveryNodes).build(); + licensesService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState)); - verify(transportService, times(2)) - .sendRequest(any(DiscoveryNode.class), - eq(LicensesService.REGISTER_TRIAL_LICENSE_ACTION_NAME), - any(TransportRequest.Empty.class), any(EmptyTransportResponseHandler.class)); + ArgumentCaptor stateUpdater = ArgumentCaptor.forClass(ClusterStateUpdateTask.class); + verify(clusterService, times(1)).submitStateUpdateTask(any(), stateUpdater.capture()); + ClusterState stateWithLicense = stateUpdater.getValue().execute(newState); + LicensesMetaData licenseMetaData = stateWithLicense.metaData().custom(LicensesMetaData.TYPE); + assertNotNull(licenseMetaData); + assertNotNull(licenseMetaData.getLicense()); + assertEquals(clock.millis() + LicensesService.TRIAL_LICENSE_DURATION.millis(), licenseMetaData.getLicense().expiryDate()); } } \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java index 4d828566ca6..131bcb27269 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java @@ -5,31 +5,39 @@ */ package org.elasticsearch.license.plugin.core; -import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.license.plugin.TestUtils; -import org.elasticsearch.transport.EmptyTransportResponseHandler; -import org.elasticsearch.transport.TransportRequest; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; -import static org.elasticsearch.mock.orig.Mockito.times; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class LicenseRegistrationTests extends AbstractLicenseServiceTestCase { public void testTrialLicenseRequestOnEmptyLicenseState() throws Exception { setInitialState(null); + when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(true); + TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee( "testTrialLicenseRequestOnEmptyLicenseState", logger); licensesService.start(); licensesService.register(licensee); - verify(transportService, times(1)) - .sendRequest(any(DiscoveryNode.class), - eq(LicensesService.REGISTER_TRIAL_LICENSE_ACTION_NAME), - any(TransportRequest.Empty.class), any(EmptyTransportResponseHandler.class)); - assertThat(licensee.statuses.size(), equalTo(0)); + + ClusterState state = ClusterState.builder(new ClusterName("a")).build(); + ArgumentCaptor stateUpdater = ArgumentCaptor.forClass(ClusterStateUpdateTask.class); + verify(clusterService, Mockito.times(1)).submitStateUpdateTask(any(), stateUpdater.capture()); + ClusterState stateWithLicense = stateUpdater.getValue().execute(state); + LicensesMetaData licenseMetaData = stateWithLicense.metaData().custom(LicensesMetaData.TYPE); + assertNotNull(licenseMetaData); + assertNotNull(licenseMetaData.getLicense()); + assertEquals(clock.millis() + LicensesService.TRIAL_LICENSE_DURATION.millis(), licenseMetaData.getLicense().expiryDate()); + licensesService.stop(); } diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java index 7eb316e7398..68a896be279 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java @@ -5,6 +5,10 @@ */ package org.elasticsearch.license.plugin.core; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + import org.elasticsearch.action.ActionListener; import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.common.unit.TimeValue; @@ -13,10 +17,6 @@ import org.elasticsearch.license.plugin.TestUtils; import org.elasticsearch.license.plugin.action.put.PutLicenseRequest; import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; diff --git a/elasticsearch/x-pack/security/src/test/resources/org/elasticsearch/transport/handlers b/elasticsearch/x-pack/security/src/test/resources/org/elasticsearch/transport/handlers index 0aa0be82698..c936c8690ee 100644 --- a/elasticsearch/x-pack/security/src/test/resources/org/elasticsearch/transport/handlers +++ b/elasticsearch/x-pack/security/src/test/resources/org/elasticsearch/transport/handlers @@ -106,5 +106,4 @@ internal:indices/flush/synced/in_flight internal:indices/flush/synced/pre internal:indices/flush/synced/sync internal:admin/repository/verify -internal:plugin/license/cluster/register_trial_license internal:transport/handshake