From 1a7e842c15fcf8cb05c1884fdb04d44643abd0b2 Mon Sep 17 00:00:00 2001 From: Jay Modi Date: Mon, 20 Mar 2017 14:23:04 -0400 Subject: [PATCH] Make XPackFeatureSet#usage calls asynchronous (elastic/x-pack-elasticsearch#738) This commit makes the XPackFeatureSet#usage calls asynchronous. Previously these were synchronous calls that would execute a multi-search request from the currently elected master node in a blocking fashion. The multi-search request is now executed asynchronously. relates elastic/x-pack-elasticsearch#213 Original commit: elastic/x-pack-elasticsearch@a0cb9884428be616f4bd9d57ed3e29eb7c395a15 --- .../elasticsearch/xpack/XPackFeatureSet.java | 3 +- .../action/TransportXPackUsageAction.java | 51 ++++++++++-- .../xpack/graph/GraphFeatureSet.java | 5 +- .../xpack/ml/MachineLearningFeatureSet.java | 5 +- .../monitoring/MonitoringFeatureSet.java | 5 +- .../xpack/security/SecurityFeatureSet.java | 48 ++++++----- .../security/audit/AuditTrailService.java | 11 --- .../authz/store/CompositeRolesStore.java | 10 ++- .../authz/store/NativeRolesStore.java | 83 +++++++++---------- .../xpack/watcher/WatcherFeatureSet.java | 7 +- .../xpack/graph/GraphFeatureSetTests.java | 15 +++- .../ml/MachineLearningFeatureSetTests.java | 16 +++- .../monitoring/MonitoringFeatureSetTests.java | 6 +- .../security/SecurityFeatureSetTests.java | 73 +++++++++------- .../authc/esnative/NativeRealmIntegTests.java | 9 +- .../xpack/watcher/WatcherFeatureSetTests.java | 5 +- 16 files changed, 213 insertions(+), 139 deletions(-) diff --git a/plugin/src/main/java/org/elasticsearch/xpack/XPackFeatureSet.java b/plugin/src/main/java/org/elasticsearch/xpack/XPackFeatureSet.java index 221f34fd44f..6fdce28433d 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/XPackFeatureSet.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/XPackFeatureSet.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.io.stream.NamedWriteable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -26,7 +27,7 @@ public interface XPackFeatureSet { Map nativeCodeInfo(); - Usage usage(); + void usage(ActionListener listener); abstract class Usage implements ToXContentObject, NamedWriteable { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/action/TransportXPackUsageAction.java b/plugin/src/main/java/org/elasticsearch/xpack/action/TransportXPackUsageAction.java index 30c3a6db434..49723cc0ffc 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/action/TransportXPackUsageAction.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/action/TransportXPackUsageAction.java @@ -17,14 +17,20 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.XPackFeatureSet; +import org.elasticsearch.xpack.XPackFeatureSet.Usage; +import org.elasticsearch.xpack.common.IteratingActionListener; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.function.BiConsumer; public class TransportXPackUsageAction extends TransportMasterNodeAction { - private final Set featureSets; + private final List featureSets; @Inject public TransportXPackUsageAction(Settings settings, ThreadPool threadPool, TransportService transportService, @@ -32,7 +38,7 @@ public class TransportXPackUsageAction extends TransportMasterNodeAction featureSets) { super(settings, XPackUsageAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver, XPackUsageRequest::new); - this.featureSets = featureSets; + this.featureSets = Collections.unmodifiableList(new ArrayList<>(featureSets)); } @Override @@ -48,8 +54,43 @@ public class TransportXPackUsageAction extends TransportMasterNodeAction listener) throws Exception { - List usages = featureSets.stream().map(XPackFeatureSet::usage).collect(Collectors.toList()); - listener.onResponse(new XPackUsageResponse(usages)); + final ActionListener> usageActionListener = new ActionListener>() { + @Override + public void onResponse(List usages) { + listener.onResponse(new XPackUsageResponse(usages)); + } + + @Override + public void onFailure(Exception e) { + listener.onFailure(e); + } + }; + final AtomicReferenceArray featureSetUsages = new AtomicReferenceArray<>(featureSets.size()); + final AtomicInteger position = new AtomicInteger(0); + final BiConsumer>> consumer = (featureSet, iteratingListener) -> { + featureSet.usage(new ActionListener() { + @Override + public void onResponse(Usage usage) { + featureSetUsages.set(position.getAndIncrement(), usage); + iteratingListener.onResponse(null); // just send null back and keep iterating + } + + @Override + public void onFailure(Exception e) { + iteratingListener.onFailure(e); + } + }); + }; + IteratingActionListener, XPackFeatureSet> iteratingActionListener = + new IteratingActionListener<>(usageActionListener, consumer, featureSets, + threadPool.getThreadContext(), () -> { + final List usageList = new ArrayList<>(featureSetUsages.length()); + for (int i = 0; i < featureSetUsages.length(); i++) { + usageList.add(featureSetUsages.get(i)); + } + return usageList; + }); + iteratingActionListener.run(); } @Override diff --git a/plugin/src/main/java/org/elasticsearch/xpack/graph/GraphFeatureSet.java b/plugin/src/main/java/org/elasticsearch/xpack/graph/GraphFeatureSet.java index f4e74f179ab..f19c0081e1e 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/graph/GraphFeatureSet.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/graph/GraphFeatureSet.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.graph; import java.io.IOException; import java.util.Map; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; @@ -54,8 +55,8 @@ public class GraphFeatureSet implements XPackFeatureSet { } @Override - public XPackFeatureSet.Usage usage() { - return new Usage(available(), enabled()); + public void usage(ActionListener listener) { + listener.onResponse(new Usage(available(), enabled())); } public static class Usage extends XPackFeatureSet.Usage { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/ml/MachineLearningFeatureSet.java b/plugin/src/main/java/org/elasticsearch/xpack/ml/MachineLearningFeatureSet.java index 9bda37db307..e0015964c15 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/ml/MachineLearningFeatureSet.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/ml/MachineLearningFeatureSet.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.ml; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; @@ -77,8 +78,8 @@ public class MachineLearningFeatureSet implements XPackFeatureSet { } @Override - public XPackFeatureSet.Usage usage() { - return new Usage(available(), enabled()); + public void usage(ActionListener listener) { + listener.onResponse(new Usage(available(), enabled())); } public static class Usage extends XPackFeatureSet.Usage { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSet.java b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSet.java index 0e6aa22a5c6..2b11ffc87ce 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSet.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSet.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; @@ -60,8 +61,8 @@ public class MonitoringFeatureSet implements XPackFeatureSet { } @Override - public XPackFeatureSet.Usage usage() { - return new Usage(available(), enabled(), exportersUsage(exporters)); + public void usage(ActionListener listener) { + listener.onResponse(new Usage(available(), enabled(), exportersUsage(exporters))); } static Map exportersUsage(Exporters exporters) { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java b/plugin/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java index 44d531d4287..9124df22ba7 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java @@ -6,20 +6,23 @@ package org.elasticsearch.xpack.security; import java.io.IOException; +import java.nio.file.Files; import java.util.Collections; +import java.util.HashMap; import java.util.Map; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.env.Environment; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.xpack.XPackFeatureSet; import org.elasticsearch.xpack.XPackPlugin; import org.elasticsearch.xpack.XPackSettings; -import org.elasticsearch.xpack.security.audit.AuditTrailService; import org.elasticsearch.xpack.security.authc.Realms; import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore; import org.elasticsearch.xpack.security.crypto.CryptoService; @@ -33,8 +36,6 @@ import static org.elasticsearch.xpack.XPackSettings.HTTP_SSL_ENABLED; */ public class SecurityFeatureSet implements XPackFeatureSet { - private static final Map DISABLED_FEATURE_MAP = Collections.singletonMap("enabled", false); - private final Settings settings; private final boolean enabled; private final XPackLicenseState licenseState; @@ -44,23 +45,19 @@ public class SecurityFeatureSet implements XPackFeatureSet { private final CompositeRolesStore rolesStore; @Nullable private final IPFilter ipFilter; - @Nullable - private final AuditTrailService auditTrailService; - @Nullable - private final CryptoService cryptoService; + private final boolean systemKeyUsed; @Inject public SecurityFeatureSet(Settings settings, @Nullable XPackLicenseState licenseState, @Nullable Realms realms, @Nullable CompositeRolesStore rolesStore, @Nullable IPFilter ipFilter, - @Nullable AuditTrailService auditTrailService, @Nullable CryptoService cryptoService) { + Environment environment) { this.enabled = XPackSettings.SECURITY_ENABLED.get(settings); this.licenseState = licenseState; this.realms = realms; this.rolesStore = rolesStore; this.settings = settings; this.ipFilter = ipFilter; - this.auditTrailService = auditTrailService; - this.cryptoService = cryptoService; + this.systemKeyUsed = enabled && Files.exists(CryptoService.resolveSystemKey(environment)); } @Override @@ -89,16 +86,21 @@ public class SecurityFeatureSet implements XPackFeatureSet { } @Override - public XPackFeatureSet.Usage usage() { + public void usage(ActionListener listener) { Map realmsUsage = buildRealmsUsage(realms); - Map rolesStoreUsage = rolesStore == null ? Collections.emptyMap() : rolesStore.usageStats(); Map sslUsage = sslUsage(settings); - Map auditUsage = auditUsage(auditTrailService); + Map auditUsage = auditUsage(settings); Map ipFilterUsage = ipFilterUsage(ipFilter); - Map systemKeyUsage = systemKeyUsage(cryptoService); + Map systemKeyUsage = systemKeyUsage(); Map anonymousUsage = Collections.singletonMap("enabled", AnonymousUser.isAnonymousEnabled(settings)); - return new Usage(available(), enabled(), realmsUsage, rolesStoreUsage, sslUsage, auditUsage, ipFilterUsage, systemKeyUsage, - anonymousUsage); + final ActionListener> rolesStoreUsageListener = + ActionListener.wrap(rolesStoreUsage -> listener.onResponse(new Usage(available(), enabled(), realmsUsage, rolesStoreUsage, + sslUsage, auditUsage, ipFilterUsage, systemKeyUsage, anonymousUsage)),listener::onFailure); + if (rolesStore == null) { + rolesStoreUsageListener.onResponse(Collections.emptyMap()); + } else { + rolesStore.usageStats(rolesStoreUsageListener); + } } static Map buildRealmsUsage(Realms realms) { @@ -112,11 +114,11 @@ public class SecurityFeatureSet implements XPackFeatureSet { return Collections.singletonMap("http", Collections.singletonMap("enabled", HTTP_SSL_ENABLED.get(settings))); } - static Map auditUsage(@Nullable AuditTrailService auditTrailService) { - if (auditTrailService == null) { - return DISABLED_FEATURE_MAP; - } - return auditTrailService.usageStats(); + static Map auditUsage(Settings settings) { + Map map = new HashMap<>(2); + map.put("enabled", XPackSettings.AUDIT_ENABLED.get(settings)); + map.put("outputs", Security.AUDIT_OUTPUTS_SETTING.get(settings)); + return map; } static Map ipFilterUsage(@Nullable IPFilter ipFilter) { @@ -126,9 +128,9 @@ public class SecurityFeatureSet implements XPackFeatureSet { return ipFilter.usageStats(); } - static Map systemKeyUsage(CryptoService cryptoService) { + Map systemKeyUsage() { // we can piggy back on the encryption enabled method as it is only enabled if there is a system key - return Collections.singletonMap("enabled", cryptoService != null && cryptoService.isEncryptionEnabled()); + return Collections.singletonMap("enabled", systemKeyUsed); } public static class Usage extends XPackFeatureSet.Usage { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrailService.java b/plugin/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrailService.java index abade63faa8..615d86066a7 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrailService.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrailService.java @@ -7,17 +7,13 @@ package org.elasticsearch.xpack.security.audit; import java.net.InetAddress; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.transport.TransportMessage; -import org.elasticsearch.xpack.XPackSettings; -import org.elasticsearch.xpack.security.Security; import org.elasticsearch.xpack.security.authc.AuthenticationToken; import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule; import org.elasticsearch.xpack.security.user.User; @@ -220,11 +216,4 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail { } } } - - public Map usageStats() { - Map map = new HashMap<>(2); - map.put("enabled", XPackSettings.AUDIT_ENABLED.get(settings)); - map.put("outputs", Security.AUDIT_OUTPUTS_SETTING.get(settings)); - return map; - } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java index 0cb17f47079..9c99272942a 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java @@ -278,11 +278,13 @@ public class CompositeRolesStore extends AbstractComponent { negativeLookupCache.remove(role); } - public Map usageStats() { - Map usage = new HashMap<>(2); + public void usageStats(ActionListener> listener) { + final Map usage = new HashMap<>(2); usage.put("file", fileRolesStore.usageStats()); - usage.put("native", nativeRolesStore.usageStats()); - return usage; + nativeRolesStore.usageStats(ActionListener.wrap(map -> { + usage.put("native", map); + listener.onResponse(usage); + }, listener::onFailure)); } /** diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java index 2c707e8c892..5b54451d57d 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java @@ -16,7 +16,6 @@ import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.search.MultiSearchRequestBuilder; import org.elasticsearch.action.search.MultiSearchResponse; import org.elasticsearch.action.search.MultiSearchResponse.Item; import org.elasticsearch.action.search.SearchRequest; @@ -202,29 +201,20 @@ public class NativeRolesStore extends AbstractComponent { } } - public Map usageStats() { - boolean dls = false; - boolean fls = false; + public void usageStats(ActionListener> listener) { Map usageStats = new HashMap<>(); if (securityLifecycleService.securityIndexExists() == false) { usageStats.put("size", 0L); - usageStats.put("fls", fls); - usageStats.put("dls", dls); - return usageStats; - } - - // FIXME this needs to be async - long count = 0L; - // query for necessary information - if (fls == false || dls == false) { - MultiSearchRequestBuilder builder = client.prepareMultiSearch() + usageStats.put("fls", false); + usageStats.put("dls", false); + listener.onResponse(usageStats); + } else { + client.prepareMultiSearch() .add(client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME) .setTypes(ROLE_DOC_TYPE) .setQuery(QueryBuilders.matchAllQuery()) - .setSize(0)); - - if (fls == false) { - builder.add(client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME) + .setSize(0)) + .add(client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME) .setTypes(ROLE_DOC_TYPE) .setQuery(QueryBuilders.boolQuery() .should(existsQuery("indices.field_security.grant")) @@ -232,41 +222,42 @@ public class NativeRolesStore extends AbstractComponent { // for backwardscompat with 2.x .should(existsQuery("indices.fields"))) .setSize(0) - .setTerminateAfter(1)); - } - - if (dls == false) { - builder.add(client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME) + .setTerminateAfter(1)) + .add(client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME) .setTypes(ROLE_DOC_TYPE) .setQuery(existsQuery("indices.query")) .setSize(0) - .setTerminateAfter(1)); - } + .setTerminateAfter(1)) + .execute(new ActionListener() { + @Override + public void onResponse(MultiSearchResponse items) { + Item[] responses = items.getResponses(); + if (responses[0].isFailure()) { + usageStats.put("size", 0); + } else { + usageStats.put("size", responses[0].getResponse().getHits().getTotalHits()); + } - MultiSearchResponse multiSearchResponse = builder.get(); - int pos = 0; - Item[] responses = multiSearchResponse.getResponses(); - if (responses[pos].isFailure() == false) { - count = responses[pos].getResponse().getHits().getTotalHits(); - } + if (responses[1].isFailure()) { + usageStats.put("fls", false); + } else { + usageStats.put("fls", responses[1].getResponse().getHits().getTotalHits() > 0L); + } - if (fls == false) { - if (responses[++pos].isFailure() == false) { - fls = responses[pos].getResponse().getHits().getTotalHits() > 0L; - } - } + if (responses[2].isFailure()) { + usageStats.put("dls", false); + } else { + usageStats.put("dls", responses[2].getResponse().getHits().getTotalHits() > 0L); + } + listener.onResponse(usageStats); + } - if (dls == false) { - if (responses[++pos].isFailure() == false) { - dls = responses[pos].getResponse().getHits().getTotalHits() > 0L; - } - } + @Override + public void onFailure(Exception e) { + listener.onFailure(e); + } + }); } - - usageStats.put("size", count); - usageStats.put("fls", fls); - usageStats.put("dls", dls); - return usageStats; } private void getRoleDescriptor(final String roleId, ActionListener roleActionListener) { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java index e7468c2a4ae..d9d4bcf26e0 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.util.Collections; import java.util.Map; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; @@ -17,6 +18,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.xpack.XPackFeatureSet; +import org.elasticsearch.xpack.XPackFeatureSet.Usage; import org.elasticsearch.xpack.XPackPlugin; import org.elasticsearch.xpack.XPackSettings; @@ -59,8 +61,9 @@ public class WatcherFeatureSet implements XPackFeatureSet { } @Override - public XPackFeatureSet.Usage usage() { - return new Usage(available(), enabled(), watcherService != null ? watcherService.usageStats() : Collections.emptyMap()); + public void usage(ActionListener listener) { + listener.onResponse( + new Usage(available(), enabled(), watcherService != null ? watcherService.usageStats() : Collections.emptyMap())); } public static class Usage extends XPackFeatureSet.Usage { diff --git a/plugin/src/test/java/org/elasticsearch/xpack/graph/GraphFeatureSetTests.java b/plugin/src/test/java/org/elasticsearch/xpack/graph/GraphFeatureSetTests.java index 4a4fda5d63e..e04f770bcef 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/graph/GraphFeatureSetTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/graph/GraphFeatureSetTests.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.graph; +import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.license.XPackLicenseState; @@ -30,10 +31,13 @@ public class GraphFeatureSetTests extends ESTestCase { boolean available = randomBoolean(); when(licenseState.isGraphAllowed()).thenReturn(available); assertThat(featureSet.available(), is(available)); - assertThat(featureSet.usage().available(), is(available)); + PlainActionFuture future = new PlainActionFuture<>(); + featureSet.usage(future); + XPackFeatureSet.Usage usage = future.get(); + assertThat(usage.available(), is(available)); BytesStreamOutput out = new BytesStreamOutput(); - featureSet.usage().writeTo(out); + usage.writeTo(out); XPackFeatureSet.Usage serializedUsage = new GraphFeatureSet.Usage(out.bytes().streamInput()); assertThat(serializedUsage.available(), is(available)); } @@ -50,10 +54,13 @@ public class GraphFeatureSetTests extends ESTestCase { } GraphFeatureSet featureSet = new GraphFeatureSet(settings.build(), licenseState); assertThat(featureSet.enabled(), is(enabled)); - assertThat(featureSet.usage().enabled(), is(enabled)); + PlainActionFuture future = new PlainActionFuture<>(); + featureSet.usage(future); + XPackFeatureSet.Usage usage = future.get(); + assertThat(usage.enabled(), is(enabled)); BytesStreamOutput out = new BytesStreamOutput(); - featureSet.usage().writeTo(out); + usage.writeTo(out); XPackFeatureSet.Usage serializedUsage = new GraphFeatureSet.Usage(out.bytes().streamInput()); assertThat(serializedUsage.enabled(), is(enabled)); } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/ml/MachineLearningFeatureSetTests.java b/plugin/src/test/java/org/elasticsearch/xpack/ml/MachineLearningFeatureSetTests.java index dec48c6e134..686d48b0b5f 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/ml/MachineLearningFeatureSetTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/ml/MachineLearningFeatureSetTests.java @@ -5,11 +5,13 @@ */ package org.elasticsearch.xpack.ml; +import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.XPackFeatureSet; +import org.elasticsearch.xpack.XPackFeatureSet.Usage; import org.junit.Before; import static org.hamcrest.core.Is.is; @@ -30,10 +32,13 @@ public class MachineLearningFeatureSetTests extends ESTestCase { boolean available = randomBoolean(); when(licenseState.isMachineLearningAllowed()).thenReturn(available); assertThat(featureSet.available(), is(available)); - assertThat(featureSet.usage().available(), is(available)); + PlainActionFuture future = new PlainActionFuture<>(); + featureSet.usage(future); + XPackFeatureSet.Usage usage = future.get(); + assertThat(usage.available(), is(available)); BytesStreamOutput out = new BytesStreamOutput(); - featureSet.usage().writeTo(out); + usage.writeTo(out); XPackFeatureSet.Usage serializedUsage = new MachineLearningFeatureSet.Usage(out.bytes().streamInput()); assertThat(serializedUsage.available(), is(available)); } @@ -49,10 +54,13 @@ public class MachineLearningFeatureSetTests extends ESTestCase { boolean expected = enabled || useDefault; MachineLearningFeatureSet featureSet = new MachineLearningFeatureSet(settings.build(), licenseState); assertThat(featureSet.enabled(), is(expected)); - assertThat(featureSet.usage().enabled(), is(expected)); + PlainActionFuture future = new PlainActionFuture<>(); + featureSet.usage(future); + XPackFeatureSet.Usage usage = future.get(); + assertThat(usage.enabled(), is(expected)); BytesStreamOutput out = new BytesStreamOutput(); - featureSet.usage().writeTo(out); + usage.writeTo(out); XPackFeatureSet.Usage serializedUsage = new MachineLearningFeatureSet.Usage(out.bytes().streamInput()); assertThat(serializedUsage.enabled(), is(expected)); } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSetTests.java b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSetTests.java index 5927278f174..7999fad14c4 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSetTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSetTests.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.monitoring; +import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.ToXContent; @@ -12,6 +13,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.XPackFeatureSet; +import org.elasticsearch.xpack.XPackFeatureSet.Usage; import org.elasticsearch.xpack.monitoring.exporter.Exporter; import org.elasticsearch.xpack.monitoring.exporter.Exporters; import org.elasticsearch.xpack.monitoring.exporter.http.HttpExporter; @@ -95,7 +97,9 @@ public class MonitoringFeatureSetTests extends ESTestCase { when(exporters.iterator()).thenReturn(exporterList.iterator()); MonitoringFeatureSet featureSet = new MonitoringFeatureSet(Settings.EMPTY, licenseState, exporters); - XPackFeatureSet.Usage monitoringUsage = featureSet.usage(); + PlainActionFuture future = new PlainActionFuture<>(); + featureSet.usage(future); + XPackFeatureSet.Usage monitoringUsage = future.get(); BytesStreamOutput out = new BytesStreamOutput(); monitoringUsage.writeTo(out); XPackFeatureSet.Usage serializedUsage = new MonitoringFeatureSet.Usage(out.bytes().streamInput()); diff --git a/plugin/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java b/plugin/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java index c407e5d333b..5e73e619cb6 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java @@ -5,16 +5,20 @@ */ package org.elasticsearch.xpack.security; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.env.Environment; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.XPackFeatureSet; import org.elasticsearch.xpack.XPackPlugin; +import org.elasticsearch.xpack.XPackSettings; import org.elasticsearch.xpack.security.audit.AuditTrailService; import org.elasticsearch.xpack.security.authc.Realms; import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore; @@ -24,6 +28,10 @@ import org.elasticsearch.xpack.security.user.AnonymousUser; import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource; import org.junit.Before; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -34,12 +42,15 @@ import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.core.Is.is; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class SecurityFeatureSetTests extends ESTestCase { private Settings settings; + private Environment environment; private XPackLicenseState licenseState; private Realms realms; private IPFilter ipFilter; @@ -50,17 +61,16 @@ public class SecurityFeatureSetTests extends ESTestCase { @Before public void init() throws Exception { settings = Settings.builder().put("path.home", createTempDir()).build(); + environment = new Environment(settings); licenseState = mock(XPackLicenseState.class); realms = mock(Realms.class); ipFilter = mock(IPFilter.class); rolesStore = mock(CompositeRolesStore.class); - auditTrail = mock(AuditTrailService.class); - cryptoService = mock(CryptoService.class); } public void testAvailable() throws Exception { SecurityFeatureSet featureSet = new SecurityFeatureSet(settings, licenseState, realms, rolesStore, - ipFilter, auditTrail, cryptoService); + ipFilter, environment); boolean available = randomBoolean(); when(licenseState.isAuthAllowed()).thenReturn(available); assertThat(featureSet.available(), is(available)); @@ -73,26 +83,26 @@ public class SecurityFeatureSetTests extends ESTestCase { .put("xpack.security.enabled", enabled) .build(); SecurityFeatureSet featureSet = new SecurityFeatureSet(settings, licenseState, realms, rolesStore, - ipFilter, auditTrail, cryptoService); + ipFilter, environment); assertThat(featureSet.enabled(), is(enabled)); } public void testEnabledDefault() throws Exception { SecurityFeatureSet featureSet = new SecurityFeatureSet(settings, licenseState, realms, rolesStore, - ipFilter, auditTrail, cryptoService); + ipFilter, environment); assertThat(featureSet.enabled(), is(true)); } - public void testSystemKeyUsageEnabledByCryptoService() { + public void testSystemKeyUsageEnabledByCryptoService() throws IOException { final boolean enabled = randomBoolean(); - - when(cryptoService.isEncryptionEnabled()).thenReturn(enabled); - - assertThat(SecurityFeatureSet.systemKeyUsage(cryptoService), hasEntry("enabled", enabled)); - } - - public void testSystemKeyUsageNotEnabledIfNull() { - assertThat(SecurityFeatureSet.systemKeyUsage(null), hasEntry("enabled", false)); + if (enabled) { + Path path = CryptoService.resolveSystemKey(environment); + Files.createDirectories(path.getParent()); + Files.write(path, new byte[0]); + } + SecurityFeatureSet featureSet = new SecurityFeatureSet(settings, licenseState, realms, rolesStore, + ipFilter, environment); + assertThat(featureSet.systemKeyUsage(), hasEntry("enabled", enabled)); } public void testUsage() throws Exception { @@ -108,13 +118,9 @@ public class SecurityFeatureSetTests extends ESTestCase { final boolean httpSSLEnabled = randomBoolean(); settings.put("xpack.security.http.ssl.enabled", httpSSLEnabled); final boolean auditingEnabled = randomBoolean(); + settings.put(XPackSettings.AUDIT_ENABLED.getKey(), auditingEnabled); final String[] auditOutputs = randomFrom(new String[] {"logfile"}, new String[] {"index"}, new String[] {"logfile", "index"}); - when(auditTrail.usageStats()) - .thenReturn(MapBuilder.newMapBuilder() - .put("enabled", auditingEnabled) - .put("outputs", auditOutputs) - .map()); - + settings.putArray(Security.AUDIT_OUTPUTS_SETTING.getKey(), auditOutputs); final boolean httpIpFilterEnabled = randomBoolean(); final boolean transportIPFilterEnabled = randomBoolean(); when(ipFilter.usageStats()) @@ -125,13 +131,21 @@ public class SecurityFeatureSetTests extends ESTestCase { final boolean rolesStoreEnabled = randomBoolean(); - if (rolesStoreEnabled) { - when(rolesStore.usageStats()).thenReturn(Collections.singletonMap("count", 1)); - } else { - when(rolesStore.usageStats()).thenReturn(Collections.emptyMap()); - } + doAnswer(invocationOnMock -> { + ActionListener> listener = (ActionListener>) invocationOnMock.getArguments()[0]; + if (rolesStoreEnabled) { + listener.onResponse(Collections.singletonMap("count", 1)); + } else { + listener.onResponse(Collections.emptyMap()); + } + return Void.TYPE; + }).when(rolesStore).usageStats(any(ActionListener.class)); final boolean useSystemKey = randomBoolean(); - when(cryptoService.isEncryptionEnabled()).thenReturn(useSystemKey); + if (useSystemKey) { + Path path = CryptoService.resolveSystemKey(environment); + Files.createDirectories(path.getParent()); + Files.write(path, new byte[0], StandardOpenOption.CREATE_NEW); + } Map realmsUsageStats = new HashMap<>(); for (int i = 0; i < 5; i++) { @@ -148,9 +162,10 @@ public class SecurityFeatureSetTests extends ESTestCase { settings.put(AnonymousUser.ROLES_SETTING.getKey(), "foo"); } - SecurityFeatureSet featureSet = new SecurityFeatureSet(settings.build(), licenseState, realms, rolesStore, - ipFilter, auditTrail, cryptoService); - XPackFeatureSet.Usage securityUsage = featureSet.usage(); + SecurityFeatureSet featureSet = new SecurityFeatureSet(settings.build(), licenseState, realms, rolesStore, ipFilter, environment); + PlainActionFuture future = new PlainActionFuture<>(); + featureSet.usage(future); + XPackFeatureSet.Usage securityUsage = future.get(); BytesStreamOutput out = new BytesStreamOutput(); securityUsage.writeTo(out); XPackFeatureSet.Usage serializedUsage = new SecurityFeatureSet.Usage(out.bytes().streamInput()); diff --git a/plugin/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java b/plugin/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java index 971009ed056..f634eae1490 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.client.Client; import org.elasticsearch.common.Strings; import org.elasticsearch.common.ValidationException; @@ -599,7 +600,9 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase { public void testRolesUsageStats() throws Exception { NativeRolesStore rolesStore = internalCluster().getInstance(NativeRolesStore.class); long roles = anonymousEnabled && roleExists ? 1L: 0L; - Map usage = rolesStore.usageStats(); + PlainActionFuture> future = new PlainActionFuture<>(); + rolesStore.usageStats(future); + Map usage = future.get(); assertEquals(roles, usage.get("size")); assertThat(usage.get("fls"), is(false)); assertThat(usage.get("dls"), is(false)); @@ -644,7 +647,9 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase { client.prepareClearRolesCache().get(); - usage = rolesStore.usageStats(); + future = new PlainActionFuture<>(); + rolesStore.usageStats(future); + usage = future.get(); assertThat(usage.get("size"), is(roles)); assertThat(usage.get("fls"), is(fls)); assertThat(usage.get("dls"), is(dls)); diff --git a/plugin/src/test/java/org/elasticsearch/xpack/watcher/WatcherFeatureSetTests.java b/plugin/src/test/java/org/elasticsearch/xpack/watcher/WatcherFeatureSetTests.java index a62f4e39fe6..756a2370107 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/watcher/WatcherFeatureSetTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/watcher/WatcherFeatureSetTests.java @@ -9,6 +9,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.ToXContent; @@ -63,7 +64,9 @@ public class WatcherFeatureSetTests extends ESTestCase { statsMap.put("foo", "bar"); when(watcherService.usageStats()).thenReturn(statsMap); - XPackFeatureSet.Usage watcherUsage = new WatcherFeatureSet(Settings.EMPTY, licenseState, watcherService).usage(); + PlainActionFuture future = new PlainActionFuture<>(); + new WatcherFeatureSet(Settings.EMPTY, licenseState, watcherService).usage(future); + XPackFeatureSet.Usage watcherUsage = future.get(); BytesStreamOutput out = new BytesStreamOutput(); watcherUsage.writeTo(out); XPackFeatureSet.Usage serializedUsage = new WatcherFeatureSet.Usage(out.bytes().streamInput());