Internal: Simplify creation of trial license
Currently each node monitors the cluster state for a license, and if it does not find one, it sends a request to the master to generate a trial license. However, the master node has this same logic. Since the master node is the only thing that can change the cluster state, we know that once some node becomes master, it will notice the lack of license, generate a trial license, and send a cluster state update. The trigger from every node telling the master to generate the trial license is not needed. This change removes the register_trial_license action that the non master nodes used. It removes the need for the TransportService in the LicensesService, which will help with deguicing. Original commit: elastic/x-pack-elasticsearch@a71656847e
This commit is contained in:
parent
149df1fd44
commit
a9ace27107
|
@ -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<ExpirationCallback> 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<TransportRequest.Empty> {
|
||||
|
||||
@Override
|
||||
public void messageReceived(TransportRequest.Empty empty, TransportChannel channel) throws Exception {
|
||||
registerTrialLicense();
|
||||
channel.sendResponse(TransportResponse.Empty.INSTANCE);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -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<ClusterStateUpdateTask> 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());
|
||||
}
|
||||
}
|
|
@ -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<ClusterStateUpdateTask> 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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue