Merge branch 'master' into immutable_map_be_gone

Original commit: elastic/x-pack-elasticsearch@62358ec345
This commit is contained in:
Nik Everett 2015-10-09 12:25:28 -04:00
commit 2bde3de3f0
37 changed files with 621 additions and 621 deletions

View File

@ -42,6 +42,13 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>mockwebserver</artifactId>
<version>2.3.0</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -60,7 +60,8 @@ public class ClusterInfoCollector extends AbstractCollector<ClusterInfoMarvelDoc
List<MarvelDoc> results = new ArrayList<>(1); List<MarvelDoc> results = new ArrayList<>(1);
// Retrieves all licenses // 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 // Retrieves additional cluster stats
ClusterStatsResponse clusterStats = client.admin().cluster().prepareClusterStats().get(marvelSettings.clusterStatsTimeout()); ClusterStatsResponse clusterStats = client.admin().cluster().prepareClusterStats().get(marvelSettings.clusterStatsTimeout());

View File

@ -54,6 +54,7 @@ public class HttpExporter extends Exporter {
public static final String HOST_SETTING = "host"; public static final String HOST_SETTING = "host";
public static final String CONNECTION_TIMEOUT_SETTING = "connection.timeout"; public static final String CONNECTION_TIMEOUT_SETTING = "connection.timeout";
public static final String CONNECTION_READ_TIMEOUT_SETTING = "connection.read_timeout"; public static final String CONNECTION_READ_TIMEOUT_SETTING = "connection.read_timeout";
public static final String CONNECTION_KEEP_ALIVE_SETTING = "connection.keep_alive";
public static final String AUTH_USERNAME_SETTING = "auth.username"; public static final String AUTH_USERNAME_SETTING = "auth.username";
public static final String AUTH_PASSWORD_SETTING = "auth.password"; public static final String AUTH_PASSWORD_SETTING = "auth.password";
@ -91,6 +92,7 @@ public class HttpExporter extends Exporter {
/** Version of the built-in template **/ /** Version of the built-in template **/
final Version templateVersion; final Version templateVersion;
boolean keepAlive;
final ConnectionKeepAliveWorker keepAliveWorker; final ConnectionKeepAliveWorker keepAliveWorker;
Thread keepAliveThread; Thread keepAliveThread;
@ -117,6 +119,7 @@ public class HttpExporter extends Exporter {
String templateCheckTimeoutValue = config.settings().get(TEMPLATE_CHECK_TIMEOUT_SETTING, null); String templateCheckTimeoutValue = config.settings().get(TEMPLATE_CHECK_TIMEOUT_SETTING, null);
templateCheckTimeout = TimeValue.parseTimeValue(templateCheckTimeoutValue, null, settingFQN(TEMPLATE_CHECK_TIMEOUT_SETTING)); templateCheckTimeout = TimeValue.parseTimeValue(templateCheckTimeoutValue, null, settingFQN(TEMPLATE_CHECK_TIMEOUT_SETTING));
keepAlive = config.settings().getAsBoolean(CONNECTION_KEEP_ALIVE_SETTING, true);
keepAliveWorker = new ConnectionKeepAliveWorker(); keepAliveWorker = new ConnectionKeepAliveWorker();
sslSocketFactory = createSSLSocketFactory(config.settings().getAsSettings(SSL_SETTING)); sslSocketFactory = createSSLSocketFactory(config.settings().getAsSettings(SSL_SETTING));
@ -511,9 +514,11 @@ public class HttpExporter extends Exporter {
} }
protected void initKeepAliveThread() { protected void initKeepAliveThread() {
keepAliveThread = new Thread(keepAliveWorker, "marvel-exporter[" + config.name() + "][keep_alive]"); if (keepAlive) {
keepAliveThread.setDaemon(true); keepAliveThread = new Thread(keepAliveWorker, "marvel-exporter[" + config.name() + "][keep_alive]");
keepAliveThread.start(); keepAliveThread.setDaemon(true);
keepAliveThread.start();
}
} }

View File

@ -37,7 +37,6 @@ public class ClusterInfoRenderer extends AbstractRenderer<ClusterInfoMarvelDoc>
builder.field(Fields.UID, license.uid()); builder.field(Fields.UID, license.uid());
builder.field(Fields.TYPE, license.type()); builder.field(Fields.TYPE, license.type());
builder.dateValueField(Fields.ISSUE_DATE_IN_MILLIS, Fields.ISSUE_DATE, license.issueDate()); 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.dateValueField(Fields.EXPIRY_DATE_IN_MILLIS, Fields.EXPIRY_DATE, license.expiryDate());
builder.field(Fields.MAX_NODES, license.maxNodes()); builder.field(Fields.MAX_NODES, license.maxNodes());
builder.field(Fields.ISSUED_TO, license.issuedTo()); 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 STATUS = new XContentBuilderString("status");
static final XContentBuilderString UID = new XContentBuilderString("uid"); static final XContentBuilderString UID = new XContentBuilderString("uid");
static final XContentBuilderString TYPE = new XContentBuilderString("type"); 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_IN_MILLIS = new XContentBuilderString("issue_date_in_millis");
static final XContentBuilderString ISSUE_DATE = new XContentBuilderString("issue_date"); static final XContentBuilderString ISSUE_DATE = new XContentBuilderString("issue_date");
static final XContentBuilderString EXPIRY_DATE_IN_MILLIS = new XContentBuilderString("expiry_date_in_millis"); static final XContentBuilderString EXPIRY_DATE_IN_MILLIS = new XContentBuilderString("expiry_date_in_millis");

View File

@ -20,6 +20,7 @@ public class ClusterStateRenderer extends AbstractRenderer<ClusterStateMarvelDoc
public static final String[] FILTERS = { public static final String[] FILTERS = {
"cluster_state.version", "cluster_state.version",
"cluster_state.master_node", "cluster_state.master_node",
"cluster_state.state_uuid",
"cluster_state.status", "cluster_state.status",
"cluster_state.nodes", "cluster_state.nodes",
}; };

View File

@ -6,110 +6,45 @@
package org.elasticsearch.marvel.license; package org.elasticsearch.marvel.license;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject; 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.logging.support.LoggerMessageFormat;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.core.License; 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.LicensesManagerService;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.marvel.MarvelPlugin; import org.elasticsearch.marvel.MarvelPlugin;
import org.elasticsearch.marvel.agent.settings.MarvelSettings; import org.elasticsearch.marvel.agent.settings.MarvelSettings;
import org.elasticsearch.marvel.mode.Mode; 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; 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 LicensesManagerService managerService;
private final LicensesClientService clientService; private final LicenseeRegistry clientService;
private final MarvelSettings marvelSettings; private final MarvelSettings marvelSettings;
private final Collection<LicensesService.ExpirationCallback> expirationLoggers;
private final LicensesClientService.AcknowledgementCallback acknowledgementCallback;
private volatile Mode mode; private volatile Mode mode;
private volatile boolean enabled; private volatile LicenseState state;
private volatile long expiryDate; private volatile long expiryDate;
@Inject @Inject
public LicenseService(Settings settings, LicensesClientService clientService, LicensesManagerService managerService, MarvelSettings marvelSettings) { public LicenseService(Settings settings, LicenseeRegistry clientService, LicensesManagerService managerService, MarvelSettings marvelSettings) {
super(settings); super(settings);
this.managerService = managerService; this.managerService = managerService;
this.clientService = clientService; this.clientService = clientService;
this.marvelSettings = marvelSettings; this.marvelSettings = marvelSettings;
this.mode = Mode.LITE; 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 @Override
protected void doStart() throws ElasticsearchException { protected void doStart() throws ElasticsearchException {
clientService.register(FEATURE_NAME, TRIAL_LICENSE_OPTIONS, expirationLoggers, acknowledgementCallback, new InternalListener(this)); clientService.register(this);
} }
@Override @Override
@ -120,14 +55,6 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
protected void doClose() throws ElasticsearchException { 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 * @return the current marvel's operating mode
*/ */
@ -138,18 +65,20 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
/** /**
* @return all registered licenses * @return all registered licenses
*/ */
public List<License> licenses() { public License license() {
return managerService.getLicenses(); return managerService.getLicense();
} }
/** /**
* @return true if the marvel license is enabled * @return true if the marvel license is enabled
*/ */
public boolean 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 * @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 * is expired but a given extra delay is not yet elapsed
*/ */
@ -164,30 +93,57 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
return expiryDate; 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) { @Override
this.service = service; 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 @Override
public void onEnabled(License license) { public void onChange(License license, LicenseState state) {
try { synchronized (this) {
service.enabled = true; this.state = state;
service.expiryDate = license.expiryDate(); if (license != null) {
service.mode = Mode.fromName(license.type()); try {
} catch (IllegalArgumentException e) { mode = Mode.fromName(license.type());
service.mode = Mode.LITE; } 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;
} }
} }
} }

View File

@ -16,6 +16,8 @@ public enum Mode {
/** /**
* Marvel runs in downgraded mode * Marvel runs in downgraded mode
*
* TODO: do we really need mode?
*/ */
TRIAL(0), TRIAL(0),
@ -55,9 +57,13 @@ public enum Mode {
public static Mode fromName(String name) { public static Mode fromName(String name) {
switch (name.toLowerCase(Locale.ROOT)) { switch (name.toLowerCase(Locale.ROOT)) {
case "trial": return TRIAL; case "trial":
case "lite": return LITE; return LITE;
case "standard" : return STANDARD; case "basic":
case "gold" :
case "silver":
case "platinum":
return STANDARD;
default: default:
throw new ElasticsearchException("unknown marvel mode name [" + name + "]"); throw new ElasticsearchException("unknown marvel mode name [" + name + "]");
} }

View File

@ -33,7 +33,7 @@ public class MarvelInternalUserHolder {
// and full access to .marvel-* and .marvel-data indices // and full access to .marvel-* and .marvel-data indices
.add(Privilege.Index.ALL, MarvelSettings.MARVEL_INDICES_PREFIX + "*") .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. // directly form the license service.
.build(); .build();

View File

@ -82,6 +82,10 @@ import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksAction;
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksRequest; import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksRequest;
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksRequestBuilder; import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksRequestBuilder;
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksResponse; import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksResponse;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateAction;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateRequest;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateRequestBuilder;
import org.elasticsearch.action.admin.cluster.validate.template.RenderSearchTemplateResponse;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesAction; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesAction;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
@ -194,10 +198,6 @@ import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryAction
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest; import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequestBuilder; import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequestBuilder;
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryResponse; import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryResponse;
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateAction;
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateRequest;
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateRequestBuilder;
import org.elasticsearch.action.admin.indices.validate.template.RenderSearchTemplateResponse;
import org.elasticsearch.action.admin.indices.warmer.delete.DeleteWarmerAction; import org.elasticsearch.action.admin.indices.warmer.delete.DeleteWarmerAction;
import org.elasticsearch.action.admin.indices.warmer.delete.DeleteWarmerRequest; import org.elasticsearch.action.admin.indices.warmer.delete.DeleteWarmerRequest;
import org.elasticsearch.action.admin.indices.warmer.delete.DeleteWarmerRequestBuilder; import org.elasticsearch.action.admin.indices.warmer.delete.DeleteWarmerRequestBuilder;
@ -1047,18 +1047,6 @@ public class SecuredClient implements Client {
return new ValidateQueryRequestBuilder(this, ValidateQueryAction.INSTANCE).setIndices(indices); return new ValidateQueryRequestBuilder(this, ValidateQueryAction.INSTANCE).setIndices(indices);
} }
public ActionFuture<RenderSearchTemplateResponse> renderSearchTemplate(RenderSearchTemplateRequest request) {
return this.execute(RenderSearchTemplateAction.INSTANCE, request);
}
public void renderSearchTemplate(RenderSearchTemplateRequest request, ActionListener<RenderSearchTemplateResponse> listener) {
this.execute(RenderSearchTemplateAction.INSTANCE, request, listener);
}
public RenderSearchTemplateRequestBuilder prepareRenderSearchTemplate() {
return new RenderSearchTemplateRequestBuilder(this, RenderSearchTemplateAction.INSTANCE);
}
public ActionFuture<PutWarmerResponse> putWarmer(PutWarmerRequest request) { public ActionFuture<PutWarmerResponse> putWarmer(PutWarmerRequest request) {
return this.execute(PutWarmerAction.INSTANCE, request); return this.execute(PutWarmerAction.INSTANCE, request);
} }
@ -1370,6 +1358,18 @@ public class SecuredClient implements Client {
public SnapshotsStatusRequestBuilder prepareSnapshotStatus() { public SnapshotsStatusRequestBuilder prepareSnapshotStatus() {
return new SnapshotsStatusRequestBuilder(this, SnapshotsStatusAction.INSTANCE); return new SnapshotsStatusRequestBuilder(this, SnapshotsStatusAction.INSTANCE);
} }
public ActionFuture<RenderSearchTemplateResponse> renderSearchTemplate(RenderSearchTemplateRequest request) {
return this.execute(RenderSearchTemplateAction.INSTANCE, request);
}
public void renderSearchTemplate(RenderSearchTemplateRequest request, ActionListener<RenderSearchTemplateResponse> listener) {
this.execute(RenderSearchTemplateAction.INSTANCE, request, listener);
}
public RenderSearchTemplateRequestBuilder prepareRenderSearchTemplate() {
return new RenderSearchTemplateRequestBuilder(this, RenderSearchTemplateAction.INSTANCE);
}
} }
static class Admin implements AdminClient { static class Admin implements AdminClient {

View File

@ -178,6 +178,10 @@
"type": "string", "type": "string",
"index": "not_analyzed" "index": "not_analyzed"
}, },
"state_uuid": {
"type": "string",
"index": "not_analyzed"
},
"status": { "status": {
"type": "string", "type": "string",
"index": "not_analyzed" "index": "not_analyzed"
@ -248,7 +252,30 @@
"index": "not_analyzed" "index": "not_analyzed"
}, },
"shard": { "shard": {
"type": "object" "properties": {
"state": {
"type": "string",
"index": "not_analyzed"
},
"primary": {
"type": "boolean"
},
"index": {
"type": "string",
"index": "not_analyzed"
},
"relocating_node": {
"type": "string",
"index": "not_analyzed"
},
"shard": {
"type": "long"
},
"node": {
"type": "string",
"index": "not_analyzed"
}
}
} }
} }
} }

View File

@ -17,12 +17,11 @@ import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.core.License; import org.elasticsearch.license.core.License;
import org.elasticsearch.license.plugin.core.LicensesClientService; import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest;
import org.elasticsearch.license.plugin.core.LicensesManagerService; import org.elasticsearch.license.plugin.action.put.PutLicenseRequest;
import org.elasticsearch.license.plugin.core.LicensesService; import org.elasticsearch.license.plugin.core.*;
import org.elasticsearch.marvel.MarvelPlugin; import org.elasticsearch.marvel.MarvelPlugin;
import org.elasticsearch.marvel.agent.settings.MarvelSettings; import org.elasticsearch.marvel.agent.settings.MarvelSettings;
import org.elasticsearch.marvel.license.LicenseService;
import org.elasticsearch.marvel.shield.MarvelShieldIntegration; import org.elasticsearch.marvel.shield.MarvelShieldIntegration;
import org.elasticsearch.marvel.shield.SecuredClient; import org.elasticsearch.marvel.shield.SecuredClient;
import org.elasticsearch.marvel.test.MarvelIntegTestCase; import org.elasticsearch.marvel.test.MarvelIntegTestCase;
@ -88,15 +87,13 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
private static License createTestingLicense(long issueDate, long expiryDate) { private static License createTestingLicense(long issueDate, long expiryDate) {
return License.builder() return License.builder()
.feature(LicenseService.FEATURE_NAME)
.expiryDate(expiryDate) .expiryDate(expiryDate)
.issueDate(issueDate) .issueDate(issueDate)
.issuedTo("AbstractCollectorTestCase") .issuedTo("AbstractCollectorTestCase")
.issuer("test") .issuer("test")
.maxNodes(Integer.MAX_VALUE) .maxNodes(Integer.MAX_VALUE)
.signature("_signature") .signature("_signature")
.type("standard") .type("basic")
.subscriptionType("all_is_good")
.uid(String.valueOf(RandomizedTest.systemPropertyAsInt(SysGlobals.CHILDVM_SYSPROP_JVM_ID, 0)) + System.identityHashCode(AbstractCollectorTestCase.class)) .uid(String.valueOf(RandomizedTest.systemPropertyAsInt(SysGlobals.CHILDVM_SYSPROP_JVM_ID, 0)) + System.identityHashCode(AbstractCollectorTestCase.class))
.build(); .build();
} }
@ -107,7 +104,7 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
final License license = createTestingLicense(issueDate, expiryDate); final License license = createTestingLicense(issueDate, expiryDate);
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) { for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
service.enable(license); service.onChange(license, LicenseState.ENABLED);
} }
for (LicensesManagerServiceForCollectors service : internalCluster().getInstances(LicensesManagerServiceForCollectors.class)) { for (LicensesManagerServiceForCollectors service : internalCluster().getInstances(LicensesManagerServiceForCollectors.class)) {
service.update(license); service.update(license);
@ -120,7 +117,7 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
final License license = createTestingLicense(issueDate, expiryDate); final License license = createTestingLicense(issueDate, expiryDate);
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) { for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
service.disable(license); service.onChange(license, LicenseState.GRACE_PERIOD);
} }
for (LicensesManagerServiceForCollectors service : internalCluster().getInstances(LicensesManagerServiceForCollectors.class)) { for (LicensesManagerServiceForCollectors service : internalCluster().getInstances(LicensesManagerServiceForCollectors.class)) {
service.update(license); service.update(license);
@ -133,7 +130,7 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
final License license = createTestingLicense(issueDate, expiryDate); final License license = createTestingLicense(issueDate, expiryDate);
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) { for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
service.disable(license); service.onChange(license, LicenseState.DISABLED);
} }
for (LicensesManagerServiceForCollectors service : internalCluster().getInstances(LicensesManagerServiceForCollectors.class)) { for (LicensesManagerServiceForCollectors service : internalCluster().getInstances(LicensesManagerServiceForCollectors.class)) {
service.update(license); service.update(license);
@ -146,7 +143,7 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
final License license = createTestingLicense(issueDate, expiryDate); final License license = createTestingLicense(issueDate, expiryDate);
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) { for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
service.disable(license); service.onChange(license, LicenseState.DISABLED);
} }
for (LicensesManagerServiceForCollectors service : internalCluster().getInstances(LicensesManagerServiceForCollectors.class)) { for (LicensesManagerServiceForCollectors service : internalCluster().getInstances(LicensesManagerServiceForCollectors.class)) {
service.update(license); service.update(license);
@ -205,7 +202,7 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
@Override @Override
protected void configure() { protected void configure() {
bind(LicenseServiceForCollectors.class).asEagerSingleton(); bind(LicenseServiceForCollectors.class).asEagerSingleton();
bind(LicensesClientService.class).to(LicenseServiceForCollectors.class); bind(LicenseeRegistry.class).to(LicenseServiceForCollectors.class);
bind(LicensesManagerServiceForCollectors.class).asEagerSingleton(); bind(LicensesManagerServiceForCollectors.class).asEagerSingleton();
bind(LicensesManagerService.class).to(LicensesManagerServiceForCollectors.class); 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 @Inject
public LicenseServiceForCollectors(Settings settings) { public LicenseServiceForCollectors(Settings settings) {
@ -223,19 +220,13 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
} }
@Override @Override
public void register(String feature, TrialLicenseOptions trialLicenseOptions, Collection<ExpirationCallback> expirationCallbacks, AcknowledgementCallback acknowledgementCallback, Listener listener) { public void register(Licensee licensee) {
listeners.add(listener); licensees.add(licensee);
} }
public void enable(License license) { public void onChange(License license, LicenseState state) {
for (Listener listener : listeners) { for (Licensee licensee : licensees) {
listener.onEnabled(license); licensee.onChange(license, state);
}
}
public void disable(License license) {
for (Listener listener : listeners) {
listener.onDisabled(license);
} }
} }
} }
@ -245,21 +236,24 @@ public class AbstractCollectorTestCase extends MarvelIntegTestCase {
private final Map<String, License> licenses = Collections.synchronizedMap(new HashMap<String, License>()); private final Map<String, License> licenses = Collections.synchronizedMap(new HashMap<String, License>());
@Override @Override
public void registerLicenses(LicensesService.PutLicenseRequestHolder requestHolder, ActionListener<LicensesService.LicensesUpdateResponse> listener) { public void registerLicense(PutLicenseRequest request, ActionListener<LicensesService.LicensesUpdateResponse> listener) {
} }
@Override @Override
public void removeLicenses(LicensesService.DeleteLicenseRequestHolder requestHolder, ActionListener<ClusterStateUpdateResponse> listener) { public void removeLicense(DeleteLicenseRequest request, ActionListener<ClusterStateUpdateResponse> listener) {
} }
@Override @Override
public Set<String> enabledFeatures() { public List<String> licenseesWithState(LicenseState state) {
return null; return null;
} }
@Override @Override
public List<License> getLicenses() { public License getLicense() {
return new ArrayList<>(licenses.values()); // 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) { public void update(License license) {

View File

@ -5,134 +5,114 @@
*/ */
package org.elasticsearch.marvel.agent.exporter.http; package org.elasticsearch.marvel.agent.exporter.http;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.QueueDispatcher;
import com.squareup.okhttp.mockwebserver.RecordedRequest;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus; import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.action.admin.indices.recovery.RecoveryResponse; import org.elasticsearch.action.admin.indices.recovery.RecoveryResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.marvel.agent.collector.cluster.ClusterStateCollector; import org.elasticsearch.marvel.agent.collector.cluster.ClusterStateCollector;
import org.elasticsearch.marvel.agent.collector.cluster.ClusterStateMarvelDoc; import org.elasticsearch.marvel.agent.collector.cluster.ClusterStateMarvelDoc;
import org.elasticsearch.marvel.agent.collector.indices.IndexRecoveryCollector; import org.elasticsearch.marvel.agent.collector.indices.IndexRecoveryCollector;
import org.elasticsearch.marvel.agent.collector.indices.IndexRecoveryMarvelDoc; import org.elasticsearch.marvel.agent.collector.indices.IndexRecoveryMarvelDoc;
import org.elasticsearch.marvel.agent.exporter.Exporters; import org.elasticsearch.marvel.agent.exporter.Exporters;
import org.elasticsearch.marvel.agent.exporter.MarvelDoc; import org.elasticsearch.marvel.agent.exporter.MarvelDoc;
import org.elasticsearch.marvel.agent.exporter.MarvelTemplateUtils;
import org.elasticsearch.marvel.agent.settings.MarvelSettings; import org.elasticsearch.marvel.agent.settings.MarvelSettings;
import org.elasticsearch.marvel.test.MarvelIntegTestCase; import org.elasticsearch.marvel.test.MarvelIntegTestCase;
import org.elasticsearch.node.Node; import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.ESIntegTestCase.Scope;
import org.elasticsearch.test.ESIntegTestCase.SuppressLocalMode;
import org.elasticsearch.test.InternalTestCluster;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormat;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.io.IOException;
import java.net.BindException;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.TimeUnit; import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import org.apache.lucene.util.LuceneTestCase.AwaitsFix; @ESIntegTestCase.ClusterScope(scope = Scope.TEST, numDataNodes = 0, numClientNodes = 0, transportClientRatio = 0.0)
// Transport Client instantiation also calls the marvel plugin, which then fails to find modules
@SuppressLocalMode
@ClusterScope(scope = TEST, transportClientRatio = 0.0, numDataNodes = 0, numClientNodes = 0)
@AwaitsFix(bugUrl = "https://github.com/elastic/x-plugins/issues/729")
public class HttpExporterTests extends MarvelIntegTestCase { public class HttpExporterTests extends MarvelIntegTestCase {
final static AtomicLong timeStampGenerator = new AtomicLong(); private int webPort;
private MockWebServer webServer;
@Override
protected boolean enableShield() {
return false;
}
@Before @Before
public void init() throws Exception { public void startWebservice() throws Exception {
startCollection(); for (webPort = 9250; webPort < 9300; webPort++) {
try {
webServer = new MockWebServer();
QueueDispatcher dispatcher = new QueueDispatcher();
dispatcher.setFailFast(true);
webServer.setDispatcher(dispatcher);
webServer.start(webPort);
return;
} catch (BindException be) {
logger.warn("port [{}] was already in use trying next port", webPort);
}
}
throw new ElasticsearchException("unable to find open port between 9200 and 9300");
} }
@After @After
public void cleanup() throws Exception { public void cleanup() throws Exception {
stopCollection(); stopCollection();
} webServer.shutdown();
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return Settings.builder()
.put(super.nodeSettings(nodeOrdinal))
.put(Node.HTTP_ENABLED, true)
.put("shield.enabled", false)
.build();
} }
@Test @Test
public void testSimpleExport() throws Exception { public void testExport() throws Exception {
TargetNode target = TargetNode.start(internalCluster()); enqueueGetClusterVersionResponse(Version.CURRENT);
enqueueResponse(404, "marvel template does not exist");
enqueueResponse(201, "marvel template created");
enqueueResponse(200, "successful bulk request ");
Settings.Builder builder = Settings.builder() Settings.Builder builder = Settings.builder()
.put(MarvelSettings.INTERVAL, "-1")
.put("marvel.agent.exporters._http.type", "http") .put("marvel.agent.exporters._http.type", "http")
.put("marvel.agent.exporters._http.host", target.httpAddress); .put("marvel.agent.exporters._http.host", webServer.getHostName() + ":" + webServer.getPort())
.put("marvel.agent.exporters._http.connection.keep_alive", false);
String agentNode = internalCluster().startNode(builder); String agentNode = internalCluster().startNode(builder);
ensureGreen();
HttpExporter exporter = getExporter(agentNode); HttpExporter exporter = getExporter(agentNode);
MarvelDoc doc = newRandomMarvelDoc(); MarvelDoc doc = newRandomMarvelDoc();
exporter.export(Collections.singletonList(doc)); exporter.export(Collections.singletonList(doc));
flush(); assertThat(webServer.getRequestCount(), greaterThanOrEqualTo(4));
refresh();
SearchResponse response = client().prepareSearch(".marvel-es-*").setTypes(doc.type()).get(); RecordedRequest recordedRequest = webServer.takeRequest();
assertThat(response, notNullValue()); assertThat(recordedRequest.getMethod(), equalTo("GET"));
assertThat(response.getHits().totalHits(), is(1L)); assertThat(recordedRequest.getPath(), equalTo("/"));
}
@Test recordedRequest = webServer.takeRequest();
public void testTemplateAdditionDespiteOfLateClusterForming() throws Exception { assertThat(recordedRequest.getMethod(), equalTo("GET"));
assertThat(recordedRequest.getPath(), equalTo("/_template/marvel"));
TargetNode target = TargetNode.start(internalCluster()); recordedRequest = webServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("PUT"));
assertThat(recordedRequest.getPath(), equalTo("/_template/marvel"));
assertThat(recordedRequest.getBody().readByteArray(), equalTo(MarvelTemplateUtils.loadDefaultTemplate()));
Settings.Builder builder = Settings.builder() recordedRequest = webServer.takeRequest();
.put(MarvelSettings.STARTUP_DELAY, "200m") assertThat(recordedRequest.getMethod(), equalTo("POST"));
.put(Node.HTTP_ENABLED, true) assertThat(recordedRequest.getPath(), equalTo("/_bulk"));
.put("discovery.type", "zen")
.put("discovery.zen.ping_timeout", "1s")
.put("discovery.initial_state_timeout", "100ms")
.put("discovery.zen.minimum_master_nodes", 2)
.put("marvel.agent.exporters._http.type", "http")
.put("marvel.agent.exporters._http.host", target.httpAddress)
.put("marvel.agent.exporters._http." + HttpExporter.BULK_TIMEOUT_SETTING, "1s")
.put("marvel.agent.exporters._http." + HttpExporter.TEMPLATE_CHECK_TIMEOUT_SETTING, "1s");
String nodeName = internalCluster().startNode(builder);
HttpExporter exporter = getExporter(nodeName);
logger.info("exporting events while there is no cluster");
exporter.export(Collections.singletonList(newRandomMarvelDoc()));
logger.info("bringing up a second node");
internalCluster().startNode(builder);
ensureGreen();
logger.info("exporting a second event");
exporter.export(Collections.singletonList(newRandomMarvelDoc()));
logger.info("verifying that template has been created");
assertMarvelTemplateInstalled();
} }
@Test @Test
public void testDynamicHostChange() { public void testDynamicHostChange() {
// disable exporting to be able to use non valid hosts // disable exporting to be able to use non valid hosts
Settings.Builder builder = Settings.builder() Settings.Builder builder = Settings.builder()
.put(MarvelSettings.INTERVAL, "-1") .put(MarvelSettings.INTERVAL, "-1")
@ -159,144 +139,222 @@ public class HttpExporterTests extends MarvelIntegTestCase {
@Test @Test
public void testHostChangeReChecksTemplate() throws Exception { public void testHostChangeReChecksTemplate() throws Exception {
TargetNode targetNode = TargetNode.start(internalCluster());
Settings.Builder builder = Settings.builder() Settings.Builder builder = Settings.builder()
.put(MarvelSettings.STARTUP_DELAY, "200m") .put(MarvelSettings.INTERVAL, "-1")
.put("marvel.agent.exporters._http.type", "http") .put("marvel.agent.exporters._http.type", "http")
.put("marvel.agent.exporters._http.host", targetNode.httpAddress); .put("marvel.agent.exporters._http.host", webServer.getHostName() + ":" + webServer.getPort())
.put("marvel.agent.exporters._http.connection.keep_alive", false);
logger.info("--> starting node");
enqueueGetClusterVersionResponse(Version.CURRENT);
enqueueResponse(404, "marvel template does not exist");
enqueueResponse(201, "marvel template created");
enqueueResponse(200, "successful bulk request ");
String agentNode = internalCluster().startNode(builder); String agentNode = internalCluster().startNode(builder);
logger.info("--> exporting data");
HttpExporter exporter = getExporter(agentNode); HttpExporter exporter = getExporter(agentNode);
logger.info("exporting an event");
exporter.export(Collections.singletonList(newRandomMarvelDoc())); exporter.export(Collections.singletonList(newRandomMarvelDoc()));
logger.info("removing the marvel template"); assertThat(webServer.getRequestCount(), greaterThanOrEqualTo(4));
assertAcked(client().admin().indices().prepareDeleteTemplate("marvel").get());
assertMarvelTemplateMissing();
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings( RecordedRequest recordedRequest = webServer.takeRequest();
Settings.builder().putArray("marvel.agent.exporters._http.host", exporter.hosts)).get()); assertThat(recordedRequest.getMethod(), equalTo("GET"));
assertThat(recordedRequest.getPath(), equalTo("/"));
// a new exporter is created on update, so we need to re-fetch it recordedRequest = webServer.takeRequest();
exporter = getExporter(agentNode); assertThat(recordedRequest.getMethod(), equalTo("GET"));
assertThat(recordedRequest.getPath(), equalTo("/_template/marvel"));
logger.info("exporting a second event"); recordedRequest = webServer.takeRequest();
exporter.export(Collections.singletonList(newRandomMarvelDoc())); assertThat(recordedRequest.getMethod(), equalTo("PUT"));
assertThat(recordedRequest.getPath(), equalTo("/_template/marvel"));
assertThat(recordedRequest.getBody().readByteArray(), equalTo(MarvelTemplateUtils.loadDefaultTemplate()));
logger.info("verifying that template has been created"); recordedRequest = webServer.takeRequest();
assertMarvelTemplateInstalled(); assertThat(recordedRequest.getMethod(), equalTo("POST"));
} assertThat(recordedRequest.getPath(), equalTo("/_bulk"));
@Test logger.info("--> setting up another web server");
public void testHostFailureChecksTemplate() throws Exception { MockWebServer secondWebServer = null;
int secondWebPort;
TargetNode target0 = TargetNode.start(internalCluster()); try {
assertThat(target0.name, is(internalCluster().getMasterName())); for (secondWebPort = 9250; secondWebPort < 9300; secondWebPort++) {
TargetNode target1 = TargetNode.start(internalCluster());
// lets start node0 & node1 first, such that node0 will be the master (it's first to start)
final String node0 = internalCluster().startNode(Settings.builder()
.put(MarvelSettings.STARTUP_DELAY, "200m")
.put("marvel.agent.exporters._http.type", "http")
.putArray("marvel.agent.exporters._http.host", target0.httpAddress, target1.httpAddress));
HttpExporter exporter = getExporter(node0);
logger.info("--> exporting events to have new settings take effect");
exporter.export(Collections.singletonList(newRandomMarvelDoc()));
logger.info("verifying that template has been created");
assertMarvelTemplateInstalled();
logger.info("--> removing the marvel template");
assertAcked(client().admin().indices().prepareDeleteTemplate("marvel").get());
assertMarvelTemplateMissing();
logger.info("--> shutting down target0");
assertThat(target0.name, is(internalCluster().getMasterName())); // just to be sure it's still the master
internalCluster().stopCurrentMasterNode();
// we use assert busy node because url caching may cause the node failure to be only detected while sending the event
assertBusy(new Runnable() {
@Override
public void run() {
try { try {
logger.info("--> exporting events from node0"); secondWebServer = new MockWebServer();
getExporter(node0).export(Collections.singletonList(newRandomMarvelDoc())); QueueDispatcher dispatcher = new QueueDispatcher();
} catch (Exception e) { dispatcher.setFailFast(true);
e.printStackTrace(); secondWebServer.setDispatcher(dispatcher);
fail("failed to export event from node0"); secondWebServer.start(secondWebPort);
break;
} catch (BindException be) {
logger.warn("port [{}] was already in use trying next port", secondWebPort);
} }
logger.debug("--> checking for template");
assertMarvelTemplateInstalled();
logger.debug("--> template exists");
} }
}, 30, TimeUnit.SECONDS);
assertNotNull("Unable to start the second mock web server", secondWebServer);
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(
Settings.builder().putArray("marvel.agent.exporters._http.host", secondWebServer.getHostName() + ":" + secondWebServer.getPort())).get());
// a new exporter is created on update, so we need to re-fetch it
exporter = getExporter(agentNode);
enqueueGetClusterVersionResponse(secondWebServer, Version.CURRENT);
enqueueResponse(secondWebServer, 404, "marvel template does not exist");
enqueueResponse(secondWebServer, 201, "marvel template created");
enqueueResponse(secondWebServer, 200, "successful bulk request ");
logger.info("--> exporting a second event");
exporter.export(Collections.singletonList(newRandomMarvelDoc()));
assertThat(secondWebServer.getRequestCount(), greaterThanOrEqualTo(4));
recordedRequest = secondWebServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("GET"));
assertThat(recordedRequest.getPath(), equalTo("/"));
recordedRequest = secondWebServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("GET"));
assertThat(recordedRequest.getPath(), equalTo("/_template/marvel"));
recordedRequest = secondWebServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("PUT"));
assertThat(recordedRequest.getPath(), equalTo("/_template/marvel"));
assertThat(recordedRequest.getBody().readByteArray(), equalTo(MarvelTemplateUtils.loadDefaultTemplate()));
recordedRequest = secondWebServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("POST"));
assertThat(recordedRequest.getPath(), equalTo("/_bulk"));
} finally {
if (secondWebServer != null) {
secondWebServer.shutdown();
}
}
} }
@Test @Test
public void testDynamicIndexFormatChange() throws Exception { public void testDynamicIndexFormatChange() throws Exception {
TargetNode targetNode = TargetNode.start(internalCluster());
Settings.Builder builder = Settings.builder() Settings.Builder builder = Settings.builder()
.put(MarvelSettings.STARTUP_DELAY, "200m") .put(MarvelSettings.INTERVAL, "-1")
.put("marvel.agent.exporters._http.type", "http") .put("marvel.agent.exporters._http.type", "http")
.put("marvel.agent.exporters._http.host", targetNode.httpAddress); .put("marvel.agent.exporters._http.host", webServer.getHostName() + ":" + webServer.getPort())
.put("marvel.agent.exporters._http.connection.keep_alive", false);
String agentNode = internalCluster().startNode(builder); String agentNode = internalCluster().startNode(builder);
logger.info("exporting a first event"); logger.info("--> exporting a first event");
enqueueGetClusterVersionResponse(Version.CURRENT);
enqueueResponse(404, "marvel template does not exist");
enqueueResponse(201, "marvel template created");
enqueueResponse(200, "successful bulk request ");
HttpExporter exporter = getExporter(agentNode); HttpExporter exporter = getExporter(agentNode);
MarvelDoc doc = newRandomMarvelDoc(); MarvelDoc doc = newRandomMarvelDoc();
exporter.export(Collections.singletonList(doc)); exporter.export(Collections.singletonList(doc));
assertThat(webServer.getRequestCount(), greaterThanOrEqualTo(4));
RecordedRequest recordedRequest = webServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("GET"));
assertThat(recordedRequest.getPath(), equalTo("/"));
recordedRequest = webServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("GET"));
assertThat(recordedRequest.getPath(), equalTo("/_template/marvel"));
recordedRequest = webServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("PUT"));
assertThat(recordedRequest.getPath(), equalTo("/_template/marvel"));
assertThat(recordedRequest.getBody().readByteArray(), equalTo(MarvelTemplateUtils.loadDefaultTemplate()));
recordedRequest = webServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("POST"));
assertThat(recordedRequest.getPath(), equalTo("/_bulk"));
String indexName = exporter.indexNameResolver().resolve(doc); String indexName = exporter.indexNameResolver().resolve(doc);
logger.info("checks that the index [{}] is created", indexName); logger.info("--> checks that the document in the bulk request is indexed in [{}]", indexName);
assertTrue(client().admin().indices().prepareExists(indexName).get().isExists());
byte[] bytes = recordedRequest.getBody().readByteArray();
Map<String, Object> data = XContentHelper.convertToMap(new BytesArray(bytes), false).v2();
Map<String, Object> index = (Map<String, Object>) data.get("index");
assertThat(index.get("_index"), equalTo(indexName));
String newTimeFormat = randomFrom("YY", "YYYY", "YYYY.MM", "YYYY-MM", "MM.YYYY", "MM"); String newTimeFormat = randomFrom("YY", "YYYY", "YYYY.MM", "YYYY-MM", "MM.YYYY", "MM");
logger.info("updating index time format setting to {}", newTimeFormat); logger.info("--> updating index time format setting to {}", newTimeFormat);
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(Settings.builder() assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(Settings.builder()
.put("marvel.agent.exporters._http.index.name.time_format", newTimeFormat))); .put("marvel.agent.exporters._http.index.name.time_format", newTimeFormat)));
exporter = getExporter(agentNode);
logger.info("exporting a second event"); logger.info("--> exporting a second event");
enqueueGetClusterVersionResponse(Version.CURRENT);
enqueueResponse(404, "marvel template does not exist");
enqueueResponse(201, "marvel template created");
enqueueResponse(200, "successful bulk request ");
doc = newRandomMarvelDoc(); doc = newRandomMarvelDoc();
exporter = getExporter(agentNode);
exporter.export(Collections.singletonList(doc)); exporter.export(Collections.singletonList(doc));
String expectedMarvelIndex = MarvelSettings.MARVEL_INDICES_PREFIX String expectedMarvelIndex = MarvelSettings.MARVEL_INDICES_PREFIX
+ DateTimeFormat.forPattern(newTimeFormat).withZoneUTC().print(doc.timestamp()); + DateTimeFormat.forPattern(newTimeFormat).withZoneUTC().print(doc.timestamp());
logger.info("checks that the index [{}] is created", expectedMarvelIndex); assertThat(webServer.getRequestCount(), greaterThanOrEqualTo(4));
assertTrue(client().admin().indices().prepareExists(expectedMarvelIndex).get().isExists());
logger.info("verifying that template has been created"); recordedRequest = webServer.takeRequest();
assertMarvelTemplateInstalled(); assertThat(recordedRequest.getMethod(), equalTo("GET"));
assertThat(recordedRequest.getPath(), equalTo("/"));
recordedRequest = webServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("GET"));
assertThat(recordedRequest.getPath(), equalTo("/_template/marvel"));
recordedRequest = webServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("PUT"));
assertThat(recordedRequest.getPath(), equalTo("/_template/marvel"));
assertThat(recordedRequest.getBody().readByteArray(), equalTo(MarvelTemplateUtils.loadDefaultTemplate()));
recordedRequest = webServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("POST"));
assertThat(recordedRequest.getPath(), equalTo("/_bulk"));
logger.info("--> checks that the document in the bulk request is indexed in [{}]", expectedMarvelIndex);
bytes = recordedRequest.getBody().readByteArray();
data = XContentHelper.convertToMap(new BytesArray(bytes), false).v2();
index = (Map<String, Object>) data.get("index");
assertThat(index.get("_index"), equalTo(expectedMarvelIndex));
} }
@Test @Test
public void testLoadRemoteClusterVersion() { public void testLoadRemoteClusterVersion() throws IOException {
final String host = webServer.getHostName() + ":" + webServer.getPort();
TargetNode targetNode = TargetNode.start(internalCluster());
Settings.Builder builder = Settings.builder() Settings.Builder builder = Settings.builder()
.put(MarvelSettings.STARTUP_DELAY, "200m") .put(MarvelSettings.INTERVAL, "-1")
.put("marvel.agent.exporters._http.type", "http") .put("marvel.agent.exporters._http.type", "http")
.put("marvel.agent.exporters._http.host", targetNode.httpAddress); .put("marvel.agent.exporters._http.host", host)
.put("marvel.agent.exporters._http.connection.keep_alive", false);
String agentNode = internalCluster().startNode(builder); String agentNode = internalCluster().startNode(builder);
HttpExporter exporter = getExporter(agentNode); HttpExporter exporter = getExporter(agentNode);
logger.info("--> loading remote cluster version"); enqueueGetClusterVersionResponse(Version.CURRENT);
Version resolved = exporter.loadRemoteClusterVersion(targetNode.httpAddress); Version resolved = exporter.loadRemoteClusterVersion(host);
assertTrue(resolved.equals(Version.CURRENT)); assertTrue(resolved.equals(Version.CURRENT));
final Version expected = randomFrom(Version.CURRENT, Version.V_0_18_0, Version.V_1_1_0, Version.V_1_2_5, Version.V_1_4_5, Version.V_1_6_0);
enqueueGetClusterVersionResponse(expected);
resolved = exporter.loadRemoteClusterVersion(host);
assertTrue(resolved.equals(expected));
} }
private HttpExporter getExporter(String nodeName) { private HttpExporter getExporter(String nodeName) {
@ -307,29 +365,26 @@ public class HttpExporterTests extends MarvelIntegTestCase {
private MarvelDoc newRandomMarvelDoc() { private MarvelDoc newRandomMarvelDoc() {
if (randomBoolean()) { if (randomBoolean()) {
return new IndexRecoveryMarvelDoc(internalCluster().getClusterName(), return new IndexRecoveryMarvelDoc(internalCluster().getClusterName(),
IndexRecoveryCollector.TYPE, timeStampGenerator.incrementAndGet(), new RecoveryResponse()); IndexRecoveryCollector.TYPE, System.currentTimeMillis(), new RecoveryResponse());
} else { } else {
return new ClusterStateMarvelDoc(internalCluster().getClusterName(), return new ClusterStateMarvelDoc(internalCluster().getClusterName(),
ClusterStateCollector.TYPE, timeStampGenerator.incrementAndGet(), ClusterState.PROTO, ClusterHealthStatus.GREEN); ClusterStateCollector.TYPE, System.currentTimeMillis(), ClusterState.PROTO, ClusterHealthStatus.GREEN);
} }
} }
static class TargetNode { private void enqueueGetClusterVersionResponse(Version v) throws IOException {
enqueueGetClusterVersionResponse(webServer, v);
}
private final String name; private void enqueueGetClusterVersionResponse(MockWebServer mockWebServer, Version v) throws IOException {
private final TransportAddress address; mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(jsonBuilder().startObject().startObject("version").field("number", v.number()).endObject().endObject().bytes().toUtf8()));
private final String httpAddress; }
private final Client client;
private TargetNode(InternalTestCluster cluster) { private void enqueueResponse(int responseCode, String body) throws IOException {
name = cluster.startNode(Settings.builder().put(Node.HTTP_ENABLED, true)); enqueueResponse(webServer, responseCode, body);
address = cluster.getInstance(HttpServerTransport.class, name).boundAddress().publishAddress(); }
httpAddress = address.getHost() + ":" + address.getPort();
this.client = cluster.client(name);
}
static TargetNode start(InternalTestCluster cluster) { private void enqueueResponse(MockWebServer mockWebServer, int responseCode, String body) throws IOException {
return new TargetNode(cluster); mockWebServer.enqueue(new MockResponse().setResponseCode(responseCode).setBody(body));
}
} }
} }

