Merge pull request elastic/elasticsearch#2788 from rjernst/license1

Internal: Simplify creation of trial license

Original commit: elastic/x-pack-elasticsearch@1c92544b65
This commit is contained in:
Ryan Ernst 2016-07-12 13:44:21 -07:00 committed by GitHub
commit 8b2cdebf88
6 changed files with 54 additions and 80 deletions

View File

@ -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);
}
}
}

View File

@ -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"));
}
}

View File

@ -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());
}
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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