diff --git a/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/LicenseVerifier.java b/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/LicenseVerifier.java index 13f34945542..49f8c0c2b14 100644 --- a/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/LicenseVerifier.java +++ b/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/LicenseVerifier.java @@ -7,12 +7,15 @@ package org.elasticsearch.license.core; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefIterator; +import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.nio.ByteBuffer; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -72,4 +75,16 @@ public class LicenseVerifier { } } } + + public static boolean verifyLicense(final License license) { + final byte[] publicKeyBytes; + try (InputStream is = LicenseVerifier.class.getResourceAsStream("/public.key")) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Streams.copy(is, out); + publicKeyBytes = out.toByteArray(); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + return verifyLicense(license, publicKeyBytes); + } } diff --git a/elasticsearch/qa/audit-tests/src/test/java/org/elasticsearch/xpack/security/audit/IndexAuditIT.java b/elasticsearch/qa/audit-tests/src/test/java/org/elasticsearch/xpack/security/audit/IndexAuditIT.java index 97b0dd672f0..b869a08d2b7 100644 --- a/elasticsearch/qa/audit-tests/src/test/java/org/elasticsearch/xpack/security/audit/IndexAuditIT.java +++ b/elasticsearch/qa/audit-tests/src/test/java/org/elasticsearch/xpack/security/audit/IndexAuditIT.java @@ -36,8 +36,9 @@ public class IndexAuditIT extends ESIntegTestCase { private static final String USER = "test_user"; private static final String PASS = "changeme"; + @AwaitsFix(bugUrl = "https://github.com/elastic/x-plugins/issues/2354") public void testShieldIndexAuditTrailWorking() throws Exception { - try (Response response = getRestClient().performRequest("GET", "/", Collections.emptyMap(), null, + try (Response response = getRestClient().performRequest("GET", "/", new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(USER, new SecuredString(PASS.toCharArray()))))) { assertThat(response.getStatusLine().getStatusCode(), is(200)); diff --git a/elasticsearch/qa/security-example-realm/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java b/elasticsearch/qa/security-example-realm/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java index 2721747ea7a..1780052b191 100644 --- a/elasticsearch/qa/security-example-realm/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java +++ b/elasticsearch/qa/security-example-realm/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java @@ -47,7 +47,7 @@ public class CustomRealmIT extends ESIntegTestCase { public void testHttpConnectionWithNoAuthentication() throws Exception { try { - getRestClient().performRequest("GET", "/", Collections.emptyMap(), null); + getRestClient().performRequest("GET", "/"); fail("request should have failed"); } catch(ResponseException e) { Response response = e.getResponse(); @@ -58,7 +58,7 @@ public class CustomRealmIT extends ESIntegTestCase { } public void testHttpAuthentication() throws Exception { - try (Response response = getRestClient().performRequest("GET", "/", Collections.emptyMap(), null, + try (Response response = getRestClient().performRequest("GET", "/", new BasicHeader(CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER), new BasicHeader(CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW))) { assertThat(response.getStatusLine().getStatusCode(), is(200)); diff --git a/elasticsearch/qa/smoke-test-monitoring-with-security/build.gradle b/elasticsearch/qa/smoke-test-monitoring-with-security/build.gradle index 5c9b4d832d6..da9d76bf26c 100644 --- a/elasticsearch/qa/smoke-test-monitoring-with-security/build.gradle +++ b/elasticsearch/qa/smoke-test-monitoring-with-security/build.gradle @@ -18,7 +18,7 @@ subprojects { cluster { systemProperty 'es.logger.level', 'TRACE' plugin 'x-pack', project(':x-plugins:elasticsearch:x-pack') - setting 'xpack.monitoring.agent.interval', '3s' + setting 'xpack.monitoring.collection.interval', '3s' extraConfigFile 'x-pack/roles.yml', '../roles.yml' setupCommand 'setupTestAdminUser', 'bin/x-pack/users', 'useradd', 'test_admin', '-p', 'changeme', '-r', 'superuser' diff --git a/elasticsearch/qa/smoke-test-plugins-ssl/build.gradle b/elasticsearch/qa/smoke-test-plugins-ssl/build.gradle index e2c6ace1b8b..fa3c5c588d3 100644 --- a/elasticsearch/qa/smoke-test-plugins-ssl/build.gradle +++ b/elasticsearch/qa/smoke-test-plugins-ssl/build.gradle @@ -153,13 +153,13 @@ project.rootProject.subprojects.findAll { it.path.startsWith(':plugins:') }.each integTest { cluster { - setting 'xpack.monitoring.agent.interval', '3s' - setting 'xpack.monitoring.agent.exporters._http.type', 'http' - setting 'xpack.monitoring.agent.exporters._http.enabled', 'false' - setting 'xpack.monitoring.agent.exporters._http.ssl.truststore.path', clientKeyStore.name - setting 'xpack.monitoring.agent.exporters._http.ssl.truststore.password', 'keypass' - setting 'xpack.monitoring.agent.exporters._http.auth.username', 'monitoring_agent' - setting 'xpack.monitoring.agent.exporters._http.auth.password', 'changeme' + setting 'xpack.monitoring.collection.interval', '3s' + setting 'xpack.monitoring.collection.exporters._http.type', 'http' + setting 'xpack.monitoring.collection.exporters._http.enabled', 'false' + setting 'xpack.monitoring.collection.exporters._http.ssl.truststore.path', clientKeyStore.name + setting 'xpack.monitoring.collection.exporters._http.ssl.truststore.password', 'keypass' + setting 'xpack.monitoring.collection.exporters._http.auth.username', 'monitoring_agent' + setting 'xpack.monitoring.collection.exporters._http.auth.password', 'changeme' setting 'xpack.security.transport.ssl.enabled', 'true' setting 'xpack.security.http.ssl.enabled', 'true' diff --git a/elasticsearch/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java b/elasticsearch/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java index 229d84bcae2..67be47776af 100644 --- a/elasticsearch/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java +++ b/elasticsearch/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java @@ -73,8 +73,8 @@ public class SmokeTestMonitoringWithSecurityIT extends ESIntegTestCase { URI uri = new URI("https", null, httpAddress.getHostString(), httpAddress.getPort(), "/", null, null); Settings exporterSettings = Settings.builder() - .put("xpack.monitoring.agent.exporters._http.enabled", true) - .put("xpack.monitoring.agent.exporters._http.host", uri.toString()) + .put("xpack.monitoring.collection.exporters._http.enabled", true) + .put("xpack.monitoring.collection.exporters._http.host", uri.toString()) .build(); assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(exporterSettings)); } @@ -82,8 +82,8 @@ public class SmokeTestMonitoringWithSecurityIT extends ESIntegTestCase { @After public void disableExporter() { Settings exporterSettings = Settings.builder() - .putNull("xpack.monitoring.agent.exporters._http.enabled") - .putNull("xpack.monitoring.agent.exporters._http.host") + .putNull("xpack.monitoring.collection.exporters._http.enabled") + .putNull("xpack.monitoring.collection.exporters._http.host") .build(); assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(exporterSettings)); } diff --git a/elasticsearch/qa/smoke-test-plugins-ssl/src/test/resources/rest-api-spec/test/smoke_test_plugins_ssl/20_settings_filter.yaml b/elasticsearch/qa/smoke-test-plugins-ssl/src/test/resources/rest-api-spec/test/smoke_test_plugins_ssl/20_settings_filter.yaml index b1b93edc116..a96b8b45786 100644 --- a/elasticsearch/qa/smoke-test-plugins-ssl/src/test/resources/rest-api-spec/test/smoke_test_plugins_ssl/20_settings_filter.yaml +++ b/elasticsearch/qa/smoke-test-plugins-ssl/src/test/resources/rest-api-spec/test/smoke_test_plugins_ssl/20_settings_filter.yaml @@ -11,9 +11,9 @@ metric: [ settings ] - is_true: nodes - - is_true: nodes.$master.settings.xpack.monitoring.agent.exporters._http.type + - is_true: nodes.$master.settings.xpack.monitoring.collection.exporters._http.type - - is_false: nodes.$master.settings.xpack.monitoring.agent.exporters._http.auth.username - - is_false: nodes.$master.settings.xpack.monitoring.agent.exporters._http.auth.password - - is_false: nodes.$master.settings.xpack.monitoring.agent.exporters._http.ssl.truststore.path - - is_false: nodes.$master.settings.xpack.monitoring.agent.exporters._http.ssl.truststore.password + - is_false: nodes.$master.settings.xpack.monitoring.collection.exporters._http.auth.username + - is_false: nodes.$master.settings.xpack.monitoring.collection.exporters._http.auth.password + - is_false: nodes.$master.settings.xpack.monitoring.collection.exporters._http.ssl.truststore.path + - is_false: nodes.$master.settings.xpack.monitoring.collection.exporters._http.ssl.truststore.password diff --git a/elasticsearch/x-pack/build.gradle b/elasticsearch/x-pack/build.gradle index 79e4af30e96..cdb38bf8425 100644 --- a/elasticsearch/x-pack/build.gradle +++ b/elasticsearch/x-pack/build.gradle @@ -133,7 +133,7 @@ integTest { // TODO: fix this rest test to not depend on a hardcoded port! systemProperty 'tests.rest.blacklist', 'getting_started/10_monitor_cluster_health/*,bulk/10_basic/*' cluster { - setting 'xpack.monitoring.agent.interval', '3s' + setting 'xpack.monitoring.collection.interval', '3s' waitCondition = { NodeInfo node, AntBuilder ant -> File tmpFile = new File(node.cwd, 'wait.success') for (int i = 0; i < 10; i++) { diff --git a/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/Graph.java b/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/Graph.java index bac749c7836..9bc973d3219 100644 --- a/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/Graph.java +++ b/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/Graph.java @@ -42,12 +42,12 @@ public class Graph extends Plugin implements ActionPlugin { return XPackPlugin.featureEnabled(settings, NAME, true); } - public Collection nodeModules() { + public Collection createGuiceModules() { return Collections.singletonList(new GraphModule(enabled, transportClientMode)); } @Override - public Collection> nodeServices() { + public Collection> getGuiceServiceClasses() { if (enabled == false|| transportClientMode) { return Collections.emptyList(); } diff --git a/elasticsearch/x-pack/graph/src/test/resources/rest-api-spec/api/graph.explore.json b/elasticsearch/x-pack/graph/src/test/resources/rest-api-spec/api/xpack.graph.explore.json similarity index 93% rename from elasticsearch/x-pack/graph/src/test/resources/rest-api-spec/api/graph.explore.json rename to elasticsearch/x-pack/graph/src/test/resources/rest-api-spec/api/xpack.graph.explore.json index 63768a7e231..ee4b05a8af0 100644 --- a/elasticsearch/x-pack/graph/src/test/resources/rest-api-spec/api/graph.explore.json +++ b/elasticsearch/x-pack/graph/src/test/resources/rest-api-spec/api/xpack.graph.explore.json @@ -1,10 +1,10 @@ { - "graph.explore": { + "xpack.graph.explore": { "documentation": "https://www.elastic.co/guide/en/graph/current/explore.html", "methods": ["GET", "POST"], "url": { "path": "/{index}/_xpack/graph/_explore", - "paths": ["/{index}/_xpack/graph/_explore", "/{index}/{type}/_xpack/graph/_explore"], + "paths": ["/{index}/_xpack/graph/_explore", "/{index}/{type}/_xpack/graph/_explore"], "parts" : { "index": { "type" : "list", @@ -23,7 +23,7 @@ "timeout": { "type" : "time", "description" : "Explicit operation timeout" - } + } } }, "body": { diff --git a/elasticsearch/x-pack/graph/src/test/resources/rest-api-spec/test/graph/10_basic.yaml b/elasticsearch/x-pack/graph/src/test/resources/rest-api-spec/test/graph/10_basic.yaml index f4a748a17a2..0ef7a255472 100644 --- a/elasticsearch/x-pack/graph/src/test/resources/rest-api-spec/test/graph/10_basic.yaml +++ b/elasticsearch/x-pack/graph/src/test/resources/rest-api-spec/test/graph/10_basic.yaml @@ -1,19 +1,21 @@ +--- +setup: +- do: + indices.create: + index: test_1 + body: + settings: + index: + number_of_shards: 1 + number_of_replicas: 0 + mappings: + test: + properties: + keys: + type : integer + --- "Test basic graph explore": - - do: - indices.create: - index: test_1 - body: - settings: - index: - number_of_replicas: 0 - number_of_shards: 1 - mappings: - test: - properties: - keys: - type : "integer" - - do: index: index: test_1 @@ -37,7 +39,7 @@ wait_for_status: green - do: - graph.explore: + xpack.graph.explore: index: test_1 type: test body: {"query": {"match": {"keys": 1}},"controls":{"use_significance":false},"vertices":[{"field": "keys","min_doc_count": 1}]} diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseResponse.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseResponse.java index 2803389b12a..ea0870924d4 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseResponse.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseResponse.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.license.plugin.core.LicensesStatus; import java.io.IOException; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -26,6 +27,10 @@ public class PutLicenseResponse extends AcknowledgedResponse implements ToXConte PutLicenseResponse() { } + public PutLicenseResponse(boolean acknowledged, LicensesStatus status) { + this(acknowledged, status, null, Collections.emptyMap()); + } + public PutLicenseResponse(boolean acknowledged, LicensesStatus status, String acknowledgeHeader, Map acknowledgeMessages) { super(acknowledged); diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/put/TransportPutLicenseAction.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/put/TransportPutLicenseAction.java index bbcb0becce8..d527be2cf0c 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/put/TransportPutLicenseAction.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/put/TransportPutLicenseAction.java @@ -20,8 +20,6 @@ import org.elasticsearch.license.plugin.core.LicensesService; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; -import static org.elasticsearch.license.plugin.core.LicensesService.LicensesUpdateResponse; - public class TransportPutLicenseAction extends TransportMasterNodeAction { private final LicensesService licensesService; @@ -53,18 +51,7 @@ public class TransportPutLicenseAction extends TransportMasterNodeAction listener) throws ElasticsearchException { - licensesService.registerLicense(request, new ActionListener() { - @Override - public void onResponse(LicensesUpdateResponse licensesUpdateResponse) { - listener.onResponse(new PutLicenseResponse(licensesUpdateResponse.isAcknowledged(), licensesUpdateResponse.status(), - licensesUpdateResponse.acknowledgementHeader(), licensesUpdateResponse.acknowledgeMessages())); - } - - @Override - public void onFailure(Exception e) { - listener.onFailure(e); - } - }); + licensesService.registerLicense(request, listener); } } diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/ExpirationCallback.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/ExpirationCallback.java new file mode 100644 index 00000000000..4166ce3d423 --- /dev/null +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/ExpirationCallback.java @@ -0,0 +1,174 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.plugin.core; + +import org.elasticsearch.common.logging.LoggerMessageFormat; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.license.core.License; +import org.elasticsearch.xpack.scheduler.SchedulerEngine; + +import java.util.UUID; + +public abstract class ExpirationCallback { + + static final String EXPIRATION_JOB_PREFIX = ".license_expiration_job_"; + + public enum Orientation {PRE, POST} + + /** + * Callback that is triggered every frequency when + * current time is between max and min + * before license expiry. + */ + public abstract static class Pre extends ExpirationCallback { + + /** + * Callback schedule prior to license expiry + * + * @param min latest relative time to execute before license expiry + * @param max earliest relative time to execute before license expiry + * @param frequency interval between execution + */ + public Pre(TimeValue min, TimeValue max, TimeValue frequency) { + super(Orientation.PRE, min, max, frequency); + } + } + + /** + * Callback that is triggered every frequency when + * current time is between min and max + * after license expiry. + */ + public abstract static class Post extends ExpirationCallback { + + /** + * Callback schedule after license expiry + * + * @param min earliest relative time to execute after license expiry + * @param max latest relative time to execute after license expiry + * @param frequency interval between execution + */ + public Post(TimeValue min, TimeValue max, TimeValue frequency) { + super(Orientation.POST, min, max, frequency); + } + } + + private final String id; + private final Orientation orientation; + private final long min; + private final long max; + private final long frequency; + + private ExpirationCallback(Orientation orientation, TimeValue min, TimeValue max, TimeValue frequency) { + this.orientation = orientation; + this.min = (min == null) ? 0 : min.getMillis(); + this.max = (max == null) ? Long.MAX_VALUE : max.getMillis(); + this.frequency = frequency.getMillis(); + this.id = String.join("", EXPIRATION_JOB_PREFIX, UUID.randomUUID().toString()); + } + + public final String getId() { + return id; + } + + public final long getFrequency() { + return frequency; + } + + /** + * Calculates the delay for the next trigger time. When now is in a + * valid time bracket with respect to expirationDate, the delay is 0. + * When now is before the time bracket, than delay to the start of the + * time bracket and when now is passed the valid time bracket, the delay + * is null + * @param expirationDate license expiry date in milliseconds + * @param now current time in milliseconds + * @return time delay + */ + final TimeValue delay(long expirationDate, long now) { + final TimeValue delay; + switch (orientation) { + case PRE: + if (expirationDate >= now) { + // license not yet expired + long preExpiryDuration = expirationDate - now; + if (preExpiryDuration > max) { + // license duration is longer than maximum duration, delay it to the first match time + delay = TimeValue.timeValueMillis(preExpiryDuration - max); + } else if (preExpiryDuration <= max && preExpiryDuration >= min) { + // no delay in valid time bracket + delay = TimeValue.timeValueMillis(0); + } else { + // passed last match time + delay = null; + } + } else { + // invalid after license expiry + delay = null; + } + break; + case POST: + if (expirationDate >= now) { + // license not yet expired, delay it to the first match time + delay = TimeValue.timeValueMillis(expirationDate - now + min); + } else { + // license has expired + long expiredDuration = now - expirationDate; + if (expiredDuration < min) { + // license expiry duration is shorter than minimum duration, delay it to the first match time + delay = TimeValue.timeValueMillis(min - expiredDuration); + } else if (expiredDuration >= min && expiredDuration <= max) { + // no delay in valid time bracket + delay = TimeValue.timeValueMillis(0); + } else { + // passed last match time + delay = null; + } + } + break; + default: + throw new IllegalStateException("orientation [" + orientation + "] unknown"); + } + return delay; + } + + /** + * {@link SchedulerEngine.Schedule#nextScheduledTimeAfter(long, long)} with respect to + * license expiry date + */ + public final long nextScheduledTimeForExpiry(long expiryDate, long startTime, long time) { + TimeValue delay = delay(expiryDate, time); + if (delay != null) { + long delayInMillis = delay.getMillis(); + if (delayInMillis == 0L) { + if (startTime == time) { + // initial trigger and in time bracket, schedule immediately + return time; + } else { + // in time bracket, add frequency + return time + frequency; + } + } else { + // not in time bracket + return time + delayInMillis; + } + } + return -1; + } + + /** + * Code to execute when the expiry callback is triggered in a valid + * time bracket + * @param license license to operate on + */ + public abstract void on(License license); + + public final String toString() { + return LoggerMessageFormat.format(null, "ExpirationCallback:(orientation [{}], min [{}], max [{}], freq [{}])", + orientation.name(), TimeValue.timeValueMillis(min), TimeValue.timeValueMillis(max), + TimeValue.timeValueMillis(frequency)); + } +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseSchedule.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseSchedule.java new file mode 100644 index 00000000000..133ea78220f --- /dev/null +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseSchedule.java @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.plugin.core; + +import org.elasticsearch.license.core.License; +import org.elasticsearch.xpack.scheduler.SchedulerEngine; + +import static org.elasticsearch.license.plugin.core.LicensesService.GRACE_PERIOD_DURATION; +import static org.elasticsearch.license.plugin.core.LicensesService.getLicenseState; + +public class LicenseSchedule implements SchedulerEngine.Schedule { + + private final License license; + + LicenseSchedule(License license) { + this.license = license; + } + + @Override + public long nextScheduledTimeAfter(long startTime, long time) { + long nextScheduledTime = -1; + switch (getLicenseState(license, time)) { + case ENABLED: + nextScheduledTime = license.expiryDate(); + break; + case GRACE_PERIOD: + nextScheduledTime = license.expiryDate() + GRACE_PERIOD_DURATION.getMillis(); + break; + case DISABLED: + if (license.issueDate() > time) { + // when we encounter a license with a future issue date + // which can happen with autogenerated license, + // we want to schedule a notification on the license issue date + // so the license is notificed once it is valid + // see https://github.com/elastic/x-plugins/issues/983 + nextScheduledTime = license.issueDate(); + } + break; + } + return nextScheduledTime; + } +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesManagerService.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesManagerService.java index f80d7dac03f..786f3dd970a 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesManagerService.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesManagerService.java @@ -12,9 +12,9 @@ import java.util.List; public interface LicensesManagerService { /** - * @return the id of registered licensees currently in state + * @return current {@link LicenseState} */ - List licenseesWithState(LicenseState state); + LicenseState licenseState(); /** * @return the currently active license, or {@code null} if no license is currently installed diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java index 52c1e50b2c8..c2bfb91d805 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java @@ -20,20 +20,17 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.component.Lifecycle; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.inject.Singleton; -import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.joda.FormatDateTimeFormatter; import org.elasticsearch.common.joda.Joda; import org.elasticsearch.common.logging.LoggerMessageFormat; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; -import org.elasticsearch.common.util.concurrent.FutureUtils; import org.elasticsearch.gateway.GatewayService; import org.elasticsearch.license.core.License; import org.elasticsearch.license.core.LicenseVerifier; import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest; import org.elasticsearch.license.plugin.action.put.PutLicenseRequest; +import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.EmptyTransportResponseHandler; import org.elasticsearch.transport.TransportChannel; @@ -41,21 +38,17 @@ import org.elasticsearch.transport.TransportRequest; import org.elasticsearch.transport.TransportRequestHandler; import org.elasticsearch.transport.TransportResponse; import org.elasticsearch.transport.TransportService; +import org.elasticsearch.xpack.scheduler.SchedulerEngine; +import org.elasticsearch.xpack.support.clock.Clock; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Queue; import java.util.UUID; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicReference; /** @@ -76,18 +69,15 @@ import java.util.concurrent.atomic.AtomicReference; *

* All registered listeners are notified of the current license upon registration or when a new license is installed in the cluster state. * When a new license is notified as enabled to the registered listener, a notification is scheduled at the time of license expiry. - * Registered listeners are notified using {@link #notifyAndSchedule(LicensesMetaData)} + * Registered listeners are notified using {@link #onUpdate(LicensesMetaData)} */ -@Singleton public class LicensesService extends AbstractLifecycleComponent implements ClusterStateListener, LicensesManagerService, - LicenseeRegistry { + LicenseeRegistry, SchedulerEngine.Listener { public static final String REGISTER_TRIAL_LICENSE_ACTION_NAME = "internal:plugin/license/cluster/register_trial_license"; private final ClusterService clusterService; - private final ThreadPool threadPool; - private final TransportService transportService; /** @@ -95,20 +85,12 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust */ private final List registeredLicensees = new CopyOnWriteArrayList<>(); - /** - * Currently active expiry notifications - */ - private final Queue expiryNotifications = new ConcurrentLinkedQueue<>(); - - /** - * Currently active event notifications for every registered listener - */ - private final Queue eventNotifications = new ConcurrentLinkedQueue<>(); - /** * Currently active license */ private final AtomicReference currentLicense = new AtomicReference<>(); + private SchedulerEngine scheduler; + private final Clock clock; /** * Callbacks to notify relative to license expiry @@ -128,7 +110,9 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust /** * Duration of grace period after a license has expired */ - private TimeValue gracePeriodDuration = days(7); + public static final TimeValue GRACE_PERIOD_DURATION = days(7); + + private static final String LICENSE_JOB = "licenseJob"; private static final FormatDateTimeFormatter DATE_FORMATTER = Joda.forPattern("EEEE, MMMMM dd, yyyy", Locale.ROOT); @@ -136,16 +120,18 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust "please read the following messages and update the license again, this time with the \"acknowledge=true\" parameter:"; @Inject - public LicensesService(Settings settings, ClusterService clusterService, ThreadPool threadPool, TransportService transportService) { + public LicensesService(Settings settings, ClusterService clusterService, TransportService transportService, Clock clock) { super(settings); this.clusterService = clusterService; - this.threadPool = threadPool; this.transportService = transportService; if (DiscoveryNode.isMasterNode(settings)) { transportService.registerRequestHandler(REGISTER_TRIAL_LICENSE_ACTION_NAME, TransportRequest.Empty::new, ThreadPool.Names.SAME, new RegisterTrialLicenseRequestHandler()); } populateExpirationCallbacks(); + this.clock = clock; + this.scheduler = new SchedulerEngine(clock); + this.scheduler.register(this); } private void populateExpirationCallbacks() { @@ -251,18 +237,17 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust * Registers new license in the cluster * Master only operation. Installs a new license on the master provided it is VALID */ - public void registerLicense(final PutLicenseRequest request, final ActionListener listener) { + public void registerLicense(final PutLicenseRequest request, final ActionListener listener) { final License newLicense = request.license(); - final long now = System.currentTimeMillis(); - if (!verifyLicense(newLicense) || newLicense.issueDate() > now) { - listener.onResponse(new LicensesUpdateResponse(true, LicensesStatus.INVALID)); + final long now = clock.millis(); + if (!LicenseVerifier.verifyLicense(newLicense) || newLicense.issueDate() > now) { + listener.onResponse(new PutLicenseResponse(true, LicensesStatus.INVALID)); } else if (newLicense.expiryDate() < now) { - listener.onResponse(new LicensesUpdateResponse(true, LicensesStatus.EXPIRED)); + listener.onResponse(new PutLicenseResponse(true, LicensesStatus.EXPIRED)); } else { if (!request.acknowledged()) { - final LicensesMetaData currentMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE); - final License currentLicense = getLicense(currentMetaData); - if (currentLicense != null && currentLicense != LicensesMetaData.LICENSE_TOMBSTONE) { + final License currentLicense = getLicense(); + if (currentLicense != null) { Map acknowledgeMessages = new HashMap<>(registeredLicensees.size() + 1); if (!License.isAutoGeneratedLicense(currentLicense.signature()) // current license is not auto-generated && currentLicense.issueDate() > newLicense.issueDate()) { // and has a later issue date @@ -278,72 +263,46 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust } if (!acknowledgeMessages.isEmpty()) { // needs acknowledgement - listener.onResponse(new LicensesUpdateResponse(false, LicensesStatus.VALID, ACKNOWLEDGEMENT_HEADER, + listener.onResponse(new PutLicenseResponse(false, LicensesStatus.VALID, ACKNOWLEDGEMENT_HEADER, acknowledgeMessages)); return; } } } clusterService.submitStateUpdateTask("register license [" + newLicense.uid() + "]", new - AckedClusterStateUpdateTask(request, listener) { - @Override - protected LicensesUpdateResponse newResponse(boolean acknowledged) { - return new LicensesUpdateResponse(acknowledged, LicensesStatus.VALID); - } + AckedClusterStateUpdateTask(request, listener) { + @Override + protected PutLicenseResponse newResponse(boolean acknowledged) { + return new PutLicenseResponse(acknowledged, LicensesStatus.VALID); + } - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); - mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(newLicense)); - return ClusterState.builder(currentState).metaData(mdBuilder).build(); - } - }); + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); + mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(newLicense)); + return ClusterState.builder(currentState).metaData(mdBuilder).build(); + } + }); } } - private boolean verifyLicense(final License license) { - final byte[] publicKeyBytes; - try (InputStream is = LicensesService.class.getResourceAsStream("/public.key")) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - Streams.copy(is, out); - publicKeyBytes = out.toByteArray(); - } catch (IOException ex) { - throw new IllegalStateException(ex); - } - return LicenseVerifier.verifyLicense(license, publicKeyBytes); - } static TimeValue days(int days) { return TimeValue.timeValueHours(days * 24); } - public static class LicensesUpdateResponse extends ClusterStateUpdateResponse { - private final LicensesStatus status; - private final String acknowledgementHeader; - private final Map acknowledgeMessages; - - public LicensesUpdateResponse(boolean acknowledged, LicensesStatus status) { - this(acknowledged, status, null, Collections.emptyMap()); - } - - public LicensesUpdateResponse(boolean acknowledged, LicensesStatus status, String acknowledgementHeader, - Map acknowledgeMessages) { - super(acknowledged); - this.status = status; - this.acknowledgeMessages = acknowledgeMessages; - this.acknowledgementHeader = acknowledgementHeader; - } - - public LicensesStatus status() { - return status; - } - - public String acknowledgementHeader() { - return acknowledgementHeader; - } - - public Map acknowledgeMessages() { - return acknowledgeMessages; + @Override + public void triggered(SchedulerEngine.Event event) { + final LicensesMetaData licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE); + if (licensesMetaData != null) { + final License license = licensesMetaData.getLicense(); + if (event.getJobName().equals(LICENSE_JOB)) { + notifyLicensees(license); + } else if (event.getJobName().startsWith(ExpirationCallback.EXPIRATION_JOB_PREFIX)) { + expirationCallbacks.stream() + .filter(expirationCallback -> expirationCallback.getId().equals(event.getJobName())) + .forEach(expirationCallback -> expirationCallback.on(license)); + } } } @@ -353,35 +312,34 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust public void removeLicense(final DeleteLicenseRequest request, final ActionListener listener) { clusterService.submitStateUpdateTask("delete license", new AckedClusterStateUpdateTask(request, listener) { - @Override - protected ClusterStateUpdateResponse newResponse(boolean acknowledged) { - return new ClusterStateUpdateResponse(acknowledged); - } + @Override + protected ClusterStateUpdateResponse newResponse(boolean acknowledged) { + return new ClusterStateUpdateResponse(acknowledged); + } - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - MetaData metaData = currentState.metaData(); - final LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE); - if (currentLicenses.getLicense() != LicensesMetaData.LICENSE_TOMBSTONE) { - MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); - mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(LicensesMetaData.LICENSE_TOMBSTONE)); - return ClusterState.builder(currentState).metaData(mdBuilder).build(); - } else { - return currentState; - } - } - }); + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + MetaData metaData = currentState.metaData(); + final LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE); + if (currentLicenses.getLicense() != LicensesMetaData.LICENSE_TOMBSTONE) { + MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); + mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(LicensesMetaData.LICENSE_TOMBSTONE)); + return ClusterState.builder(currentState).metaData(mdBuilder).build(); + } else { + return currentState; + } + } + }); } @Override - public List licenseesWithState(LicenseState state) { - List licensees = new ArrayList<>(registeredLicensees.size()); - for (InternalLicensee licensee : registeredLicensees) { - if (licensee.currentLicenseState == state) { - licensees.add(licensee.id()); - } + public LicenseState licenseState() { + if (registeredLicensees.size() > 0) { + return registeredLicensees.get(0).currentLicenseState; + } else { + final License license = getLicense(clusterService.state().metaData().custom(LicensesMetaData.TYPE)); + return getLicenseState(license, clock.millis()); } - return licensees; } @Override @@ -412,7 +370,7 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); // do not generate a trial license if any license is present if (currentLicensesMetaData == null) { - long issueDate = System.currentTimeMillis(); + long issueDate = clock.millis(); License.Builder specBuilder = License.builder() .uid(UUID.randomUUID().toString()) .issuedTo(clusterService.getClusterName().value()) @@ -437,26 +395,16 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust @Override protected void doStart() throws ElasticsearchException { clusterService.add(this); + scheduler.start(Collections.emptyList()); } @Override protected void doStop() throws ElasticsearchException { clusterService.remove(this); - - // cancel all notifications - for (ScheduledFuture scheduledNotification : expiryNotifications) { - FutureUtils.cancel(scheduledNotification); - } - for (ScheduledFuture eventNotification : eventNotifications) { - FutureUtils.cancel(eventNotification); - } - + scheduler.stop(); // clear all handlers registeredLicensees.clear(); - // empty out notification queue - expiryNotifications.clear(); - // clear current license currentLicense.set(null); } @@ -482,16 +430,13 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust logger.debug("current [{}]", currentLicensesMetaData); } // notify all interested plugins - if (previousClusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) { - notifyAndSchedule(currentLicensesMetaData); - } else { - if (prevLicensesMetaData == null) { - if (currentLicensesMetaData != null) { - notifyAndSchedule(currentLicensesMetaData); - } - } else if (!prevLicensesMetaData.equals(currentLicensesMetaData)) { - notifyAndSchedule(currentLicensesMetaData); + if (previousClusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK) + || prevLicensesMetaData == null) { + if (currentLicensesMetaData != null) { + onUpdate(currentLicensesMetaData); } + } else if (!prevLicensesMetaData.equals(currentLicensesMetaData)) { + onUpdate(currentLicensesMetaData); } // auto-generate license if no licenses ever existed // this will trigger a subsequent cluster changed event @@ -504,245 +449,75 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust } } - /** - * Notifies registered licensees of license state change and/or new active license - * based on the license in currentLicensesMetaData. - * Additionally schedules license expiry notifications and event callbacks - * relative to the current license's expiry - */ - private void notifyAndSchedule(final LicensesMetaData currentLicensesMetaData) { - final License license = getLicense(currentLicensesMetaData); + private void notifyLicensees(final License license) { if (license == LicensesMetaData.LICENSE_TOMBSTONE) { // implies license has been explicitly deleted // update licensee states registeredLicensees.forEach(InternalLicensee::onRemove); return; } + if (license != null) { + logger.debug("notifying [{}] listeners", registeredLicensees.size()); + switch (getLicenseState(license, clock.millis())) { + case ENABLED: + for (InternalLicensee licensee : registeredLicensees) { + licensee.onChange(license, LicenseState.ENABLED); + } + logger.debug("license [{}] - valid", license.uid()); + break; + case GRACE_PERIOD: + for (InternalLicensee licensee : registeredLicensees) { + licensee.onChange(license, LicenseState.GRACE_PERIOD); + } + logger.warn("license [{}] - grace", license.uid()); + break; + case DISABLED: + for (InternalLicensee licensee : registeredLicensees) { + licensee.onChange(license, LicenseState.DISABLED); + } + logger.warn("license [{}] - expired", license.uid()); + break; + } + } + } + + static LicenseState getLicenseState(final License license, long time) { + if (license == null) { + return LicenseState.DISABLED; + } + if (license.issueDate() > time) { + return LicenseState.DISABLED; + } + if (license.expiryDate() > time) { + return LicenseState.ENABLED; + } + if ((license.expiryDate() + GRACE_PERIOD_DURATION.getMillis()) > time) { + return LicenseState.GRACE_PERIOD; + } + return LicenseState.DISABLED; + } + + /** + * Notifies registered licensees of license state change and/or new active license + * based on the license in currentLicensesMetaData. + * Additionally schedules license expiry notifications and event callbacks + * relative to the current license's expiry + */ + void onUpdate(final LicensesMetaData currentLicensesMetaData) { + final License license = getLicense(currentLicensesMetaData); // license can be null if the trial license is yet to be auto-generated // in this case, it is a no-op if (license != null) { - logger.debug("notifying [{}] listeners", registeredLicensees.size()); - long now = System.currentTimeMillis(); - if (license.issueDate() > now) { - logger.warn("license [{}] - invalid", license.uid()); - return; - } - long expiryDuration = license.expiryDate() - now; - if (license.expiryDate() > now) { - for (InternalLicensee licensee : registeredLicensees) { - licensee.onChange(license, LicenseState.ENABLED); - } - logger.debug("license [{}] - valid", license.uid()); - final TimeValue delay = TimeValue.timeValueMillis(expiryDuration); - // cancel any previous notifications - cancelNotifications(expiryNotifications); - try { - logger.debug("schedule grace notification after [{}] for license [{}]", delay.toString(), license.uid()); - expiryNotifications.add(threadPool.schedule(delay, executorName(), new LicensingClientNotificationJob())); - } catch (EsRejectedExecutionException ex) { - logger.debug("couldn't schedule grace notification", ex); - } - } else if ((license.expiryDate() + gracePeriodDuration.getMillis()) > now) { - for (InternalLicensee licensee : registeredLicensees) { - licensee.onChange(license, LicenseState.GRACE_PERIOD); - } - logger.warn("license [{}] - grace", license.uid()); - final TimeValue delay = TimeValue.timeValueMillis(expiryDuration + gracePeriodDuration.getMillis()); - // cancel any previous notifications - cancelNotifications(expiryNotifications); - try { - logger.debug("schedule expiry notification after [{}] for license [{}]", delay.toString(), license.uid()); - expiryNotifications.add(threadPool.schedule(delay, executorName(), new LicensingClientNotificationJob())); - } catch (EsRejectedExecutionException ex) { - logger.debug("couldn't schedule expiry notification", ex); - } - } else { - for (InternalLicensee licensee : registeredLicensees) { - licensee.onChange(license, LicenseState.DISABLED); - } - logger.warn("license [{}] - expired", license.uid()); - } - if (!license.equals(currentLicense.get())) { + notifyLicensees(license); + if (license.equals(currentLicense.get()) == false) { currentLicense.set(license); - // cancel all scheduled event notifications - cancelNotifications(eventNotifications); - // schedule expiry callbacks - for (ExpirationCallback expirationCallback : this.expirationCallbacks) { - final TimeValue delay; - if (expirationCallback.matches(license.expiryDate(), now)) { - expirationCallback.on(license); - TimeValue frequency = expirationCallback.frequency(); - delay = frequency != null ? frequency : expirationCallback.delay(expiryDuration); - } else { - delay = expirationCallback.delay(expiryDuration); - } - if (delay != null) { - eventNotifications.add(threadPool.schedule(delay, executorName(), new EventNotificationJob(expirationCallback))); - } - if (logger.isDebugEnabled()) { - logger.debug("schedule [{}] after [{}]", expirationCallback, delay); - } - } - logger.debug("scheduled expiry callbacks for [{}] expiring after [{}]", license.uid(), - TimeValue.timeValueMillis(expiryDuration)); - } - } - } - - private class LicensingClientNotificationJob implements Runnable { - @Override - public void run() { - logger.debug("running expiry notification"); - final ClusterState currentClusterState = clusterService.state(); - if (!currentClusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) { - final LicensesMetaData currentLicensesMetaData = currentClusterState.metaData().custom(LicensesMetaData.TYPE); - notifyAndSchedule(currentLicensesMetaData); - } else if (logger.isDebugEnabled()) { - // next clusterChanged event will deal with the missed notifications - logger.debug("skip expiry notification [{}]", GatewayService.STATE_NOT_RECOVERED_BLOCK); - } - } - } - - private class EventNotificationJob implements Runnable { - private final ExpirationCallback expirationCallback; - - EventNotificationJob(ExpirationCallback expirationCallback) { - this.expirationCallback = expirationCallback; - } - - @Override - public void run() { - logger.debug("running event notification for [{}]", expirationCallback); - LicensesMetaData currentLicensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE); - License license = getLicense(currentLicensesMetaData); - if (license != null) { - long now = System.currentTimeMillis(); - if (expirationCallback.matches(license.expiryDate(), now)) { - expirationCallback.on(license); - if (expirationCallback.frequency() != null) { - // schedule next event - eventNotifications.add(threadPool.schedule(expirationCallback.frequency(), executorName(), this)); - } - } else if (logger.isDebugEnabled()) { - logger.debug("skip scheduling notification for [{}] with license expiring after [{}]", expirationCallback, - TimeValue.timeValueMillis(license.expiryDate() - now)); + scheduler.add(new SchedulerEngine.Job(LICENSE_JOB, new LicenseSchedule(license))); + for (ExpirationCallback expirationCallback : expirationCallbacks) { + scheduler.add(new SchedulerEngine.Job(expirationCallback.getId(), + (startTime, now) -> + expirationCallback.nextScheduledTimeForExpiry(license.expiryDate(), startTime, now))); } } - // clear out any finished event notifications - while (!eventNotifications.isEmpty()) { - ScheduledFuture notification = eventNotifications.peek(); - if (notification != null && notification.isDone()) { - // remove the notifications that are done - eventNotifications.poll(); - } else { - // stop emptying out the queue as soon as the first undone future hits - break; - } - } - } - } - - public abstract static class ExpirationCallback { - - public enum Orientation {PRE, POST} - - public abstract static class Pre extends ExpirationCallback { - - /** - * Callback schedule prior to license expiry - * - * @param min latest relative time to execute before license expiry - * @param max earliest relative time to execute before license expiry - * @param frequency interval between execution - */ - public Pre(TimeValue min, TimeValue max, TimeValue frequency) { - super(Orientation.PRE, min, max, frequency); - } - - @Override - public boolean matches(long expirationDate, long now) { - long expiryDuration = expirationDate - now; - if (expiryDuration > 0L) { - if (expiryDuration <= max.getMillis()) { - return expiryDuration >= min.getMillis(); - } - } - return false; - } - - @Override - public TimeValue delay(long expiryDuration) { - return TimeValue.timeValueMillis(expiryDuration - max.getMillis()); - } - } - - public abstract static class Post extends ExpirationCallback { - - /** - * Callback schedule after license expiry - * - * @param min earliest relative time to execute after license expiry - * @param max latest relative time to execute after license expiry - * @param frequency interval between execution - */ - public Post(TimeValue min, TimeValue max, TimeValue frequency) { - super(Orientation.POST, min, max, frequency); - } - - @Override - public boolean matches(long expirationDate, long now) { - long postExpiryDuration = now - expirationDate; - if (postExpiryDuration > 0L) { - if (postExpiryDuration <= max.getMillis()) { - return postExpiryDuration >= min.getMillis(); - } - } - return false; - } - - @Override - public TimeValue delay(long expiryDuration) { - final long delay; - if (expiryDuration >= 0L) { - delay = expiryDuration + min.getMillis(); - } else { - delay = (-1L * expiryDuration) - min.getMillis(); - } - if (delay > 0L) { - return TimeValue.timeValueMillis(delay); - } else { - return null; - } - } - } - - protected final Orientation orientation; - protected final TimeValue min; - protected final TimeValue max; - private final TimeValue frequency; - - private ExpirationCallback(Orientation orientation, TimeValue min, TimeValue max, TimeValue frequency) { - this.orientation = orientation; - this.min = (min == null) ? TimeValue.timeValueMillis(0) : min; - this.max = (max == null) ? TimeValue.timeValueMillis(Long.MAX_VALUE) : max; - this.frequency = frequency; - } - - public TimeValue frequency() { - return frequency; - } - - public abstract TimeValue delay(long expiryDuration); - - public abstract boolean matches(long expirationDate, long now); - - public abstract void on(License license); - - @Override - public String toString() { - return LoggerMessageFormat.format(null, "ExpirationCallback:(orientation [{}], min [{}], max [{}], freq [{}])", - orientation.name(), min, max, frequency); } } @@ -764,8 +539,8 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust // triggers a cluster changed event // eventually notifying the current licensee requestTrialLicense(clusterState); - } else { - notifyAndSchedule(currentMetaData); + } else if (lifecycleState() == Lifecycle.State.STARTED) { + notifyLicensees(currentMetaData.getLicense()); } } } @@ -787,7 +562,7 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust } else { boolean autoGeneratedLicense = License.isAutoGeneratedLicense(license.signature()); if ((autoGeneratedLicense && TrialLicense.verify(license)) - || (!autoGeneratedLicense && verifyLicense(license))) { + || (!autoGeneratedLicense && LicenseVerifier.verifyLicense(license))) { return license; } } @@ -795,25 +570,6 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust return null; } - /** - * Cancels out all notification futures - */ - private static void cancelNotifications(Queue scheduledNotifications) { - // clear out notification queue - while (!scheduledNotifications.isEmpty()) { - ScheduledFuture notification = scheduledNotifications.peek(); - if (notification != null) { - // cancel - FutureUtils.cancel(notification); - scheduledNotifications.poll(); - } - } - } - - private String executorName() { - return ThreadPool.Names.GENERIC; - } - /** * Stores acknowledgement, expiration and license notification callbacks * for a registered listener @@ -879,19 +635,4 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust channel.sendResponse(TransportResponse.Empty.INSTANCE); } } - - // TODO - temporary hack for tests, should be removed once we introduce `ClockMock` - public void setGracePeriodDuration(TimeValue gracePeriodDuration) { - this.gracePeriodDuration = gracePeriodDuration; - } - - // only for adding expiration callbacks for tests - public void setExpirationCallbacks(List expirationCallbacks) { - this.expirationCallbacks = expirationCallbacks; - } - - // TODO - temporary hack for tests, should be removed once we introduce `ClockMock` - public void setTrialLicenseDuration(TimeValue trialLicenseDuration) { - this.trialLicenseDuration = trialLicenseDuration; - } -} +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/AbstractLicensesConsumerPluginIntegrationTestCase.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/AbstractLicensesConsumerPluginIntegrationTestCase.java deleted file mode 100644 index 29ad100cea1..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/AbstractLicensesConsumerPluginIntegrationTestCase.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin; - -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.gateway.GatewayService; -import org.elasticsearch.license.plugin.consumer.TestConsumerPluginBase; -import org.elasticsearch.license.plugin.consumer.TestPluginServiceBase; -import org.elasticsearch.license.plugin.core.LicenseState; -import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.test.ESIntegTestCase.ClusterScope; -import org.elasticsearch.test.InternalTestCluster; -import org.elasticsearch.xpack.XPackPlugin; -import org.junit.After; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST; - -/** - * Framework to test licensing plugin integration for existing/new consumer plugins - * see {@link org.elasticsearch.license.plugin.LicensesEagerConsumerPluginIntegrationTests} - * and {@link org.elasticsearch.license.plugin.LicensesLazyConsumerPluginIntegrationTests} - * for example usage - */ -@ClusterScope(scope = TEST, numDataNodes = 2, numClientNodes = 0, transportClientRatio = 0.0) -public abstract class AbstractLicensesConsumerPluginIntegrationTestCase extends AbstractLicensesIntegrationTestCase { - protected final TestConsumerPluginBase consumerPlugin; - - public AbstractLicensesConsumerPluginIntegrationTestCase(TestConsumerPluginBase consumerPlugin) { - this.consumerPlugin = consumerPlugin; - } - - private final int trialLicenseDurationInSeconds = 20; - - @Override - protected Settings nodeSettings(int nodeOrdinal) { - return Settings.builder() - .put(super.nodeSettings(nodeOrdinal)) - // this setting is only used in tests - .put("_trial_license_duration_in_seconds", trialLicenseDurationInSeconds) - .build(); - } - - @Override - protected Collection> nodePlugins() { - return Arrays.asList(XPackPlugin.class, consumerPlugin.getClass()); - } - - @Override - protected Collection> transportClientPlugins() { - return nodePlugins(); - } - - @After - public void afterTest() throws Exception { - wipeAllLicenses(); - assertTrue(awaitBusy(() -> !clusterService().state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK))); - } - - public void testTrialLicenseAndSignedLicenseNotification() throws Exception { - logger.info("using {} consumer plugin", consumerPlugin.getClass().getName()); - logger.info(" --> trial license generated"); - // managerService should report feature to be enabled on all data nodes - assertLicenseeState(consumerPlugin.id(), LicenseState.ENABLED); - // consumer plugin service should return enabled on all data nodes - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.ENABLED, 2); - - logger.info(" --> check trial license expiry notification"); - // consumer plugin should notify onDisabled on all data nodes (expired trial license) - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.GRACE_PERIOD, trialLicenseDurationInSeconds * 2); - assertLicenseeState(consumerPlugin.id(), LicenseState.GRACE_PERIOD); - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.DISABLED, trialLicenseDurationInSeconds * 2); - assertLicenseeState(consumerPlugin.id(), LicenseState.DISABLED); - - logger.info(" --> put signed license"); - putLicense(TimeValue.timeValueSeconds(trialLicenseDurationInSeconds)); - - logger.info(" --> check signed license enabled notification"); - // consumer plugin should notify onEnabled on all data nodes (signed license) - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.ENABLED, 1); - assertLicenseeState(consumerPlugin.id(), LicenseState.ENABLED); - - logger.info(" --> check signed license expiry notification"); - // consumer plugin should notify onDisabled on all data nodes (expired signed license) - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.GRACE_PERIOD, trialLicenseDurationInSeconds * 2); - assertLicenseeState(consumerPlugin.id(), LicenseState.GRACE_PERIOD); - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.DISABLED, trialLicenseDurationInSeconds * 2); - assertLicenseeState(consumerPlugin.id(), LicenseState.DISABLED); - } - - public void testTrialLicenseNotification() throws Exception { - logger.info(" --> check onEnabled for trial license"); - // managerService should report feature to be enabled on all data nodes - assertLicenseeState(consumerPlugin.id(), LicenseState.ENABLED); - // consumer plugin service should return enabled on all data nodes - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.ENABLED, 1); - - logger.info(" --> check trial license expiry notification"); - // consumer plugin should notify onDisabled on all data nodes (expired signed license) - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.GRACE_PERIOD, trialLicenseDurationInSeconds); - assertLicenseeState(consumerPlugin.id(), LicenseState.GRACE_PERIOD); - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.DISABLED, trialLicenseDurationInSeconds); - assertLicenseeState(consumerPlugin.id(), LicenseState.DISABLED); - } - - public void testOverlappingTrialAndSignedLicenseNotification() throws Exception { - logger.info(" --> check onEnabled for trial license"); - // managerService should report feature to be enabled on all data nodes - assertLicenseeState(consumerPlugin.id(), LicenseState.ENABLED); - // consumer plugin service should return enabled on all data nodes - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.ENABLED, 1); - - logger.info(" --> put signed license while trial license is in effect"); - putLicense(TimeValue.timeValueSeconds(trialLicenseDurationInSeconds * 2)); - - logger.info(" --> check signed license enabled notification"); - // consumer plugin should notify onEnabled on all data nodes (signed license) - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.ENABLED, 1); - assertLicenseeState(consumerPlugin.id(), LicenseState.ENABLED); - - logger.info(" --> sleep for rest of trailLicense duration"); - Thread.sleep(trialLicenseDurationInSeconds * 1000L); - - logger.info(" --> check consumer is still enabled [signed license]"); - // consumer plugin should notify onEnabled on all data nodes (signed license) - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.ENABLED, 1); - assertLicenseeState(consumerPlugin.id(), LicenseState.ENABLED); - - logger.info(" --> check signed license expiry notification"); - // consumer plugin should notify onDisabled on all data nodes (expired signed license) - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.GRACE_PERIOD, trialLicenseDurationInSeconds * 2 * 2); - assertLicenseeState(consumerPlugin.id(), LicenseState.GRACE_PERIOD); - assertConsumerPluginNotification(consumerPluginServices(), LicenseState.DISABLED, trialLicenseDurationInSeconds * 2 * 2); - assertLicenseeState(consumerPlugin.id(), LicenseState.DISABLED); - } - - private List consumerPluginServices() { - final InternalTestCluster clients = internalCluster(); - List consumerPluginServices = new ArrayList<>(); - for (TestPluginServiceBase service : clients.getDataNodeInstances(consumerPlugin.service())) { - consumerPluginServices.add(service); - } - return consumerPluginServices; - } -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/AbstractLicensesIntegrationTestCase.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/AbstractLicensesIntegrationTestCase.java index c2248c739b5..b725ea31189 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/AbstractLicensesIntegrationTestCase.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/AbstractLicensesIntegrationTestCase.java @@ -11,37 +11,19 @@ import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.license.core.License; -import org.elasticsearch.license.plugin.action.put.PutLicenseAction; -import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder; -import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; -import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService; -import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationPluginService; -import org.elasticsearch.license.plugin.consumer.TestPluginServiceBase; -import org.elasticsearch.license.plugin.core.LicenseState; -import org.elasticsearch.license.plugin.core.LicensesManagerService; import org.elasticsearch.license.plugin.core.LicensesMetaData; -import org.elasticsearch.license.plugin.core.LicensesStatus; import org.elasticsearch.xpack.monitoring.Monitoring; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.xpack.security.Security; import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.xpack.watcher.Watcher; import org.elasticsearch.xpack.XPackPlugin; import org.elasticsearch.xpack.graph.Graph; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.List; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.Matchers.greaterThan; public abstract class AbstractLicensesIntegrationTestCase extends ESIntegTestCase { @@ -71,6 +53,30 @@ public abstract class AbstractLicensesIntegrationTestCase extends ESIntegTestCas return nodeSettings(0); } + protected void putLicense(final License license) throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + ClusterService clusterService = internalCluster().getInstance(ClusterService.class, internalCluster().getMasterName()); + clusterService.submitStateUpdateTask("putting license", new ClusterStateUpdateTask() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + } + + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); + mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license)); + return ClusterState.builder(currentState).metaData(mdBuilder).build(); + } + + @Override + public void onFailure(String source, @Nullable Exception e) { + logger.error("error on metaData cleanup after test", e); + } + }); + latch.await(); + } + protected void wipeAllLicenses() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); ClusterService clusterService = internalCluster().getInstance(ClusterService.class, internalCluster().getMasterName()); @@ -94,67 +100,4 @@ public abstract class AbstractLicensesIntegrationTestCase extends ESIntegTestCas }); latch.await(); } - - protected void putLicense(TimeValue expiryDuration) throws Exception { - License license1 = generateSignedLicense(expiryDuration); - final PutLicenseResponse putLicenseResponse = new PutLicenseRequestBuilder(client().admin().cluster(), - PutLicenseAction.INSTANCE).setLicense(license1).get(); - assertThat(putLicenseResponse.isAcknowledged(), equalTo(true)); - assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID)); - } - - protected void assertLicenseeState(final String id, final LicenseState state) throws InterruptedException { - assertTrue("LicensesManagerService for licensee " + id + " should have status " + state.name(), awaitBusy(() -> { - final InternalTestCluster clients = internalCluster(); - for (LicensesManagerService managerService : clients.getDataNodeInstances(LicensesManagerService.class)) { - if (!managerService.licenseesWithState(state).contains(id)) { - return false; - } - } - return true; - })); - } - - protected void assertLazyConsumerPluginNotification(final LicenseState state, int timeoutInSec) throws InterruptedException { - final List consumerPluginServices = consumerLazyPluginServices(); - assertConsumerPluginNotification(consumerPluginServices, state, timeoutInSec); - } - - protected void assertEagerConsumerPluginNotification(final LicenseState state, int timeoutInSec) throws InterruptedException { - final List consumerPluginServices = consumerEagerPluginServices(); - assertConsumerPluginNotification(consumerPluginServices, state, timeoutInSec); - } - - protected void assertConsumerPluginNotification(final List consumerPluginServices, final LicenseState state, - int timeoutInSec) throws InterruptedException { - assertThat("At least one instance has to be present", consumerPluginServices.size(), greaterThan(0)); - boolean success = awaitBusy(() -> { - for (TestPluginServiceBase pluginService : consumerPluginServices) { - if (state != pluginService.state()) { - return false; - } - } - return true; - }, timeoutInSec + 1, TimeUnit.SECONDS); - logger.debug("Notification assertion complete"); - assertThat(consumerPluginServices.get(0).getClass().getName() + " should have status " + state.name(), success, equalTo(true)); - } - - private List consumerLazyPluginServices() { - final InternalTestCluster clients = internalCluster(); - List consumerPluginServices = new ArrayList<>(); - for (TestPluginServiceBase service : clients.getDataNodeInstances(LazyLicenseRegistrationPluginService.class)) { - consumerPluginServices.add(service); - } - return consumerPluginServices; - } - - private List consumerEagerPluginServices() { - final InternalTestCluster clients = internalCluster(); - List consumerPluginServices = new ArrayList<>(); - for (TestPluginServiceBase service : clients.getDataNodeInstances(EagerLicenseRegistrationPluginService.class)) { - consumerPluginServices.add(service); - } - return consumerPluginServices; - } } diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesEagerConsumerPluginIntegrationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesEagerConsumerPluginIntegrationTests.java deleted file mode 100644 index fc87ca63184..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesEagerConsumerPluginIntegrationTests.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin; - -import org.apache.lucene.util.LuceneTestCase.BadApple; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin; - -// test is just too slow, please fix it to not be sleep-based -@BadApple(bugUrl = "https://github.com/elastic/x-plugins/issues/1007") -public class LicensesEagerConsumerPluginIntegrationTests extends AbstractLicensesConsumerPluginIntegrationTestCase { - - public LicensesEagerConsumerPluginIntegrationTests() { - super(new EagerLicenseRegistrationConsumerPlugin(Settings.EMPTY)); - } -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesLazyConsumerPluginIntegrationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesLazyConsumerPluginIntegrationTests.java deleted file mode 100644 index 6204bbf9cae..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesLazyConsumerPluginIntegrationTests.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin; - -import org.apache.lucene.util.LuceneTestCase.BadApple; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationConsumerPlugin; - -//test is just too slow, please fix it to not be sleep-based -@BadApple(bugUrl = "https://github.com/elastic/x-plugins/issues/1007") -public class LicensesLazyConsumerPluginIntegrationTests extends AbstractLicensesConsumerPluginIntegrationTestCase { - - public LicensesLazyConsumerPluginIntegrationTests() { - super(new LazyLicenseRegistrationConsumerPlugin(Settings.EMPTY)); - } -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesPluginIntegrationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesPluginIntegrationTests.java deleted file mode 100644 index 3f1a0539e5c..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesPluginIntegrationTests.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin; - -import org.apache.lucene.util.LuceneTestCase.BadApple; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.gateway.GatewayService; -import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin; -import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService; -import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationConsumerPlugin; -import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationPluginService; -import org.elasticsearch.license.plugin.core.LicenseState; -import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.test.ESIntegTestCase.ClusterScope; -import org.elasticsearch.xpack.XPackPlugin; -import org.junit.After; - -import java.util.Arrays; -import java.util.Collection; - -import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST; - -//test is just too slow, please fix it to not be sleep-based -@BadApple(bugUrl = "https://github.com/elastic/x-plugins/issues/1007") -@ClusterScope(scope = TEST, numDataNodes = 2, numClientNodes = 0) -public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationTestCase { - private final boolean useEagerLicenseRegistrationPlugin = randomBoolean(); - - private final int trialLicenseDurationInSeconds = 10; - - @Override - protected Settings nodeSettings(int nodeOrdinal) { - return Settings.builder() - .put(super.nodeSettings(nodeOrdinal)) - // this setting is only used in tests - .put("_trial_license_duration_in_seconds", trialLicenseDurationInSeconds) - .build(); - } - - @Override - protected Collection> nodePlugins() { - if (useEagerLicenseRegistrationPlugin) { - return Arrays.asList(XPackPlugin.class, EagerLicenseRegistrationConsumerPlugin.class); - } else { - return Arrays.asList(XPackPlugin.class, LazyLicenseRegistrationConsumerPlugin.class); - } - } - - @Override - protected Collection> transportClientPlugins() { - return nodePlugins(); - } - - @After - public void afterTest() throws Exception { - wipeAllLicenses(); - assertTrue(awaitBusy(() -> !clusterService().state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK))); - } - - public void testTrialLicenseAndSignedLicenseNotification() throws Exception { - logger.info("using {} consumer plugin", useEagerLicenseRegistrationPlugin ? "eager" : "lazy"); - logger.info(" --> trial license generated"); - // managerService should report feature to be enabled on all data nodes - assertLicenseeState(getCurrentFeatureName(), LicenseState.ENABLED); - // consumer plugin service should return enabled on all data nodes - assertConsumerPluginEnabledNotification(2); - - logger.info(" --> check trial license expiry notification"); - // consumer plugin should notify onDisabled on all data nodes (expired trial license) - assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2); - assertLicenseeState(getCurrentFeatureName(), LicenseState.GRACE_PERIOD); - - assertLicenseeState(getCurrentFeatureName(), LicenseState.DISABLED); - - logger.info(" --> put signed license"); - putLicense(TimeValue.timeValueSeconds(trialLicenseDurationInSeconds)); - - logger.info(" --> check signed license enabled notification"); - // consumer plugin should notify onEnabled on all data nodes (signed license) - assertConsumerPluginEnabledNotification(1); - assertLicenseeState(getCurrentFeatureName(), LicenseState.ENABLED); - - logger.info(" --> check signed license expiry notification"); - // consumer plugin should notify onDisabled on all data nodes (expired signed license) - assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2); - assertLicenseeState(getCurrentFeatureName(), LicenseState.GRACE_PERIOD); - assertLicenseeState(getCurrentFeatureName(), LicenseState.DISABLED); - } - - public void testTrialLicenseNotification() throws Exception { - logger.info(" --> check onEnabled for trial license"); - // managerService should report feature to be enabled on all data nodes - assertLicenseeState(getCurrentFeatureName(), LicenseState.ENABLED); - // consumer plugin service should return enabled on all data nodes - assertConsumerPluginEnabledNotification(1); - - logger.info(" --> check trial license expiry notification"); - // consumer plugin should notify onDisabled on all data nodes (expired signed license) - assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2); - assertLicenseeState(getCurrentFeatureName(), LicenseState.GRACE_PERIOD); - assertLicenseeState(getCurrentFeatureName(), LicenseState.DISABLED); - } - - public void testOverlappingTrialAndSignedLicenseNotification() throws Exception { - logger.info(" --> check onEnabled for trial license"); - // managerService should report feature to be enabled on all data nodes - assertLicenseeState(getCurrentFeatureName(), LicenseState.ENABLED); - // consumer plugin service should return enabled on all data nodes - assertConsumerPluginEnabledNotification(1); - - logger.info(" --> put signed license while trial license is in effect"); - putLicense(TimeValue.timeValueSeconds(trialLicenseDurationInSeconds * 2)); - - logger.info(" --> check signed license enabled notification"); - // consumer plugin should notify onEnabled on all data nodes (signed license) - assertConsumerPluginEnabledNotification(1); - assertLicenseeState(getCurrentFeatureName(), LicenseState.ENABLED); - - logger.info(" --> sleep for rest of trailLicense duration"); - Thread.sleep(trialLicenseDurationInSeconds * 1000L); - - logger.info(" --> check consumer is still enabled [signed license]"); - // consumer plugin should notify onEnabled on all data nodes (signed license) - assertConsumerPluginEnabledNotification(1); - assertLicenseeState(getCurrentFeatureName(), LicenseState.ENABLED); - - logger.info(" --> check signed license expiry notification"); - // consumer plugin should notify onDisabled on all data nodes (expired signed license) - assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2 * 2); - assertLicenseeState(getCurrentFeatureName(), LicenseState.GRACE_PERIOD); - assertLicenseeState(getCurrentFeatureName(), LicenseState.DISABLED); - } - - private String getCurrentFeatureName() { - if (useEagerLicenseRegistrationPlugin) { - return EagerLicenseRegistrationPluginService.ID; - } else { - return LazyLicenseRegistrationPluginService.ID; - } - } - - private void assertConsumerPluginEnabledNotification(int timeoutInSec) throws InterruptedException { - if (useEagerLicenseRegistrationPlugin) { - assertEagerConsumerPluginNotification(LicenseState.ENABLED, timeoutInSec); - } else { - assertLazyConsumerPluginNotification(LicenseState.ENABLED, timeoutInSec); - } - } - - private void assertConsumerPluginDisabledNotification(int timeoutInSec) throws InterruptedException { - if (useEagerLicenseRegistrationPlugin) { - assertEagerConsumerPluginNotification(LicenseState.GRACE_PERIOD, timeoutInSec); - } else { - assertLazyConsumerPluginNotification(LicenseState.GRACE_PERIOD, timeoutInSec); - } - } - -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesPluginsIntegrationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesPluginsIntegrationTests.java deleted file mode 100644 index 657afe6525a..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesPluginsIntegrationTests.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin; - -import org.apache.lucene.util.LuceneTestCase.BadApple; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin; -import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService; -import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationConsumerPlugin; -import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationPluginService; -import org.elasticsearch.license.plugin.core.LicenseState; -import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.test.ESIntegTestCase.ClusterScope; -import org.elasticsearch.xpack.XPackPlugin; -import org.junit.After; - -import java.util.Arrays; -import java.util.Collection; - -import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST; - -//test is just too slow, please fix it to not be sleep-based -@BadApple(bugUrl = "https://github.com/elastic/x-plugins/issues/1007") -@ClusterScope(scope = TEST, numDataNodes = 0, numClientNodes = 0) -public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegrationTestCase { - - private static final String ID_1 = EagerLicenseRegistrationPluginService.ID; - private static final String ID_2 = LazyLicenseRegistrationPluginService.ID; - - @Override - protected Settings nodeSettings(int nodeOrdinal) { - return Settings.builder() - .put(super.nodeSettings(nodeOrdinal)) - .build(); - } - - private Settings nodeSettingsWithConsumerPlugin(int trialLicenseDuration) { - return Settings.builder() - .put(super.nodeSettings(0)) - // this setting is only used in tests - .put("_trial_license_duration_in_seconds", trialLicenseDuration) - .build(); - - } - - @Override - protected Collection> nodePlugins() { - return Arrays.asList(XPackPlugin.class, EagerLicenseRegistrationConsumerPlugin.class, LazyLicenseRegistrationConsumerPlugin.class); - } - - @Override - protected Collection> transportClientPlugins() { - return nodePlugins(); - } - - @After - public void afterTest() throws Exception { - wipeAllLicenses(); - } - - public void testMultipleConsumerPlugins() throws Exception { - int nNodes = randomIntBetween(2, 3); - int trialLicenseDurationInSec = 20; - int signedLicenseDuration = 5; - startNodesWithConsumerPlugins(nNodes, trialLicenseDurationInSec); - - logger.info(" --> trial license generated"); - // managerService should report feature to be enabled on all data nodes - assertLicenseeState(ID_1, LicenseState.ENABLED); - assertLicenseeState(ID_2, LicenseState.ENABLED); - // consumer plugin service should return enabled on all data nodes - assertEagerConsumerPluginNotification(LicenseState.ENABLED, 1); - assertLazyConsumerPluginNotification(LicenseState.ENABLED, 1); - - logger.info(" --> check trial license expiry notification"); - // consumer plugin should notify onDisabled on all data nodes (expired trial license) - assertEagerConsumerPluginNotification(LicenseState.GRACE_PERIOD, trialLicenseDurationInSec * 2); - assertLazyConsumerPluginNotification(LicenseState.GRACE_PERIOD, trialLicenseDurationInSec * 2); - assertLicenseeState(ID_1, LicenseState.GRACE_PERIOD); - assertLicenseeState(ID_2, LicenseState.GRACE_PERIOD); - assertLicenseeState(ID_1, LicenseState.DISABLED); - assertLicenseeState(ID_2, LicenseState.DISABLED); - - logger.info(" --> put signed license"); - putLicense(TimeValue.timeValueSeconds(signedLicenseDuration)); - - logger.info(" --> check signed license enabled notification"); - // consumer plugin should notify onEnabled on all data nodes (signed license) - assertEagerConsumerPluginNotification(LicenseState.ENABLED, 1); - assertLazyConsumerPluginNotification(LicenseState.ENABLED, 1); - assertLicenseeState(ID_1, LicenseState.ENABLED); - assertLicenseeState(ID_2, LicenseState.ENABLED); - - logger.info(" --> check signed license expiry notification"); - // consumer plugin should notify onDisabled on all data nodes (expired signed license) - assertEagerConsumerPluginNotification(LicenseState.GRACE_PERIOD, signedLicenseDuration * 2); - assertLazyConsumerPluginNotification(LicenseState.GRACE_PERIOD, signedLicenseDuration * 2); - assertLicenseeState(ID_1, LicenseState.GRACE_PERIOD); - assertLicenseeState(ID_2, LicenseState.GRACE_PERIOD); - - assertEagerConsumerPluginNotification(LicenseState.DISABLED, 10 * 2); - assertLazyConsumerPluginNotification(LicenseState.DISABLED, 10 * 2); - assertLicenseeState(ID_1, LicenseState.DISABLED); - assertLicenseeState(ID_2, LicenseState.DISABLED); - } - - public void testRandomFeatureLicensesActions() throws Exception { - int nNodes = randomIntBetween(2, 3); - - startNodesWithConsumerPlugins(nNodes, 10); - - logger.info(" --> check license enabled notification"); - assertEagerConsumerPluginNotification(LicenseState.ENABLED, 1); - assertLazyConsumerPluginNotification(LicenseState.ENABLED, 1); - assertLicenseeState(ID_1, LicenseState.ENABLED); - assertLicenseeState(ID_2, LicenseState.ENABLED); - - logger.info(" --> check license expiry notification"); - // consumer plugin should notify onDisabled on all data nodes (expired signed license) - assertEagerConsumerPluginNotification(LicenseState.GRACE_PERIOD, 10 * 2); - assertLazyConsumerPluginNotification(LicenseState.GRACE_PERIOD, 10 * 2); - assertLicenseeState(ID_1, LicenseState.GRACE_PERIOD); - assertLicenseeState(ID_2, LicenseState.GRACE_PERIOD); - assertEagerConsumerPluginNotification(LicenseState.DISABLED, 10 * 2); - assertLazyConsumerPluginNotification(LicenseState.DISABLED, 10 * 2); - assertLicenseeState(ID_1, LicenseState.DISABLED); - assertLicenseeState(ID_2, LicenseState.DISABLED); - } - - private void startNodesWithConsumerPlugins(int nNodes, int trialLicenseDuration) { - for (int i = 0; i < nNodes; i++) { - internalCluster().startNode(nodeSettingsWithConsumerPlugin(trialLicenseDuration)); - } - } -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTests.java index f65862a3c64..dff4dc1d955 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTests.java @@ -19,20 +19,16 @@ import org.elasticsearch.license.plugin.action.get.GetLicenseResponse; import org.elasticsearch.license.plugin.action.put.PutLicenseAction; import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder; import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; -import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin; -import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService; -import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationConsumerPlugin; -import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationPluginService; import org.elasticsearch.license.plugin.core.LicenseState; import org.elasticsearch.license.plugin.core.LicensesMetaData; +import org.elasticsearch.license.plugin.core.LicensesService; import org.elasticsearch.license.plugin.core.LicensesStatus; -import org.elasticsearch.node.Node; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.xpack.XPackPlugin; -import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense; import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST; @@ -43,7 +39,6 @@ import static org.hamcrest.CoreMatchers.nullValue; @ClusterScope(scope = TEST, numDataNodes = 0, numClientNodes = 0, maxNumDataNodes = 0, transportClientRatio = 0) public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTestCase { - private final String[] PLUGINS = {EagerLicenseRegistrationPluginService.ID, LazyLicenseRegistrationPluginService.ID}; @Override protected Settings transportClientSettings() { @@ -59,16 +54,12 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest return Settings.builder() .put(super.nodeSettings(nodeOrdinal)) .put("node.data", true) - // this setting is only used in tests - .put("_trial_license_duration_in_seconds", 9) - // this setting is only used in tests - .put("_grace_duration_in_seconds", 9) .put(NetworkModule.HTTP_ENABLED.getKey(), true); } @Override protected Collection> nodePlugins() { - return Arrays.asList(XPackPlugin.class, EagerLicenseRegistrationConsumerPlugin.class, LazyLicenseRegistrationConsumerPlugin.class); + return Collections.singletonList(XPackPlugin.class); } @Override @@ -107,46 +98,57 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest wipeAllLicenses(); } + + private void assertLicenseState(LicenseState state) throws InterruptedException { + boolean success = awaitBusy(() -> { + for (LicensesService service : internalCluster().getDataNodeInstances(LicensesService.class)) { + if (service.licenseState() == state) { + return true; + } + } + return false; + }); + assertTrue(success); + } + public void testClusterRestartWhileEnabled() throws Exception { wipeAllLicenses(); internalCluster().startNode(); ensureGreen(); - assertEagerConsumerPluginNotification(LicenseState.ENABLED, 5); - assertLazyConsumerPluginNotification(LicenseState.ENABLED, 5); + assertLicenseState(LicenseState.ENABLED); logger.info("--> restart node"); internalCluster().fullRestart(); ensureYellow(); logger.info("--> await node for enabled"); - assertEagerConsumerPluginNotification(LicenseState.ENABLED, 5); - assertLazyConsumerPluginNotification(LicenseState.ENABLED, 5); + assertLicenseState(LicenseState.ENABLED); } public void testClusterRestartWhileGrace() throws Exception { wipeAllLicenses(); internalCluster().startNode(); + assertLicenseState(LicenseState.ENABLED); + putLicense(TestUtils.generateSignedLicense(TimeValue.timeValueMillis(0))); ensureGreen(); - assertEagerConsumerPluginNotification(LicenseState.GRACE_PERIOD, 10); - assertLazyConsumerPluginNotification(LicenseState.GRACE_PERIOD, 10); + assertLicenseState(LicenseState.GRACE_PERIOD); logger.info("--> restart node"); internalCluster().fullRestart(); ensureYellow(); logger.info("--> await node for grace_period"); - assertEagerConsumerPluginNotification(LicenseState.GRACE_PERIOD, 5); - assertLazyConsumerPluginNotification(LicenseState.GRACE_PERIOD, 5); + assertLicenseState(LicenseState.GRACE_PERIOD); } public void testClusterRestartWhileExpired() throws Exception { wipeAllLicenses(); internalCluster().startNode(); ensureGreen(); - assertEagerConsumerPluginNotification(LicenseState.DISABLED, 20); - assertLazyConsumerPluginNotification(LicenseState.DISABLED, 20); + assertLicenseState(LicenseState.ENABLED); + putLicense(TestUtils.generateExpiredLicense(System.currentTimeMillis() - LicensesService.GRACE_PERIOD_DURATION.getMillis())); + assertLicenseState(LicenseState.DISABLED); logger.info("--> restart node"); internalCluster().fullRestart(); ensureYellow(); logger.info("--> await node for disabled"); - assertEagerConsumerPluginNotification(LicenseState.DISABLED, 5); - assertLazyConsumerPluginNotification(LicenseState.DISABLED, 5); + assertLicenseState(LicenseState.DISABLED); } public void testClusterNotRecovered() throws Exception { @@ -154,27 +156,7 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest internalCluster().startNode(nodeSettingsBuilder(0).put("discovery.zen.minimum_master_nodes", 2).put("node.master", true)); logger.info("--> start second master out of two [recovered state]"); internalCluster().startNode(nodeSettingsBuilder(1).put("discovery.zen.minimum_master_nodes", 2).put("node.master", true)); - assertLicenseesStateEnabled(); - assertConsumerPluginEnabledNotification(1); - } - - public void testAtMostOnceTrialLicenseGeneration() throws Exception { - wipeAllLicenses(); - logger.info("--> start one node [trial license should be generated & enabled]"); - internalCluster().startNode(nodeSettingsBuilder(0)); - assertLicenseesStateEnabled(); - assertConsumerPluginEnabledNotification(1); - - logger.info("--> start another node [trial license should be propagated from the old master not generated]"); - internalCluster().startNode(nodeSettings(1)); - assertLicenseesStateEnabled(); - assertConsumerPluginEnabledNotification(1); - - logger.info("--> check if multiple trial licenses are found for a id"); - LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE); - assertThat(licensesMetaData.getLicense(), not(LicensesMetaData.LICENSE_TOMBSTONE)); - - wipeAllLicenses(); + assertLicenseState(LicenseState.ENABLED); } private void removeLicense() throws Exception { @@ -216,15 +198,4 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest assertThat(licensesMetaData, notNullValue()); assertThat(licensesMetaData.getLicense(), not(LicensesMetaData.LICENSE_TOMBSTONE)); } - - private void assertLicenseesStateEnabled() throws Exception { - for (String id : PLUGINS) { - assertLicenseeState(id, LicenseState.ENABLED); - } - } - - private void assertConsumerPluginEnabledNotification(int timeoutInSec) throws InterruptedException { - assertEagerConsumerPluginNotification(LicenseState.ENABLED, timeoutInSec); - assertLazyConsumerPluginNotification(LicenseState.ENABLED, timeoutInSec); - } } diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceNodeTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceNodeTests.java deleted file mode 100644 index 13b77e9eec1..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceNodeTests.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin; - -import org.elasticsearch.common.network.NetworkModule; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin; -import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService; -import org.elasticsearch.license.plugin.core.LicenseState; -import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.xpack.XPackPlugin; - -import java.util.Arrays; -import java.util.Collection; - -import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST; - -/** - */ -@ESIntegTestCase.ClusterScope(scope = TEST, supportsDedicatedMasters = false, numDataNodes = 10, numClientNodes = 0) -public class LicensesServiceNodeTests extends AbstractLicensesIntegrationTestCase { - - @Override - protected Settings nodeSettings(int nodeOrdinal) { - return Settings.builder() - .put(super.nodeSettings(nodeOrdinal)) - .put(NetworkModule.HTTP_ENABLED.getKey(), true) - .build(); - } - - @Override - protected Collection> nodePlugins() { - return Arrays.asList(XPackPlugin.class, EagerLicenseRegistrationConsumerPlugin.class); - } - - @Override - protected Collection> transportClientPlugins() { - return nodePlugins(); - } - - public void testPluginStatus() throws Exception { - final Iterable testPluginServices = - internalCluster().getDataNodeInstances(EagerLicenseRegistrationPluginService.class); - assertTrue(awaitBusy(() -> { - for (EagerLicenseRegistrationPluginService pluginService : testPluginServices) { - if (pluginService.state() != LicenseState.ENABLED) { - return false; - } - } - return true; - })); - - } - -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesTransportTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesTransportTests.java index 997e14062a7..a184dc50570 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesTransportTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesTransportTests.java @@ -31,6 +31,7 @@ import java.util.Collection; import java.util.Collections; import static org.elasticsearch.license.plugin.TestUtils.dateMath; +import static org.elasticsearch.license.plugin.TestUtils.generateExpiredLicense; import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; @@ -129,7 +130,7 @@ public class LicensesTransportTests extends ESSingleNodeTestCase { } public void testPutExpiredLicense() throws Exception { - License expiredLicense = generateSignedLicense(dateMath("now-10d/d", System.currentTimeMillis()), TimeValue.timeValueMinutes(2)); + License expiredLicense = generateExpiredLicense(); PutLicenseRequestBuilder builder = new PutLicenseRequestBuilder(client().admin().cluster(), PutLicenseAction.INSTANCE); builder.setLicense(expiredLicense); PutLicenseResponse putLicenseResponse = builder.get(); @@ -162,7 +163,7 @@ public class LicensesTransportTests extends ESSingleNodeTestCase { License goldLicense = generateSignedLicense("gold", TimeValue.timeValueMinutes(5)); PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster(), PutLicenseAction.INSTANCE).setLicense(goldLicense) - .setAcknowledge(true); + .setAcknowledge(true); PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.get(); assertThat(putLicenseResponse.isAcknowledged(), equalTo(true)); assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID)); @@ -177,4 +178,4 @@ public class LicensesTransportTests extends ESSingleNodeTestCase { getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).get(); assertNull(getLicenseResponse.license()); } -} +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/TestUtils.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/TestUtils.java index e8e1e579bef..31ed2b7e3b3 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/TestUtils.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/TestUtils.java @@ -23,11 +23,13 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.license.core.License; import org.elasticsearch.license.licensor.LicenseSigner; import org.elasticsearch.license.plugin.action.put.PutLicenseRequest; +import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; import org.elasticsearch.license.plugin.core.Licensee; import org.elasticsearch.license.plugin.core.LicensesService; import org.elasticsearch.license.plugin.core.LicensesStatus; import org.junit.Assert; +import java.io.IOException; import java.nio.file.Path; import java.util.List; import java.util.Set; @@ -90,7 +92,7 @@ public class TestUtils { } public static License generateSignedLicense(String type, long issueDate, TimeValue expiryDuration) throws Exception { - long issue = (issueDate != -1L) ? issueDate : System.currentTimeMillis(); + long issue = (issueDate != -1L) ? issueDate : System.currentTimeMillis() - TimeValue.timeValueHours(2).getMillis(); int version = randomIntBetween(License.VERSION_START, License.VERSION_CURRENT); final String licenseType; if (version < License.VERSION_NO_FEATURE_TYPE) { @@ -101,7 +103,7 @@ public class TestUtils { final License.Builder builder = License.builder() .uid(UUID.randomUUID().toString()) .version(version) - .expiryDate(issue + expiryDuration.getMillis()) + .expiryDate(System.currentTimeMillis() + expiryDuration.getMillis()) .issueDate(issue) .type(licenseType) .issuedTo("customer") @@ -115,6 +117,24 @@ public class TestUtils { return signer.sign(builder.build()); } + public static License generateExpiredLicense() throws Exception { + return generateExpiredLicense(System.currentTimeMillis() - TimeValue.timeValueHours(randomIntBetween(1, 10)).getMillis()); + } + + public static License generateExpiredLicense(long expiryDate) throws Exception { + final License.Builder builder = License.builder() + .uid(UUID.randomUUID().toString()) + .version(License.VERSION_CURRENT) + .expiryDate(expiryDate) + .issueDate(expiryDate - TimeValue.timeValueMinutes(10).getMillis()) + .type(randomFrom("basic", "silver", "dev", "gold", "platinum")) + .issuedTo("customer") + .issuer("elasticsearch") + .maxNodes(5); + LicenseSigner signer = new LicenseSigner(getTestPriKeyPath(), getTestPubKeyPath()); + return signer.sign(builder.build()); + } + private static Path getResourcePath(String resource) throws Exception { return PathUtils.get(TestUtils.class.getResource(resource).toURI()); } @@ -138,9 +158,9 @@ public class TestUtils { PutLicenseRequest putLicenseRequest = new PutLicenseRequest().license(license); final CountDownLatch latch = new CountDownLatch(1); final AtomicReference status = new AtomicReference<>(); - licensesService.registerLicense(putLicenseRequest, new ActionListener() { + licensesService.registerLicense(putLicenseRequest, new ActionListener() { @Override - public void onResponse(LicensesService.LicensesUpdateResponse licensesUpdateResponse) { + public void onResponse(PutLicenseResponse licensesUpdateResponse) { status.set(licensesUpdateResponse.status()); latch.countDown(); } @@ -198,4 +218,4 @@ public class TestUtils { statuses.add(status); } } -} +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/EagerLicenseRegistrationConsumerPlugin.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/EagerLicenseRegistrationConsumerPlugin.java deleted file mode 100644 index d3fcd8e72d7..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/EagerLicenseRegistrationConsumerPlugin.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin.consumer; - -import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.settings.Settings; - -/** - * Registers licenses upon the start of the service lifecycle - * see {@link EagerLicenseRegistrationPluginService} - *

- * License registration might happen before clusterService start() - */ -public class EagerLicenseRegistrationConsumerPlugin extends TestConsumerPluginBase { - - public static final String NAME = "test_consumer_plugin_1"; - - @Inject - public EagerLicenseRegistrationConsumerPlugin(Settings settings) { - super(settings); - } - - @Override - public Class service() { - return EagerLicenseRegistrationPluginService.class; - } - - @Override - public String id() { - return EagerLicenseRegistrationPluginService.ID; - } -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/EagerLicenseRegistrationPluginService.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/EagerLicenseRegistrationPluginService.java deleted file mode 100644 index 2afc647ad69..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/EagerLicenseRegistrationPluginService.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin.consumer; - -import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.inject.Singleton; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.license.plugin.core.LicensesService; - -@Singleton -public class EagerLicenseRegistrationPluginService extends TestPluginServiceBase { - - public static String ID = "id1"; - - @Inject - public EagerLicenseRegistrationPluginService(Settings settings, LicensesService licensesClientService) { - super(true, settings, licensesClientService, null); - } - - @Override - public String id() { - return ID; - } -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/LazyLicenseRegistrationConsumerPlugin.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/LazyLicenseRegistrationConsumerPlugin.java deleted file mode 100644 index 78bc7fb4ad7..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/LazyLicenseRegistrationConsumerPlugin.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin.consumer; - -import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.settings.Settings; - -/** - * Registers licenses only after cluster has recovered - * see {@link LazyLicenseRegistrationPluginService} - *

- * License registration happens after clusterservice start() - */ -public class LazyLicenseRegistrationConsumerPlugin extends TestConsumerPluginBase { - - @Inject - public LazyLicenseRegistrationConsumerPlugin(Settings settings) { - super(settings); - } - - @Override - public Class service() { - return LazyLicenseRegistrationPluginService.class; - } - - @Override - public String id() { - return LazyLicenseRegistrationPluginService.ID; - } -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/LazyLicenseRegistrationPluginService.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/LazyLicenseRegistrationPluginService.java deleted file mode 100644 index 9041d2e8a1b..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/LazyLicenseRegistrationPluginService.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin.consumer; - -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.inject.Singleton; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.license.plugin.core.LicensesService; - -@Singleton -public class LazyLicenseRegistrationPluginService extends TestPluginServiceBase { - - public static String ID = "id2"; - - @Inject - public LazyLicenseRegistrationPluginService(Settings settings, LicensesService licensesClientService, ClusterService clusterService) { - super(false, settings, licensesClientService, clusterService); - } - - @Override - public String id() { - return ID; - } -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/TestConsumerPluginBase.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/TestConsumerPluginBase.java deleted file mode 100644 index 0156911b81b..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/TestConsumerPluginBase.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin.consumer; - -import org.elasticsearch.client.Client; -import org.elasticsearch.client.transport.TransportClient; -import org.elasticsearch.common.component.LifecycleComponent; -import org.elasticsearch.common.settings.Setting; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.settings.SettingsModule; -import org.elasticsearch.plugins.Plugin; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -public abstract class TestConsumerPluginBase extends Plugin { - - private final boolean isEnabled; - - public TestConsumerPluginBase(Settings settings) { - this.isEnabled = TransportClient.CLIENT_TYPE.equals(settings.get(Client.CLIENT_TYPE_SETTING_S.getKey())) == false; - } - - @Override - public Collection> nodeServices() { - Collection> services = new ArrayList<>(); - if (isEnabled) { - services.add(service()); - } - return services; - } - - @Override - public List> getSettings() { - return Arrays.asList(Setting.simpleString("_trial_license_duration_in_seconds", Setting.Property.NodeScope, - Setting.Property.Shared), Setting.simpleString("_grace_duration_in_seconds", Setting.Property.NodeScope, - Setting.Property.Shared)); - } - - public abstract Class service(); - - public abstract String id(); -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/TestPluginServiceBase.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/TestPluginServiceBase.java deleted file mode 100644 index ae32f19d00e..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/consumer/TestPluginServiceBase.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin.consumer; - -import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.cluster.ClusterChangedEvent; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.cluster.ClusterStateListener; -import org.elasticsearch.common.component.AbstractLifecycleComponent; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.gateway.GatewayService; -import org.elasticsearch.license.core.License; -import org.elasticsearch.license.plugin.core.LicenseState; -import org.elasticsearch.license.plugin.core.Licensee; -import org.elasticsearch.license.plugin.core.LicensesService; - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -public abstract class TestPluginServiceBase extends AbstractLifecycleComponent - implements ClusterStateListener, Licensee { - - private LicensesService licensesClientService; - private final ClusterService clusterService; - final boolean eagerLicenseRegistration; - public final AtomicBoolean registered = new AtomicBoolean(false); - private AtomicReference state = new AtomicReference<>(LicenseState.DISABLED); - - public TestPluginServiceBase(boolean eagerLicenseRegistration, Settings settings, LicensesService licensesClientService, - ClusterService clusterService) { - super(settings); - this.eagerLicenseRegistration = eagerLicenseRegistration; - this.licensesClientService = licensesClientService; - int trialDurationInSec = settings.getAsInt("_trial_license_duration_in_seconds", -1); - if (trialDurationInSec != -1) { - licensesClientService.setTrialLicenseDuration(TimeValue.timeValueSeconds(trialDurationInSec)); - } - int graceDurationInSec = settings.getAsInt("_grace_duration_in_seconds", 5); - licensesClientService.setGracePeriodDuration(TimeValue.timeValueSeconds(graceDurationInSec)); - if (!eagerLicenseRegistration) { - this.clusterService = clusterService; - clusterService.add(this); - } else { - this.clusterService = null; - } - } - - // should be the same string used by the license Manger to generate - // signed license - public abstract String id(); - - // check if feature is enabled - public LicenseState state() { - return state.get(); - } - - @Override - public void clusterChanged(ClusterChangedEvent event) { - if (!eagerLicenseRegistration && !event.state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) { - if (registered.compareAndSet(false, true)) { - logger.info("Registering to licensesService [lazy]"); - licensesClientService.register(this); - } - } - } - - protected void doStart() throws ElasticsearchException { - if (eagerLicenseRegistration) { - if (registered.compareAndSet(false, true)) { - logger.info("Registering to licensesService [eager]"); - licensesClientService.register(this); - } - } - } - - @Override - public String[] expirationMessages() { - return new String[0]; - } - - @Override - public String[] acknowledgmentMessages(License currentLicense, License newLicense) { - return new String[0]; - } - - @Override - public void onChange(Status status) { - this.state.set(status.getLicenseState()); - } - - @Override - protected void doStop() throws ElasticsearchException { - if (clusterService != null) { - clusterService.remove(this); - } - } - - @Override - protected void doClose() throws ElasticsearchException { - } -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java new file mode 100644 index 00000000000..4f49c2bc116 --- /dev/null +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.plugin.core; + +import org.elasticsearch.Version; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.block.ClusterBlocks; +import org.elasticsearch.cluster.metadata.MetaData; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.component.Lifecycle; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.transport.LocalTransportAddress; +import org.elasticsearch.license.core.License; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.transport.TransportService; +import org.elasticsearch.xpack.scheduler.SchedulerEngine; +import org.elasticsearch.xpack.support.clock.ClockMock; +import org.junit.Before; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public abstract class AbstractLicenseServiceTestCase extends ESTestCase { + + protected LicensesService licensesService; + protected ClusterService clusterService; + protected TransportService transportService; + protected ClockMock clock; + + @Before + public void init() throws Exception { + clusterService = mock(ClusterService.class); + transportService = mock(TransportService.class); + clock = new ClockMock(); + licensesService = new LicensesService(Settings.EMPTY, clusterService, transportService, clock); + } + + protected void setInitialState(License license) { + ClusterState state = mock(ClusterState.class); + final ClusterBlocks noBlock = ClusterBlocks.builder().build(); + when(state.blocks()).thenReturn(noBlock); + MetaData metaData = mock(MetaData.class); + when(metaData.custom(LicensesMetaData.TYPE)).thenReturn(new LicensesMetaData(license)); + when(state.metaData()).thenReturn(metaData); + final DiscoveryNodes discoveryNodes = mock(DiscoveryNodes.class); + final DiscoveryNode mockNode = new DiscoveryNode("b", LocalTransportAddress.buildUnique(), emptyMap(), emptySet(), Version.CURRENT); + when(discoveryNodes.getMasterNode()).thenReturn(mockNode); + when(state.nodes()).thenReturn(discoveryNodes); + when(clusterService.state()).thenReturn(state); + when(clusterService.lifecycleState()).thenReturn(Lifecycle.State.STARTED); + } +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/ExpirationCallbackTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/ExpirationCallbackTests.java new file mode 100644 index 00000000000..0e5bc9c46a5 --- /dev/null +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/ExpirationCallbackTests.java @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.plugin.core; + +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.license.core.License; +import org.elasticsearch.test.ESTestCase; + +import static org.elasticsearch.common.unit.TimeValue.timeValueMillis; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +public class ExpirationCallbackTests extends ESTestCase { + + public void testPostExpirationDelay() throws Exception { + TimeValue expiryDuration = TimeValue.timeValueSeconds(randomIntBetween(5, 10)); + TimeValue min = TimeValue.timeValueSeconds(1); + TimeValue max = TimeValue.timeValueSeconds(4); + TimeValue frequency = TimeValue.timeValueSeconds(1); + NoopPostExpirationCallback post = new NoopPostExpirationCallback(min, max, frequency); + long now = System.currentTimeMillis(); + long expiryDate = now + expiryDuration.getMillis(); + assertThat(post.delay(expiryDate, now), + equalTo(TimeValue.timeValueMillis(expiryDuration.getMillis() + min.getMillis()))); // before license expiry + assertThat(post.delay(expiryDate, expiryDate), equalTo(min)); // on license expiry + int latestValidTriggerDelay = (int) (expiryDuration.getMillis() + max.getMillis()); + int earliestValidTriggerDelay = (int) (expiryDuration.getMillis() + min.getMillis()); + assertExpirationCallbackDelay(post, expiryDuration.millis(), latestValidTriggerDelay, earliestValidTriggerDelay); + } + + public void testPreExpirationDelay() throws Exception { + TimeValue expiryDuration = TimeValue.timeValueSeconds(randomIntBetween(5, 10)); + TimeValue min = TimeValue.timeValueSeconds(1); + TimeValue max = TimeValue.timeValueSeconds(4); + TimeValue frequency = TimeValue.timeValueSeconds(1); + NoopPreExpirationCallback pre = new NoopPreExpirationCallback(min, max, frequency); + long now = System.currentTimeMillis(); + long expiryDate = now + expiryDuration.getMillis(); + assertThat(pre.delay(expiryDate, expiryDate), nullValue()); // on license expiry + int latestValidTriggerDelay = (int) (expiryDuration.getMillis() - min.getMillis()); + int earliestValidTriggerDelay = (int) (expiryDuration.getMillis() - max.getMillis()); + assertExpirationCallbackDelay(pre, expiryDuration.millis(), latestValidTriggerDelay, earliestValidTriggerDelay); + } + + public void testPostExpirationWithNullMax() throws Exception { + int postExpirySeconds = randomIntBetween(5, 10); + TimeValue postExpiryDuration = TimeValue.timeValueSeconds(postExpirySeconds); + TimeValue min = TimeValue.timeValueSeconds(postExpirySeconds - randomIntBetween(1, 3)); + + final ExpirationCallback.Post post = new NoopPostExpirationCallback(min, null, timeValueMillis(10)); + long now = System.currentTimeMillis(); + assertThat(post.delay(now - postExpiryDuration.millis(), now), equalTo(TimeValue.timeValueMillis(0))); + } + + public void testPreExpirationWithNullMin() throws Exception { + int expirySeconds = randomIntBetween(5, 10); + TimeValue expiryDuration = TimeValue.timeValueSeconds(expirySeconds); + TimeValue max = TimeValue.timeValueSeconds(expirySeconds + randomIntBetween(1, 10)); + + final ExpirationCallback.Pre pre = new NoopPreExpirationCallback(null, max, timeValueMillis(10)); + long now = System.currentTimeMillis(); + assertThat(pre.delay(expiryDuration.millis() + now, now), equalTo(TimeValue.timeValueMillis(0))); + } + + public void testPreExpirationScheduleTime() throws Exception { + TimeValue expiryDuration = TimeValue.timeValueSeconds(randomIntBetween(5, 10)); + TimeValue min = TimeValue.timeValueSeconds(1); + TimeValue max = TimeValue.timeValueSeconds(4); + TimeValue frequency = TimeValue.timeValueSeconds(1); + NoopPreExpirationCallback pre = new NoopPreExpirationCallback(min, max, frequency); + int latestValidTriggerDelay = (int) (expiryDuration.getMillis() - min.getMillis()); + int earliestValidTriggerDelay = (int) (expiryDuration.getMillis() - max.getMillis()); + assertExpirationCallbackScheduleTime(pre, expiryDuration.millis(), latestValidTriggerDelay, earliestValidTriggerDelay); + } + + public void testPostExpirationScheduleTime() throws Exception { + TimeValue expiryDuration = TimeValue.timeValueSeconds(randomIntBetween(5, 10)); + TimeValue min = TimeValue.timeValueSeconds(1); + TimeValue max = TimeValue.timeValueSeconds(4); + TimeValue frequency = TimeValue.timeValueSeconds(1); + NoopPostExpirationCallback pre = new NoopPostExpirationCallback(min, max, frequency); + int latestValidTriggerDelay = (int) (expiryDuration.getMillis() + max.getMillis()); + int earliestValidTriggerDelay = (int) (expiryDuration.getMillis() + min.getMillis()); + assertExpirationCallbackScheduleTime(pre, expiryDuration.millis(), latestValidTriggerDelay, earliestValidTriggerDelay); + } + + private void assertExpirationCallbackDelay(ExpirationCallback expirationCallback, long expiryDuration, + int latestValidTriggerDelay, int earliestValidTriggerDelay) { + long now = System.currentTimeMillis(); + long expiryDate = now + expiryDuration; + // bounds + assertThat(expirationCallback.delay(expiryDate, now + earliestValidTriggerDelay), equalTo(TimeValue.timeValueMillis(0))); + assertThat(expirationCallback.delay(expiryDate, now + latestValidTriggerDelay), equalTo(TimeValue.timeValueMillis(0))); + // in match + assertThat(expirationCallback.delay(expiryDate, + now + randomIntBetween(earliestValidTriggerDelay, latestValidTriggerDelay)), + equalTo(TimeValue.timeValueMillis(0))); + // out of bounds + int deltaBeforeEarliestMatch = between(1, earliestValidTriggerDelay); + assertThat(expirationCallback.delay(expiryDate, now + deltaBeforeEarliestMatch), + equalTo(TimeValue.timeValueMillis(earliestValidTriggerDelay - deltaBeforeEarliestMatch))); + int deltaAfterLatestMatch = between(latestValidTriggerDelay + 1, Integer.MAX_VALUE); // after expiry and after max + assertThat(expirationCallback.delay(expiryDate, expiryDate + deltaAfterLatestMatch), nullValue()); + } + + public void assertExpirationCallbackScheduleTime(ExpirationCallback expirationCallback, long expiryDuration, + int latestValidTriggerDelay, int earliestValidTriggerDelay) { + long now = System.currentTimeMillis(); + long expiryDate = now + expiryDuration; + int validTriggerInterval = between(earliestValidTriggerDelay, latestValidTriggerDelay); + assertThat(expirationCallback.nextScheduledTimeForExpiry(expiryDate, + now + validTriggerInterval, now + validTriggerInterval), + equalTo(now + validTriggerInterval)); + assertThat(expirationCallback.nextScheduledTimeForExpiry(expiryDate, now, now + validTriggerInterval), + equalTo(now + validTriggerInterval + expirationCallback.getFrequency())); + + int deltaBeforeEarliestMatch = between(1, earliestValidTriggerDelay); + assertThat(expirationCallback.nextScheduledTimeForExpiry(expiryDate, now, now + deltaBeforeEarliestMatch), + equalTo(now + deltaBeforeEarliestMatch + + expirationCallback.delay(expiryDate, now + deltaBeforeEarliestMatch).getMillis())); + assertThat(expirationCallback.nextScheduledTimeForExpiry(expiryDate, + now + deltaBeforeEarliestMatch, now + deltaBeforeEarliestMatch), + equalTo(now + deltaBeforeEarliestMatch + + expirationCallback.delay(expiryDate, now + deltaBeforeEarliestMatch).getMillis())); + + int deltaAfterLatestMatch = between(latestValidTriggerDelay + 1, Integer.MAX_VALUE); // after expiry and after max + assertThat(expirationCallback.nextScheduledTimeForExpiry(expiryDate, now, now + deltaAfterLatestMatch), equalTo(-1L)); + assertThat(expirationCallback.nextScheduledTimeForExpiry(expiryDate, + now + deltaAfterLatestMatch, now + deltaAfterLatestMatch), + equalTo(-1L)); + } + + private static class NoopPostExpirationCallback extends ExpirationCallback.Post { + + public NoopPostExpirationCallback(TimeValue min, TimeValue max, TimeValue frequency) { + super(min, max, frequency); + } + + @Override + public void on(License license) {} + } + + private static class NoopPreExpirationCallback extends ExpirationCallback.Pre { + + public NoopPreExpirationCallback(TimeValue min, TimeValue max, TimeValue frequency) { + super(min, max, frequency); + } + + @Override + public void on(License license) {} + } +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseClusterChangeTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseClusterChangeTests.java new file mode 100644 index 00000000000..8aeec1fd457 --- /dev/null +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseClusterChangeTests.java @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.plugin.core; + +import org.elasticsearch.Version; +import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.metadata.MetaData; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.common.transport.LocalTransportAddress; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.license.core.License; +import org.elasticsearch.license.plugin.TestUtils; +import org.elasticsearch.transport.EmptyTransportResponseHandler; +import org.elasticsearch.transport.TransportRequest; +import org.junit.After; +import org.junit.Before; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase { + + private TestUtils.AssertingLicensee licensee; + + @Before + public void setup() { + setInitialState(null); + licensesService.start(); + licensee = new TestUtils.AssertingLicensee("LicenseClusterChangeTests", logger); + licensesService.register(licensee); + } + + @After + public void teardown() { + licensesService.stop(); + } + + + public void testNotificationOnNewLicense() throws Exception { + ClusterState oldState = ClusterState.builder(new ClusterName("a")).build(); + final License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(24)); + MetaData metaData = MetaData.builder().putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license)).build(); + ClusterState newState = ClusterState.builder(new ClusterName("a")).metaData(metaData).build(); + licensesService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState)); + assertThat(licensee.statuses.size(), equalTo(1)); + assertTrue(licensee.statuses.get(0).getLicenseState() == LicenseState.ENABLED); + } + + public void testNoNotificationOnExistingLicense() throws Exception { + final License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(24)); + MetaData metaData = MetaData.builder().putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license)).build(); + ClusterState newState = ClusterState.builder(new ClusterName("a")).metaData(metaData).build(); + ClusterState oldState = ClusterState.builder(newState).build(); + licensesService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState)); + assertThat(licensee.statuses.size(), equalTo(0)); + } + + public void testTrialLicenseGeneration() throws Exception { + DiscoveryNode master = new DiscoveryNode("b", LocalTransportAddress.buildUnique(), emptyMap(), emptySet(), Version.CURRENT); + ClusterState oldState = ClusterState.builder(new ClusterName("a")) + .nodes(DiscoveryNodes.builder().masterNodeId(master.getId()).put(master)).build(); + ClusterState newState = ClusterState.builder(oldState).build(); + licensesService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState)); + verify(transportService, times(2)) + .sendRequest(any(DiscoveryNode.class), + eq(LicensesService.REGISTER_TRIAL_LICENSE_ACTION_NAME), + any(TransportRequest.Empty.class), any(EmptyTransportResponseHandler.class)); + } +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java new file mode 100644 index 00000000000..4d828566ca6 --- /dev/null +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.plugin.core; + +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.license.plugin.TestUtils; +import org.elasticsearch.transport.EmptyTransportResponseHandler; +import org.elasticsearch.transport.TransportRequest; + +import static org.elasticsearch.mock.orig.Mockito.times; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; + +public class LicenseRegistrationTests extends AbstractLicenseServiceTestCase { + + public void testTrialLicenseRequestOnEmptyLicenseState() throws Exception { + setInitialState(null); + TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee( + "testTrialLicenseRequestOnEmptyLicenseState", logger); + licensesService.start(); + licensesService.register(licensee); + verify(transportService, times(1)) + .sendRequest(any(DiscoveryNode.class), + eq(LicensesService.REGISTER_TRIAL_LICENSE_ACTION_NAME), + any(TransportRequest.Empty.class), any(EmptyTransportResponseHandler.class)); + assertThat(licensee.statuses.size(), equalTo(0)); + licensesService.stop(); + } + + public void testNotificationOnRegistration() throws Exception { + setInitialState(TestUtils.generateSignedLicense(TimeValue.timeValueHours(2))); + TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee( + "testNotificationOnRegistration", logger); + licensesService.start(); + licensesService.register(licensee); + assertThat(licensee.statuses.size(), equalTo(1)); + final LicenseState licenseState = licensee.statuses.get(0).getLicenseState(); + assertTrue(licenseState == LicenseState.ENABLED); + licensesService.stop(); + } +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseScheduleTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseScheduleTests.java new file mode 100644 index 00000000000..a8a179bb463 --- /dev/null +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseScheduleTests.java @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.plugin.core; + +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.license.core.License; +import org.elasticsearch.license.plugin.TestUtils; +import org.elasticsearch.test.ESTestCase; +import org.junit.Before; + +import static org.hamcrest.Matchers.equalTo; + +public class LicenseScheduleTests extends ESTestCase { + + private License license; + private LicenseSchedule schedule; + + @Before + public void setuo() throws Exception { + license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(12)); + schedule = new LicenseSchedule(license); + } + + public void testEnabledLicenseSchedule() throws Exception { + int expiryDuration = (int) (license.expiryDate() - license.issueDate()); + long triggeredTime = license.issueDate() + between(0, expiryDuration); + assertThat(schedule.nextScheduledTimeAfter(license.issueDate(), triggeredTime), equalTo(license.expiryDate())); + } + + public void testGraceLicenseSchedule() throws Exception { + long triggeredTime = license.expiryDate() + between(1, + ((int) LicensesService.GRACE_PERIOD_DURATION.getMillis())); + assertThat(schedule.nextScheduledTimeAfter(license.issueDate(), triggeredTime), + equalTo(license.expiryDate() + LicensesService.GRACE_PERIOD_DURATION.getMillis())); + } + + public void testExpiredLicenseSchedule() throws Exception { + long triggeredTime = license.expiryDate() + LicensesService.GRACE_PERIOD_DURATION.getMillis() + + randomIntBetween(1, 1000); + assertThat(schedule.nextScheduledTimeAfter(license.issueDate(), triggeredTime), + equalTo(-1L)); + } + + public void testInvalidLicenseSchedule() throws Exception { + long triggeredTime = license.issueDate() - randomIntBetween(1, 1000); + assertThat(schedule.nextScheduledTimeAfter(triggeredTime, triggeredTime), + equalTo(license.issueDate())); + } +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java index 6bfbb8188ce..7eb316e7398 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java @@ -6,79 +6,58 @@ package org.elasticsearch.license.plugin.core; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.cluster.metadata.MetaData; +import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.license.core.License; import org.elasticsearch.license.plugin.TestUtils; import org.elasticsearch.license.plugin.action.put.PutLicenseRequest; -import org.elasticsearch.test.ESSingleNodeTestCase; +import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import static org.elasticsearch.license.plugin.TestUtils.awaitNoBlock; -import static org.elasticsearch.license.plugin.TestUtils.awaitNoPendingTasks; import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; -public class LicensesAcknowledgementTests extends ESSingleNodeTestCase { - static { - MetaData.registerPrototype(LicensesMetaData.TYPE, LicensesMetaData.PROTO); - } - - @Override - protected boolean resetNodeAfterTest() { - return true; - } +public class LicensesAcknowledgementTests extends AbstractLicenseServiceTestCase { public void testAcknowledgment() throws Exception { - final LicensesService licensesService = getInstanceFromNode(LicensesService.class); + setInitialState(TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2))); licensesService.start(); String id = "testAcknowledgment"; String[] acknowledgeMessages = new String[] {"message"}; TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee(id, logger); licensee.setAcknowledgementMessages(acknowledgeMessages); - awaitNoBlock(client()); licensesService.register(licensee); - awaitNoPendingTasks(client()); // try installing a signed license License signedLicense = generateSignedLicense(TimeValue.timeValueHours(10)); PutLicenseRequest putLicenseRequest = new PutLicenseRequest().license(signedLicense); - CountDownLatch latch = new CountDownLatch(1); // ensure acknowledgement message was part of the response licensesService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID, - Collections.singletonMap(id, acknowledgeMessages), latch)); - if (!latch.await(5, TimeUnit.SECONDS)) { - fail("waiting too long for a response to license registration"); - } - awaitNoPendingTasks(client()); + Collections.singletonMap(id, acknowledgeMessages))); assertThat(licensee.acknowledgementRequested.size(), equalTo(1)); assertThat(licensee.acknowledgementRequested.get(0).v2(), equalTo(signedLicense)); assertThat(licensesService.getLicense(), not(signedLicense)); - latch = new CountDownLatch(1); // try installing a signed license with acknowledgement putLicenseRequest = new PutLicenseRequest().license(signedLicense).acknowledge(true); // ensure license was installed and no acknowledgment message was returned licensee.setAcknowledgementMessages(new String[0]); licensesService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(true, LicensesStatus.VALID, - Collections.emptyMap(), latch)); - if (!latch.await(5, TimeUnit.SECONDS)) { - fail("waiting too long for a response to license registration"); - } - awaitNoPendingTasks(client()); + Collections.emptyMap())); + verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class)); assertThat(licensee.acknowledgementRequested.size(), equalTo(1)); assertThat(licensee.acknowledgementRequested.get(0).v2(), equalTo(signedLicense)); - assertThat(licensesService.getLicense(), equalTo(signedLicense)); licensesService.stop(); } public void testAcknowledgementMultipleLicensee() throws Exception { - final LicensesService licensesService = getInstanceFromNode(LicensesService.class); + setInitialState(TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2))); licensesService.start(); String id1 = "testAcknowledgementMultipleLicensee_1"; String[] acknowledgeMessages1 = new String[] {"testAcknowledgementMultipleLicensee_1"}; @@ -88,62 +67,49 @@ public class LicensesAcknowledgementTests extends ESSingleNodeTestCase { licensee1.setAcknowledgementMessages(acknowledgeMessages1); TestUtils.AssertingLicensee licensee2 = new TestUtils.AssertingLicensee(id2, logger); licensee2.setAcknowledgementMessages(acknowledgeMessages2); - awaitNoBlock(client()); licensesService.register(licensee1); licensesService.register(licensee2); - awaitNoPendingTasks(client()); // try installing a signed license License signedLicense = generateSignedLicense(TimeValue.timeValueHours(10)); PutLicenseRequest putLicenseRequest = new PutLicenseRequest().license(signedLicense); - CountDownLatch latch = new CountDownLatch(1); // ensure acknowledgement message was part of the response final HashMap expectedMessages = new HashMap<>(); expectedMessages.put(id1, acknowledgeMessages1); expectedMessages.put(id2, acknowledgeMessages2); licensesService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID, - expectedMessages, latch)); - if (!latch.await(5, TimeUnit.SECONDS)) { - fail("waiting too long for a response to license registration"); - } - awaitNoPendingTasks(client()); + expectedMessages)); + verify(clusterService, times(0)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class)); assertThat(licensee2.acknowledgementRequested.size(), equalTo(1)); assertThat(licensee2.acknowledgementRequested.get(0).v2(), equalTo(signedLicense)); assertThat(licensee1.acknowledgementRequested.size(), equalTo(1)); assertThat(licensee1.acknowledgementRequested.get(0).v2(), equalTo(signedLicense)); assertThat(licensesService.getLicense(), not(signedLicense)); - latch = new CountDownLatch(1); // try installing a signed license with acknowledgement putLicenseRequest = new PutLicenseRequest().license(signedLicense).acknowledge(true); // ensure license was installed and no acknowledgment message was returned licensee1.setAcknowledgementMessages(new String[0]); licensee2.setAcknowledgementMessages(new String[0]); licensesService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(true, LicensesStatus.VALID, - Collections.emptyMap(), latch)); - if (!latch.await(5, TimeUnit.SECONDS)) { - fail("waiting too long for a response to license registration"); - } - awaitNoPendingTasks(client()); - assertThat(licensesService.getLicense(), equalTo(signedLicense)); + Collections.emptyMap())); + verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class)); licensesService.stop(); } - private static class AssertingLicensesUpdateResponse implements ActionListener { + private static class AssertingLicensesUpdateResponse implements ActionListener { private final boolean expectedAcknowledgement; private final LicensesStatus expectedStatus; private final Map expectedAckMessages; - private final CountDownLatch latch; public AssertingLicensesUpdateResponse(boolean expectedAcknowledgement, LicensesStatus expectedStatus, - Map expectedAckMessages, CountDownLatch latch) { + Map expectedAckMessages) { this.expectedAcknowledgement = expectedAcknowledgement; this.expectedStatus = expectedStatus; this.expectedAckMessages = expectedAckMessages; - this.latch = latch; } @Override - public void onResponse(LicensesService.LicensesUpdateResponse licensesUpdateResponse) { + public void onResponse(PutLicenseResponse licensesUpdateResponse) { assertThat(licensesUpdateResponse.isAcknowledged(), equalTo(expectedAcknowledgement)); assertThat(licensesUpdateResponse.status(), equalTo(expectedStatus)); assertThat(licensesUpdateResponse.acknowledgeMessages().size(), equalTo(expectedAckMessages.size())); @@ -153,12 +119,10 @@ public class LicensesAcknowledgementTests extends ESSingleNodeTestCase { String[] actualMessages = actual.get(expectedEntry.getKey()); assertThat(actualMessages, equalTo(expectedEntry.getValue())); } - latch.countDown(); } @Override public void onFailure(Exception throwable) { - latch.countDown(); } } -} +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesExpirationCallbackTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesExpirationCallbackTests.java deleted file mode 100644 index 13f9a132ed7..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesExpirationCallbackTests.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin.core; - -import org.elasticsearch.cluster.metadata.MetaData; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.license.core.License; -import org.elasticsearch.license.plugin.TestUtils; -import org.elasticsearch.test.ESSingleNodeTestCase; - -import java.util.Arrays; -import java.util.Collections; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.elasticsearch.common.unit.TimeValue.timeValueMillis; -import static org.hamcrest.Matchers.equalTo; - -public class LicensesExpirationCallbackTests extends ESSingleNodeTestCase { - static { - MetaData.registerPrototype(LicensesMetaData.TYPE, LicensesMetaData.PROTO); - } - - @Override - protected boolean resetNodeAfterTest() { - return true; - } - - public void testPostExpiration() throws Exception { - int postExpirySeconds = randomIntBetween(5, 10); - TimeValue postExpiryDuration = TimeValue.timeValueSeconds(postExpirySeconds); - TimeValue min = TimeValue.timeValueSeconds(postExpirySeconds - randomIntBetween(1, 3)); - TimeValue max = TimeValue.timeValueSeconds(postExpirySeconds + randomIntBetween(1, 10)); - - final LicensesService.ExpirationCallback.Post post = new LicensesService.ExpirationCallback.Post(min, max, timeValueMillis(10)) { - @Override - public void on(License license) { - } - }; - long now = System.currentTimeMillis(); - assertThat(post.matches(now - postExpiryDuration.millis(), now), equalTo(true)); - assertThat(post.matches(now + postExpiryDuration.getMillis(), now), equalTo(false)); - } - - public void testPostExpirationWithNullMax() throws Exception { - int postExpirySeconds = randomIntBetween(5, 10); - TimeValue postExpiryDuration = TimeValue.timeValueSeconds(postExpirySeconds); - TimeValue min = TimeValue.timeValueSeconds(postExpirySeconds - randomIntBetween(1, 3)); - - final LicensesService.ExpirationCallback.Post post = new LicensesService.ExpirationCallback.Post(min, null, timeValueMillis(10)) { - @Override - public void on(License license) { - } - }; - long now = System.currentTimeMillis(); - assertThat(post.matches(now - postExpiryDuration.millis(), now), equalTo(true)); - } - - public void testPreExpirationWithNullMin() throws Exception { - int expirySeconds = randomIntBetween(5, 10); - TimeValue expiryDuration = TimeValue.timeValueSeconds(expirySeconds); - TimeValue max = TimeValue.timeValueSeconds(expirySeconds + randomIntBetween(1, 10)); - - final LicensesService.ExpirationCallback.Pre pre = new LicensesService.ExpirationCallback.Pre(null, max, timeValueMillis(10)) { - @Override - public void on(License license) { - } - }; - long now = System.currentTimeMillis(); - assertThat(pre.matches(expiryDuration.millis() + now, now), equalTo(true)); - } - - public void testPreExpiration() throws Exception { - int expirySeconds = randomIntBetween(5, 10); - TimeValue expiryDuration = TimeValue.timeValueSeconds(expirySeconds); - TimeValue min = TimeValue.timeValueSeconds(expirySeconds - randomIntBetween(0, 3)); - TimeValue max = TimeValue.timeValueSeconds(expirySeconds + randomIntBetween(1, 10)); - - final LicensesService.ExpirationCallback.Pre pre = new LicensesService.ExpirationCallback.Pre(min, max, timeValueMillis(10)) { - @Override - public void on(License license) { - } - }; - long now = System.currentTimeMillis(); - assertThat(pre.matches(expiryDuration.millis() + now, now), equalTo(true)); - assertThat(pre.matches(now - expiryDuration.getMillis(), now), equalTo(false)); - } - - public void testPreExpirationNotification() throws Exception { - final LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(5)); - AtomicInteger counter = new AtomicInteger(0); - // 2000, 1600, 1200 - licensesService.setExpirationCallbacks(Collections.singletonList( - preCallbackLatch(TimeValue.timeValueSeconds(1), TimeValue.timeValueSeconds(2), timeValueMillis(400), counter)) - ); - licensesService.start(); - TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee("testPreExpirationNotification", logger); - licensesService.register(licensee); - boolean success = awaitBusy(() -> (counter.get() == 3 || counter.get() == 2)); - assertThat("counter: actual: " + counter.get() + "vs expected: 3", success, equalTo(true)); - licensesService.stop(); - } - - public void testPostExpirationNotification() throws Exception { - final LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(3)); - AtomicInteger counter = new AtomicInteger(0); - // 700, 1700, 2700 - licensesService.setExpirationCallbacks(Collections.singletonList( - postCallbackLatch(timeValueMillis(700), TimeValue.timeValueSeconds(3), TimeValue.timeValueSeconds(1), counter)) - ); - licensesService.start(); - TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee("testPostExpirationNotification", logger); - licensesService.register(licensee); - // callback can be called only twice if the third notification is triggered with a delay - // causing the trigger time to be out of the post expiry callback window - boolean success = awaitBusy(() -> (counter.get() == 3 || counter.get() == 2)); - assertThat("counter: actual: " + counter.get() + "vs expected: 3", success, equalTo(true)); - licensesService.stop(); - } - - public void testMultipleExpirationNotification() throws Exception { - final LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(4)); - AtomicInteger postCounter = new AtomicInteger(0); - AtomicInteger preCounter = new AtomicInteger(0); - licensesService.setExpirationCallbacks(Arrays.asList( - // 2000, 1600, 1200 - preCallbackLatch(TimeValue.timeValueSeconds(1), TimeValue.timeValueSeconds(2), timeValueMillis(400), preCounter), - // 100, 500, 900, 1300, 1700 - postCallbackLatch(timeValueMillis(100), TimeValue.timeValueSeconds(2), timeValueMillis(400), postCounter)) - ); - licensesService.start(); - TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee("testMultipleExpirationNotification", logger); - licensesService.register(licensee); - // callback can be called one less than expected if the last notification is triggered - // with a delay, causing the trigger time to be out of the expiry callback window - boolean success = awaitBusy(() -> ((preCounter.get() == 3 || preCounter.get() == 2) - && (postCounter.get() == 5 || postCounter.get() == 4))); - assertThat("post count: actual: " + postCounter.get() + "vs expected: 5 " + - "pre count: actual: " + preCounter.get() + " vs expected: 3", success, equalTo(true)); - licensesService.stop(); - } - - private static LicensesService.ExpirationCallback preCallbackLatch(TimeValue min, TimeValue max, TimeValue frequency, - final AtomicInteger count) { - return new LicensesService.ExpirationCallback.Pre(min, max, frequency) { - @Override - public void on(License license) { - count.incrementAndGet(); - } - }; - } - - private static LicensesService.ExpirationCallback postCallbackLatch(TimeValue min, TimeValue max, TimeValue frequency, - final AtomicInteger count) { - return new LicensesService.ExpirationCallback.Post(min, max, frequency) { - @Override - public void on(License license) { - count.incrementAndGet(); - } - }; - } -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesExpiryNotificationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesExpiryNotificationTests.java deleted file mode 100644 index dceab4627aa..00000000000 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesExpiryNotificationTests.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.license.plugin.core; - -import org.apache.lucene.util.LuceneTestCase.BadApple; -import org.elasticsearch.cluster.metadata.MetaData; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.license.plugin.TestUtils.AssertingLicensee; -import org.elasticsearch.test.ESSingleNodeTestCase; - -import java.util.List; - -import static org.elasticsearch.license.plugin.TestUtils.awaitNoBlock; -import static org.elasticsearch.license.plugin.TestUtils.awaitNoPendingTasks; -import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense; -import static org.elasticsearch.license.plugin.TestUtils.registerAndAckSignedLicenses; -import static org.hamcrest.Matchers.equalTo; - -//test is just too slow, please fix it to not be sleep-based -@BadApple(bugUrl = "https://github.com/elastic/x-plugins/issues/1007") -public class LicensesExpiryNotificationTests extends ESSingleNodeTestCase { - static { - MetaData.registerPrototype(LicensesMetaData.TYPE, LicensesMetaData.PROTO); - } - - @Override - protected boolean resetNodeAfterTest() { - return true; - } - - public void testTrialLicenseEnforcement() throws Exception { - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(5)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(3)); - licensesService.start(); - String id1 = "testTrialLicenseEnforcement"; - final AssertingLicensee licensee = new AssertingLicensee(id1, logger); - awaitNoBlock(client()); - licensesService.register(licensee); - awaitNoPendingTasks(client()); - boolean success = awaitBusy(() -> licensee.statuses.size() == 3); - // trail license: enable, grace, disabled - assertLicenseStates(licensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - assertTrue(dumpLicensingStates(licensee.statuses), success); - licensesService.stop(); - } - - public void testTrialLicenseEnforcementMultipleLicensees() throws Exception { - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(5)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(3)); - licensesService.start(); - String id1 = "testTrialLicenseEnforcementMultipleLicensees_1"; - final AssertingLicensee licensee1 = new AssertingLicensee(id1, logger); - String id12 = "testTrialLicenseEnforcementMultipleLicensees_2"; - final AssertingLicensee licensee2 = new AssertingLicensee(id12, logger); - awaitNoBlock(client()); - licensesService.register(licensee1); - licensesService.register(licensee2); - awaitNoPendingTasks(client()); - boolean success = awaitBusy(() -> licensee1.statuses.size() == 3); - assertTrue(dumpLicensingStates(licensee1.statuses), success); - success = awaitBusy(() -> licensee2.statuses.size() == 3); - assertTrue(dumpLicensingStates(licensee2.statuses), success); - // trail license: enable, grace, disabled - assertLicenseStates(licensee1, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - assertLicenseStates(licensee2, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - licensesService.stop(); - } - - public void testTrialSignedLicenseEnforcement() throws Exception { - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(2)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(3)); - licensesService.start(); - String id1 = "testTrialSignedLicenseEnforcement"; - final AssertingLicensee licensee = new AssertingLicensee(id1, logger); - awaitNoBlock(client()); - licensesService.register(licensee); - awaitNoPendingTasks(client()); - boolean success = awaitBusy(() -> licensee.statuses.size() == 1); - assertTrue(dumpLicensingStates(licensee.statuses), success); - registerAndAckSignedLicenses(licensesService, generateSignedLicense(TimeValue.timeValueSeconds(4)), LicensesStatus.VALID); - success = awaitBusy(() -> licensee.statuses.size() == 4); - // trial: enable, signed: enable, signed: grace, signed: disabled - assertLicenseStates(licensee, LicenseState.ENABLED, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - assertTrue(dumpLicensingStates(licensee.statuses), success); - licensesService.stop(); - } - - public void testSignedLicenseEnforcement() throws Exception { - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(4)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(3)); - licensesService.start(); - String id1 = "testSignedLicenseEnforcement"; - final AssertingLicensee licensee = new AssertingLicensee(id1, logger); - awaitNoBlock(client()); - registerAndAckSignedLicenses(licensesService, generateSignedLicense(TimeValue.timeValueSeconds(2)), LicensesStatus.VALID); - licensesService.register(licensee); - awaitNoPendingTasks(client()); - boolean success = awaitBusy(() -> licensee.statuses.size() == 3); - // signed: enable, signed: grace, signed: disabled - assertLicenseStates(licensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - assertTrue(dumpLicensingStates(licensee.statuses), success); - licensesService.stop(); - } - - public void testSingedLicenseEnforcementMultipleLicensees() throws Exception { - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(4)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(3)); - licensesService.start(); - String id1 = "testSingedLicenseEnforcementMultipleLicensees_1"; - final AssertingLicensee licensee1 = new AssertingLicensee(id1, logger); - String id12 = "testSingedLicenseEnforcementMultipleLicensees_2"; - final AssertingLicensee licensee2 = new AssertingLicensee(id12, logger); - awaitNoBlock(client()); - registerAndAckSignedLicenses(licensesService, generateSignedLicense(TimeValue.timeValueSeconds(2)), LicensesStatus.VALID); - licensesService.register(licensee1); - licensesService.register(licensee2); - awaitNoPendingTasks(client()); - boolean success = awaitBusy(() -> licensee1.statuses.size() == 3); - assertTrue(dumpLicensingStates(licensee1.statuses), success); - success = awaitBusy(() -> licensee2.statuses.size() == 3); - assertTrue(dumpLicensingStates(licensee2.statuses), success); - // signed license: enable, grace, disabled - assertLicenseStates(licensee1, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - assertLicenseStates(licensee2, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - licensesService.stop(); - } - - public void testMultipleSignedLicenseEnforcement() throws Exception { - // register with trial license and assert onEnable and onDisable notification - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(4)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(1)); - licensesService.start(); - String id1 = "testMultipleSignedLicenseEnforcement"; - final AssertingLicensee licensee = new AssertingLicensee(id1, logger); - awaitNoBlock(client()); - licensesService.register(licensee); - awaitNoPendingTasks(client()); - // trial license enabled - boolean success = awaitBusy(() -> licensee.statuses.size() == 1); - assertTrue(dumpLicensingStates(licensee.statuses), success); - registerAndAckSignedLicenses(licensesService, generateSignedLicense("basic", TimeValue.timeValueSeconds(3)), LicensesStatus.VALID); - // signed license enabled - success = awaitBusy(() -> licensee.statuses.size() == 2); - assertTrue(dumpLicensingStates(licensee.statuses), success); - registerAndAckSignedLicenses(licensesService, generateSignedLicense("gold", TimeValue.timeValueSeconds(2)), LicensesStatus.VALID); - // second signed license enabled, grace and expired - success = awaitBusy(() ->licensee.statuses.size() == 5); - assertLicenseStates(licensee, LicenseState.ENABLED, LicenseState.ENABLED, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, - LicenseState.DISABLED); - assertTrue(dumpLicensingStates(licensee.statuses), success); - licensesService.stop(); - } - - public void testNonOverlappingMultipleLicensesEnforcement() throws Exception { - // register with trial license and assert onEnable and onDisable notification - LicensesService licensesService = getInstanceFromNode(LicensesService.class); - licensesService.setTrialLicenseDuration(TimeValue.timeValueSeconds(3)); - licensesService.setGracePeriodDuration(TimeValue.timeValueSeconds(1)); - licensesService.start(); - String id1 = "testNonOverlappingMultipleLicensesEnforcement"; - final AssertingLicensee licensee = new AssertingLicensee(id1, logger); - awaitNoBlock(client()); - licensesService.register(licensee); - // trial license: enabled, grace, disabled - boolean success = awaitBusy(() -> licensee.statuses.size() == 3); - - assertTrue(dumpLicensingStates(licensee.statuses), success); - // install license - registerAndAckSignedLicenses(licensesService, generateSignedLicense("basic", TimeValue.timeValueSeconds(2)), LicensesStatus.VALID); - // trial license: enabled, grace, disabled, signed license: enabled, grace, disabled - success = awaitBusy(() -> licensee.statuses.size() == 6); - assertLicenseStates(licensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED, LicenseState.ENABLED, - LicenseState.GRACE_PERIOD, LicenseState.DISABLED); - assertTrue(dumpLicensingStates(licensee.statuses), success); - licensesService.stop(); - } - - private void assertLicenseStates(AssertingLicensee licensee, LicenseState... states) { - StringBuilder msg = new StringBuilder(); - msg.append("Actual: "); - msg.append(dumpLicensingStates(licensee.statuses)); - msg.append(" Expected: "); - msg.append(dumpLicensingStates(states)); - assertThat(msg.toString(), licensee.statuses.size(), equalTo(states.length)); - for (int i = 0; i < states.length; i++) { - assertThat(msg.toString(), licensee.statuses.get(i).getLicenseState(), equalTo(states[i])); - } - } - - private String dumpLicensingStates(List statuses) { - return dumpLicensingStates(statuses.toArray(new Licensee.Status[statuses.size()])); - } - - private String dumpLicensingStates(Licensee.Status... statuses) { - LicenseState[] states = new LicenseState[statuses.length]; - for (int i = 0; i < statuses.length; i++) { - states[i] = statuses[i].getLicenseState(); - } - return dumpLicensingStates(states); - } - - private String dumpLicensingStates(LicenseState... states) { - StringBuilder sb = new StringBuilder(); - sb.append("["); - for (int i = 0; i < states.length; i++) { - sb.append(states[i].name()); - if (i != states.length - 1) { - sb.append(", "); - } - } - sb.append("]"); - return sb.toString(); - } -} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesManagerServiceTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesManagerServiceTests.java index cab7a9ce79d..b7d8f75afa2 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesManagerServiceTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesManagerServiceTests.java @@ -7,14 +7,22 @@ package org.elasticsearch.license.plugin.core; import org.elasticsearch.action.ActionListener; import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse; -import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.xpack.graph.Graph; import org.elasticsearch.license.core.License; import org.elasticsearch.license.plugin.TestUtils; import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest; +import org.elasticsearch.xpack.monitoring.Monitoring; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.xpack.security.Security; import org.elasticsearch.test.ESSingleNodeTestCase; +import org.elasticsearch.xpack.XPackPlugin; +import org.elasticsearch.xpack.watcher.Watcher; +import java.util.Collection; +import java.util.Collections; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; @@ -26,8 +34,19 @@ import static org.hamcrest.Matchers.not; public class LicensesManagerServiceTests extends ESSingleNodeTestCase { - static { - MetaData.registerPrototype(LicensesMetaData.TYPE, LicensesMetaData.PROTO); + @Override + protected Collection> getPlugins() { + return Collections.singletonList(XPackPlugin.class); + } + + @Override + protected Settings nodeSettings() { + return Settings.builder(). + put(XPackPlugin.featureEnabledSetting(Security.NAME), false) + .put(XPackPlugin.featureEnabledSetting(Monitoring.NAME), false) + .put(XPackPlugin.featureEnabledSetting(Watcher.NAME), false) + .put(XPackPlugin.featureEnabledSetting(Graph.NAME), false) + .build(); } @Override @@ -90,9 +109,7 @@ public class LicensesManagerServiceTests extends ESSingleNodeTestCase { // ensure that the invalid license never made it to cluster state LicensesMetaData licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE); - if (licensesMetaData != null) { - assertThat(licensesMetaData.getLicense(), equalTo(LicensesMetaData.LICENSE_TOMBSTONE)); - } + assertThat(licensesMetaData.getLicense(), not(equalTo(tamperedLicense))); } public void testRemoveLicenses() throws Exception { @@ -185,4 +202,4 @@ public class LicensesManagerServiceTests extends ESSingleNodeTestCase { } assertThat("remove license(s) failed", success.get(), equalTo(true)); } -} +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesNotificationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesNotificationTests.java new file mode 100644 index 00000000000..a6824ad5867 --- /dev/null +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesNotificationTests.java @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.plugin.core; + +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.license.core.License; +import org.elasticsearch.license.plugin.TestUtils; +import org.elasticsearch.license.plugin.TestUtils.AssertingLicensee; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; + +import java.util.List; + +import static org.hamcrest.Matchers.equalTo; + +public class LicensesNotificationTests extends AbstractLicenseServiceTestCase { + + public void testLicenseNotification() throws Exception { + final License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(48)); + setInitialState(license); + licensesService.start(); + int nLicensee = randomIntBetween(1, 3); + AssertingLicensee[] assertingLicensees = new AssertingLicensee[nLicensee]; + for (int i = 0; i < assertingLicensees.length; i++) { + assertingLicensees[i] = new AssertingLicensee("testLicenseNotification" + i, logger); + licensesService.register(assertingLicensees[i]); + assertLicenseStates(assertingLicensees[i], LicenseState.ENABLED); + } + clock.fastForward(TimeValue.timeValueMillis(license.expiryDate() - clock.millis())); + final LicensesMetaData licensesMetaData = new LicensesMetaData(license); + licensesService.onUpdate(licensesMetaData); + for (AssertingLicensee assertingLicensee : assertingLicensees) { + assertLicenseStates(assertingLicensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD); + } + clock.fastForward(TimeValue.timeValueMillis((license.expiryDate() + + LicensesService.GRACE_PERIOD_DURATION.getMillis()) - clock.millis())); + licensesService.onUpdate(licensesMetaData); + for (AssertingLicensee assertingLicensee : assertingLicensees) { + assertLicenseStates(assertingLicensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); + } + clock.setTime(new DateTime(DateTimeZone.UTC)); + final License newLicense = TestUtils.generateSignedLicense(TimeValue.timeValueHours(2)); + clock.fastForward(TimeValue.timeValueHours(1)); + licensesService.onUpdate(new LicensesMetaData(newLicense)); + for (AssertingLicensee assertingLicensee : assertingLicensees) { + assertLicenseStates(assertingLicensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED, + LicenseState.ENABLED); + } + licensesService.stop(); + } + + private void assertLicenseStates(AssertingLicensee licensee, LicenseState... states) { + StringBuilder msg = new StringBuilder(); + msg.append("Actual: "); + msg.append(dumpLicensingStates(licensee.statuses)); + msg.append(" Expected: "); + msg.append(dumpLicensingStates(states)); + assertThat(msg.toString(), licensee.statuses.size(), equalTo(states.length)); + for (int i = 0; i < states.length; i++) { + assertThat(msg.toString(), licensee.statuses.get(i).getLicenseState(), equalTo(states[i])); + } + } + + private String dumpLicensingStates(List statuses) { + return dumpLicensingStates(statuses.toArray(new Licensee.Status[statuses.size()])); + } + + private String dumpLicensingStates(Licensee.Status... statuses) { + LicenseState[] states = new LicenseState[statuses.length]; + for (int i = 0; i < statuses.length; i++) { + states[i] = statuses[i].getLicenseState(); + } + return dumpLicensingStates(states); + } + + private String dumpLicensingStates(LicenseState... states) { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < states.length; i++) { + sb.append(states[i].name()); + if (i != states.length - 1) { + sb.append(", "); + } + } + sb.append("]"); + return sb.toString(); + } +} \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/license.delete.json b/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/xpack.license.delete.json similarity index 90% rename from elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/license.delete.json rename to elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/xpack.license.delete.json index f30f75fc478..7a9ad12eef3 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/license.delete.json +++ b/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/xpack.license.delete.json @@ -1,5 +1,5 @@ { - "license.delete": { + "xpack.license.delete": { "documentation": "https://www.elastic.co/guide/en/shield/current/license-management.html", "methods": ["DELETE"], "url": { diff --git a/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/license.get.json b/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/xpack.license.get.json similarity index 94% rename from elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/license.get.json rename to elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/xpack.license.get.json index 88d6d6b2d77..2849ad2e31b 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/license.get.json +++ b/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/xpack.license.get.json @@ -1,5 +1,5 @@ { - "license.get": { + "xpack.license.get": { "documentation": "https://www.elastic.co/guide/en/shield/current/license-management.html", "methods": ["GET"], "url": { diff --git a/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/license.post.json b/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/xpack.license.post.json similarity index 95% rename from elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/license.post.json rename to elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/xpack.license.post.json index 3596a0b547b..0b51d2c5ad7 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/license.post.json +++ b/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/api/xpack.license.post.json @@ -1,5 +1,5 @@ { - "license.post": { + "xpack.license.post": { "documentation": "https://www.elastic.co/guide/en/shield/current/license-management.html", "methods": ["PUT", "POST"], "url": { diff --git a/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/test/license/20_put_license.yaml b/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/test/license/20_put_license.yaml index 21af326eeeb..a5873dba99f 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/test/license/20_put_license.yaml +++ b/elasticsearch/x-pack/license-plugin/src/test/resources/rest-api-spec/test/license/20_put_license.yaml @@ -1,72 +1,78 @@ --- -"installing and getting license works": +teardown: + - do: + xpack.license.post: + acknowledge: true + body: | + {"licenses":[{"uid":"894371dc-9t49-4997-93cb-8o2e3r7fa6a8","type":"trial","issue_date_in_millis":1411948800000,"expiry_date_in_millis":1916956799999,"max_nodes":1,"issued_to":"issuedTo","issuer":"issuer","signature":"AAAAAgAAAA0FWh0T9njItjQ2qammAAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTnFrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVmZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQBZhvozA0trrxhUZ1QbaTsKTna9C5KVQ6pv8yg1pnsBpZXCl8kX1SrgoFn1bXq61IvJwfw5qnmYNiH3hRhTO9EyaCBqaLk8NXZQ6TrRkQSpEnnBwAYUkZeKXsIuBoOk4B4mzwC/r8aMAkzrTiEBtBbog+57cSaU9y37Gkdd+1jXCQrxP+jOEUf7gnXWZvE6oeRroLvCt1fYn09k0CF8kKTbrPTSjC6igZR3uvTHyee74XQ9PRavvHax73T4UOEdQZX/P1ibSQIWKbBRD5YQ1POYVjTayoltTnWLMxfEcAkkATJZLhpBEHST7kZWjrTS6J1dCReJc7a8Vsj/78HXvOIy"}]} +--- +"Installing and getting license works": ## current license version - do: - license.post: - acknowledge: true - body: | - {"license":{"uid":"893361dc-9749-4997-93cb-802e3d7fa4a8","type":"basic","issue_date_in_millis":1411948800000,"expiry_date_in_millis":1914278399999,"max_nodes":1,"issued_to":"issuedTo","issuer":"issuer","signature":"AAAAAgAAAA0lKPZ0a7aZquUltho/AAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTnFrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVmZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQAALuQ44S3IG6SzolcXVJ6Z4CIXORDrYQ+wdLCeey0XdujTslAOj+k+vNgo6wauc7Uswi01esHu4lb5IgpvKy7RRCbh5bj/z2ubu2qMJqopp9BQyD7VQjVfqmG6seUMJwJ1a5Avvm9r41YPSPcrii3bKK2e1l6jK6N8ibCvnTyY/XkYGCJrBWTSJePDbg6ErbyodrZ37x1StLbPWcNAkmweyHjDJnvYnbeZZO7A3NmubXZjW7Ttf8/YwQyE00PqMcl7fVPY3hkKpAeHf8aaJbqkKYbqZuER3EWJX7ZvLVb1dNdNg8aXRn7YrkQcYwWgptYQpfV+D7yEJ4j5muAEoler"}} + xpack.license.post: + acknowledge: true + body: | + {"license":{"uid":"893361dc-9749-4997-93cb-802e3d7fa4a8","type":"basic","issue_date_in_millis":1411948800000,"expiry_date_in_millis":1914278399999,"max_nodes":1,"issued_to":"issuedTo","issuer":"issuer","signature":"AAAAAgAAAA0lKPZ0a7aZquUltho/AAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTnFrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVmZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQAALuQ44S3IG6SzolcXVJ6Z4CIXORDrYQ+wdLCeey0XdujTslAOj+k+vNgo6wauc7Uswi01esHu4lb5IgpvKy7RRCbh5bj/z2ubu2qMJqopp9BQyD7VQjVfqmG6seUMJwJ1a5Avvm9r41YPSPcrii3bKK2e1l6jK6N8ibCvnTyY/XkYGCJrBWTSJePDbg6ErbyodrZ37x1StLbPWcNAkmweyHjDJnvYnbeZZO7A3NmubXZjW7Ttf8/YwQyE00PqMcl7fVPY3hkKpAeHf8aaJbqkKYbqZuER3EWJX7ZvLVb1dNdNg8aXRn7YrkQcYwWgptYQpfV+D7yEJ4j5muAEoler"}} - match: { license_status: "valid" } - do: - license.get: {} + xpack.license.get: {} ## a license object has 10 attributes - length: { license: 10 } ## bwc for licenses format - do: - license.post: - acknowledge: true - body: | - {"licenses":[{"uid":"893361dc-9749-4997-93cb-802e3d7fa4a8","type":"basic","issue_date_in_millis":1411948800000,"expiry_date_in_millis":1914278399999,"max_nodes":1,"issued_to":"issuedTo","issuer":"issuer","signature":"AAAAAgAAAA0lKPZ0a7aZquUltho/AAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTnFrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVmZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQAALuQ44S3IG6SzolcXVJ6Z4CIXORDrYQ+wdLCeey0XdujTslAOj+k+vNgo6wauc7Uswi01esHu4lb5IgpvKy7RRCbh5bj/z2ubu2qMJqopp9BQyD7VQjVfqmG6seUMJwJ1a5Avvm9r41YPSPcrii3bKK2e1l6jK6N8ibCvnTyY/XkYGCJrBWTSJePDbg6ErbyodrZ37x1StLbPWcNAkmweyHjDJnvYnbeZZO7A3NmubXZjW7Ttf8/YwQyE00PqMcl7fVPY3hkKpAeHf8aaJbqkKYbqZuER3EWJX7ZvLVb1dNdNg8aXRn7YrkQcYwWgptYQpfV+D7yEJ4j5muAEoler"}]} + xpack.license.post: + acknowledge: true + body: | + {"licenses":[{"uid":"893361dc-9749-4997-93cb-802e3d7fa4a8","type":"basic","issue_date_in_millis":1411948800000,"expiry_date_in_millis":1914278399999,"max_nodes":1,"issued_to":"issuedTo","issuer":"issuer","signature":"AAAAAgAAAA0lKPZ0a7aZquUltho/AAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTnFrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVmZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQAALuQ44S3IG6SzolcXVJ6Z4CIXORDrYQ+wdLCeey0XdujTslAOj+k+vNgo6wauc7Uswi01esHu4lb5IgpvKy7RRCbh5bj/z2ubu2qMJqopp9BQyD7VQjVfqmG6seUMJwJ1a5Avvm9r41YPSPcrii3bKK2e1l6jK6N8ibCvnTyY/XkYGCJrBWTSJePDbg6ErbyodrZ37x1StLbPWcNAkmweyHjDJnvYnbeZZO7A3NmubXZjW7Ttf8/YwQyE00PqMcl7fVPY3hkKpAeHf8aaJbqkKYbqZuER3EWJX7ZvLVb1dNdNg8aXRn7YrkQcYwWgptYQpfV+D7yEJ4j5muAEoler"}]} - match: { license_status: "valid" } - do: - license.get: {} + xpack.license.get: {} - length: { license: 10 } ## license version: 1.x - do: - license.post: - acknowledge: true - body: | - {"licenses":[{"uid":"893361dc-9749-4997-93cb-802e3d7fa4a8","type":"subscription","subscription_type":"gold","issue_date_in_millis":1411948800000,"feature":"shield","expiry_date_in_millis":1914278399999,"max_nodes":1,"issued_to":"issuedTo","issuer":"issuer","signature":"AAAAAQAAAA0LVAywwpSH94cyXr4zAAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTnFrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVmZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQA4qscc/URRZVdFoLwgy9dqybYEQLW8YLkiAyPV5XHHHdtk+dtZIepiNEDkUXhSX2waVJlsNRF8/4kqplDfwNoD2TUM8fTgiIfiSiZYGDTGST+yW/5eAveEU5J5v1liBN27bwkqL+V4YAa0Tcm7NKKwjScWKAHiTU3vF8chPkGfCHE0kQgVwPC9RE82pTw0s6/uR4PfLGNFfqPM0uiE5nucfVrtj89JQiO/KA/7ZyFbo7VTNXxZQt7T7rZWBCP9KIjptXzcWuk08Q5S+rSoJNYbFo3HGKtrCVsRz/55rceNtdwKKXu1IwnSeir4I1/KLduQTtFLy0+1th87VS8T88UT"}]} + xpack.license.post: + acknowledge: true + body: | + {"licenses":[{"uid":"893361dc-9749-4997-93cb-802e3d7fa4a8","type":"subscription","subscription_type":"gold","issue_date_in_millis":1411948800000,"feature":"shield","expiry_date_in_millis":1914278399999,"max_nodes":1,"issued_to":"issuedTo","issuer":"issuer","signature":"AAAAAQAAAA0LVAywwpSH94cyXr4zAAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTnFrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVmZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQA4qscc/URRZVdFoLwgy9dqybYEQLW8YLkiAyPV5XHHHdtk+dtZIepiNEDkUXhSX2waVJlsNRF8/4kqplDfwNoD2TUM8fTgiIfiSiZYGDTGST+yW/5eAveEU5J5v1liBN27bwkqL+V4YAa0Tcm7NKKwjScWKAHiTU3vF8chPkGfCHE0kQgVwPC9RE82pTw0s6/uR4PfLGNFfqPM0uiE5nucfVrtj89JQiO/KA/7ZyFbo7VTNXxZQt7T7rZWBCP9KIjptXzcWuk08Q5S+rSoJNYbFo3HGKtrCVsRz/55rceNtdwKKXu1IwnSeir4I1/KLduQTtFLy0+1th87VS8T88UT"}]} - match: { license_status: "valid" } - do: - license.get: {} + xpack.license.get: {} - length: { license: 10 } ## multiple licenses version: 1.x - do: - license.post: - acknowledge: true - body: | - {"licenses":[{"uid":"893361dc-9749-4997-93cb-802e3d7fa4a8","type":"internal","subscription_type":"none","issue_date_in_millis":1411948800000,"feature":"shield","expiry_date_in_millis":1440892799999,"max_nodes":1,"issued_to":"issuedTo","issuer":"issuer","signature":"AAAAAQAAAA04Q4ky3rFyyWLFkytEAAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTnFrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVmZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQBxMvUMn4h2E4R4TQMijahTxQj4LPQO4f1M79UxX/XkDlGcH+J5pRHx08OtTRPsFL1lED+h+PIXx307Vo+PNDsOxrWvoYZeYBkOLAO3ny9vhQga+52jYhMxIuFrT9xbcSCSNpMhGojgOIPU2WgiopVdVcimo1+Gk8VtklPB1wPwFzfOjOnPgp/Icx3WYpfkeAUUOyWUYiFIBAe4bnz84iF+xwLKbgYk6aHF25ECBtdb/Uruhcm9+jEFpoIEUtCouvvk9C+NJZ4OickV4xpRgaRG2x9PONH8ZN0QGhGYhJGbisoCxuDmlLsyVxqxfMu3n/r7/jdsEJScjAlSrsLDOu6H"},{"uid":"893361dc-9749-4997-93cb-802e3dofh7aa","type":"internal","subscription_type":"none","issue_date_in_millis":1443484800000,"feature":"watcher","expiry_date_in_millis":1914278399999,"max_nodes":1,"issued_to":"issuedTo","issuer":"issuer","signature":"AAAAAQAAAA0Sc90guRIaQEmgLvMnAAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTnFrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVmZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQCQ94dju0pnDZR3Uuypi0ic3aQJ+nvVqe+U8u79Dga5n1qIjcHDh7HvIBJEkF+tnVPlo/PXV/x7BZSwVY1PVErit+6rYix1yuHEgqwxmx/VdRICjCaZM6tk0Ob4dZCPv6Ebn2Mmk89KHC/PwiLPqF6QfwV/Pkpa8k2A3ORJmvYSDvXhe6tCs8dqc4ebrsFxqrZjwWh5CZSpzqqZBFXlngDv2N0hHhpGlueRszD0JJ5dfEL5ZA1DDOrgO9OJVejSHyRqe1L5QRUNdXPVfS+EAG0Dd1cNdJ/sMpYCPnVjbw6iq2/YgM3cuztsXVBY7ij4WnoP3ce7Zjs9TwHn+IqzftC6"}]} + xpack.license.post: + acknowledge: true + body: | + {"licenses":[{"uid":"893361dc-9749-4997-93cb-802e3d7fa4a8","type":"internal","subscription_type":"none","issue_date_in_millis":1411948800000,"feature":"shield","expiry_date_in_millis":1440892799999,"max_nodes":1,"issued_to":"issuedTo","issuer":"issuer","signature":"AAAAAQAAAA04Q4ky3rFyyWLFkytEAAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTnFrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVmZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQBxMvUMn4h2E4R4TQMijahTxQj4LPQO4f1M79UxX/XkDlGcH+J5pRHx08OtTRPsFL1lED+h+PIXx307Vo+PNDsOxrWvoYZeYBkOLAO3ny9vhQga+52jYhMxIuFrT9xbcSCSNpMhGojgOIPU2WgiopVdVcimo1+Gk8VtklPB1wPwFzfOjOnPgp/Icx3WYpfkeAUUOyWUYiFIBAe4bnz84iF+xwLKbgYk6aHF25ECBtdb/Uruhcm9+jEFpoIEUtCouvvk9C+NJZ4OickV4xpRgaRG2x9PONH8ZN0QGhGYhJGbisoCxuDmlLsyVxqxfMu3n/r7/jdsEJScjAlSrsLDOu6H"},{"uid":"893361dc-9749-4997-93cb-802e3dofh7aa","type":"internal","subscription_type":"none","issue_date_in_millis":1443484800000,"feature":"watcher","expiry_date_in_millis":1914278399999,"max_nodes":1,"issued_to":"issuedTo","issuer":"issuer","signature":"AAAAAQAAAA0Sc90guRIaQEmgLvMnAAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTnFrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVmZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQCQ94dju0pnDZR3Uuypi0ic3aQJ+nvVqe+U8u79Dga5n1qIjcHDh7HvIBJEkF+tnVPlo/PXV/x7BZSwVY1PVErit+6rYix1yuHEgqwxmx/VdRICjCaZM6tk0Ob4dZCPv6Ebn2Mmk89KHC/PwiLPqF6QfwV/Pkpa8k2A3ORJmvYSDvXhe6tCs8dqc4ebrsFxqrZjwWh5CZSpzqqZBFXlngDv2N0hHhpGlueRszD0JJ5dfEL5ZA1DDOrgO9OJVejSHyRqe1L5QRUNdXPVfS+EAG0Dd1cNdJ/sMpYCPnVjbw6iq2/YgM3cuztsXVBY7ij4WnoP3ce7Zjs9TwHn+IqzftC6"}]} - match: { license_status: "valid" } - do: - license.get: {} + xpack.license.get: {} - length: { license: 10 } - match: { license.uid: "893361dc-9749-4997-93cb-802e3dofh7aa" } --- "Should throw 404 after license deletion": - - do: - license.delete: {} + xpack.license.delete: {} - match: { acknowledged: true } - do: + xpack.license.get: {} catch: missing - license.get: {} diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringSettings.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringSettings.java index 806070b2072..dc5f039b430 100644 --- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringSettings.java +++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringSettings.java @@ -52,55 +52,55 @@ public class MonitoringSettings extends AbstractComponent { * Sampling interval between two collections (default to 10s) */ public static final Setting INTERVAL = - timeSetting(key("agent.interval"), TimeValue.timeValueSeconds(10), Property.Dynamic, Property.NodeScope); + timeSetting(collectionKey("interval"), TimeValue.timeValueSeconds(10), Property.Dynamic, Property.NodeScope); /** * Timeout value when collecting index statistics (default to 10m) */ public static final Setting INDEX_STATS_TIMEOUT = - timeSetting(key("agent.index.stats.timeout"), TimeValue.timeValueSeconds(10), Property.Dynamic, Property.NodeScope); + timeSetting(collectionKey("index.stats.timeout"), TimeValue.timeValueSeconds(10), Property.Dynamic, Property.NodeScope); /** * Timeout value when collecting total indices statistics (default to 10m) */ public static final Setting INDICES_STATS_TIMEOUT = - timeSetting(key("agent.indices.stats.timeout"), TimeValue.timeValueSeconds(10), Property.Dynamic, Property.NodeScope); + timeSetting(collectionKey("indices.stats.timeout"), TimeValue.timeValueSeconds(10), Property.Dynamic, Property.NodeScope); /** * List of indices names whose stats will be exported (default to all indices) */ public static final Setting> INDICES = - listSetting(key("agent.indices"), Collections.emptyList(), Function.identity(), Property.Dynamic, Property.NodeScope); + listSetting(collectionKey("indices"), Collections.emptyList(), Function.identity(), Property.Dynamic, Property.NodeScope); /** * Timeout value when collecting the cluster state (default to 10m) */ public static final Setting CLUSTER_STATE_TIMEOUT = - timeSetting(key("agent.cluster.state.timeout"), TimeValue.timeValueSeconds(10), Property.Dynamic, Property.NodeScope); + timeSetting(collectionKey("cluster.state.timeout"), TimeValue.timeValueSeconds(10), Property.Dynamic, Property.NodeScope); /** * Timeout value when collecting the recovery information (default to 10m) */ public static final Setting CLUSTER_STATS_TIMEOUT = - timeSetting(key("agent.cluster.stats.timeout"), TimeValue.timeValueSeconds(10), Property.Dynamic, Property.NodeScope); + timeSetting(collectionKey("cluster.stats.timeout"), TimeValue.timeValueSeconds(10), Property.Dynamic, Property.NodeScope); /** * Timeout value when collecting the recovery information (default to 10m) */ public static final Setting INDEX_RECOVERY_TIMEOUT = - timeSetting(key("agent.index.recovery.timeout"), TimeValue.timeValueSeconds(10), Property.Dynamic, Property.NodeScope); + timeSetting(collectionKey("index.recovery.timeout"), TimeValue.timeValueSeconds(10), Property.Dynamic, Property.NodeScope); /** * Flag to indicate if only active recoveries should be collected (default to false: all recoveries are collected) */ public static final Setting INDEX_RECOVERY_ACTIVE_ONLY = - boolSetting(key("agent.index.recovery.active_only"), false, Property.Dynamic, Property.NodeScope) ; + boolSetting(collectionKey("index.recovery.active_only"), false, Property.Dynamic, Property.NodeScope) ; /** * List of collectors allowed to collect data (default to all) */ public static final Setting> COLLECTORS = - listSetting(key("agent.collectors"), Collections.emptyList(), Function.identity(), Property.NodeScope); + listSetting(collectionKey("collectors"), Collections.emptyList(), Function.identity(), Property.NodeScope); /** * The default retention duration of the monitoring history data. @@ -123,7 +123,7 @@ public class MonitoringSettings extends AbstractComponent { * Settings/Options per configured exporter */ public static final Setting EXPORTERS_SETTINGS = - groupSetting(key("agent.exporters."), Property.Dynamic, Property.NodeScope); + groupSetting(collectionKey("exporters."), Property.Dynamic, Property.NodeScope); public static List> getSettings() { return Arrays.asList(INDICES, @@ -141,7 +141,7 @@ public class MonitoringSettings extends AbstractComponent { } public static List getSettingsFilter() { - return Arrays.asList("xpack.monitoring.agent.exporters.*.auth.*", "xpack.monitoring.agent.exporters.*.ssl.*"); + return Arrays.asList("xpack.monitoring.collection.exporters.*.auth.*", "xpack.monitoring.collection.exporters.*.ssl.*"); } @@ -227,6 +227,17 @@ public class MonitoringSettings extends AbstractComponent { this.indices = indices.toArray(new String[0]); } + /** + * Prefix the {@code key} with the Monitoring prefix and "collection." . + * + * @param key The key to prefix + * @return The key prefixed by the product prefixes + "collection." . + * @see #key(String) + */ + static String collectionKey(String key) { + return key("collection." + key); + } + /** * Prefix the {@code key} with the Monitoring prefix. * diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkRequest.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkRequest.java index cb09ad5b301..cbbc56d506f 100644 --- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkRequest.java +++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkRequest.java @@ -58,7 +58,7 @@ public class MonitoringBulkRequest extends ActionRequest validationException); } if (Strings.hasLength(doc.getMonitoringVersion()) == false) { - validationException = addValidationError("monitored system version is missing for monitoring document [" + i + "]", + validationException = addValidationError("monitored system API version is missing for monitoring document [" + i + "]", validationException); } if (Strings.hasLength(doc.getType()) == false) { @@ -84,7 +84,7 @@ public class MonitoringBulkRequest extends ActionRequest /** * Parses a monitoring bulk request and builds the list of documents to be indexed. */ - public MonitoringBulkRequest add(BytesReference content, String defaultMonitoringId, String defaultMonitoringVersion, + public MonitoringBulkRequest add(BytesReference content, String defaultMonitoringId, String defaultMonitoringApiVersion, String defaultType) throws Exception { // MonitoringBulkRequest accepts a body request that has the same format as the BulkRequest: // instead of duplicating the parsing logic here we use a new BulkRequest instance to parse the content. @@ -97,7 +97,7 @@ public class MonitoringBulkRequest extends ActionRequest // builds a new monitoring document based on the index request MonitoringBulkDoc doc = new MonitoringBulkDoc(defaultMonitoringId, - defaultMonitoringVersion, + defaultMonitoringApiVersion, MonitoringIndex.from(indexRequest.index()), indexRequest.type(), indexRequest.id(), diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkRequestBuilder.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkRequestBuilder.java index bdcd31f90c3..69aa19a6a17 100644 --- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkRequestBuilder.java +++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkRequestBuilder.java @@ -21,9 +21,9 @@ public class MonitoringBulkRequestBuilder return this; } - public MonitoringBulkRequestBuilder add(BytesReference content, String defaultId, String defaultVersion, String defaultType) + public MonitoringBulkRequestBuilder add(BytesReference content, String defaultId, String defaultApiVersion, String defaultType) throws Exception { - request.add(content, defaultId, defaultVersion, defaultType); + request.add(content, defaultId, defaultApiVersion, defaultType); return this; } } diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/exporter/MonitoringTemplateUtils.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/exporter/MonitoringTemplateUtils.java index a71beefb314..5d69b2715c0 100644 --- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/exporter/MonitoringTemplateUtils.java +++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/exporter/MonitoringTemplateUtils.java @@ -16,13 +16,13 @@ public final class MonitoringTemplateUtils { private static final String TEMPLATE_VERSION_PROPERTY = Pattern.quote("${monitoring.template.version}"); /** Current version of es and data templates **/ - public static final Integer TEMPLATE_VERSION = 2; + public static final String TEMPLATE_VERSION = "2"; private MonitoringTemplateUtils() { } public static String loadTemplate(String id) { String resource = String.format(Locale.ROOT, TEMPLATE_FILE, id); - return TemplateUtils.loadTemplate(resource, String.valueOf(TEMPLATE_VERSION), TEMPLATE_VERSION_PROPERTY); + return TemplateUtils.loadTemplate(resource, TEMPLATE_VERSION, TEMPLATE_VERSION_PROPERTY); } } diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/exporter/http/HttpExporter.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/exporter/http/HttpExporter.java index e2bb090d295..14e340355c8 100644 --- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/exporter/http/HttpExporter.java +++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/exporter/http/HttpExporter.java @@ -536,11 +536,8 @@ public class HttpExporter extends Exporter { for (String host : hosts) { try { HttpExporterUtils.parseHostWithPath(host, ""); - } catch (URISyntaxException e) { - throw new SettingsException("[xpack.monitoring.agent.exporter] invalid host: [" + host + "]." + - " error: [" + e.getMessage() + "]"); - } catch (MalformedURLException e) { - throw new SettingsException("[xpack.monitoring.agent.exporter] invalid host: [" + host + "]." + + } catch (URISyntaxException | MalformedURLException e) { + throw new SettingsException("[xpack.monitoring.collection.exporters] invalid host: [" + host + "]." + " error: [" + e.getMessage() + "]"); } } diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/resolver/MonitoringIndexNameResolver.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/resolver/MonitoringIndexNameResolver.java index e7b6eb692de..6a08c196146 100644 --- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/resolver/MonitoringIndexNameResolver.java +++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/resolver/MonitoringIndexNameResolver.java @@ -137,8 +137,8 @@ public abstract class MonitoringIndexNameResolver { } // Used in tests - protected Data(Integer version) { - this.index = String.join(DELIMITER, PREFIX, DATA, String.valueOf(version)); + protected Data(String version) { + this.index = String.join(DELIMITER, PREFIX, DATA, version); } @Override @@ -153,7 +153,7 @@ public abstract class MonitoringIndexNameResolver { @Override public String templateName() { - return String.format(Locale.ROOT, "%s-%s-%d", PREFIX, DATA, MonitoringTemplateUtils.TEMPLATE_VERSION); + return String.format(Locale.ROOT, "%s-%s-%s", PREFIX, DATA, MonitoringTemplateUtils.TEMPLATE_VERSION); } @Override @@ -180,9 +180,9 @@ public abstract class MonitoringIndexNameResolver { } // Used in tests - protected Timestamped(MonitoredSystem system, Settings settings, Integer version) { + protected Timestamped(MonitoredSystem system, Settings settings, String version) { this.system = system; - this.index = String.join(DELIMITER, PREFIX, system.getSystem(), String.valueOf(version)); + this.index = String.join(DELIMITER, PREFIX, system.getSystem(), version); String format = INDEX_NAME_TIME_FORMAT_SETTING.get(settings); try { this.formatter = DateTimeFormat.forPattern(format).withZoneUTC(); @@ -209,7 +209,7 @@ public abstract class MonitoringIndexNameResolver { @Override public String templateName() { - return String.format(Locale.ROOT, "%s-%s-%d", PREFIX, getId(), MonitoringTemplateUtils.TEMPLATE_VERSION); + return String.format(Locale.ROOT, "%s-%s-%s", PREFIX, getId(), MonitoringTemplateUtils.TEMPLATE_VERSION); } @Override diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/resolver/ResolversRegistry.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/resolver/ResolversRegistry.java index 64beb63eab1..5a00f956ca3 100644 --- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/resolver/ResolversRegistry.java +++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/resolver/ResolversRegistry.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.monitoring.agent.resolver; -import org.elasticsearch.Version; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.xpack.monitoring.MonitoredSystem; import org.elasticsearch.xpack.monitoring.action.MonitoringBulkDoc; @@ -21,6 +20,7 @@ import org.elasticsearch.xpack.monitoring.agent.collector.indices.IndicesStatsMo import org.elasticsearch.xpack.monitoring.agent.collector.node.NodeStatsMonitoringDoc; import org.elasticsearch.xpack.monitoring.agent.collector.shards.ShardMonitoringDoc; import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc; +import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringTemplateUtils; import org.elasticsearch.xpack.monitoring.agent.resolver.bulk.MonitoringBulkDataResolver; import org.elasticsearch.xpack.monitoring.agent.resolver.bulk.MonitoringBulkTimestampedResolver; import org.elasticsearch.xpack.monitoring.agent.resolver.cluster.ClusterInfoResolver; @@ -74,8 +74,13 @@ public class ResolversRegistry implements Iterable private void registerMonitoredSystem(MonitoredSystem id, Settings settings) { final MonitoringBulkDataResolver dataResolver = new MonitoringBulkDataResolver(); final MonitoringBulkTimestampedResolver timestampedResolver = new MonitoringBulkTimestampedResolver(id, settings); - registrations.add(resolveByClassSystemVersion(id, dataResolver, MonitoringIndex.DATA, Version.CURRENT)); - registrations.add(resolveByClassSystemVersion(id, timestampedResolver, MonitoringIndex.TIMESTAMPED, Version.CURRENT)); + + final String currentApiVersion = MonitoringTemplateUtils.TEMPLATE_VERSION; + + // Note: We resolve requests by the API version that is supplied; this allows us to translate and up-convert any older + // requests that come through the _xpack/monitoring/_bulk endpoint + registrations.add(resolveByClassSystemVersion(id, dataResolver, MonitoringIndex.DATA, currentApiVersion)); + registrations.add(resolveByClassSystemVersion(id, timestampedResolver, MonitoringIndex.TIMESTAMPED, currentApiVersion)); } /** @@ -100,7 +105,7 @@ public class ResolversRegistry implements Iterable } static Registration resolveByClassSystemVersion(MonitoredSystem system, MonitoringIndexNameResolver resolver, MonitoringIndex index, - Version version) { + String apiVersion) { return new Registration(resolver, doc -> { try { if (doc instanceof MonitoringBulkDoc == false || index != ((MonitoringBulkDoc)doc).getIndex()) { @@ -109,7 +114,7 @@ public class ResolversRegistry implements Iterable if (system != MonitoredSystem.fromSystem(doc.getMonitoringId())) { return false; } - return version == Version.fromString(doc.getMonitoringVersion()); + return apiVersion.equals(doc.getMonitoringVersion()); } catch (Exception e) { return false; } diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/rest/action/RestMonitoringBulkAction.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/rest/action/RestMonitoringBulkAction.java index a4d4fc9cb77..c0cb17800df 100644 --- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/rest/action/RestMonitoringBulkAction.java +++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/rest/action/RestMonitoringBulkAction.java @@ -29,7 +29,7 @@ import static org.elasticsearch.rest.RestRequest.Method.PUT; public class RestMonitoringBulkAction extends MonitoringRestHandler { public static final String MONITORING_ID = "system_id"; - public static final String MONITORING_VERSION = "system_version"; + public static final String MONITORING_VERSION = "system_api_version"; @Inject public RestMonitoringBulkAction(Settings settings, RestController controller) { diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringF.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringF.java index 9c2a3aec6a4..e2b018975cb 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringF.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringF.java @@ -32,9 +32,9 @@ public class MonitoringF { settings.put("script.inline", "true"); settings.put("security.manager.enabled", "false"); settings.put("cluster.name", MonitoringF.class.getSimpleName()); - settings.put("xpack.monitoring.agent.interval", "5s"); + settings.put("xpack.monitoring.collection.interval", "1s"); if (!CollectionUtils.isEmpty(args)) { - settings.putArray("xpack.monitoring.agent.collectors", args); + settings.putArray("xpack.monitoring.collection.collectors", args); } final CountDownLatch latch = new CountDownLatch(1); diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkRequestTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkRequestTests.java index 93f4d391ec2..9e54d940f53 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkRequestTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkRequestTests.java @@ -36,13 +36,13 @@ public class MonitoringBulkRequestTests extends ESTestCase { MonitoringBulkDoc doc = new MonitoringBulkDoc(null, null); assertValidationErrors(new MonitoringBulkRequest().add(doc), hasItems("monitored system id is missing for monitoring document [0]", - "monitored system version is missing for monitoring document [0]", + "monitored system API version is missing for monitoring document [0]", "type is missing for monitoring document [0]", "source is missing for monitoring document [0]")); doc = new MonitoringBulkDoc("id", null); assertValidationErrors(new MonitoringBulkRequest().add(doc), - hasItems("monitored system version is missing for monitoring document [0]", + hasItems("monitored system API version is missing for monitoring document [0]", "type is missing for monitoring document [0]", "source is missing for monitoring document [0]")); @@ -92,7 +92,7 @@ public class MonitoringBulkRequestTests extends ESTestCase { assertValidationErrors(request, hasItems("type is missing for monitoring document [1]", "source is missing for monitoring document [2]", - "monitored system version is missing for monitoring document [3]", + "monitored system API version is missing for monitoring document [3]", "monitored system id is missing for monitoring document [4]")); } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkTests.java index 46a67d2ab65..2f44b7ab130 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/action/MonitoringBulkTests.java @@ -5,13 +5,12 @@ */ package org.elasticsearch.xpack.monitoring.action; -import org.elasticsearch.Version; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.search.SearchHit; -import org.elasticsearch.test.junit.annotations.TestLogging; import org.elasticsearch.xpack.monitoring.MonitoredSystem; +import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringTemplateUtils; import org.elasticsearch.xpack.monitoring.agent.resolver.bulk.MonitoringBulkTimestampedResolver; import org.elasticsearch.xpack.monitoring.test.MonitoringIntegTestCase; @@ -29,7 +28,6 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -@TestLogging("_root:DEBUG") public class MonitoringBulkTests extends MonitoringIntegTestCase { @Override @@ -43,7 +41,7 @@ public class MonitoringBulkTests extends MonitoringIntegTestCase { int numDocs = scaledRandomIntBetween(100, 5000); for (int i = 0; i < numDocs; i++) { - MonitoringBulkDoc doc = new MonitoringBulkDoc(MonitoredSystem.KIBANA.getSystem(), Version.CURRENT.toString()); + MonitoringBulkDoc doc = new MonitoringBulkDoc(MonitoredSystem.KIBANA.getSystem(), MonitoringTemplateUtils.TEMPLATE_VERSION); doc.setType(randomFrom(types)); doc.setSource(jsonBuilder().startObject().field("num", numDocs).endObject().bytes()); requestBuilder.add(doc); @@ -95,7 +93,8 @@ public class MonitoringBulkTests extends MonitoringIntegTestCase { int numDocs = scaledRandomIntBetween(10, 50); for (int k = 0; k < numDocs; k++) { - MonitoringBulkDoc doc = new MonitoringBulkDoc(MonitoredSystem.KIBANA.getSystem(), Version.CURRENT.toString()); + MonitoringBulkDoc doc = + new MonitoringBulkDoc(MonitoredSystem.KIBANA.getSystem(), MonitoringTemplateUtils.TEMPLATE_VERSION); doc.setType("concurrent"); doc.setSource(jsonBuilder().startObject().field("num", k).endObject().bytes()); requestBuilder.add(doc); @@ -133,10 +132,10 @@ public class MonitoringBulkTests extends MonitoringIntegTestCase { for (int i = 0; i < totalDocs; i++) { MonitoringBulkDoc doc; if (randomBoolean()) { - doc = new MonitoringBulkDoc("unknown", Version.CURRENT.toString()); + doc = new MonitoringBulkDoc("unknown", MonitoringTemplateUtils.TEMPLATE_VERSION); unsupportedDocs++; } else { - doc = new MonitoringBulkDoc(MonitoredSystem.KIBANA.getSystem(), Version.CURRENT.toString()); + doc = new MonitoringBulkDoc(MonitoredSystem.KIBANA.getSystem(), MonitoringTemplateUtils.TEMPLATE_VERSION); } doc.setType(randomFrom(types)); doc.setSource(jsonBuilder().startObject().field("num", i).endObject().bytes()); diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java index ba5a3578a0c..a88cd9e248f 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java @@ -34,6 +34,7 @@ import org.elasticsearch.xpack.monitoring.MonitoringSettings; import org.elasticsearch.xpack.monitoring.test.MonitoringIntegTestCase; import org.junit.Before; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -220,7 +221,7 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase public static class InternalXPackPlugin extends XPackPlugin { - public InternalXPackPlugin(Settings settings) { + public InternalXPackPlugin(Settings settings) throws IOException { super(settings); licensing = new InternalLicensing(); } @@ -252,7 +253,7 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase private volatile License license; @Override - public List licenseesWithState(LicenseState state) { + public LicenseState licenseState() { return null; } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/AbstractExporterTemplateTestCase.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/AbstractExporterTemplateTestCase.java index a0ce443e710..70c657b260f 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/AbstractExporterTemplateTestCase.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/AbstractExporterTemplateTestCase.java @@ -32,7 +32,7 @@ public abstract class AbstractExporterTemplateTestCase extends MonitoringIntegTe .put(MonitoringSettings.INTERVAL.getKey(), "-1"); for (Map.Entry setting : exporterSettings().getAsMap().entrySet()) { - settings.put("xpack.monitoring.agent.exporters._exporter." + setting.getKey(), setting.getValue()); + settings.put("xpack.monitoring.collection.exporters._exporter." + setting.getKey(), setting.getValue()); } return settings.build(); } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/ExportersTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/ExportersTests.java index c9a9563feec..38a18b6a8e6 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/ExportersTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/ExportersTests.java @@ -176,8 +176,8 @@ public class ExportersTests extends ESTestCase { final AtomicReference settingsHolder = new AtomicReference<>(); Settings nodeSettings = Settings.builder() - .put("xpack.monitoring.agent.exporters._name0.type", "_type") - .put("xpack.monitoring.agent.exporters._name1.type", "_type") + .put("xpack.monitoring.collection.exporters._name0.type", "_type") + .put("xpack.monitoring.collection.exporters._name1.type", "_type") .build(); clusterSettings = new ClusterSettings(nodeSettings, new HashSet<>(Arrays.asList(MonitoringSettings.EXPORTERS_SETTINGS))); @@ -197,8 +197,8 @@ public class ExportersTests extends ESTestCase { assertThat(settings, hasEntry("_name1.type", "_type")); Settings update = Settings.builder() - .put("xpack.monitoring.agent.exporters._name0.foo", "bar") - .put("xpack.monitoring.agent.exporters._name1.foo", "bar") + .put("xpack.monitoring.collection.exporters._name0.foo", "bar") + .put("xpack.monitoring.collection.exporters._name1.foo", "bar") .build(); clusterSettings.applySettings(update); assertThat(settingsHolder.get(), notNullValue()); @@ -216,8 +216,8 @@ public class ExportersTests extends ESTestCase { factories.put("mock", factory); factories.put("mock_master_only", masterOnlyFactory); Exporters exporters = new Exporters(Settings.builder() - .put("xpack.monitoring.agent.exporters._name0.type", "mock") - .put("xpack.monitoring.agent.exporters._name1.type", "mock_master_only") + .put("xpack.monitoring.collection.exporters._name0.type", "mock") + .put("xpack.monitoring.collection.exporters._name1.type", "mock_master_only") .build(), factories, clusterService, clusterSettings); exporters.start(); @@ -241,8 +241,8 @@ public class ExportersTests extends ESTestCase { factories.put("mock", factory); factories.put("mock_master_only", masterOnlyFactory); Exporters exporters = new Exporters(Settings.builder() - .put("xpack.monitoring.agent.exporters._name0.type", "mock") - .put("xpack.monitoring.agent.exporters._name1.type", "mock_master_only") + .put("xpack.monitoring.collection.exporters._name0.type", "mock") + .put("xpack.monitoring.collection.exporters._name1.type", "mock_master_only") .build(), factories, clusterService, clusterSettings); exporters.start(); @@ -270,7 +270,7 @@ public class ExportersTests extends ESTestCase { logger.info("--> creating {} exporters", nbExporters); Settings.Builder settings = Settings.builder(); for (int i = 0; i < nbExporters; i++) { - settings.put("xpack.monitoring.agent.exporters._name" + String.valueOf(i) + ".type", "record"); + settings.put("xpack.monitoring.collection.exporters._name" + String.valueOf(i) + ".type", "record"); } Exporter.Factory factory = new CountingExportFactory("record", false); diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/http/HttpExporterTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/http/HttpExporterTests.java index 2e41597501f..9d5fd29023e 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/http/HttpExporterTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/http/HttpExporterTests.java @@ -95,10 +95,10 @@ public class HttpExporterTests extends MonitoringIntegTestCase { Settings.Builder builder = Settings.builder() .put(MonitoringSettings.INTERVAL.getKey(), "-1") - .put("xpack.monitoring.agent.exporters._http.type", "http") - .put("xpack.monitoring.agent.exporters._http.host", webServer.getHostName() + ":" + webServer.getPort()) - .put("xpack.monitoring.agent.exporters._http.connection.keep_alive", false) - .put("xpack.monitoring.agent.exporters._http.update_mappings", false); + .put("xpack.monitoring.collection.exporters._http.type", "http") + .put("xpack.monitoring.collection.exporters._http.host", webServer.getHostName() + ":" + webServer.getPort()) + .put("xpack.monitoring.collection.exporters._http.connection.keep_alive", false) + .put("xpack.monitoring.collection.exporters._http.update_mappings", false); internalCluster().startNode(builder); @@ -133,23 +133,23 @@ public class HttpExporterTests extends MonitoringIntegTestCase { // disable exporting to be able to use non valid hosts Settings.Builder builder = Settings.builder() .put(MonitoringSettings.INTERVAL.getKey(), "-1") - .put("xpack.monitoring.agent.exporters._http.type", "http") - .put("xpack.monitoring.agent.exporters._http.host", "test0"); + .put("xpack.monitoring.collection.exporters._http.type", "http") + .put("xpack.monitoring.collection.exporters._http.host", "test0"); String nodeName = internalCluster().startNode(builder); assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(Settings.builder() - .putArray("xpack.monitoring.agent.exporters._http.host", "test1"))); + .putArray("xpack.monitoring.collection.exporters._http.host", "test1"))); assertThat(getExporter(nodeName).hosts, Matchers.arrayContaining("test1")); // wipes the non array settings assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(Settings.builder() - .putArray("xpack.monitoring.agent.exporters._http.host", "test2") - .put("xpack.monitoring.agent.exporters._http.host", ""))); + .putArray("xpack.monitoring.collection.exporters._http.host", "test2") + .put("xpack.monitoring.collection.exporters._http.host", ""))); assertThat(getExporter(nodeName).hosts, Matchers.arrayContaining("test2")); assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(Settings.builder() - .putArray("xpack.monitoring.agent.exporters._http.host", "test3"))); + .putArray("xpack.monitoring.collection.exporters._http.host", "test3"))); assertThat(getExporter(nodeName).hosts, Matchers.arrayContaining("test3")); } @@ -157,10 +157,10 @@ public class HttpExporterTests extends MonitoringIntegTestCase { Settings.Builder builder = Settings.builder() .put(MonitoringSettings.INTERVAL.getKey(), "-1") - .put("xpack.monitoring.agent.exporters._http.type", "http") - .put("xpack.monitoring.agent.exporters._http.host", webServer.getHostName() + ":" + webServer.getPort()) - .put("xpack.monitoring.agent.exporters._http.connection.keep_alive", false) - .put("xpack.monitoring.agent.exporters._http.update_mappings", false); + .put("xpack.monitoring.collection.exporters._http.type", "http") + .put("xpack.monitoring.collection.exporters._http.host", webServer.getHostName() + ":" + webServer.getPort()) + .put("xpack.monitoring.collection.exporters._http.connection.keep_alive", false) + .put("xpack.monitoring.collection.exporters._http.update_mappings", false); logger.info("--> starting node"); @@ -221,7 +221,7 @@ public class HttpExporterTests extends MonitoringIntegTestCase { assertNotNull("Unable to start the second mock web server", secondWebServer); assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings( - Settings.builder().putArray("xpack.monitoring.agent.exporters._http.host", + Settings.builder().putArray("xpack.monitoring.collection.exporters._http.host", secondWebServer.getHostName() + ":" + secondWebServer.getPort())).get()); // a new exporter is created on update, so we need to re-fetch it @@ -274,9 +274,9 @@ public class HttpExporterTests extends MonitoringIntegTestCase { public void testUnsupportedClusterVersion() throws Exception { Settings.Builder builder = Settings.builder() .put(MonitoringSettings.INTERVAL.getKey(), "-1") - .put("xpack.monitoring.agent.exporters._http.type", "http") - .put("xpack.monitoring.agent.exporters._http.host", webServer.getHostName() + ":" + webServer.getPort()) - .put("xpack.monitoring.agent.exporters._http.connection.keep_alive", false); + .put("xpack.monitoring.collection.exporters._http.type", "http") + .put("xpack.monitoring.collection.exporters._http.host", webServer.getHostName() + ":" + webServer.getPort()) + .put("xpack.monitoring.collection.exporters._http.connection.keep_alive", false); logger.info("--> starting node"); @@ -302,10 +302,10 @@ public class HttpExporterTests extends MonitoringIntegTestCase { public void testDynamicIndexFormatChange() throws Exception { Settings.Builder builder = Settings.builder() .put(MonitoringSettings.INTERVAL.getKey(), "-1") - .put("xpack.monitoring.agent.exporters._http.type", "http") - .put("xpack.monitoring.agent.exporters._http.host", webServer.getHostName() + ":" + webServer.getPort()) - .put("xpack.monitoring.agent.exporters._http.connection.keep_alive", false) - .put("xpack.monitoring.agent.exporters._http.update_mappings", false); + .put("xpack.monitoring.collection.exporters._http.type", "http") + .put("xpack.monitoring.collection.exporters._http.host", webServer.getHostName() + ":" + webServer.getPort()) + .put("xpack.monitoring.collection.exporters._http.connection.keep_alive", false) + .put("xpack.monitoring.collection.exporters._http.update_mappings", false); String agentNode = internalCluster().startNode(builder); @@ -356,7 +356,7 @@ public class HttpExporterTests extends MonitoringIntegTestCase { String newTimeFormat = randomFrom("YY", "YYYY", "YYYY.MM", "YYYY-MM", "MM.YYYY", "MM"); logger.info("--> updating index time format setting to {}", newTimeFormat); assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(Settings.builder() - .put("xpack.monitoring.agent.exporters._http.index.name.time_format", newTimeFormat))); + .put("xpack.monitoring.collection.exporters._http.index.name.time_format", newTimeFormat))); logger.info("--> exporting a second event"); @@ -402,9 +402,9 @@ public class HttpExporterTests extends MonitoringIntegTestCase { Settings.Builder builder = Settings.builder() .put(MonitoringSettings.INTERVAL.getKey(), "-1") - .put("xpack.monitoring.agent.exporters._http.type", "http") - .put("xpack.monitoring.agent.exporters._http.host", host) - .put("xpack.monitoring.agent.exporters._http.connection.keep_alive", false); + .put("xpack.monitoring.collection.exporters._http.type", "http") + .put("xpack.monitoring.collection.exporters._http.host", host) + .put("xpack.monitoring.collection.exporters._http.connection.keep_alive", false); String agentNode = internalCluster().startNode(builder); HttpExporter exporter = getExporter(agentNode); diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/local/LocalExporterTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/local/LocalExporterTests.java index e75abdbec94..a3e67d97930 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/local/LocalExporterTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/exporter/local/LocalExporterTests.java @@ -65,8 +65,8 @@ public class LocalExporterTests extends MonitoringIntegTestCase { public void testSimpleExport() throws Exception { internalCluster().startNode(Settings.builder() - .put("xpack.monitoring.agent.exporters._local.type", LocalExporter.TYPE) - .put("xpack.monitoring.agent.exporters._local.enabled", true) + .put("xpack.monitoring.collection.exporters._local.type", LocalExporter.TYPE) + .put("xpack.monitoring.collection.exporters._local.enabled", true) .build()); securedEnsureGreen(); @@ -96,7 +96,7 @@ public class LocalExporterTests extends MonitoringIntegTestCase { public void testTemplateCreation() throws Exception { internalCluster().startNode(Settings.builder() - .put("xpack.monitoring.agent.exporters._local.type", LocalExporter.TYPE) + .put("xpack.monitoring.collection.exporters._local.type", LocalExporter.TYPE) .build()); securedEnsureGreen(); @@ -111,8 +111,8 @@ public class LocalExporterTests extends MonitoringIntegTestCase { String timeFormat = randomFrom("YY", "YYYY", "YYYY.MM", "YYYY-MM", "MM.YYYY", "MM"); internalCluster().startNode(Settings.builder() - .put("xpack.monitoring.agent.exporters._local.type", LocalExporter.TYPE) - .put("xpack.monitoring.agent.exporters._local." + LocalExporter.INDEX_NAME_TIME_FORMAT_SETTING, timeFormat) + .put("xpack.monitoring.collection.exporters._local.type", LocalExporter.TYPE) + .put("xpack.monitoring.collection.exporters._local." + LocalExporter.INDEX_NAME_TIME_FORMAT_SETTING, timeFormat) .build()); securedEnsureGreen(); @@ -130,7 +130,7 @@ public class LocalExporterTests extends MonitoringIntegTestCase { logger.debug("--> updates the timestamp"); timeFormat = randomFrom("dd", "dd.MM.YYYY", "dd.MM"); - updateClusterSettings(Settings.builder().put("xpack.monitoring.agent.exporters._local.index.name.time_format", timeFormat)); + updateClusterSettings(Settings.builder().put("xpack.monitoring.collection.exporters._local.index.name.time_format", timeFormat)); exporter = getLocalExporter("_local"); // we need to get it again.. as it was rebuilt indexName = ".monitoring-es-" + MonitoringTemplateUtils.TEMPLATE_VERSION + "-" + DateTimeFormat.forPattern(timeFormat).withZoneUTC().print(doc.getTimestamp()); @@ -144,8 +144,8 @@ public class LocalExporterTests extends MonitoringIntegTestCase { public void testLocalExporterFlush() throws Exception { internalCluster().startNode(Settings.builder() - .put("xpack.monitoring.agent.exporters._local.type", LocalExporter.TYPE) - .put("xpack.monitoring.agent.exporters._local.enabled", true) + .put("xpack.monitoring.collection.exporters._local.type", LocalExporter.TYPE) + .put("xpack.monitoring.collection.exporters._local.enabled", true) .build()); securedEnsureGreen(); diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/bulk/MonitoringBulkDataResolverTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/bulk/MonitoringBulkDataResolverTests.java index 614ff5f9f0e..641aa8f1169 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/bulk/MonitoringBulkDataResolverTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/bulk/MonitoringBulkDataResolverTests.java @@ -13,6 +13,7 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.xpack.monitoring.MonitoredSystem; import org.elasticsearch.xpack.monitoring.action.MonitoringBulkDoc; import org.elasticsearch.xpack.monitoring.action.MonitoringIndex; +import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringTemplateUtils; import org.elasticsearch.xpack.monitoring.agent.resolver.MonitoringIndexNameResolverTestCase; import static java.util.Collections.emptyMap; @@ -29,7 +30,7 @@ public class MonitoringBulkDataResolverTests extends MonitoringIndexNameResolver @Override protected MonitoringBulkDoc newMonitoringDoc() { - MonitoringBulkDoc doc = new MonitoringBulkDoc(MonitoredSystem.KIBANA.getSystem(), Version.CURRENT.toString(), + MonitoringBulkDoc doc = new MonitoringBulkDoc(MonitoredSystem.KIBANA.getSystem(), MonitoringTemplateUtils.TEMPLATE_VERSION, MonitoringIndex.DATA, "kibana", id, new BytesArray("{\"field1\" : \"value1\"}")); diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/bulk/MonitoringBulkTimestampedResolverTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/bulk/MonitoringBulkTimestampedResolverTests.java index 34b717e499d..9d4db7f421f 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/bulk/MonitoringBulkTimestampedResolverTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/bulk/MonitoringBulkTimestampedResolverTests.java @@ -13,6 +13,7 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.xpack.monitoring.MonitoredSystem; import org.elasticsearch.xpack.monitoring.action.MonitoringBulkDoc; import org.elasticsearch.xpack.monitoring.action.MonitoringIndex; +import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringTemplateUtils; import org.elasticsearch.xpack.monitoring.agent.resolver.MonitoringIndexNameResolverTestCase; import static java.util.Collections.emptyMap; @@ -28,7 +29,7 @@ public class MonitoringBulkTimestampedResolverTests @Override protected MonitoringBulkDoc newMonitoringDoc() { - MonitoringBulkDoc doc = new MonitoringBulkDoc(MonitoredSystem.KIBANA.getSystem(), Version.CURRENT.toString(), + MonitoringBulkDoc doc = new MonitoringBulkDoc(MonitoredSystem.KIBANA.getSystem(), MonitoringTemplateUtils.TEMPLATE_VERSION, MonitoringIndex.TIMESTAMPED, "kibana_stats", null, new BytesArray("{\"field1\" : \"value1\"}")); diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/cluster/ClusterStateTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/cluster/ClusterStateTests.java index 68431bb3209..cb9d317eaa8 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/cluster/ClusterStateTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/cluster/ClusterStateTests.java @@ -44,7 +44,7 @@ public class ClusterStateTests extends MonitoringIntegTestCase { .put(super.nodeSettings(nodeOrdinal)) .put(MonitoringSettings.INTERVAL.getKey(), "-1") .put(MonitoringSettings.COLLECTORS.getKey(), ClusterStateCollector.NAME) - .put("xpack.monitoring.agent.exporters.default_local.type", "local") + .put("xpack.monitoring.collection.exporters.default_local.type", "local") .put("node.attr.custom", randomInt) .build(); } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/cluster/ClusterStatsTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/cluster/ClusterStatsTests.java index a965e2bb71e..a2a591a7cf9 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/cluster/ClusterStatsTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/cluster/ClusterStatsTests.java @@ -32,7 +32,7 @@ public class ClusterStatsTests extends MonitoringIntegTestCase { .put(super.nodeSettings(nodeOrdinal)) .put(MonitoringSettings.INTERVAL.getKey(), "-1") .put(MonitoringSettings.COLLECTORS.getKey(), ClusterStatsCollector.NAME) - .put("xpack.monitoring.agent.exporters.default_local.type", "local") + .put("xpack.monitoring.collection.exporters.default_local.type", "local") .build(); } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/indices/IndexRecoveryTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/indices/IndexRecoveryTests.java index 45f2d48168d..b4656ba24ef 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/indices/IndexRecoveryTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/indices/IndexRecoveryTests.java @@ -38,7 +38,7 @@ public class IndexRecoveryTests extends MonitoringIntegTestCase { .put(MonitoringSettings.INTERVAL.getKey(), "-1") .put(MonitoringSettings.INDICES.getKey(), INDEX_PREFIX + "*") .put(MonitoringSettings.COLLECTORS.getKey(), IndexRecoveryCollector.NAME) - .put("xpack.monitoring.agent.exporters.default_local.type", "local") + .put("xpack.monitoring.collection.exporters.default_local.type", "local") .build(); } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/indices/IndexStatsTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/indices/IndexStatsTests.java index 76f439e0e0c..d8441c6dbe3 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/indices/IndexStatsTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/indices/IndexStatsTests.java @@ -30,7 +30,7 @@ public class IndexStatsTests extends MonitoringIntegTestCase { .put(super.nodeSettings(nodeOrdinal)) .put(MonitoringSettings.INTERVAL.getKey(), "-1") .put(MonitoringSettings.COLLECTORS.getKey(), IndexStatsCollector.NAME) - .put("xpack.monitoring.agent.exporters.default_local.type", "local") + .put("xpack.monitoring.collection.exporters.default_local.type", "local") .build(); } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/indices/IndicesStatsTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/indices/IndicesStatsTests.java index bce40dc9f86..d2f7ce45ea2 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/indices/IndicesStatsTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/indices/IndicesStatsTests.java @@ -30,7 +30,7 @@ public class IndicesStatsTests extends MonitoringIntegTestCase { .put(super.nodeSettings(nodeOrdinal)) .put(MonitoringSettings.INTERVAL.getKey(), "-1") .put(MonitoringSettings.COLLECTORS.getKey(), IndicesStatsCollector.NAME) - .put("xpack.monitoring.agent.exporters.default_local.type", "local") + .put("xpack.monitoring.collection.exporters.default_local.type", "local") .build(); } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/node/MultiNodesStatsTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/node/MultiNodesStatsTests.java index c5002592bf8..f452d6cb9f4 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/node/MultiNodesStatsTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/node/MultiNodesStatsTests.java @@ -35,7 +35,7 @@ public class MultiNodesStatsTests extends MonitoringIntegTestCase { return Settings.builder() .put(super.nodeSettings(nodeOrdinal)) .put(MonitoringSettings.INTERVAL.getKey(), "-1") - .put("xpack.monitoring.agent.exporters.default_local.type", "local") + .put("xpack.monitoring.collection.exporters.default_local.type", "local") .build(); } @@ -102,7 +102,7 @@ public class MultiNodesStatsTests extends MonitoringIntegTestCase { assertThat(((StringTerms) aggregation).getBuckets().size(), equalTo(nbNodes)); for (String nodeName : internalCluster().getNodeNames()) { - StringTerms.Bucket bucket = (StringTerms.Bucket) ((StringTerms) aggregation) + StringTerms.Bucket bucket = ((StringTerms) aggregation) .getBucketByKey(internalCluster().clusterService(nodeName).localNode().getId()); // At least 1 doc must exist per node, but it can be more than 1 // because the first node may have already collected many node stats documents diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/node/NodeStatsTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/node/NodeStatsTests.java index 867423fa29e..d071f67dc51 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/node/NodeStatsTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/node/NodeStatsTests.java @@ -33,7 +33,7 @@ public class NodeStatsTests extends MonitoringIntegTestCase { .put(super.nodeSettings(nodeOrdinal)) .put(MonitoringSettings.INTERVAL.getKey(), "-1") .put(MonitoringSettings.COLLECTORS.getKey(), NodeStatsCollector.NAME) - .put("xpack.monitoring.agent.exporters.default_local.type", LocalExporter.TYPE) + .put("xpack.monitoring.collection.exporters.default_local.type", LocalExporter.TYPE) .build(); } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/shards/ShardsTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/shards/ShardsTests.java index d1cb30ee4b7..27a456d0b0d 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/shards/ShardsTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/resolver/shards/ShardsTests.java @@ -46,7 +46,7 @@ public class ShardsTests extends MonitoringIntegTestCase { .put(MonitoringSettings.INTERVAL.getKey(), "-1") .put(MonitoringSettings.COLLECTORS.getKey(), ShardsCollector.NAME) .put(MonitoringSettings.INDICES.getKey(), INDEX_PREFIX + "*") - .put("xpack.monitoring.agent.exporters.default_local.type", "local") + .put("xpack.monitoring.collection.exporters.default_local.type", "local") .build(); } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/AbstractIndicesCleanerTestCase.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/AbstractIndicesCleanerTestCase.java index 7d6f8e00eed..bcb2bd95a2f 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/AbstractIndicesCleanerTestCase.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/AbstractIndicesCleanerTestCase.java @@ -77,8 +77,8 @@ public abstract class AbstractIndicesCleanerTestCase extends MonitoringIntegTest // Won't be deleted createIndex(MonitoringSettings.LEGACY_DATA_INDEX_NAME, now().minusYears(1)); - createDataIndex(now().minusDays(10), 0); - createDataIndex(now().minusDays(10), 1); + createDataIndex(now().minusDays(10), "0"); + createDataIndex(now().minusDays(10), "1"); assertIndicesCount(4); CleanerService.Listener listener = getListener(); @@ -108,9 +108,9 @@ public abstract class AbstractIndicesCleanerTestCase extends MonitoringIntegTest createTimestampedIndex(now().minusDays(10)); // Won't be deleted - createTimestampedIndex(now().minusDays(10), 0); - createTimestampedIndex(now().minusDays(10), 1); - createTimestampedIndex(now().minusDays(10), Integer.MAX_VALUE); + createTimestampedIndex(now().minusDays(10), "0"); + createTimestampedIndex(now().minusDays(10), "1"); + createTimestampedIndex(now().minusDays(10), String.valueOf(Integer.MAX_VALUE)); assertIndicesCount(4); CleanerService.Listener listener = getListener(); @@ -198,7 +198,7 @@ public abstract class AbstractIndicesCleanerTestCase extends MonitoringIntegTest /** * Creates a monitoring data index in a given version. */ - protected void createDataIndex(DateTime creationDate, int version) { + protected void createDataIndex(DateTime creationDate, String version) { createIndex(new MockDataIndexNameResolver(version).index(randomMonitoringDoc()), creationDate); } @@ -212,7 +212,7 @@ public abstract class AbstractIndicesCleanerTestCase extends MonitoringIntegTest /** * Creates a monitoring timestamped index using a given template version. */ - protected void createTimestampedIndex(DateTime creationDate, int version) { + protected void createTimestampedIndex(DateTime creationDate, String version) { MonitoringDoc monitoringDoc = randomMonitoringDoc(); monitoringDoc.setTimestamp(creationDate.getMillis()); diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/local/LocalIndicesCleanerTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/local/LocalIndicesCleanerTests.java index ff56bb9fd11..7a5df1ab5db 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/local/LocalIndicesCleanerTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/local/LocalIndicesCleanerTests.java @@ -33,7 +33,7 @@ public class LocalIndicesCleanerTests extends AbstractIndicesCleanerTestCase { protected Settings nodeSettings(int nodeOrdinal) { return Settings.builder() .put(super.nodeSettings(nodeOrdinal)) - .put("xpack.monitoring.agent.exporters._local.type", LocalExporter.TYPE) + .put("xpack.monitoring.collection.exporters._local.type", LocalExporter.TYPE) .build(); } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java index 1b908ea82e2..6d2b17d60d7 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java @@ -26,6 +26,7 @@ import org.elasticsearch.xpack.XPackPlugin; import org.elasticsearch.xpack.monitoring.MonitoringLicensee; import org.elasticsearch.xpack.monitoring.test.MonitoringIntegTestCase; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -150,12 +151,8 @@ public class LicenseIntegrationTests extends MonitoringIntegTestCase { } @Override - public List licenseesWithState(LicenseState state) { - List licenseesWithState = new ArrayList<>(); - for (Licensee licensee : licensees) { - licenseesWithState.add(licensee.id()); - } - return licenseesWithState; + public LicenseState licenseState() { + return null; } @Override @@ -165,7 +162,7 @@ public class LicenseIntegrationTests extends MonitoringIntegTestCase { } public static class InternalXPackPlugin extends XPackPlugin { - public InternalXPackPlugin(Settings settings) { + public InternalXPackPlugin(Settings settings) throws IOException { super(settings); licensing = new MockLicensing(); } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/security/MonitoringSettingsFilterTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/security/MonitoringSettingsFilterTests.java index c6603f67969..08f98a20822 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/security/MonitoringSettingsFilterTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/security/MonitoringSettingsFilterTests.java @@ -14,7 +14,6 @@ import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.xpack.monitoring.MonitoringSettings; import org.elasticsearch.xpack.monitoring.test.MonitoringIntegTestCase; import org.elasticsearch.xpack.security.authc.support.SecuredString; -import org.hamcrest.Matchers; import java.util.Collections; import java.util.Map; @@ -22,7 +21,8 @@ import java.util.Map; import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue; import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.BASIC_AUTH_HEADER; import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; -import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.equalTo; public class MonitoringSettingsFilterTests extends MonitoringIntegTestCase { @@ -32,13 +32,13 @@ public class MonitoringSettingsFilterTests extends MonitoringIntegTestCase { .put(super.nodeSettings(nodeOrdinal)) .put(NetworkModule.HTTP_ENABLED.getKey(), true) .put(MonitoringSettings.INTERVAL.getKey(), "-1") - .put("xpack.monitoring.agent.exporters._http.type", "http") - .put("xpack.monitoring.agent.exporters._http.enabled", false) - .put("xpack.monitoring.agent.exporters._http.auth.username", "_user") - .put("xpack.monitoring.agent.exporters._http.auth.password", "_passwd") - .put("xpack.monitoring.agent.exporters._http.ssl.truststore.path", "/path/to/truststore") - .put("xpack.monitoring.agent.exporters._http.ssl.truststore.password", "_passwd") - .put("xpack.monitoring.agent.exporters._http.ssl.hostname_verification", true) + .put("xpack.monitoring.collection.exporters._http.type", "http") + .put("xpack.monitoring.collection.exporters._http.enabled", false) + .put("xpack.monitoring.collection.exporters._http.auth.username", "_user") + .put("xpack.monitoring.collection.exporters._http.auth.password", "_passwd") + .put("xpack.monitoring.collection.exporters._http.ssl.truststore.path", "/path/to/truststore") + .put("xpack.monitoring.collection.exporters._http.ssl.truststore.password", "_passwd") + .put("xpack.monitoring.collection.exporters._http.ssl.hostname_verification", true) .build(); } @@ -60,13 +60,13 @@ public class MonitoringSettingsFilterTests extends MonitoringIntegTestCase { for (Object node : nodes.values()) { @SuppressWarnings("unchecked") Map settings = (Map) ((Map) node).get("settings"); - assertThat(extractValue("xpack.monitoring.agent.exporters._http.type", settings), Matchers.equalTo("http")); - assertThat(extractValue("xpack.monitoring.agent.exporters._http.enabled", settings), Matchers.equalTo("false")); - assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.auth.username"); - assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.auth.password"); - assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.ssl.truststore.path"); - assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.ssl.truststore.password"); - assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.ssl.hostname_verification"); + assertThat(extractValue("xpack.monitoring.collection.exporters._http.type", settings), equalTo("http")); + assertThat(extractValue("xpack.monitoring.collection.exporters._http.enabled", settings), equalTo("false")); + assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.auth.username"); + assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.auth.password"); + assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.ssl.truststore.path"); + assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.ssl.truststore.password"); + assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.ssl.hostname_verification"); } } } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/test/MonitoringIntegTestCase.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/test/MonitoringIntegTestCase.java index 620ded5c39b..048819c998a 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/test/MonitoringIntegTestCase.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/test/MonitoringIntegTestCase.java @@ -37,7 +37,7 @@ import org.elasticsearch.xpack.security.authc.file.FileRealm; import org.elasticsearch.xpack.security.authc.support.Hasher; import org.elasticsearch.xpack.security.authc.support.SecuredString; import org.elasticsearch.xpack.security.authz.store.FileRolesStore; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.watcher.Watcher; import org.hamcrest.Matcher; import org.jboss.netty.util.internal.SystemPropertyUtil; @@ -440,7 +440,7 @@ public abstract class MonitoringIntegTestCase extends ESIntegTestCase { public class MockDataIndexNameResolver extends MonitoringIndexNameResolver.Data { - public MockDataIndexNameResolver(Integer version) { + public MockDataIndexNameResolver(String version) { super(version); } @@ -462,7 +462,7 @@ public abstract class MonitoringIntegTestCase extends ESIntegTestCase { protected class MockTimestampedIndexNameResolver extends MonitoringIndexNameResolver.Timestamped { - public MockTimestampedIndexNameResolver(MonitoredSystem system, Settings settings, Integer version) { + public MockTimestampedIndexNameResolver(MonitoredSystem system, Settings settings, String version) { super(system, settings, version); } @@ -537,7 +537,7 @@ public abstract class MonitoringIntegTestCase extends ESIntegTestCase { .put("xpack.security.authc.realms.esusers.files.users", writeFile(folder, "users", USERS)) .put("xpack.security.authc.realms.esusers.files.users_roles", writeFile(folder, "users_roles", USER_ROLES)) .put(FileRolesStore.ROLES_FILE_SETTING.getKey(), writeFile(folder, "roles.yml", ROLES)) - .put(InternalCryptoService.FILE_SETTING.getKey(), writeFile(folder, "system_key.yml", systemKey)) + .put(CryptoService.FILE_SETTING.getKey(), writeFile(folder, "system_key.yml", systemKey)) .put("xpack.security.authc.sign_user_header", false) .put("xpack.security.audit.enabled", auditLogsEnabled); } catch (IOException ex) { @@ -547,7 +547,7 @@ public abstract class MonitoringIntegTestCase extends ESIntegTestCase { static byte[] generateKey() { try { - return InternalCryptoService.generateKey(); + return CryptoService.generateKey(); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/elasticsearch/x-pack/monitoring/src/test/resources/rest-api-spec/api/xpack.monitoring.bulk.json b/elasticsearch/x-pack/monitoring/src/test/resources/rest-api-spec/api/xpack.monitoring.bulk.json index a884fa43bbe..42d7ae43d55 100644 --- a/elasticsearch/x-pack/monitoring/src/test/resources/rest-api-spec/api/xpack.monitoring.bulk.json +++ b/elasticsearch/x-pack/monitoring/src/test/resources/rest-api-spec/api/xpack.monitoring.bulk.json @@ -16,9 +16,9 @@ "type": "string", "description" : "Identifier of the monitored system" }, - "system_version" : { + "system_api_version" : { "type" : "string", - "description" : "Version of the monitored system" + "description" : "API Version of the monitored system" } } }, diff --git a/elasticsearch/x-pack/monitoring/src/test/resources/rest-api-spec/test/monitoring/bulk/10_basic.yaml b/elasticsearch/x-pack/monitoring/src/test/resources/rest-api-spec/test/monitoring/bulk/10_basic.yaml index 9d76e346ea3..b238b7ffb50 100644 --- a/elasticsearch/x-pack/monitoring/src/test/resources/rest-api-spec/test/monitoring/bulk/10_basic.yaml +++ b/elasticsearch/x-pack/monitoring/src/test/resources/rest-api-spec/test/monitoring/bulk/10_basic.yaml @@ -13,14 +13,11 @@ setup: --- "Bulk indexing of monitoring data": - # Get the current version - - do: {info: {}} - - set: {version.number: version} - do: xpack.monitoring.bulk: system_id: "kibana" - system_version: $version + system_api_version: "2" body: - index: _type: test_type @@ -54,7 +51,7 @@ setup: - do: xpack.monitoring.bulk: system_id: "kibana" - system_version: $version + system_api_version: "2" type: "default_type" body: - '{"index": {}}' diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java index 16d613c9ad7..e8b03c7fc73 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -5,6 +5,16 @@ */ package org.elasticsearch.xpack.security; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.support.ActionFilter; @@ -12,6 +22,7 @@ import org.elasticsearch.common.Booleans; import org.elasticsearch.common.Strings; import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.common.inject.Module; +import org.elasticsearch.common.inject.util.Providers; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.LoggerMessageFormat; import org.elasticsearch.common.logging.Loggers; @@ -21,6 +32,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexModule; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.rest.RestHandler; @@ -64,8 +76,7 @@ import org.elasticsearch.xpack.security.authz.accesscontrol.OptOutQueryCache; import org.elasticsearch.xpack.security.authz.accesscontrol.SecurityIndexSearcherWrapper; import org.elasticsearch.xpack.security.authz.store.FileRolesStore; import org.elasticsearch.xpack.security.authz.store.NativeRolesStore; -import org.elasticsearch.xpack.security.crypto.CryptoModule; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.security.rest.SecurityRestModule; import org.elasticsearch.xpack.security.rest.action.RestAuthenticateAction; import org.elasticsearch.xpack.security.rest.action.realm.RestClearRealmCacheAction; @@ -90,15 +101,6 @@ import org.elasticsearch.xpack.security.user.AnonymousUser; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; - import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; @@ -117,13 +119,17 @@ public class Security implements ActionPlugin { private final boolean enabled; private final boolean transportClientMode; private SecurityLicenseState securityLicenseState; + private final CryptoService cryptoService; - public Security(Settings settings) { + public Security(Settings settings, Environment env) throws IOException { this.settings = settings; this.transportClientMode = XPackPlugin.transportClientMode(settings); this.enabled = XPackPlugin.featureEnabled(settings, NAME, true); - if (enabled && !transportClientMode) { + if (enabled && transportClientMode == false) { validateAutoCreateIndex(settings); + cryptoService = new CryptoService(settings, env); + } else { + cryptoService = null; } } @@ -143,8 +149,8 @@ public class Security implements ActionPlugin { modules.add(new AuthenticationModule(settings)); modules.add(new AuthorizationModule(settings)); if (enabled == false) { + modules.add(b -> b.bind(CryptoService.class).toProvider(Providers.of(null))); modules.add(new SecurityModule(settings, securityLicenseState)); - modules.add(new CryptoModule(settings)); modules.add(new AuditTrailModule(settings)); modules.add(new SecurityTransportModule(settings)); return modules; @@ -154,8 +160,8 @@ public class Security implements ActionPlugin { // which might not be the case during Plugin class instantiation. Once nodeModules are pulled // everything should have been loaded securityLicenseState = new SecurityLicenseState(); + modules.add(b -> b.bind(CryptoService.class).toInstance(cryptoService)); modules.add(new SecurityModule(settings, securityLicenseState)); - modules.add(new CryptoModule(settings)); modules.add(new AuditTrailModule(settings)); modules.add(new SecurityRestModule(settings)); modules.add(new SecurityActionModule(settings)); @@ -175,7 +181,6 @@ public class Security implements ActionPlugin { list.add(LoggingAuditTrail.class); } list.add(SecurityLicensee.class); - list.add(InternalCryptoService.class); list.add(FileRolesStore.class); list.add(Realms.class); return list; @@ -186,13 +191,18 @@ public class Security implements ActionPlugin { return Settings.EMPTY; } + return additionalSettings(settings); + } + + // pkg private for testing + static Settings additionalSettings(Settings settings) { Settings.Builder settingsBuilder = Settings.builder(); settingsBuilder.put(NetworkModule.TRANSPORT_TYPE_KEY, Security.NAME); settingsBuilder.put(NetworkModule.TRANSPORT_SERVICE_TYPE_KEY, Security.NAME); settingsBuilder.put(NetworkModule.HTTP_TYPE_SETTING.getKey(), Security.NAME); SecurityNettyHttpServerTransport.overrideSettings(settingsBuilder, settings); - addUserSettings(settingsBuilder); - addTribeSettings(settingsBuilder); + addUserSettings(settings, settingsBuilder); + addTribeSettings(settings, settingsBuilder); return settingsBuilder.build(); } @@ -234,7 +244,7 @@ public class Security implements ActionPlugin { SecurityNettyHttpServerTransport.addSettings(settingsList); // encryption settings - InternalCryptoService.addSettings(settingsList); + CryptoService.addSettings(settingsList); // hide settings settingsList.add(Setting.listSetting(setting("hide_settings"), Collections.emptyList(), Function.identity(), @@ -346,7 +356,7 @@ public class Security implements ActionPlugin { } } - private void addUserSettings(Settings.Builder settingsBuilder) { + private static void addUserSettings(Settings settings, Settings.Builder settingsBuilder) { String authHeaderSettingName = ThreadContext.PREFIX + "." + UsernamePasswordToken.BASIC_AUTH_HEADER; if (settings.get(authHeaderSettingName) != null) { return; @@ -374,7 +384,7 @@ public class Security implements ActionPlugin { * * - forcibly enabling it (that means it's not possible to disable security on the tribe clients) */ - private void addTribeSettings(Settings.Builder settingsBuilder) { + private static void addTribeSettings(Settings settings, Settings.Builder settingsBuilder) { Map tribesSettings = settings.getGroups("tribe", true); if (tribesSettings.isEmpty()) { // it's not a tribe node diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java index ab019c37fab..8ebc8a6a6d4 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java @@ -143,7 +143,7 @@ public class SecurityFeatureSet implements XPackFeatureSet { static boolean systemKeyUsage(CryptoService cryptoService) { // we can piggy back on the encryption enabled method as it is only enabled if there is a system key - return cryptoService.encryptionEnabled(); + return cryptoService != null && cryptoService.isEncryptionEnabled(); } static class Usage extends XPackFeatureSet.Usage { diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilter.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilter.java index 6adde44d6dc..74e8bdda4c1 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilter.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilter.java @@ -191,7 +191,7 @@ public class SecurityActionFilter extends AbstractComponent implements ActionFil if (response instanceof SearchResponse) { SearchResponse searchResponse = (SearchResponse) response; String scrollId = searchResponse.getScrollId(); - if (scrollId != null && !cryptoService.signed(scrollId)) { + if (scrollId != null && !cryptoService.isSigned(scrollId)) { searchResponse.scrollId(cryptoService.sign(scrollId)); } return response; diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldExtractor.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldExtractor.java new file mode 100644 index 00000000000..bf5ab116a83 --- /dev/null +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldExtractor.java @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.security.authz.accesscontrol; + +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.DisjunctionMaxQuery; +import org.apache.lucene.search.DocValuesNumbersQuery; +import org.apache.lucene.search.DocValuesRangeQuery; +import org.apache.lucene.search.FieldValueQuery; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.MatchNoDocsQuery; +import org.apache.lucene.search.MultiPhraseQuery; +import org.apache.lucene.search.PhraseQuery; +import org.apache.lucene.search.PointInSetQuery; +import org.apache.lucene.search.PointRangeQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.SynonymQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.search.spans.SpanTermQuery; + +import java.util.Set; + +/** + * Extracts fields from a query, or throws UnsupportedOperationException. + *

+ * Lucene queries have {@link Weight#extractTerms}, but this is really geared at things + * such as highlighting, not security. For example terms in a Boolean {@code MUST_NOT} clause + * are not included, TermsQuery doesn't implement the method as it could be terribly slow, etc. + */ +class FieldExtractor { + + /** + * Populates {@code fields} with the set of fields used by the query, or throws + * UnsupportedOperationException if it doesn't know how to do this. + */ + static void extractFields(Query query, Set fields) throws UnsupportedOperationException { + // NOTE: we expect a rewritten query, so we only need logic for "atomic" queries here: + if (query instanceof BooleanQuery) { + // extract from all clauses + BooleanQuery q = (BooleanQuery) query; + for (BooleanClause clause : q.clauses()) { + extractFields(clause.getQuery(), fields); + } + } else if (query instanceof DisjunctionMaxQuery) { + // extract from all clauses + DisjunctionMaxQuery q = (DisjunctionMaxQuery) query; + for (Query clause : q.getDisjuncts()) { + extractFields(clause, fields); + } + } else if (query instanceof SpanTermQuery) { + // we just do SpanTerm, other spans are trickier, they could contain + // the evil FieldMaskingSpanQuery: so SpanQuery.getField cannot be trusted. + fields.add(((SpanTermQuery)query).getField()); + } else if (query instanceof TermQuery) { + fields.add(((TermQuery)query).getTerm().field()); + } else if (query instanceof SynonymQuery) { + SynonymQuery q = (SynonymQuery) query; + // all terms must have the same field + fields.add(q.getTerms().get(0).field()); + } else if (query instanceof PhraseQuery) { + PhraseQuery q = (PhraseQuery) query; + // all terms must have the same field + fields.add(q.getTerms()[0].field()); + } else if (query instanceof MultiPhraseQuery) { + MultiPhraseQuery q = (MultiPhraseQuery) query; + // all terms must have the same field + fields.add(q.getTermArrays()[0][0].field()); + } else if (query instanceof PointRangeQuery) { + fields.add(((PointRangeQuery)query).getField()); + } else if (query instanceof PointInSetQuery) { + fields.add(((PointInSetQuery)query).getField()); + } else if (query instanceof FieldValueQuery) { + fields.add(((FieldValueQuery)query).getField()); + } else if (query instanceof DocValuesNumbersQuery) { + fields.add(((DocValuesNumbersQuery)query).getField()); + } else if (query instanceof DocValuesRangeQuery) { + fields.add(((DocValuesRangeQuery)query).getField()); + } else if (query instanceof MatchAllDocsQuery) { + // no field + } else if (query instanceof MatchNoDocsQuery) { + // no field + } else { + throw new UnsupportedOperationException(); // we don't know how to get the fields from it + } + } +} diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldSubsetReader.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldSubsetReader.java index ea454c5b0ef..03b1a24dd00 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldSubsetReader.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldSubsetReader.java @@ -56,30 +56,33 @@ public final class FieldSubsetReader extends FilterLeafReader { * and so on. * @param in reader to filter * @param fieldNames fields to filter. + * @param negate {@code true} if this should be a negative set, meaning set of field names that is denied. */ - public static DirectoryReader wrap(DirectoryReader in, Set fieldNames) throws IOException { - return new FieldSubsetDirectoryReader(in, fieldNames); + public static DirectoryReader wrap(DirectoryReader in, Set fieldNames, boolean negate) throws IOException { + return new FieldSubsetDirectoryReader(in, fieldNames, negate); } // wraps subreaders with fieldsubsetreaders. static class FieldSubsetDirectoryReader extends FilterDirectoryReader { private final Set fieldNames; + private final boolean negate; - FieldSubsetDirectoryReader(DirectoryReader in, final Set fieldNames) throws IOException { + FieldSubsetDirectoryReader(DirectoryReader in, Set fieldNames, boolean negate) throws IOException { super(in, new FilterDirectoryReader.SubReaderWrapper() { @Override public LeafReader wrap(LeafReader reader) { - return new FieldSubsetReader(reader, fieldNames); + return new FieldSubsetReader(reader, fieldNames, negate); } }); this.fieldNames = fieldNames; + this.negate = negate; verifyNoOtherFieldSubsetDirectoryReaderIsWrapped(in); } @Override protected DirectoryReader doWrapDirectoryReader(DirectoryReader in) throws IOException { - return new FieldSubsetDirectoryReader(in, fieldNames); + return new FieldSubsetDirectoryReader(in, fieldNames, negate); } public Set getFieldNames() { @@ -111,17 +114,23 @@ public final class FieldSubsetReader extends FilterLeafReader { /** * Wrap a single segment, exposing a subset of its fields. + * @param fields set of field names that should be allowed + * @param negate {@code true} if this should be a negative set, meaning set of field names that is denied. */ - FieldSubsetReader(LeafReader in, Set fieldNames) { + FieldSubsetReader(LeafReader in, Set fields, boolean negate) { super(in); + // look at what fields the reader has, and preprocess a subset of them that are allowed ArrayList filteredInfos = new ArrayList<>(); for (FieldInfo fi : in.getFieldInfos()) { - if (fieldNames.contains(fi.name)) { + if (fields.contains(fi.name) ^ negate) { filteredInfos.add(fi); } } fieldInfos = new FieldInfos(filteredInfos.toArray(new FieldInfo[filteredInfos.size()])); - this.fieldNames = fieldNames.toArray(new String[fieldNames.size()]); + fieldNames = new String[filteredInfos.size()]; + for (int i = 0; i < fieldNames.length; i++) { + fieldNames[i] = filteredInfos.get(i).name; + } } /** returns true if this field is allowed. */ diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/OptOutQueryCache.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/OptOutQueryCache.java index c95d44096c8..7cf8a0e0e83 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/OptOutQueryCache.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/OptOutQueryCache.java @@ -16,8 +16,12 @@ import org.elasticsearch.indices.IndicesQueryCache; import org.elasticsearch.search.internal.ShardSearchRequest; import org.elasticsearch.xpack.security.authz.InternalAuthorizationService; +import java.util.HashSet; +import java.util.Set; + /** - * Opts out of the query cache if field level security is active for the current request. + * Opts out of the query cache if field level security is active for the current request, + * and its unsafe to cache. */ public final class OptOutQueryCache extends AbstractIndexComponent implements QueryCache { @@ -64,13 +68,41 @@ public final class OptOutQueryCache extends AbstractIndexComponent implements Qu IndicesAccessControl.IndexAccessControl indexAccessControl = indicesAccessControl.getIndexPermissions(indexName); if (indexAccessControl != null && indexAccessControl.getFields() != null) { - logger.debug("opting out of the query cache. request for index [{}] has field level security enabled", indexName); - // If in the future there is a Query#extractFields() then we can be smart on when to skip the query cache. - // (only cache if all fields in the query also are defined in the role) - return weight; + if (cachingIsSafe(weight, indexAccessControl)) { + logger.trace("not opting out of the query cache. request for index [{}] is safe to cache", indexName); + return indicesQueryCache.doCache(weight, policy); + } else { + logger.trace("opting out of the query cache. request for index [{}] is unsafe to cache", indexName); + return weight; + } } else { logger.trace("not opting out of the query cache. request for index [{}] has field level security disabled", indexName); return indicesQueryCache.doCache(weight, policy); } } + + /** + * Returns true if its safe to use the query cache for this query. + */ + static boolean cachingIsSafe(Weight weight, IndicesAccessControl.IndexAccessControl permissions) { + // support caching for common queries, by inspecting the field + // TODO: If in the future there is a Query#extractFields() then we can do a better job + Set fields = new HashSet<>(); + try { + FieldExtractor.extractFields(weight.getQuery(), fields); + } catch (UnsupportedOperationException ok) { + // we don't know how to safely extract the fields of this query, don't cache. + return false; + } + + // we successfully extracted the set of fields: check each one + for (String field : fields) { + // don't cache any internal fields (e.g. _field_names), these are complicated. + if (field.startsWith("_") || permissions.getFields().contains(field) == false) { + return false; + } + } + // we can cache, all fields are ok + return true; + } } diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapper.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapper.java index 6245e44c688..2e133336eea 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapper.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapper.java @@ -144,7 +144,8 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper { allowedFields.addAll(mapperService.simpleMatchToIndexNames(field)); } resolveParentChildJoinFields(allowedFields); - reader = FieldSubsetReader.wrap(reader, allowedFields); + // TODO: support 'denied' fields (pass true as the 3rd parameter in this case) + reader = FieldSubsetReader.wrap(reader, allowedFields, false); } return reader; diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoModule.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoModule.java deleted file mode 100644 index 7b400e7d8e5..00000000000 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoModule.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.xpack.security.crypto; - -import org.elasticsearch.common.inject.util.Providers; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.xpack.security.support.AbstractSecurityModule; - -/** - * - */ -public class CryptoModule extends AbstractSecurityModule.Node { - - public CryptoModule(Settings settings) { - super(settings); - } - - @Override - protected void configureNode() { - if (securityEnabled == false) { - bind(CryptoService.class).toProvider(Providers.of(null)); - return; - } - bind(InternalCryptoService.class).asEagerSingleton(); - bind(CryptoService.class).to(InternalCryptoService.class).asEagerSingleton(); - } -} diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoService.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoService.java index cbc4796eb9d..4447c1c0f47 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoService.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoService.java @@ -5,128 +5,497 @@ */ package org.elasticsearch.xpack.security.crypto; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; +import java.util.Objects; +import java.util.regex.Pattern; + +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.component.AbstractComponent; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.xpack.XPackPlugin; +import org.elasticsearch.xpack.security.authc.support.CharArrays; + +import static org.elasticsearch.xpack.security.Security.setting; +import static org.elasticsearch.xpack.security.authc.support.SecuredString.constantTimeEquals; /** * Service that provides cryptographic methods based on a shared system key */ -public interface CryptoService { +public class CryptoService extends AbstractComponent { + + public static final String KEY_ALGO = "HmacSHA512"; + public static final int KEY_SIZE = 1024; + + static final String FILE_NAME = "system_key"; + static final String HMAC_ALGO = "HmacSHA1"; + static final String DEFAULT_ENCRYPTION_ALGORITHM = "AES/CTR/NoPadding"; + static final String DEFAULT_KEY_ALGORITH = "AES"; + static final String ENCRYPTED_TEXT_PREFIX = "::es_encrypted::"; + static final byte[] ENCRYPTED_BYTE_PREFIX = ENCRYPTED_TEXT_PREFIX.getBytes(StandardCharsets.UTF_8); + static final int DEFAULT_KEY_LENGTH = 128; + static final int RANDOM_KEY_SIZE = 128; + + private static final Pattern SIG_PATTERN = Pattern.compile("^\\$\\$[0-9]+\\$\\$[^\\$]*\\$\\$.+"); + private static final byte[] HKDF_APP_INFO = "es-security-crypto-service".getBytes(StandardCharsets.UTF_8); + + public static final Setting FILE_SETTING = Setting.simpleString(setting("system_key.file"), Setting.Property.NodeScope); + public static final Setting ENCRYPTION_ALGO_SETTING = + new Setting<>(setting("encryption.algorithm"), s -> DEFAULT_ENCRYPTION_ALGORITHM, s -> s, Setting.Property.NodeScope); + public static final Setting ENCRYPTION_KEY_LENGTH_SETTING = + Setting.intSetting(setting("encryption_key.length"), DEFAULT_KEY_LENGTH, Setting.Property.NodeScope); + public static final Setting ENCRYPTION_KEY_ALGO_SETTING = + new Setting<>(setting("encryption_key.algorithm"), DEFAULT_KEY_ALGORITH, s -> s, Setting.Property.NodeScope); + + private final SecureRandom secureRandom = new SecureRandom(); + private final String encryptionAlgorithm; + private final String keyAlgorithm; + private final int keyLength; + private final int ivLength; + + private final Path keyFile; + + private final SecretKey randomKey; + private final String randomKeyBase64; + + private final SecretKey encryptionKey; + private final SecretKey systemKey; + private final SecretKey signingKey; + + public CryptoService(Settings settings, Environment env) throws IOException { + super(settings); + this.encryptionAlgorithm = ENCRYPTION_ALGO_SETTING.get(settings); + this.keyLength = ENCRYPTION_KEY_LENGTH_SETTING.get(settings); + this.ivLength = keyLength / 8; + this.keyAlgorithm = ENCRYPTION_KEY_ALGO_SETTING.get(settings); + + if (keyLength % 8 != 0) { + throw new IllegalArgumentException("invalid key length [" + keyLength + "]. value must be a multiple of 8"); + } + + keyFile = resolveSystemKey(settings, env); + systemKey = readSystemKey(keyFile); + randomKey = generateSecretKey(RANDOM_KEY_SIZE); + randomKeyBase64 = Base64.getUrlEncoder().encodeToString(randomKey.getEncoded()); + + signingKey = createSigningKey(systemKey, randomKey); + + try { + encryptionKey = encryptionKey(systemKey, keyLength, keyAlgorithm); + } catch (NoSuchAlgorithmException nsae) { + throw new ElasticsearchException("failed to start crypto service. could not load encryption key", nsae); + } + logger.info("system key [{}] has been loaded", keyFile.toAbsolutePath()); + } + + public static byte[] generateKey() { + return generateSecretKey(KEY_SIZE).getEncoded(); + } + + static SecretKey generateSecretKey(int keyLength) { + try { + KeyGenerator generator = KeyGenerator.getInstance(KEY_ALGO); + generator.init(keyLength); + return generator.generateKey(); + } catch (NoSuchAlgorithmException e) { + throw new ElasticsearchException("failed to generate key", e); + } + } + + public static Path resolveSystemKey(Settings settings, Environment env) { + String location = FILE_SETTING.get(settings); + if (location.isEmpty()) { + return XPackPlugin.resolveConfigFile(env, FILE_NAME); + } + return XPackPlugin.resolveConfigFile(env, location); + } + + static SecretKey createSigningKey(@Nullable SecretKey systemKey, SecretKey randomKey) { + assert randomKey != null; + if (systemKey != null) { + return systemKey; + } else { + // the random key is only 128 bits so we use HKDF to expand to 1024 bits with some application specific data mixed in + byte[] keyBytes = HmacSHA1HKDF.extractAndExpand(null, randomKey.getEncoded(), HKDF_APP_INFO, (KEY_SIZE / 8)); + assert keyBytes.length * 8 == KEY_SIZE; + return new SecretKeySpec(keyBytes, KEY_ALGO); + } + } + + private static SecretKey readSystemKey(Path file) { + if (!Files.exists(file)) { + return null; + } + try { + byte[] bytes = Files.readAllBytes(file); + return new SecretKeySpec(bytes, KEY_ALGO); + } catch (IOException e) { + throw new ElasticsearchException("could not read secret key", e); + } + } /** * Signs the given text and returns the signed text (original text + signature) * @param text the string to sign */ - String sign(String text) throws IOException; + public String sign(String text) throws IOException { + String sigStr = signInternal(text, signingKey); + return "$$" + sigStr.length() + "$$" + (systemKey == signingKey ? "" : randomKeyBase64) + "$$" + sigStr + text; + } /** * Unsigns the given signed text, verifies the original text with the attached signature and if valid returns * the unsigned (original) text. If signature verification fails a {@link IllegalArgumentException} is thrown. - * @param text the string to unsign and verify + * @param signedText the string to unsign and verify */ - String unsignAndVerify(String text); + public String unsignAndVerify(String signedText) { + if (!signedText.startsWith("$$") || signedText.length() < 2) { + throw new IllegalArgumentException("tampered signed text"); + } - /** - * Signs the given text and returns the signed text (original text + signature) - * @param text the string to sign - * @param key the key to sign the text with - * @param systemKey the system key. This is optional and if the key != systemKey then the format of the - * message will change - */ - String sign(String text, SecretKey key, SecretKey systemKey) throws IOException; + // $$34$$randomKeyBase64$$sigtext + String[] pieces = signedText.split("\\$\\$"); + if (pieces.length != 4 || !pieces[0].equals("")) { + logger.debug("received signed text [{}] with [{}] parts", signedText, pieces.length); + throw new IllegalArgumentException("tampered signed text"); + } + String text; + String base64RandomKey; + String receivedSignature; + try { + int length = Integer.parseInt(pieces[1]); + base64RandomKey = pieces[2]; + receivedSignature = pieces[3].substring(0, length); + text = pieces[3].substring(length); + } catch (Exception e) { + logger.error("error occurred while parsing signed text", e); + throw new IllegalArgumentException("tampered signed text"); + } - /** - * Unsigns the given signed text, verifies the original text with the attached signature and if valid returns - * the unsigned (original) text. If signature verification fails a {@link IllegalArgumentException} is thrown. - * @param text the string to unsign and verify - * @param key the key to unsign the text with - */ - String unsignAndVerify(String text, SecretKey key); + SecretKey signingKey; + // no random key, so we must have a system key + if (base64RandomKey.isEmpty()) { + if (systemKey == null) { + logger.debug("received signed text without random key information and no system key is present"); + throw new IllegalArgumentException("tampered signed text"); + } + signingKey = systemKey; + } else if (systemKey != null) { + // we have a system key and there is some random key data, this is an error + logger.debug("received signed text with random key information but a system key is present"); + throw new IllegalArgumentException("tampered signed text"); + } else { + byte[] randomKeyBytes; + try { + randomKeyBytes = Base64.getUrlDecoder().decode(base64RandomKey); + } catch (IllegalArgumentException e) { + logger.error("error occurred while decoding key data", e); + throw new IllegalStateException("error while verifying the signed text"); + } + if (randomKeyBytes.length * 8 != RANDOM_KEY_SIZE) { + logger.debug("incorrect random key data length. received [{}] bytes", randomKeyBytes.length); + throw new IllegalArgumentException("tampered signed text"); + } + SecretKey randomKey = new SecretKeySpec(randomKeyBytes, KEY_ALGO); + signingKey = createSigningKey(systemKey, randomKey); + } + + try { + String sig = signInternal(text, signingKey); + if (constantTimeEquals(sig, receivedSignature)) { + return text; + } + } catch (Exception e) { + logger.error("error occurred while verifying signed text", e); + throw new IllegalStateException("error while verifying the signed text"); + } + + throw new IllegalArgumentException("tampered signed text"); + } /** * Checks whether the given text is signed. */ - boolean signed(String text); + public boolean isSigned(String text) { + return SIG_PATTERN.matcher(text).matches(); + } /** * Encrypts the provided char array and returns the encrypted values in a char array * @param chars the characters to encrypt * @return character array representing the encrypted data */ - char[] encrypt(char[] chars); + public char[] encrypt(char[] chars) { + SecretKey key = this.encryptionKey; + if (key == null) { + logger.warn("encrypt called without a key, returning plain text. run syskeygen and copy same key to all nodes to enable " + + "encryption"); + return chars; + } - /** - * Encrypts the provided byte array and returns the encrypted value - * @param bytes the data to encrypt - * @return encrypted data - */ - byte[] encrypt(byte[] bytes); + byte[] charBytes = CharArrays.toUtf8Bytes(chars); + String base64 = Base64.getEncoder().encodeToString(encryptInternal(charBytes, key)); + return ENCRYPTED_TEXT_PREFIX.concat(base64).toCharArray(); + } /** * Decrypts the provided char array and returns the plain-text chars * @param chars the data to decrypt * @return plaintext chars */ - char[] decrypt(char[] chars); + public char[] decrypt(char[] chars) { + if (encryptionKey == null) { + return chars; + } - /** - * Decrypts the provided char array and returns the plain-text chars - * @param chars the data to decrypt - * @param key the key to decrypt the data with - * @return plaintext chars - */ - char[] decrypt(char[] chars, SecretKey key); + if (!isEncrypted(chars)) { + // Not encrypted + return chars; + } - /** - * Decrypts the provided byte array and returns the unencrypted bytes - * @param bytes the bytes to decrypt - * @return plaintext bytes - */ - byte[] decrypt(byte[] bytes); + String encrypted = new String(chars, ENCRYPTED_TEXT_PREFIX.length(), chars.length - ENCRYPTED_TEXT_PREFIX.length()); + byte[] bytes; + try { + bytes = Base64.getDecoder().decode(encrypted); + } catch (IllegalArgumentException e) { + throw new ElasticsearchException("unable to decode encrypted data", e); + } - /** - * Decrypts the provided byte array and returns the unencrypted bytes - * @param bytes the bytes to decrypt - * @param key the key to decrypt the data with - * @return plaintext bytes - */ - byte[] decrypt(byte[] bytes, SecretKey key); + byte[] decrypted = decryptInternal(bytes, encryptionKey); + return CharArrays.utf8BytesToChars(decrypted); + } /** * Checks whether the given chars are encrypted * @param chars the chars to check if they are encrypted * @return true is data is encrypted */ - boolean encrypted(char[] chars); - - /** - * Checks whether the given bytes are encrypted - * @param bytes the chars to check if they are encrypted - * @return true is data is encrypted - */ - boolean encrypted(byte[] bytes); - - /** - * Registers a listener to be notified of key changes - * @param listener the listener to be notified - */ - void register(Listener listener); + public boolean isEncrypted(char[] chars) { + return CharArrays.charsBeginsWith(ENCRYPTED_TEXT_PREFIX, chars); + } /** * Flag for callers to determine if values will actually be encrypted or returned plaintext * @return true if values will be encrypted */ - boolean encryptionEnabled(); + public boolean isEncryptionEnabled() { + return this.encryptionKey != null; + } + + private byte[] encryptInternal(byte[] bytes, SecretKey key) { + byte[] iv = new byte[ivLength]; + secureRandom.nextBytes(iv); + Cipher cipher = cipher(Cipher.ENCRYPT_MODE, encryptionAlgorithm, key, iv); + try { + byte[] encrypted = cipher.doFinal(bytes); + byte[] output = new byte[iv.length + encrypted.length]; + System.arraycopy(iv, 0, output, 0, iv.length); + System.arraycopy(encrypted, 0, output, iv.length, encrypted.length); + return output; + } catch (BadPaddingException | IllegalBlockSizeException e) { + throw new ElasticsearchException("error encrypting data", e); + } + } + + private byte[] decryptInternal(byte[] bytes, SecretKey key) { + if (bytes.length < ivLength) { + logger.error("received data for decryption with size [{}] that is less than IV length [{}]", bytes.length, ivLength); + throw new IllegalArgumentException("invalid data to decrypt"); + } + + byte[] iv = new byte[ivLength]; + System.arraycopy(bytes, 0, iv, 0, ivLength); + byte[] data = new byte[bytes.length - ivLength]; + System.arraycopy(bytes, ivLength, data, 0, bytes.length - ivLength); + + Cipher cipher = cipher(Cipher.DECRYPT_MODE, encryptionAlgorithm, key, iv); + try { + return cipher.doFinal(data); + } catch (BadPaddingException | IllegalBlockSizeException e) { + throw new IllegalStateException("error decrypting data", e); + } + } + + static Mac createMac(SecretKey key) { + try { + Mac mac = HmacSHA1Provider.hmacSHA1(); + mac.init(key); + return mac; + } catch (Exception e) { + throw new ElasticsearchException("could not initialize mac", e); + } + } + + private static String signInternal(String text, SecretKey key) throws IOException { + Mac mac = createMac(key); + byte[] sig = mac.doFinal(text.getBytes(StandardCharsets.UTF_8)); + return Base64.getUrlEncoder().encodeToString(sig); + } + + + static Cipher cipher(int mode, String encryptionAlgorithm, SecretKey key, byte[] initializationVector) { + try { + Cipher cipher = Cipher.getInstance(encryptionAlgorithm); + cipher.init(mode, key, new IvParameterSpec(initializationVector)); + return cipher; + } catch (Exception e) { + throw new ElasticsearchException("error creating cipher", e); + } + } + + static SecretKey encryptionKey(SecretKey systemKey, int keyLength, String algorithm) throws NoSuchAlgorithmException { + if (systemKey == null) { + return null; + } + + byte[] bytes = systemKey.getEncoded(); + if ((bytes.length * 8) < keyLength) { + throw new IllegalArgumentException("at least " + keyLength + " bits should be provided as key data"); + } + + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + byte[] digest = messageDigest.digest(bytes); + assert digest.length == (256 / 8); + + if ((digest.length * 8) < keyLength) { + throw new IllegalArgumentException("requested key length is too large"); + } + byte[] truncatedDigest = Arrays.copyOfRange(digest, 0, (keyLength / 8)); + + return new SecretKeySpec(truncatedDigest, algorithm); + } + + /** + * Provider class for the HmacSHA1 {@link Mac} that provides an optimization by using a thread local instead of calling + * Mac#getInstance and obtaining a lock (in the internals) + */ + private static class HmacSHA1Provider { + + private static final ThreadLocal MAC = ThreadLocal.withInitial(() -> { + try { + return Mac.getInstance(HMAC_ALGO); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("could not create Mac instance with algorithm [" + HMAC_ALGO + "]", e); + } + }); + + private static Mac hmacSHA1() { + Mac instance = MAC.get(); + instance.reset(); + return instance; + } + + } + + /** + * Simplified implementation of HKDF using the HmacSHA1 algortihm. + * + * @see RFC 5869 + */ + private static class HmacSHA1HKDF { + private static final int HMAC_SHA1_BYTE_LENGTH = 20; + private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; - interface Listener { /** - * This method will be called immediately after a new system key and encryption key are loaded by the - * service. This provides the old keys back to the clients so that they may perform decryption and re-encryption - * of data after a key has been changed + * This method performs the extract and expand steps of HKDF in one call with the given + * data. The output of the extract step is used as the input to the expand step * - * @param oldSystemKey the pre-existing system key - * @param oldEncryptionKey the pre-existing encryption key + * @param salt optional salt value (a non-secret random value); if not provided, it is set to a string of HashLen zeros. + * @param ikm the input keying material + * @param info optional context and application specific information; if not provided a zero length byte[] is used + * @param outputLength length of output keying material in octets (<= 255*HashLen) + * @return the output keying material */ - void onKeyChange(SecretKey oldSystemKey, SecretKey oldEncryptionKey); + static byte[] extractAndExpand(@Nullable SecretKey salt, byte[] ikm, @Nullable byte[] info, int outputLength) { + // arg checking + Objects.requireNonNull(ikm, "the input keying material must not be null"); + if (outputLength < 1) { + throw new IllegalArgumentException("output length must be positive int >= 1"); + } + if (outputLength > 255 * HMAC_SHA1_BYTE_LENGTH) { + throw new IllegalArgumentException("output length must be <= 255*" + HMAC_SHA1_BYTE_LENGTH); + } + if (salt == null) { + salt = new SecretKeySpec(new byte[HMAC_SHA1_BYTE_LENGTH], HMAC_SHA1_ALGORITHM); + } + if (info == null) { + info = new byte[0]; + } + + // extract + Mac mac = createMac(salt); + byte[] keyBytes = mac.doFinal(ikm); + final SecretKey pseudoRandomKey = new SecretKeySpec(keyBytes, HMAC_SHA1_ALGORITHM); + + /* + * The output OKM is calculated as follows: + * N = ceil(L/HashLen) + * T = T(1) | T(2) | T(3) | ... | T(N) + * OKM = first L octets of T + * + * where: + * T(0) = empty string (zero length) + * T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) + * T(2) = HMAC-Hash(PRK, T(1) | info | 0x02) + * T(3) = HMAC-Hash(PRK, T(2) | info | 0x03) + * ... + * + * (where the constant concatenated to the end of each T(n) is a single octet.) + */ + int n = (outputLength % HMAC_SHA1_BYTE_LENGTH == 0) ? + outputLength / HMAC_SHA1_BYTE_LENGTH : + (outputLength / HMAC_SHA1_BYTE_LENGTH) + 1; + + byte[] hashRound = new byte[0]; + + ByteBuffer generatedBytes = ByteBuffer.allocate(Math.multiplyExact(n, HMAC_SHA1_BYTE_LENGTH)); + try { + // initiliaze the mac with the new key + mac.init(pseudoRandomKey); + } catch (InvalidKeyException e) { + throw new ElasticsearchException("failed to initialize the mac", e); + } + for (int roundNum = 1; roundNum <= n; roundNum++) { + mac.reset(); + mac.update(hashRound); + mac.update(info); + mac.update((byte) roundNum); + hashRound = mac.doFinal(); + generatedBytes.put(hashRound); + } + + byte[] result = new byte[outputLength]; + generatedBytes.rewind(); + generatedBytes.get(result, 0, outputLength); + return result; + } + } + + public static void addSettings(List> settings) { + settings.add(FILE_SETTING); + settings.add(ENCRYPTION_KEY_LENGTH_SETTING); + settings.add(ENCRYPTION_KEY_ALGO_SETTING); + settings.add(ENCRYPTION_ALGO_SETTING); } } diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/InternalCryptoService.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/InternalCryptoService.java deleted file mode 100644 index 08155f3b34e..00000000000 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/InternalCryptoService.java +++ /dev/null @@ -1,673 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.xpack.security.crypto; - -import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.common.Nullable; -import org.elasticsearch.common.component.AbstractLifecycleComponent; -import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.settings.Setting; -import org.elasticsearch.common.settings.Setting.Property; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.watcher.FileChangesListener; -import org.elasticsearch.watcher.FileWatcher; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xpack.XPackPlugin; -import org.elasticsearch.xpack.security.authc.support.CharArrays; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.KeyGenerator; -import javax.crypto.Mac; -import javax.crypto.SecretKey; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.Base64; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.regex.Pattern; - -import static org.elasticsearch.xpack.security.Security.setting; -import static org.elasticsearch.xpack.security.authc.support.SecuredString.constantTimeEquals; - -public class InternalCryptoService extends AbstractLifecycleComponent implements CryptoService { - - public static final String KEY_ALGO = "HmacSHA512"; - public static final int KEY_SIZE = 1024; - - static final String FILE_NAME = "system_key"; - static final String HMAC_ALGO = "HmacSHA1"; - static final String DEFAULT_ENCRYPTION_ALGORITHM = "AES/CTR/NoPadding"; - static final String DEFAULT_KEY_ALGORITH = "AES"; - static final String ENCRYPTED_TEXT_PREFIX = "::es_encrypted::"; - static final byte[] ENCRYPTED_BYTE_PREFIX = ENCRYPTED_TEXT_PREFIX.getBytes(StandardCharsets.UTF_8); - static final int DEFAULT_KEY_LENGTH = 128; - static final int RANDOM_KEY_SIZE = 128; - - private static final Pattern SIG_PATTERN = Pattern.compile("^\\$\\$[0-9]+\\$\\$[^\\$]*\\$\\$.+"); - private static final byte[] HKDF_APP_INFO = "es-security-crypto-service".getBytes(StandardCharsets.UTF_8); - - public static final Setting FILE_SETTING = Setting.simpleString(setting("system_key.file"), Property.NodeScope); - public static final Setting ENCRYPTION_ALGO_SETTING = - new Setting<>(setting("encryption.algorithm"), s -> DEFAULT_ENCRYPTION_ALGORITHM, s -> s, Property.NodeScope); - public static final Setting ENCRYPTION_KEY_LENGTH_SETTING = - Setting.intSetting(setting("encryption_key.length"), DEFAULT_KEY_LENGTH, Property.NodeScope); - public static final Setting ENCRYPTION_KEY_ALGO_SETTING = - new Setting<>(setting("encryption_key.algorithm"), DEFAULT_KEY_ALGORITH, s -> s, Property.NodeScope); - - private final Environment env; - private final ResourceWatcherService watcherService; - private final List listeners; - private final SecureRandom secureRandom = new SecureRandom(); - private final String encryptionAlgorithm; - private final String keyAlgorithm; - private final int keyLength; - private final int ivLength; - - private Path keyFile; - - private SecretKey randomKey; - private String randomKeyBase64; - - private volatile SecretKey encryptionKey; - private volatile SecretKey systemKey; - private volatile SecretKey signingKey; - - @Inject - public InternalCryptoService(Settings settings, Environment env, ResourceWatcherService watcherService) { - this(settings, env, watcherService, Collections.emptyList()); - } - - InternalCryptoService(Settings settings, Environment env, ResourceWatcherService watcherService, List listeners) { - super(settings); - this.env = env; - this.watcherService = watcherService; - this.listeners = new CopyOnWriteArrayList<>(listeners); - this.encryptionAlgorithm = ENCRYPTION_ALGO_SETTING.get(settings); - this.keyLength = ENCRYPTION_KEY_LENGTH_SETTING.get(settings); - this.ivLength = keyLength / 8; - this.keyAlgorithm = ENCRYPTION_KEY_ALGO_SETTING.get(settings); - } - - @Override - protected void doStart() throws ElasticsearchException { - if (keyLength % 8 != 0) { - throw new IllegalArgumentException("invalid key length [" + keyLength + "]. value must be a multiple of 8"); - } - - loadKeys(); - FileWatcher watcher = new FileWatcher(keyFile.getParent()); - watcher.addListener(new FileListener(listeners)); - try { - watcherService.add(watcher, ResourceWatcherService.Frequency.HIGH); - } catch (IOException e) { - throw new ElasticsearchException("failed to start watching system key file [" + keyFile.toAbsolutePath() + "]", e); - } - } - - @Override - protected void doStop() throws ElasticsearchException { - } - - @Override - protected void doClose() throws ElasticsearchException { - } - - private void loadKeys() { - keyFile = resolveSystemKey(settings, env); - systemKey = readSystemKey(keyFile); - randomKey = generateSecretKey(RANDOM_KEY_SIZE); - randomKeyBase64 = Base64.getUrlEncoder().encodeToString(randomKey.getEncoded()); - - signingKey = createSigningKey(systemKey, randomKey); - - try { - encryptionKey = encryptionKey(systemKey, keyLength, keyAlgorithm); - } catch (NoSuchAlgorithmException nsae) { - throw new ElasticsearchException("failed to start crypto service. could not load encryption key", nsae); - } - } - - public static byte[] generateKey() { - return generateSecretKey(KEY_SIZE).getEncoded(); - } - - static SecretKey generateSecretKey(int keyLength) { - try { - KeyGenerator generator = KeyGenerator.getInstance(KEY_ALGO); - generator.init(keyLength); - return generator.generateKey(); - } catch (NoSuchAlgorithmException e) { - throw new ElasticsearchException("failed to generate key", e); - } - } - - public static Path resolveSystemKey(Settings settings, Environment env) { - String location = FILE_SETTING.get(settings); - if (location.isEmpty()) { - return XPackPlugin.resolveConfigFile(env, FILE_NAME); - } - return XPackPlugin.resolveConfigFile(env, location); - } - - static SecretKey createSigningKey(@Nullable SecretKey systemKey, SecretKey randomKey) { - assert randomKey != null; - if (systemKey != null) { - return systemKey; - } else { - // the random key is only 128 bits so we use HKDF to expand to 1024 bits with some application specific data mixed in - byte[] keyBytes = HmacSHA1HKDF.extractAndExpand(null, randomKey.getEncoded(), HKDF_APP_INFO, (KEY_SIZE / 8)); - assert keyBytes.length * 8 == KEY_SIZE; - return new SecretKeySpec(keyBytes, KEY_ALGO); - } - } - - private static SecretKey readSystemKey(Path file) { - if (!Files.exists(file)) { - return null; - } - try { - byte[] bytes = Files.readAllBytes(file); - return new SecretKeySpec(bytes, KEY_ALGO); - } catch (IOException e) { - throw new ElasticsearchException("could not read secret key", e); - } - } - - @Override - public String sign(String text) throws IOException { - return sign(text, this.signingKey, this.systemKey); - } - - @Override - public String sign(String text, SecretKey signingKey, @Nullable SecretKey systemKey) throws IOException { - assert signingKey != null; - String sigStr = signInternal(text, signingKey); - return "$$" + sigStr.length() + "$$" + (systemKey == signingKey ? "" : randomKeyBase64) + "$$" + sigStr + text; - } - - @Override - public String unsignAndVerify(String signedText) { - return unsignAndVerify(signedText, this.systemKey); - } - - @Override - public String unsignAndVerify(String signedText, SecretKey systemKey) { - if (!signedText.startsWith("$$") || signedText.length() < 2) { - throw new IllegalArgumentException("tampered signed text"); - } - - // $$34$$randomKeyBase64$$sigtext - String[] pieces = signedText.split("\\$\\$"); - if (pieces.length != 4 || !pieces[0].equals("")) { - logger.debug("received signed text [{}] with [{}] parts", signedText, pieces.length); - throw new IllegalArgumentException("tampered signed text"); - } - String text; - String base64RandomKey; - String receivedSignature; - try { - int length = Integer.parseInt(pieces[1]); - base64RandomKey = pieces[2]; - receivedSignature = pieces[3].substring(0, length); - text = pieces[3].substring(length); - } catch (Exception e) { - logger.error("error occurred while parsing signed text", e); - throw new IllegalArgumentException("tampered signed text"); - } - - SecretKey signingKey; - // no random key, so we must have a system key - if (base64RandomKey.isEmpty()) { - if (systemKey == null) { - logger.debug("received signed text without random key information and no system key is present"); - throw new IllegalArgumentException("tampered signed text"); - } - signingKey = systemKey; - } else if (systemKey != null) { - // we have a system key and there is some random key data, this is an error - logger.debug("received signed text with random key information but a system key is present"); - throw new IllegalArgumentException("tampered signed text"); - } else { - byte[] randomKeyBytes; - try { - randomKeyBytes = Base64.getUrlDecoder().decode(base64RandomKey); - } catch (IllegalArgumentException e) { - logger.error("error occurred while decoding key data", e); - throw new IllegalStateException("error while verifying the signed text"); - } - if (randomKeyBytes.length * 8 != RANDOM_KEY_SIZE) { - logger.debug("incorrect random key data length. received [{}] bytes", randomKeyBytes.length); - throw new IllegalArgumentException("tampered signed text"); - } - SecretKey randomKey = new SecretKeySpec(randomKeyBytes, KEY_ALGO); - signingKey = createSigningKey(systemKey, randomKey); - } - - try { - String sig = signInternal(text, signingKey); - if (constantTimeEquals(sig, receivedSignature)) { - return text; - } - } catch (Exception e) { - logger.error("error occurred while verifying signed text", e); - throw new IllegalStateException("error while verifying the signed text"); - } - - throw new IllegalArgumentException("tampered signed text"); - } - - @Override - public boolean signed(String text) { - return SIG_PATTERN.matcher(text).matches(); - } - - @Override - public char[] encrypt(char[] chars) { - SecretKey key = this.encryptionKey; - if (key == null) { - logger.warn("encrypt called without a key, returning plain text. run syskeygen and copy same key to all nodes to enable " + - "encryption"); - return chars; - } - - byte[] charBytes = CharArrays.toUtf8Bytes(chars); - String base64 = Base64.getEncoder().encodeToString(encryptInternal(charBytes, key)); - return ENCRYPTED_TEXT_PREFIX.concat(base64).toCharArray(); - } - - @Override - public byte[] encrypt(byte[] bytes) { - SecretKey key = this.encryptionKey; - if (key == null) { - logger.warn("encrypt called without a key, returning plain text. run syskeygen and copy same key to all nodes to enable " + - "encryption"); - return bytes; - } - byte[] encrypted = encryptInternal(bytes, key); - byte[] prefixed = new byte[ENCRYPTED_BYTE_PREFIX.length + encrypted.length]; - System.arraycopy(ENCRYPTED_BYTE_PREFIX, 0, prefixed, 0, ENCRYPTED_BYTE_PREFIX.length); - System.arraycopy(encrypted, 0, prefixed, ENCRYPTED_BYTE_PREFIX.length, encrypted.length); - return prefixed; - } - - @Override - public char[] decrypt(char[] chars) { - return decrypt(chars, this.encryptionKey); - } - - @Override - public char[] decrypt(char[] chars, SecretKey key) { - if (key == null) { - return chars; - } - - if (!encrypted(chars)) { - // Not encrypted - return chars; - } - - String encrypted = new String(chars, ENCRYPTED_TEXT_PREFIX.length(), chars.length - ENCRYPTED_TEXT_PREFIX.length()); - byte[] bytes; - try { - bytes = Base64.getDecoder().decode(encrypted); - } catch (IllegalArgumentException e) { - throw new ElasticsearchException("unable to decode encrypted data", e); - } - - byte[] decrypted = decryptInternal(bytes, key); - return CharArrays.utf8BytesToChars(decrypted); - } - - @Override - public byte[] decrypt(byte[] bytes) { - return decrypt(bytes, this.encryptionKey); - } - - @Override - public byte[] decrypt(byte[] bytes, SecretKey key) { - if (key == null) { - return bytes; - } - - if (!encrypted(bytes)) { - return bytes; - } - - byte[] encrypted = Arrays.copyOfRange(bytes, ENCRYPTED_BYTE_PREFIX.length, bytes.length); - return decryptInternal(encrypted, key); - } - - @Override - public boolean encrypted(char[] chars) { - return CharArrays.charsBeginsWith(ENCRYPTED_TEXT_PREFIX, chars); - } - - @Override - public boolean encrypted(byte[] bytes) { - return bytesBeginsWith(ENCRYPTED_BYTE_PREFIX, bytes); - } - - @Override - public void register(Listener listener) { - this.listeners.add(listener); - } - - @Override - public boolean encryptionEnabled() { - return this.encryptionKey != null; - } - - private byte[] encryptInternal(byte[] bytes, SecretKey key) { - byte[] iv = new byte[ivLength]; - secureRandom.nextBytes(iv); - Cipher cipher = cipher(Cipher.ENCRYPT_MODE, encryptionAlgorithm, key, iv); - try { - byte[] encrypted = cipher.doFinal(bytes); - byte[] output = new byte[iv.length + encrypted.length]; - System.arraycopy(iv, 0, output, 0, iv.length); - System.arraycopy(encrypted, 0, output, iv.length, encrypted.length); - return output; - } catch (BadPaddingException | IllegalBlockSizeException e) { - throw new ElasticsearchException("error encrypting data", e); - } - } - - private byte[] decryptInternal(byte[] bytes, SecretKey key) { - if (bytes.length < ivLength) { - logger.error("received data for decryption with size [{}] that is less than IV length [{}]", bytes.length, ivLength); - throw new IllegalArgumentException("invalid data to decrypt"); - } - - byte[] iv = new byte[ivLength]; - System.arraycopy(bytes, 0, iv, 0, ivLength); - byte[] data = new byte[bytes.length - ivLength]; - System.arraycopy(bytes, ivLength, data, 0, bytes.length - ivLength); - - Cipher cipher = cipher(Cipher.DECRYPT_MODE, encryptionAlgorithm, key, iv); - try { - return cipher.doFinal(data); - } catch (BadPaddingException | IllegalBlockSizeException e) { - throw new IllegalStateException("error decrypting data", e); - } - } - - static Mac createMac(SecretKey key) { - try { - Mac mac = HmacSHA1Provider.hmacSHA1(); - mac.init(key); - return mac; - } catch (Exception e) { - throw new ElasticsearchException("could not initialize mac", e); - } - } - - private static String signInternal(String text, SecretKey key) throws IOException { - Mac mac = createMac(key); - byte[] sig = mac.doFinal(text.getBytes(StandardCharsets.UTF_8)); - return Base64.getUrlEncoder().encodeToString(sig); - } - - - static Cipher cipher(int mode, String encryptionAlgorithm, SecretKey key, byte[] initializationVector) { - try { - Cipher cipher = Cipher.getInstance(encryptionAlgorithm); - cipher.init(mode, key, new IvParameterSpec(initializationVector)); - return cipher; - } catch (Exception e) { - throw new ElasticsearchException("error creating cipher", e); - } - } - - static SecretKey encryptionKey(SecretKey systemKey, int keyLength, String algorithm) throws NoSuchAlgorithmException { - if (systemKey == null) { - return null; - } - - byte[] bytes = systemKey.getEncoded(); - if ((bytes.length * 8) < keyLength) { - throw new IllegalArgumentException("at least " + keyLength + " bits should be provided as key data"); - } - - MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); - byte[] digest = messageDigest.digest(bytes); - assert digest.length == (256 / 8); - - if ((digest.length * 8) < keyLength) { - throw new IllegalArgumentException("requested key length is too large"); - } - byte[] truncatedDigest = Arrays.copyOfRange(digest, 0, (keyLength / 8)); - - return new SecretKeySpec(truncatedDigest, algorithm); - } - - private static boolean bytesBeginsWith(byte[] prefix, byte[] bytes) { - if (bytes == null || prefix == null) { - return false; - } - - if (prefix.length > bytes.length) { - return false; - } - - for (int i = 0; i < prefix.length; i++) { - if (bytes[i] != prefix[i]) { - return false; - } - } - - return true; - } - - private class FileListener extends FileChangesListener { - - private final List listeners; - - private FileListener(List listeners) { - this.listeners = listeners; - } - - @Override - public void onFileCreated(Path file) { - if (file.equals(keyFile)) { - final SecretKey oldSystemKey = systemKey; - final SecretKey oldEncryptionKey = encryptionKey; - - systemKey = readSystemKey(file); - signingKey = createSigningKey(systemKey, randomKey); - try { - encryptionKey = encryptionKey(signingKey, keyLength, keyAlgorithm); - } catch (NoSuchAlgorithmException nsae) { - logger.error("could not load encryption key", nsae); - encryptionKey = null; - } - logger.info("system key [{}] has been loaded", file.toAbsolutePath()); - callListeners(oldSystemKey, oldEncryptionKey); - } - } - - @Override - public void onFileDeleted(Path file) { - if (file.equals(keyFile)) { - final SecretKey oldSystemKey = systemKey; - final SecretKey oldEncryptionKey = encryptionKey; - logger.error("system key file was removed! as long as the system key file is missing, elasticsearch " + - "won't function as expected for some requests (e.g. scroll/scan)"); - systemKey = null; - encryptionKey = null; - signingKey = createSigningKey(systemKey, randomKey); - - callListeners(oldSystemKey, oldEncryptionKey); - } - } - - @Override - public void onFileChanged(Path file) { - if (file.equals(keyFile)) { - final SecretKey oldSystemKey = systemKey; - final SecretKey oldEncryptionKey = encryptionKey; - - logger.warn("system key file changed!"); - SecretKey systemKey = readSystemKey(file); - signingKey = createSigningKey(systemKey, randomKey); - try { - encryptionKey = encryptionKey(signingKey, keyLength, keyAlgorithm); - } catch (NoSuchAlgorithmException nsae) { - logger.error("could not load encryption key", nsae); - encryptionKey = null; - } - - callListeners(oldSystemKey, oldEncryptionKey); - } - } - - private void callListeners(SecretKey oldSystemKey, SecretKey oldEncryptionKey) { - RuntimeException ex = null; - for (Listener listener : listeners) { - try { - listener.onKeyChange(oldSystemKey, oldEncryptionKey); - } catch (Exception e) { - if (ex == null) ex = new RuntimeException("exception calling key change listeners"); - ex.addSuppressed(e); - } - } - - // all listeners were notified now rethrow - if (ex != null) { - logger.error("called all key change listeners but one or more exceptions was thrown", ex); - throw ex; - } - } - } - - /** - * Provider class for the HmacSHA1 {@link Mac} that provides an optimization by using a thread local instead of calling - * Mac#getInstance and obtaining a lock (in the internals) - */ - private static class HmacSHA1Provider { - - private static final ThreadLocal MAC = ThreadLocal.withInitial(() -> { - try { - return Mac.getInstance(HMAC_ALGO); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("could not create Mac instance with algorithm [" + HMAC_ALGO + "]", e); - } - }); - - private static Mac hmacSHA1() { - Mac instance = MAC.get(); - instance.reset(); - return instance; - } - - } - - /** - * Simplified implementation of HKDF using the HmacSHA1 algortihm. - * - * @see RFC 5869 - */ - private static class HmacSHA1HKDF { - private static final int HMAC_SHA1_BYTE_LENGTH = 20; - private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; - - /** - * This method performs the extract and expand steps of HKDF in one call with the given - * data. The output of the extract step is used as the input to the expand step - * - * @param salt optional salt value (a non-secret random value); if not provided, it is set to a string of HashLen zeros. - * @param ikm the input keying material - * @param info optional context and application specific information; if not provided a zero length byte[] is used - * @param outputLength length of output keying material in octets (<= 255*HashLen) - * @return the output keying material - */ - static byte[] extractAndExpand(@Nullable SecretKey salt, byte[] ikm, @Nullable byte[] info, int outputLength) { - // arg checking - Objects.requireNonNull(ikm, "the input keying material must not be null"); - if (outputLength < 1) { - throw new IllegalArgumentException("output length must be positive int >= 1"); - } - if (outputLength > 255 * HMAC_SHA1_BYTE_LENGTH) { - throw new IllegalArgumentException("output length must be <= 255*" + HMAC_SHA1_BYTE_LENGTH); - } - if (salt == null) { - salt = new SecretKeySpec(new byte[HMAC_SHA1_BYTE_LENGTH], HMAC_SHA1_ALGORITHM); - } - if (info == null) { - info = new byte[0]; - } - - // extract - Mac mac = createMac(salt); - byte[] keyBytes = mac.doFinal(ikm); - final SecretKey pseudoRandomKey = new SecretKeySpec(keyBytes, HMAC_SHA1_ALGORITHM); - - /* - * The output OKM is calculated as follows: - * N = ceil(L/HashLen) - * T = T(1) | T(2) | T(3) | ... | T(N) - * OKM = first L octets of T - * - * where: - * T(0) = empty string (zero length) - * T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) - * T(2) = HMAC-Hash(PRK, T(1) | info | 0x02) - * T(3) = HMAC-Hash(PRK, T(2) | info | 0x03) - * ... - * - * (where the constant concatenated to the end of each T(n) is a single octet.) - */ - int n = (outputLength % HMAC_SHA1_BYTE_LENGTH == 0) ? - outputLength / HMAC_SHA1_BYTE_LENGTH : - (outputLength / HMAC_SHA1_BYTE_LENGTH) + 1; - - byte[] hashRound = new byte[0]; - - ByteBuffer generatedBytes = ByteBuffer.allocate(Math.multiplyExact(n, HMAC_SHA1_BYTE_LENGTH)); - try { - // initiliaze the mac with the new key - mac.init(pseudoRandomKey); - } catch (InvalidKeyException e) { - throw new ElasticsearchException("failed to initialize the mac", e); - } - for (int roundNum = 1; roundNum <= n; roundNum++) { - mac.reset(); - mac.update(hashRound); - mac.update(info); - mac.update((byte) roundNum); - hashRound = mac.doFinal(); - generatedBytes.put(hashRound); - } - - byte[] result = new byte[outputLength]; - generatedBytes.rewind(); - generatedBytes.get(result, 0, outputLength); - return result; - } - } - - public static void addSettings(List> settings) { - settings.add(FILE_SETTING); - settings.add(ENCRYPTION_KEY_LENGTH_SETTING); - settings.add(ENCRYPTION_KEY_ALGO_SETTING); - settings.add(ENCRYPTION_ALGO_SETTING); - } -} diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java index 602b6c4d8a8..24a4e277bc6 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java @@ -17,7 +17,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.env.Environment; import org.elasticsearch.node.internal.InternalSettingsPreparer; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import java.nio.file.Files; import java.nio.file.Path; @@ -66,12 +66,12 @@ public class SystemKeyTool extends SettingCommand { } keyPath = parsePath(args.get(0)); } else { - keyPath = InternalCryptoService.resolveSystemKey(env.settings(), env); + keyPath = CryptoService.resolveSystemKey(env.settings(), env); } // write the key terminal.println(Terminal.Verbosity.VERBOSE, "generating..."); - byte[] key = InternalCryptoService.generateKey(); + byte[] key = CryptoService.generateKey(); terminal.println(String.format(Locale.ROOT, "Storing generated key in [%s]...", keyPath.toAbsolutePath())); Files.write(keyPath, key, StandardOpenOption.CREATE_NEW); diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateAction.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateAction.java index 6b9797b8913..d592d59832a 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateAction.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateAction.java @@ -35,6 +35,12 @@ public class RestAuthenticateAction extends BaseRestHandler { super(settings); this.securityContext = securityContext; controller.registerHandler(GET, "/_xpack/security/_authenticate", this); + + // @deprecated: Remove in 6.0 + controller.registerAsDeprecatedHandler(GET, "/_shield/authenticate", this, + "[GET /_shield/authenticate] is deprecated! Use " + + "[GET /_xpack/security/_authenticate] instead.", + deprecationLogger); } @Override diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/realm/RestClearRealmCacheAction.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/realm/RestClearRealmCacheAction.java index 8f5dabef254..77d1f274288 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/realm/RestClearRealmCacheAction.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/realm/RestClearRealmCacheAction.java @@ -23,8 +23,17 @@ public class RestClearRealmCacheAction extends BaseRestHandler { @Inject public RestClearRealmCacheAction(Settings settings, RestController controller) { super(settings); - controller.registerHandler(POST, "/_xpack/security/realm/{realms}/_cache/clear", this); // deprecated controller.registerHandler(POST, "/_xpack/security/realm/{realms}/_clear_cache", this); + + // @deprecated: Remove in 6.0 + controller.registerAsDeprecatedHandler(POST, "/_shield/realm/{realms}/_cache/clear", this, + "[POST /_shield/realm/{realms}/_cache/clear] is deprecated! Use " + + "[POST /_xpack/security/realm/{realms}/_clear_cache] instead.", + deprecationLogger); + controller.registerAsDeprecatedHandler(POST, "/_shield/realm/{realms}/_clear_cache", this, + "[POST /_shield/realm/{realms}/_clear_cache] is deprecated! Use " + + "[POST /_xpack/security/realm/{realms}/_clear_cache] instead.", + deprecationLogger); } @Override diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestClearRolesCacheAction.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestClearRolesCacheAction.java index f591ea9fc14..c71401eca77 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestClearRolesCacheAction.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestClearRolesCacheAction.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.security.rest.action.role; -import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; @@ -28,6 +27,12 @@ public class RestClearRolesCacheAction extends BaseRestHandler { public RestClearRolesCacheAction(Settings settings, RestController controller) { super(settings); controller.registerHandler(POST, "/_xpack/security/role/{name}/_clear_cache", this); + + // @deprecated: Remove in 6.0 + controller.registerAsDeprecatedHandler(POST, "/_shield/role/{name}/_clear_cache", this, + "[POST /_shield/role/{name}/_clear_cache] is deprecated! Use " + + "[POST /_xpack/security/role/{name}/_clear_cache] instead.", + deprecationLogger); } @Override diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestDeleteRoleAction.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestDeleteRoleAction.java index a39f35465a3..36328cf427e 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestDeleteRoleAction.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestDeleteRoleAction.java @@ -21,6 +21,8 @@ import org.elasticsearch.xpack.security.action.role.DeleteRoleRequestBuilder; import org.elasticsearch.xpack.security.action.role.DeleteRoleResponse; import org.elasticsearch.xpack.security.client.SecurityClient; +import static org.elasticsearch.rest.RestRequest.Method.DELETE; + /** * Rest endpoint to delete a Role from the security index */ @@ -29,7 +31,13 @@ public class RestDeleteRoleAction extends BaseRestHandler { @Inject public RestDeleteRoleAction(Settings settings, RestController controller) { super(settings); - controller.registerHandler(RestRequest.Method.DELETE, "/_xpack/security/role/{name}", this); + controller.registerHandler(DELETE, "/_xpack/security/role/{name}", this); + + // @deprecated: Remove in 6.0 + controller.registerAsDeprecatedHandler(DELETE, "/_shield/role/{name}", this, + "[DELETE /_shield/role/{name}] is deprecated! Use " + + "[DELETE /_xpack/security/role/{name}] instead.", + deprecationLogger); } @Override diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestGetRolesAction.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestGetRolesAction.java index c8852bc954c..d9f4966d0a0 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestGetRolesAction.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestGetRolesAction.java @@ -22,6 +22,8 @@ import org.elasticsearch.xpack.security.action.role.GetRolesResponse; import org.elasticsearch.xpack.security.client.SecurityClient; import org.elasticsearch.xpack.security.authz.RoleDescriptor; +import static org.elasticsearch.rest.RestRequest.Method.GET; + /** * Rest endpoint to retrieve a Role from the security index */ @@ -30,8 +32,18 @@ public class RestGetRolesAction extends BaseRestHandler { @Inject public RestGetRolesAction(Settings settings, RestController controller) { super(settings); - controller.registerHandler(RestRequest.Method.GET, "/_xpack/security/role/", this); - controller.registerHandler(RestRequest.Method.GET, "/_xpack/security/role/{name}", this); + controller.registerHandler(GET, "/_xpack/security/role/", this); + controller.registerHandler(GET, "/_xpack/security/role/{name}", this); + + // @deprecated: Remove in 6.0 + controller.registerAsDeprecatedHandler(GET, "/_shield/role", this, + "[GET /_shield/role] is deprecated! Use " + + "[GET /_xpack/security/role] instead.", + deprecationLogger); + controller.registerAsDeprecatedHandler(GET, "/_shield/role/{name}", this, + "[GET /_shield/role/{name}] is deprecated! Use " + + "[GET /_xpack/security/role/{name}] instead.", + deprecationLogger); } @Override diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestPutRoleAction.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestPutRoleAction.java index c52e03d4908..8d7f3672837 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestPutRoleAction.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/role/RestPutRoleAction.java @@ -21,6 +21,9 @@ import org.elasticsearch.xpack.security.action.role.PutRoleRequestBuilder; import org.elasticsearch.xpack.security.action.role.PutRoleResponse; import org.elasticsearch.xpack.security.client.SecurityClient; +import static org.elasticsearch.rest.RestRequest.Method.POST; +import static org.elasticsearch.rest.RestRequest.Method.PUT; + /** * Rest endpoint to add a Role to the security index */ @@ -29,8 +32,18 @@ public class RestPutRoleAction extends BaseRestHandler { @Inject public RestPutRoleAction(Settings settings, RestController controller) { super(settings); - controller.registerHandler(RestRequest.Method.POST, "/_xpack/security/role/{name}", this); - controller.registerHandler(RestRequest.Method.PUT, "/_xpack/security/role/{name}", this); + controller.registerHandler(POST, "/_xpack/security/role/{name}", this); + controller.registerHandler(PUT, "/_xpack/security/role/{name}", this); + + // @deprecated: Remove in 6.0 + controller.registerAsDeprecatedHandler(POST, "/_shield/role/{name}", this, + "[POST /_shield/role/{name}] is deprecated! Use " + + "[POST /_xpack/security/role/{name}] instead.", + deprecationLogger); + controller.registerAsDeprecatedHandler(PUT, "/_shield/role/{name}", this, + "[PUT /_shield/role/{name}] is deprecated! Use " + + "[PUT /_xpack/security/role/{name}] instead.", + deprecationLogger); } @Override diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestChangePasswordAction.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestChangePasswordAction.java index aaa67ff3521..a564a85d3bd 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestChangePasswordAction.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestChangePasswordAction.java @@ -22,6 +22,9 @@ import org.elasticsearch.xpack.security.user.User; import org.elasticsearch.xpack.security.action.user.ChangePasswordResponse; import org.elasticsearch.xpack.security.client.SecurityClient; +import static org.elasticsearch.rest.RestRequest.Method.POST; +import static org.elasticsearch.rest.RestRequest.Method.PUT; + /** */ public class RestChangePasswordAction extends BaseRestHandler { @@ -32,10 +35,10 @@ public class RestChangePasswordAction extends BaseRestHandler { public RestChangePasswordAction(Settings settings, RestController controller, SecurityContext securityContext) { super(settings); this.securityContext = securityContext; - controller.registerHandler(RestRequest.Method.POST, "/_xpack/security/user/{username}/_password", this); - controller.registerHandler(RestRequest.Method.PUT, "/_xpack/security/user/{username}/_password", this); - controller.registerHandler(RestRequest.Method.POST, "/_xpack/security/user/_password", this); - controller.registerHandler(RestRequest.Method.PUT, "/_xpack/security/user/_password", this); + controller.registerHandler(POST, "/_xpack/security/user/{username}/_password", this); + controller.registerHandler(PUT, "/_xpack/security/user/{username}/_password", this); + controller.registerHandler(POST, "/_xpack/security/user/_password", this); + controller.registerHandler(PUT, "/_xpack/security/user/_password", this); } @Override diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestDeleteUserAction.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestDeleteUserAction.java index 9f56479ca2c..197a70cffaa 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestDeleteUserAction.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestDeleteUserAction.java @@ -17,11 +17,12 @@ import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestResponse; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.action.support.RestBuilderListener; -import org.elasticsearch.xpack.security.action.user.DeleteUserRequest; import org.elasticsearch.xpack.security.action.user.DeleteUserRequestBuilder; import org.elasticsearch.xpack.security.action.user.DeleteUserResponse; import org.elasticsearch.xpack.security.client.SecurityClient; +import static org.elasticsearch.rest.RestRequest.Method.DELETE; + /** * Rest action to delete a user from the security index */ @@ -30,7 +31,13 @@ public class RestDeleteUserAction extends BaseRestHandler { @Inject public RestDeleteUserAction(Settings settings, RestController controller) { super(settings); - controller.registerHandler(RestRequest.Method.DELETE, "/_xpack/security/user/{username}", this); + controller.registerHandler(DELETE, "/_xpack/security/user/{username}", this); + + // @deprecated: Remove in 6.0 + controller.registerAsDeprecatedHandler(DELETE, "/_shield/user/{username}", this, + "[DELETE /_shield/user/{username}] is deprecated! Use " + + "[DELETE /_xpack/security/user/{username}] instead.", + deprecationLogger); } @Override diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestGetUsersAction.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestGetUsersAction.java index 61a78692485..12576fc0258 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestGetUsersAction.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestGetUsersAction.java @@ -9,7 +9,6 @@ import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.BytesRestResponse; @@ -23,6 +22,8 @@ import org.elasticsearch.xpack.security.user.User; import org.elasticsearch.xpack.security.action.user.GetUsersResponse; import org.elasticsearch.xpack.security.client.SecurityClient; +import static org.elasticsearch.rest.RestRequest.Method.GET; + /** * Rest action to retrieve a user from the security index */ @@ -31,8 +32,18 @@ public class RestGetUsersAction extends BaseRestHandler { @Inject public RestGetUsersAction(Settings settings, RestController controller) { super(settings); - controller.registerHandler(RestRequest.Method.GET, "/_xpack/security/user/", this); - controller.registerHandler(RestRequest.Method.GET, "/_xpack/security/user/{username}", this); + controller.registerHandler(GET, "/_xpack/security/user/", this); + controller.registerHandler(GET, "/_xpack/security/user/{username}", this); + + // @deprecated: Remove in 6.0 + controller.registerAsDeprecatedHandler(GET, "/_shield/user", this, + "[GET /_shield/user] is deprecated! Use " + + "[GET /_xpack/security/user] instead.", + deprecationLogger); + controller.registerAsDeprecatedHandler(GET, "/_shield/user/{username}", this, + "[GET /_shield/user/{username}] is deprecated! Use " + + "[GET /_xpack/security/user/{username}] instead.", + deprecationLogger); } @Override diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestPutUserAction.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestPutUserAction.java index 1660017dd73..4b9c7e8c9f9 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestPutUserAction.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/user/RestPutUserAction.java @@ -21,6 +21,9 @@ import org.elasticsearch.xpack.security.action.user.PutUserRequestBuilder; import org.elasticsearch.xpack.security.action.user.PutUserResponse; import org.elasticsearch.xpack.security.client.SecurityClient; +import static org.elasticsearch.rest.RestRequest.Method.POST; +import static org.elasticsearch.rest.RestRequest.Method.PUT; + /** * Rest endpoint to add a User to the security index */ @@ -29,8 +32,18 @@ public class RestPutUserAction extends BaseRestHandler { @Inject public RestPutUserAction(Settings settings, RestController controller) { super(settings); - controller.registerHandler(RestRequest.Method.POST, "/_xpack/security/user/{username}", this); - controller.registerHandler(RestRequest.Method.PUT, "/_xpack/security/user/{username}", this); + controller.registerHandler(POST, "/_xpack/security/user/{username}", this); + controller.registerHandler(PUT, "/_xpack/security/user/{username}", this); + + // @deprecated: Remove in 6.0 + controller.registerAsDeprecatedHandler(POST, "/_shield/user/{username}", this, + "[POST /_shield/user/{username}] is deprecated! Use " + + "[POST /_xpack/security/user/{username}] instead.", + deprecationLogger); + controller.registerAsDeprecatedHandler(PUT, "/_shield/user/{username}", this, + "[PUT /_shield/user/{username}] is deprecated! Use " + + "[PUT /_xpack/security/user/{username}] instead.", + deprecationLogger); } @Override diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/transport/netty/SecurityNettyTransport.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/transport/netty/SecurityNettyTransport.java index 9a4adc8e320..b11bab1acf9 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/transport/netty/SecurityNettyTransport.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/transport/netty/SecurityNettyTransport.java @@ -31,6 +31,7 @@ import org.jboss.netty.handler.ssl.SslHandler; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; +import java.io.IOException; import java.net.InetSocketAddress; import java.util.List; @@ -111,7 +112,7 @@ public class SecurityNettyTransport extends NettyTransport { } @Override - protected void onException(Channel channel, Exception e) { + protected void onException(Channel channel, Exception e) throws IOException { if (isNotSslRecordException(e)) { if (logger.isTraceEnabled()) { logger.trace("received plaintext traffic on a encrypted channel, closing connection {}", e, channel); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/BulkUpdateTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/BulkUpdateTests.java index cda5beff25f..2c8f083af27 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/BulkUpdateTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/BulkUpdateTests.java @@ -84,7 +84,7 @@ public class BulkUpdateTests extends SecurityIntegTestCase { assertThat(response.getStatusLine().getStatusCode(), equalTo(201)); } - try (Response response = getRestClient().performRequest("GET", path, Collections.emptyMap(), null, basicAuthHeader)) { + try (Response response = getRestClient().performRequest("GET", path, basicAuthHeader)) { assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); assertThat(EntityUtils.toString(response.getEntity()), containsString("\"test\":\"test\"")); } @@ -100,7 +100,7 @@ public class BulkUpdateTests extends SecurityIntegTestCase { assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); } - try (Response response = getRestClient().performRequest("GET", path, Collections.emptyMap(), null, basicAuthHeader)) { + try (Response response = getRestClient().performRequest("GET", path, basicAuthHeader)) { assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); String responseBody = EntityUtils.toString(response.getEntity()); assertThat(responseBody, containsString("\"test\":\"test\"")); @@ -118,7 +118,7 @@ public class BulkUpdateTests extends SecurityIntegTestCase { assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); } - try (Response response = getRestClient().performRequest("GET", path, Collections.emptyMap(), null, basicAuthHeader)) { + try (Response response = getRestClient().performRequest("GET", path, basicAuthHeader)) { String responseBody = EntityUtils.toString(response.getEntity()); assertThat(responseBody, containsString("\"test\":\"test\"")); assertThat(responseBody, containsString("\"not test\":\"not test\"")); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ClearRealmsCacheTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ClearRealmsCacheTests.java index 7bb5fa93f25..6909db4b6a5 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ClearRealmsCacheTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ClearRealmsCacheTests.java @@ -162,7 +162,7 @@ public class ClearRealmsCacheTests extends SecurityIntegTestCase { } static void executeHttpRequest(String path, Map params) throws Exception { - try (Response response = getRestClient().performRequest("POST", path, params, null, + try (Response response = getRestClient().performRequest("POST", path, params, new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME, new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) { diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ClearRolesCacheTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ClearRolesCacheTests.java index a9eca134b68..022ca6d4d07 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ClearRolesCacheTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ClearRolesCacheTests.java @@ -15,6 +15,8 @@ import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.test.NativeRealmIntegTestCase; +import org.elasticsearch.test.SecuritySettingsSource; import org.elasticsearch.xpack.security.SecurityTemplateService; import org.elasticsearch.xpack.security.action.role.GetRolesResponse; import org.elasticsearch.xpack.security.action.role.PutRoleResponse; @@ -23,13 +25,10 @@ import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken; import org.elasticsearch.xpack.security.authz.RoleDescriptor; import org.elasticsearch.xpack.security.authz.store.NativeRolesStore; import org.elasticsearch.xpack.security.client.SecurityClient; -import org.elasticsearch.test.NativeRealmIntegTestCase; -import org.elasticsearch.test.SecuritySettingsSource; import org.junit.Before; import org.junit.BeforeClass; import java.util.Arrays; -import java.util.Collections; import java.util.List; import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; @@ -138,7 +137,7 @@ public class ClearRolesCacheTests extends NativeRealmIntegTestCase { } else { path = "/_xpack/security/role/" + Strings.arrayToCommaDelimitedString(rolesToClear) + "/_clear_cache"; } - try (Response response = getRestClient().performRequest("POST", path, Collections.emptyMap(), null, + try (Response response = getRestClient().performRequest("POST", path, new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME, new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) { diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/IndexPrivilegeTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/IndexPrivilegeTests.java index 1bcd3b5042d..aed1ca642fb 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/IndexPrivilegeTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/IndexPrivilegeTests.java @@ -305,7 +305,7 @@ public class IndexPrivilegeTests extends AbstractPrivilegeTestCase { public void testThatUnknownUserIsRejectedProperly() throws Exception { try { - getRestClient().performRequest("GET", "/", Collections.emptyMap(), null, + getRestClient().performRequest("GET", "/", new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue("idonotexist", new SecuredString("passwd".toCharArray())))); fail("request should have failed"); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java index f50b86277ca..f9f33bde1ca 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java @@ -42,6 +42,7 @@ import org.elasticsearch.xpack.security.Security; import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken; import org.junit.After; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -168,7 +169,7 @@ public class LicensingTests extends SecurityIntegTestCase { } public void testRestAuthenticationByLicenseType() throws Exception { - try (Response response = getRestClient().performRequest("GET", "/", Collections.emptyMap(), null)) { + try (Response response = getRestClient().performRequest("GET", "/")) { // the default of the licensing tests is basic assertThat(response.getStatusLine().getStatusCode(), is(200)); } @@ -177,7 +178,7 @@ public class LicensingTests extends SecurityIntegTestCase { OperationMode mode = randomFrom(OperationMode.GOLD, OperationMode.TRIAL, OperationMode.PLATINUM, OperationMode.STANDARD); enableLicensing(mode); try { - getRestClient().performRequest("GET", "/", Collections.emptyMap(), null); + getRestClient().performRequest("GET", "/"); fail("request should have failed"); } catch(ResponseException e) { assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401)); @@ -265,7 +266,7 @@ public class LicensingTests extends SecurityIntegTestCase { public static class InternalXPackPlugin extends XPackPlugin { - public InternalXPackPlugin(Settings settings) { + public InternalXPackPlugin(Settings settings) throws IOException { super(settings); licensing = new InternalLicensing(); } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ScrollIdSigningTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ScrollIdSigningTests.java index bf76bfe7ac0..ab0609141af 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ScrollIdSigningTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ScrollIdSigningTests.java @@ -11,7 +11,7 @@ import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.xpack.security.crypto.CryptoService; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.test.SecurityIntegTestCase; import java.util.Locale; @@ -106,8 +106,8 @@ public class ScrollIdSigningTests extends SecurityIntegTestCase { } private void assertSigned(String scrollId) { - CryptoService cryptoService = internalCluster().getDataNodeInstance(InternalCryptoService.class); + CryptoService cryptoService = internalCluster().getDataNodeInstance(CryptoService.class); String message = String.format(Locale.ROOT, "Expected scrollId [%s] to be signed, but was not", scrollId); - assertThat(message, cryptoService.signed(scrollId), is(true)); + assertThat(message, cryptoService.isSigned(scrollId), is(true)); } } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java index ca6720069c0..5548437e633 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java @@ -16,6 +16,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.transport.MockTcpTransportPlugin; import org.elasticsearch.xpack.security.InternalClient; import org.elasticsearch.xpack.security.Security; import org.elasticsearch.xpack.security.authc.support.SecuredString; @@ -177,11 +178,8 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase { } @Override - protected Collection> getMockPlugins() { - Set> plugins = new HashSet<>(super.getMockPlugins()); - plugins.remove(MockTransportService.TestPlugin.class); // security has its own transport service - plugins.remove(AssertingLocalTransport.TestPlugin.class); // security has its own transport - return plugins; + protected boolean addMockTransportService() { + return false; // security has its own transport service } @Override @@ -355,10 +353,6 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase { return internalCluster().getInstance(InternalClient.class); } - protected InternalClient internalClient(String node) { - return internalCluster().getInstance(InternalClient.class, node); - } - protected SecurityClient securityClient() { return securityClient(client()); } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java index 0a026b3b6f8..6c0396fa494 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java @@ -21,7 +21,7 @@ import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail; import org.elasticsearch.xpack.security.authc.support.Hasher; import org.elasticsearch.xpack.security.authc.support.SecuredString; import org.elasticsearch.xpack.security.authz.store.FileRolesStore; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.security.test.SecurityTestUtils; import org.elasticsearch.xpack.security.transport.netty.SecurityNettyHttpServerTransport; import org.elasticsearch.xpack.security.transport.netty.SecurityNettyTransport; @@ -131,7 +131,7 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas .put(LoggingAuditTrail.HOST_ADDRESS_SETTING.getKey(), randomBoolean()) .put(LoggingAuditTrail.HOST_NAME_SETTING.getKey(), randomBoolean()) .put(LoggingAuditTrail.NODE_NAME_SETTING.getKey(), randomBoolean()) - .put(InternalCryptoService.FILE_SETTING.getKey(), writeFile(folder, "system_key", systemKey)) + .put(CryptoService.FILE_SETTING.getKey(), writeFile(folder, "system_key", systemKey)) .put("xpack.security.authc.realms.file.type", FileRealm.TYPE) .put("xpack.security.authc.realms.file.order", 0) .put("xpack.security.authc.realms.file.files.users", writeFile(folder, "users", configUsers())) @@ -206,7 +206,7 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas private static byte[] generateKey() { try { - return InternalCryptoService.generateKey(); + return CryptoService.generateKey(); } catch (Exception e) { throw new ElasticsearchException("exception while generating the system key", e); } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SettingsFilterTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SettingsFilterTests.java index 9a1b790556e..b38dbc16371 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SettingsFilterTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SettingsFilterTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.xpack.security.ssl.SSLConfiguration; import org.elasticsearch.xpack.XPackPlugin; import org.hamcrest.Matcher; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -28,7 +29,7 @@ public class SettingsFilterTests extends ESTestCase { private Settings.Builder configuredSettingsBuilder = Settings.builder(); private Map settingsMatcherMap = new HashMap<>(); - public void testFiltering() { + public void testFiltering() throws IOException { configureUnfilteredSetting("xpack.security.authc.realms.file.type", "file"); // ldap realm filtering diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java index ca5e8a5b5b1..ae4689fbefb 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java @@ -93,6 +93,18 @@ public class SecurityFeatureSetTests extends ESTestCase { assertThat(featureSet.enabled(), is(true)); } + public void testSystemKeyUsageEnabledByCryptoService() { + final boolean enabled = randomBoolean(); + + when(cryptoService.isEncryptionEnabled()).thenReturn(enabled); + + assertThat(SecurityFeatureSet.systemKeyUsage(cryptoService), is(enabled)); + } + + public void testSystemKeyUsageNotEnabledIfNull() { + assertThat(SecurityFeatureSet.systemKeyUsage(null), is(false)); + } + public void testUsage() throws Exception { boolean authcAuthzAvailable = randomBoolean(); @@ -131,7 +143,7 @@ public class SecurityFeatureSetTests extends ESTestCase { when(rolesStore.usageStats()).thenReturn(Collections.emptyMap()); } final boolean useSystemKey = randomBoolean(); - when(cryptoService.encryptionEnabled()).thenReturn(useSystemKey); + when(cryptoService.isEncryptionEnabled()).thenReturn(useSystemKey); List realmsList= new ArrayList<>(); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityPluginTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityPluginTests.java index 9c800857517..2f400a12447 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityPluginTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityPluginTests.java @@ -9,13 +9,12 @@ import org.apache.http.message.BasicHeader; import org.elasticsearch.client.Response; import org.elasticsearch.client.ResponseException; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.xpack.security.authc.support.SecuredString; -import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken; import org.elasticsearch.test.SecurityIntegTestCase; import org.elasticsearch.test.SecuritySettingsSource; +import org.elasticsearch.xpack.security.authc.support.SecuredString; +import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken; import java.io.IOException; -import java.util.Collections; import static org.elasticsearch.rest.RestStatus.OK; import static org.elasticsearch.rest.RestStatus.UNAUTHORIZED; @@ -35,14 +34,14 @@ public class SecurityPluginTests extends SecurityIntegTestCase { public void testThatPluginIsLoaded() throws IOException { try { logger.info("executing unauthorized request to /_xpack info"); - getRestClient().performRequest("GET", "/_xpack", Collections.emptyMap(), null); + getRestClient().performRequest("GET", "/_xpack"); fail("request should have failed"); } catch(ResponseException e) { assertThat(e.getResponse().getStatusLine().getStatusCode(), is(UNAUTHORIZED.getStatus())); } logger.info("executing authorized request to /_xpack infos"); - try (Response response = getRestClient().performRequest("GET", "/_xpack", Collections.emptyMap(), null, + try (Response response = getRestClient().performRequest("GET", "/_xpack", new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME, new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) { diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecuritySettingsTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecuritySettingsTests.java index 64eda1a9f47..daffe7a159e 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecuritySettingsTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecuritySettingsTests.java @@ -5,8 +5,11 @@ */ package org.elasticsearch.xpack.security; +import java.io.IOException; + import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; import org.elasticsearch.xpack.security.audit.AuditTrailModule; import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail; import org.elasticsearch.test.ESTestCase; @@ -25,13 +28,11 @@ public class SecuritySettingsTests extends ESTestCase { private static final String TRIBE_T1_SECURITY_ENABLED = "tribe.t1." + Security.enabledSetting(); private static final String TRIBE_T2_SECURITY_ENABLED = "tribe.t2." + Security.enabledSetting(); - public void testSecurityIsMandatoryOnTribes() { + public void testSecurityIsMandatoryOnTribes() throws IOException { Settings settings = Settings.builder().put("tribe.t1.cluster.name", "non_existing") .put("tribe.t2.cluster.name", "non_existing").build(); - Security security = new Security(settings); - - Settings additionalSettings = security.additionalSettings(); + Settings additionalSettings = Security.additionalSettings(settings); assertThat(additionalSettings.getAsArray("tribe.t1.plugin.mandatory", null), arrayContaining(XPackPlugin.NAME)); @@ -42,11 +43,9 @@ public class SecuritySettingsTests extends ESTestCase { Settings settings = Settings.builder().put("tribe.t1.cluster.name", "non_existing") .putArray("tribe.t1.plugin.mandatory", "test_plugin").build(); - Security security = new Security(settings); - //simulate what PluginsService#updatedSettings does to make sure we don't override existing mandatory plugins try { - Settings.builder().put(settings).put(security.additionalSettings()).build(); + Settings.builder().put(settings).put(Security.additionalSettings(settings)).build(); fail("security cannot change the value of a setting that is already defined, so a exception should be thrown"); } catch (IllegalStateException e) { assertThat(e.getMessage(), containsString(XPackPlugin.NAME)); @@ -58,10 +57,8 @@ public class SecuritySettingsTests extends ESTestCase { Settings settings = Settings.builder().put("tribe.t1.cluster.name", "non_existing") .putArray("tribe.t1.plugin.mandatory", "test_plugin", XPackPlugin.NAME).build(); - Security security = new Security(settings); - //simulate what PluginsService#updatedSettings does to make sure we don't override existing mandatory plugins - Settings finalSettings = Settings.builder().put(settings).put(security.additionalSettings()).build(); + Settings finalSettings = Settings.builder().put(settings).put(Security.additionalSettings(settings)).build(); String[] finalMandatoryPlugins = finalSettings.getAsArray("tribe.t1.plugin.mandatory", null); assertThat(finalMandatoryPlugins, notNullValue()); @@ -74,9 +71,7 @@ public class SecuritySettingsTests extends ESTestCase { Settings settings = Settings.builder().put("tribe.t1.cluster.name", "non_existing") .put("tribe.t2.cluster.name", "non_existing").build(); - Security security = new Security(settings); - - Settings additionalSettings = security.additionalSettings(); + Settings additionalSettings = Security.additionalSettings(settings); assertThat(additionalSettings.getAsBoolean(TRIBE_T1_SECURITY_ENABLED, null), equalTo(true)); assertThat(additionalSettings.getAsBoolean(TRIBE_T2_SECURITY_ENABLED, null), equalTo(true)); @@ -87,10 +82,8 @@ public class SecuritySettingsTests extends ESTestCase { .put(TRIBE_T1_SECURITY_ENABLED, false) .put("tribe.t2.cluster.name", "non_existing").build(); - Security security = new Security(settings); - try { - security.additionalSettings(); + Security.additionalSettings(settings); fail("security cannot change the value of a setting that is already defined, so a exception should be thrown"); } catch (IllegalStateException e) { assertThat(e.getMessage(), containsString(TRIBE_T1_SECURITY_ENABLED)); @@ -103,10 +96,8 @@ public class SecuritySettingsTests extends ESTestCase { .put("tribe.t2.cluster.name", "non_existing") .putArray("tribe.t1.plugin.mandatory", "test_plugin", XPackPlugin.NAME).build(); - Security security = new Security(settings); - try { - security.additionalSettings(); + Security.additionalSettings(settings); fail("security cannot change the value of a setting that is already defined, so a exception should be thrown"); } catch (IllegalStateException e) { assertThat(e.getMessage(), containsString(TRIBE_T1_SECURITY_ENABLED)); @@ -122,8 +113,7 @@ public class SecuritySettingsTests extends ESTestCase { .putArray("xpack.security.something.else.here", new String[] { "foo", "bar" }) .build(); - Security security = new Security(settings); - Settings additionalSettings = security.additionalSettings(); + Settings additionalSettings = Security.additionalSettings(settings); assertThat(additionalSettings.get("xpack.security.foo"), nullValue()); assertThat(additionalSettings.get("xpack.security.bar"), nullValue()); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilterTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilterTests.java index 730b473a424..8794175d772 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilterTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilterTests.java @@ -105,7 +105,7 @@ public class SecurityActionFilterTests extends ESTestCase { Task task = mock(Task.class); Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null); when(authcService.authenticate("_action", request, SystemUser.INSTANCE)).thenReturn(authentication); - when(cryptoService.signed("signed_scroll_id")).thenReturn(true); + when(cryptoService.isSigned("signed_scroll_id")).thenReturn(true); when(cryptoService.unsignAndVerify("signed_scroll_id")).thenReturn("scroll_id"); filter.apply(task, "_action", request, listener, chain); assertThat(request.scrollId(), equalTo("scroll_id")); @@ -122,7 +122,7 @@ public class SecurityActionFilterTests extends ESTestCase { Task task = mock(Task.class); Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null); when(authcService.authenticate("_action", request, SystemUser.INSTANCE)).thenReturn(authentication); - when(cryptoService.signed("scroll_id")).thenReturn(true); + when(cryptoService.isSigned("scroll_id")).thenReturn(true); doThrow(sigException).when(cryptoService).unsignAndVerify("scroll_id"); filter.apply(task, "_action", request, listener, chain); verify(listener).onFailure(isA(ElasticsearchSecurityException.class)); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/user/TransportGetUsersActionTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/user/TransportGetUsersActionTests.java index c5573602968..603e54e7dc3 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/user/TransportGetUsersActionTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/user/TransportGetUsersActionTests.java @@ -91,10 +91,11 @@ public class TransportGetUsersActionTests extends ESTestCase { assertThat(throwableRef.get(), is(nullValue())); assertThat(responseRef.get(), is(notNullValue())); + final User[] users = responseRef.get().users(); if (anonymousEnabled) { - assertThat(responseRef.get().users(), arrayContaining(AnonymousUser.INSTANCE)); + assertThat("expected array with anonymous but got: " + Arrays.toString(users), users, arrayContaining(AnonymousUser.INSTANCE)); } else { - assertThat(responseRef.get().users(), emptyArray()); + assertThat("expected an empty array but got: " + Arrays.toString(users), users, emptyArray()); } verifyZeroInteractions(usersStore); } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java index 4dadbd0cdf8..2144bc8ba06 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java @@ -39,7 +39,7 @@ import org.elasticsearch.transport.TransportRequest; import org.elasticsearch.xpack.security.Security; import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail.Message; import org.elasticsearch.xpack.security.authc.AuthenticationToken; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.security.transport.filter.IPFilter; import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule; import org.elasticsearch.xpack.security.transport.netty.SecurityNettyTransport; @@ -99,7 +99,7 @@ public class IndexAuditTrailTests extends SecurityIntegTestCase { @BeforeClass public static void configureBeforeClass() { remoteIndexing = randomBoolean(); - systemKey = InternalCryptoService.generateKey(); + systemKey = CryptoService.generateKey(); if (remoteIndexing == false) { remoteSettings = Settings.EMPTY; } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/RunAsIntegTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/RunAsIntegTests.java index 21cdc518047..f14992f5751 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/RunAsIntegTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/RunAsIntegTests.java @@ -118,7 +118,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase { public void testUserImpersonationUsingHttp() throws Exception { // use the transport client user and try to run as try { - getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null, + getRestClient().performRequest("GET", "/_nodes", new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(TRANSPORT_CLIENT_USER, SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD))), @@ -130,7 +130,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase { try { //the run as user shouldn't have access to the nodes api - getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null, + getRestClient().performRequest("GET", "/_nodes", new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER, SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD)))); @@ -140,7 +140,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase { } // but when running as a different user it should work - try (Response response = getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null, + try (Response response = getRestClient().performRequest("GET", "/_nodes", new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER, SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD))), @@ -173,7 +173,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase { public void testEmptyHeaderUsingHttp() throws Exception { try { - getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null, + getRestClient().performRequest("GET", "/_nodes", new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER, SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD))), @@ -208,7 +208,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase { public void testNonExistentRunAsUserUsingHttp() throws Exception { try { - getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null, + getRestClient().performRequest("GET", "/_nodes", new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER, SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD))), diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthenticationTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthenticationTests.java index c00b088eb36..16e8b4411fb 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthenticationTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthenticationTests.java @@ -99,7 +99,7 @@ public class PkiAuthenticationTests extends SecurityIntegTestCase { public void testRestAuthenticationViaPki() throws Exception { SSLContext context = getRestSSLContext("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks", "testnode"); - try (CloseableHttpClient client = HttpClients.custom().setSslcontext(context).build()) { + try (CloseableHttpClient client = HttpClients.custom().setSSLContext(context).build()) { HttpPut put = new HttpPut(getNodeUrl() + "foo"); try (CloseableHttpResponse response = client.execute(put)) { String body = EntityUtils.toString(response.getEntity()); @@ -110,7 +110,7 @@ public class PkiAuthenticationTests extends SecurityIntegTestCase { public void testRestAuthenticationFailure() throws Exception { SSLContext context = getRestSSLContext("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks", "testclient"); - try (CloseableHttpClient client = HttpClients.custom().setSslcontext(context).build()) { + try (CloseableHttpClient client = HttpClients.custom().setSSLContext(context).build()) { HttpPut put = new HttpPut(getNodeUrl() + "foo"); try (CloseableHttpResponse response = client.execute(put)) { assertThat(response.getStatusLine().getStatusCode(), is(401)); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiOptionalClientAuthTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiOptionalClientAuthTests.java index ad3d3d22964..4afab4edb5a 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiOptionalClientAuthTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiOptionalClientAuthTests.java @@ -35,7 +35,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.security.KeyStore; import java.security.SecureRandom; -import java.util.Collections; import static org.elasticsearch.test.SecuritySettingsSource.DEFAULT_PASSWORD; import static org.elasticsearch.test.SecuritySettingsSource.DEFAULT_USER_NAME; @@ -83,13 +82,13 @@ public class PkiOptionalClientAuthTests extends SecurityIntegTestCase { CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(getSSLContext()).build(); try (RestClient restClient = createRestClient(httpClient, "https")) { try { - restClient.performRequest("GET", "_nodes", Collections.emptyMap(), null); + restClient.performRequest("GET", "_nodes"); fail("request should have failed"); } catch(ResponseException e) { assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401)); } - try (Response response = restClient.performRequest("GET", "_nodes", Collections.emptyMap(), null, + try (Response response = restClient.performRequest("GET", "_nodes", new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME, new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) { diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiWithoutClientAuthenticationTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiWithoutClientAuthenticationTests.java index 6be44408b81..9e20b64d711 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiWithoutClientAuthenticationTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiWithoutClientAuthenticationTests.java @@ -28,7 +28,6 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.security.SecureRandom; import java.security.cert.X509Certificate; -import java.util.Collections; import java.util.Locale; import static org.hamcrest.Matchers.is; @@ -81,7 +80,7 @@ public class PkiWithoutClientAuthenticationTests extends SecurityIntegTestCase { sc.init(null, trustAllCerts, new SecureRandom()); CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(sc).build(); try (RestClient restClient = createRestClient(httpClient, "https")) { - try (Response response = restClient.performRequest("GET", "/_nodes", Collections.emptyMap(), null, + try (Response response = restClient.performRequest("GET", "/_nodes", new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME, new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) { diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiWithoutSSLTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiWithoutSSLTests.java index 6cea5fa9b08..b1c691ecc35 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiWithoutSSLTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiWithoutSSLTests.java @@ -16,8 +16,6 @@ import org.elasticsearch.test.SecuritySettingsSource; import org.elasticsearch.xpack.security.authc.support.SecuredString; import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken; -import java.util.Collections; - import static org.hamcrest.Matchers.is; @ClusterScope(numClientNodes = 0, supportsDedicatedMasters = false, numDataNodes = 1) @@ -43,7 +41,7 @@ public class PkiWithoutSSLTests extends SecurityIntegTestCase { } public void testThatHttpWorks() throws Exception { - try (Response response = getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null, + try (Response response = getRestClient().performRequest("GET", "/_nodes", new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME, new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) { diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldDataCacheWithFieldSubsetReaderTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldDataCacheWithFieldSubsetReaderTests.java index 7ed7130950d..8bea84a0f63 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldDataCacheWithFieldSubsetReaderTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldDataCacheWithFieldSubsetReaderTests.java @@ -97,7 +97,7 @@ public class FieldDataCacheWithFieldSubsetReaderTests extends ESTestCase { assertThat(atomic.getOrdinalsValues().getValueCount(), equalTo(numDocs)); assertThat(indexFieldDataCache.topLevelBuilds, equalTo(1)); - DirectoryReader ir = FieldSubsetReader.wrap(this.ir, Collections.emptySet()); + DirectoryReader ir = FieldSubsetReader.wrap(this.ir, Collections.emptySet(), false); global = sortedSetDVOrdinalsIndexFieldData.loadGlobal(ir); atomic = global.load(ir.leaves().get(0)); assertThat(atomic.getOrdinalsValues().getValueCount(), equalTo(0L)); @@ -110,7 +110,7 @@ public class FieldDataCacheWithFieldSubsetReaderTests extends ESTestCase { assertThat(atomic.getOrdinalsValues().getValueCount(), greaterThanOrEqualTo(1L)); } - DirectoryReader ir = FieldSubsetReader.wrap(this.ir, Collections.emptySet()); + DirectoryReader ir = FieldSubsetReader.wrap(this.ir, Collections.emptySet(), false); for (LeafReaderContext context : ir.leaves()) { AtomicOrdinalsFieldData atomic = sortedSetDVOrdinalsIndexFieldData.load(context); assertThat(atomic.getOrdinalsValues().getValueCount(), equalTo(0L)); @@ -126,7 +126,7 @@ public class FieldDataCacheWithFieldSubsetReaderTests extends ESTestCase { assertThat(atomic.getOrdinalsValues().getValueCount(), equalTo(numDocs)); assertThat(indexFieldDataCache.topLevelBuilds, equalTo(1)); - DirectoryReader ir = FieldSubsetReader.wrap(this.ir, Collections.emptySet()); + DirectoryReader ir = FieldSubsetReader.wrap(this.ir, Collections.emptySet(), false); global = pagedBytesIndexFieldData.loadGlobal(ir); atomic = global.load(ir.leaves().get(0)); assertThat(atomic.getOrdinalsValues().getValueCount(), equalTo(0L)); @@ -141,7 +141,7 @@ public class FieldDataCacheWithFieldSubsetReaderTests extends ESTestCase { } assertThat(indexFieldDataCache.leafLevelBuilds, equalTo(ir.leaves().size())); - DirectoryReader ir = FieldSubsetReader.wrap(this.ir, Collections.emptySet()); + DirectoryReader ir = FieldSubsetReader.wrap(this.ir, Collections.emptySet(), false); for (LeafReaderContext context : ir.leaves()) { AtomicOrdinalsFieldData atomic = pagedBytesIndexFieldData.load(context); assertThat(atomic.getOrdinalsValues().getValueCount(), equalTo(0L)); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldExtractorTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldExtractorTests.java new file mode 100644 index 00000000000..b0e42a0d121 --- /dev/null +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldExtractorTests.java @@ -0,0 +1,138 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.security.authz.accesscontrol; + +import org.apache.lucene.document.IntPoint; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.AssertingQuery; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.DisjunctionMaxQuery; +import org.apache.lucene.search.DocValuesNumbersQuery; +import org.apache.lucene.search.DocValuesRangeQuery; +import org.apache.lucene.search.FieldValueQuery; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.MatchNoDocsQuery; +import org.apache.lucene.search.MultiPhraseQuery; +import org.apache.lucene.search.PhraseQuery; +import org.apache.lucene.search.SynonymQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.spans.SpanTermQuery; +import org.elasticsearch.test.ESTestCase; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** Simple tests for query field extraction */ +public class FieldExtractorTests extends ESTestCase { + + public void testBoolean() { + Set fields = new HashSet<>(); + BooleanQuery.Builder builder = new BooleanQuery.Builder(); + builder.add(new TermQuery(new Term("foo", "bar")), BooleanClause.Occur.MUST); + builder.add(new TermQuery(new Term("no", "baz")), BooleanClause.Occur.MUST_NOT); + FieldExtractor.extractFields(builder.build(), fields); + assertEquals(asSet("foo", "no"), fields); + } + + public void testDisjunctionMax() { + Set fields = new HashSet<>(); + DisjunctionMaxQuery query = new DisjunctionMaxQuery(Arrays.asList( + new TermQuery(new Term("one", "bar")), + new TermQuery(new Term("two", "baz")) + ), 1.0F); + FieldExtractor.extractFields(query, fields); + assertEquals(asSet("one", "two"), fields); + } + + public void testSpanTerm() { + Set fields = new HashSet<>(); + FieldExtractor.extractFields(new SpanTermQuery(new Term("foo", "bar")), fields); + assertEquals(asSet("foo"), fields); + } + + public void testTerm() { + Set fields = new HashSet<>(); + FieldExtractor.extractFields(new TermQuery(new Term("foo", "bar")), fields); + assertEquals(asSet("foo"), fields); + } + + public void testSynonym() { + Set fields = new HashSet<>(); + SynonymQuery query = new SynonymQuery(new Term("foo", "bar"), new Term("foo", "baz")); + FieldExtractor.extractFields(query, fields); + assertEquals(asSet("foo"), fields); + } + + public void testPhrase() { + Set fields = new HashSet<>(); + PhraseQuery.Builder builder = new PhraseQuery.Builder(); + builder.add(new Term("foo", "bar")); + builder.add(new Term("foo", "baz")); + FieldExtractor.extractFields(builder.build(), fields); + assertEquals(asSet("foo"), fields); + } + + public void testMultiPhrase() { + Set fields = new HashSet<>(); + MultiPhraseQuery.Builder builder = new MultiPhraseQuery.Builder(); + builder.add(new Term("foo", "bar")); + builder.add(new Term[] { new Term("foo", "baz"), new Term("foo", "baz2") }); + FieldExtractor.extractFields(builder.build(), fields); + assertEquals(asSet("foo"), fields); + } + + public void testPointRange() { + Set fields = new HashSet<>(); + FieldExtractor.extractFields(IntPoint.newRangeQuery("foo", 3, 4), fields); + assertEquals(asSet("foo"), fields); + } + + public void testPointSet() { + Set fields = new HashSet<>(); + FieldExtractor.extractFields(IntPoint.newSetQuery("foo", 3, 4, 5), fields); + assertEquals(asSet("foo"), fields); + } + + public void testFieldValue() { + Set fields = new HashSet<>(); + FieldExtractor.extractFields(new FieldValueQuery("foo"), fields); + assertEquals(asSet("foo"), fields); + } + + public void testDocValuesNumbers() { + Set fields = new HashSet<>(); + FieldExtractor.extractFields(new DocValuesNumbersQuery("foo", 5L), fields); + assertEquals(asSet("foo"), fields); + } + + public void testDocValuesRange() { + Set fields = new HashSet<>(); + FieldExtractor.extractFields(DocValuesRangeQuery.newLongRange("foo", 1L, 2L, true, true), fields); + assertEquals(asSet("foo"), fields); + } + + public void testMatchAllDocs() { + Set fields = new HashSet<>(); + FieldExtractor.extractFields(new MatchAllDocsQuery(), fields); + assertEquals(Collections.emptySet(), fields); + } + + public void testMatchNoDocs() { + Set fields = new HashSet<>(); + FieldExtractor.extractFields(new MatchNoDocsQuery(), fields); + assertEquals(Collections.emptySet(), fields); + } + + public void testUnsupported() { + Set fields = new HashSet<>(); + expectThrows(UnsupportedOperationException.class, () -> { + FieldExtractor.extractFields(new AssertingQuery(random(), new MatchAllDocsQuery()), fields); + }); + } +} diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldSubsetReaderTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldSubsetReaderTests.java index 58a015abc26..82e0a93b309 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldSubsetReaderTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/FieldSubsetReaderTests.java @@ -70,7 +70,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -102,7 +102,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -189,7 +189,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field Document d2 = ir.document(0); @@ -216,7 +216,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field Document d2 = ir.document(0); @@ -243,7 +243,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field Document d2 = ir.document(0); @@ -270,7 +270,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field Document d2 = ir.document(0); @@ -297,7 +297,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field Document d2 = ir.document(0); @@ -324,7 +324,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field Document d2 = ir.document(0); @@ -353,7 +353,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field Fields vectors = ir.getTermVectors(0); @@ -383,7 +383,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -410,7 +410,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -442,7 +442,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -474,7 +474,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -506,7 +506,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -542,7 +542,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -577,7 +577,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -610,7 +610,7 @@ public class FieldSubsetReaderTests extends ESTestCase { Set fields = new HashSet<>(); fields.add("fieldA"); fields.add(SourceFieldMapper.NAME); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field Document d2 = ir.document(0); @@ -641,7 +641,7 @@ public class FieldSubsetReaderTests extends ESTestCase { Set fields = new HashSet<>(); fields.add("fieldA"); fields.add(FieldNamesFieldMapper.NAME); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -690,7 +690,7 @@ public class FieldSubsetReaderTests extends ESTestCase { fields.add("fieldA"); fields.add("fieldC"); fields.add(FieldNamesFieldMapper.NAME); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only two fields LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -738,7 +738,7 @@ public class FieldSubsetReaderTests extends ESTestCase { fields.add("fieldA"); fields.add("fieldC"); fields.add(FieldNamesFieldMapper.NAME); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -774,7 +774,7 @@ public class FieldSubsetReaderTests extends ESTestCase { Set fields = new HashSet<>(); fields.add("fieldA"); fields.add(FieldNamesFieldMapper.NAME); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see only one field LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -803,7 +803,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("id"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); assertEquals(2, ir.numDocs()); assertEquals(1, ir.leaves().size()); @@ -838,7 +838,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldB"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // sees no fields assertNull(ir.getTermVectors(0)); @@ -858,7 +858,7 @@ public class FieldSubsetReaderTests extends ESTestCase { // open reader Set fields = Collections.singleton("fieldA"); - DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, false); // see no fields LeafReader segmentReader = ir.leaves().get(0).reader(); @@ -888,9 +888,9 @@ public class FieldSubsetReaderTests extends ESTestCase { iw.close(); DirectoryReader directoryReader = DirectoryReader.open(dir); - directoryReader = FieldSubsetReader.wrap(directoryReader, Collections.emptySet()); + directoryReader = FieldSubsetReader.wrap(directoryReader, Collections.emptySet(), false); try { - FieldSubsetReader.wrap(directoryReader, Collections.emptySet()); + FieldSubsetReader.wrap(directoryReader, Collections.emptySet(), false); fail("shouldn't be able to wrap FieldSubsetDirectoryReader twice"); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), equalTo("Can't wrap [class org.elasticsearch.xpack.security.authz.accesscontrol" + @@ -899,4 +899,36 @@ public class FieldSubsetReaderTests extends ESTestCase { directoryReader.close(); dir.close(); } + + /** + * test filtering two string fields, with negated set + */ + public void testNegative() throws Exception { + Directory dir = newDirectory(); + IndexWriterConfig iwc = new IndexWriterConfig(null); + IndexWriter iw = new IndexWriter(dir, iwc); + + // add document with 2 fields + Document doc = new Document(); + doc.add(new StringField("fieldA", "test", Field.Store.NO)); + doc.add(new StringField("fieldB", "test", Field.Store.NO)); + iw.addDocument(doc); + + // open reader + Set fields = Collections.singleton("fieldB"); + DirectoryReader ir = FieldSubsetReader.wrap(DirectoryReader.open(iw), fields, true); + + // see only one field + LeafReader segmentReader = ir.leaves().get(0).reader(); + Set seenFields = new HashSet<>(); + for (String field : segmentReader.fields()) { + seenFields.add(field); + } + assertEquals(Collections.singleton("fieldA"), seenFields); + assertNotNull(segmentReader.terms("fieldA")); + assertNull(segmentReader.terms("fieldB")); + + TestUtil.checkReader(ir); + IOUtils.close(ir, iw, dir); + } } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/CryptoServiceTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/CryptoServiceTests.java new file mode 100644 index 00000000000..f7151b1ac2d --- /dev/null +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/CryptoServiceTests.java @@ -0,0 +1,187 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.security.crypto; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.test.ESTestCase; +import org.junit.Before; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +public class CryptoServiceTests extends ESTestCase { + private Settings settings; + private Environment env; + private Path keyFile; + + @Before + public void init() throws Exception { + keyFile = createTempDir().resolve("system_key"); + Files.write(keyFile, CryptoService.generateKey()); + settings = Settings.builder() + .put(CryptoService.FILE_SETTING.getKey(), keyFile.toAbsolutePath()) + .put("resource.reload.interval.high", "2s") + .put("path.home", createTempDir()) + .build(); + env = new Environment(settings); + } + + public void testSigned() throws Exception { + // randomize whether to use a system key or not + Settings settings = randomBoolean() ? this.settings : Settings.EMPTY; + CryptoService service = new CryptoService(settings, env); + String text = randomAsciiOfLength(10); + String signed = service.sign(text); + assertThat(service.isSigned(signed), is(true)); + } + + public void testSignAndUnsign() throws Exception { + CryptoService service = new CryptoService(settings, env); + String text = randomAsciiOfLength(10); + String signed = service.sign(text); + assertThat(text.equals(signed), is(false)); + String text2 = service.unsignAndVerify(signed); + assertThat(text, equalTo(text2)); + } + + public void testSignAndUnsignNoKeyFile() throws Exception { + CryptoService service = new CryptoService(Settings.EMPTY, env); + final String text = randomAsciiOfLength(10); + String signed = service.sign(text); + // we always have some sort of key to sign with + assertThat(text, not(equalTo(signed))); + String unsigned = service.unsignAndVerify(signed); + assertThat(unsigned, equalTo(text)); + } + + public void testTamperedSignature() throws Exception { + CryptoService service = new CryptoService(settings, env); + String text = randomAsciiOfLength(10); + String signed = service.sign(text); + int i = signed.indexOf("$$", 2); + int length = Integer.parseInt(signed.substring(2, i)); + String fakeSignature = randomAsciiOfLength(length); + String fakeSignedText = "$$" + length + "$$" + fakeSignature + signed.substring(i + 2 + length); + + try { + service.unsignAndVerify(fakeSignedText); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), is(equalTo("tampered signed text"))); + assertThat(e.getCause(), is(nullValue())); + } + } + + public void testTamperedSignatureOneChar() throws Exception { + CryptoService service = new CryptoService(settings, env); + String text = randomAsciiOfLength(10); + String signed = service.sign(text); + int i = signed.indexOf("$$", 2); + int length = Integer.parseInt(signed.substring(2, i)); + StringBuilder fakeSignature = new StringBuilder(signed.substring(i + 2, i + 2 + length)); + fakeSignature.setCharAt(randomIntBetween(0, fakeSignature.length() - 1), randomAsciiOfLength(1).charAt(0)); + + String fakeSignedText = "$$" + length + "$$" + fakeSignature.toString() + signed.substring(i + 2 + length); + + try { + service.unsignAndVerify(fakeSignedText); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), is(equalTo("tampered signed text"))); + assertThat(e.getCause(), is(nullValue())); + } + } + + public void testTamperedSignatureLength() throws Exception { + CryptoService service = new CryptoService(settings, env); + String text = randomAsciiOfLength(10); + String signed = service.sign(text); + int i = signed.indexOf("$$", 2); + int length = Integer.parseInt(signed.substring(2, i)); + String fakeSignature = randomAsciiOfLength(length); + + // Smaller sig length + String fakeSignedText = "$$" + randomIntBetween(0, length - 1) + "$$" + fakeSignature + signed.substring(i + 2 + length); + + try { + service.unsignAndVerify(fakeSignedText); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), is(equalTo("tampered signed text"))); + } + + // Larger sig length + fakeSignedText = "$$" + randomIntBetween(length + 1, Integer.MAX_VALUE) + "$$" + fakeSignature + signed.substring(i + 2 + length); + try { + service.unsignAndVerify(fakeSignedText); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), is(equalTo("tampered signed text"))); + assertThat(e.getCause(), is(nullValue())); + } + } + + public void testEncryptionAndDecryptionChars() throws Exception { + CryptoService service = new CryptoService(settings, env); + assertThat(service.isEncryptionEnabled(), is(true)); + final char[] chars = randomAsciiOfLengthBetween(0, 1000).toCharArray(); + final char[] encrypted = service.encrypt(chars); + assertThat(encrypted, notNullValue()); + assertThat(Arrays.equals(encrypted, chars), is(false)); + + final char[] decrypted = service.decrypt(encrypted); + assertThat(Arrays.equals(chars, decrypted), is(true)); + } + + public void testEncryptionAndDecryptionCharsWithoutKey() throws Exception { + CryptoService service = new CryptoService(Settings.EMPTY, env); + assertThat(service.isEncryptionEnabled(), is(false)); + final char[] chars = randomAsciiOfLengthBetween(0, 1000).toCharArray(); + final char[] encryptedChars = service.encrypt(chars); + final char[] decryptedChars = service.decrypt(encryptedChars); + assertThat(chars, equalTo(encryptedChars)); + assertThat(chars, equalTo(decryptedChars)); + } + + public void testEncryptionEnabledWithKey() throws Exception { + CryptoService service = new CryptoService(settings, env); + assertThat(service.isEncryptionEnabled(), is(true)); + } + + public void testEncryptionEnabledWithoutKey() throws Exception { + CryptoService service = new CryptoService(Settings.EMPTY, env); + assertThat(service.isEncryptionEnabled(), is(false)); + } + + public void testEncryptedChar() throws Exception { + CryptoService service = new CryptoService(settings, env); + assertThat(service.isEncryptionEnabled(), is(true)); + + assertThat(service.isEncrypted((char[]) null), is(false)); + assertThat(service.isEncrypted(new char[0]), is(false)); + assertThat(service.isEncrypted(new char[CryptoService.ENCRYPTED_TEXT_PREFIX.length()]), is(false)); + assertThat(service.isEncrypted(CryptoService.ENCRYPTED_TEXT_PREFIX.toCharArray()), is(true)); + assertThat(service.isEncrypted(randomAsciiOfLengthBetween(0, 100).toCharArray()), is(false)); + assertThat(service.isEncrypted(service.encrypt(randomAsciiOfLength(10).toCharArray())), is(true)); + } + + public void testSigningKeyCanBeRecomputedConsistently() { + final SecretKey systemKey = new SecretKeySpec(CryptoService.generateKey(), CryptoService.KEY_ALGO); + final SecretKey randomKey = CryptoService.generateSecretKey(CryptoService.RANDOM_KEY_SIZE); + int iterations = randomInt(100); + final SecretKey signingKey = CryptoService.createSigningKey(systemKey, randomKey); + for (int i = 0; i < iterations; i++) { + SecretKey regenerated = CryptoService.createSigningKey(systemKey, randomKey); + assertThat(regenerated, equalTo(signingKey)); + } + } +} diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/InternalCryptoServiceTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/InternalCryptoServiceTests.java deleted file mode 100644 index 8101e202a35..00000000000 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/InternalCryptoServiceTests.java +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.xpack.security.crypto; - -import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.common.io.Streams; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.threadpool.TestThreadPool; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.junit.After; -import org.junit.Before; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Collections; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; - -/** - * - */ -public class InternalCryptoServiceTests extends ESTestCase { - private ResourceWatcherService watcherService; - private Settings settings; - private Environment env; - private Path keyFile; - private ThreadPool threadPool; - - @Before - public void init() throws Exception { - keyFile = createTempDir().resolve("system_key"); - Files.write(keyFile, InternalCryptoService.generateKey()); - settings = Settings.builder() - .put(InternalCryptoService.FILE_SETTING.getKey(), keyFile.toAbsolutePath()) - .put("resource.reload.interval.high", "2s") - .put("path.home", createTempDir()) - .build(); - env = new Environment(settings); - threadPool = new TestThreadPool("test"); - watcherService = new ResourceWatcherService(settings, threadPool); - watcherService.start(); - } - - @After - public void shutdown() throws InterruptedException { - watcherService.stop(); - terminate(threadPool); - } - - public void testSigned() throws Exception { - // randomize whether to use a system key or not - Settings settings = randomBoolean() ? this.settings : Settings.EMPTY; - InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - String text = randomAsciiOfLength(10); - String signed = service.sign(text); - assertThat(service.signed(signed), is(true)); - } - - public void testSignAndUnsign() throws Exception { - InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - String text = randomAsciiOfLength(10); - String signed = service.sign(text); - assertThat(text.equals(signed), is(false)); - String text2 = service.unsignAndVerify(signed); - assertThat(text, equalTo(text2)); - } - - public void testSignAndUnsignNoKeyFile() throws Exception { - InternalCryptoService service = new InternalCryptoService(Settings.EMPTY, env, watcherService); - service.start(); - final String text = randomAsciiOfLength(10); - String signed = service.sign(text); - // we always have some sort of key to sign with - assertThat(text, not(equalTo(signed))); - String unsigned = service.unsignAndVerify(signed); - assertThat(unsigned, equalTo(text)); - } - - public void testTamperedSignature() throws Exception { - InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - String text = randomAsciiOfLength(10); - String signed = service.sign(text); - int i = signed.indexOf("$$", 2); - int length = Integer.parseInt(signed.substring(2, i)); - String fakeSignature = randomAsciiOfLength(length); - String fakeSignedText = "$$" + length + "$$" + fakeSignature + signed.substring(i + 2 + length); - - try { - service.unsignAndVerify(fakeSignedText); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage(), is(equalTo("tampered signed text"))); - assertThat(e.getCause(), is(nullValue())); - } - } - - public void testTamperedSignatureOneChar() throws Exception { - InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - String text = randomAsciiOfLength(10); - String signed = service.sign(text); - int i = signed.indexOf("$$", 2); - int length = Integer.parseInt(signed.substring(2, i)); - StringBuilder fakeSignature = new StringBuilder(signed.substring(i + 2, i + 2 + length)); - fakeSignature.setCharAt(randomIntBetween(0, fakeSignature.length() - 1), randomAsciiOfLength(1).charAt(0)); - - String fakeSignedText = "$$" + length + "$$" + fakeSignature.toString() + signed.substring(i + 2 + length); - - try { - service.unsignAndVerify(fakeSignedText); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage(), is(equalTo("tampered signed text"))); - assertThat(e.getCause(), is(nullValue())); - } - } - - public void testTamperedSignatureLength() throws Exception { - InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - String text = randomAsciiOfLength(10); - String signed = service.sign(text); - int i = signed.indexOf("$$", 2); - int length = Integer.parseInt(signed.substring(2, i)); - String fakeSignature = randomAsciiOfLength(length); - - // Smaller sig length - String fakeSignedText = "$$" + randomIntBetween(0, length - 1) + "$$" + fakeSignature + signed.substring(i + 2 + length); - - try { - service.unsignAndVerify(fakeSignedText); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage(), is(equalTo("tampered signed text"))); - } - - // Larger sig length - fakeSignedText = "$$" + randomIntBetween(length + 1, Integer.MAX_VALUE) + "$$" + fakeSignature + signed.substring(i + 2 + length); - try { - service.unsignAndVerify(fakeSignedText); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage(), is(equalTo("tampered signed text"))); - assertThat(e.getCause(), is(nullValue())); - } - } - - public void testEncryptionAndDecryptionChars() { - InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - assertThat(service.encryptionEnabled(), is(true)); - final char[] chars = randomAsciiOfLengthBetween(0, 1000).toCharArray(); - final char[] encrypted = service.encrypt(chars); - assertThat(encrypted, notNullValue()); - assertThat(Arrays.equals(encrypted, chars), is(false)); - - final char[] decrypted = service.decrypt(encrypted); - assertThat(Arrays.equals(chars, decrypted), is(true)); - } - - public void testEncryptionAndDecryptionBytes() { - InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - assertThat(service.encryptionEnabled(), is(true)); - final byte[] bytes = randomByteArray(); - final byte[] encrypted = service.encrypt(bytes); - assertThat(encrypted, notNullValue()); - assertThat(Arrays.equals(encrypted, bytes), is(false)); - - final byte[] decrypted = service.decrypt(encrypted); - assertThat(Arrays.equals(bytes, decrypted), is(true)); - } - - public void testEncryptionAndDecryptionCharsWithoutKey() { - InternalCryptoService service = new InternalCryptoService(Settings.EMPTY, env, watcherService); - service.start(); - assertThat(service.encryptionEnabled(), is(false)); - final char[] chars = randomAsciiOfLengthBetween(0, 1000).toCharArray(); - final char[] encryptedChars = service.encrypt(chars); - final char[] decryptedChars = service.decrypt(encryptedChars); - assertThat(chars, equalTo(encryptedChars)); - assertThat(chars, equalTo(decryptedChars)); - } - - public void testEncryptionAndDecryptionBytesWithoutKey() { - InternalCryptoService service = new InternalCryptoService(Settings.EMPTY, env, watcherService); - service.start(); - assertThat(service.encryptionEnabled(), is(false)); - final byte[] bytes = randomByteArray(); - final byte[] encryptedBytes = service.encrypt(bytes); - final byte[] decryptedBytes = service.decrypt(bytes); - assertThat(bytes, equalTo(encryptedBytes)); - assertThat(decryptedBytes, equalTo(encryptedBytes)); - } - - public void testEncryptionEnabledWithKey() { - InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - assertThat(service.encryptionEnabled(), is(true)); - } - - public void testEncryptionEnabledWithoutKey() { - InternalCryptoService service = new InternalCryptoService(Settings.EMPTY, env, watcherService); - service.start(); - assertThat(service.encryptionEnabled(), is(false)); - } - - public void testChangingAByte() { - InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - assertThat(service.encryptionEnabled(), is(true)); - // We need at least one byte to test changing a byte, otherwise output is always the same - final byte[] bytes = randomByteArray(1); - final byte[] encrypted = service.encrypt(bytes); - assertThat(encrypted, notNullValue()); - assertThat(Arrays.equals(encrypted, bytes), is(false)); - - int tamperedIndex = randomIntBetween(InternalCryptoService.ENCRYPTED_BYTE_PREFIX.length, encrypted.length - 1); - final byte untamperedByte = encrypted[tamperedIndex]; - byte tamperedByte = randomByte(); - while (tamperedByte == untamperedByte) { - tamperedByte = randomByte(); - } - encrypted[tamperedIndex] = tamperedByte; - final byte[] decrypted = service.decrypt(encrypted); - assertThat(Arrays.equals(bytes, decrypted), is(false)); - } - - public void testEncryptedChar() { - InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - assertThat(service.encryptionEnabled(), is(true)); - - assertThat(service.encrypted((char[]) null), is(false)); - assertThat(service.encrypted(new char[0]), is(false)); - assertThat(service.encrypted(new char[InternalCryptoService.ENCRYPTED_TEXT_PREFIX.length()]), is(false)); - assertThat(service.encrypted(InternalCryptoService.ENCRYPTED_TEXT_PREFIX.toCharArray()), is(true)); - assertThat(service.encrypted(randomAsciiOfLengthBetween(0, 100).toCharArray()), is(false)); - assertThat(service.encrypted(service.encrypt(randomAsciiOfLength(10).toCharArray())), is(true)); - } - - public void testEncryptedByte() { - InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - assertThat(service.encryptionEnabled(), is(true)); - - assertThat(service.encrypted((byte[]) null), is(false)); - assertThat(service.encrypted(new byte[0]), is(false)); - assertThat(service.encrypted(new byte[InternalCryptoService.ENCRYPTED_BYTE_PREFIX.length]), is(false)); - assertThat(service.encrypted(InternalCryptoService.ENCRYPTED_BYTE_PREFIX), is(true)); - assertThat(service.encrypted(randomAsciiOfLengthBetween(0, 100).getBytes(StandardCharsets.UTF_8)), is(false)); - assertThat(service.encrypted(service.encrypt(randomAsciiOfLength(10).getBytes(StandardCharsets.UTF_8))), is(true)); - } - - public void testReloadKey() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - final CryptoService.Listener listener = new CryptoService.Listener() { - @Override - public void onKeyChange(SecretKey oldSystemKey, SecretKey oldEncryptionKey) { - latch.countDown(); - } - }; - - // randomize how we set the listener - InternalCryptoService service; - if (randomBoolean()) { - service = new InternalCryptoService(settings, env, watcherService, Collections.singletonList(listener)); - service.start(); - } else { - service = new InternalCryptoService(settings, env, watcherService); - service.start(); - service.register(listener); - } - - String text = randomAsciiOfLength(10); - String signed = service.sign(text); - char[] textChars = text.toCharArray(); - char[] encrypted = service.encrypt(textChars); - - // we need to sleep to ensure the timestamp of the file will definitely change - // and so the resource watcher will pick up the change. - Thread.sleep(1000L); - - try (OutputStream os = Files.newOutputStream(keyFile)) { - Streams.copy(InternalCryptoService.generateKey(), os); - } - if (!latch.await(10, TimeUnit.SECONDS)) { - fail("waiting too long for test to complete. Expected callback is not called"); - } - String signed2 = service.sign(text); - assertThat(signed.equals(signed2), is(false)); - - char[] encrypted2 = service.encrypt(textChars); - - char[] decrypted = service.decrypt(encrypted); - char[] decrypted2 = service.decrypt(encrypted2); - assertThat(Arrays.equals(textChars, decrypted), is(false)); - assertThat(Arrays.equals(textChars, decrypted2), is(true)); - } - - public void testReencryptValuesOnKeyChange() throws Exception { - final InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - assertThat(service.encryptionEnabled(), is(true)); - final char[] text = randomAsciiOfLength(10).toCharArray(); - final char[] encrypted = service.encrypt(text); - assertThat(text, not(equalTo(encrypted))); - - final CountDownLatch latch = new CountDownLatch(1); - service.register(new CryptoService.Listener() { - @Override - public void onKeyChange(SecretKey oldSystemKey, SecretKey oldEncryptionKey) { - final char[] plainText = service.decrypt(encrypted, oldEncryptionKey); - assertThat(plainText, equalTo(text)); - final char[] newEncrypted = service.encrypt(plainText); - assertThat(newEncrypted, not(equalTo(encrypted))); - assertThat(newEncrypted, not(equalTo(plainText))); - latch.countDown(); - } - }); - - // we need to sleep to ensure the timestamp of the file will definitely change - // and so the resource watcher will pick up the change. - Thread.sleep(1000); - - Files.write(keyFile, InternalCryptoService.generateKey()); - if (!latch.await(10, TimeUnit.SECONDS)) { - fail("waiting too long for test to complete. Expected callback is not called or finished running"); - } - } - - public void testResignValuesOnKeyChange() throws Exception { - final InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - final String text = randomAsciiOfLength(10); - final String signed = service.sign(text); - assertThat(text, not(equalTo(signed))); - - final CountDownLatch latch = new CountDownLatch(1); - service.register(new CryptoService.Listener() { - @Override - public void onKeyChange(SecretKey oldSystemKey, SecretKey oldEncryptionKey) { - try { - assertThat(oldSystemKey, notNullValue()); - final String unsigned = service.unsignAndVerify(signed, oldSystemKey); - assertThat(unsigned, equalTo(text)); - final String newSigned = service.sign(unsigned); - assertThat(newSigned, not(equalTo(signed))); - assertThat(newSigned, not(equalTo(text))); - latch.countDown(); - } catch (IOException e) { - logger.error("caught exception in key change listener", e); - } - } - }); - - // we need to sleep to ensure the timestamp of the file will definitely change - // and so the resource watcher will pick up the change. - Thread.sleep(1000); - - Files.write(keyFile, InternalCryptoService.generateKey()); - if (!latch.await(10, TimeUnit.SECONDS)) { - fail("waiting too long for test to complete. Expected callback is not called or finished running"); - } - } - - public void testReencryptValuesOnKeyDeleted() throws Exception { - final InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - assertThat(service.encryptionEnabled(), is(true)); - final char[] text = randomAsciiOfLength(10).toCharArray(); - final char[] encrypted = service.encrypt(text); - assertThat(text, not(equalTo(encrypted))); - - final CountDownLatch latch = new CountDownLatch(1); - service.register(new CryptoService.Listener() { - @Override - public void onKeyChange(SecretKey oldSystemKey, SecretKey oldEncryptionKey) { - final char[] plainText = service.decrypt(encrypted, oldEncryptionKey); - assertThat(plainText, equalTo(text)); - final char[] newEncrypted = service.encrypt(plainText); - assertThat(newEncrypted, not(equalTo(encrypted))); - assertThat(newEncrypted, equalTo(plainText)); - latch.countDown(); - } - }); - - // we need to sleep to ensure the timestamp of the file will definitely change - // and so the resource watcher will pick up the change. - Thread.sleep(1000); - - Files.delete(keyFile); - if (!latch.await(10, TimeUnit.SECONDS)) { - fail("waiting too long for test to complete. Expected callback is not called or finished running"); - } - } - - public void testAllListenersCalledWhenExceptionThrown() throws Exception { - final InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - assertThat(service.encryptionEnabled(), is(true)); - - final CountDownLatch latch = new CountDownLatch(3); - service.register(new CryptoService.Listener() { - @Override - public void onKeyChange(SecretKey oldSystemKey, SecretKey oldEncryptionKey) { - latch.countDown(); - } - }); - service.register(new CryptoService.Listener() { - @Override - public void onKeyChange(SecretKey oldSystemKey, SecretKey oldEncryptionKey) { - latch.countDown(); - throw new RuntimeException("misbehaving listener"); - } - }); - service.register(new CryptoService.Listener() { - @Override - public void onKeyChange(SecretKey oldSystemKey, SecretKey oldEncryptionKey) { - latch.countDown(); - } - }); - - // we need to sleep to ensure the timestamp of the file will definitely change - // and so the resource watcher will pick up the change. - Thread.sleep(1000); - - Files.write(keyFile, InternalCryptoService.generateKey()); - if (!latch.await(10, TimeUnit.SECONDS)) { - fail("waiting too long for test to complete. Expected callback is not called or finished running"); - } - } - - public void testSigningOnKeyDeleted() throws Exception { - final InternalCryptoService service = new InternalCryptoService(settings, env, watcherService); - service.start(); - final String text = randomAsciiOfLength(10); - final String signed = service.sign(text); - assertThat(text, not(equalTo(signed))); - - final CountDownLatch latch = new CountDownLatch(1); - service.register(new CryptoService.Listener() { - @Override - public void onKeyChange(SecretKey oldSystemKey, SecretKey oldEncryptionKey) { - final String plainText = service.unsignAndVerify(signed, oldSystemKey); - assertThat(plainText, equalTo(text)); - try { - final String newSigned = service.sign(plainText); - assertThat(newSigned, not(equalTo(signed))); - assertThat(newSigned, not(equalTo(plainText))); - assertThat(service.unsignAndVerify(newSigned), equalTo(plainText)); - latch.countDown(); - } catch (IOException e) { - throw new ElasticsearchException("unexpected exception while signing", e); - } - } - }); - - // we need to sleep to ensure the timestamp of the file will definitely change - // and so the resource watcher will pick up the change. - Thread.sleep(1000); - - Files.delete(keyFile); - if (!latch.await(10, TimeUnit.SECONDS)) { - fail("waiting too long for test to complete. Expected callback is not called or finished running"); - } - } - - public void testSigningKeyCanBeRecomputedConsistently() { - final SecretKey systemKey = new SecretKeySpec(InternalCryptoService.generateKey(), InternalCryptoService.KEY_ALGO); - final SecretKey randomKey = InternalCryptoService.generateSecretKey(InternalCryptoService.RANDOM_KEY_SIZE); - int iterations = randomInt(100); - final SecretKey signingKey = InternalCryptoService.createSigningKey(systemKey, randomKey); - for (int i = 0; i < iterations; i++) { - SecretKey regenerated = InternalCryptoService.createSigningKey(systemKey, randomKey); - assertThat(regenerated, equalTo(signingKey)); - } - } - - private static byte[] randomByteArray() { - return randomByteArray(0); - } - - private static byte[] randomByteArray(int min) { - int count = randomIntBetween(min, 1000); - byte[] bytes = new byte[count]; - for (int i = 0; i < count; i++) { - bytes[i] = randomByte(); - } - return bytes; - } -} diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolTests.java index d8e3f3e7e6b..7b9f8483d82 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolTests.java @@ -11,7 +11,7 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.CommandTestCase; import org.elasticsearch.common.io.PathUtilsForTesting; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.junit.After; import java.nio.file.FileSystem; @@ -52,7 +52,7 @@ public class SystemKeyToolTests extends CommandTestCase { execute("-Epath.home=" + homeDir, path.toString()); byte[] bytes = Files.readAllBytes(path); // TODO: maybe we should actually check the key is...i dunno...valid? - assertEquals(InternalCryptoService.KEY_SIZE / 8, bytes.length); + assertEquals(CryptoService.KEY_SIZE / 8, bytes.length); Set perms = Files.getPosixFilePermissions(path); assertTrue(perms.toString(), perms.contains(PosixFilePermission.OWNER_READ)); @@ -67,7 +67,7 @@ public class SystemKeyToolTests extends CommandTestCase { Files.createDirectories(path.getParent()); execute("-Epath.home=" + homeDir.toString(), "-Expack.security.system_key.file=" + path.toAbsolutePath().toString()); byte[] bytes = Files.readAllBytes(path); - assertEquals(InternalCryptoService.KEY_SIZE / 8, bytes.length); + assertEquals(CryptoService.KEY_SIZE / 8, bytes.length); } public void testGenerateDefaultPath() throws Exception { @@ -76,7 +76,7 @@ public class SystemKeyToolTests extends CommandTestCase { Files.createDirectories(keyPath.getParent()); execute("-Epath.home=" + homeDir.toString()); byte[] bytes = Files.readAllBytes(keyPath); - assertEquals(InternalCryptoService.KEY_SIZE / 8, bytes.length); + assertEquals(CryptoService.KEY_SIZE / 8, bytes.length); } public void testThatSystemKeyMayOnlyBeReadByOwner() throws Exception { diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java index dc95118b79f..b48fec3e5f8 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java @@ -13,15 +13,14 @@ import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; -import org.elasticsearch.xpack.security.authc.support.SecuredString; -import org.elasticsearch.xpack.security.authz.InternalAuthorizationService; -import org.elasticsearch.xpack.security.user.AnonymousUser; import org.elasticsearch.test.SecurityIntegTestCase; import org.elasticsearch.test.SecuritySettingsSource; import org.elasticsearch.test.rest.ObjectPath; +import org.elasticsearch.xpack.security.authc.support.SecuredString; +import org.elasticsearch.xpack.security.authz.InternalAuthorizationService; +import org.elasticsearch.xpack.security.user.AnonymousUser; import org.junit.BeforeClass; -import java.util.Collections; import java.util.List; import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; @@ -54,7 +53,7 @@ public class RestAuthenticateActionTests extends SecurityIntegTestCase { public void testAuthenticateApi() throws Exception { try (Response response = getRestClient().performRequest( - "GET", "/_xpack/security/_authenticate", Collections.emptyMap(), null, + "GET", "/_xpack/security/_authenticate", new BasicHeader("Authorization", basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME, new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) { assertThat(response.getStatusLine().getStatusCode(), is(200)); @@ -69,8 +68,7 @@ public class RestAuthenticateActionTests extends SecurityIntegTestCase { } public void testAuthenticateApiWithoutAuthentication() throws Exception { - try (Response response = getRestClient().performRequest("GET", "/_xpack/security/_authenticate", - Collections.emptyMap(), null)) { + try (Response response = getRestClient().performRequest("GET", "/_xpack/security/_authenticate")) { if (anonymousEnabled) { assertThat(response.getStatusLine().getStatusCode(), is(200)); ObjectPath objectPath = ObjectPath.createFromXContent(XContentFactory.xContent(XContentType.JSON), diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ClientSSLServiceTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ClientSSLServiceTests.java index e6626571b08..18beb8e0b34 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ClientSSLServiceTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ClientSSLServiceTests.java @@ -41,8 +41,8 @@ import static org.hamcrest.Matchers.sameInstance; public class ClientSSLServiceTests extends ESTestCase { - Environment env; - Path testclientStore; + private Environment env; + private Path testclientStore; @Before public void setup() throws Exception { @@ -181,7 +181,7 @@ public class ClientSSLServiceTests extends ESTestCase { public void testThatSSLContextWithoutSettingsWorks() throws Exception { ClientSSLService sslService = createClientSSLService(Settings.EMPTY); SSLContext sslContext = sslService.sslContext(); - try (CloseableHttpClient client = HttpClients.custom().setSslcontext(sslContext).build()) { + try (CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext).build()) { // Execute a GET on a site known to have a valid certificate signed by a trusted public CA // This will result in a SSLHandshakeException if the SSLContext does not trust the CA, but the default // truststore trusts all common public CAs so the handshake will succeed @@ -196,7 +196,7 @@ public class ClientSSLServiceTests extends ESTestCase { .put("xpack.security.ssl.keystore.password", "testclient") .build()); SSLContext sslContext = sslService.sslContext(); - try (CloseableHttpClient client = HttpClients.custom().setSslcontext(sslContext).build()) { + try (CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext).build()) { // Execute a GET on a site known to have a valid certificate signed by a trusted public CA which will succeed because the JDK // certs are trusted by default client.execute(new HttpGet("https://www.elastic.co/")).close(); @@ -208,7 +208,7 @@ public class ClientSSLServiceTests extends ESTestCase { .put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), "false") .build()); sslContext = sslService.sslContext(); - try (CloseableHttpClient client = HttpClients.custom().setSslcontext(sslContext).build()) { + try (CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext).build()) { // Execute a GET on a site known to have a valid certificate signed by a trusted public CA // This will result in a SSLHandshakeException because the truststore is the testnodestore, which doesn't // trust any public CAs @@ -283,7 +283,7 @@ public class ClientSSLServiceTests extends ESTestCase { } } - ClientSSLService createClientSSLService(Settings settings) { + private ClientSSLService createClientSSLService(Settings settings) { ClientSSLService clientSSLService = new ClientSSLService(settings, new Global(settings)); clientSSLService.setEnvironment(env); return clientSSLService; diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ServerSSLServiceTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ServerSSLServiceTests.java index 682d2e9ff22..6d633279f52 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ServerSSLServiceTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ServerSSLServiceTests.java @@ -35,8 +35,8 @@ import static org.hamcrest.Matchers.sameInstance; public class ServerSSLServiceTests extends ESTestCase { - Path testnodeStore; - Environment env; + private Path testnodeStore; + private Environment env; @Before public void setup() throws Exception { diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java index 891392aa162..cb046fdfe4d 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java @@ -17,7 +17,7 @@ import org.elasticsearch.node.Node; import org.elasticsearch.xpack.security.authc.file.FileRealm; import org.elasticsearch.xpack.security.Security; import org.elasticsearch.xpack.security.authz.store.FileRolesStore; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.security.transport.netty.SecurityNettyTransport; import org.elasticsearch.test.SecurityIntegTestCase; import org.elasticsearch.transport.Transport; @@ -82,7 +82,7 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase public void testThatConnectionToServerTypeConnectionWorks() throws IOException { Settings dataNodeSettings = internalCluster().getDataNodeInstance(Settings.class); - String systemKeyFile = InternalCryptoService.FILE_SETTING.get(dataNodeSettings); + String systemKeyFile = CryptoService.FILE_SETTING.get(dataNodeSettings); Transport transport = internalCluster().getDataNodeInstance(Transport.class); TransportAddress transportAddress = transport.boundAddress().publishAddress(); @@ -102,7 +102,7 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase .put("xpack.security.audit.enabled", false) .put("path.home", createTempDir()) .put(NetworkModule.HTTP_ENABLED.getKey(), false) - .put(InternalCryptoService.FILE_SETTING.getKey(), systemKeyFile) + .put(CryptoService.FILE_SETTING.getKey(), systemKeyFile) .build(); try (Node node = new MockNode(nodeSettings, Collections.singletonList(XPackPlugin.class))) { node.start(); @@ -112,7 +112,7 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase public void testThatConnectionToClientTypeConnectionIsRejected() throws IOException { Settings dataNodeSettings = internalCluster().getDataNodeInstance(Settings.class); - String systemKeyFile = InternalCryptoService.FILE_SETTING.get(dataNodeSettings); + String systemKeyFile = CryptoService.FILE_SETTING.get(dataNodeSettings); Path folder = createFolder(createTempDir(), getClass().getSimpleName() + "-" + randomAsciiOfLength(10)); @@ -132,7 +132,7 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase .put(SecurityNettyTransport.SSL_SETTING.getKey(), sslTransportEnabled()) .put("xpack.security.audit.enabled", false) .put(NetworkModule.HTTP_ENABLED.getKey(), false) - .put(InternalCryptoService.FILE_SETTING.getKey(), systemKeyFile) + .put(CryptoService.FILE_SETTING.getKey(), systemKeyFile) .put("discovery.initial_state_timeout", "2s") .put("path.home", createTempDir()) .put(Node.NODE_MASTER_SETTING.getKey(), false) diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/TransportFilterTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/TransportFilterTests.java index 8e1b1ab3829..c053fa9c2f5 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/TransportFilterTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/TransportFilterTests.java @@ -116,8 +116,8 @@ public class TransportFilterTests extends ESIntegTestCase { public static class InternalPlugin extends Plugin { @Override - public Collection nodeModules() { - return Collections.singletonList(new TestTransportFilterModule()); + public Collection createGuiceModules() { + return Collections.singletonList(new TestTransportFilterModule()); } } @@ -269,7 +269,7 @@ public class TransportFilterTests extends ESIntegTestCase { } } - static void await(CountDownLatch latch) throws Exception { + private static void await(CountDownLatch latch) throws Exception { if (!latch.await(5, TimeUnit.SECONDS)) { fail("waiting too long for request"); } @@ -298,7 +298,7 @@ public class TransportFilterTests extends ESIntegTestCase { @Override protected Map initializeProfileFilters() { - return Collections.singletonMap(TransportSettings.DEFAULT_PROFILE, + return Collections.singletonMap(TransportSettings.DEFAULT_PROFILE, mock(ServerTransportFilter.NodeProfile.class)); } } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslClientAuthTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslClientAuthTests.java index db8836dbf19..2667b1dfa9c 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslClientAuthTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslClientAuthTests.java @@ -5,11 +5,12 @@ */ package org.elasticsearch.xpack.security.transport.ssl; +import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicHeader; +import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.client.Response; @@ -18,23 +19,22 @@ import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; +import org.elasticsearch.test.SecurityIntegTestCase; +import org.elasticsearch.transport.Transport; +import org.elasticsearch.xpack.XPackPlugin; import org.elasticsearch.xpack.security.Security; import org.elasticsearch.xpack.security.ssl.ClientSSLService; import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global; import org.elasticsearch.xpack.security.transport.netty.SecurityNettyHttpServerTransport; import org.elasticsearch.xpack.security.transport.netty.SecurityNettyTransport; -import org.elasticsearch.test.SecurityIntegTestCase; -import org.elasticsearch.transport.Transport; -import org.elasticsearch.xpack.XPackPlugin; import javax.net.ssl.SSLHandshakeException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; -import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; import static org.elasticsearch.test.SecuritySettingsSource.getSSLSettingsForStore; +import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -60,10 +60,10 @@ public class SslClientAuthTests extends SecurityIntegTestCase { public void testThatHttpFailsWithoutSslClientAuth() throws IOException { SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory( SSLContexts.createDefault(), - SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + NoopHostnameVerifier.INSTANCE); try (RestClient restClient = createRestClient(HttpClients.custom().setSSLSocketFactory(socketFactory).build(), "https")) { - restClient.performRequest("GET", "/", Collections.emptyMap(), null); + restClient.performRequest("GET", "/"); fail("Expected SSLHandshakeException"); } catch (SSLHandshakeException e) { assertThat(e.getMessage(), containsString("unable to find valid certification path to requested target")); @@ -78,12 +78,12 @@ public class SslClientAuthTests extends SecurityIntegTestCase { SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory( sslService.sslContext(), - SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + NoopHostnameVerifier.INSTANCE); CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).build(); try (RestClient restClient = createRestClient(client, "https")) { - try (Response response = restClient.performRequest("GET", "/", Collections.emptyMap(), null, + try (Response response = restClient.performRequest("GET", "/", new BasicHeader("Authorization", basicAuthHeaderValue(transportClientUsername(), transportClientPassword())))) { assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); assertThat(EntityUtils.toString(response.getEntity()), containsString("You Know, for Search")); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java index 184becaaf1b..4d8b1299ffb 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java @@ -10,6 +10,7 @@ import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; @@ -103,7 +104,7 @@ public class SslIntegrationTests extends SecurityIntegTestCase { CredentialsProvider provider = new BasicCredentialsProvider(); provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(nodeClientUsername(), new String(nodeClientPassword().internalChars()))); - try (CloseableHttpClient client = HttpClients.custom().setSslcontext(service.sslContext()) + try (CloseableHttpClient client = HttpClients.custom().setSSLContext(service.sslContext()) .setDefaultCredentialsProvider(provider).build(); CloseableHttpResponse response = client.execute(new HttpGet(getNodeUrl()))) { assertThat(response.getStatusLine().getStatusCode(), is(200)); @@ -119,7 +120,7 @@ public class SslIntegrationTests extends SecurityIntegTestCase { sslContext.init(null, factory.getTrustManagers(), new SecureRandom()); SSLConnectionSocketFactory sf = new SSLConnectionSocketFactory(sslContext, new String[]{ "SSLv3" }, null, - SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + NoopHostnameVerifier.INSTANCE); try (CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sf).build()) { client.execute(new HttpGet(getNodeUrl())); fail("Expected a connection error due to SSLv3 not being supported by default"); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/user/AnonymousUserIntegTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/user/AnonymousUserIntegTests.java index ee9fe3a43c2..85f72ef525d 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/user/AnonymousUserIntegTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/user/AnonymousUserIntegTests.java @@ -9,10 +9,8 @@ import org.elasticsearch.client.Response; import org.elasticsearch.client.ResponseException; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.xpack.security.authz.InternalAuthorizationService; import org.elasticsearch.test.SecurityIntegTestCase; - -import java.util.Collections; +import org.elasticsearch.xpack.security.authz.InternalAuthorizationService; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @@ -43,7 +41,7 @@ public class AnonymousUserIntegTests extends SecurityIntegTestCase { public void testAnonymousViaHttp() throws Exception { try { - getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null); + getRestClient().performRequest("GET", "/_nodes"); fail("request should have failed"); } catch(ResponseException e) { int statusCode = e.getResponse().getStatusLine().getStatusCode(); diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java index 17be9e38b95..d3d1aa4e49a 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack; +import java.io.IOException; import java.nio.file.Path; import java.security.AccessController; import java.security.PrivilegedAction; @@ -41,7 +42,6 @@ import org.elasticsearch.xpack.action.XPackInfoAction; import org.elasticsearch.xpack.action.XPackUsageAction; import org.elasticsearch.xpack.common.ScriptServiceProxy; import org.elasticsearch.xpack.common.http.HttpClientModule; -import org.elasticsearch.xpack.common.secret.SecretModule; import org.elasticsearch.xpack.common.text.TextTemplateModule; import org.elasticsearch.xpack.extensions.XPackExtension; import org.elasticsearch.xpack.extensions.XPackExtensionsService; @@ -55,7 +55,8 @@ import org.elasticsearch.xpack.rest.action.RestXPackInfoAction; import org.elasticsearch.xpack.rest.action.RestXPackUsageAction; import org.elasticsearch.xpack.security.Security; import org.elasticsearch.xpack.security.authc.AuthenticationModule; -import org.elasticsearch.xpack.support.clock.ClockModule; +import org.elasticsearch.xpack.support.clock.Clock; +import org.elasticsearch.xpack.support.clock.SystemClock; import org.elasticsearch.xpack.watcher.Watcher; public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin { @@ -109,18 +110,19 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin { protected Graph graph; protected Notification notification; - public XPackPlugin(Settings settings) { + public XPackPlugin(Settings settings) throws IOException { this.settings = settings; this.transportClientMode = transportClientMode(settings); + final Environment env = transportClientMode ? null : new Environment(settings); + this.licensing = new Licensing(settings); - this.security = new Security(settings); + this.security = new Security(settings, env); this.monitoring = new Monitoring(settings); this.watcher = new Watcher(settings); this.graph = new Graph(settings); this.notification = new Notification(settings); // Check if the node is a transport client. if (transportClientMode == false) { - Environment env = new Environment(settings); this.extensionsService = new XPackExtensionsService(settings, resolveXPackExtensionsFile(env), getExtensions()); } else { this.extensionsService = null; @@ -132,34 +134,38 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin { return Collections.emptyList(); } + // overridable by tests + protected Clock getClock() { + return SystemClock.INSTANCE; + } + @Override - public Collection nodeModules() { + public Collection createGuiceModules() { ArrayList modules = new ArrayList<>(); - modules.add(new ClockModule()); + modules.add(b -> b.bind(Clock.class).toInstance(getClock())); modules.addAll(notification.nodeModules()); modules.addAll(licensing.nodeModules()); modules.addAll(security.nodeModules()); modules.addAll(watcher.nodeModules()); modules.addAll(monitoring.nodeModules()); - modules.addAll(graph.nodeModules()); + modules.addAll(graph.createGuiceModules()); if (transportClientMode == false) { modules.add(new HttpClientModule()); - modules.add(new SecretModule(settings)); modules.add(new TextTemplateModule()); } return modules; } @Override - public Collection> nodeServices() { + public Collection> getGuiceServiceClasses() { ArrayList> services = new ArrayList<>(); services.addAll(notification.nodeServices()); services.addAll(licensing.nodeServices()); services.addAll(security.nodeServices()); services.addAll(watcher.nodeServices()); services.addAll(monitoring.nodeServices()); - services.addAll(graph.nodeServices()); + services.addAll(graph.getGuiceServiceClasses()); return services; } diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/TransportXPackUsageAction.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/TransportXPackUsageAction.java index 86dca578ad6..9438c85e2e6 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/TransportXPackUsageAction.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/TransportXPackUsageAction.java @@ -7,8 +7,11 @@ package org.elasticsearch.xpack.action; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; -import org.elasticsearch.action.support.HandledTransportAction; +import org.elasticsearch.action.support.master.TransportMasterNodeAction; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.block.ClusterBlockException; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.threadpool.ThreadPool; @@ -21,22 +24,38 @@ import java.util.stream.Collectors; /** */ -public class TransportXPackUsageAction extends HandledTransportAction { +public class TransportXPackUsageAction extends TransportMasterNodeAction { private final Set featureSets; @Inject public TransportXPackUsageAction(Settings settings, ThreadPool threadPool, TransportService transportService, - ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver, - Set featureSets) { - super(settings, XPackUsageAction.NAME, threadPool, transportService, actionFilters, indexNameExpressionResolver, + ClusterService clusterService, ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver, Set featureSets) { + super(settings, XPackUsageAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver, XPackUsageRequest::new); this.featureSets = featureSets; } @Override - protected void doExecute(XPackUsageRequest request, ActionListener listener) { + protected String executor() { + return ThreadPool.Names.MANAGEMENT; + } + + @Override + protected XPackUsageResponse newResponse() { + return new XPackUsageResponse(); + } + + @Override + protected void masterOperation(XPackUsageRequest request, ClusterState state, ActionListener listener) + throws Exception { List usages = featureSets.stream().map(XPackFeatureSet::usage).collect(Collectors.toList()); listener.onResponse(new XPackUsageResponse(usages)); } + + @Override + protected ClusterBlockException checkBlock(XPackUsageRequest request, ClusterState state) { + return null; + } } diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackUsageRequest.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackUsageRequest.java index 5b91a7565d1..506bc5d30fd 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackUsageRequest.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackUsageRequest.java @@ -5,13 +5,13 @@ */ package org.elasticsearch.xpack.action; -import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.support.master.MasterNodeRequest; /** * */ -public class XPackUsageRequest extends ActionRequest { +public class XPackUsageRequest extends MasterNodeRequest { @Override public ActionRequestValidationException validate() { diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackUsageRequestBuilder.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackUsageRequestBuilder.java index fe1986445ce..d7d984f306e 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackUsageRequestBuilder.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackUsageRequestBuilder.java @@ -5,12 +5,11 @@ */ package org.elasticsearch.xpack.action; -import org.elasticsearch.action.ActionRequestBuilder; +import org.elasticsearch.action.support.master.MasterNodeOperationRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; -/** - */ -public class XPackUsageRequestBuilder extends ActionRequestBuilder { +public class XPackUsageRequestBuilder + extends MasterNodeOperationRequestBuilder { public XPackUsageRequestBuilder(ElasticsearchClient client) { this(client, XPackUsageAction.INSTANCE); diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/common/http/auth/basic/ApplicableBasicAuth.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/common/http/auth/basic/ApplicableBasicAuth.java index d52d8d644d0..28e2d311927 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/common/http/auth/basic/ApplicableBasicAuth.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/common/http/auth/basic/ApplicableBasicAuth.java @@ -5,20 +5,20 @@ */ package org.elasticsearch.xpack.common.http.auth.basic; -import org.elasticsearch.xpack.common.http.auth.ApplicableHttpAuth; -import org.elasticsearch.xpack.common.secret.SecretService; - import java.net.HttpURLConnection; import java.nio.charset.StandardCharsets; import java.util.Base64; +import org.elasticsearch.xpack.common.http.auth.ApplicableHttpAuth; +import org.elasticsearch.xpack.security.crypto.CryptoService; + /** */ public class ApplicableBasicAuth extends ApplicableHttpAuth { private final String basicAuth; - public ApplicableBasicAuth(BasicAuth auth, SecretService service) { + public ApplicableBasicAuth(BasicAuth auth, CryptoService service) { super(auth); basicAuth = headerValue(auth.username, auth.password.text(service)); } diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/common/http/auth/basic/BasicAuthFactory.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/common/http/auth/basic/BasicAuthFactory.java index f2839bd0ceb..48dfbe42cf8 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/common/http/auth/basic/BasicAuthFactory.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/common/http/auth/basic/BasicAuthFactory.java @@ -5,10 +5,11 @@ */ package org.elasticsearch.xpack.common.http.auth.basic; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.xpack.common.http.auth.HttpAuthFactory; -import org.elasticsearch.xpack.common.secret.SecretService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import java.io.IOException; @@ -17,11 +18,11 @@ import java.io.IOException; */ public class BasicAuthFactory extends HttpAuthFactory { - private final SecretService secretService; + private final CryptoService cryptoService; @Inject - public BasicAuthFactory(SecretService secretService) { - this.secretService = secretService; + public BasicAuthFactory(@Nullable CryptoService cryptoService) { + this.cryptoService = cryptoService; } public String type() { @@ -34,6 +35,6 @@ public class BasicAuthFactory extends HttpAuthFactory counters = new ObjectLongHashMap<>(); + + public Counters(String ... names) { + for (String name : names) { + set(name); + } + } + + /** + * Sets a counter. This ensures that the counter is there, even though it is never incremented. + * @param name Name of the counter + */ + public void set(String name) { + counters.put(name, 0); + } + + /** + * Increment the counter by one + * @param name Name of the counter + */ + public void inc(String name) { + inc(name, 1); + } + + /** + * Increment the counter by configured number + * @param name The name of the counter + * @param count Incremental value + */ + public void inc(String name, long count) { + counters.addTo(name, count); + } + + /** + * Convert the counters to a nested map, using the "." as a splitter to create deeper maps + * @return A nested map with all the current configured counters + */ + @SuppressWarnings("unchecked") + public Map toMap() { + Map map = new HashMap<>(); + for (ObjectLongCursor counter : counters) { + if (counter.key.contains(".")) { + String[] parts = counter.key.split("\\."); + Map curr = map; + for (int i = 0; i < parts.length; i++) { + String part = parts[i]; + boolean isLast = i == parts.length - 1; + if (isLast == false) { + if (curr.containsKey(part) == false) { + curr.put(part, new HashMap()); + curr = (Map) curr.get(part); + } else { + curr = (Map) curr.get(part); + } + } else { + curr.put(part, counter.value); + } + } + } else { + map.put(counter.key, counter.value); + } + } + + return map; + } +} diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/notification/email/Account.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/notification/email/Account.java index fed92a3408a..f1d5571106d 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/notification/email/Account.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/notification/email/Account.java @@ -5,13 +5,6 @@ */ package org.elasticsearch.xpack.notification.email; -import org.elasticsearch.SpecialPermission; -import org.elasticsearch.common.logging.ESLogger; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.settings.SettingsException; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.xpack.common.secret.SecretService; - import javax.activation.CommandMap; import javax.activation.MailcapCommandMap; import javax.mail.MessagingException; @@ -24,6 +17,13 @@ import java.security.PrivilegedAction; import java.util.Map; import java.util.Properties; +import org.elasticsearch.SpecialPermission; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.settings.SettingsException; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.xpack.security.crypto.CryptoService; + /** * */ @@ -62,13 +62,13 @@ public class Account { .build(); private final Config config; - private final SecretService secretService; + private final CryptoService cryptoService; private final ESLogger logger; private final Session session; - Account(Config config, SecretService secretService, ESLogger logger) { + Account(Config config, CryptoService cryptoService, ESLogger logger) { this.config = config; - this.secretService = secretService; + this.cryptoService = cryptoService; this.logger = logger; session = config.createSession(); } @@ -102,7 +102,7 @@ public class Account { String password = null; if (auth != null && auth.password() != null) { - password = new String(auth.password().text(secretService)); + password = new String(auth.password().text(cryptoService)); } else if (config.smtp.password != null) { password = new String(config.smtp.password); } diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/notification/email/Accounts.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/notification/email/Accounts.java index 12473336c4d..b1317480b34 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/notification/email/Accounts.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/notification/email/Accounts.java @@ -5,13 +5,13 @@ */ package org.elasticsearch.xpack.notification.email; +import java.util.HashMap; +import java.util.Map; + import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsException; -import org.elasticsearch.xpack.common.secret.SecretService; - -import java.util.HashMap; -import java.util.Map; +import org.elasticsearch.xpack.security.crypto.CryptoService; /** * @@ -21,12 +21,12 @@ public class Accounts { private final String defaultAccountName; private final Map accounts; - public Accounts(Settings settings, SecretService secretService, ESLogger logger) { + public Accounts(Settings settings, CryptoService cryptoService, ESLogger logger) { Settings accountsSettings = settings.getAsSettings("account"); accounts = new HashMap<>(); for (String name : accountsSettings.names()) { Account.Config config = new Account.Config(name, accountsSettings.getAsSettings(name)); - Account account = new Account(config, secretService, logger); + Account account = new Account(config, cryptoService, logger); accounts.put(name, account); } diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/notification/email/InternalEmailService.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/notification/email/InternalEmailService.java index 26a0f8b7279..8fdbf9adc59 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/notification/email/InternalEmailService.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/notification/email/InternalEmailService.java @@ -5,32 +5,33 @@ */ package org.elasticsearch.xpack.notification.email; +import javax.mail.MessagingException; + import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.xpack.common.secret.SecretService; - -import javax.mail.MessagingException; +import org.elasticsearch.xpack.security.crypto.CryptoService; /** * */ public class InternalEmailService extends AbstractLifecycleComponent implements EmailService { - private final SecretService secretService; + private final CryptoService cryptoService; public static final Setting EMAIL_ACCOUNT_SETTING = Setting.groupSetting("xpack.notification.email.", Setting.Property.Dynamic, Setting.Property.NodeScope); private volatile Accounts accounts; @Inject - public InternalEmailService(Settings settings, SecretService secretService, ClusterSettings clusterSettings) { + public InternalEmailService(Settings settings, @Nullable CryptoService cryptoService, ClusterSettings clusterSettings) { super(settings); - this.secretService = secretService; + this.cryptoService = cryptoService; clusterSettings.addSettingsUpdateConsumer(EMAIL_ACCOUNT_SETTING, this::setEmailAccountSettings); setEmailAccountSettings(EMAIL_ACCOUNT_SETTING.get(settings)); } @@ -78,7 +79,7 @@ public class InternalEmailService extends AbstractLifecycleComponent implements } protected Accounts createAccounts(Settings settings, ESLogger logger) { - return new Accounts(settings, secretService, logger); + return new Accounts(settings, cryptoService, logger); } } diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/rest/action/RestXPackUsageAction.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/rest/action/RestXPackUsageAction.java index 83e25523315..45cc13c5da1 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/rest/action/RestXPackUsageAction.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/rest/action/RestXPackUsageAction.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.rest.action; +import org.elasticsearch.action.support.master.MasterNodeRequest; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -33,7 +34,9 @@ public class RestXPackUsageAction extends XPackRestHandler { @Override public void handleRequest(RestRequest request, RestChannel restChannel, XPackClient client) throws Exception { - new XPackUsageRequestBuilder(client.es()).execute(new RestBuilderListener(restChannel) { + new XPackUsageRequestBuilder(client.es()) + .setMasterNodeTimeout(request.paramAsTime("master_timeout", MasterNodeRequest.DEFAULT_MASTER_NODE_TIMEOUT)) + .execute(new RestBuilderListener(restChannel) { @Override public RestResponse buildResponse(XPackUsageResponse response, XContentBuilder builder) throws Exception { builder.startObject(); diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/support/clock/ClockModule.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/support/clock/ClockModule.java deleted file mode 100644 index b024dbf4dea..00000000000 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/support/clock/ClockModule.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.xpack.support.clock; - -import org.elasticsearch.common.inject.AbstractModule; - -/** - * - */ -public class ClockModule extends AbstractModule { - - @Override - protected void configure() { - bind(Clock.class).toInstance(SystemClock.INSTANCE); - } - -} diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/TimeWarpedXPackPlugin.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/TimeWarpedXPackPlugin.java index b2b69c4d284..e7824575e09 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/TimeWarpedXPackPlugin.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/TimeWarpedXPackPlugin.java @@ -5,43 +5,22 @@ */ package org.elasticsearch.xpack; -import org.elasticsearch.common.inject.Module; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.xpack.support.clock.Clock; import org.elasticsearch.xpack.support.clock.ClockMock; -import org.elasticsearch.xpack.support.clock.ClockModule; import org.elasticsearch.xpack.watcher.test.TimeWarpedWatcher; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +import java.io.IOException; public class TimeWarpedXPackPlugin extends XPackPlugin { - public TimeWarpedXPackPlugin(Settings settings) { + public TimeWarpedXPackPlugin(Settings settings) throws IOException { super(settings); watcher = new TimeWarpedWatcher(settings); } @Override - public Collection nodeModules() { - List modules = new ArrayList<>(super.nodeModules()); - for (int i = 0; i < modules.size(); ++i) { - Module module = modules.get(i); - if (module instanceof ClockModule) { - // replacing the clock module so we'll be able - // to control time in tests - modules.set(i, new MockClockModule()); - } - } - return modules; - } - - public static class MockClockModule extends ClockModule { - @Override - protected void configure() { - bind(ClockMock.class).asEagerSingleton(); - bind(Clock.class).to(ClockMock.class); - } + protected Clock getClock() { + return new ClockMock(); } } diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/common/http/HttpClientTests.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/common/http/HttpClientTests.java index 702319c55c0..d659ee93b3b 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/common/http/HttpClientTests.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/common/http/HttpClientTests.java @@ -19,7 +19,6 @@ import org.elasticsearch.test.junit.annotations.Network; import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry; import org.elasticsearch.xpack.common.http.auth.basic.BasicAuth; import org.elasticsearch.xpack.common.http.auth.basic.BasicAuthFactory; -import org.elasticsearch.xpack.common.secret.SecretService; import org.junit.After; import org.junit.Before; @@ -49,15 +48,13 @@ public class HttpClientTests extends ESTestCase { private MockWebServer webServer; private HttpClient httpClient; private HttpAuthRegistry authRegistry; - private SecretService secretService; private Environment environment = new Environment(Settings.builder().put("path.home", createTempDir()).build()); private int webPort; @Before public void init() throws Exception { - secretService = SecretService.Insecure.INSTANCE; - authRegistry = new HttpAuthRegistry(singletonMap(BasicAuth.TYPE, new BasicAuthFactory(secretService))); + authRegistry = new HttpAuthRegistry(singletonMap(BasicAuth.TYPE, new BasicAuthFactory(null))); webServer = startWebServer(); webPort = webServer.getPort(); httpClient = new HttpClient(Settings.EMPTY, authRegistry, environment); diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/common/http/HttpRequestTemplateTests.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/common/http/HttpRequestTemplateTests.java index 850def118f4..aca7e83630f 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/common/http/HttpRequestTemplateTests.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/common/http/HttpRequestTemplateTests.java @@ -16,7 +16,6 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry; import org.elasticsearch.xpack.common.http.auth.basic.BasicAuth; import org.elasticsearch.xpack.common.http.auth.basic.BasicAuthFactory; -import org.elasticsearch.xpack.common.secret.SecretService; import org.elasticsearch.xpack.common.text.TextTemplate; import org.elasticsearch.xpack.watcher.test.MockTextTemplateEngine; import org.jboss.netty.handler.codec.http.HttpHeaders; @@ -139,7 +138,7 @@ public class HttpRequestTemplateTests extends ESTestCase { HttpRequestTemplate template = builder.build(); HttpAuthRegistry registry = new HttpAuthRegistry(singletonMap(BasicAuth.TYPE, - new BasicAuthFactory(SecretService.Insecure.INSTANCE))); + new BasicAuthFactory(null))); HttpRequestTemplate.Parser parser = new HttpRequestTemplate.Parser(registry); XContentBuilder xContentBuilder = template.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS); diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/common/stats/CountersTests.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/common/stats/CountersTests.java new file mode 100644 index 00000000000..711dab70333 --- /dev/null +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/common/stats/CountersTests.java @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.common.stats; + +import org.elasticsearch.test.ESTestCase; + +import java.util.Map; + +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.instanceOf; + +public class CountersTests extends ESTestCase { + + public void testCounters() { + Counters counters = new Counters(); + counters.inc("f", 200); + counters.inc("foo.bar"); + counters.inc("foo.baz"); + counters.inc("foo.baz"); + Map map = counters.toMap(); + assertThat(map, hasEntry("f", 200L)); + assertThat(map, hasKey("foo")); + assertThat(map.get("foo"), instanceOf(Map.class)); + Map fooMap = (Map) map.get("foo"); + assertThat(fooMap, hasEntry("bar", 1L)); + assertThat(fooMap, hasEntry("baz", 2L)); + } +} diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/AccountTests.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/AccountTests.java index 70238bfcf26..61adce9ac04 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/AccountTests.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/AccountTests.java @@ -11,7 +11,6 @@ import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.notification.email.support.EmailServer; import org.elasticsearch.xpack.common.secret.Secret; -import org.elasticsearch.xpack.common.secret.SecretService; import org.junit.After; import org.junit.Before; @@ -169,7 +168,7 @@ public class AccountTests extends ESTestCase { .put("smtp.port", server.port()) .put("smtp.user", USERNAME) .put("smtp.password", PASSWORD) - .build()), SecretService.Insecure.INSTANCE, logger); + .build()), null, logger); Email email = Email.builder() .id("_id") @@ -206,7 +205,7 @@ public class AccountTests extends ESTestCase { .put("smtp.port", server.port()) .put("smtp.user", USERNAME) .put("smtp.password", PASSWORD) - .build()), SecretService.Insecure.INSTANCE, logger); + .build()), null, logger); Email email = Email.builder() .id("_id") @@ -246,7 +245,7 @@ public class AccountTests extends ESTestCase { Account account = new Account(new Account.Config("default", Settings.builder() .put("smtp.host", "localhost") .put("smtp.port", server.port()) - .build()), SecretService.Insecure.INSTANCE, logger); + .build()), null, logger); Email email = Email.builder() .id("_id") @@ -277,7 +276,7 @@ public class AccountTests extends ESTestCase { Account account = new Account(new Account.Config("default", Settings.builder() .put("smtp.host", "localhost") .put("smtp.port", server.port()) - .build()), SecretService.Insecure.INSTANCE, logger); + .build()), null, logger); Properties mailProperties = account.getConfig().smtp.properties; assertThat(mailProperties.get("mail.smtp.connectiontimeout"), is(String.valueOf(TimeValue.timeValueMinutes(2).millis()))); @@ -292,7 +291,7 @@ public class AccountTests extends ESTestCase { .put("smtp.connection_timeout", TimeValue.timeValueMinutes(4)) .put("smtp.write_timeout", TimeValue.timeValueMinutes(6)) .put("smtp.timeout", TimeValue.timeValueMinutes(8)) - .build()), SecretService.Insecure.INSTANCE, logger); + .build()), null, logger); Properties mailProperties = account.getConfig().smtp.properties; @@ -307,7 +306,7 @@ public class AccountTests extends ESTestCase { .put("smtp.host", "localhost") .put("smtp.port", server.port()) .put("smtp.connection_timeout", 4000) - .build()), SecretService.Insecure.INSTANCE, logger); + .build()), null, logger); }); } } diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/AccountsTests.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/AccountsTests.java index d2f8cffe826..548aad3a356 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/AccountsTests.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/AccountsTests.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.notification.email; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsException; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.xpack.common.secret.SecretService; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -24,7 +23,7 @@ public class AccountsTests extends ESTestCase { .put("default_account", "account1"); addAccountSettings("account1", builder); - Accounts accounts = new Accounts(builder.build(), SecretService.Insecure.INSTANCE, logger); + Accounts accounts = new Accounts(builder.build(), null, logger); Account account = accounts.account("account1"); assertThat(account, notNullValue()); assertThat(account.name(), equalTo("account1")); @@ -37,7 +36,7 @@ public class AccountsTests extends ESTestCase { Settings.Builder builder = Settings.builder(); addAccountSettings("account1", builder); - Accounts accounts = new Accounts(builder.build(), SecretService.Insecure.INSTANCE, logger); + Accounts accounts = new Accounts(builder.build(), null, logger); Account account = accounts.account("account1"); assertThat(account, notNullValue()); assertThat(account.name(), equalTo("account1")); @@ -52,7 +51,7 @@ public class AccountsTests extends ESTestCase { addAccountSettings("account1", builder); addAccountSettings("account2", builder); - Accounts accounts = new Accounts(builder.build(), SecretService.Insecure.INSTANCE, logger); + Accounts accounts = new Accounts(builder.build(), null, logger); Account account = accounts.account("account1"); assertThat(account, notNullValue()); assertThat(account.name(), equalTo("account1")); @@ -70,7 +69,7 @@ public class AccountsTests extends ESTestCase { addAccountSettings("account1", builder); addAccountSettings("account2", builder); - Accounts accounts = new Accounts(builder.build(), SecretService.Insecure.INSTANCE, logger); + Accounts accounts = new Accounts(builder.build(), null, logger); Account account = accounts.account("account1"); assertThat(account, notNullValue()); assertThat(account.name(), equalTo("account1")); @@ -88,7 +87,7 @@ public class AccountsTests extends ESTestCase { addAccountSettings("account1", builder); addAccountSettings("account2", builder); try { - new Accounts(builder.build(), SecretService.Insecure.INSTANCE, logger); + new Accounts(builder.build(), null, logger); fail("Expected SettingsException"); } catch (SettingsException e) { assertThat(e.getMessage(), is("could not find default email account [unknown]")); @@ -97,7 +96,7 @@ public class AccountsTests extends ESTestCase { public void testNoAccount() throws Exception { Settings.Builder builder = Settings.builder(); - Accounts accounts = new Accounts(builder.build(), SecretService.Insecure.INSTANCE, logger); + Accounts accounts = new Accounts(builder.build(), null, logger); try { accounts.account(null); fail("no accounts are configured so trying to get the default account should throw an IllegalStateException"); @@ -110,7 +109,7 @@ public class AccountsTests extends ESTestCase { Settings.Builder builder = Settings.builder() .put("default_account", "unknown"); try { - new Accounts(builder.build(), SecretService.Insecure.INSTANCE, logger); + new Accounts(builder.build(), null, logger); fail("Expected SettingsException"); } catch (SettingsException e) { assertThat(e.getMessage(), is("could not find default email account [unknown]")); diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/EmailSecretsIntegrationTests.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/EmailSecretsIntegrationTests.java index c5ba29a50e1..c17a2896820 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/EmailSecretsIntegrationTests.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/EmailSecretsIntegrationTests.java @@ -8,9 +8,9 @@ package org.elasticsearch.xpack.notification.email; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.support.XContentMapValues; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.watcher.client.WatcherClient; import org.elasticsearch.xpack.watcher.execution.ActionExecutionMode; -import org.elasticsearch.xpack.common.secret.SecretService; import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource; import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase; import org.elasticsearch.xpack.watcher.transport.actions.execute.ExecuteWatchResponse; @@ -34,7 +34,6 @@ import static org.elasticsearch.xpack.watcher.condition.ConditionBuilders.always import static org.elasticsearch.xpack.watcher.input.InputBuilders.simpleInput; import static org.elasticsearch.xpack.watcher.trigger.TriggerBuilders.schedule; import static org.elasticsearch.xpack.watcher.trigger.schedule.Schedules.cron; -import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; @@ -97,18 +96,10 @@ public class EmailSecretsIntegrationTests extends AbstractWatcherIntegrationTest assertThat(value, notNullValue()); if (securityEnabled() && encryptSensitiveData) { assertThat(value, not(is((Object) PASSWORD))); - SecretService secretService = getInstanceFromMaster(SecretService.class); - assertThat(secretService, instanceOf(SecretService.Secure.class)); - assertThat(new String(secretService.decrypt(((String) value).toCharArray())), is(PASSWORD)); + CryptoService cryptoService = getInstanceFromMaster(CryptoService.class); + assertThat(new String(cryptoService.decrypt(((String) value).toCharArray())), is(PASSWORD)); } else { assertThat(value, is((Object) PASSWORD)); - SecretService secretService = getInstanceFromMaster(SecretService.class); - if (securityEnabled()) { - assertThat(secretService, instanceOf(SecretService.Secure.class)); - } else { - assertThat(secretService, instanceOf(SecretService.Insecure.class)); - } - assertThat(new String(secretService.decrypt(((String) value).toCharArray())), is(PASSWORD)); } // verifying the password is not returned by the GET watch API diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/InternalEmailServiceTests.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/InternalEmailServiceTests.java index bcc9c5246c1..952d5d7c301 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/InternalEmailServiceTests.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/InternalEmailServiceTests.java @@ -10,7 +10,6 @@ import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.common.secret.Secret; -import org.elasticsearch.xpack.common.secret.SecretService; import org.junit.After; import org.junit.Before; @@ -33,7 +32,7 @@ public class InternalEmailServiceTests extends ESTestCase { @Before public void init() throws Exception { accounts = mock(Accounts.class); - service = new InternalEmailService(Settings.EMPTY, SecretService.Insecure.INSTANCE, + service = new InternalEmailService(Settings.EMPTY, null, new ClusterSettings(Settings.EMPTY, Collections.singleton(InternalEmailService.EMAIL_ACCOUNT_SETTING))) { @Override protected Accounts createAccounts(Settings settings, ESLogger logger) { diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/ManualPublicSmtpServersTester.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/ManualPublicSmtpServersTester.java index b648136513e..7df1aa0f019 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/ManualPublicSmtpServersTester.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/ManualPublicSmtpServersTester.java @@ -11,7 +11,6 @@ import org.elasticsearch.cli.Terminal; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.xpack.common.secret.SecretService; import java.util.Collections; import java.util.Locale; @@ -123,7 +122,7 @@ public class ManualPublicSmtpServersTester { static InternalEmailService startEmailService(Settings.Builder builder) { Settings settings = builder.build(); - InternalEmailService service = new InternalEmailService(settings, SecretService.Insecure.INSTANCE, + InternalEmailService service = new InternalEmailService(settings, null, new ClusterSettings(settings, Collections.singleton(InternalEmailService.EMAIL_ACCOUNT_SETTING))); service.start(); return service; diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/ProfileTests.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/ProfileTests.java index bda4490a98a..304de681427 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/ProfileTests.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/ProfileTests.java @@ -7,7 +7,6 @@ package org.elasticsearch.xpack.notification.email; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.xpack.common.secret.SecretService; import javax.mail.BodyPart; import javax.mail.Part; @@ -38,7 +37,7 @@ public class ProfileTests extends ESTestCase { .put("account.foo.smtp.host", "_host") .build(); - Accounts accounts = new Accounts(settings, SecretService.Insecure.INSTANCE, logger); + Accounts accounts = new Accounts(settings, null, logger); Session session = accounts.account("foo").getConfig().createSession(); MimeMessage mimeMessage = Profile.STANDARD.toMimeMessage(email, session); diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/attachment/HttpEmailAttachementParserTests.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/attachment/HttpEmailAttachementParserTests.java index f9c9e4d285c..7cb1f4d5393 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/attachment/HttpEmailAttachementParserTests.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/notification/email/attachment/HttpEmailAttachementParserTests.java @@ -17,7 +17,6 @@ import org.elasticsearch.xpack.common.http.HttpResponse; import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry; import org.elasticsearch.xpack.common.http.auth.basic.BasicAuth; import org.elasticsearch.xpack.common.http.auth.basic.BasicAuthFactory; -import org.elasticsearch.xpack.common.secret.SecretService; import org.elasticsearch.xpack.watcher.test.MockTextTemplateEngine; import org.junit.Before; @@ -42,8 +41,7 @@ public class HttpEmailAttachementParserTests extends ESTestCase { @Before public void init() throws Exception { - SecretService.Insecure secretService = SecretService.Insecure.INSTANCE; - HttpAuthRegistry authRegistry = new HttpAuthRegistry(singletonMap(BasicAuth.TYPE, new BasicAuthFactory(secretService))); + HttpAuthRegistry authRegistry = new HttpAuthRegistry(singletonMap(BasicAuth.TYPE, new BasicAuthFactory(null))); httpRequestTemplateParser = new HttpRequestTemplate.Parser(authRegistry); httpClient = mock(HttpClient.class); diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/support/clock/ClockMock.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/support/clock/ClockMock.java index 25ea390ae66..318b01d4c60 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/support/clock/ClockMock.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/support/clock/ClockMock.java @@ -12,7 +12,7 @@ import org.joda.time.DateTimeZone; import java.util.concurrent.TimeUnit; /** - * + * A clock that can be modified for testing. */ public class ClockMock implements Clock { diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/test/rest/XPackRestTestCase.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/test/rest/XPackRestTestCase.java index ddcd5479716..0a107f7705a 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/test/rest/XPackRestTestCase.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/test/rest/XPackRestTestCase.java @@ -8,33 +8,15 @@ package org.elasticsearch.xpack.test.rest; import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; -import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.ThreadContext; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.license.plugin.TestUtils; import org.elasticsearch.test.rest.ESRestTestCase; import org.elasticsearch.test.rest.RestTestCandidate; -import org.elasticsearch.test.rest.client.RestTestResponse; import org.elasticsearch.test.rest.parser.RestTestParseException; -import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm; import org.elasticsearch.xpack.security.authc.support.SecuredString; -import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore; -import org.junit.After; -import org.junit.Before; import java.io.IOException; -import java.util.List; -import java.util.Map; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; public abstract class XPackRestTestCase extends ESRestTestCase { @@ -50,19 +32,6 @@ public abstract class XPackRestTestCase extends ESRestTestCase { return ESRestTestCase.createParameters(0, 1); } - @Before - public void installLicense() throws Exception { - final XContentBuilder builder = XContentFactory.jsonBuilder(); - TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2)).toXContent(builder, ToXContent.EMPTY_PARAMS); - final BytesReference bytes = builder.bytes(); - try (XContentParser parser = XContentFactory.xContent(bytes).createParser(bytes)) { - final List> bodies = singletonList(singletonMap("license", - parser.map())); - getAdminExecutionContext().callApi("license.post", singletonMap("acknowledge", "true"), - bodies, singletonMap("Authorization", BASIC_AUTH_VALUE)); - } - } - @Override protected Settings restClientSettings() { return Settings.builder() diff --git a/elasticsearch/x-pack/src/test/resources/rest-api-spec/api/xpack.usage.json b/elasticsearch/x-pack/src/test/resources/rest-api-spec/api/xpack.usage.json index f66f3afad99..81a6204a4f0 100644 --- a/elasticsearch/x-pack/src/test/resources/rest-api-spec/api/xpack.usage.json +++ b/elasticsearch/x-pack/src/test/resources/rest-api-spec/api/xpack.usage.json @@ -7,6 +7,10 @@ "paths": [ "/_xpack/usage" ], "parts": {}, "params": { + "master_timeout": { + "type": "duration", + "description": "Specify timeout for watch write operation" + } } }, "body": null diff --git a/elasticsearch/x-pack/src/test/resources/rest-api-spec/test/xpack/15_basic.yaml b/elasticsearch/x-pack/src/test/resources/rest-api-spec/test/xpack/15_basic.yaml index 515670ce7f2..b65872fee18 100644 --- a/elasticsearch/x-pack/src/test/resources/rest-api-spec/test/xpack/15_basic.yaml +++ b/elasticsearch/x-pack/src/test/resources/rest-api-spec/test/xpack/15_basic.yaml @@ -7,7 +7,7 @@ wait_for_status: yellow - do: - license.delete: {} + xpack.license.delete: {} - match: { acknowledged: true } # we don't have a license now @@ -30,7 +30,7 @@ # - is_false: features.monitoring.available TODO fix once licensing is fixed - do: - license.post: + xpack.license.post: body: > { "license": { @@ -49,7 +49,7 @@ - match: { license_status: "valid" } - do: - license.get: {} + xpack.license.get: {} - match: { license.uid: "893361dc-9749-4997-93cb-802e3dofh7aa" } - match: { license.type: "internal" } - match: { license.status: "active" } @@ -84,7 +84,7 @@ - is_true: tagline - do: - xpack.usage: {} + xpack.usage: {} - is_true: watcher.enabled - is_true: watcher.available - is_true: security.enabled @@ -93,19 +93,21 @@ - is_true: graph.available - is_true: monitoring.enabled - is_true: monitoring.available + - gte: { watcher.count.total: 0 } + - gte: { watcher.count.active: 0 } - do: - xpack.info: - categories: "_none" + xpack.info: + categories: "_none" - is_false: build - is_false: features - is_false: license - match: { tagline: "You know, for X" } - do: - xpack.info: - categories: "_none" - human: false + xpack.info: + categories: "_none" + human: false - is_false: build - is_false: features - is_false: license diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java index 3ec90aedeef..30e9654f19b 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java @@ -19,6 +19,7 @@ import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.rest.RestHandler; @@ -29,6 +30,7 @@ import org.elasticsearch.xpack.watcher.actions.WatcherActionModule; import org.elasticsearch.xpack.watcher.client.WatcherClientModule; import org.elasticsearch.xpack.watcher.condition.ConditionModule; import org.elasticsearch.xpack.watcher.execution.ExecutionModule; +import org.elasticsearch.xpack.watcher.execution.ExecutionService; import org.elasticsearch.xpack.watcher.execution.InternalWatchExecutor; import org.elasticsearch.xpack.watcher.history.HistoryModule; import org.elasticsearch.xpack.watcher.history.HistoryStore; @@ -44,7 +46,6 @@ import org.elasticsearch.xpack.watcher.rest.action.RestWatchServiceAction; import org.elasticsearch.xpack.watcher.rest.action.RestWatcherStatsAction; import org.elasticsearch.xpack.watcher.support.WatcherIndexTemplateRegistry; import org.elasticsearch.xpack.watcher.support.WatcherIndexTemplateRegistry.TemplateConfig; -import org.elasticsearch.xpack.watcher.support.validation.WatcherSettingsValidation; import org.elasticsearch.xpack.watcher.transform.TransformModule; import org.elasticsearch.xpack.watcher.transport.actions.ack.AckWatchAction; import org.elasticsearch.xpack.watcher.transport.actions.ack.TransportAckWatchAction; @@ -87,6 +88,8 @@ public class Watcher implements ActionPlugin { new Setting<>("index.xpack.watcher.template.version", "", Function.identity(), Setting.Property.IndexScope); public static final Setting ENCRYPT_SENSITIVE_DATA_SETTING = Setting.boolSetting("xpack.watcher.encrypt_sensitive_data", false, Setting.Property.NodeScope); + public static final Setting MAX_STOP_TIMEOUT_SETTING = + Setting.timeSetting("xpack.watcher.stop.timeout", TimeValue.timeValueSeconds(30), Setting.Property.NodeScope); private static final ESLogger logger = Loggers.getLogger(XPackPlugin.class); @@ -127,9 +130,7 @@ public class Watcher implements ActionPlugin { if (enabled == false|| transportClient) { return Collections.emptyList(); } - return Arrays.>asList( - WatcherLicensee.class, - WatcherSettingsValidation.class); + return Collections.singletonList(WatcherLicensee.class); } public Settings additionalSettings() { @@ -144,6 +145,8 @@ public class Watcher implements ActionPlugin { } settings.add(INDEX_WATCHER_VERSION_SETTING); settings.add(INDEX_WATCHER_TEMPLATE_VERSION_SETTING); + settings.add(MAX_STOP_TIMEOUT_SETTING); + settings.add(ExecutionService.DEFAULT_THROTTLE_PERIOD_SETTING); settings.add(Setting.intSetting("xpack.watcher.execution.scroll.size", 0, Setting.Property.NodeScope)); settings.add(Setting.intSetting("xpack.watcher.watch.scroll.size", 0, Setting.Property.NodeScope)); settings.add(Setting.boolSetting(XPackPlugin.featureEnabledSetting(Watcher.NAME), true, Setting.Property.NodeScope)); @@ -152,7 +155,6 @@ public class Watcher implements ActionPlugin { settings.add(Setting.simpleString("xpack.watcher.internal.ops.search.default_timeout", Setting.Property.NodeScope)); settings.add(Setting.simpleString("xpack.watcher.internal.ops.bulk.default_timeout", Setting.Property.NodeScope)); settings.add(Setting.simpleString("xpack.watcher.internal.ops.index.default_timeout", Setting.Property.NodeScope)); - settings.add(Setting.simpleString("xpack.watcher.execution.default_throttle_period", Setting.Property.NodeScope)); settings.add(Setting.simpleString("xpack.watcher.actions.index.default_timeout", Setting.Property.NodeScope)); settings.add(Setting.simpleString("xpack.watcher.index.rest.direct_access", Setting.Property.NodeScope)); settings.add(Setting.simpleString("xpack.watcher.trigger.schedule.engine", Setting.Property.NodeScope)); diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java index dd97c2f6e47..daa629cfae7 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java @@ -9,10 +9,14 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; 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.xpack.XPackFeatureSet; import java.io.IOException; +import java.util.Collections; +import java.util.Map; /** * @@ -21,9 +25,12 @@ public class WatcherFeatureSet implements XPackFeatureSet { private final boolean enabled; private final WatcherLicensee licensee; + private final WatcherService watcherService; @Inject - public WatcherFeatureSet(Settings settings, @Nullable WatcherLicensee licensee, NamedWriteableRegistry namedWriteableRegistry) { + public WatcherFeatureSet(Settings settings, @Nullable WatcherLicensee licensee, NamedWriteableRegistry namedWriteableRegistry, + @Nullable WatcherService watcherService) { + this.watcherService = watcherService; this.enabled = Watcher.enabled(settings); this.licensee = licensee; namedWriteableRegistry.register(Usage.class, Usage.writeableName(Watcher.NAME), Usage::new); @@ -51,18 +58,41 @@ public class WatcherFeatureSet implements XPackFeatureSet { @Override public XPackFeatureSet.Usage usage() { - return new Usage(available(), enabled()); + return new Usage(available(), enabled(), watcherService != null ? watcherService.usageStats() : Collections.emptyMap()); } static class Usage extends XPackFeatureSet.Usage { - public Usage(StreamInput input) throws IOException { - super(input); + private final Map stats; + + public Usage(StreamInput in) throws IOException { + super(in); + stats = in.readMap(); } - public Usage(boolean available, boolean enabled) { + public Usage(boolean available, boolean enabled, Map stats) { super(Watcher.NAME, available, enabled); + this.stats = stats; } + public Map stats() { + return stats; + } + + @Override + protected void innerXContent(XContentBuilder builder, Params params) throws IOException { + super.innerXContent(builder, params); + if (enabled) { + for (Map.Entry entry : stats.entrySet()) { + builder.field(entry.getKey(), entry.getValue()); + } + } + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeMap(stats); + } } } diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherModule.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherModule.java index a51e4a7bffe..c656acfb661 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherModule.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherModule.java @@ -10,7 +10,6 @@ import org.elasticsearch.common.inject.AbstractModule; import org.elasticsearch.common.inject.util.Providers; import org.elasticsearch.xpack.XPackPlugin; import org.elasticsearch.xpack.watcher.support.WatcherIndexTemplateRegistry; -import org.elasticsearch.xpack.watcher.support.validation.WatcherSettingsValidation; public class WatcherModule extends AbstractModule { @@ -25,16 +24,21 @@ public class WatcherModule extends AbstractModule { @Override protected void configure() { - XPackPlugin.bindFeatureSet(binder(), WatcherFeatureSet.class); - - if (enabled == false || transportClientMode) { + if (transportClientMode) { bind(WatcherLicensee.class).toProvider(Providers.of(null)); return; } - bind(WatcherLicensee.class).asEagerSingleton(); - bind(WatcherLifeCycleService.class).asEagerSingleton(); - bind(WatcherSettingsValidation.class).asEagerSingleton(); - bind(WatcherIndexTemplateRegistry.class).asEagerSingleton(); + if (enabled == false) { + bind(WatcherLicensee.class).toProvider(Providers.of(null)); + // watcher service must be null, so that the watcher feature set can be instantiated even if watcher is not enabled + bind(WatcherService.class).toProvider(Providers.of(null)); + } else { + bind(WatcherLicensee.class).asEagerSingleton(); + bind(WatcherLifeCycleService.class).asEagerSingleton(); + bind(WatcherIndexTemplateRegistry.class).asEagerSingleton(); + } + + XPackPlugin.bindFeatureSet(binder(), WatcherFeatureSet.class); } } diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherService.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherService.java index aa1016d3b92..88836a6448e 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherService.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherService.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.index.engine.VersionConflictEngineException; +import org.elasticsearch.xpack.common.stats.Counters; import org.elasticsearch.xpack.watcher.execution.ExecutionService; import org.elasticsearch.xpack.watcher.support.WatcherIndexTemplateRegistry; import org.elasticsearch.xpack.support.clock.Clock; @@ -28,6 +29,8 @@ import org.joda.time.DateTimeZone; import org.joda.time.PeriodType; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import static org.elasticsearch.xpack.watcher.support.Exceptions.illegalArgument; @@ -285,4 +288,9 @@ public class WatcherService extends AbstractComponent { } } + public Map usageStats() { + Map innerMap = executionService.usageStats(); + innerMap.putAll(watchStore.usageStats()); + return innerMap; + } } diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java index c560fe8ae65..1447e03a7b8 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java @@ -10,16 +10,20 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.metrics.MeanMetric; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; import org.elasticsearch.xpack.support.clock.Clock; +import org.elasticsearch.xpack.watcher.Watcher; +import org.elasticsearch.xpack.watcher.WatcherFeatureSet; +import org.elasticsearch.xpack.common.stats.Counters; import org.elasticsearch.xpack.watcher.actions.ActionWrapper; import org.elasticsearch.xpack.watcher.condition.Condition; import org.elasticsearch.xpack.watcher.history.HistoryStore; import org.elasticsearch.xpack.watcher.history.WatchRecord; import org.elasticsearch.xpack.watcher.input.Input; -import org.elasticsearch.xpack.watcher.support.validation.WatcherSettingsValidation; import org.elasticsearch.xpack.watcher.transform.Transform; import org.elasticsearch.xpack.watcher.trigger.TriggerEvent; import org.elasticsearch.xpack.watcher.watch.Watch; @@ -33,17 +37,20 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.concurrent.TimeUnit; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -/** - */ public class ExecutionService extends AbstractComponent { - private static final TimeValue DEFAULT_MAX_STOP_TIMEOUT = new TimeValue(30, TimeUnit.SECONDS); - private static final String DEFAULT_MAX_STOP_TIMEOUT_SETTING = "xpack.watcher.stop.timeout"; + public static final Setting DEFAULT_THROTTLE_PERIOD_SETTING = + Setting.positiveTimeSetting("xpack.watcher.execution.default_throttle_period", + TimeValue.timeValueSeconds(5), Setting.Property.NodeScope); + + private final MeanMetric totalExecutionsTime = new MeanMetric(); + private final Map actionByTypeExecutionTime = new HashMap<>(); private final HistoryStore historyStore; private final TriggeredWatchStore triggeredWatchStore; @@ -59,8 +66,7 @@ public class ExecutionService extends AbstractComponent { @Inject public ExecutionService(Settings settings, HistoryStore historyStore, TriggeredWatchStore triggeredWatchStore, WatchExecutor executor, - WatchStore watchStore, WatchLockService watchLockService, Clock clock, - WatcherSettingsValidation settingsValidation) { + WatchStore watchStore, WatchLockService watchLockService, Clock clock) { super(settings); this.historyStore = historyStore; this.triggeredWatchStore = triggeredWatchStore; @@ -68,11 +74,8 @@ public class ExecutionService extends AbstractComponent { this.watchStore = watchStore; this.watchLockService = watchLockService; this.clock = clock; - this.defaultThrottlePeriod = settings.getAsTime("xpack.watcher.execution.default_throttle_period", TimeValue.timeValueSeconds(5)); - maxStopTimeout = settings.getAsTime(DEFAULT_MAX_STOP_TIMEOUT_SETTING, DEFAULT_MAX_STOP_TIMEOUT); - if (ExecutionService.this.defaultThrottlePeriod.millis() < 0) { - settingsValidation.addError("xpack.watcher.execution.default_throttle_period", "time value cannot be negative"); - } + this.defaultThrottlePeriod = DEFAULT_THROTTLE_PERIOD_SETTING.get(settings); + this.maxStopTimeout = Watcher.MAX_STOP_TIMEOUT_SETTING.get(settings); } public void start(ClusterState state) throws Exception { @@ -385,12 +388,19 @@ public class ExecutionService extends AbstractComponent { // actions ctx.beforeActions(); for (ActionWrapper action : watch.actions()) { + long now = System.currentTimeMillis(); ActionWrapper.Result actionResult = action.execute(ctx); + long executionTime = System.currentTimeMillis() - now; + String type = action.action().type(); + actionByTypeExecutionTime.putIfAbsent(type, new MeanMetric()); + actionByTypeExecutionTime.get(type).inc(executionTime); ctx.onActionResult(actionResult); } } - return ctx.finish(); + WatchRecord record = ctx.finish(); + totalExecutionsTime.inc(record.result().executionDurationMs()); + return record; } void executeTriggeredWatches(Collection triggeredWatches) throws Exception { @@ -415,6 +425,19 @@ public class ExecutionService extends AbstractComponent { logger.debug("executed [{}] watches from the watch history", counter); } + public Map usageStats() { + Counters counters = new Counters(); + counters.inc("execution.actions._all.total", totalExecutionsTime.count()); + counters.inc("execution.actions._all.total_time_in_ms", totalExecutionsTime.sum()); + + for (Map.Entry entry : actionByTypeExecutionTime.entrySet()) { + counters.inc("execution.actions." + entry.getKey() + ".total", entry.getValue().count()); + counters.inc("execution.actions." + entry.getKey() + ".total_time_in_ms", entry.getValue().sum()); + } + + return counters.toMap(); + } + private static final class StartupExecutionContext extends TriggeredExecutionContext { public StartupExecutionContext(Watch watch, DateTime executionTime, TriggerEvent triggerEvent, TimeValue defaultThrottlePeriod) { diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestWatchServiceAction.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestWatchServiceAction.java index c9161123aea..0d0bc6232a7 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestWatchServiceAction.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestWatchServiceAction.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.watcher.rest.action; -import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.rest.RestChannel; @@ -15,7 +14,6 @@ import org.elasticsearch.rest.action.support.AcknowledgedRestListener; import org.elasticsearch.xpack.watcher.client.WatcherClient; import org.elasticsearch.xpack.watcher.rest.WatcherRestHandler; import org.elasticsearch.xpack.watcher.transport.actions.service.WatcherServiceRequest; -import org.elasticsearch.xpack.watcher.transport.actions.service.WatcherServiceResponse; /** */ @@ -31,7 +29,7 @@ public class RestWatchServiceAction extends WatcherRestHandler { @Override public void handleRequest(RestRequest request, RestChannel channel, WatcherClient client) throws Exception { - client.watcherService(new WatcherServiceRequest().restart(), new AcknowledgedRestListener(channel)); + client.watcherService(new WatcherServiceRequest().restart(), new AcknowledgedRestListener<>(channel)); } static class StartRestHandler extends WatcherRestHandler { @@ -42,7 +40,7 @@ public class RestWatchServiceAction extends WatcherRestHandler { @Override public void handleRequest(RestRequest request, RestChannel channel, WatcherClient client) throws Exception { - client.watcherService(new WatcherServiceRequest().start(), new AcknowledgedRestListener(channel)); + client.watcherService(new WatcherServiceRequest().start(), new AcknowledgedRestListener<>(channel)); } } @@ -54,7 +52,7 @@ public class RestWatchServiceAction extends WatcherRestHandler { @Override public void handleRequest(RestRequest request, RestChannel channel, WatcherClient client) throws Exception { - client.watcherService(new WatcherServiceRequest().stop(), new AcknowledgedRestListener(channel)); + client.watcherService(new WatcherServiceRequest().stop(), new AcknowledgedRestListener<>(channel)); } } } diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/validation/WatcherSettingsValidation.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/validation/WatcherSettingsValidation.java deleted file mode 100644 index f320d151963..00000000000 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/validation/WatcherSettingsValidation.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.xpack.watcher.support.validation; - -import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.common.component.AbstractLifecycleComponent; -import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.logging.LoggerMessageFormat; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.xpack.watcher.support.Exceptions; - -import java.util.ArrayList; -import java.util.List; - -/** - * - */ -public class WatcherSettingsValidation extends AbstractLifecycleComponent { - - private List errors = new ArrayList<>(); - - @Inject - public WatcherSettingsValidation(Settings settings) { - super(settings); - } - - @Override - protected void doStart() throws ElasticsearchException { - validate(); - } - - @Override - protected void doStop() throws ElasticsearchException { - } - - @Override - protected void doClose() throws ElasticsearchException { - } - - public void addError(String setting, String reason) { - errors.add(LoggerMessageFormat.format("", "invalid [{}] setting value [{}]. {}", setting, settings.get(setting), reason)); - } - - private void validate() throws ElasticsearchException { - if (errors.isEmpty()) { - return; - } - StringBuilder sb = new StringBuilder("encountered invalid watcher settings:\n"); - for (String error : errors) { - sb.append("- ").append(error).append("\n"); - } - throw Exceptions.invalidSettings(sb.toString()); - } -} diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/xcontent/WatcherXContentParser.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/xcontent/WatcherXContentParser.java index 67afaf36634..6239c7903ec 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/xcontent/WatcherXContentParser.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/xcontent/WatcherXContentParser.java @@ -15,17 +15,17 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.xcontent.XContentLocation; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.support.clock.Clock; import org.elasticsearch.xpack.support.clock.SystemClock; import org.elasticsearch.xpack.common.secret.Secret; -import org.elasticsearch.xpack.common.secret.SecretService; /** * A xcontent parser that is used by watcher. This is a special parser that is * aware of watcher services. In particular, it's aware of the used {@link Clock} - * and the {@link SecretService}. The former (clock) may be used when the current time - * is required during the parse phase of construct. The latter (secret service) is used - * to convert secret values (e.g. passwords, security tokens, etc..) to {@link Secret}s. + * and the {@link CryptoService}. The former (clock) may be used when the current time + * is required during the parse phase of construct. The latter (crypto service) is used + * to encode secret values (e.g. passwords, security tokens, etc..) to {@link Secret}s. * {@link Secret}s are encrypted values that are stored in memory and are decrypted * on demand when needed. */ @@ -35,8 +35,8 @@ public class WatcherXContentParser implements XContentParser { char[] chars = parser.text().toCharArray(); if (parser instanceof WatcherXContentParser) { WatcherXContentParser watcherParser = (WatcherXContentParser) parser; - if (watcherParser.secretService != null) { - chars = watcherParser.secretService.encrypt(chars); + if (watcherParser.cryptoService != null) { + chars = watcherParser.cryptoService.encrypt(chars); } } return new Secret(chars); @@ -50,8 +50,8 @@ public class WatcherXContentParser implements XContentParser { char[] chars = parser.text().toCharArray(); if (parser instanceof WatcherXContentParser) { WatcherXContentParser watcherParser = (WatcherXContentParser) parser; - if (watcherParser.secretService != null) { - chars = watcherParser.secretService.encrypt(text.toCharArray()); + if (watcherParser.cryptoService != null) { + chars = watcherParser.cryptoService.encrypt(text.toCharArray()); } return new Secret(chars); } @@ -67,12 +67,12 @@ public class WatcherXContentParser implements XContentParser { private final Clock clock; private final XContentParser parser; - @Nullable private final SecretService secretService; + @Nullable private final CryptoService cryptoService; - public WatcherXContentParser(XContentParser parser, Clock clock, @Nullable SecretService secretService) { + public WatcherXContentParser(XContentParser parser, Clock clock, @Nullable CryptoService cryptoService) { this.clock = clock; this.parser = parser; - this.secretService = secretService; + this.cryptoService = cryptoService; } @Override diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/watch/Watch.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/watch/Watch.java index dc2e02d661a..cbe3d90bffd 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/watch/Watch.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/watch/Watch.java @@ -19,6 +19,9 @@ import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.xpack.common.secret.Secret; +import org.elasticsearch.xpack.security.crypto.CryptoService; +import org.elasticsearch.xpack.support.clock.Clock; +import org.elasticsearch.xpack.support.clock.HaltedClock; import org.elasticsearch.xpack.watcher.Watcher; import org.elasticsearch.xpack.watcher.actions.ActionRegistry; import org.elasticsearch.xpack.watcher.actions.ActionStatus; @@ -31,9 +34,6 @@ import org.elasticsearch.xpack.watcher.input.ExecutableInput; import org.elasticsearch.xpack.watcher.input.InputRegistry; import org.elasticsearch.xpack.watcher.input.none.ExecutableNoneInput; import org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils; -import org.elasticsearch.xpack.support.clock.Clock; -import org.elasticsearch.xpack.support.clock.HaltedClock; -import org.elasticsearch.xpack.common.secret.SecretService; import org.elasticsearch.xpack.watcher.support.xcontent.WatcherParams; import org.elasticsearch.xpack.watcher.support.xcontent.WatcherXContentParser; import org.elasticsearch.xpack.watcher.transform.ExecutableTransform; @@ -213,7 +213,7 @@ public class Watch implements TriggerEngine.Job, ToXContent { private final TransformRegistry transformRegistry; private final ActionRegistry actionRegistry; private final InputRegistry inputRegistry; - private final SecretService secretService; + private final CryptoService cryptoService; private final ExecutableInput defaultInput; private final ExecutableCondition defaultCondition; private final ExecutableActions defaultActions; @@ -222,7 +222,7 @@ public class Watch implements TriggerEngine.Job, ToXContent { @Inject public Parser(Settings settings, ConditionRegistry conditionRegistry, TriggerService triggerService, TransformRegistry transformRegistry, ActionRegistry actionRegistry, - InputRegistry inputRegistry, SecretService secretService, Clock clock) { + InputRegistry inputRegistry, @Nullable CryptoService cryptoService, Clock clock) { super(settings); this.conditionRegistry = conditionRegistry; @@ -230,7 +230,7 @@ public class Watch implements TriggerEngine.Job, ToXContent { this.triggerService = triggerService; this.actionRegistry = actionRegistry; this.inputRegistry = inputRegistry; - this.secretService = Watcher.ENCRYPT_SENSITIVE_DATA_SETTING.get(settings) ? secretService : SecretService.Insecure.INSTANCE; + this.cryptoService = Watcher.ENCRYPT_SENSITIVE_DATA_SETTING.get(settings) ? cryptoService : null; this.defaultInput = new ExecutableNoneInput(logger); this.defaultCondition = new ExecutableAlwaysCondition(logger); this.defaultActions = new ExecutableActions(Collections.emptyList()); @@ -249,10 +249,8 @@ public class Watch implements TriggerEngine.Job, ToXContent { * Parses the watch represented by the given source. When parsing, any sensitive data that the * source might contain (e.g. passwords) will be converted to {@link Secret secrets} * Such that the returned watch will potentially hide this sensitive data behind a "secret". A secret - * is an abstraction around sensitive data (text). There can be different implementations of how the - * secret holds the data, depending on the wired up {@link SecretService}. When security is enabled, a - * {@link SecretService.Secure} is used, that potentially encrypts the data - * using the configured system key. + * is an abstraction around sensitive data (text). When security is enabled, the + * {@link CryptoService} is used to encrypt the secrets. * * This method is only called once - when the user adds a new watch. From that moment on, all representations * of the watch in the system will be use secrets for sensitive data. @@ -269,7 +267,7 @@ public class Watch implements TriggerEngine.Job, ToXContent { } XContentParser parser = null; try { - parser = new WatcherXContentParser(createParser(source), new HaltedClock(now), withSecrets ? secretService : null); + parser = new WatcherXContentParser(createParser(source), new HaltedClock(now), withSecrets ? cryptoService : null); parser.nextToken(); return parse(id, includeStatus, parser); } catch (IOException ioe) { diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/watch/WatchStore.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/watch/WatchStore.java index eb3a53a8bcd..4e18fcaa3b6 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/watch/WatchStore.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/watch/WatchStore.java @@ -32,11 +32,17 @@ import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.xpack.security.InternalClient; +import org.elasticsearch.xpack.watcher.WatcherFeatureSet; +import org.elasticsearch.xpack.common.stats.Counters; +import org.elasticsearch.xpack.watcher.actions.ActionWrapper; import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy; +import org.elasticsearch.xpack.watcher.trigger.schedule.Schedule; +import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTrigger; import java.io.IOException; import java.util.Collection; import java.util.HashSet; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; @@ -204,8 +210,66 @@ public class WatchStore extends AbstractComponent { return watches; } - public int watchCount() { - return watches.size(); + public Map usageStats() { + Counters counters = new Counters("count.total", "count.active"); + for (Watch watch : watches.values()) { + boolean isActive = watch.status().state().isActive(); + addToCounters("count", isActive, counters); + + // schedule + if (watch.trigger() != null) { + addToCounters("watch.trigger._all", isActive, counters); + if ("schedule".equals(watch.trigger().type())) { + Schedule schedule = ((ScheduleTrigger) watch.trigger()).getSchedule(); + addToCounters("watch.trigger.schedule._all", isActive, counters); + addToCounters("watch.trigger.schedule." + schedule.type(), isActive, counters); + } + } + + // input + if (watch.input() != null) { + String type = watch.input().type(); + addToCounters("watch.input._all", isActive, counters); + addToCounters("watch.input." + type, isActive, counters); + } + + // condition + if (watch.condition() != null) { + String type = watch.condition().type(); + addToCounters("watch.condition._all", isActive, counters); + addToCounters("watch.condition." + type, isActive, counters); + } + + // actions + for (ActionWrapper actionWrapper : watch.actions()) { + String type = actionWrapper.action().type(); + addToCounters("watch.action." + type, isActive, counters); + if (actionWrapper.transform() != null) { + String transformType = actionWrapper.transform().type(); + addToCounters("watch.transform." + transformType, isActive, counters); + } + } + + // transform + if (watch.transform() != null) { + String type = watch.transform().type(); + addToCounters("watch.transform." + type, isActive, counters); + } + + // metadata + if (watch.metadata() != null && watch.metadata().size() > 0) { + addToCounters("watch.metadata", isActive, counters); + } + } + + return counters.toMap(); + } + + private void addToCounters(String name, boolean isActive, Counters counters) { + counters.inc(name + ".total"); + if (isActive) { + counters.inc(name + ".active"); + } } IndexRequest createIndexRequest(String id, BytesReference source, long version) { diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/script/MockMustacheScriptEngine.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/script/MockMustacheScriptEngine.java index 30f4105ba89..0fe5e1b6fa6 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/script/MockMustacheScriptEngine.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/script/MockMustacheScriptEngine.java @@ -8,7 +8,9 @@ package org.elasticsearch.script; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.xpack.common.text.DefaultTextTemplateEngine; +import java.util.Collections; import java.util.Map; +import java.util.function.Function; /** * A mock script engine that registers itself under the 'mustache' name so that @@ -19,11 +21,16 @@ public class MockMustacheScriptEngine extends MockScriptEngine { public static final String NAME = "mustache"; - public static class TestPlugin extends MockScriptEngine.TestPlugin { + public static class TestPlugin extends MockScriptPlugin { @Override public ScriptEngineService getScriptEngineService(Settings settings) { return new MockMustacheScriptEngine(); } + + @Override + protected Map, Object>> pluginScripts() { + return Collections.emptyMap(); + } } @Override diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherFeatureSetTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherFeatureSetTests.java index 3eb671a08aa..1736ee5997d 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherFeatureSetTests.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherFeatureSetTests.java @@ -7,9 +7,18 @@ package org.elasticsearch.xpack.watcher; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource; import org.junit.Before; +import java.util.HashMap; +import java.util.Map; + +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.core.Is.is; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.eq; @@ -24,20 +33,22 @@ public class WatcherFeatureSetTests extends ESTestCase { private WatcherLicensee licensee; private NamedWriteableRegistry namedWriteableRegistry; + private WatcherService watcherService; @Before public void init() throws Exception { licensee = mock(WatcherLicensee.class); namedWriteableRegistry = mock(NamedWriteableRegistry.class); + watcherService = mock(WatcherService.class); } public void testWritableRegistration() throws Exception { - new WatcherFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry); + new WatcherFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry, watcherService); verify(namedWriteableRegistry).register(eq(WatcherFeatureSet.Usage.class), eq("xpack.usage.watcher"), anyObject()); } public void testAvailable() throws Exception { - WatcherFeatureSet featureSet = new WatcherFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry); + WatcherFeatureSet featureSet = new WatcherFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry, watcherService); boolean available = randomBoolean(); when(licensee.isAvailable()).thenReturn(available); assertThat(featureSet.available(), is(available)); @@ -53,8 +64,24 @@ public class WatcherFeatureSetTests extends ESTestCase { } else { settings.put("xpack.watcher.enabled", enabled); } - WatcherFeatureSet featureSet = new WatcherFeatureSet(settings.build(), licensee, namedWriteableRegistry); + WatcherFeatureSet featureSet = new WatcherFeatureSet(settings.build(), licensee, namedWriteableRegistry, watcherService); assertThat(featureSet.enabled(), is(enabled)); } + public void testUsageStats() throws Exception { + Map statsMap = new HashMap<>(); + statsMap.put("foo", "bar"); + when(watcherService.usageStats()).thenReturn(statsMap); + + WatcherFeatureSet featureSet = new WatcherFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry, watcherService); + XContentBuilder builder = jsonBuilder(); + featureSet.usage().toXContent(builder, ToXContent.EMPTY_PARAMS); + + XContentSource source = new XContentSource(builder); + assertThat(source.getValue("foo"), is("bar")); + + assertThat(featureSet.usage(), instanceOf(WatcherFeatureSet.Usage.class)); + WatcherFeatureSet.Usage usage = (WatcherFeatureSet.Usage) featureSet.usage(); + assertThat(usage.stats(), hasEntry("foo", "bar")); + } } diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginDisableTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginDisableTests.java index edd002d8d13..fdae9edaf6d 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginDisableTests.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginDisableTests.java @@ -68,7 +68,7 @@ public class WatcherPluginDisableTests extends ESIntegTestCase { public void testRestEndpoints() throws Exception { HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class); try { - getRestClient().performRequest("GET", "/_xpack/watcher", Collections.emptyMap(), null); + getRestClient().performRequest("GET", "/_xpack/watcher"); fail("request should have failed"); } catch(ResponseException e) { assertThat(e.getResponse().getStatusLine().getStatusCode(), is(HttpStatus.SC_BAD_REQUEST)); diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherServiceTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherServiceTests.java index 998dffda5a3..1275e3c9105 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherServiceTests.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherServiceTests.java @@ -56,13 +56,14 @@ public class WatcherServiceTests extends ESTestCase { private WatcherService watcherService; private WatchLockService watchLockService; private ClockMock clock; + private ExecutionService executionService; @Before public void init() throws Exception { triggerService = mock(TriggerService.class); watchStore = mock(WatchStore.class); watchParser = mock(Watch.Parser.class); - ExecutionService executionService = mock(ExecutionService.class); + executionService = mock(ExecutionService.class); watchLockService = mock(WatchLockService.class); clock = new ClockMock(); WatcherIndexTemplateRegistry watcherIndexTemplateRegistry = mock(WatcherIndexTemplateRegistry.class); diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailActionTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailActionTests.java index d5e2fa7895c..f3ab788e03c 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailActionTests.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailActionTests.java @@ -25,7 +25,6 @@ import org.elasticsearch.xpack.common.http.HttpResponse; import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry; import org.elasticsearch.xpack.common.http.auth.basic.BasicAuthFactory; import org.elasticsearch.xpack.common.secret.Secret; -import org.elasticsearch.xpack.common.secret.SecretService; import org.elasticsearch.xpack.common.text.TextTemplate; import org.elasticsearch.xpack.common.text.TextTemplateEngine; import org.elasticsearch.xpack.watcher.support.xcontent.WatcherParams; @@ -82,8 +81,7 @@ import static org.mockito.Mockito.when; */ public class EmailActionTests extends ESTestCase { - private SecretService secretService = mock(SecretService.class); - private HttpAuthRegistry registry = new HttpAuthRegistry(singletonMap("basic", new BasicAuthFactory(secretService))); + private HttpAuthRegistry registry = new HttpAuthRegistry(singletonMap("basic", new BasicAuthFactory(null))); private HttpClient httpClient = mock(HttpClient.class); private EmailAttachmentsParser emailAttachmentParser; diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookActionTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookActionTests.java index 00c55c20ed6..644a9034a80 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookActionTests.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookActionTests.java @@ -28,7 +28,6 @@ import org.elasticsearch.xpack.common.http.HttpResponse; import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry; import org.elasticsearch.xpack.common.http.auth.basic.BasicAuthFactory; import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy; -import org.elasticsearch.xpack.common.secret.SecretService; import org.elasticsearch.xpack.common.text.TextTemplate; import org.elasticsearch.xpack.common.text.TextTemplateEngine; import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService; @@ -87,10 +86,9 @@ public class WebhookActionTests extends ESTestCase { @Before public void init() throws Exception { templateEngine = new MockTextTemplateEngine(); - SecretService secretService = mock(SecretService.class); testBody = TextTemplate.inline(TEST_BODY_STRING).build(); testPath = TextTemplate.inline(TEST_PATH_STRING).build(); - authRegistry = new HttpAuthRegistry(singletonMap("basic", new BasicAuthFactory(secretService))); + authRegistry = new HttpAuthRegistry(singletonMap("basic", new BasicAuthFactory(null))); } public void testExecute() throws Exception { diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/execution/ExecutionServiceTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/execution/ExecutionServiceTests.java index 8395da0acda..1eea012f815 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/execution/ExecutionServiceTests.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/execution/ExecutionServiceTests.java @@ -8,6 +8,8 @@ package org.elasticsearch.xpack.watcher.execution; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.support.clock.Clock; +import org.elasticsearch.xpack.support.clock.ClockMock; import org.elasticsearch.xpack.watcher.actions.Action; import org.elasticsearch.xpack.watcher.actions.ActionStatus; import org.elasticsearch.xpack.watcher.actions.ActionWrapper; @@ -23,9 +25,7 @@ import org.elasticsearch.xpack.watcher.history.HistoryStore; import org.elasticsearch.xpack.watcher.history.WatchRecord; import org.elasticsearch.xpack.watcher.input.ExecutableInput; import org.elasticsearch.xpack.watcher.input.Input; -import org.elasticsearch.xpack.support.clock.Clock; -import org.elasticsearch.xpack.support.clock.ClockMock; -import org.elasticsearch.xpack.watcher.support.validation.WatcherSettingsValidation; +import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource; import org.elasticsearch.xpack.watcher.transform.ExecutableTransform; import org.elasticsearch.xpack.watcher.transform.Transform; import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent; @@ -44,6 +44,7 @@ import java.util.concurrent.ArrayBlockingQueue; import static java.util.Collections.singletonMap; import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds; +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -89,10 +90,9 @@ public class ExecutionServiceTests extends ESTestCase { when(executor.queue()).thenReturn(new ArrayBlockingQueue(1)); watchLockService = mock(WatchLockService.class); - WatcherSettingsValidation settingsValidator = mock(WatcherSettingsValidation.class); clock = new ClockMock(); executionService = new ExecutionService(Settings.EMPTY, historyStore, triggeredWatchStore, executor, watchStore, - watchLockService, clock, settingsValidator); + watchLockService, clock); ClusterState clusterState = mock(ClusterState.class); when(triggeredWatchStore.loadTriggeredWatches(clusterState)).thenReturn(new ArrayList()); @@ -138,6 +138,7 @@ public class ExecutionServiceTests extends ESTestCase { when(actionResult.type()).thenReturn("_action_type"); when(actionResult.status()).thenReturn(Action.Result.Status.SUCCESS); ExecutableAction action = mock(ExecutableAction.class); + when(action.type()).thenReturn("MY_AWESOME_TYPE"); when(action.execute("_action", context, payload)).thenReturn(actionResult); ActionWrapper actionWrapper = new ActionWrapper("_action", throttler, actionTransform, action); @@ -165,6 +166,13 @@ public class ExecutionServiceTests extends ESTestCase { verify(condition, times(1)).execute(context); verify(watchTransform, times(1)).execute(context, payload); verify(action, times(1)).execute("_action", context, payload); + + // test stats + XContentSource source = new XContentSource(jsonBuilder().map(executionService.usageStats())); + assertThat(source.getValue("execution.actions._all.total_time_in_ms"), is(notNullValue())); + assertThat(source.getValue("execution.actions._all.total"), is(1)); + assertThat(source.getValue("execution.actions.MY_AWESOME_TYPE.total_time_in_ms"), is(notNullValue())); + assertThat(source.getValue("execution.actions.MY_AWESOME_TYPE.total"), is(1)); } public void testExecuteFailedInput() throws Exception { diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/input/http/HttpInputTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/input/http/HttpInputTests.java index 33dad76f532..5fd52fa4c92 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/input/http/HttpInputTests.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/input/http/HttpInputTests.java @@ -32,7 +32,6 @@ import org.elasticsearch.xpack.common.http.auth.HttpAuth; import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry; import org.elasticsearch.xpack.common.http.auth.basic.BasicAuth; import org.elasticsearch.xpack.common.http.auth.basic.BasicAuthFactory; -import org.elasticsearch.xpack.common.secret.SecretService; import org.elasticsearch.xpack.common.text.TextTemplate; import org.elasticsearch.xpack.common.text.TextTemplateEngine; import org.elasticsearch.xpack.watcher.trigger.schedule.IntervalSchedule; @@ -71,15 +70,13 @@ import static org.mockito.Mockito.when; public class HttpInputTests extends ESTestCase { private HttpClient httpClient; private HttpInputFactory httpParser; - private SecretService secretService; private TextTemplateEngine templateEngine; @Before public void init() throws Exception { httpClient = mock(HttpClient.class); templateEngine = mock(TextTemplateEngine.class); - secretService = mock(SecretService.class); - HttpAuthRegistry registry = new HttpAuthRegistry(singletonMap("basic", new BasicAuthFactory(secretService))); + HttpAuthRegistry registry = new HttpAuthRegistry(singletonMap("basic", new BasicAuthFactory(null))); httpParser = new HttpInputFactory(Settings.EMPTY, httpClient, templateEngine, new HttpRequestTemplate.Parser(registry)); } diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java index bc635db8019..af254eb1a09 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java @@ -32,13 +32,14 @@ import org.elasticsearch.xpack.security.authc.file.FileRealm; import org.elasticsearch.xpack.security.authc.support.Hasher; import org.elasticsearch.xpack.security.authc.support.SecuredString; import org.elasticsearch.xpack.security.authz.store.FileRolesStore; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.TestCluster; import org.elasticsearch.test.store.MockFSIndexStore; import org.elasticsearch.test.transport.AssertingLocalTransport; import org.elasticsearch.test.transport.MockTransportService; +import org.elasticsearch.xpack.support.clock.Clock; import org.elasticsearch.xpack.watcher.WatcherLifeCycleService; import org.elasticsearch.xpack.watcher.WatcherService; import org.elasticsearch.xpack.watcher.WatcherState; @@ -270,7 +271,7 @@ public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase private void setupTimeWarp() throws Exception { if (timeWarped()) { - timeWarp = new TimeWarp(getInstanceFromMaster(ScheduleTriggerEngineMock.class), getInstanceFromMaster(ClockMock.class)); + timeWarp = new TimeWarp(getInstanceFromMaster(ScheduleTriggerEngineMock.class), (ClockMock)getInstanceFromMaster(Clock.class)); } } @@ -699,7 +700,7 @@ public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase .put("xpack.security.authc.realms.esusers.files.users", writeFile(folder, "users", USERS)) .put("xpack.security.authc.realms.esusers.files.users_roles", writeFile(folder, "users_roles", USER_ROLES)) .put(FileRolesStore.ROLES_FILE_SETTING.getKey(), writeFile(folder, "roles.yml", ROLES)) - .put(InternalCryptoService.FILE_SETTING.getKey(), writeFile(folder, "system_key.yml", systemKey)) + .put(CryptoService.FILE_SETTING.getKey(), writeFile(folder, "system_key.yml", systemKey)) .put("xpack.security.authc.sign_user_header", false) .put("xpack.security.audit.enabled", auditLogsEnabled) .build(); @@ -710,7 +711,7 @@ public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase static byte[] generateKey() { try { - return InternalCryptoService.generateKey(); + return CryptoService.generateKey(); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/bench/WatcherExecutorServiceBenchmark.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/bench/WatcherExecutorServiceBenchmark.java index e40b45629ee..cbfef411e76 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/bench/WatcherExecutorServiceBenchmark.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/bench/WatcherExecutorServiceBenchmark.java @@ -22,6 +22,7 @@ import org.elasticsearch.xpack.watcher.trigger.ScheduleTriggerEngineMock; import org.elasticsearch.xpack.watcher.trigger.TriggerModule; import org.elasticsearch.xpack.XPackPlugin; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -204,7 +205,7 @@ public class WatcherExecutorServiceBenchmark { public static final class XPackBenchmarkPlugin extends XPackPlugin { - public XPackBenchmarkPlugin(Settings settings) { + public XPackBenchmarkPlugin(Settings settings) throws IOException { super(settings); watcher = new BenchmarkWatcher(settings); } diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/HttpSecretsIntegrationTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/HttpSecretsIntegrationTests.java index 3cf46f318f3..5b81379d618 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/HttpSecretsIntegrationTests.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/HttpSecretsIntegrationTests.java @@ -12,12 +12,12 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.support.XContentMapValues; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.watcher.client.WatcherClient; import org.elasticsearch.xpack.watcher.execution.ActionExecutionMode; import org.elasticsearch.xpack.common.http.HttpRequestTemplate; import org.elasticsearch.xpack.common.http.auth.basic.ApplicableBasicAuth; import org.elasticsearch.xpack.common.http.auth.basic.BasicAuth; -import org.elasticsearch.xpack.common.secret.SecretService; import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource; import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase; import org.elasticsearch.xpack.watcher.transport.actions.execute.ExecuteWatchResponse; @@ -117,18 +117,10 @@ public class HttpSecretsIntegrationTests extends AbstractWatcherIntegrationTestC assertThat(value, notNullValue()); if (securityEnabled() && encryptSensitiveData) { assertThat(value, not(is((Object) PASSWORD))); - SecretService secretService = getInstanceFromMaster(SecretService.class); - assertThat(secretService, instanceOf(SecretService.Secure.class)); - assertThat(new String(secretService.decrypt(((String) value).toCharArray())), is(PASSWORD)); + CryptoService cryptoService = getInstanceFromMaster(CryptoService.class); + assertThat(new String(cryptoService.decrypt(((String) value).toCharArray())), is(PASSWORD)); } else { assertThat(value, is((Object) PASSWORD)); - SecretService secretService = getInstanceFromMaster(SecretService.class); - if (securityEnabled()) { - assertThat(secretService, instanceOf(SecretService.Secure.class)); - } else { - assertThat(secretService, instanceOf(SecretService.Insecure.class)); - } - assertThat(new String(secretService.decrypt(((String) value).toCharArray())), is(PASSWORD)); } // verifying the password is not returned by the GET watch API @@ -189,18 +181,10 @@ public class HttpSecretsIntegrationTests extends AbstractWatcherIntegrationTestC if (securityEnabled() && encryptSensitiveData) { assertThat(value, not(is((Object) PASSWORD))); - SecretService secretService = getInstanceFromMaster(SecretService.class); - assertThat(secretService, instanceOf(SecretService.Secure.class)); - assertThat(new String(secretService.decrypt(((String) value).toCharArray())), is(PASSWORD)); + CryptoService cryptoService = getInstanceFromMaster(CryptoService.class); + assertThat(new String(cryptoService.decrypt(((String) value).toCharArray())), is(PASSWORD)); } else { assertThat(value, is((Object) PASSWORD)); - SecretService secretService = getInstanceFromMaster(SecretService.class); - if (securityEnabled()) { - assertThat(secretService, instanceOf(SecretService.Secure.class)); - } else { - assertThat(secretService, instanceOf(SecretService.Insecure.class)); - } - assertThat(new String(secretService.decrypt(((String) value).toCharArray())), is(PASSWORD)); } // verifying the password is not returned by the GET watch API diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchStoreTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchStoreTests.java index f97a6504345..7422df2b747 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchStoreTests.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchStoreTests.java @@ -32,11 +32,29 @@ import org.elasticsearch.search.SearchHitField; import org.elasticsearch.search.internal.InternalSearchHit; import org.elasticsearch.search.internal.InternalSearchHits; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.watcher.actions.ActionWrapper; +import org.elasticsearch.xpack.watcher.actions.ExecutableAction; +import org.elasticsearch.xpack.watcher.actions.ExecutableActions; +import org.elasticsearch.xpack.watcher.condition.always.ExecutableAlwaysCondition; +import org.elasticsearch.xpack.watcher.condition.never.ExecutableNeverCondition; +import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext; +import org.elasticsearch.xpack.watcher.input.none.ExecutableNoneInput; import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy; +import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource; +import org.elasticsearch.xpack.watcher.transform.ExecutableTransform; +import org.elasticsearch.xpack.watcher.transform.Transform; +import org.elasticsearch.xpack.watcher.trigger.schedule.Schedule; +import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTrigger; import org.junit.Before; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.List; +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsEqual.equalTo; import static org.mockito.Matchers.any; @@ -280,6 +298,127 @@ public class WatchStoreTests extends ESTestCase { verify(clientProxy, times(1)).clearScroll(anyString()); } + public void testUsageStats() throws Exception { + ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name")); + MetaData.Builder metaDateBuilder = MetaData.builder(); + RoutingTable.Builder routingTableBuilder = RoutingTable.builder(); + Settings settings = settings(Version.CURRENT) + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1) + .build(); + metaDateBuilder.put(IndexMetaData.builder(WatchStore.INDEX).settings(settings).numberOfShards(1).numberOfReplicas(1)); + final Index index = metaDateBuilder.get(WatchStore.INDEX).getIndex(); + IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(index); + indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(index, 0)) + .addShard(TestShardRouting.newShardRouting(WatchStore.INDEX, 0, "_node_id", null, true, ShardRoutingState.STARTED)) + .build()); + indexRoutingTableBuilder.addReplica(); + routingTableBuilder.add(indexRoutingTableBuilder.build()); + csBuilder.metaData(metaDateBuilder); + csBuilder.routingTable(routingTableBuilder.build()); + + RefreshResponse refreshResponse = mockRefreshResponse(1, 1); + when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse); + + BytesReference source = new BytesArray("{}"); + int hitCount = randomIntBetween(50, 100); + int activeHitCount = 0; + + List hits = new ArrayList<>(); + for (int i = 0; i < hitCount; i++) { + InternalSearchHit hit = new InternalSearchHit(0, "_id" + i, new Text("type"), Collections.emptyMap()); + hits.add(hit.sourceRef(source)); + + Watch watch = mock(Watch.class); + WatchStatus status = mock(WatchStatus.class); + when(watch.status()).thenReturn(status); + + boolean isActive = usually(); + WatchStatus.State state = mock(WatchStatus.State.class); + when(state.isActive()).thenReturn(isActive); + when(status.state()).thenReturn(state); + if (isActive) { + activeHitCount++; + } + + // random schedule + ScheduleTrigger mockTricker = mock(ScheduleTrigger.class); + when(watch.trigger()).thenReturn(mockTricker); + when(mockTricker.type()).thenReturn("schedule"); + String scheduleType = randomFrom("a", "b", "c"); + Schedule mockSchedule = mock(Schedule.class); + when(mockSchedule.type()).thenReturn(scheduleType); + when(mockTricker.getSchedule()).thenReturn(mockSchedule); + + // either a none input, or null + when(watch.input()).thenReturn(randomFrom(new ExecutableNoneInput(logger), null)); + + // random conditions + when(watch.condition()).thenReturn(randomFrom(new ExecutableAlwaysCondition(logger), null, + new ExecutableNeverCondition(logger))); + + // random actions + ActionWrapper actionWrapper = mock(ActionWrapper.class); + ExecutableAction action = mock(ExecutableAction.class); + when(actionWrapper.action()).thenReturn(action); + when(action.type()).thenReturn(randomFrom("a", "b", "c")); + when(watch.actions()).thenReturn(new ExecutableActions(Arrays.asList(actionWrapper))); + + // random transform, not always set + Transform mockTransform = mock(Transform.class); + when(mockTransform.type()).thenReturn("TYPE"); + + @SuppressWarnings("unchecked") + ExecutableTransform testTransform = new ExecutableTransform(mockTransform, logger) { + @Override + public Transform.Result execute(WatchExecutionContext ctx, Payload payload) { + return null; + } + }; + when(watch.transform()).thenReturn(randomFrom(testTransform, null)); + + when(parser.parse("_id" + i, true, source)).thenReturn(watch); + } + + SearchResponse searchResponse = mockSearchResponse(1, 1, hitCount, hits.toArray(new InternalSearchHit[] {})); + when(clientProxy.search(any(SearchRequest.class), any(TimeValue.class))).thenReturn(searchResponse); + SearchResponse noHitsResponse = mockSearchResponse(1, 1, 2); + when(clientProxy.searchScroll(anyString(), any(TimeValue.class))).thenReturn(noHitsResponse); + when(clientProxy.clearScroll(anyString())).thenReturn(new ClearScrollResponse(true, 0)); + + ClusterState cs = csBuilder.build(); + watchStore.start(cs); + + XContentSource stats = new XContentSource(jsonBuilder().map(watchStore.usageStats())); + + assertThat(stats.getValue("count.total"), is(hitCount)); + assertThat(stats.getValue("count.active"), is(activeHitCount)); + + // schedule count + int scheduleCountA = stats.getValue("watch.trigger.schedule.a.active"); + int scheduleCountB = stats.getValue("watch.trigger.schedule.b.active"); + int scheduleCountC = stats.getValue("watch.trigger.schedule.c.active"); + assertThat(scheduleCountA + scheduleCountB + scheduleCountC, is(activeHitCount)); + + // input count + assertThat(stats.getValue("watch.input.none.active"), is(greaterThan(0))); + assertThat(stats.getValue("watch.input.none.total"), is(greaterThan(0))); + assertThat(stats.getValue("watch.input.none.total"), is(lessThan(activeHitCount))); + + // condition count + assertThat(stats.getValue("watch.condition.never.active"), is(greaterThan(0))); + assertThat(stats.getValue("watch.condition.always.active"), is(greaterThan(0))); + + // action count + int actionCountA = stats.getValue("watch.action.a.active"); + int actionCountB = stats.getValue("watch.action.b.active"); + int actionCountC = stats.getValue("watch.action.c.active"); + assertThat(actionCountA + actionCountB + actionCountC, is(activeHitCount)); + + // transform count + assertThat(stats.getValue("watch.transform.TYPE.active"), is(greaterThan(0))); + } + private RefreshResponse mockRefreshResponse(int total, int successful) { RefreshResponse refreshResponse = mock(RefreshResponse.class); when(refreshResponse.getTotalShards()).thenReturn(total); diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchTests.java index 5e06c2dd4dd..c867e1d43d5 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchTests.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchTests.java @@ -23,7 +23,6 @@ import org.elasticsearch.xpack.common.http.HttpMethod; import org.elasticsearch.xpack.common.http.HttpRequestTemplate; import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry; import org.elasticsearch.xpack.common.http.auth.basic.BasicAuthFactory; -import org.elasticsearch.xpack.common.secret.SecretService; import org.elasticsearch.xpack.common.text.TextTemplate; import org.elasticsearch.xpack.common.text.TextTemplateEngine; import org.elasticsearch.xpack.notification.email.DataAttachment; @@ -149,7 +148,6 @@ public class WatchTests extends ESTestCase { private TextTemplateEngine templateEngine; private HtmlSanitizer htmlSanitizer; private HttpAuthRegistry authRegistry; - private SecretService secretService; private WatcherLicensee watcherLicensee; private ESLogger logger; private Settings settings = Settings.EMPTY; @@ -163,9 +161,8 @@ public class WatchTests extends ESTestCase { emailService = mock(EmailService.class); templateEngine = mock(TextTemplateEngine.class); htmlSanitizer = mock(HtmlSanitizer.class); - secretService = mock(SecretService.class); watcherLicensee = mock(WatcherLicensee.class); - authRegistry = new HttpAuthRegistry(singletonMap("basic", new BasicAuthFactory(secretService))); + authRegistry = new HttpAuthRegistry(singletonMap("basic", new BasicAuthFactory(null))); logger = Loggers.getLogger(WatchTests.class); searchTemplateService = mock(WatcherSearchTemplateService.class); } @@ -181,7 +178,6 @@ public class WatchTests extends ESTestCase { ScheduleRegistry scheduleRegistry = registry(schedule); TriggerEngine triggerEngine = new ParseOnlyScheduleTriggerEngine(Settings.EMPTY, scheduleRegistry, clock); TriggerService triggerService = new TriggerService(Settings.EMPTY, singleton(triggerEngine)); - SecretService secretService = SecretService.Insecure.INSTANCE; ExecutableInput input = randomInput(); InputRegistry inputRegistry = registry(input); @@ -209,7 +205,7 @@ public class WatchTests extends ESTestCase { BytesReference bytes = XContentFactory.jsonBuilder().value(watch).bytes(); logger.info("{}", bytes.utf8ToString()); Watch.Parser watchParser = new Watch.Parser(settings, conditionRegistry, triggerService, transformRegistry, actionRegistry, - inputRegistry, secretService, clock); + inputRegistry, null, clock); Watch parsedWatch = watchParser.parse("_name", includeStatus, bytes); @@ -231,7 +227,6 @@ public class WatchTests extends ESTestCase { ScheduleRegistry scheduleRegistry = registry(randomSchedule()); TriggerEngine triggerEngine = new ParseOnlyScheduleTriggerEngine(Settings.EMPTY, scheduleRegistry, clock); TriggerService triggerService = new TriggerService(Settings.EMPTY, singleton(triggerEngine)); - SecretService secretService = SecretService.Insecure.INSTANCE; ExecutableCondition condition = randomCondition(); ConditionRegistry conditionRegistry = registry(condition); ExecutableInput input = randomInput(); @@ -248,7 +243,7 @@ public class WatchTests extends ESTestCase { .startArray("actions").endArray() .endObject(); Watch.Parser watchParser = new Watch.Parser(settings, conditionRegistry, triggerService, transformRegistry, actionRegistry, - inputRegistry, secretService, clock); + inputRegistry, null, clock); try { watchParser.parse("failure", false, jsonBuilder.bytes()); fail("This watch should fail to parse as actions is an array"); @@ -262,7 +257,6 @@ public class WatchTests extends ESTestCase { ScheduleRegistry scheduleRegistry = registry(schedule); TriggerEngine triggerEngine = new ParseOnlyScheduleTriggerEngine(Settings.EMPTY, scheduleRegistry, SystemClock.INSTANCE); TriggerService triggerService = new TriggerService(Settings.EMPTY, singleton(triggerEngine)); - SecretService secretService = SecretService.Insecure.INSTANCE; ConditionRegistry conditionRegistry = registry(new ExecutableAlwaysCondition(logger)); InputRegistry inputRegistry = registry(new ExecutableNoneInput(logger)); @@ -277,7 +271,7 @@ public class WatchTests extends ESTestCase { .endObject(); builder.endObject(); Watch.Parser watchParser = new Watch.Parser(settings, conditionRegistry, triggerService, transformRegistry, actionRegistry, - inputRegistry, secretService, SystemClock.INSTANCE); + inputRegistry, null, SystemClock.INSTANCE); Watch watch = watchParser.parse("failure", false, builder.bytes()); assertThat(watch, notNullValue()); assertThat(watch.trigger(), instanceOf(ScheduleTrigger.class)); diff --git a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/array_compare_watch/10_basic.yaml b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/array_compare_watch/10_basic.yaml index 509fe741583..102a69cd778 100644 --- a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/array_compare_watch/10_basic.yaml +++ b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/array_compare_watch/10_basic.yaml @@ -10,7 +10,8 @@ wait_for_status: yellow - - do: {xpack.watcher.stats:{}} + - do: + xpack.watcher.stats: {} - match: { "watcher_state": "started" } - match: { "watch_count": 0 } @@ -93,7 +94,8 @@ - do: indices.refresh: {} - - do: {xpack.watcher.stats:{}} + - do: + xpack.watcher.stats: {} - match: { "watch_count": 1 } # Simulate a Thread.sleep() @@ -142,5 +144,6 @@ - match: { found: true } - - do: {xpack.watcher.stats:{}} + - do: + xpack.watcher.stats: {} - match: { "watch_count": 0 } diff --git a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/get_watch/10_basic.yaml b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/get_watch/10_basic.yaml index faa96303041..355a52a71aa 100644 --- a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/get_watch/10_basic.yaml +++ b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/get_watch/10_basic.yaml @@ -1,13 +1,22 @@ --- -"Test get watch api": +setup: - do: cluster.health: wait_for_status: yellow +--- +teardown: + - do: + xpack.watcher.delete_watch: + id: "my_watch" + ignore: 404 + +--- +"Test get watch api": - do: xpack.watcher.put_watch: id: "my_watch" - body: > + body: > { "trigger": { "schedule": { @@ -43,5 +52,5 @@ id: "my_watch" - match: { found : true} - match: { _id: "my_watch" } - - match: { _status.version: 1 } + - is_true: _status.version - is_false: watch.status diff --git a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/getting_started/10_monitor_cluster_health.yaml b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/getting_started/10_monitor_cluster_health.yaml index c84dc9b07dd..e3353c4cf6c 100644 --- a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/getting_started/10_monitor_cluster_health.yaml +++ b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/getting_started/10_monitor_cluster_health.yaml @@ -1,18 +1,27 @@ +--- +setup: + - do: + cluster.health: + wait_for_status: yellow + +--- +teardown: + - do: + xpack.watcher.delete_watch: + id: "cluster_health_watch" + ignore: 404 + --- "Getting started - Monitor cluster health": - do: - cluster.health: - wait_for_status: yellow - - - - do: {xpack.watcher.stats:{}} + xpack.watcher.stats: {} - match: { "watcher_state": "started" } - match: { "watch_count": 0 } - do: xpack.watcher.put_watch: id: "cluster_health_watch" - body: > + body: > { "trigger": { "schedule": { @@ -52,7 +61,8 @@ - match: { _id: "cluster_health_watch" } - match: { created: true } - - do: {xpack.watcher.stats:{}} + - do: + xpack.watcher.stats: {} - match: { "watch_count": 1 } # Simulate a Thread.sleep() @@ -101,5 +111,6 @@ - match: { found: true } - - do: {xpack.watcher.stats:{}} + - do: + xpack.watcher.stats: {} - match: { "watch_count": 0 } diff --git a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/put_watch/20_put_watch_with_throttle_period.yaml b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/put_watch/20_put_watch_with_throttle_period.yaml index d9ba99221cd..4d36f92e92d 100644 --- a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/put_watch/20_put_watch_with_throttle_period.yaml +++ b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/put_watch/20_put_watch_with_throttle_period.yaml @@ -1,9 +1,18 @@ --- -"Test put watch api with watch level throttle": +setup: - do: cluster.health: - wait_for_status: yellow + wait_for_status: yellow +--- +teardown: + - do: + xpack.watcher.delete_watch: + id: "my_watch1" + ignore: 404 + +--- +"Test put watch api with watch level throttle": - do: xpack.watcher.put_watch: id: "my_watch1" diff --git a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/put_watch/30_put_watch_with_action_throttle_period.yaml b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/put_watch/30_put_watch_with_action_throttle_period.yaml index c53847a0064..30348737d2e 100644 --- a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/put_watch/30_put_watch_with_action_throttle_period.yaml +++ b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/put_watch/30_put_watch_with_action_throttle_period.yaml @@ -1,9 +1,18 @@ --- -"Test put watch api with action level throttle period": +setup: - do: cluster.health: - wait_for_status: yellow + wait_for_status: yellow +--- +teardown: + - do: + xpack.watcher.delete_watch: + id: "my_watch1" + ignore: 404 + +--- +"Test put watch api with action level throttle period": - do: xpack.watcher.put_watch: id: "my_watch1" diff --git a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/put_watch/40_put_watch_as_inactive.yaml b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/put_watch/40_put_watch_as_inactive.yaml index 2a533f3320b..ee25ab5acc1 100644 --- a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/put_watch/40_put_watch_as_inactive.yaml +++ b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/xpack/watcher/put_watch/40_put_watch_as_inactive.yaml @@ -1,15 +1,24 @@ --- -"Test put inactive watch": +setup: - do: cluster.health: wait_for_status: yellow +--- +teardown: + - do: + xpack.watcher.delete_watch: + id: "my_watch" + ignore: 404 + +--- +"Test put inactive watch": - do: xpack.watcher.put_watch: id: "my_watch" master_timeout: "40s" active: false - body: > + body: > { "trigger": { "schedule": { @@ -45,5 +54,4 @@ - match: { found : true } - match: { _id: "my_watch" } - - match: { _status.version: 1 } - match: { _status.state.active: false }