View File

@ -93,7 +93,6 @@ public class ClusterInfoIT extends MarvelIntegTestCase {
String recalculated = ClusterInfoRenderer.hash(status, uid, type, String.valueOf(expiryDate), clusterUUID); String recalculated = ClusterInfoRenderer.hash(status, uid, type, String.valueOf(expiryDate), clusterUUID);
assertThat(hkey, equalTo(recalculated)); 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.ISSUER.underscore().toString()), not(isEmptyOrNullString()));
assertThat((String) license.get(ClusterInfoRenderer.Fields.ISSUED_TO.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)); assertThat((Long) license.get(ClusterInfoRenderer.Fields.ISSUE_DATE_IN_MILLIS.underscore().toString()), greaterThan(0L));

View File

@ -6,27 +6,37 @@
package org.elasticsearch.marvel.agent.renderer.shards; package org.elasticsearch.marvel.agent.renderer.shards;
import org.apache.lucene.util.LuceneTestCase.AwaitsFix; import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.marvel.agent.collector.shards.ShardsCollector; import org.elasticsearch.marvel.agent.collector.shards.ShardsCollector;
import org.elasticsearch.marvel.agent.settings.MarvelSettings; import org.elasticsearch.marvel.agent.settings.MarvelSettings;
import org.elasticsearch.marvel.test.MarvelIntegTestCase; import org.elasticsearch.marvel.test.MarvelIntegTestCase;
import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.junit.Test; import org.junit.Test;
import java.util.Map; import java.util.Map;
import static org.hamcrest.Matchers.greaterThan; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.*;
@AwaitsFix(bugUrl="https://github.com/elastic/x-plugins/issues/729") @AwaitsFix(bugUrl="https://github.com/elastic/x-plugins/issues/729")
public class ShardsIT extends MarvelIntegTestCase { public class ShardsIT extends MarvelIntegTestCase {
private static final String INDEX_PREFIX = "test-shards-";
@Override @Override
protected Settings nodeSettings(int nodeOrdinal) { protected Settings nodeSettings(int nodeOrdinal) {
return Settings.builder() return Settings.builder()
.put(super.nodeSettings(nodeOrdinal)) .put(super.nodeSettings(nodeOrdinal))
.put(MarvelSettings.INTERVAL, "3s") .put(MarvelSettings.INTERVAL, "3s")
.put(MarvelSettings.COLLECTORS, ShardsCollector.NAME) .put(MarvelSettings.COLLECTORS, ShardsCollector.NAME)
.put(MarvelSettings.INDICES, INDEX_PREFIX + "*")
.build(); .build();
} }
@ -34,7 +44,7 @@ public class ShardsIT extends MarvelIntegTestCase {
public void testShards() throws Exception { public void testShards() throws Exception {
logger.debug("--> creating some indices so that shards collector reports data"); logger.debug("--> creating some indices so that shards collector reports data");
for (int i = 0; i < randomIntBetween(1, 5); i++) { for (int i = 0; i < randomIntBetween(1, 5); i++) {
client().prepareIndex("test-" + i, "foo").setRefresh(true).setSource("field1", "value1").get(); client().prepareIndex(INDEX_PREFIX + i, "foo").setRefresh(true).setSource("field1", "value1").get();
} }
awaitMarvelDocsCount(greaterThan(0L), ShardsCollector.TYPE); awaitMarvelDocsCount(greaterThan(0L), ShardsCollector.TYPE);
@ -55,4 +65,34 @@ public class ShardsIT extends MarvelIntegTestCase {
logger.debug("--> shards successfully collected"); logger.debug("--> shards successfully collected");
} }
/**
* This test uses a terms aggregation to check that the "not_analyzed"
* fields of the "shards" document type are indeed not analyzed
*/
@Test
public void testNotAnalyzedFields() throws Exception {
final String indexName = INDEX_PREFIX + randomInt();
assertAcked(prepareCreate(indexName).setSettings(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1, IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0));
awaitMarvelDocsCount(greaterThan(0L), ShardsCollector.TYPE);
SearchRequestBuilder searchRequestBuilder = client()
.prepareSearch()
.setTypes(ShardsCollector.TYPE)
.setQuery(QueryBuilders.termQuery("shard.index", indexName));
String[] notAnalyzedFields = {"state_uuid", "shard.state", "shard.index", "shard.node"};
for (String field : notAnalyzedFields) {
searchRequestBuilder.addAggregation(AggregationBuilders.terms("agg_" + field.replace('.', '_')).field(field));
}
SearchResponse response = searchRequestBuilder.get();
assertThat(response.getHits().getTotalHits(), greaterThanOrEqualTo(1L));
for (Aggregation aggregation : response.getAggregations()) {
assertThat(aggregation, instanceOf(StringTerms.class));
assertThat(((StringTerms) aggregation).getBuckets().size(), equalTo(1));
}
}
} }

View File

@ -13,8 +13,9 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.Module; import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.core.License; 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.LicensesService; import org.elasticsearch.license.plugin.core.Licensee;
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
import org.elasticsearch.marvel.MarvelPlugin; import org.elasticsearch.marvel.MarvelPlugin;
import org.elasticsearch.marvel.mode.Mode; import org.elasticsearch.marvel.mode.Mode;
import org.elasticsearch.marvel.test.MarvelIntegTestCase; import org.elasticsearch.marvel.test.MarvelIntegTestCase;
@ -106,26 +107,24 @@ public class LicenseIntegrationTests extends MarvelIntegTestCase {
@Override @Override
protected void configure() { protected void configure() {
bind(MockLicenseService.class).asEagerSingleton(); 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() static final License DUMMY_LICENSE = License.builder()
.feature(LicenseService.FEATURE_NAME)
.expiryDate(System.currentTimeMillis()) .expiryDate(System.currentTimeMillis())
.issueDate(System.currentTimeMillis()) .issueDate(System.currentTimeMillis())
.issuedTo("LicensingTests") .issuedTo("LicensingTests")
.issuer("test") .issuer("test")
.maxNodes(Integer.MAX_VALUE) .maxNodes(Integer.MAX_VALUE)
.signature("_signature") .signature("_signature")
.type("standard") .type("basic")
.subscriptionType("all_is_good")
.uid(String.valueOf(RandomizedTest.systemPropertyAsInt(SysGlobals.CHILDVM_SYSPROP_JVM_ID, 0)) + System.identityHashCode(LicenseIntegrationTests.class)) .uid(String.valueOf(RandomizedTest.systemPropertyAsInt(SysGlobals.CHILDVM_SYSPROP_JVM_ID, 0)) + System.identityHashCode(LicenseIntegrationTests.class))
.build(); .build();
private final List<Listener> listeners = new ArrayList<>(); private final List<Licensee> licensees = new ArrayList<>();
@Inject @Inject
public MockLicenseService(Settings settings) { public MockLicenseService(Settings settings) {
@ -134,22 +133,20 @@ public class LicenseIntegrationTests extends MarvelIntegTestCase {
} }
@Override @Override
public void register(String s, LicensesService.TrialLicenseOptions trialLicenseOptions, Collection<LicensesService.ExpirationCallback> collection, AcknowledgementCallback acknowledgementCallback, Listener listener) { public void register(Licensee licensee) {
listeners.add(listener); licensees.add(licensee);
enable(); enable();
} }
public void enable() { public void enable() {
// enabled all listeners (incl. shield) for (Licensee licensee : licensees) {
for (Listener listener : listeners) { licensee.onChange(DUMMY_LICENSE, randomBoolean() ? LicenseState.GRACE_PERIOD : LicenseState.ENABLED);
listener.onEnabled(DUMMY_LICENSE);
} }
} }
public void disable() { public void disable() {
// only disable watcher listener (we need shield to work) for (Licensee licensee : licensees) {
for (Listener listener : listeners) { licensee.onChange(DUMMY_LICENSE, LicenseState.DISABLED);
listener.onDisabled(DUMMY_LICENSE);
} }
} }
} }

View File

@ -4,6 +4,7 @@
"cluster_state": { "cluster_state": {
"status": "yellow", "status": "yellow",
"version": 14, "version": 14,
"state_uuid": "lj0hNoO9QaeNa1eR2ukktQ",
"master_node": "__node_id__", "master_node": "__node_id__",
"nodes": { "nodes": {
"__node_id__": { "__node_id__": {

View File

@ -23,7 +23,7 @@
<skip.unit.tests>true</skip.unit.tests> <skip.unit.tests>true</skip.unit.tests>
<elasticsearch.integ.antfile>${project.basedir}/integration-tests.xml</elasticsearch.integ.antfile> <elasticsearch.integ.antfile>${project.basedir}/integration-tests.xml</elasticsearch.integ.antfile>
<tests.rest.load_packaged>true</tests.rest.load_packaged> <tests.rest.load_packaged>true</tests.rest.load_packaged>
<tests.rest.blacklist>indices.get/10_basic/*allow_no_indices*,cat.count/10_basic/Test cat count output,cat.aliases/10_basic/Empty cluster,indices.segments/10_basic/no segments test,indices.clear_cache/10_basic/clear_cache test,indices.status/10_basic/Indices status test,cat.indices/10_basic/Test cat indices output,cat.recovery/10_basic/Test cat recovery output,cat.shards/10_basic/Test cat shards output,termvector/20_issue7121/*,index/10_with_id/Index with ID,indices.get_alias/20_emtpy/*,cat.segments/10_basic/Test cat segments output,indices.put_settings/10_basic/Test indices settings allow_no_indices,indices.put_settings/10_basic/Test indices settings ignore_unavailable,indices.refresh/10_basic/Indices refresh test no-match wildcard,indices.stats/10_index/Index - star*,indices.recovery/10_basic/Indices recovery test*,template/30_render_search_template/*,indices.shard_stores/10_basic/no indices test,cat.nodeattrs/10_basic/Test cat nodes attrs output</tests.rest.blacklist> <tests.rest.blacklist>indices.get/10_basic/*allow_no_indices*,cat.count/10_basic/Test cat count output,cat.aliases/10_basic/Empty cluster,indices.segments/10_basic/no segments test,indices.clear_cache/10_basic/clear_cache test,indices.status/10_basic/Indices status test,cat.indices/10_basic/Test cat indices output,cat.recovery/10_basic/Test cat recovery output,cat.shards/10_basic/Test cat shards output,termvector/20_issue7121/*,index/10_with_id/Index with ID,indices.get_alias/20_emtpy/*,cat.segments/10_basic/Test cat segments output,indices.put_settings/10_basic/Test indices settings allow_no_indices,indices.put_settings/10_basic/Test indices settings ignore_unavailable,indices.refresh/10_basic/Indices refresh test no-match wildcard,indices.stats/10_index/Index - star*,indices.recovery/10_basic/Indices recovery test*,indices.shard_stores/10_basic/no indices test,cat.nodeattrs/10_basic/Test cat nodes attrs output</tests.rest.blacklist>
<xplugins.list>license,shield</xplugins.list> <xplugins.list>license,shield</xplugins.list>
</properties> </properties>

View File

@ -33,14 +33,8 @@ NOTE: If you are using a <<deb-rpm-install, DEB/RPM distribution>> of Elasticsea
bin/elasticsearch bin/elasticsearch
---------------------------------------------------------- ----------------------------------------------------------
. To verify that Shield is up and running, use the `_shield` API to get the Shield version: . To verify that Shield is up and running, check the startup log entries. When Shield is operating
+ normally, the log indicates that the network transports are using Shield:
[source,shell]
----------------------------------------------------------
curl -u es_admin -XGET 'http://localhost:9200/_shield'
----------------------------------------------------------
+
You can also check the startup log entries. When Shield is operating normally, the log indicates that the network transports are using Shield:
+ +
[source,shell] [source,shell]
---------------- ----------------
@ -49,7 +43,6 @@ You can also check the startup log entries. When Shield is operating normally, t
[2014-10-09 13:47:38,842][INFO ][http ] [Ezekiel Stane] Using [org.elasticsearch.shield.transport.netty.ShieldNettyHttpServerTransport] as http transport, overridden by [shield] [2014-10-09 13:47:38,842][INFO ][http ] [Ezekiel Stane] Using [org.elasticsearch.shield.transport.netty.ShieldNettyHttpServerTransport] as http transport, overridden by [shield]
---------------- ----------------
Now you're ready to secure your cluster! Here are a few things Now you're ready to secure your cluster! Here are a few things
you might want to do to start with: you might want to do to start with:

View File

@ -22,7 +22,7 @@ To install or update the license use the following REST API:
[source,shell] [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: Where:
@ -42,7 +42,7 @@ You can list all currently installed licenses by executing the following REST AP
[source,shell] [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 The response of this command will be a JSON listing all available licenses. In the case of Shield, the following

View File

@ -67,6 +67,7 @@ Elasticsearch. This only applies to publicly available indices and cluster actio
[float] [float]
===== Cluster actions privileges ===== Cluster actions privileges
* `cluster:admin/render/template/search`
* `cluster:admin/repository/delete` * `cluster:admin/repository/delete`
* `cluster:admin/repository/get` * `cluster:admin/repository/get`
* `cluster:admin/repository/put` * `cluster:admin/repository/put`
@ -118,7 +119,6 @@ NOTE: While indices template actions typically relate to indices, they are categ
* `indices:admin/open` * `indices:admin/open`
* `indices:admin/optimize` * `indices:admin/optimize`
* `indices:admin/refresh` * `indices:admin/refresh`
* `indices:admin/render/template/search`
* `indices:admin/settings/update` * `indices:admin/settings/update`
* `indices:admin/shards/search_shards` * `indices:admin/shards/search_shards`
* `indices:admin/template/delete` * `indices:admin/template/delete`

View File

@ -42,6 +42,18 @@ version of Shield. We recommend copying the changes listed below to your `roles.
[[changelist]] [[changelist]]
=== Change List === Change List
[float]
==== 2.0.0-rc1
.enhancements
* Added a caching interface that can be used by <<custom-realms, custom authentication realms>> to integrate with the <<cache-eviction-api, cache eviction api>>.
.bug fixes
* <<configuring-auditing, Auditing>> now captures requests from nodes using a different system key as tampered requests.
* The <<audit-index, index output for auditing>> stores the type of request when available.
* <<ip-filtering, IP filtering>> could have allowed a user to block all access to their node if the system was incorrectly configured, but now explicitly
allows connections from all addresses that the node is bound to so that connections coming from the node's host will not be blocked.
[float] [float]
==== 2.0.0-beta2 ==== 2.0.0-beta2

View File

@ -16,6 +16,7 @@ import org.elasticsearch.action.support.ActionFilterChain;
import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.license.plugin.core.LicenseUtils; import org.elasticsearch.license.plugin.core.LicenseUtils;
import org.elasticsearch.shield.User; import org.elasticsearch.shield.User;
import org.elasticsearch.shield.action.interceptor.RequestInterceptor; import org.elasticsearch.shield.action.interceptor.RequestInterceptor;
@ -62,13 +63,8 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
this.actionMapper = actionMapper; this.actionMapper = actionMapper;
licenseEventsNotifier.register(new LicenseEventsNotifier.Listener() { licenseEventsNotifier.register(new LicenseEventsNotifier.Listener() {
@Override @Override
public void enabled() { public void notify(LicenseState state) {
licenseEnabled = true; licenseEnabled = state != LicenseState.DISABLED;
}
@Override
public void disabled() {
licenseEnabled = false;
} }
}); });
this.requestInterceptors = requestInterceptors; this.requestInterceptors = requestInterceptors;

View File

@ -5,6 +5,8 @@
*/ */
package org.elasticsearch.shield.license; package org.elasticsearch.shield.license;
import org.elasticsearch.license.plugin.core.LicenseState;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -26,22 +28,14 @@ public class LicenseEventsNotifier {
listeners.add(listener); listeners.add(listener);
} }
protected void notifyEnabled() { protected void notify(LicenseState state) {
for (Listener listener : listeners) { for (Listener listener : listeners) {
listener.enabled(); listener.notify(state);
}
}
protected void notifyDisabled() {
for (Listener listener : listeners) {
listener.disabled();
} }
} }
public static interface Listener { public static interface Listener {
void enabled(); void notify(LicenseState state);
void disabled();
} }
} }

View File

@ -6,99 +6,95 @@
package org.elasticsearch.shield.license; package org.elasticsearch.shield.license;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject; 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.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.core.License; 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 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; public static final String FEATURE_NAME = ShieldPlugin.NAME;
private static final LicensesClientService.TrialLicenseOptions TRIAL_LICENSE_OPTIONS = private final LicenseeRegistry licenseeRegistry;
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 LicenseEventsNotifier notifier; 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 @Inject
public LicenseService(Settings settings, LicensesClientService licensesClientService, LicenseEventsNotifier notifier) { public LicenseService(Settings settings, LicenseeRegistry licenseeRegistry, LicenseEventsNotifier notifier) {
super(settings); super(settings);
this.licensesClientService = licensesClientService; this.licenseeRegistry = licenseeRegistry;
this.notifier = notifier; this.notifier = notifier;
this.expirationLoggers = Arrays.asList( }
new LicensesClientService.ExpirationCallback.Pre(days(7), days(30), days(1)) {
@Override @Override
public void on(License license, LicensesClientService.ExpirationStatus status) { public String id() {
logger.error("\n" + return FEATURE_NAME;
"#\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" + @Override
"# have a new license, please update it. Otherwise, please reach out to your support contact.\n" + public String[] expirationMessages() {
"#", DATE_FORMATTER.printer().print(license.expiryDate())); return new String[] {
} "Cluster health, cluster stats and indices stats operations are blocked",
}, "All data operations (read and write) continue to work"
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();
}
}; };
} }
public synchronized boolean enabled() { @Override
return enabled; 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 @Override
protected void doStart() throws ElasticsearchException { protected void doStart() throws ElasticsearchException {
if (settings.getGroups("tribe", true).isEmpty()) { if (settings.getGroups("tribe", true).isEmpty()) {
licensesClientService.register(FEATURE_NAME, TRIAL_LICENSE_OPTIONS, expirationLoggers, acknowledgementCallback, new InternalListener()); licenseeRegistry.register(this);
} else { } else {
//TODO currently we disable licensing on tribe node. remove this once es core supports merging cluster //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 @Override
protected void doClose() throws ElasticsearchException { 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();
}
}
}
} }

View File

@ -12,6 +12,7 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.internal.Nullable; import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.rest.*; import org.elasticsearch.rest.*;
import org.elasticsearch.shield.ShieldBuild; import org.elasticsearch.shield.ShieldBuild;
import org.elasticsearch.shield.ShieldPlugin; import org.elasticsearch.shield.ShieldPlugin;
@ -71,7 +72,7 @@ public class RestShieldInfoAction extends BaseRestHandler {
private Status resolveStatus() { private Status resolveStatus() {
if (shieldEnabled) { if (shieldEnabled) {
if (licenseService.enabled()) { if (licenseService.state() != LicenseState.DISABLED) {
return Status.ENABLED; return Status.ENABLED;
} }
return Status.UNLICENSED; return Status.UNLICENSED;

View File

@ -5,8 +5,8 @@
*/ */
package org.elasticsearch.shield.transport.filter; package org.elasticsearch.shield.transport.filter;
import com.google.common.net.InetAddresses;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.network.InetAddresses;
import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.transport.TransportAddress;

View File

@ -19,7 +19,9 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.Module; import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.core.License; 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.plugins.Plugin;
import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.shield.license.LicenseService; import org.elasticsearch.shield.license.LicenseService;
@ -169,13 +171,13 @@ public class LicensingTests extends ShieldIntegTestCase {
} }
public static void disableLicensing() { public static void disableLicensing() {
for (InternalLicensesClientService service : internalCluster().getInstances(InternalLicensesClientService.class)) { for (InternalLicenseeRegistry service : internalCluster().getInstances(InternalLicenseeRegistry.class)) {
service.disable(); service.disable();
} }
} }
public static void enableLicensing() { public static void enableLicensing() {
for (InternalLicensesClientService service : internalCluster().getInstances(InternalLicensesClientService.class)) { for (InternalLicenseeRegistry service : internalCluster().getInstances(InternalLicenseeRegistry.class)) {
service.enable(); service.enable();
} }
} }
@ -203,49 +205,47 @@ public class LicensingTests extends ShieldIntegTestCase {
public static class InternalLicenseModule extends AbstractModule { public static class InternalLicenseModule extends AbstractModule {
@Override @Override
protected void configure() { protected void configure() {
bind(InternalLicensesClientService.class).asEagerSingleton(); bind(InternalLicenseeRegistry.class).asEagerSingleton();
bind(LicensesClientService.class).to(InternalLicensesClientService.class); 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() static final License DUMMY_LICENSE = License.builder()
.feature(LicenseService.FEATURE_NAME)
.expiryDate(System.currentTimeMillis()) .expiryDate(System.currentTimeMillis())
.issueDate(System.currentTimeMillis()) .issueDate(System.currentTimeMillis())
.issuedTo("LicensingTests") .issuedTo("LicensingTests")
.issuer("test") .issuer("test")
.maxNodes(Integer.MAX_VALUE) .maxNodes(Integer.MAX_VALUE)
.signature("_signature") .signature("_signature")
.type("test_license_for_shield") .type("basic")
.subscriptionType("all_is_good")
.uid(String.valueOf(randomLong()) + System.identityHashCode(LicensingTests.class)) .uid(String.valueOf(randomLong()) + System.identityHashCode(LicensingTests.class))
.build(); .build();
@Inject @Inject
public InternalLicensesClientService(Settings settings) { public InternalLicenseeRegistry(Settings settings) {
super(settings); super(settings);
enable(); enable();
} }
@Override @Override
public void register(String s, LicensesClientService.TrialLicenseOptions trialLicenseOptions, Collection<LicensesClientService.ExpirationCallback> collection, AcknowledgementCallback acknowledgementCallback, Listener listener) { public void register(Licensee licensee) {
listeners.add(listener); licensees.add(licensee);
enable(); enable();
} }
void enable() { void enable() {
for (Listener listener : listeners) { for (Licensee licensee : licensees) {
listener.onEnabled(DUMMY_LICENSE); licensee.onChange(DUMMY_LICENSE, LicenseState.ENABLED);
} }
} }
void disable() { void disable() {
for (Listener listener : listeners) { for (Licensee licensee : licensees) {
listener.onDisabled(DUMMY_LICENSE); licensee.onChange(DUMMY_LICENSE, LicenseState.DISABLED);
} }
} }
} }

View File

@ -11,6 +11,7 @@ import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.search.SearchScrollRequest; import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.support.ActionFilterChain; import org.elasticsearch.action.support.ActionFilterChain;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.shield.User; import org.elasticsearch.shield.User;
import org.elasticsearch.shield.action.interceptor.RequestInterceptor; import org.elasticsearch.shield.action.interceptor.RequestInterceptor;
import org.elasticsearch.shield.audit.AuditTrail; import org.elasticsearch.shield.audit.AuditTrail;
@ -112,7 +113,7 @@ public class ShieldActionFilterTests extends ESTestCase {
private class MockLicenseEventsNotifier extends LicenseEventsNotifier { private class MockLicenseEventsNotifier extends LicenseEventsNotifier {
@Override @Override
public void register(MockLicenseEventsNotifier.Listener listener) { public void register(MockLicenseEventsNotifier.Listener listener) {
listener.enabled(); listener.notify(LicenseState.ENABLED);
} }
} }
} }

View File

@ -43,8 +43,7 @@ import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.internal.ParentFieldMapper; import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.IndexShard;
import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.similarity.SimilarityLookupService; import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.indices.InternalIndicesLifecycle;
import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.aggregations.LeafBucketCollector; import org.elasticsearch.search.aggregations.LeafBucketCollector;
import org.elasticsearch.shield.authz.InternalAuthorizationService; import org.elasticsearch.shield.authz.InternalAuthorizationService;
@ -80,9 +79,9 @@ public class ShieldIndexSearcherWrapperUnitTests extends ESTestCase {
Index index = new Index("_index"); Index index = new Index("_index");
Settings settings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build(); Settings settings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build();
AnalysisService analysisService = new AnalysisService(index, settings); AnalysisService analysisService = new AnalysisService(index, settings);
SimilarityLookupService similarityLookupService = new SimilarityLookupService(index, settings); SimilarityService similarityService = new SimilarityService(index, settings);
ScriptService scriptService = mock(ScriptService.class); ScriptService scriptService = mock(ScriptService.class);
mapperService = new MapperService(index, settings, analysisService, similarityLookupService, scriptService); mapperService = new MapperService(index, settings, analysisService, similarityService, scriptService);
shardId = new ShardId(index, 0); shardId = new ShardId(index, 0);
shieldIndexSearcherWrapper = new ShieldIndexSearcherWrapper(settings, null, mapperService, null); shieldIndexSearcherWrapper = new ShieldIndexSearcherWrapper(settings, null, mapperService, null);

View File

@ -5,8 +5,8 @@
*/ */
package org.elasticsearch.shield.transport.filter; package org.elasticsearch.shield.transport.filter;
import com.google.common.net.InetAddresses;
import org.elasticsearch.common.component.Lifecycle; import org.elasticsearch.common.component.Lifecycle;
import org.elasticsearch.common.network.InetAddresses;
import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.BoundTransportAddress; import org.elasticsearch.common.transport.BoundTransportAddress;

View File

@ -5,8 +5,8 @@
*/ */
package org.elasticsearch.shield.transport.netty; package org.elasticsearch.shield.transport.netty;
import com.google.common.net.InetAddresses;
import org.elasticsearch.common.component.Lifecycle; import org.elasticsearch.common.component.Lifecycle;
import org.elasticsearch.common.network.InetAddresses;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.BoundTransportAddress; import org.elasticsearch.common.transport.BoundTransportAddress;
import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.common.transport.InetSocketTransportAddress;

View File

@ -1,3 +1,4 @@
cluster:admin/render/template/search
cluster:admin/repository/delete cluster:admin/repository/delete
cluster:admin/repository/get cluster:admin/repository/get
cluster:admin/repository/put cluster:admin/repository/put
@ -33,7 +34,6 @@ indices:admin/mappings/get
indices:admin/open indices:admin/open
indices:admin/optimize indices:admin/optimize
indices:admin/refresh indices:admin/refresh
indices:admin/render/template/search
indices:admin/settings/update indices:admin/settings/update
indices:admin/shards/search_shards indices:admin/shards/search_shards
indices:admin/template/delete indices:admin/template/delete

View File

@ -1,3 +1,4 @@
cluster:admin/render/template/search
cluster:admin/snapshot/status[nodes] cluster:admin/snapshot/status[nodes]
cluster:admin/snapshot/status[nodes][n] cluster:admin/snapshot/status[nodes][n]
cluster:monitor/nodes/hot_threads[n] cluster:monitor/nodes/hot_threads[n]
@ -16,7 +17,6 @@ indices:admin/mappings/fields/get[index][s]
indices:admin/optimize[n] indices:admin/optimize[n]
indices:admin/refresh[s] indices:admin/refresh[s]
indices:admin/refresh[s][r] indices:admin/refresh[s][r]
indices:admin/render/template/search
indices:admin/upgrade indices:admin/upgrade
indices:admin/upgrade[n] indices:admin/upgrade[n]
indices:admin/validate/query[s] indices:admin/validate/query[s]

View File

@ -36,6 +36,12 @@ bin/plugin remove watcher
[[change-list]] [[change-list]]
=== Change List === Change List
[float]
==== 2.0.0
.Bug fixes
* Fixed an issue where the scheduler may get stuck during Watcher startup. This caused no watches to ever fire.
[float] [float]
==== 2.0.0-rc1 ==== 2.0.0-rc1

View File

@ -6,93 +6,70 @@
package org.elasticsearch.watcher.license; package org.elasticsearch.watcher.license;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject; 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.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.core.License; 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.LicensesService; import org.elasticsearch.license.plugin.core.Licensee;
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
import org.elasticsearch.watcher.WatcherPlugin; 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; public static final String FEATURE_NAME = WatcherPlugin.NAME;
private static final LicensesClientService.TrialLicenseOptions TRIAL_LICENSE_OPTIONS = private final LicenseeRegistry clientService;
new LicensesClientService.TrialLicenseOptions(TimeValue.timeValueHours(30 * 24), 1000); private volatile LicenseState state;
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;
@Inject @Inject
public LicenseService(Settings settings, LicensesClientService clientService) { public LicenseService(Settings settings, LicenseeRegistry clientService) {
super(settings); super(settings);
this.clientService = clientService; this.clientService = clientService;
this.expirationLoggers = Arrays.asList( }
new LicensesService.ExpirationCallback.Pre(days(7), days(30), days(1)) {
@Override @Override
public void on(License license, LicensesService.ExpirationStatus status) { public String id() {
logger.error("\n" + return FEATURE_NAME;
"#\n" + }
"# Watcher license will expire on [{}]. All configured actions on\n" +
"# all registered watches are throttled (not executed) on Watcher license expiration. \n" + @Override
"# Watches will continue be evaluated and watch history will continue being recorded.\n" + public String[] expirationMessages() {
"# Have a new license? please update it. Otherwise, please reach out to your support contact.\n" + // TODO add messages to be logged around license expiry
"#", DATE_FORMATTER.printer().print(license.expiryDate())); return new String[0];
} }
},
new LicensesService.ExpirationCallback.Pre(days(0), days(7), minutes(10)) { @Override
@Override public String[] acknowledgmentMessages(License currentLicense, License newLicense) {
public void on(License license, LicensesService.ExpirationStatus status) { switch (newLicense.operationMode()) {
logger.error("\n" + case BASIC:
"#\n" + if (currentLicense != null) {
"# Watcher license will expire on [{}]. All configured actions on\n" + switch (currentLicense.operationMode()) {
"# all registered watches are throttled (not executed) on Watcher license expiration. \n" + case TRIAL:
"# Watches will continue be evaluated and watch history will continue being recorded.\n" + case GOLD:
"# Have a new license? please update it. Otherwise, please reach out to your support contact.\n" + case PLATINUM:
"#", DATE_FORMATTER.printer().print(license.expiryDate())); return new String[] { "Watcher will be disabled" };
}
},
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()));
} }
} }
); break;
this.acknowledgementCallback = new LicensesClientService.AcknowledgementCallback() { }
@Override return Strings.EMPTY_ARRAY;
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 @Override
// a trial license could be generated public void onChange(License license, LicenseState state) {
return Collections.emptyList(); synchronized (this) {
} this.state = state;
}; }
} }
@Override @Override
protected void doStart() throws ElasticsearchException { protected void doStart() throws ElasticsearchException {
clientService.register(FEATURE_NAME, TRIAL_LICENSE_OPTIONS, expirationLoggers, acknowledgementCallback, new InternalListener(this)); clientService.register(this);
} }
@Override @Override
@ -104,33 +81,6 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
} }
public boolean enabled() { public boolean enabled() {
return enabled; return state != LicenseState.DISABLED;
}
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;
}
} }
} }

View File

@ -136,10 +136,6 @@ public class TickerScheduleTriggerEngine extends ScheduleTriggerEngine {
@Override @Override
public void run() { public void run() {
// calibrate with round clock
while (clock.millis() % 1000 > 15) {
}
while (active) { while (active) {
logger.trace("checking jobs [{}]", clock.nowUTC()); logger.trace("checking jobs [{}]", clock.nowUTC());
checkJobs(); checkJobs();

View File

@ -14,8 +14,9 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.Module; import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.core.License; 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.LicensesService; import org.elasticsearch.license.plugin.core.Licensee;
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.watcher.actions.ActionStatus; import org.elasticsearch.watcher.actions.ActionStatus;
@ -46,15 +47,13 @@ import static org.hamcrest.Matchers.*;
public class LicenseIntegrationTests extends AbstractWatcherIntegrationTestCase { public class LicenseIntegrationTests extends AbstractWatcherIntegrationTestCase {
static final License DUMMY_LICENSE = License.builder() static final License DUMMY_LICENSE = License.builder()
.feature(LicenseService.FEATURE_NAME)
.expiryDate(System.currentTimeMillis()) .expiryDate(System.currentTimeMillis())
.issueDate(System.currentTimeMillis()) .issueDate(System.currentTimeMillis())
.issuedTo("LicensingTests") .issuedTo("LicensingTests")
.issuer("test") .issuer("test")
.maxNodes(Integer.MAX_VALUE) .maxNodes(Integer.MAX_VALUE)
.signature("_signature") .signature("_signature")
.type("test_license_for_watcher") .type("basic")
.subscriptionType("all_is_good")
.uid(String.valueOf(RandomizedTest.systemPropertyAsInt(SysGlobals.CHILDVM_SYSPROP_JVM_ID, 0)) + System.identityHashCode(LicenseIntegrationTests.class)) .uid(String.valueOf(RandomizedTest.systemPropertyAsInt(SysGlobals.CHILDVM_SYSPROP_JVM_ID, 0)) + System.identityHashCode(LicenseIntegrationTests.class))
.build(); .build();
@ -301,13 +300,13 @@ public class LicenseIntegrationTests extends AbstractWatcherIntegrationTestCase
@Override @Override
protected void configure() { protected void configure() {
bind(MockLicenseService.class).asEagerSingleton(); 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 @Inject
public MockLicenseService(Settings settings) { public MockLicenseService(Settings settings) {
@ -316,23 +315,23 @@ public class LicenseIntegrationTests extends AbstractWatcherIntegrationTestCase
} }
@Override @Override
public void register(String s, LicensesService.TrialLicenseOptions trialLicenseOptions, Collection<LicensesService.ExpirationCallback> collection, AcknowledgementCallback acknowledgementCallback, Listener listener) { public void register(Licensee licensee) {
listeners.add(listener); licensees.add(licensee);
enable(); enable();
} }
public void enable() { public void enable() {
// enabled all listeners (incl. shield) // enabled all listeners (incl. shield)
for (Listener listener : listeners) { for (Licensee licensee : licensees) {
listener.onEnabled(DUMMY_LICENSE); licensee.onChange(DUMMY_LICENSE, LicenseState.ENABLED);
} }
} }
public void disable() { public void disable() {
// only disable watcher listener (we need shield to work) // only disable watcher listener (we need shield to work)
for (Listener listener : listeners) { for (Licensee licensee : licensees) {
if (listener instanceof LicenseService.InternalListener) { if (licensee instanceof LicenseService) {
listener.onDisabled(DUMMY_LICENSE); licensee.onChange(DUMMY_LICENSE, LicenseState.DISABLED);
} }
} }
} }