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>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>mockwebserver</artifactId>
<version>2.3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -60,7 +60,8 @@ public class ClusterInfoCollector extends AbstractCollector<ClusterInfoMarvelDoc
List<MarvelDoc> results = new ArrayList<>(1);
// Retrieves all licenses
List<License> licenses = licenseService.licenses();
// TODO: we should only work with one license
List<License> licenses = Collections.singletonList(licenseService.license());
// Retrieves additional cluster stats
ClusterStatsResponse clusterStats = client.admin().cluster().prepareClusterStats().get(marvelSettings.clusterStatsTimeout());

View File

@ -54,6 +54,7 @@ public class HttpExporter extends Exporter {
public static final String HOST_SETTING = "host";
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_KEEP_ALIVE_SETTING = "connection.keep_alive";
public static final String AUTH_USERNAME_SETTING = "auth.username";
public static final String AUTH_PASSWORD_SETTING = "auth.password";
@ -91,6 +92,7 @@ public class HttpExporter extends Exporter {
/** Version of the built-in template **/
final Version templateVersion;
boolean keepAlive;
final ConnectionKeepAliveWorker keepAliveWorker;
Thread keepAliveThread;
@ -117,6 +119,7 @@ public class HttpExporter extends Exporter {
String templateCheckTimeoutValue = config.settings().get(TEMPLATE_CHECK_TIMEOUT_SETTING, null);
templateCheckTimeout = TimeValue.parseTimeValue(templateCheckTimeoutValue, null, settingFQN(TEMPLATE_CHECK_TIMEOUT_SETTING));
keepAlive = config.settings().getAsBoolean(CONNECTION_KEEP_ALIVE_SETTING, true);
keepAliveWorker = new ConnectionKeepAliveWorker();
sslSocketFactory = createSSLSocketFactory(config.settings().getAsSettings(SSL_SETTING));
@ -511,9 +514,11 @@ public class HttpExporter extends Exporter {
}
protected void initKeepAliveThread() {
keepAliveThread = new Thread(keepAliveWorker, "marvel-exporter[" + config.name() + "][keep_alive]");
keepAliveThread.setDaemon(true);
keepAliveThread.start();
if (keepAlive) {
keepAliveThread = new Thread(keepAliveWorker, "marvel-exporter[" + config.name() + "][keep_alive]");
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.TYPE, license.type());
builder.dateValueField(Fields.ISSUE_DATE_IN_MILLIS, Fields.ISSUE_DATE, license.issueDate());
builder.field(Fields.FEATURE, license.feature());
builder.dateValueField(Fields.EXPIRY_DATE_IN_MILLIS, Fields.EXPIRY_DATE, license.expiryDate());
builder.field(Fields.MAX_NODES, license.maxNodes());
builder.field(Fields.ISSUED_TO, license.issuedTo());
@ -77,7 +76,6 @@ public class ClusterInfoRenderer extends AbstractRenderer<ClusterInfoMarvelDoc>
static final XContentBuilderString STATUS = new XContentBuilderString("status");
static final XContentBuilderString UID = new XContentBuilderString("uid");
static final XContentBuilderString TYPE = new XContentBuilderString("type");
static final XContentBuilderString FEATURE = new XContentBuilderString("feature");
static final XContentBuilderString ISSUE_DATE_IN_MILLIS = new XContentBuilderString("issue_date_in_millis");
static final XContentBuilderString ISSUE_DATE = new XContentBuilderString("issue_date");
static final XContentBuilderString EXPIRY_DATE_IN_MILLIS = new XContentBuilderString("expiry_date_in_millis");

View File

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

View File

@ -6,110 +6,45 @@
package org.elasticsearch.marvel.license;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
import org.elasticsearch.common.joda.Joda;
import org.elasticsearch.common.logging.support.LoggerMessageFormat;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.core.License;
import org.elasticsearch.license.plugin.core.LicensesClientService;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.license.plugin.core.Licensee;
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
import org.elasticsearch.license.plugin.core.LicensesManagerService;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.marvel.MarvelPlugin;
import org.elasticsearch.marvel.agent.settings.MarvelSettings;
import org.elasticsearch.marvel.mode.Mode;
import java.util.*;
public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
public class LicenseService extends AbstractLifecycleComponent<LicenseService> implements Licensee {
public static final String FEATURE_NAME = MarvelPlugin.NAME;
private static final LicensesService.TrialLicenseOptions TRIAL_LICENSE_OPTIONS =
new LicensesService.TrialLicenseOptions(TimeValue.timeValueHours(30 * 24), 1000);
private static final FormatDateTimeFormatter DATE_FORMATTER = Joda.forPattern("EEEE, MMMMM dd, yyyy", Locale.ROOT);
static final TimeValue GRACE_PERIOD = days(7);
private final LicensesManagerService managerService;
private final LicensesClientService clientService;
private final LicenseeRegistry clientService;
private final MarvelSettings marvelSettings;
private final Collection<LicensesService.ExpirationCallback> expirationLoggers;
private final LicensesClientService.AcknowledgementCallback acknowledgementCallback;
private volatile Mode mode;
private volatile boolean enabled;
private volatile LicenseState state;
private volatile long expiryDate;
@Inject
public LicenseService(Settings settings, LicensesClientService clientService, LicensesManagerService managerService, MarvelSettings marvelSettings) {
public LicenseService(Settings settings, LicenseeRegistry clientService, LicensesManagerService managerService, MarvelSettings marvelSettings) {
super(settings);
this.managerService = managerService;
this.clientService = clientService;
this.marvelSettings = marvelSettings;
this.mode = Mode.LITE;
this.expirationLoggers = Arrays.asList(
new LicensesService.ExpirationCallback.Pre(days(7), days(30), days(1)) {
@Override
public void on(License license, LicensesService.ExpirationStatus status) {
logger.error("\n" +
"#\n" +
"# Marvel license will expire on [{}].\n" +
"# Have a new license? please update it. Otherwise, please reach out to your support contact.\n" +
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
}
},
new LicensesService.ExpirationCallback.Pre(days(0), days(7), minutes(10)) {
@Override
public void on(License license, LicensesService.ExpirationStatus status) {
logger.error("\n" +
"#\n" +
"# Marvel license will expire on [{}].\n" +
"# Have a new license? please update it. Otherwise, please reach out to your support contact.\n" +
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
}
},
new LicensesService.ExpirationCallback.Post(days(0), GRACE_PERIOD, minutes(10)) {
@Override
public void on(License license, LicensesService.ExpirationStatus status) {
long endOfGracePeriod = license.expiryDate() + GRACE_PERIOD.getMillis();
logger.error("\n" +
"#\n" +
"# MARVEL LICENSE HAS EXPIRED ON [{}].\n" +
"# MARVEL WILL STOP COLLECTING DATA ON [{}].\n" +
"# HAVE A NEW LICENSE? PLEASE UPDATE IT. OTHERWISE, PLEASE REACH OUT TO YOUR SUPPORT CONTACT.\n" +
"#", DATE_FORMATTER.printer().print(endOfGracePeriod), DATE_FORMATTER.printer().print(license.expiryDate()));
}
}
);
this.acknowledgementCallback = new LicensesClientService.AcknowledgementCallback() {
@Override
public List<String> acknowledge(License currentLicense, License newLicense) {
switch (newLicense.type()) {
case "trial":
case "gold":
case "platinum":
return Collections.emptyList();
default: // "basic" - we also fall back to basic for an unknown type
return Collections.singletonList(LoggerMessageFormat.format(
"Marvel: Multi-cluster support is disabled for clusters with [{}] licenses.\n" +
"If you are running multiple customers, users won't be able to access this\n" +
"all the clusters with [{}] licenses from a single Marvel instance. To access them\n" +
"a dedicated and separated marvel instance will be required for each cluster",
newLicense.type(), newLicense.type()));
}
}
};
}
@Override
protected void doStart() throws ElasticsearchException {
clientService.register(FEATURE_NAME, TRIAL_LICENSE_OPTIONS, expirationLoggers, acknowledgementCallback, new InternalListener(this));
clientService.register(this);
}
@Override
@ -120,14 +55,6 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
protected void doClose() throws ElasticsearchException {
}
static TimeValue days(int days) {
return TimeValue.timeValueHours(days * 24);
}
static TimeValue minutes(int minutes) {
return TimeValue.timeValueMinutes(minutes);
}
/**
* @return the current marvel's operating mode
*/
@ -138,18 +65,20 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
/**
* @return all registered licenses
*/
public List<License> licenses() {
return managerService.getLicenses();
public License license() {
return managerService.getLicense();
}
/**
* @return true if the marvel license is enabled
*/
public boolean enabled() {
return enabled;
return state == LicenseState.ENABLED || state == LicenseState.GRACE_PERIOD;
}
/**
* TODO: remove licensing grace period, just check for state == LicensesClientService.LicenseState.GRACE_PERIOD instead
*
* @return true if marvel is running within the "grace period", ie when the license
* is expired but a given extra delay is not yet elapsed
*/
@ -164,30 +93,57 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
return expiryDate;
}
class InternalListener implements LicensesClientService.Listener {
@Override
public String id() {
return FEATURE_NAME;
}
private final LicenseService service;
@Override
public String[] expirationMessages() {
// TODO add messages to be logged around license expiry
return Strings.EMPTY_ARRAY;
}
public InternalListener(LicenseService service) {
this.service = service;
@Override
public String[] acknowledgmentMessages(License currentLicense, License newLicense) {
switch (newLicense.operationMode()) {
case BASIC:
if (currentLicense != null) {
switch (currentLicense.operationMode()) {
case TRIAL:
case GOLD:
case PLATINUM:
return new String[] {
LoggerMessageFormat.format(
"Multi-cluster support is disabled for clusters with [{}] licenses.\n" +
"If you are running multiple customers, users won't be able to access this\n" +
"all the clusters with [{}] licenses from a single Marvel instance. To access them\n" +
"a dedicated and separated marvel instance will be required for each cluster",
newLicense.type(), newLicense.type())
};
}
}
}
return Strings.EMPTY_ARRAY;
}
@Override
public void onEnabled(License license) {
try {
service.enabled = true;
service.expiryDate = license.expiryDate();
service.mode = Mode.fromName(license.type());
} catch (IllegalArgumentException e) {
service.mode = Mode.LITE;
@Override
public void onChange(License license, LicenseState state) {
synchronized (this) {
this.state = state;
if (license != null) {
try {
mode = Mode.fromName(license.type());
} catch (IllegalArgumentException e) {
mode = Mode.LITE;
}
expiryDate = license.expiryDate();
} else {
mode = Mode.LITE;
}
if (state == LicenseState.DISABLED) {
mode = Mode.LITE;
}
}
@Override
public void onDisabled(License license) {
service.enabled = false;
service.expiryDate = license.expiryDate();
service.mode = Mode.LITE;
}
}
}

View File

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

View File

@ -33,7 +33,7 @@ public class MarvelInternalUserHolder {
// and full access to .marvel-* and .marvel-data indices
.add(Privilege.Index.ALL, MarvelSettings.MARVEL_INDICES_PREFIX + "*")
// note, we don't need _licenses permission as we're taking the licenses
// note, we don't need _license permission as we're taking the licenses
// directly form the license service.
.build();

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.PendingClusterTasksRequestBuilder;
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.IndicesAliasesRequest;
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.ValidateQueryRequestBuilder;
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.DeleteWarmerRequest;
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);
}
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) {
return this.execute(PutWarmerAction.INSTANCE, request);
}
@ -1370,6 +1358,18 @@ public class SecuredClient implements Client {
public SnapshotsStatusRequestBuilder prepareSnapshotStatus() {
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 {

View File

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

View File

@ -5,134 +5,114 @@
*/
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.action.admin.cluster.health.ClusterHealthStatus;
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.common.bytes.BytesArray;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.marvel.agent.collector.cluster.ClusterStateCollector;
import org.elasticsearch.marvel.agent.collector.cluster.ClusterStateMarvelDoc;
import org.elasticsearch.marvel.agent.collector.indices.IndexRecoveryCollector;
import org.elasticsearch.marvel.agent.collector.indices.IndexRecoveryMarvelDoc;
import org.elasticsearch.marvel.agent.exporter.Exporters;
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.test.MarvelIntegTestCase;
import org.elasticsearch.node.Node;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
import org.elasticsearch.test.ESIntegTestCase.SuppressLocalMode;
import org.elasticsearch.test.InternalTestCluster;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.ESIntegTestCase.Scope;
import org.hamcrest.Matchers;
import org.joda.time.format.DateTimeFormat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.net.BindException;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.Map;
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.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
// 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")
@ESIntegTestCase.ClusterScope(scope = Scope.TEST, numDataNodes = 0, numClientNodes = 0, transportClientRatio = 0.0)
public class HttpExporterTests extends MarvelIntegTestCase {
final static AtomicLong timeStampGenerator = new AtomicLong();
@Override
protected boolean enableShield() {
return false;
}
private int webPort;
private MockWebServer webServer;
@Before
public void init() throws Exception {
startCollection();
public void startWebservice() throws Exception {
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
public void cleanup() throws Exception {
stopCollection();
}
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return Settings.builder()
.put(super.nodeSettings(nodeOrdinal))
.put(Node.HTTP_ENABLED, true)
.put("shield.enabled", false)
.build();
webServer.shutdown();
}
@Test
public void testSimpleExport() throws Exception {
TargetNode target = TargetNode.start(internalCluster());
public void testExport() throws Exception {
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()
.put(MarvelSettings.INTERVAL, "-1")
.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);
ensureGreen();
HttpExporter exporter = getExporter(agentNode);
MarvelDoc doc = newRandomMarvelDoc();
exporter.export(Collections.singletonList(doc));
flush();
refresh();
assertThat(webServer.getRequestCount(), greaterThanOrEqualTo(4));
SearchResponse response = client().prepareSearch(".marvel-es-*").setTypes(doc.type()).get();
assertThat(response, notNullValue());
assertThat(response.getHits().totalHits(), is(1L));
}
RecordedRequest recordedRequest = webServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("GET"));
assertThat(recordedRequest.getPath(), equalTo("/"));
@Test
public void testTemplateAdditionDespiteOfLateClusterForming() throws Exception {
recordedRequest = webServer.takeRequest();
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()
.put(MarvelSettings.STARTUP_DELAY, "200m")
.put(Node.HTTP_ENABLED, true)
.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();
recordedRequest = webServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("POST"));
assertThat(recordedRequest.getPath(), equalTo("/_bulk"));
}
@Test
public void testDynamicHostChange() {
// disable exporting to be able to use non valid hosts
Settings.Builder builder = Settings.builder()
.put(MarvelSettings.INTERVAL, "-1")
@ -159,144 +139,222 @@ public class HttpExporterTests extends MarvelIntegTestCase {
@Test
public void testHostChangeReChecksTemplate() throws Exception {
TargetNode targetNode = TargetNode.start(internalCluster());
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.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);
logger.info("--> exporting data");
HttpExporter exporter = getExporter(agentNode);
logger.info("exporting an event");
exporter.export(Collections.singletonList(newRandomMarvelDoc()));
logger.info("removing the marvel template");
assertAcked(client().admin().indices().prepareDeleteTemplate("marvel").get());
assertMarvelTemplateMissing();
assertThat(webServer.getRequestCount(), greaterThanOrEqualTo(4));
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(
Settings.builder().putArray("marvel.agent.exporters._http.host", exporter.hosts)).get());
RecordedRequest recordedRequest = webServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("GET"));
assertThat(recordedRequest.getPath(), equalTo("/"));
// a new exporter is created on update, so we need to re-fetch it
exporter = getExporter(agentNode);
recordedRequest = webServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("GET"));
assertThat(recordedRequest.getPath(), equalTo("/_template/marvel"));
logger.info("exporting a second event");
exporter.export(Collections.singletonList(newRandomMarvelDoc()));
recordedRequest = webServer.takeRequest();
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");
assertMarvelTemplateInstalled();
}
recordedRequest = webServer.takeRequest();
assertThat(recordedRequest.getMethod(), equalTo("POST"));
assertThat(recordedRequest.getPath(), equalTo("/_bulk"));
@Test
public void testHostFailureChecksTemplate() throws Exception {
logger.info("--> setting up another web server");
MockWebServer secondWebServer = null;
int secondWebPort;
TargetNode target0 = TargetNode.start(internalCluster());
assertThat(target0.name, is(internalCluster().getMasterName()));
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 {
for (secondWebPort = 9250; secondWebPort < 9300; secondWebPort++) {
try {
logger.info("--> exporting events from node0");
getExporter(node0).export(Collections.singletonList(newRandomMarvelDoc()));
} catch (Exception e) {
e.printStackTrace();
fail("failed to export event from node0");
secondWebServer = new MockWebServer();
QueueDispatcher dispatcher = new QueueDispatcher();
dispatcher.setFailFast(true);
secondWebServer.setDispatcher(dispatcher);
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
public void testDynamicIndexFormatChange() throws Exception {
TargetNode targetNode = TargetNode.start(internalCluster());
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.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);
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);
MarvelDoc doc = newRandomMarvelDoc();
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);
logger.info("checks that the index [{}] is created", indexName);
assertTrue(client().admin().indices().prepareExists(indexName).get().isExists());
logger.info("--> checks that the document in the bulk request is indexed in [{}]", indexName);
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");
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()
.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();
exporter = getExporter(agentNode);
exporter.export(Collections.singletonList(doc));
String expectedMarvelIndex = MarvelSettings.MARVEL_INDICES_PREFIX
+ DateTimeFormat.forPattern(newTimeFormat).withZoneUTC().print(doc.timestamp());
logger.info("checks that the index [{}] is created", expectedMarvelIndex);
assertTrue(client().admin().indices().prepareExists(expectedMarvelIndex).get().isExists());
assertThat(webServer.getRequestCount(), greaterThanOrEqualTo(4));
logger.info("verifying that template has been created");
assertMarvelTemplateInstalled();
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"));
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
public void testLoadRemoteClusterVersion() {
TargetNode targetNode = TargetNode.start(internalCluster());
public void testLoadRemoteClusterVersion() throws IOException {
final String host = webServer.getHostName() + ":" + webServer.getPort();
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.host", targetNode.httpAddress);
.put("marvel.agent.exporters._http.host", host)
.put("marvel.agent.exporters._http.connection.keep_alive", false);
String agentNode = internalCluster().startNode(builder);
HttpExporter exporter = getExporter(agentNode);
logger.info("--> loading remote cluster version");
Version resolved = exporter.loadRemoteClusterVersion(targetNode.httpAddress);
enqueueGetClusterVersionResponse(Version.CURRENT);
Version resolved = exporter.loadRemoteClusterVersion(host);
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) {
@ -307,29 +365,26 @@ public class HttpExporterTests extends MarvelIntegTestCase {
private MarvelDoc newRandomMarvelDoc() {
if (randomBoolean()) {
return new IndexRecoveryMarvelDoc(internalCluster().getClusterName(),
IndexRecoveryCollector.TYPE, timeStampGenerator.incrementAndGet(), new RecoveryResponse());
IndexRecoveryCollector.TYPE, System.currentTimeMillis(), new RecoveryResponse());
} else {
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 final TransportAddress address;
private final String httpAddress;
private final Client client;
private void enqueueGetClusterVersionResponse(MockWebServer mockWebServer, Version v) throws IOException {
mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(jsonBuilder().startObject().startObject("version").field("number", v.number()).endObject().endObject().bytes().toUtf8()));
}
private TargetNode(InternalTestCluster cluster) {
name = cluster.startNode(Settings.builder().put(Node.HTTP_ENABLED, true));
address = cluster.getInstance(HttpServerTransport.class, name).boundAddress().publishAddress();
httpAddress = address.getHost() + ":" + address.getPort();
this.client = cluster.client(name);
}
private void enqueueResponse(int responseCode, String body) throws IOException {
enqueueResponse(webServer, responseCode, body);
}
static TargetNode start(InternalTestCluster cluster) {
return new TargetNode(cluster);
}
private void enqueueResponse(MockWebServer mockWebServer, int responseCode, String body) throws IOException {
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);
assertThat(hkey, equalTo(recalculated));
assertThat((String) license.get(ClusterInfoRenderer.Fields.FEATURE.underscore().toString()), not(isEmptyOrNullString()));
assertThat((String) license.get(ClusterInfoRenderer.Fields.ISSUER.underscore().toString()), not(isEmptyOrNullString()));
assertThat((String) license.get(ClusterInfoRenderer.Fields.ISSUED_TO.underscore().toString()), not(isEmptyOrNullString()));
assertThat((Long) license.get(ClusterInfoRenderer.Fields.ISSUE_DATE_IN_MILLIS.underscore().toString()), greaterThan(0L));

View File

@ -6,27 +6,37 @@
package org.elasticsearch.marvel.agent.renderer.shards;
import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.cluster.metadata.IndexMetaData;
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.settings.MarvelSettings;
import org.elasticsearch.marvel.test.MarvelIntegTestCase;
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 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")
public class ShardsIT extends MarvelIntegTestCase {
private static final String INDEX_PREFIX = "test-shards-";
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return Settings.builder()
.put(super.nodeSettings(nodeOrdinal))
.put(MarvelSettings.INTERVAL, "3s")
.put(MarvelSettings.COLLECTORS, ShardsCollector.NAME)
.put(MarvelSettings.INDICES, INDEX_PREFIX + "*")
.build();
}
@ -34,7 +44,7 @@ public class ShardsIT extends MarvelIntegTestCase {
public void testShards() throws Exception {
logger.debug("--> creating some indices so that shards collector reports data");
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);
@ -55,4 +65,34 @@ public class ShardsIT extends MarvelIntegTestCase {
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.settings.Settings;
import org.elasticsearch.license.core.License;
import org.elasticsearch.license.plugin.core.LicensesClientService;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.license.plugin.core.Licensee;
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
import org.elasticsearch.marvel.MarvelPlugin;
import org.elasticsearch.marvel.mode.Mode;
import org.elasticsearch.marvel.test.MarvelIntegTestCase;
@ -106,26 +107,24 @@ public class LicenseIntegrationTests extends MarvelIntegTestCase {
@Override
protected void configure() {
bind(MockLicenseService.class).asEagerSingleton();
bind(LicensesClientService.class).to(MockLicenseService.class);
bind(LicenseeRegistry.class).to(MockLicenseService.class);
}
}
public static class MockLicenseService extends AbstractComponent implements LicensesClientService {
public static class MockLicenseService extends AbstractComponent implements LicenseeRegistry {
static final License DUMMY_LICENSE = License.builder()
.feature(LicenseService.FEATURE_NAME)
.expiryDate(System.currentTimeMillis())
.issueDate(System.currentTimeMillis())
.issuedTo("LicensingTests")
.issuer("test")
.maxNodes(Integer.MAX_VALUE)
.signature("_signature")
.type("standard")
.subscriptionType("all_is_good")
.type("basic")
.uid(String.valueOf(RandomizedTest.systemPropertyAsInt(SysGlobals.CHILDVM_SYSPROP_JVM_ID, 0)) + System.identityHashCode(LicenseIntegrationTests.class))
.build();
private final List<Listener> listeners = new ArrayList<>();
private final List<Licensee> licensees = new ArrayList<>();
@Inject
public MockLicenseService(Settings settings) {
@ -134,22 +133,20 @@ public class LicenseIntegrationTests extends MarvelIntegTestCase {
}
@Override
public void register(String s, LicensesService.TrialLicenseOptions trialLicenseOptions, Collection<LicensesService.ExpirationCallback> collection, AcknowledgementCallback acknowledgementCallback, Listener listener) {
listeners.add(listener);
public void register(Licensee licensee) {
licensees.add(licensee);
enable();
}
public void enable() {
// enabled all listeners (incl. shield)
for (Listener listener : listeners) {
listener.onEnabled(DUMMY_LICENSE);
for (Licensee licensee : licensees) {
licensee.onChange(DUMMY_LICENSE, randomBoolean() ? LicenseState.GRACE_PERIOD : LicenseState.ENABLED);
}
}
public void disable() {
// only disable watcher listener (we need shield to work)
for (Listener listener : listeners) {
listener.onDisabled(DUMMY_LICENSE);
for (Licensee licensee : licensees) {
licensee.onChange(DUMMY_LICENSE, LicenseState.DISABLED);
}
}
}

View File

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

View File

@ -23,7 +23,7 @@
<skip.unit.tests>true</skip.unit.tests>
<elasticsearch.integ.antfile>${project.basedir}/integration-tests.xml</elasticsearch.integ.antfile>
<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>
</properties>

View File

@ -33,14 +33,8 @@ NOTE: If you are using a <<deb-rpm-install, DEB/RPM distribution>> of Elasticsea
bin/elasticsearch
----------------------------------------------------------
. To verify that Shield is up and running, use the `_shield` API to get the Shield version:
+
[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:
. 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]
----------------
@ -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]
----------------
Now you're ready to secure your cluster! Here are a few things
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]
-----------------------------------------------------------------------
curl -XPUT -u admin 'http://<host>:<port>/_licenses' -d @license.json
curl -XPUT -u admin 'http://<host>:<port>/_license' -d @license.json
-----------------------------------------------------------------------
Where:
@ -42,7 +42,7 @@ You can list all currently installed licenses by executing the following REST AP
[source,shell]
-----------------------------------------------------
curl -XGET -u admin:password 'http://<host>:<port>/_licenses'
curl -XGET -u admin:password 'http://<host>:<port>/_license'
-----------------------------------------------------
The response of this command will be a JSON listing all available licenses. In the case of Shield, the following

View File

@ -67,6 +67,7 @@ Elasticsearch. This only applies to publicly available indices and cluster actio
[float]
===== Cluster actions privileges
* `cluster:admin/render/template/search`
* `cluster:admin/repository/delete`
* `cluster:admin/repository/get`
* `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/optimize`
* `indices:admin/refresh`
* `indices:admin/render/template/search`
* `indices:admin/settings/update`
* `indices:admin/shards/search_shards`
* `indices:admin/template/delete`

View File

@ -42,6 +42,18 @@ version of Shield. We recommend copying the changes listed below to your `roles.
[[changelist]]
=== 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]
==== 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.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.license.plugin.core.LicenseUtils;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.action.interceptor.RequestInterceptor;
@ -62,13 +63,8 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
this.actionMapper = actionMapper;
licenseEventsNotifier.register(new LicenseEventsNotifier.Listener() {
@Override
public void enabled() {
licenseEnabled = true;
}
@Override
public void disabled() {
licenseEnabled = false;
public void notify(LicenseState state) {
licenseEnabled = state != LicenseState.DISABLED;
}
});
this.requestInterceptors = requestInterceptors;

View File

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

View File

@ -6,99 +6,95 @@
package org.elasticsearch.shield.license;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
import org.elasticsearch.common.joda.Joda;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.core.License;
import org.elasticsearch.license.plugin.core.LicensesClientService;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.license.plugin.core.Licensee;
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
import org.elasticsearch.shield.ShieldPlugin;
import java.util.*;
/**
*
*/
public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
public class LicenseService extends AbstractLifecycleComponent<LicenseService> implements Licensee {
public static final String FEATURE_NAME = ShieldPlugin.NAME;
private static final LicensesClientService.TrialLicenseOptions TRIAL_LICENSE_OPTIONS =
new LicensesClientService.TrialLicenseOptions(TimeValue.timeValueHours(30 * 24), 1000);
private static final FormatDateTimeFormatter DATE_FORMATTER = Joda.forPattern("EEEE, MMMMM dd, yyyy", Locale.ROOT);
private final LicensesClientService licensesClientService;
private final LicenseeRegistry licenseeRegistry;
private final LicenseEventsNotifier notifier;
private final Collection<LicensesClientService.ExpirationCallback> expirationLoggers;
private final LicensesClientService.AcknowledgementCallback acknowledgementCallback;
private boolean enabled = false;
private volatile LicenseState state = LicenseState.DISABLED;
@Inject
public LicenseService(Settings settings, LicensesClientService licensesClientService, LicenseEventsNotifier notifier) {
public LicenseService(Settings settings, LicenseeRegistry licenseeRegistry, LicenseEventsNotifier notifier) {
super(settings);
this.licensesClientService = licensesClientService;
this.licenseeRegistry = licenseeRegistry;
this.notifier = notifier;
this.expirationLoggers = Arrays.asList(
new LicensesClientService.ExpirationCallback.Pre(days(7), days(30), days(1)) {
@Override
public void on(License license, LicensesClientService.ExpirationStatus status) {
logger.error("\n" +
"#\n" +
"# Shield license will expire on [{}]. Cluster health, cluster stats and indices stats operations are\n" +
"# blocked on Shield license expiration. All data operations (read and write) continue to work. If you\n" +
"# have a new license, please update it. Otherwise, please reach out to your support contact.\n" +
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
}
},
new LicensesClientService.ExpirationCallback.Pre(days(0), days(7), minutes(10)) {
@Override
public void on(License license, LicensesClientService.ExpirationStatus status) {
logger.error("\n" +
"#\n" +
"# Shield license will expire on [{}]. Cluster health, cluster stats and indices stats operations are\n" +
"# blocked on Shield license expiration. All data operations (read and write) continue to work. If you\n" +
"# have a new license, please update it. Otherwise, please reach out to your support contact.\n" +
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
}
},
new LicensesClientService.ExpirationCallback.Post(days(0), null, minutes(10)) {
@Override
public void on(License license, LicensesClientService.ExpirationStatus status) {
logger.error("\n" +
"#\n" +
"# SHIELD LICENSE EXPIRED ON [{}]! CLUSTER HEALTH, CLUSTER STATS AND INDICES STATS OPERATIONS ARE\n" +
"# NOW BLOCKED. ALL DATA OPERATIONS (READ AND WRITE) CONTINUE TO WORK. IF YOU HAVE A NEW LICENSE, PLEASE\n" +
"# UPDATE IT. OTHERWISE, PLEASE REACH OUT TO YOUR SUPPORT CONTACT.\n" +
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
}
}
);
this.acknowledgementCallback = new LicensesClientService.AcknowledgementCallback() {
@Override
public List<String> acknowledge(License currentLicense, License newLicense) {
// TODO: add messages to be acknowledged when installing newLicense from currentLicense
// NOTE: currentLicense can be null, as a license registration can happen before
// a trial license could be generated
return Collections.emptyList();
}
}
@Override
public String id() {
return FEATURE_NAME;
}
@Override
public String[] expirationMessages() {
return new String[] {
"Cluster health, cluster stats and indices stats operations are blocked",
"All data operations (read and write) continue to work"
};
}
public synchronized boolean enabled() {
return enabled;
@Override
public String[] acknowledgmentMessages(License currentLicense, License newLicense) {
switch (newLicense.operationMode()) {
case BASIC:
if (currentLicense != null) {
switch (currentLicense.operationMode()) {
case TRIAL:
case GOLD:
case PLATINUM:
return new String[] { "The following Shield functionality will be disabled: authentication, authorization, ip filtering, auditing, SSL will be disabled on node restart. Please restart your node after applying the license." };
}
}
break;
case GOLD:
if (currentLicense != null) {
switch (currentLicense.operationMode()) {
case TRIAL:
case BASIC:
case PLATINUM:
return new String[] {
"Field and document level access control will be disabled"
};
}
}
break;
}
return Strings.EMPTY_ARRAY;
}
@Override
public void onChange(License license, LicenseState state) {
synchronized (this) {
this.state = state;
notifier.notify(state);
}
}
public LicenseState state() {
return state;
}
@Override
protected void doStart() throws ElasticsearchException {
if (settings.getGroups("tribe", true).isEmpty()) {
licensesClientService.register(FEATURE_NAME, TRIAL_LICENSE_OPTIONS, expirationLoggers, acknowledgementCallback, new InternalListener());
licenseeRegistry.register(this);
} else {
//TODO currently we disable licensing on tribe node. remove this once es core supports merging cluster
new InternalListener().onEnabled(null);
onChange(null, LicenseState.ENABLED);
}
}
@ -109,34 +105,4 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
@Override
protected void doClose() throws ElasticsearchException {
}
static TimeValue days(int days) {
return TimeValue.timeValueHours(days * 24);
}
static TimeValue minutes(int minutes) {
return TimeValue.timeValueMinutes(minutes);
}
class InternalListener implements LicensesClientService.Listener {
@Override
public void onEnabled(License license) {
synchronized (LicenseService.this) {
logger.info("enabling license for [{}]", FEATURE_NAME);
enabled = true;
notifier.notifyEnabled();
}
}
@Override
public void onDisabled(License license) {
synchronized (LicenseService.this) {
logger.info("DISABLING LICENSE FOR [{}]", FEATURE_NAME);
enabled = false;
notifier.notifyDisabled();
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
cluster:admin/render/template/search
cluster:admin/repository/delete
cluster:admin/repository/get
cluster:admin/repository/put
@ -33,7 +34,6 @@ indices:admin/mappings/get
indices:admin/open
indices:admin/optimize
indices:admin/refresh
indices:admin/render/template/search
indices:admin/settings/update
indices:admin/shards/search_shards
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][n]
cluster:monitor/nodes/hot_threads[n]
@ -16,7 +17,6 @@ indices:admin/mappings/fields/get[index][s]
indices:admin/optimize[n]
indices:admin/refresh[s]
indices:admin/refresh[s][r]
indices:admin/render/template/search
indices:admin/upgrade
indices:admin/upgrade[n]
indices:admin/validate/query[s]

View File

@ -36,6 +36,12 @@ bin/plugin remove watcher
[[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]
==== 2.0.0-rc1

View File

@ -6,93 +6,70 @@
package org.elasticsearch.watcher.license;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
import org.elasticsearch.common.joda.Joda;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.core.License;
import org.elasticsearch.license.plugin.core.LicensesClientService;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.license.plugin.core.Licensee;
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
import org.elasticsearch.watcher.WatcherPlugin;
import java.util.*;
/**
*
*/
public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
public class LicenseService extends AbstractLifecycleComponent<LicenseService> implements Licensee {
public static final String FEATURE_NAME = WatcherPlugin.NAME;
private static final LicensesClientService.TrialLicenseOptions TRIAL_LICENSE_OPTIONS =
new LicensesClientService.TrialLicenseOptions(TimeValue.timeValueHours(30 * 24), 1000);
private static final FormatDateTimeFormatter DATE_FORMATTER = Joda.forPattern("EEEE, MMMMM dd, yyyy", Locale.ROOT);
private final LicensesClientService clientService;
private final Collection<LicensesService.ExpirationCallback> expirationLoggers;
private final LicensesClientService.AcknowledgementCallback acknowledgementCallback;
private volatile boolean enabled;
private final LicenseeRegistry clientService;
private volatile LicenseState state;
@Inject
public LicenseService(Settings settings, LicensesClientService clientService) {
public LicenseService(Settings settings, LicenseeRegistry clientService) {
super(settings);
this.clientService = clientService;
this.expirationLoggers = Arrays.asList(
new LicensesService.ExpirationCallback.Pre(days(7), days(30), days(1)) {
@Override
public void on(License license, LicensesService.ExpirationStatus status) {
logger.error("\n" +
"#\n" +
"# Watcher license will expire on [{}]. All configured actions on\n" +
"# all registered watches are throttled (not executed) on Watcher license expiration. \n" +
"# Watches will continue be evaluated and watch history will continue being recorded.\n" +
"# Have a new license? please update it. Otherwise, please reach out to your support contact.\n" +
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
}
},
new LicensesService.ExpirationCallback.Pre(days(0), days(7), minutes(10)) {
@Override
public void on(License license, LicensesService.ExpirationStatus status) {
logger.error("\n" +
"#\n" +
"# Watcher license will expire on [{}]. All configured actions on\n" +
"# all registered watches are throttled (not executed) on Watcher license expiration. \n" +
"# Watches will continue be evaluated and watch history will continue being recorded.\n" +
"# Have a new license? please update it. Otherwise, please reach out to your support contact.\n" +
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
}
},
new LicensesService.ExpirationCallback.Post(days(0), null, minutes(10)) {
@Override
public void on(License license, LicensesService.ExpirationStatus status) {
logger.error("\n" +
"#\n" +
"# WATCHER LICENSE WAS EXPIRED ON [{}]. ALL CONFIGURED ACTIONS ON\n" +
"# ALL REGISTERED WATCHES ARE THROTTLED (NOT EXECUTED) ON WATCHER LICENSE EXPIRATION. \n" +
"# WATCHES WILL CONTINUE BE EVALUATED AND WATCH HISTORY WILL CONTINUE BEING RECORDED.\n" +
"# HAVE A NEW LICENSE? PLEASE UPDATE IT. OTHERWISE, PLEASE REACH OUT TO YOUR SUPPORT CONTACT.\n" +
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
}
@Override
public String id() {
return FEATURE_NAME;
}
@Override
public String[] expirationMessages() {
// TODO add messages to be logged around license expiry
return new String[0];
}
@Override
public String[] acknowledgmentMessages(License currentLicense, License newLicense) {
switch (newLicense.operationMode()) {
case BASIC:
if (currentLicense != null) {
switch (currentLicense.operationMode()) {
case TRIAL:
case GOLD:
case PLATINUM:
return new String[] { "Watcher will be disabled" };
}
}
);
this.acknowledgementCallback = new LicensesClientService.AcknowledgementCallback() {
@Override
public List<String> acknowledge(License currentLicense, License newLicense) {
// TODO: add messages to be acknowledged when installing newLicense from currentLicense
// NOTE: currentLicense can be null, as a license registration can happen before
// a trial license could be generated
return Collections.emptyList();
}
};
break;
}
return Strings.EMPTY_ARRAY;
}
@Override
public void onChange(License license, LicenseState state) {
synchronized (this) {
this.state = state;
}
}
@Override
protected void doStart() throws ElasticsearchException {
clientService.register(FEATURE_NAME, TRIAL_LICENSE_OPTIONS, expirationLoggers, acknowledgementCallback, new InternalListener(this));
clientService.register(this);
}
@Override
@ -104,33 +81,6 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
}
public boolean enabled() {
return enabled;
}
static TimeValue days(int days) {
return TimeValue.timeValueHours(days * 24);
}
static TimeValue minutes(int minutes) {
return TimeValue.timeValueMinutes(minutes);
}
class InternalListener implements LicensesClientService.Listener {
private final LicenseService service;
public InternalListener(LicenseService service) {
this.service = service;
}
@Override
public void onEnabled(License license) {
service.enabled = true;
}
@Override
public void onDisabled(License license) {
service.enabled = false;
}
return state != LicenseState.DISABLED;
}
}

View File

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

View File

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