[License] Feature agnostic licensing model
This commit changes the license plugin to work with license that are not tied to any specific feature in a bwc way. It refactors the license plugin api into a lighter weight API, enabling the license plugin to manage license expiration and acknowledgment triggers. closes elastic/elasticsearch#683, elastic/elasticsearch#686, elastic/elasticsearch#687, elastic/elasticsearch#691 Original commit: elastic/x-pack-elasticsearch@537cd3933a
This commit is contained in:
parent
fa85d04523
commit
daf4a9765c
|
@ -60,7 +60,8 @@ public class ClusterInfoCollector extends AbstractCollector<ClusterInfoMarvelDoc
|
|||
List<MarvelDoc> results = new ArrayList<>(1);
|
||||
|
||||
// Retrieves all licenses
|
||||
List<License> licenses = licenseService.licenses();
|
||||
// TODO: we should only work with one license
|
||||
List<License> licenses = Collections.singletonList(licenseService.license());
|
||||
|
||||
// Retrieves additional cluster stats
|
||||
ClusterStatsResponse clusterStats = client.admin().cluster().prepareClusterStats().get(marvelSettings.clusterStatsTimeout());
|
||||
|
|
|
@ -37,7 +37,6 @@ public class ClusterInfoRenderer extends AbstractRenderer<ClusterInfoMarvelDoc>
|
|||
builder.field(Fields.UID, license.uid());
|
||||
builder.field(Fields.TYPE, license.type());
|
||||
builder.dateValueField(Fields.ISSUE_DATE_IN_MILLIS, Fields.ISSUE_DATE, license.issueDate());
|
||||
builder.field(Fields.FEATURE, license.feature());
|
||||
builder.dateValueField(Fields.EXPIRY_DATE_IN_MILLIS, Fields.EXPIRY_DATE, license.expiryDate());
|
||||
builder.field(Fields.MAX_NODES, license.maxNodes());
|
||||
builder.field(Fields.ISSUED_TO, license.issuedTo());
|
||||
|
@ -77,7 +76,6 @@ public class ClusterInfoRenderer extends AbstractRenderer<ClusterInfoMarvelDoc>
|
|||
static final XContentBuilderString STATUS = new XContentBuilderString("status");
|
||||
static final XContentBuilderString UID = new XContentBuilderString("uid");
|
||||
static final XContentBuilderString TYPE = new XContentBuilderString("type");
|
||||
static final XContentBuilderString FEATURE = new XContentBuilderString("feature");
|
||||
static final XContentBuilderString ISSUE_DATE_IN_MILLIS = new XContentBuilderString("issue_date_in_millis");
|
||||
static final XContentBuilderString ISSUE_DATE = new XContentBuilderString("issue_date");
|
||||
static final XContentBuilderString EXPIRY_DATE_IN_MILLIS = new XContentBuilderString("expiry_date_in_millis");
|
||||
|
|
|
@ -6,110 +6,45 @@
|
|||
package org.elasticsearch.marvel.license;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.logging.support.LoggerMessageFormat;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.license.plugin.core.Licensee;
|
||||
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
|
||||
import org.elasticsearch.license.plugin.core.LicensesManagerService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
import org.elasticsearch.marvel.MarvelPlugin;
|
||||
import org.elasticsearch.marvel.agent.settings.MarvelSettings;
|
||||
import org.elasticsearch.marvel.mode.Mode;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
|
||||
public class LicenseService extends AbstractLifecycleComponent<LicenseService> implements Licensee {
|
||||
|
||||
public static final String FEATURE_NAME = MarvelPlugin.NAME;
|
||||
|
||||
private static final LicensesService.TrialLicenseOptions TRIAL_LICENSE_OPTIONS =
|
||||
new LicensesService.TrialLicenseOptions(TimeValue.timeValueHours(30 * 24), 1000);
|
||||
|
||||
private static final FormatDateTimeFormatter DATE_FORMATTER = Joda.forPattern("EEEE, MMMMM dd, yyyy", Locale.ROOT);
|
||||
|
||||
static final TimeValue GRACE_PERIOD = days(7);
|
||||
|
||||
private final LicensesManagerService managerService;
|
||||
private final LicensesClientService clientService;
|
||||
private final LicenseeRegistry clientService;
|
||||
private final MarvelSettings marvelSettings;
|
||||
private final Collection<LicensesService.ExpirationCallback> expirationLoggers;
|
||||
private final LicensesClientService.AcknowledgementCallback acknowledgementCallback;
|
||||
|
||||
private volatile Mode mode;
|
||||
private volatile boolean enabled;
|
||||
private volatile LicenseState state;
|
||||
private volatile long expiryDate;
|
||||
|
||||
@Inject
|
||||
public LicenseService(Settings settings, LicensesClientService clientService, LicensesManagerService managerService, MarvelSettings marvelSettings) {
|
||||
public LicenseService(Settings settings, LicenseeRegistry clientService, LicensesManagerService managerService, MarvelSettings marvelSettings) {
|
||||
super(settings);
|
||||
this.managerService = managerService;
|
||||
this.clientService = clientService;
|
||||
this.marvelSettings = marvelSettings;
|
||||
this.mode = Mode.LITE;
|
||||
this.expirationLoggers = Arrays.asList(
|
||||
new LicensesService.ExpirationCallback.Pre(days(7), days(30), days(1)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
logger.error("\n" +
|
||||
"#\n" +
|
||||
"# Marvel license will expire on [{}].\n" +
|
||||
"# Have a new license? please update it. Otherwise, please reach out to your support contact.\n" +
|
||||
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
|
||||
}
|
||||
},
|
||||
new LicensesService.ExpirationCallback.Pre(days(0), days(7), minutes(10)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
logger.error("\n" +
|
||||
"#\n" +
|
||||
"# Marvel license will expire on [{}].\n" +
|
||||
"# Have a new license? please update it. Otherwise, please reach out to your support contact.\n" +
|
||||
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
|
||||
}
|
||||
},
|
||||
new LicensesService.ExpirationCallback.Post(days(0), GRACE_PERIOD, minutes(10)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
long endOfGracePeriod = license.expiryDate() + GRACE_PERIOD.getMillis();
|
||||
logger.error("\n" +
|
||||
"#\n" +
|
||||
"# MARVEL LICENSE HAS EXPIRED ON [{}].\n" +
|
||||
"# MARVEL WILL STOP COLLECTING DATA ON [{}].\n" +
|
||||
"# HAVE A NEW LICENSE? PLEASE UPDATE IT. OTHERWISE, PLEASE REACH OUT TO YOUR SUPPORT CONTACT.\n" +
|
||||
"#", DATE_FORMATTER.printer().print(endOfGracePeriod), DATE_FORMATTER.printer().print(license.expiryDate()));
|
||||
}
|
||||
}
|
||||
);
|
||||
this.acknowledgementCallback = new LicensesClientService.AcknowledgementCallback() {
|
||||
@Override
|
||||
public List<String> acknowledge(License currentLicense, License newLicense) {
|
||||
switch (newLicense.type()) {
|
||||
|
||||
case "trial":
|
||||
case "gold":
|
||||
case "platinum":
|
||||
return Collections.emptyList();
|
||||
|
||||
default: // "basic" - we also fall back to basic for an unknown type
|
||||
return Collections.singletonList(LoggerMessageFormat.format(
|
||||
"Marvel: Multi-cluster support is disabled for clusters with [{}] licenses.\n" +
|
||||
"If you are running multiple customers, users won't be able to access this\n" +
|
||||
"all the clusters with [{}] licenses from a single Marvel instance. To access them\n" +
|
||||
"a dedicated and separated marvel instance will be required for each cluster",
|
||||
newLicense.type(), newLicense.type()));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws ElasticsearchException {
|
||||
clientService.register(FEATURE_NAME, TRIAL_LICENSE_OPTIONS, expirationLoggers, acknowledgementCallback, new InternalListener(this));
|
||||
clientService.register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -120,14 +55,6 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
|
|||
protected void doClose() throws ElasticsearchException {
|
||||
}
|
||||
|
||||
static TimeValue days(int days) {
|
||||
return TimeValue.timeValueHours(days * 24);
|
||||
}
|
||||
|
||||
static TimeValue minutes(int minutes) {
|
||||
return TimeValue.timeValueMinutes(minutes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current marvel's operating mode
|
||||
*/
|
||||
|
@ -138,18 +65,20 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
|
|||
/**
|
||||
* @return all registered licenses
|
||||
*/
|
||||
public List<License> licenses() {
|
||||
return managerService.getLicenses();
|
||||
public License license() {
|
||||
return managerService.getLicense();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the marvel license is enabled
|
||||
*/
|
||||
public boolean enabled() {
|
||||
return enabled;
|
||||
return state == LicenseState.ENABLED || state == LicenseState.GRACE_PERIOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: remove licensing grace period, just check for state == LicensesClientService.LicenseState.GRACE_PERIOD instead
|
||||
*
|
||||
* @return true if marvel is running within the "grace period", ie when the license
|
||||
* is expired but a given extra delay is not yet elapsed
|
||||
*/
|
||||
|
@ -164,30 +93,57 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
|
|||
return expiryDate;
|
||||
}
|
||||
|
||||
class InternalListener implements LicensesClientService.Listener {
|
||||
@Override
|
||||
public String id() {
|
||||
return FEATURE_NAME;
|
||||
}
|
||||
|
||||
private final LicenseService service;
|
||||
@Override
|
||||
public String[] expirationMessages() {
|
||||
// TODO add messages to be logged around license expiry
|
||||
return Strings.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
public InternalListener(LicenseService service) {
|
||||
this.service = service;
|
||||
@Override
|
||||
public String[] acknowledgmentMessages(License currentLicense, License newLicense) {
|
||||
switch (newLicense.operationMode()) {
|
||||
case BASIC:
|
||||
if (currentLicense != null) {
|
||||
switch (currentLicense.operationMode()) {
|
||||
case TRIAL:
|
||||
case GOLD:
|
||||
case PLATINUM:
|
||||
return new String[] {
|
||||
LoggerMessageFormat.format(
|
||||
"Multi-cluster support is disabled for clusters with [{}] licenses.\n" +
|
||||
"If you are running multiple customers, users won't be able to access this\n" +
|
||||
"all the clusters with [{}] licenses from a single Marvel instance. To access them\n" +
|
||||
"a dedicated and separated marvel instance will be required for each cluster",
|
||||
newLicense.type(), newLicense.type())
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return Strings.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnabled(License license) {
|
||||
try {
|
||||
service.enabled = true;
|
||||
service.expiryDate = license.expiryDate();
|
||||
service.mode = Mode.fromName(license.type());
|
||||
} catch (IllegalArgumentException e) {
|
||||
service.mode = Mode.LITE;
|
||||
@Override
|
||||
public void onChange(License license, LicenseState state) {
|
||||
synchronized (this) {
|
||||
this.state = state;
|
||||
if (license != null) {
|
||||
try {
|
||||
mode = Mode.fromName(license.type());
|
||||
} catch (IllegalArgumentException e) {
|
||||
mode = Mode.LITE;
|
||||
}
|
||||
expiryDate = license.expiryDate();
|
||||
} else {
|
||||
mode = Mode.LITE;
|
||||
}
|
||||
if (state == LicenseState.DISABLED) {
|
||||
mode = Mode.LITE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled(License license) {
|
||||
service.enabled = false;
|
||||
service.expiryDate = license.expiryDate();
|
||||
service.mode = Mode.LITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ public enum Mode {
|
|||
|
||||
/**
|
||||
* Marvel runs in downgraded mode
|
||||
*
|
||||
* TODO: do we really need mode?
|
||||
*/
|
||||
TRIAL(0),
|
||||
|
||||
|
@ -55,9 +57,13 @@ public enum Mode {
|
|||
|
||||
public static Mode fromName(String name) {
|
||||
switch (name.toLowerCase(Locale.ROOT)) {
|
||||
case "trial": return TRIAL;
|
||||
case "lite": return LITE;
|
||||
case "standard" : return STANDARD;
|
||||
case "trial":
|
||||
return LITE;
|
||||
case "basic":
|
||||
case "gold" :
|
||||
case "silver":
|
||||
case "platinum":
|
||||
return STANDARD;
|
||||
default:
|
||||
throw new ElasticsearchException("unknown marvel mode name [" + name + "]");
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ public class MarvelInternalUserHolder {
|
|||
// and full access to .marvel-* and .marvel-data indices
|
||||
.add(Privilege.Index.ALL, MarvelSettings.MARVEL_INDICES_PREFIX + "*")
|
||||
|
||||
// note, we don't need _licenses permission as we're taking the licenses
|
||||
// note, we don't need _license permission as we're taking the licenses
|
||||
// directly form the license service.
|
||||
|
||||
.build();
|
||||
|
|
|
@ -17,12 +17,11 @@ import org.elasticsearch.common.inject.Module;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesManagerService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseRequest;
|
||||
import org.elasticsearch.license.plugin.core.*;
|
||||
import org.elasticsearch.marvel.MarvelPlugin;
|
||||
import org.elasticsearch.marvel.agent.settings.MarvelSettings;
|
||||
import org.elasticsearch.marvel.license.LicenseService;
|
||||
import org.elasticsearch.marvel.shield.MarvelShieldIntegration;
|
||||
import org.elasticsearch.marvel.shield.SecuredClient;
|
||||
import org.elasticsearch.marvel.test.MarvelIntegTestCase;
|
||||
|
@ -88,15 +87,13 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
|
|||
|
||||
private static License createTestingLicense(long issueDate, long expiryDate) {
|
||||
return License.builder()
|
||||
.feature(LicenseService.FEATURE_NAME)
|
||||
.expiryDate(expiryDate)
|
||||
.issueDate(issueDate)
|
||||
.issuedTo("AbstractCollectorTestCase")
|
||||
.issuer("test")
|
||||
.maxNodes(Integer.MAX_VALUE)
|
||||
.signature("_signature")
|
||||
.type("standard")
|
||||
.subscriptionType("all_is_good")
|
||||
.type("basic")
|
||||
.uid(String.valueOf(RandomizedTest.systemPropertyAsInt(SysGlobals.CHILDVM_SYSPROP_JVM_ID, 0)) + System.identityHashCode(AbstractCollectorTestCase.class))
|
||||
.build();
|
||||
}
|
||||
|
@ -107,7 +104,7 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
|
|||
|
||||
final License license = createTestingLicense(issueDate, expiryDate);
|
||||
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
|
||||
service.enable(license);
|
||||
service.onChange(license, LicenseState.ENABLED);
|
||||
}
|
||||
for (LicensesManagerServiceForCollectors service : internalCluster().getInstances(LicensesManagerServiceForCollectors.class)) {
|
||||
service.update(license);
|
||||
|
@ -120,7 +117,7 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
|
|||
|
||||
final License license = createTestingLicense(issueDate, expiryDate);
|
||||
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
|
||||
service.disable(license);
|
||||
service.onChange(license, LicenseState.GRACE_PERIOD);
|
||||
}
|
||||
for (LicensesManagerServiceForCollectors service : internalCluster().getInstances(LicensesManagerServiceForCollectors.class)) {
|
||||
service.update(license);
|
||||
|
@ -133,7 +130,7 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
|
|||
|
||||
final License license = createTestingLicense(issueDate, expiryDate);
|
||||
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
|
||||
service.disable(license);
|
||||
service.onChange(license, LicenseState.DISABLED);
|
||||
}
|
||||
for (LicensesManagerServiceForCollectors service : internalCluster().getInstances(LicensesManagerServiceForCollectors.class)) {
|
||||
service.update(license);
|
||||
|
@ -146,7 +143,7 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
|
|||
|
||||
final License license = createTestingLicense(issueDate, expiryDate);
|
||||
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
|
||||
service.disable(license);
|
||||
service.onChange(license, LicenseState.DISABLED);
|
||||
}
|
||||
for (LicensesManagerServiceForCollectors service : internalCluster().getInstances(LicensesManagerServiceForCollectors.class)) {
|
||||
service.update(license);
|
||||
|
@ -205,7 +202,7 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
|
|||
@Override
|
||||
protected void configure() {
|
||||
bind(LicenseServiceForCollectors.class).asEagerSingleton();
|
||||
bind(LicensesClientService.class).to(LicenseServiceForCollectors.class);
|
||||
bind(LicenseeRegistry.class).to(LicenseServiceForCollectors.class);
|
||||
bind(LicensesManagerServiceForCollectors.class).asEagerSingleton();
|
||||
bind(LicensesManagerService.class).to(LicensesManagerServiceForCollectors.class);
|
||||
}
|
||||
|
@ -213,9 +210,9 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public static class LicenseServiceForCollectors extends AbstractComponent implements LicensesClientService {
|
||||
public static class LicenseServiceForCollectors extends AbstractComponent implements LicenseeRegistry {
|
||||
|
||||
private final List<Listener> listeners = new ArrayList<>();
|
||||
private final List<Licensee> licensees = new ArrayList<>();
|
||||
|
||||
@Inject
|
||||
public LicenseServiceForCollectors(Settings settings) {
|
||||
|
@ -223,19 +220,13 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void register(String feature, TrialLicenseOptions trialLicenseOptions, Collection<ExpirationCallback> expirationCallbacks, AcknowledgementCallback acknowledgementCallback, Listener listener) {
|
||||
listeners.add(listener);
|
||||
public void register(Licensee licensee) {
|
||||
licensees.add(licensee);
|
||||
}
|
||||
|
||||
public void enable(License license) {
|
||||
for (Listener listener : listeners) {
|
||||
listener.onEnabled(license);
|
||||
}
|
||||
}
|
||||
|
||||
public void disable(License license) {
|
||||
for (Listener listener : listeners) {
|
||||
listener.onDisabled(license);
|
||||
public void onChange(License license, LicenseState state) {
|
||||
for (Licensee licensee : licensees) {
|
||||
licensee.onChange(license, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,21 +236,24 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
|
|||
private final Map<String, License> licenses = Collections.synchronizedMap(new HashMap<String, License>());
|
||||
|
||||
@Override
|
||||
public void registerLicenses(LicensesService.PutLicenseRequestHolder requestHolder, ActionListener<LicensesService.LicensesUpdateResponse> listener) {
|
||||
public void registerLicense(PutLicenseRequest request, ActionListener<LicensesService.LicensesUpdateResponse> listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLicenses(LicensesService.DeleteLicenseRequestHolder requestHolder, ActionListener<ClusterStateUpdateResponse> listener) {
|
||||
public void removeLicense(DeleteLicenseRequest request, ActionListener<ClusterStateUpdateResponse> listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> enabledFeatures() {
|
||||
public List<String> licenseesWithState(LicenseState state) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<License> getLicenses() {
|
||||
return new ArrayList<>(licenses.values());
|
||||
public License getLicense() {
|
||||
// TODO: we only take the first of the licenses that are updated
|
||||
// FIXME
|
||||
Iterator<License> iterator = licenses.values().iterator();
|
||||
return iterator.hasNext() ? iterator.next() : null;
|
||||
}
|
||||
|
||||
public void update(License license) {
|
||||
|
|
|
@ -93,7 +93,6 @@ public class ClusterInfoIT extends MarvelIntegTestCase {
|
|||
String recalculated = ClusterInfoRenderer.hash(status, uid, type, String.valueOf(expiryDate), clusterUUID);
|
||||
assertThat(hkey, equalTo(recalculated));
|
||||
|
||||
assertThat((String) license.get(ClusterInfoRenderer.Fields.FEATURE.underscore().toString()), not(isEmptyOrNullString()));
|
||||
assertThat((String) license.get(ClusterInfoRenderer.Fields.ISSUER.underscore().toString()), not(isEmptyOrNullString()));
|
||||
assertThat((String) license.get(ClusterInfoRenderer.Fields.ISSUED_TO.underscore().toString()), not(isEmptyOrNullString()));
|
||||
assertThat((Long) license.get(ClusterInfoRenderer.Fields.ISSUE_DATE_IN_MILLIS.underscore().toString()), greaterThan(0L));
|
||||
|
|
|
@ -13,8 +13,9 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.inject.Module;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.license.plugin.core.Licensee;
|
||||
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
|
||||
import org.elasticsearch.marvel.MarvelPlugin;
|
||||
import org.elasticsearch.marvel.mode.Mode;
|
||||
import org.elasticsearch.marvel.test.MarvelIntegTestCase;
|
||||
|
@ -106,26 +107,24 @@ public class LicenseIntegrationTests extends MarvelIntegTestCase {
|
|||
@Override
|
||||
protected void configure() {
|
||||
bind(MockLicenseService.class).asEagerSingleton();
|
||||
bind(LicensesClientService.class).to(MockLicenseService.class);
|
||||
bind(LicenseeRegistry.class).to(MockLicenseService.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static class MockLicenseService extends AbstractComponent implements LicensesClientService {
|
||||
public static class MockLicenseService extends AbstractComponent implements LicenseeRegistry {
|
||||
|
||||
static final License DUMMY_LICENSE = License.builder()
|
||||
.feature(LicenseService.FEATURE_NAME)
|
||||
.expiryDate(System.currentTimeMillis())
|
||||
.issueDate(System.currentTimeMillis())
|
||||
.issuedTo("LicensingTests")
|
||||
.issuer("test")
|
||||
.maxNodes(Integer.MAX_VALUE)
|
||||
.signature("_signature")
|
||||
.type("standard")
|
||||
.subscriptionType("all_is_good")
|
||||
.type("basic")
|
||||
.uid(String.valueOf(RandomizedTest.systemPropertyAsInt(SysGlobals.CHILDVM_SYSPROP_JVM_ID, 0)) + System.identityHashCode(LicenseIntegrationTests.class))
|
||||
.build();
|
||||
|
||||
private final List<Listener> listeners = new ArrayList<>();
|
||||
private final List<Licensee> licensees = new ArrayList<>();
|
||||
|
||||
@Inject
|
||||
public MockLicenseService(Settings settings) {
|
||||
|
@ -134,22 +133,20 @@ public class LicenseIntegrationTests extends MarvelIntegTestCase {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void register(String s, LicensesService.TrialLicenseOptions trialLicenseOptions, Collection<LicensesService.ExpirationCallback> collection, AcknowledgementCallback acknowledgementCallback, Listener listener) {
|
||||
listeners.add(listener);
|
||||
public void register(Licensee licensee) {
|
||||
licensees.add(licensee);
|
||||
enable();
|
||||
}
|
||||
|
||||
public void enable() {
|
||||
// enabled all listeners (incl. shield)
|
||||
for (Listener listener : listeners) {
|
||||
listener.onEnabled(DUMMY_LICENSE);
|
||||
for (Licensee licensee : licensees) {
|
||||
licensee.onChange(DUMMY_LICENSE, randomBoolean() ? LicenseState.GRACE_PERIOD : LicenseState.ENABLED);
|
||||
}
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
// only disable watcher listener (we need shield to work)
|
||||
for (Listener listener : listeners) {
|
||||
listener.onDisabled(DUMMY_LICENSE);
|
||||
for (Licensee licensee : licensees) {
|
||||
licensee.onChange(DUMMY_LICENSE, LicenseState.DISABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ To install or update the license use the following REST API:
|
|||
|
||||
[source,shell]
|
||||
-----------------------------------------------------------------------
|
||||
curl -XPUT -u admin 'http://<host>:<port>/_licenses' -d @license.json
|
||||
curl -XPUT -u admin 'http://<host>:<port>/_license' -d @license.json
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Where:
|
||||
|
@ -42,7 +42,7 @@ You can list all currently installed licenses by executing the following REST AP
|
|||
|
||||
[source,shell]
|
||||
-----------------------------------------------------
|
||||
curl -XGET -u admin:password 'http://<host>:<port>/_licenses'
|
||||
curl -XGET -u admin:password 'http://<host>:<port>/_license'
|
||||
-----------------------------------------------------
|
||||
|
||||
The response of this command will be a JSON listing all available licenses. In the case of Shield, the following
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.elasticsearch.action.support.ActionFilterChain;
|
|||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.license.plugin.core.LicenseUtils;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.shield.action.interceptor.RequestInterceptor;
|
||||
|
@ -62,13 +63,8 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
|
|||
this.actionMapper = actionMapper;
|
||||
licenseEventsNotifier.register(new LicenseEventsNotifier.Listener() {
|
||||
@Override
|
||||
public void enabled() {
|
||||
licenseEnabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disabled() {
|
||||
licenseEnabled = false;
|
||||
public void notify(LicenseState state) {
|
||||
licenseEnabled = state != LicenseState.DISABLED;
|
||||
}
|
||||
});
|
||||
this.requestInterceptors = requestInterceptors;
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
*/
|
||||
package org.elasticsearch.shield.license;
|
||||
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -26,22 +28,14 @@ public class LicenseEventsNotifier {
|
|||
listeners.add(listener);
|
||||
}
|
||||
|
||||
protected void notifyEnabled() {
|
||||
protected void notify(LicenseState state) {
|
||||
for (Listener listener : listeners) {
|
||||
listener.enabled();
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyDisabled() {
|
||||
for (Listener listener : listeners) {
|
||||
listener.disabled();
|
||||
listener.notify(state);
|
||||
}
|
||||
}
|
||||
|
||||
public static interface Listener {
|
||||
|
||||
void enabled();
|
||||
|
||||
void disabled();
|
||||
void notify(LicenseState state);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,99 +6,95 @@
|
|||
package org.elasticsearch.shield.license;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.license.plugin.core.Licensee;
|
||||
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
|
||||
import org.elasticsearch.shield.ShieldPlugin;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
|
||||
public class LicenseService extends AbstractLifecycleComponent<LicenseService> implements Licensee {
|
||||
|
||||
public static final String FEATURE_NAME = ShieldPlugin.NAME;
|
||||
|
||||
private static final LicensesClientService.TrialLicenseOptions TRIAL_LICENSE_OPTIONS =
|
||||
new LicensesClientService.TrialLicenseOptions(TimeValue.timeValueHours(30 * 24), 1000);
|
||||
|
||||
private static final FormatDateTimeFormatter DATE_FORMATTER = Joda.forPattern("EEEE, MMMMM dd, yyyy", Locale.ROOT);
|
||||
|
||||
private final LicensesClientService licensesClientService;
|
||||
private final LicenseeRegistry licenseeRegistry;
|
||||
private final LicenseEventsNotifier notifier;
|
||||
private final Collection<LicensesClientService.ExpirationCallback> expirationLoggers;
|
||||
private final LicensesClientService.AcknowledgementCallback acknowledgementCallback;
|
||||
|
||||
private boolean enabled = false;
|
||||
private volatile LicenseState state = LicenseState.DISABLED;
|
||||
|
||||
@Inject
|
||||
public LicenseService(Settings settings, LicensesClientService licensesClientService, LicenseEventsNotifier notifier) {
|
||||
public LicenseService(Settings settings, LicenseeRegistry licenseeRegistry, LicenseEventsNotifier notifier) {
|
||||
super(settings);
|
||||
this.licensesClientService = licensesClientService;
|
||||
this.licenseeRegistry = licenseeRegistry;
|
||||
this.notifier = notifier;
|
||||
this.expirationLoggers = Arrays.asList(
|
||||
new LicensesClientService.ExpirationCallback.Pre(days(7), days(30), days(1)) {
|
||||
@Override
|
||||
public void on(License license, LicensesClientService.ExpirationStatus status) {
|
||||
logger.error("\n" +
|
||||
"#\n" +
|
||||
"# Shield license will expire on [{}]. Cluster health, cluster stats and indices stats operations are\n" +
|
||||
"# blocked on Shield license expiration. All data operations (read and write) continue to work. If you\n" +
|
||||
"# have a new license, please update it. Otherwise, please reach out to your support contact.\n" +
|
||||
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
|
||||
}
|
||||
},
|
||||
new LicensesClientService.ExpirationCallback.Pre(days(0), days(7), minutes(10)) {
|
||||
@Override
|
||||
public void on(License license, LicensesClientService.ExpirationStatus status) {
|
||||
logger.error("\n" +
|
||||
"#\n" +
|
||||
"# Shield license will expire on [{}]. Cluster health, cluster stats and indices stats operations are\n" +
|
||||
"# blocked on Shield license expiration. All data operations (read and write) continue to work. If you\n" +
|
||||
"# have a new license, please update it. Otherwise, please reach out to your support contact.\n" +
|
||||
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
|
||||
}
|
||||
},
|
||||
new LicensesClientService.ExpirationCallback.Post(days(0), null, minutes(10)) {
|
||||
@Override
|
||||
public void on(License license, LicensesClientService.ExpirationStatus status) {
|
||||
logger.error("\n" +
|
||||
"#\n" +
|
||||
"# SHIELD LICENSE EXPIRED ON [{}]! CLUSTER HEALTH, CLUSTER STATS AND INDICES STATS OPERATIONS ARE\n" +
|
||||
"# NOW BLOCKED. ALL DATA OPERATIONS (READ AND WRITE) CONTINUE TO WORK. IF YOU HAVE A NEW LICENSE, PLEASE\n" +
|
||||
"# UPDATE IT. OTHERWISE, PLEASE REACH OUT TO YOUR SUPPORT CONTACT.\n" +
|
||||
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
|
||||
}
|
||||
}
|
||||
);
|
||||
this.acknowledgementCallback = new LicensesClientService.AcknowledgementCallback() {
|
||||
@Override
|
||||
public List<String> acknowledge(License currentLicense, License newLicense) {
|
||||
// TODO: add messages to be acknowledged when installing newLicense from currentLicense
|
||||
// NOTE: currentLicense can be null, as a license registration can happen before
|
||||
// a trial license could be generated
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return FEATURE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] expirationMessages() {
|
||||
return new String[] {
|
||||
"Cluster health, cluster stats and indices stats operations are blocked",
|
||||
"All data operations (read and write) continue to work"
|
||||
};
|
||||
}
|
||||
|
||||
public synchronized boolean enabled() {
|
||||
return enabled;
|
||||
@Override
|
||||
public String[] acknowledgmentMessages(License currentLicense, License newLicense) {
|
||||
switch (newLicense.operationMode()) {
|
||||
case BASIC:
|
||||
if (currentLicense != null) {
|
||||
switch (currentLicense.operationMode()) {
|
||||
case TRIAL:
|
||||
case GOLD:
|
||||
case PLATINUM:
|
||||
return new String[] { "The following Shield functionality will be disabled: authentication, authorization, ip filtering, auditing, SSL will be disabled on node restart. Please restart your node after applying the license." };
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GOLD:
|
||||
if (currentLicense != null) {
|
||||
switch (currentLicense.operationMode()) {
|
||||
case TRIAL:
|
||||
case BASIC:
|
||||
case PLATINUM:
|
||||
return new String[] {
|
||||
"Field and document level access control will be disabled"
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Strings.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(License license, LicenseState state) {
|
||||
synchronized (this) {
|
||||
this.state = state;
|
||||
notifier.notify(state);
|
||||
}
|
||||
}
|
||||
public LicenseState state() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws ElasticsearchException {
|
||||
if (settings.getGroups("tribe", true).isEmpty()) {
|
||||
licensesClientService.register(FEATURE_NAME, TRIAL_LICENSE_OPTIONS, expirationLoggers, acknowledgementCallback, new InternalListener());
|
||||
licenseeRegistry.register(this);
|
||||
} else {
|
||||
//TODO currently we disable licensing on tribe node. remove this once es core supports merging cluster
|
||||
new InternalListener().onEnabled(null);
|
||||
onChange(null, LicenseState.ENABLED);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,34 +105,4 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
|
|||
@Override
|
||||
protected void doClose() throws ElasticsearchException {
|
||||
}
|
||||
|
||||
static TimeValue days(int days) {
|
||||
return TimeValue.timeValueHours(days * 24);
|
||||
}
|
||||
|
||||
static TimeValue minutes(int minutes) {
|
||||
return TimeValue.timeValueMinutes(minutes);
|
||||
}
|
||||
|
||||
class InternalListener implements LicensesClientService.Listener {
|
||||
|
||||
@Override
|
||||
public void onEnabled(License license) {
|
||||
synchronized (LicenseService.this) {
|
||||
logger.info("enabling license for [{}]", FEATURE_NAME);
|
||||
enabled = true;
|
||||
notifier.notifyEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled(License license) {
|
||||
synchronized (LicenseService.this) {
|
||||
logger.info("DISABLING LICENSE FOR [{}]", FEATURE_NAME);
|
||||
enabled = false;
|
||||
notifier.notifyDisabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.inject.internal.Nullable;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.rest.*;
|
||||
import org.elasticsearch.shield.ShieldBuild;
|
||||
import org.elasticsearch.shield.ShieldPlugin;
|
||||
|
@ -71,7 +72,7 @@ public class RestShieldInfoAction extends BaseRestHandler {
|
|||
|
||||
private Status resolveStatus() {
|
||||
if (shieldEnabled) {
|
||||
if (licenseService.enabled()) {
|
||||
if (licenseService.state() != LicenseState.DISABLED) {
|
||||
return Status.ENABLED;
|
||||
}
|
||||
return Status.UNLICENSED;
|
||||
|
|
|
@ -19,7 +19,9 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.inject.Module;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.license.plugin.core.Licensee;
|
||||
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.shield.license.LicenseService;
|
||||
|
@ -169,13 +171,13 @@ public class LicensingTests extends ShieldIntegTestCase {
|
|||
}
|
||||
|
||||
public static void disableLicensing() {
|
||||
for (InternalLicensesClientService service : internalCluster().getInstances(InternalLicensesClientService.class)) {
|
||||
for (InternalLicenseeRegistry service : internalCluster().getInstances(InternalLicenseeRegistry.class)) {
|
||||
service.disable();
|
||||
}
|
||||
}
|
||||
|
||||
public static void enableLicensing() {
|
||||
for (InternalLicensesClientService service : internalCluster().getInstances(InternalLicensesClientService.class)) {
|
||||
for (InternalLicenseeRegistry service : internalCluster().getInstances(InternalLicenseeRegistry.class)) {
|
||||
service.enable();
|
||||
}
|
||||
}
|
||||
|
@ -203,49 +205,47 @@ public class LicensingTests extends ShieldIntegTestCase {
|
|||
public static class InternalLicenseModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(InternalLicensesClientService.class).asEagerSingleton();
|
||||
bind(LicensesClientService.class).to(InternalLicensesClientService.class);
|
||||
bind(InternalLicenseeRegistry.class).asEagerSingleton();
|
||||
bind(LicenseeRegistry.class).to(InternalLicenseeRegistry.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static class InternalLicensesClientService extends AbstractComponent implements LicensesClientService {
|
||||
public static class InternalLicenseeRegistry extends AbstractComponent implements LicenseeRegistry {
|
||||
|
||||
private final List<Listener> listeners = new ArrayList<>();
|
||||
private final List<Licensee> licensees = new ArrayList<>();
|
||||
|
||||
static final License DUMMY_LICENSE = License.builder()
|
||||
.feature(LicenseService.FEATURE_NAME)
|
||||
.expiryDate(System.currentTimeMillis())
|
||||
.issueDate(System.currentTimeMillis())
|
||||
.issuedTo("LicensingTests")
|
||||
.issuer("test")
|
||||
.maxNodes(Integer.MAX_VALUE)
|
||||
.signature("_signature")
|
||||
.type("test_license_for_shield")
|
||||
.subscriptionType("all_is_good")
|
||||
.type("basic")
|
||||
.uid(String.valueOf(randomLong()) + System.identityHashCode(LicensingTests.class))
|
||||
.build();
|
||||
|
||||
@Inject
|
||||
public InternalLicensesClientService(Settings settings) {
|
||||
public InternalLicenseeRegistry(Settings settings) {
|
||||
super(settings);
|
||||
enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String s, LicensesClientService.TrialLicenseOptions trialLicenseOptions, Collection<LicensesClientService.ExpirationCallback> collection, AcknowledgementCallback acknowledgementCallback, Listener listener) {
|
||||
listeners.add(listener);
|
||||
public void register(Licensee licensee) {
|
||||
licensees.add(licensee);
|
||||
enable();
|
||||
}
|
||||
|
||||
void enable() {
|
||||
for (Listener listener : listeners) {
|
||||
listener.onEnabled(DUMMY_LICENSE);
|
||||
for (Licensee licensee : licensees) {
|
||||
licensee.onChange(DUMMY_LICENSE, LicenseState.ENABLED);
|
||||
}
|
||||
}
|
||||
|
||||
void disable() {
|
||||
for (Listener listener : listeners) {
|
||||
listener.onDisabled(DUMMY_LICENSE);
|
||||
for (Licensee licensee : licensees) {
|
||||
licensee.onChange(DUMMY_LICENSE, LicenseState.DISABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.elasticsearch.action.ActionRequest;
|
|||
import org.elasticsearch.action.search.SearchScrollRequest;
|
||||
import org.elasticsearch.action.support.ActionFilterChain;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.shield.action.interceptor.RequestInterceptor;
|
||||
import org.elasticsearch.shield.audit.AuditTrail;
|
||||
|
@ -112,7 +113,7 @@ public class ShieldActionFilterTests extends ESTestCase {
|
|||
private class MockLicenseEventsNotifier extends LicenseEventsNotifier {
|
||||
@Override
|
||||
public void register(MockLicenseEventsNotifier.Listener listener) {
|
||||
listener.enabled();
|
||||
listener.notify(LicenseState.ENABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,93 +6,70 @@
|
|||
package org.elasticsearch.watcher.license;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.license.plugin.core.Licensee;
|
||||
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
|
||||
import org.elasticsearch.watcher.WatcherPlugin;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
|
||||
public class LicenseService extends AbstractLifecycleComponent<LicenseService> implements Licensee {
|
||||
|
||||
public static final String FEATURE_NAME = WatcherPlugin.NAME;
|
||||
|
||||
private static final LicensesClientService.TrialLicenseOptions TRIAL_LICENSE_OPTIONS =
|
||||
new LicensesClientService.TrialLicenseOptions(TimeValue.timeValueHours(30 * 24), 1000);
|
||||
|
||||
private static final FormatDateTimeFormatter DATE_FORMATTER = Joda.forPattern("EEEE, MMMMM dd, yyyy", Locale.ROOT);
|
||||
|
||||
private final LicensesClientService clientService;
|
||||
private final Collection<LicensesService.ExpirationCallback> expirationLoggers;
|
||||
private final LicensesClientService.AcknowledgementCallback acknowledgementCallback;
|
||||
|
||||
private volatile boolean enabled;
|
||||
private final LicenseeRegistry clientService;
|
||||
private volatile LicenseState state;
|
||||
|
||||
@Inject
|
||||
public LicenseService(Settings settings, LicensesClientService clientService) {
|
||||
public LicenseService(Settings settings, LicenseeRegistry clientService) {
|
||||
super(settings);
|
||||
this.clientService = clientService;
|
||||
this.expirationLoggers = Arrays.asList(
|
||||
new LicensesService.ExpirationCallback.Pre(days(7), days(30), days(1)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
logger.error("\n" +
|
||||
"#\n" +
|
||||
"# Watcher license will expire on [{}]. All configured actions on\n" +
|
||||
"# all registered watches are throttled (not executed) on Watcher license expiration. \n" +
|
||||
"# Watches will continue be evaluated and watch history will continue being recorded.\n" +
|
||||
"# Have a new license? please update it. Otherwise, please reach out to your support contact.\n" +
|
||||
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
|
||||
}
|
||||
},
|
||||
new LicensesService.ExpirationCallback.Pre(days(0), days(7), minutes(10)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
logger.error("\n" +
|
||||
"#\n" +
|
||||
"# Watcher license will expire on [{}]. All configured actions on\n" +
|
||||
"# all registered watches are throttled (not executed) on Watcher license expiration. \n" +
|
||||
"# Watches will continue be evaluated and watch history will continue being recorded.\n" +
|
||||
"# Have a new license? please update it. Otherwise, please reach out to your support contact.\n" +
|
||||
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
|
||||
}
|
||||
},
|
||||
new LicensesService.ExpirationCallback.Post(days(0), null, minutes(10)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
logger.error("\n" +
|
||||
"#\n" +
|
||||
"# WATCHER LICENSE WAS EXPIRED ON [{}]. ALL CONFIGURED ACTIONS ON\n" +
|
||||
"# ALL REGISTERED WATCHES ARE THROTTLED (NOT EXECUTED) ON WATCHER LICENSE EXPIRATION. \n" +
|
||||
"# WATCHES WILL CONTINUE BE EVALUATED AND WATCH HISTORY WILL CONTINUE BEING RECORDED.\n" +
|
||||
"# HAVE A NEW LICENSE? PLEASE UPDATE IT. OTHERWISE, PLEASE REACH OUT TO YOUR SUPPORT CONTACT.\n" +
|
||||
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return FEATURE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] expirationMessages() {
|
||||
// TODO add messages to be logged around license expiry
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] acknowledgmentMessages(License currentLicense, License newLicense) {
|
||||
switch (newLicense.operationMode()) {
|
||||
case BASIC:
|
||||
if (currentLicense != null) {
|
||||
switch (currentLicense.operationMode()) {
|
||||
case TRIAL:
|
||||
case GOLD:
|
||||
case PLATINUM:
|
||||
return new String[] { "Watcher will be disabled" };
|
||||
}
|
||||
}
|
||||
);
|
||||
this.acknowledgementCallback = new LicensesClientService.AcknowledgementCallback() {
|
||||
@Override
|
||||
public List<String> acknowledge(License currentLicense, License newLicense) {
|
||||
// TODO: add messages to be acknowledged when installing newLicense from currentLicense
|
||||
// NOTE: currentLicense can be null, as a license registration can happen before
|
||||
// a trial license could be generated
|
||||
return Collections.emptyList();
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
return Strings.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(License license, LicenseState state) {
|
||||
synchronized (this) {
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws ElasticsearchException {
|
||||
clientService.register(FEATURE_NAME, TRIAL_LICENSE_OPTIONS, expirationLoggers, acknowledgementCallback, new InternalListener(this));
|
||||
clientService.register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -104,33 +81,6 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
|
|||
}
|
||||
|
||||
public boolean enabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
static TimeValue days(int days) {
|
||||
return TimeValue.timeValueHours(days * 24);
|
||||
}
|
||||
|
||||
static TimeValue minutes(int minutes) {
|
||||
return TimeValue.timeValueMinutes(minutes);
|
||||
}
|
||||
|
||||
class InternalListener implements LicensesClientService.Listener {
|
||||
|
||||
private final LicenseService service;
|
||||
|
||||
public InternalListener(LicenseService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnabled(License license) {
|
||||
service.enabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled(License license) {
|
||||
service.enabled = false;
|
||||
}
|
||||
return state != LicenseState.DISABLED;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,9 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.inject.Module;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.license.plugin.core.Licensee;
|
||||
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.watcher.actions.ActionStatus;
|
||||
|
@ -46,15 +47,13 @@ import static org.hamcrest.Matchers.*;
|
|||
public class LicenseIntegrationTests extends AbstractWatcherIntegrationTestCase {
|
||||
|
||||
static final License DUMMY_LICENSE = License.builder()
|
||||
.feature(LicenseService.FEATURE_NAME)
|
||||
.expiryDate(System.currentTimeMillis())
|
||||
.issueDate(System.currentTimeMillis())
|
||||
.issuedTo("LicensingTests")
|
||||
.issuer("test")
|
||||
.maxNodes(Integer.MAX_VALUE)
|
||||
.signature("_signature")
|
||||
.type("test_license_for_watcher")
|
||||
.subscriptionType("all_is_good")
|
||||
.type("basic")
|
||||
.uid(String.valueOf(RandomizedTest.systemPropertyAsInt(SysGlobals.CHILDVM_SYSPROP_JVM_ID, 0)) + System.identityHashCode(LicenseIntegrationTests.class))
|
||||
.build();
|
||||
|
||||
|
@ -301,13 +300,13 @@ public class LicenseIntegrationTests extends AbstractWatcherIntegrationTestCase
|
|||
@Override
|
||||
protected void configure() {
|
||||
bind(MockLicenseService.class).asEagerSingleton();
|
||||
bind(LicensesClientService.class).to(MockLicenseService.class);
|
||||
bind(LicenseeRegistry.class).to(MockLicenseService.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static class MockLicenseService extends AbstractComponent implements LicensesClientService {
|
||||
public static class MockLicenseService extends AbstractComponent implements LicenseeRegistry {
|
||||
|
||||
private final List<Listener> listeners = new ArrayList<>();
|
||||
private final List<Licensee> licensees = new ArrayList<>();
|
||||
|
||||
@Inject
|
||||
public MockLicenseService(Settings settings) {
|
||||
|
@ -316,23 +315,23 @@ public class LicenseIntegrationTests extends AbstractWatcherIntegrationTestCase
|
|||
}
|
||||
|
||||
@Override
|
||||
public void register(String s, LicensesService.TrialLicenseOptions trialLicenseOptions, Collection<LicensesService.ExpirationCallback> collection, AcknowledgementCallback acknowledgementCallback, Listener listener) {
|
||||
listeners.add(listener);
|
||||
public void register(Licensee licensee) {
|
||||
licensees.add(licensee);
|
||||
enable();
|
||||
}
|
||||
|
||||
public void enable() {
|
||||
// enabled all listeners (incl. shield)
|
||||
for (Listener listener : listeners) {
|
||||
listener.onEnabled(DUMMY_LICENSE);
|
||||
for (Licensee licensee : licensees) {
|
||||
licensee.onChange(DUMMY_LICENSE, LicenseState.ENABLED);
|
||||
}
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
// only disable watcher listener (we need shield to work)
|
||||
for (Listener listener : listeners) {
|
||||
if (listener instanceof LicenseService.InternalListener) {
|
||||
listener.onDisabled(DUMMY_LICENSE);
|
||||
for (Licensee licensee : licensees) {
|
||||
if (licensee instanceof LicenseService) {
|
||||
licensee.onChange(DUMMY_LICENSE, LicenseState.DISABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue